Repository: CosmWasm/sylvia
Pull request #253 (2023-11-13 11:17:00)

feat: Generic support

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/CosmWasm/sylvia/pull/253?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) Report
> Merging [#253](https://app.codecov.io/gh/CosmWasm/sylvia/pull/253?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) (25df909) into [main](https://app.codecov.io/gh/CosmWasm/sylvia/commit/e6131e4cda694de6cb2afb93789a093fe4ad4f4d?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) (e6131e4) will **decrease** coverage by `1.51%`.
> Report is 3 commits behind head on main.
> The diff coverage is `83.65%`.

```diff
@@            Coverage Diff             @@
##             main     #253      +/-   ##
==========================================
- Coverage   87.72%   86.21%   -1.51%     
==========================================
  Files          24       25       +1     
  Lines        1303     1741     +438     
==========================================
+ Hits         1143     1501     +358     
- Misses        160      240      +80     
```


| [Files](https://app.codecov.io/gh/CosmWasm/sylvia/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) | Coverage Δ | |
|---|---|---|
| [sylvia-derive/src/lib.rs](https://app.codecov.io/gh/CosmWasm/sylvia/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm#diff-c3lsdmlhLWRlcml2ZS9zcmMvbGliLnJz) | `100.00% <ø> (ø)` | |
| [sylvia-derive/src/remote.rs](https://app.codecov.io/gh/CosmWasm/sylvia/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm#diff-c3lsdmlhLWRlcml2ZS9zcmMvcmVtb3RlLnJz) | `100.00% <100.00%> (ø)` | |
| [sylvia/src/into\_response.rs](https://app.codecov.io/gh/CosmWasm/sylvia/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm#diff-c3lsdmlhL3NyYy9pbnRvX3Jlc3BvbnNlLnJz) | `100.00% <100.00%> (ø)` | |
| [sylvia/src/types.rs](https://app.codecov.io/gh/CosmWasm/sylvia/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm#diff-c3lsdmlhL3NyYy90eXBlcy5ycw==) | `80.00% <ø> (ø)` | |
| [sylvia/tests/custom\_msg.rs](https://app.codecov.io/gh/CosmWasm/sylvia/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm#diff-c3lsdmlhL3Rlc3RzL2N1c3RvbV9tc2cucnM=) | `100.00% <ø> (ø)` | |
| [sylvia/tests/custom\_query.rs](https://app.codecov.io/gh/CosmWasm/sylvia/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm#diff-c3lsdmlhL3Rlc3RzL2N1c3RvbV9xdWVyeS5ycw==) | `92.00% <ø> (ø)` | |
| [sylvia/tests/dispatching.rs](https://app.codecov.io/gh/CosmWasm/sylvia/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm#diff-c3lsdmlhL3Rlc3RzL2Rpc3BhdGNoaW5nLnJz) | `93.33% <100.00%> (ø)` | |
| [sylvia/tests/entry\_points.rs](https://app.codecov.io/gh/CosmWasm/sylvia/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm#diff-c3lsdmlhL3Rlc3RzL2VudHJ5X3BvaW50cy5ycw==) | `100.00% <ø> (ø)` | |
| [sylvia/tests/messages\_generation.rs](https://app.codecov.io/gh/CosmWasm/sylvia/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm#diff-c3lsdmlhL3Rlc3RzL21lc3NhZ2VzX2dlbmVyYXRpb24ucnM=) | `0.00% <ø> (ø)` | |
| [sylvia/tests/querier.rs](https://app.codecov.io/gh/CosmWasm/sylvia/pull/253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm#diff-c3lsdmlhL3Rlc3RzL3F1ZXJpZXIucnM=) | `96.66% <100.00%> (+0.11%)` | :arrow_up: |
| ... and [10 more](https://app.codecov.io/gh/CosmWasm/sylvia/pull/253?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) | |

... and [1 file with indirect coverage changes](https://app.codecov.io/gh/CosmWasm/sylvia/pull/253/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm)

:mega: Codecov offers a browser extension for seamless coverage viewing on GitHub. Try it in [Chrome](https://chrome.google.com/webstore/detail/codecov/gedikamndpbemklijjkncpnolildpbgo) or [Firefox](https://addons.mozilla.org/en-US/firefox/addon/codecov/) today!


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 31b3ba7..8eb303f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -66,6 +66,7 @@ jobs:
         run: cargo clippy --all-targets -- -D warnings 
       - name: Fmt check project
         run: cargo fmt --check
+
       - name: Test examples
         working-directory: examples
         run: cargo test --locked
@@ -75,6 +76,7 @@ jobs:
       - name: Fmt check examples
         working-directory: examples
         run: cargo fmt --check
+
       - name: Build cw20-base example
         working-directory: examples/contracts/cw20-base
         run: cargo build --release --target wasm32-unknown-unknown --locked --lib
@@ -90,10 +92,18 @@ jobs:
       - name: Build custom
         working-directory: examples/contracts/custom
         run: cargo build --release --target wasm32-unknown-unknown --locked --lib
+      - name: Build generic_contract 
+        working-directory: examples/contracts/generic_contract
+        run: cargo build --release --target wasm32-unknown-unknown --locked --lib
+      - name: Build generic_iface_on_contract 
+        working-directory: examples/contracts/generic_iface_on_contract
+        run: cargo build --release --target wasm32-unknown-unknown --locked --lib
+
       - name: Install cosmwasm-check
         run: cargo install cosmwasm-check --force
       - name: Check contracts
         run: find examples/target/wasm32-unknown-unknown/release/ -type f -name "*.wasm" -exec cosmwasm-check {} \;
+
       - name: Cw1-whitelist schema
         working-directory: examples/contracts/cw1-whitelist/
         run: cargo schema
@@ -109,6 +119,13 @@ jobs:
       - name: Custom schema
         working-directory: examples/contracts/custom
         run: cargo schema
+      - name: Generic_contract schema
+        working-directory: examples/contracts/generic_contract
+        run: cargo schema
+      - name: generic_iface_on_contract schema
+        working-directory: examples/contracts/generic_iface_on_contract
+        run: cargo schema
+
       - name: Cw1-whitelist ts-codegen 
         working-directory: examples/contracts/cw1-whitelist/
         run: cosmwasm-ts-codegen generate --plugin client --schema ./schema --out ./ts --name cw1-whitelist --no-bundle
@@ -124,6 +141,13 @@ jobs:
       - name: Custom ts-codegen
         working-directory: examples/contracts/custom/
         run: cosmwasm-ts-codegen generate --plugin client --schema ./schema --out ./ts --name custom --no-bundle
+      - name: Generic_contract ts-codegen
+        working-directory: examples/contracts/generic_contract/
+        run: cosmwasm-ts-codegen generate --plugin client --schema ./schema --out ./ts --name custom --no-bundle
+      - name: Generic_iface_on_contract ts-codegen
+        working-directory: examples/contracts/generic_iface_on_contract
+        run: cosmwasm-ts-codegen generate --plugin client --schema ./schema --out ./ts --name custom --no-bundle
+
       - name: Archive schema artifats
         uses: actions/upload-artifact@v3
         with:
@@ -134,6 +158,8 @@ jobs:
             examples/contracts/cw20-base/schema/cw20-base.json
             examples/contracts/entry-points-overriding/schema/entry-points-overriding.json
             examples/contracts/custom/schema/custom.json
+            examples/contracts/generic_contract/schema/generic_contract.json
+            examples/contracts/generic_iface_on_contract/schema/generic_iface_on_contract.json
 
   coverage:
     name: Code coverage
diff --git a/examples/Cargo.lock b/examples/Cargo.lock
index ae267fe..a606c72 100644
--- a/examples/Cargo.lock
+++ b/examples/Cargo.lock
@@ -242,6 +242,18 @@ dependencies = [
  "sylvia",
 ]
 
+[[package]]
+name = "custom-and-generic"
+version = "0.5.0"
+dependencies = [
+ "anyhow",
+ "cosmwasm-schema",
+ "cosmwasm-std",
+ "cw-multi-test",
+ "serde",
+ "sylvia",
+]
+
 [[package]]
 name = "cw-multi-test"
 version = "0.16.5"
@@ -316,6 +328,7 @@ dependencies = [
  "serde",
  "sylvia",
  "thiserror",
+ "whitelist",
 ]
 
 [[package]]
@@ -334,6 +347,7 @@ dependencies = [
  "serde",
  "sylvia",
  "thiserror",
+ "whitelist",
 ]
 
 [[package]]
@@ -550,6 +564,18 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e"
 
+[[package]]
+name = "generic"
+version = "0.5.0"
+dependencies = [
+ "anyhow",
+ "cosmwasm-schema",
+ "cosmwasm-std",
+ "cw-multi-test",
+ "serde",
+ "sylvia",
+]
+
 [[package]]
 name = "generic-array"
 version = "0.14.7"
@@ -560,6 +586,40 @@ dependencies = [
  "version_check",
 ]
 
+[[package]]
+name = "generic_contract"
+version = "0.5.0"
+dependencies = [
+ "anyhow",
+ "cosmwasm-schema",
+ "cosmwasm-std",
+ "custom-and-generic",
+ "cw-multi-test",
+ "cw-storage-plus",
+ "cw-utils",
+ "cw1",
+ "generic",
+ "serde",
+ "sylvia",
+]
+
+[[package]]
+name = "generic_iface_on_contract"
+version = "0.5.0"
+dependencies = [
+ "anyhow",
+ "cosmwasm-schema",
+ "cosmwasm-std",
+ "custom-and-generic",
+ "cw-multi-test",
+ "cw-storage-plus",
+ "cw-utils",
+ "cw1",
+ "generic",
+ "serde",
+ "sylvia",
+]
+
 [[package]]
 name = "getrandom"
 version = "0.2.10"
@@ -1160,6 +1220,18 @@ version = "0.2.86"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
 
+[[package]]
+name = "whitelist"
+version = "0.5.0"
+dependencies = [
+ "anyhow",
+ "cosmwasm-schema",
+ "cosmwasm-std",
+ "cw-multi-test",
+ "serde",
+ "sylvia",
+]
+
 [[package]]
 name = "winnow"
 version = "0.4.6"
diff --git a/examples/Cargo.toml b/examples/Cargo.toml
index a8a0e3a..0becc45 100644
--- a/examples/Cargo.toml
+++ b/examples/Cargo.toml
@@ -1,18 +1,23 @@
 [workspace]
 members = [
-  # Contract intefaces
-  "interfaces/cw1",
-  "interfaces/cw4",
-  "interfaces/cw20-allowances",
-  "interfaces/cw20-minting",
-  "interfaces/cw20-marketing",
+    # Contract intefaces
+    "interfaces/cw1",
+    "interfaces/whitelist",
+    "interfaces/cw4",
+    "interfaces/cw20-allowances",
+    "interfaces/cw20-minting",
+    "interfaces/cw20-marketing",
+    "interfaces/custom-and-generic",
+    "interfaces/generic",
 
-  # Contracts
-  "contracts/cw1-whitelist",
-  "contracts/cw1-subkeys",
-  "contracts/cw20-base",
-  "contracts/entry-points-overriding",
-  "contracts/custom",
+    # Contracts
+    "contracts/cw1-whitelist",
+    "contracts/cw1-subkeys",
+    "contracts/cw20-base",
+    "contracts/entry-points-overriding",
+    "contracts/custom",
+    "contracts/generic_contract",
+    "contracts/generic_iface_on_contract",
 ]
 resolver = "2"
 
diff --git a/examples/contracts/custom/src/bin/schema.rs b/examples/contracts/custom/src/bin/schema.rs
index a60261b..554d313 100644
--- a/examples/contracts/custom/src/bin/schema.rs
+++ b/examples/contracts/custom/src/bin/schema.rs
@@ -1,6 +1,6 @@
 use cosmwasm_schema::write_api;
 
-use custom::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg};
+use custom::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg};
 
 #[cfg(not(tarpaulin_include))]
 fn main() {
diff --git a/examples/contracts/cw1-subkeys/Cargo.toml b/examples/contracts/cw1-subkeys/Cargo.toml
index 12dc5f7..41f8b08 100644
--- a/examples/contracts/cw1-subkeys/Cargo.toml
+++ b/examples/contracts/cw1-subkeys/Cargo.toml
@@ -18,6 +18,7 @@ cw-multi-test = { version = "0.16.2", optional = true }
 cw-storage-plus = "1.0"
 cw-utils = "1.0"
 cw1 = { path = "../../interfaces/cw1" }
+whitelist = { path = "../../interfaces/whitelist" }
 cw1-whitelist = { path = "../cw1-whitelist", features = ["library"] }
 cw2 = "1.0"
 getrandom = { version = "0.2.8", features = ["js"] }
diff --git a/examples/contracts/cw1-subkeys/src/bin/schema.rs b/examples/contracts/cw1-subkeys/src/bin/schema.rs
index b07cdba..c512923 100644
--- a/examples/contracts/cw1-subkeys/src/bin/schema.rs
+++ b/examples/contracts/cw1-subkeys/src/bin/schema.rs
@@ -1,6 +1,6 @@
 use cosmwasm_schema::write_api;
 
-use cw1_subkeys::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg};
+use cw1_subkeys::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg};
 
 #[cfg(not(tarpaulin_include))]
 fn main() {
diff --git a/examples/contracts/cw1-subkeys/src/contract.rs b/examples/contracts/cw1-subkeys/src/contract.rs
index cd86a33..5bd9b49 100644
--- a/examples/contracts/cw1-subkeys/src/contract.rs
+++ b/examples/contracts/cw1-subkeys/src/contract.rs
@@ -3,7 +3,6 @@ use cosmwasm_std::{
     StakingMsg, StdResult,
 };
 use cw1_whitelist::contract::Cw1WhitelistContract;
-use cw1_whitelist::whitelist;
 use cw2::set_contract_version;
 use cw_storage_plus::{Bound, Map};
 use cw_utils::Expiration;
diff --git a/examples/contracts/cw1-subkeys/src/whitelist.rs b/examples/contracts/cw1-subkeys/src/whitelist.rs
index c36556a..eb29e84 100644
--- a/examples/contracts/cw1-subkeys/src/whitelist.rs
+++ b/examples/contracts/cw1-subkeys/src/whitelist.rs
@@ -1,8 +1,8 @@
 use cosmwasm_std::{Response, StdResult};
-use cw1_whitelist::responses::AdminListResponse;
-use cw1_whitelist::whitelist::{self, Whitelist};
 use sylvia::contract;
 use sylvia::types::{ExecCtx, QueryCtx};
+use whitelist::responses::AdminListResponse;
+use whitelist::Whitelist;
 
 use crate::contract::Cw1SubkeysContract;
 use crate::error::ContractError;
diff --git a/examples/contracts/cw1-whitelist/Cargo.toml b/examples/contracts/cw1-whitelist/Cargo.toml
index c307894..6718fbe 100644
--- a/examples/contracts/cw1-whitelist/Cargo.toml
+++ b/examples/contracts/cw1-whitelist/Cargo.toml
@@ -22,6 +22,7 @@ cosmwasm-schema = "1.2"
 serde = { version = "1.0", default-features = false, features = ["derive"] }
 sylvia = { path = "../../../sylvia" }
 cw1 = { path = "../../interfaces/cw1" }
+whitelist = { path = "../../interfaces/whitelist" }
 cw-storage-plus = "1.0"
 thiserror = { version = "1.0" }
 cw2 = "1.0"
diff --git a/examples/contracts/cw1-whitelist/src/bin/schema.rs b/examples/contracts/cw1-whitelist/src/bin/schema.rs
index fa957ed..3096b12 100644
--- a/examples/contracts/cw1-whitelist/src/bin/schema.rs
+++ b/examples/contracts/cw1-whitelist/src/bin/schema.rs
@@ -1,6 +1,6 @@
 use cosmwasm_schema::write_api;
 
-use cw1_whitelist::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg};
+use cw1_whitelist::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg};
 
 #[cfg(not(tarpaulin_include))]
 fn main() {
diff --git a/examples/contracts/cw1-whitelist/src/contract.rs b/examples/contracts/cw1-whitelist/src/contract.rs
index f27bdd8..2bf302a 100644
--- a/examples/contracts/cw1-whitelist/src/contract.rs
+++ b/examples/contracts/cw1-whitelist/src/contract.rs
@@ -1,5 +1,4 @@
 use crate::error::ContractError;
-use crate::whitelist;
 use cosmwasm_std::{Addr, Deps, Empty, Response};
 
 use cw2::set_contract_version;
@@ -57,11 +56,11 @@ impl Cw1WhitelistContract<'_> {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::responses::AdminListResponse;
-    use crate::whitelist::{self, Whitelist};
     use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
     use cosmwasm_std::{coin, coins, to_binary, BankMsg, CosmosMsg, StakingMsg, SubMsg, WasmMsg};
     use cw1::Cw1;
+    use whitelist::responses::AdminListResponse;
+    use whitelist::Whitelist;
 
     #[test]
     fn instantiate_and_modify_config() {
@@ -182,7 +181,7 @@ mod tests {
             )
             .unwrap();
 
-        let freeze = whitelist::ExecMsg::Freeze {};
+        let freeze = whitelist::sv::ExecMsg::Freeze {};
         let msgs = vec![
             BankMsg::Send {
                 to_address: bob.to_string(),
@@ -289,15 +288,13 @@ mod tests {
     }
 
     mod msgs {
-        use super::*;
-
         use cosmwasm_std::{from_binary, from_slice, to_binary, BankMsg};
 
-        use crate::contract::{ContractExecMsg, ContractQueryMsg};
+        use crate::contract::sv::{ContractExecMsg, ContractQueryMsg};
 
         #[test]
         fn freeze() {
-            let original = whitelist::ExecMsg::Freeze {};
+            let original = whitelist::sv::ExecMsg::Freeze {};
             let serialized = to_binary(&original).unwrap();
             let deserialized = from_binary(&serialized).unwrap();
 
@@ -309,14 +306,14 @@ mod tests {
             let deserialized = from_slice(json).unwrap();
 
             assert_eq!(
-                ContractExecMsg::Whitelist(whitelist::ExecMsg::Freeze {}),
+                ContractExecMsg::Whitelist(whitelist::sv::ExecMsg::Freeze {}),
                 deserialized
             );
         }
 
         #[test]
         fn update_admins() {
-            let original = whitelist::ExecMsg::UpdateAdmins {
+            let original = whitelist::sv::ExecMsg::UpdateAdmins {
                 admins: vec!["admin1".to_owned(), "admin2".to_owned()],
             };
             let serialized = to_binary(&original).unwrap();
@@ -332,7 +329,7 @@ mod tests {
             let deserialized = from_slice(json).unwrap();
 
             assert_eq!(
-                ContractExecMsg::Whitelist(whitelist::ExecMsg::UpdateAdmins {
+                ContractExecMsg::Whitelist(whitelist::sv::ExecMsg::UpdateAdmins {
                     admins: vec!["admin1".to_owned(), "admin3".to_owned()]
                 }),
                 deserialized
@@ -341,7 +338,7 @@ mod tests {
 
         #[test]
         fn admin_list() {
-            let original = whitelist::QueryMsg::AdminList {};
+            let original = whitelist::sv::QueryMsg::AdminList {};
             let serialized = to_binary(&original).unwrap();
             let deserialized = from_binary(&serialized).unwrap();
 
@@ -353,14 +350,14 @@ mod tests {
             let deserialized = from_slice(json).unwrap();
 
             assert_eq!(
-                ContractQueryMsg::Whitelist(whitelist::QueryMsg::AdminList {}),
+                ContractQueryMsg::Whitelist(whitelist::sv::QueryMsg::AdminList {}),
                 deserialized
             );
         }
 
         #[test]
         fn execute() {
-            let original = cw1::ExecMsg::Execute {
+            let original = cw1::sv::ExecMsg::Execute {
                 msgs: vec![BankMsg::Send {
                     to_address: "admin1".to_owned(),
                     amount: vec![],
@@ -374,7 +371,7 @@ mod tests {
 
         #[test]
         fn can_execute() {
-            let original = cw1::QueryMsg::CanExecute {
+            let original = cw1::sv::QueryMsg::CanExecute {
                 sender: "admin".to_owned(),
                 msg: BankMsg::Send {
                     to_address: "admin1".to_owned(),
diff --git a/examples/contracts/cw1-whitelist/src/lib.rs b/examples/contracts/cw1-whitelist/src/lib.rs
index cc023ca..52af57a 100644
--- a/examples/contracts/cw1-whitelist/src/lib.rs
+++ b/examples/contracts/cw1-whitelist/src/lib.rs
@@ -3,5 +3,4 @@ mod cw1;
 pub mod error;
 #[cfg(any(test, feature = "tests"))]
 pub mod multitest;
-pub mod responses;
 pub mod whitelist;
diff --git a/examples/contracts/cw1-whitelist/src/whitelist.rs b/examples/contracts/cw1-whitelist/src/whitelist.rs
index 68e2a79..156a4af 100644
--- a/examples/contracts/cw1-whitelist/src/whitelist.rs
+++ b/examples/contracts/cw1-whitelist/src/whitelist.rs
@@ -1,26 +1,14 @@
 use cosmwasm_std::{Empty, Order, Response, StdResult};
+use sylvia::contract;
 use sylvia::types::{ExecCtx, QueryCtx};
-use sylvia::{contract, interface, schemars};
+use whitelist::responses::AdminListResponse;
+use whitelist::Whitelist;
 
 use crate::contract::Cw1WhitelistContract;
 use crate::error::ContractError;
-use crate::responses::AdminListResponse;
-
-#[interface]
-pub trait Whitelist {
-    type Error: From<cosmwasm_std::StdError>;
-
-    #[msg(exec)]
-    fn freeze(&self, ctx: ExecCtx) -> Result<Response, Self::Error>;
-
-    #[msg(exec)]
-    fn update_admins(&self, ctx: ExecCtx, admins: Vec<String>) -> Result<Response, Self::Error>;
-
-    #[msg(query)]
-    fn admin_list(&self, ctx: QueryCtx) -> StdResult<AdminListResponse>;
-}
 
 #[contract(module=crate::contract)]
+#[messages(whitelist as Whitelist)]
 impl Whitelist for Cw1WhitelistContract<'_> {
     type Error = ContractError;
 
diff --git a/examples/contracts/cw20-base/src/bin/schema.rs b/examples/contracts/cw20-base/src/bin/schema.rs
index 3482c2f..ee47c95 100644
--- a/examples/contracts/cw20-base/src/bin/schema.rs
+++ b/examples/contracts/cw20-base/src/bin/schema.rs
@@ -1,6 +1,6 @@
 use cosmwasm_schema::write_api;
 
-use cw20_base::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg};
+use cw20_base::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg};
 
 #[cfg(not(tarpaulin_include))]
 fn main() {
diff --git a/examples/contracts/entry-points-overriding/src/bin/schema.rs b/examples/contracts/entry-points-overriding/src/bin/schema.rs
index 0c9fdd4..6d5bd4e 100644
--- a/examples/contracts/entry-points-overriding/src/bin/schema.rs
+++ b/examples/contracts/entry-points-overriding/src/bin/schema.rs
@@ -1,6 +1,6 @@
 use cosmwasm_schema::write_api;
 
-use entry_points_overriding::contract::{ContractQueryMsg, InstantiateMsg};
+use entry_points_overriding::contract::sv::{ContractQueryMsg, InstantiateMsg};
 use entry_points_overriding::messages::CustomExecMsg;
 use entry_points_overriding::messages::SudoMsg;
 
diff --git a/examples/contracts/entry-points-overriding/src/messages.rs b/examples/contracts/entry-points-overriding/src/messages.rs
index c032e2b..56b372b 100644
--- a/examples/contracts/entry-points-overriding/src/messages.rs
+++ b/examples/contracts/entry-points-overriding/src/messages.rs
@@ -2,7 +2,8 @@ use cosmwasm_schema::cw_serde;
 use cosmwasm_std::{DepsMut, Env, MessageInfo, Response, StdError, StdResult};
 use sylvia::types::ExecCtx;
 
-use crate::contract::{ContractExecMsg, CounterContract};
+use crate::contract::sv::ContractExecMsg;
+use crate::contract::CounterContract;
 
 #[cw_serde]
 pub struct CountResponse {
diff --git a/examples/contracts/generic_contract/.cargo/config b/examples/contracts/generic_contract/.cargo/config
new file mode 100644
index 0000000..d8ab80f
--- /dev/null
+++ b/examples/contracts/generic_contract/.cargo/config
@@ -0,0 +1,6 @@
+[alias]
+wasm = "build --release --target wasm32-unknown-unknown --lib"
+wasm-debug = "build --target wasm32-unknown-unknown --lib"
+unit-test = "test --lib"
+integration-test = "test --test integration"
+schema = "run --bin schema"
diff --git a/examples/contracts/generic_contract/Cargo.toml b/examples/contracts/generic_contract/Cargo.toml
new file mode 100644
index 0000000..af6f57d
--- /dev/null
+++ b/examples/contracts/generic_contract/Cargo.toml
@@ -0,0 +1,34 @@
+[package]
+name = "generic_contract"
+version = { workspace = true }
+authors = ["Jan Woźniak <jan@confio.gmbh>"]
+edition = { workspace = true }
+description = "Example of generic contract"
+license = "Apache-2.0"
+repository = "https://github.com/CosmWasm/sylvia"
+homepage = "https://cosmwasm.com"
+
+[lib]
+crate-type = ["cdylib", "rlib"]
+
+[features]
+library = []
+tests = ["library", "cw-multi-test", "anyhow"]
+
+[dependencies]
+anyhow = { version = "1.0", optional = true }
+cosmwasm-schema = "1.2"
+cosmwasm-std = { version = "1.3", features = ["staking"] }
+cw-multi-test = { version = "0.16", optional = true }
+cw-storage-plus = "1.0"
+cw-utils = "1.0"
+serde = { version = "1.0", default-features = false, features = ["derive"] }
+sylvia = { path = "../../../sylvia" }
+cw1 = { path = "../../interfaces/cw1" }
+generic = { path = "../../interfaces/generic" }
+custom-and-generic = { path = "../../interfaces/custom-and-generic" }
+
+[dev-dependencies]
+anyhow = "1.0"
+cw-multi-test = "0.16"
+sylvia = { path = "../../../sylvia", features = ["mt"] }
diff --git a/examples/contracts/generic_contract/src/bin/schema.rs b/examples/contracts/generic_contract/src/bin/schema.rs
new file mode 100644
index 0000000..db8d754
--- /dev/null
+++ b/examples/contracts/generic_contract/src/bin/schema.rs
@@ -0,0 +1,13 @@
+use cosmwasm_schema::write_api;
+
+#[cfg(not(tarpaulin_include))]
+fn main() {
+    use generic_contract::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg};
+    use sylvia::types::SvCustomMsg;
+
+    write_api! {
+        instantiate: InstantiateMsg<SvCustomMsg>,
+        execute: ContractExecMsg<SvCustomMsg>,
+        query: ContractQueryMsg<SvCustomMsg>,
+    }
+}
diff --git a/examples/contracts/generic_contract/src/contract.rs b/examples/contracts/generic_contract/src/contract.rs
new file mode 100644
index 0000000..12f605f
--- /dev/null
+++ b/examples/contracts/generic_contract/src/contract.rs
@@ -0,0 +1,132 @@
+use cosmwasm_std::{Reply, Response, StdResult};
+use cw_storage_plus::Item;
+use serde::de::DeserializeOwned;
+use serde::Deserialize;
+use sylvia::types::{
+    CustomMsg, ExecCtx, InstantiateCtx, MigrateCtx, QueryCtx, ReplyCtx, SvCustomMsg,
+};
+use sylvia::{contract, schemars};
+
+#[cfg(not(feature = "library"))]
+use sylvia::entry_points;
+
+pub struct GenericContract<
+    InstantiateParam,
+    ExecParam,
+    QueryParam,
+    MigrateParam,
+    RetType,
+    FieldType,
+> {
+    _field: Item<'static, FieldType>,
+    _phantom: std::marker::PhantomData<(
+        InstantiateParam,
+        ExecParam,
+        QueryParam,
+        MigrateParam,
+        RetType,
+    )>,
+}
+
+#[cfg_attr(not(feature = "library"), entry_points(generics<SvCustomMsg, SvCustomMsg, SvCustomMsg, sylvia::types::SvCustomMsg, SvCustomMsg, String>))]
+#[contract]
+#[messages(cw1 as Cw1: custom(msg))]
+#[messages(generic<SvCustomMsg, SvCustomMsg, sylvia::types::SvCustomMsg> as Generic: custom(msg))]
+#[messages(custom_and_generic<SvCustomMsg, SvCustomMsg, sylvia::types::SvCustomMsg> as CustomAndGeneric)]
+#[sv::custom(msg=SvCustomMsg)]
+impl<InstantiateParam, ExecParam, QueryParam, MigrateParam, RetType, FieldType>
+    GenericContract<InstantiateParam, ExecParam, QueryParam, MigrateParam, RetType, FieldType>
+where
+    for<'msg_de> InstantiateParam: CustomMsg + Deserialize<'msg_de> + 'msg_de,
+    ExecParam: CustomMsg + DeserializeOwned + 'static,
+    QueryParam: CustomMsg + DeserializeOwned + 'static,
+    MigrateParam: CustomMsg + DeserializeOwned + 'static,
+    RetType: CustomMsg + DeserializeOwned + 'static,
+    FieldType: 'static,
+{
+    pub const fn new() -> Self {
+        Self {
+            _field: Item::new("field"),
+            _phantom: std::marker::PhantomData,
+        }
+    }
+
+    #[msg(instantiate)]
+    pub fn instantiate(
+        &self,
+        _ctx: InstantiateCtx,
+        _msg: InstantiateParam,
+    ) -> StdResult<Response<SvCustomMsg>> {
+        Ok(Response::new())
+    }
+
+    #[msg(exec)]
+    pub fn contract_execute(
+        &self,
+        _ctx: ExecCtx,
+        _msg: ExecParam,
+    ) -> StdResult<Response<SvCustomMsg>> {
+        Ok(Response::new())
+    }
+
+    #[msg(query)]
+    pub fn contract_query(
+        &self,
+        _ctx: QueryCtx,
+        _msg: QueryParam,
+    ) -> StdResult<Response<SvCustomMsg>> {
+        Ok(Response::new())
+    }
+
+    #[msg(migrate)]
+    pub fn migrate(
+        &self,
+        _ctx: MigrateCtx,
+        _msg: MigrateParam,
+    ) -> StdResult<Response<SvCustomMsg>> {
+        Ok(Response::new())
+    }
+
+    #[allow(dead_code)]
+    #[msg(reply)]
+    fn reply(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult<Response<SvCustomMsg>> {
+        Ok(Response::new())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::sv::multitest_utils::CodeId;
+    use sylvia::multitest::App;
+    use sylvia::types::SvCustomMsg;
+
+    #[test]
+    fn generic_contract() {
+        let app = App::<cw_multi_test::BasicApp<SvCustomMsg>>::custom(|_, _, _| {});
+        let code_id: CodeId<
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+            super::SvCustomMsg,
+            super::SvCustomMsg,
+            String,
+            _,
+        > = CodeId::store_code(&app);
+
+        let owner = "owner";
+
+        let contract = code_id
+            .instantiate(SvCustomMsg {})
+            .with_label("GenericContract")
+            .with_admin(owner)
+            .call(owner)
+            .unwrap();
+
+        contract.contract_execute(SvCustomMsg).call(owner).unwrap();
+        contract.contract_query(SvCustomMsg).unwrap();
+        contract
+            .migrate(SvCustomMsg)
+            .call(owner, code_id.code_id())
+            .unwrap();
+    }
+}
diff --git a/examples/contracts/generic_contract/src/custom_and_generic.rs b/examples/contracts/generic_contract/src/custom_and_generic.rs
new file mode 100644
index 0000000..2dfadcd
--- /dev/null
+++ b/examples/contracts/generic_contract/src/custom_and_generic.rs
@@ -0,0 +1,79 @@
+use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult};
+use custom_and_generic::CustomAndGeneric;
+use sylvia::contract;
+use sylvia::types::{ExecCtx, QueryCtx, SvCustomMsg};
+
+#[contract(module = crate::contract)]
+#[messages(custom_and_generic as CustomAndGeneric)]
+#[sv::custom(msg=sylvia::types::SvCustomMsg)]
+impl<InstantiateParam, ExecParam, QueryParam, MigrateParam, RetType, FieldType>
+    CustomAndGeneric<SvCustomMsg, SvCustomMsg, sylvia::types::SvCustomMsg>
+    for crate::contract::GenericContract<
+        InstantiateParam,
+        ExecParam,
+        QueryParam,
+        MigrateParam,
+        RetType,
+        FieldType,
+    >
+{
+    type Error = StdError;
+
+    #[msg(exec)]
+    fn custom_generic_execute(
+        &self,
+        _ctx: ExecCtx,
+        _msgs: Vec<CosmosMsg<sylvia::types::SvCustomMsg>>,
+    ) -> StdResult<Response<SvCustomMsg>> {
+        Ok(Response::new())
+    }
+
+    #[msg(query)]
+    fn custom_generic_query(
+        &self,
+        _ctx: QueryCtx,
+        _msg: sylvia::types::SvCustomMsg,
+    ) -> StdResult<SvCustomMsg> {
+        Ok(SvCustomMsg {})
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::sv::test_utils::CustomAndGeneric;
+    use crate::contract::sv::multitest_utils::CodeId;
+    use sylvia::{multitest::App, types::SvCustomMsg};
+
+    #[test]
+    fn proxy_methods() {
+        let app = App::<cw_multi_test::BasicApp<SvCustomMsg>>::custom(|_, _, _| {});
+        let code_id = CodeId::<
+            SvCustomMsg,
+            sylvia::types::SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+            sylvia::types::SvCustomMsg,
+            String,
+            _,
+        >::store_code(&app);
+
+        let owner = "owner";
+
+        let contract = code_id
+            .instantiate(SvCustomMsg {})
+            .with_label("GenericContract")
+            .with_admin(owner)
+            .call(owner)
+            .unwrap();
+
+        contract
+            .custom_and_generic_proxy()
+            .custom_generic_execute(vec![])
+            .call(owner)
+            .unwrap();
+        contract
+            .custom_and_generic_proxy()
+            .custom_generic_query(SvCustomMsg {})
+            .unwrap();
+    }
+}
diff --git a/examples/contracts/generic_contract/src/cw1.rs b/examples/contracts/generic_contract/src/cw1.rs
new file mode 100644
index 0000000..410bd13
--- /dev/null
+++ b/examples/contracts/generic_contract/src/cw1.rs
@@ -0,0 +1,72 @@
+use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult};
+use cw1::{CanExecuteResp, Cw1};
+use sylvia::contract;
+use sylvia::types::{ExecCtx, QueryCtx};
+
+#[contract(module = crate::contract)]
+#[messages(cw1 as Cw1)]
+#[sv::custom(msg=sylvia::types::SvCustomMsg)]
+impl<InstantiateParam, ExecParam, QueryParam, MigrateParam, RetType, FieldType> Cw1
+    for crate::contract::GenericContract<
+        InstantiateParam,
+        ExecParam,
+        QueryParam,
+        MigrateParam,
+        RetType,
+        FieldType,
+    >
+{
+    type Error = StdError;
+
+    #[msg(exec)]
+    fn execute(&self, _ctx: ExecCtx, _msgs: Vec<CosmosMsg>) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    #[msg(query)]
+    fn can_execute(
+        &self,
+        _ctx: QueryCtx,
+        _sender: String,
+        _msg: CosmosMsg,
+    ) -> StdResult<CanExecuteResp> {
+        Ok(CanExecuteResp::default())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::sv::test_utils::Cw1;
+    use crate::contract::sv::multitest_utils::CodeId;
+    use cosmwasm_std::{CosmosMsg, Empty};
+    use sylvia::{multitest::App, types::SvCustomMsg};
+
+    #[test]
+    fn proxy_methods() {
+        let app = App::<cw_multi_test::BasicApp<SvCustomMsg>>::custom(|_, _, _| {});
+        let code_id = CodeId::<
+            SvCustomMsg,
+            sylvia::types::SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+            sylvia::types::SvCustomMsg,
+            String,
+            _,
+        >::store_code(&app);
+
+        let owner = "owner";
+
+        let contract = code_id
+            .instantiate(SvCustomMsg {})
+            .with_label("GenericContract")
+            .with_admin(owner)
+            .call(owner)
+            .unwrap();
+
+        contract.cw1_proxy().execute(vec![]).call(owner).unwrap();
+        contract
+            .cw1_proxy()
+            .can_execute("sender".to_owned(), CosmosMsg::Custom(Empty {}))
+            .unwrap();
+    }
+}
diff --git a/examples/contracts/generic_contract/src/generic.rs b/examples/contracts/generic_contract/src/generic.rs
new file mode 100644
index 0000000..27176d9
--- /dev/null
+++ b/examples/contracts/generic_contract/src/generic.rs
@@ -0,0 +1,82 @@
+use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult};
+use generic::Generic;
+use sylvia::contract;
+use sylvia::types::{ExecCtx, QueryCtx, SvCustomMsg};
+
+#[contract(module = crate::contract)]
+#[messages(generic as Generic)]
+#[sv::custom(msg=SvCustomMsg)]
+impl<InstantiateParam, ExecParam, QueryParam, MigrateParam, RetType, FieldType>
+    Generic<SvCustomMsg, SvCustomMsg, sylvia::types::SvCustomMsg>
+    for crate::contract::GenericContract<
+        InstantiateParam,
+        ExecParam,
+        QueryParam,
+        MigrateParam,
+        RetType,
+        FieldType,
+    >
+{
+    type Error = StdError;
+
+    #[msg(exec)]
+    fn generic_exec(
+        &self,
+        _ctx: ExecCtx,
+        _msgs: Vec<CosmosMsg<sylvia::types::SvCustomMsg>>,
+    ) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    // Sylvia will fail if single type is used to match against two different generics
+    // It's because we have to map unique generics used as they can be used multiple times.
+    // If for some reason like here one type would be used in place of two generics either full
+    // path or some alias has to be used.
+    #[msg(query)]
+    fn generic_query(
+        &self,
+        _ctx: QueryCtx,
+        _msg: sylvia::types::SvCustomMsg,
+    ) -> StdResult<SvCustomMsg> {
+        Ok(SvCustomMsg {})
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::sv::test_utils::Generic;
+    use crate::contract::sv::multitest_utils::CodeId;
+    use cosmwasm_std::CosmosMsg;
+    use sylvia::multitest::App;
+    use sylvia::types::SvCustomMsg;
+
+    #[test]
+    fn proxy_methods() {
+        let app = App::<cw_multi_test::BasicApp<SvCustomMsg>>::custom(|_, _, _| {});
+        let code_id: CodeId<
+            SvCustomMsg,
+            sylvia::types::SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+            sylvia::types::SvCustomMsg,
+            String,
+            _,
+        > = CodeId::store_code(&app);
+
+        let owner = "owner";
+
+        let contract = code_id
+            .instantiate(SvCustomMsg {})
+            .with_label("GenericContract")
+            .with_admin(owner)
+            .call(owner)
+            .unwrap();
+
+        contract
+            .generic_proxy()
+            .generic_exec(vec![CosmosMsg::Custom(SvCustomMsg {})])
+            .call(owner)
+            .unwrap();
+        contract.generic_proxy().generic_query(SvCustomMsg).unwrap();
+    }
+}
diff --git a/examples/contracts/generic_contract/src/lib.rs b/examples/contracts/generic_contract/src/lib.rs
new file mode 100644
index 0000000..ea8b373
--- /dev/null
+++ b/examples/contracts/generic_contract/src/lib.rs
@@ -0,0 +1,4 @@
+pub mod contract;
+pub mod custom_and_generic;
+pub mod cw1;
+pub mod generic;
diff --git a/examples/contracts/generic_iface_on_contract/.cargo/config b/examples/contracts/generic_iface_on_contract/.cargo/config
new file mode 100644
index 0000000..d8ab80f
--- /dev/null
+++ b/examples/contracts/generic_iface_on_contract/.cargo/config
@@ -0,0 +1,6 @@
+[alias]
+wasm = "build --release --target wasm32-unknown-unknown --lib"
+wasm-debug = "build --target wasm32-unknown-unknown --lib"
+unit-test = "test --lib"
+integration-test = "test --test integration"
+schema = "run --bin schema"
diff --git a/examples/contracts/generic_iface_on_contract/Cargo.toml b/examples/contracts/generic_iface_on_contract/Cargo.toml
new file mode 100644
index 0000000..99ad5aa
--- /dev/null
+++ b/examples/contracts/generic_iface_on_contract/Cargo.toml
@@ -0,0 +1,34 @@
+[package]
+name = "generic_iface_on_contract"
+version = { workspace = true }
+authors = ["Jan Woźniak <jan@confio.gmbh>"]
+edition = { workspace = true }
+description = "Generic interfaces implemented on non generic contract"
+license = "Apache-2.0"
+repository = "https://github.com/CosmWasm/sylvia"
+homepage = "https://cosmwasm.com"
+
+[lib]
+crate-type = ["cdylib", "rlib"]
+
+[features]
+library = []
+tests = ["library", "cw-multi-test", "anyhow"]
+
+[dependencies]
+anyhow = { version = "1.0", optional = true }
+cosmwasm-schema = "1.2"
+cosmwasm-std = { version = "1.3", features = ["staking"] }
+cw-multi-test = { version = "0.16", optional = true }
+cw-storage-plus = "1.0"
+cw-utils = "1.0"
+serde = { version = "1.0", default-features = false, features = ["derive"] }
+sylvia = { path = "../../../sylvia" }
+cw1 = { path = "../../interfaces/cw1" }
+generic = { path = "../../interfaces/generic" }
+custom-and-generic = { path = "../../interfaces/custom-and-generic" }
+
+[dev-dependencies]
+anyhow = "1.0"
+cw-multi-test = "0.16"
+sylvia = { path = "../../../sylvia", features = ["mt"] }
diff --git a/examples/contracts/generic_iface_on_contract/src/bin/schema.rs b/examples/contracts/generic_iface_on_contract/src/bin/schema.rs
new file mode 100644
index 0000000..03983dc
--- /dev/null
+++ b/examples/contracts/generic_iface_on_contract/src/bin/schema.rs
@@ -0,0 +1,14 @@
+use cosmwasm_schema::write_api;
+
+#[cfg(not(tarpaulin_include))]
+fn main() {
+    use generic_iface_on_contract::contract::sv::{
+        ContractExecMsg, ContractQueryMsg, InstantiateMsg,
+    };
+
+    write_api! {
+        instantiate: InstantiateMsg,
+        execute: ContractExecMsg,
+        query: ContractQueryMsg,
+    }
+}
diff --git a/examples/contracts/generic_iface_on_contract/src/contract.rs b/examples/contracts/generic_iface_on_contract/src/contract.rs
new file mode 100644
index 0000000..a8f9280
--- /dev/null
+++ b/examples/contracts/generic_iface_on_contract/src/contract.rs
@@ -0,0 +1,85 @@
+use cosmwasm_std::{Response, StdResult};
+use sylvia::types::{InstantiateCtx, SvCustomMsg};
+use sylvia::{contract, schemars};
+
+#[cfg(not(feature = "library"))]
+use sylvia::entry_points;
+
+pub struct NonGenericContract;
+
+#[cfg_attr(not(feature = "library"), entry_points)]
+#[contract]
+#[messages(generic<SvCustomMsg, sylvia::types::SvCustomMsg, SvCustomMsg> as Generic: custom(msg))]
+#[messages(custom_and_generic<SvCustomMsg, SvCustomMsg, sylvia::types::SvCustomMsg> as CustomAndGeneric)]
+#[messages(cw1 as Cw1: custom(msg))]
+/// Required if interface returns generic `Response`
+#[sv::custom(msg=SvCustomMsg)]
+impl NonGenericContract {
+    pub const fn new() -> Self {
+        Self
+    }
+
+    #[msg(instantiate)]
+    pub fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult<Response<SvCustomMsg>> {
+        Ok(Response::new())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use cosmwasm_std::{CosmosMsg, Empty};
+    use sylvia::{multitest::App, types::SvCustomMsg};
+
+    use super::NonGenericContract;
+    use crate::custom_and_generic::sv::test_utils::CustomAndGeneric;
+    use crate::cw1::sv::test_utils::Cw1;
+    use crate::generic::sv::test_utils::Generic;
+
+    #[test]
+    fn mt_helpers() {
+        let _ = NonGenericContract::new();
+        let app = App::<cw_multi_test::BasicApp<SvCustomMsg>>::custom(|_, _, _| {});
+        let code_id = super::sv::multitest_utils::CodeId::store_code(&app);
+
+        let owner = "owner";
+
+        let contract = code_id
+            .instantiate()
+            .with_label("Cw1Contract")
+            .call(owner)
+            .unwrap();
+
+        // Non custom non generic interface
+        contract
+            .cw1_proxy()
+            .can_execute("sender".to_owned(), CosmosMsg::Custom(Empty {}))
+            .unwrap();
+        contract
+            .cw1_proxy()
+            .execute(vec![CosmosMsg::Custom(Empty {})])
+            .call(owner)
+            .unwrap();
+
+        // Non-Custom generic Interface
+        contract
+            .generic_proxy()
+            .generic_query(SvCustomMsg {})
+            .unwrap();
+        contract
+            .generic_proxy()
+            .generic_exec(vec![CosmosMsg::Custom(SvCustomMsg {})])
+            .call(owner)
+            .unwrap();
+
+        // Custom generic Interface
+        contract
+            .custom_and_generic_proxy()
+            .custom_generic_query(SvCustomMsg {})
+            .unwrap();
+        contract
+            .custom_and_generic_proxy()
+            .custom_generic_execute(vec![CosmosMsg::Custom(SvCustomMsg {})])
+            .call(owner)
+            .unwrap();
+    }
+}
diff --git a/examples/contracts/generic_iface_on_contract/src/custom_and_generic.rs b/examples/contracts/generic_iface_on_contract/src/custom_and_generic.rs
new file mode 100644
index 0000000..90052b8
--- /dev/null
+++ b/examples/contracts/generic_iface_on_contract/src/custom_and_generic.rs
@@ -0,0 +1,31 @@
+use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult};
+use custom_and_generic::CustomAndGeneric;
+use sylvia::contract;
+use sylvia::types::{ExecCtx, QueryCtx, SvCustomMsg};
+
+#[contract(module = crate::contract)]
+#[messages(custom_and_generic as CustomAndGeneric)]
+#[sv::custom(msg=sylvia::types::SvCustomMsg)]
+impl CustomAndGeneric<SvCustomMsg, SvCustomMsg, sylvia::types::SvCustomMsg>
+    for crate::contract::NonGenericContract
+{
+    type Error = StdError;
+
+    #[msg(exec)]
+    fn custom_generic_execute(
+        &self,
+        _ctx: ExecCtx,
+        _msgs: Vec<CosmosMsg<sylvia::types::SvCustomMsg>>,
+    ) -> StdResult<Response<SvCustomMsg>> {
+        Ok(Response::new())
+    }
+
+    #[msg(query)]
+    fn custom_generic_query(
+        &self,
+        _ctx: QueryCtx,
+        _msg: sylvia::types::SvCustomMsg,
+    ) -> StdResult<SvCustomMsg> {
+        Ok(SvCustomMsg {})
+    }
+}
diff --git a/examples/contracts/generic_iface_on_contract/src/cw1.rs b/examples/contracts/generic_iface_on_contract/src/cw1.rs
new file mode 100644
index 0000000..8d45bc0
--- /dev/null
+++ b/examples/contracts/generic_iface_on_contract/src/cw1.rs
@@ -0,0 +1,26 @@
+use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult};
+use cw1::{CanExecuteResp, Cw1};
+use sylvia::contract;
+use sylvia::types::{ExecCtx, QueryCtx};
+
+#[contract(module = crate::contract)]
+#[messages(cw1 as Cw1)]
+#[sv::custom(msg=sylvia::types::SvCustomMsg)]
+impl Cw1 for crate::contract::NonGenericContract {
+    type Error = StdError;
+
+    #[msg(exec)]
+    fn execute(&self, _ctx: ExecCtx, _msgs: Vec<CosmosMsg>) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    #[msg(query)]
+    fn can_execute(
+        &self,
+        _ctx: QueryCtx,
+        _sender: String,
+        _msg: CosmosMsg,
+    ) -> StdResult<CanExecuteResp> {
+        Ok(CanExecuteResp::default())
+    }
+}
diff --git a/examples/contracts/generic_iface_on_contract/src/generic.rs b/examples/contracts/generic_iface_on_contract/src/generic.rs
new file mode 100644
index 0000000..d13827a
--- /dev/null
+++ b/examples/contracts/generic_iface_on_contract/src/generic.rs
@@ -0,0 +1,35 @@
+use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult};
+use generic::Generic;
+use sylvia::contract;
+use sylvia::types::{ExecCtx, QueryCtx, SvCustomMsg};
+
+#[contract(module = crate::contract)]
+#[messages(generic as Generic)]
+#[sv::custom(msg = SvCustomMsg)]
+impl Generic<SvCustomMsg, SvCustomMsg, sylvia::types::SvCustomMsg>
+    for crate::contract::NonGenericContract
+{
+    type Error = StdError;
+
+    #[msg(exec)]
+    fn generic_exec(
+        &self,
+        _ctx: ExecCtx,
+        _msgs: Vec<CosmosMsg<sylvia::types::SvCustomMsg>>,
+    ) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    // Sylvia will fail if single type is used to match against two different generics
+    // It's because we have to map unique generics used as they can be used multiple times.
+    // If for some reason like here one type would be used in place of two generics either full
+    // path or some alias has to be used.
+    #[msg(query)]
+    fn generic_query(
+        &self,
+        _ctx: QueryCtx,
+        _msg: sylvia::types::SvCustomMsg,
+    ) -> StdResult<SvCustomMsg> {
+        Ok(SvCustomMsg {})
+    }
+}
diff --git a/examples/contracts/generic_iface_on_contract/src/lib.rs b/examples/contracts/generic_iface_on_contract/src/lib.rs
new file mode 100644
index 0000000..ea8b373
--- /dev/null
+++ b/examples/contracts/generic_iface_on_contract/src/lib.rs
@@ -0,0 +1,4 @@
+pub mod contract;
+pub mod custom_and_generic;
+pub mod cw1;
+pub mod generic;
diff --git a/examples/interfaces/custom-and-generic/Cargo.toml b/examples/interfaces/custom-and-generic/Cargo.toml
new file mode 100644
index 0000000..c1f38f9
--- /dev/null
+++ b/examples/interfaces/custom-and-generic/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "custom-and-generic"
+version = { workspace = true }
+authors = ["Jan Woźniak <jan@confio.gmbh>"]
+edition = { workspace = true }
+description = "Interface with custom msg and generic support."
+license = "Apache-2.0"
+repository = "https://github.com/CosmWasm/sylvia"
+homepage = "https://cosmwasm.com"
+
+[features]
+mt = ["sylvia/mt"]
+
+[dependencies]
+cosmwasm-std = { version = "1.3", features = ["staking"] }
+cosmwasm-schema = "1.2"
+serde = { version = "1.0", default-features = false, features = ["derive"] }
+sylvia = { path = "../../../sylvia" }
+
+[dev-dependencies]
+anyhow = "1.0"
+cw-multi-test = "0.16"
diff --git a/examples/interfaces/custom-and-generic/src/lib.rs b/examples/interfaces/custom-and-generic/src/lib.rs
new file mode 100644
index 0000000..f56d450
--- /dev/null
+++ b/examples/interfaces/custom-and-generic/src/lib.rs
@@ -0,0 +1,69 @@
+use cosmwasm_std::{CosmosMsg, CustomMsg, Response, StdError};
+
+use serde::de::DeserializeOwned;
+use serde::Deserialize;
+use sylvia::types::{ExecCtx, QueryCtx};
+use sylvia::{interface, schemars};
+
+#[interface]
+#[sv::custom(msg=RetType)]
+pub trait CustomAndGeneric<ExecParam, QueryParam, RetType>
+where
+    for<'msg_de> ExecParam: CustomMsg + Deserialize<'msg_de>,
+    QueryParam: sylvia::types::CustomMsg,
+    RetType: CustomMsg + DeserializeOwned,
+{
+    type Error: From<StdError>;
+
+    #[msg(exec)]
+    fn custom_generic_execute(
+        &self,
+        ctx: ExecCtx,
+        msgs: Vec<CosmosMsg<ExecParam>>,
+    ) -> Result<Response<RetType>, Self::Error>;
+
+    #[msg(query)]
+    fn custom_generic_query(
+        &self,
+        ctx: QueryCtx,
+        param: QueryParam,
+    ) -> Result<RetType, Self::Error>;
+}
+
+#[cfg(test)]
+mod tests {
+    use cosmwasm_std::testing::mock_dependencies;
+    use cosmwasm_std::{Addr, CosmosMsg, Empty, QuerierWrapper};
+    use sylvia::types::{InterfaceApi, SvCustomMsg};
+
+    use crate::sv::Querier;
+
+    #[test]
+    fn construct_messages() {
+        let contract = Addr::unchecked("contract");
+
+        // Direct message construction
+        let _ = super::sv::QueryMsg::<_, Empty>::custom_generic_query(SvCustomMsg {});
+        let _ = super::sv::ExecMsg::custom_generic_execute(vec![CosmosMsg::Custom(SvCustomMsg {})]);
+        let _ = super::sv::ExecMsg::custom_generic_execute(vec![CosmosMsg::Custom(SvCustomMsg {})]);
+
+        // Querier
+        let deps = mock_dependencies();
+        let querier_wrapper: QuerierWrapper = QuerierWrapper::new(&deps.querier);
+
+        let querier = super::sv::BoundQuerier::borrowed(&contract, &querier_wrapper);
+        let _: Result<SvCustomMsg, _> =
+            super::sv::Querier::custom_generic_query(&querier, SvCustomMsg {});
+        let _: Result<SvCustomMsg, _> = querier.custom_generic_query(SvCustomMsg {});
+
+        // Construct messages with Interface extension
+        let _ =
+            <super::sv::Api<SvCustomMsg, _, SvCustomMsg> as InterfaceApi>::Query::custom_generic_query(
+                SvCustomMsg {},
+            );
+        let _=
+            <super::sv::Api<_, SvCustomMsg, cosmwasm_std::Empty> as InterfaceApi>::Exec::custom_generic_execute(
+            vec![ CosmosMsg::Custom(SvCustomMsg{}),
+            ]);
+    }
+}
diff --git a/examples/interfaces/cw1/src/lib.rs b/examples/interfaces/cw1/src/lib.rs
index 3e12c2e..46d204b 100644
--- a/examples/interfaces/cw1/src/lib.rs
+++ b/examples/interfaces/cw1/src/lib.rs
@@ -36,11 +36,9 @@ pub trait Cw1 {
 mod tests {
     use cosmwasm_std::{coins, from_binary, from_slice, to_binary, BankMsg};
 
-    use super::*;
-
     #[test]
     fn execute() {
-        let original = ExecMsg::Execute {
+        let original = super::sv::ExecMsg::Execute {
             msgs: vec![BankMsg::Send {
                 to_address: "receiver".to_owned(),
                 amount: coins(10, "atom"),
@@ -57,12 +55,12 @@ mod tests {
     #[test]
     fn execute_from_slice() {
         let deserialized = from_slice(br#"{"execute": { "msgs": [] }}"#).unwrap();
-        assert_eq!(ExecMsg::Execute { msgs: vec![] }, deserialized);
+        assert_eq!(super::sv::ExecMsg::Execute { msgs: vec![] }, deserialized);
     }
 
     #[test]
     fn query() {
-        let original = QueryMsg::CanExecute {
+        let original = super::sv::QueryMsg::CanExecute {
             sender: "sender".to_owned(),
             msg: BankMsg::Send {
                 to_address: "receiver".to_owned(),
@@ -99,7 +97,7 @@ mod tests {
         )
         .unwrap();
         assert_eq!(
-            QueryMsg::CanExecute {
+            super::sv::QueryMsg::CanExecute {
                 sender: "address".to_owned(),
                 msg: BankMsg::Send {
                     to_address: "receiver".to_owned(),
diff --git a/examples/interfaces/cw4/src/lib.rs b/examples/interfaces/cw4/src/lib.rs
index cc73074..0bb2551 100644
--- a/examples/interfaces/cw4/src/lib.rs
+++ b/examples/interfaces/cw4/src/lib.rs
@@ -39,7 +39,7 @@ pub trait Cw4 {
 mod tests {
     use cosmwasm_std::{from_binary, from_slice, to_binary};
 
-    use super::*;
+    use super::sv::*;
 
     #[test]
     fn execute() {
@@ -84,7 +84,7 @@ mod tests {
     #[test]
     fn exec_msgs() {
         assert_eq!(
-            Cw4ExecMsg::messages(),
+            execute_messages(),
             ["add_hook", "remove_hook", "update_admin", "update_members"]
         );
     }
@@ -92,7 +92,7 @@ mod tests {
     #[test]
     fn query_msgs() {
         assert_eq!(
-            Cw4QueryMsg::messages(),
+            query_messages(),
             ["admin", "hooks", "list_members", "member", "total_weight"]
         );
     }
diff --git a/examples/interfaces/generic/Cargo.toml b/examples/interfaces/generic/Cargo.toml
new file mode 100644
index 0000000..c5832b1
--- /dev/null
+++ b/examples/interfaces/generic/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "generic"
+version = { workspace = true }
+authors = ["Jan Woźniak <jan@confio.gmbh>"]
+edition = { workspace = true }
+description = "Interface with generic support."
+license = "Apache-2.0"
+repository = "https://github.com/CosmWasm/sylvia"
+homepage = "https://cosmwasm.com"
+
+[features]
+mt = ["sylvia/mt"]
+
+[dependencies]
+cosmwasm-std = { version = "1.3", features = ["staking"] }
+cosmwasm-schema = "1.2"
+serde = { version = "1.0", default-features = false, features = ["derive"] }
+sylvia = { path = "../../../sylvia" }
+
+[dev-dependencies]
+anyhow = "1.0"
+cw-multi-test = "0.16"
diff --git a/examples/interfaces/generic/src/lib.rs b/examples/interfaces/generic/src/lib.rs
new file mode 100644
index 0000000..f680153
--- /dev/null
+++ b/examples/interfaces/generic/src/lib.rs
@@ -0,0 +1,60 @@
+use cosmwasm_std::{CosmosMsg, CustomMsg, Response, StdError};
+
+use serde::{de::DeserializeOwned, Deserialize};
+use sylvia::types::{ExecCtx, QueryCtx};
+use sylvia::{interface, schemars};
+
+#[interface]
+pub trait Generic<ExecParam, QueryParam, RetType>
+where
+    for<'msg_de> ExecParam: CustomMsg + Deserialize<'msg_de>,
+    QueryParam: sylvia::types::CustomMsg,
+    RetType: CustomMsg + DeserializeOwned,
+{
+    type Error: From<StdError>;
+
+    #[msg(exec)]
+    fn generic_exec(
+        &self,
+        ctx: ExecCtx,
+        msgs: Vec<CosmosMsg<ExecParam>>,
+    ) -> Result<Response, Self::Error>;
+
+    #[msg(query)]
+    fn generic_query(&self, ctx: QueryCtx, param: QueryParam) -> Result<RetType, Self::Error>;
+}
+
+#[cfg(test)]
+mod tests {
+    use cosmwasm_std::{testing::mock_dependencies, Addr, CosmosMsg, Empty, QuerierWrapper};
+    use sylvia::types::{InterfaceApi, SvCustomMsg};
+
+    use crate::sv::Querier;
+
+    #[test]
+    fn construct_messages() {
+        let contract = Addr::unchecked("contract");
+
+        // Direct message construction
+        let _ = super::sv::QueryMsg::<_, Empty>::generic_query(SvCustomMsg {});
+        let _ = super::sv::ExecMsg::generic_exec(vec![CosmosMsg::Custom(SvCustomMsg {})]);
+        let _ = super::sv::ExecMsg::generic_exec(vec![CosmosMsg::Custom(SvCustomMsg {})]);
+
+        // Querier
+        let deps = mock_dependencies();
+        let querier_wrapper: QuerierWrapper = QuerierWrapper::new(&deps.querier);
+
+        let querier = super::sv::BoundQuerier::borrowed(&contract, &querier_wrapper);
+        let _: Result<SvCustomMsg, _> = super::sv::Querier::generic_query(&querier, SvCustomMsg {});
+        let _: Result<SvCustomMsg, _> = querier.generic_query(SvCustomMsg {});
+
+        // Construct messages with Interface extension
+        let _ = <super::sv::Api<SvCustomMsg, _, SvCustomMsg> as InterfaceApi>::Query::generic_query(
+            SvCustomMsg {},
+        );
+        let _=
+            <super::sv::Api<_, SvCustomMsg, cosmwasm_std::Empty> as InterfaceApi>::Exec::generic_exec(vec![
+                CosmosMsg::Custom(SvCustomMsg{}),
+            ]);
+    }
+}
diff --git a/examples/interfaces/whitelist/Cargo.toml b/examples/interfaces/whitelist/Cargo.toml
new file mode 100644
index 0000000..5ec641e
--- /dev/null
+++ b/examples/interfaces/whitelist/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "whitelist"
+version = { workspace = true }
+authors = ["Jan Woźniak <jan@confio.gmbh>"]
+edition = { workspace = true }
+description = "Coverage of whitelist sylvia message generation."
+license = "Apache-2.0"
+repository = "https://github.com/CosmWasm/sylvia"
+homepage = "https://cosmwasm.com"
+
+[features]
+mt = ["sylvia/mt"]
+
+[dependencies]
+cosmwasm-std = { version = "1.3", features = ["staking"] }
+cosmwasm-schema = "1.2"
+serde = { version = "1.0", default-features = false, features = ["derive"] }
+sylvia = { path = "../../../sylvia" }
+
+[dev-dependencies]
+anyhow = "1.0"
+cw-multi-test = "0.16"
diff --git a/examples/interfaces/whitelist/src/lib.rs b/examples/interfaces/whitelist/src/lib.rs
new file mode 100644
index 0000000..34daafe
--- /dev/null
+++ b/examples/interfaces/whitelist/src/lib.rs
@@ -0,0 +1,76 @@
+use cosmwasm_std::{Response, StdResult};
+use responses::AdminListResponse;
+use sylvia::types::{ExecCtx, QueryCtx};
+use sylvia::{interface, schemars};
+
+pub mod responses;
+
+#[interface]
+pub trait Whitelist {
+    type Error: From<cosmwasm_std::StdError>;
+
+    #[msg(exec)]
+    fn freeze(&self, ctx: ExecCtx) -> Result<Response, Self::Error>;
+
+    #[msg(exec)]
+    fn update_admins(&self, ctx: ExecCtx, admins: Vec<String>) -> Result<Response, Self::Error>;
+
+    #[msg(query)]
+    fn admin_list(&self, ctx: QueryCtx) -> StdResult<AdminListResponse>;
+}
+
+#[cfg(test)]
+mod tests {
+    use cosmwasm_std::{from_binary, from_slice, to_binary};
+
+    use super::*;
+
+    #[test]
+    fn exec_from_binary() {
+        let original = sv::ExecMsg::Freeze {};
+
+        let serialized = to_binary(&original).unwrap();
+        let deserialized = from_binary(&serialized).unwrap();
+
+        assert_eq!(original, deserialized);
+
+        let original = sv::ExecMsg::UpdateAdmins {
+            admins: vec!["new_admin".to_owned()],
+        };
+
+        let serialized = to_binary(&original).unwrap();
+        let deserialized = from_binary(&serialized).unwrap();
+
+        assert_eq!(original, deserialized);
+    }
+
+    #[test]
+    fn exec_from_slice() {
+        let deserialized = from_slice(br#"{"freeze": { }}"#).unwrap();
+        assert_eq!(sv::ExecMsg::Freeze {}, deserialized);
+
+        let deserialized =
+            from_slice(br#"{"update_admins": { "admins": ["new_admin"] }}"#).unwrap();
+        assert_eq!(
+            sv::ExecMsg::UpdateAdmins {
+                admins: vec!["new_admin".to_owned()]
+            },
+            deserialized
+        );
+    }
+
+    #[test]
+    fn query_from_binary() {
+        let original = sv::QueryMsg::AdminList {};
+        let serialized = to_binary(&original).unwrap();
+        let deserialized = from_binary(&serialized).unwrap();
+
+        assert_eq!(original, deserialized);
+    }
+
+    #[test]
+    fn query_from_slice() {
+        let deserialized = from_slice(br#"{"admin_list": {}}"#).unwrap();
+        assert_eq!(sv::QueryMsg::AdminList {}, deserialized);
+    }
+}
diff --git a/examples/contracts/cw1-whitelist/src/responses.rs b/examples/interfaces/whitelist/src/responses.rs
similarity index 100%
rename from examples/contracts/cw1-whitelist/src/responses.rs
rename to examples/interfaces/whitelist/src/responses.rs
diff --git a/sylvia-derive/src/check_generics.rs b/sylvia-derive/src/check_generics.rs
index fbd2188..c31ca40 100644
--- a/sylvia-derive/src/check_generics.rs
+++ b/sylvia-derive/src/check_generics.rs
@@ -1,25 +1,57 @@
 use syn::visit::Visit;
-use syn::GenericParam;
+use syn::{parse_quote, GenericArgument, GenericParam, Type};
 
-pub struct CheckGenerics<'g> {
-    generics: &'g [&'g GenericParam],
-    used: Vec<&'g GenericParam>,
+pub trait GetPath {
+    fn get_path(&self) -> Option<syn::Path>;
 }
 
-impl<'g> CheckGenerics<'g> {
-    pub fn new(generics: &'g [&'g GenericParam]) -> Self {
+impl GetPath for GenericParam {
+    fn get_path(&self) -> Option<syn::Path> {
+        match self {
+            GenericParam::Type(ty) => {
+                let ident = &ty.ident;
+                Some(parse_quote! { #ident })
+            }
+            _ => None,
+        }
+    }
+}
+
+impl GetPath for GenericArgument {
+    fn get_path(&self) -> Option<syn::Path> {
+        match self {
+            GenericArgument::Type(Type::Path(path)) => {
+                let path = &path.path;
+                Some(parse_quote! { #path })
+            }
+            _ => None,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub struct CheckGenerics<'g, Generic> {
+    generics: &'g [&'g Generic],
+    used: Vec<&'g Generic>,
+}
+
+impl<'g, Generic> CheckGenerics<'g, Generic>
+where
+    Generic: GetPath + PartialEq,
+{
+    pub fn new(generics: &'g [&'g Generic]) -> Self {
         Self {
             generics,
             used: vec![],
         }
     }
 
-    pub fn used(self) -> Vec<&'g GenericParam> {
+    pub fn used(self) -> Vec<&'g Generic> {
         self.used
     }
 
     /// Returns split between used and unused generics
-    pub fn used_unused(self) -> (Vec<&'g GenericParam>, Vec<&'g GenericParam>) {
+    pub fn used_unused(self) -> (Vec<&'g Generic>, Vec<&'g Generic>) {
         let unused = self
             .generics
             .iter()
@@ -31,17 +63,18 @@ impl<'g> CheckGenerics<'g> {
     }
 }
 
-impl<'ast, 'g> Visit<'ast> for CheckGenerics<'g> {
+impl<'ast, 'g, Generic> Visit<'ast> for CheckGenerics<'g, Generic>
+where
+    Generic: GetPath + PartialEq,
+{
     fn visit_path(&mut self, p: &'ast syn::Path) {
-        if let Some(p) = p.get_ident() {
-            if let Some(gen) = self
-                .generics
-                .iter()
-                .find(|gen| matches!(gen, GenericParam::Type(ty) if ty.ident == *p))
-            {
-                if !self.used.contains(gen) {
-                    self.used.push(gen);
-                }
+        if let Some(gen) = self
+            .generics
+            .iter()
+            .find(|gen| gen.get_path().as_ref() == Some(p))
+        {
+            if !self.used.contains(gen) {
+                self.used.push(gen);
             }
         }
 
diff --git a/sylvia-derive/src/input.rs b/sylvia-derive/src/input.rs
index 6fe658c..839702c 100644
--- a/sylvia-derive/src/input.rs
+++ b/sylvia-derive/src/input.rs
@@ -1,13 +1,20 @@
 use proc_macro2::{Span, TokenStream};
 use proc_macro_error::emit_error;
-use quote::quote;
+use quote::{quote, ToTokens};
 use syn::parse::{Parse, Parser};
 use syn::spanned::Spanned;
-use syn::{parse_quote, GenericParam, Ident, ItemImpl, ItemTrait, TraitItem, Type};
+use syn::{
+    parse_quote, GenericArgument, GenericParam, Ident, ItemImpl, ItemTrait, PathArguments,
+    TraitItem, Type,
+};
 
+use crate::check_generics::GetPath;
 use crate::crate_module;
 use crate::interfaces::Interfaces;
-use crate::message::{ContractEnumMessage, EnumMessage, GlueMessage, MsgVariants, StructMessage};
+use crate::message::{
+    ContractApi, ContractEnumMessage, EnumMessage, GlueMessage, InterfaceApi, MsgVariants,
+    StructMessage,
+};
 use crate::multitest::{MultitestHelpers, TraitMultitestHelpers};
 use crate::parser::{ContractArgs, ContractErrorAttr, Custom, MsgType, OverrideEntryPoints};
 use crate::remote::Remote;
@@ -62,18 +69,32 @@ impl<'a> TraitInput<'a> {
         let messages = self.emit_messages();
         let multitest_helpers = self.emit_helpers();
         let remote = Remote::new(&Interfaces::default()).emit();
-        let querier = MsgVariants::new(self.item.as_variants(), &self.generics).emit_querier();
+
+        let querier = MsgVariants::new(
+            self.item.as_variants(),
+            MsgType::Query,
+            &self.generics,
+            &self.item.generics.where_clause,
+        )
+        .emit_querier();
+
+        let interface_messages = InterfaceApi::new(self.item, &self.generics).emit();
 
         #[cfg(not(tarpaulin_include))]
         {
             quote! {
-                #messages
+                pub mod sv {
+                    use super::*;
+                    #messages
 
-                #multitest_helpers
+                    #multitest_helpers
+
+                    #remote
 
-                #remote
+                    #querier
 
-                #querier
+                    #interface_messages
+                }
             }
         }
     }
@@ -144,69 +165,70 @@ impl<'a> ImplInput<'a> {
 
     pub fn process(&self) -> TokenStream {
         let is_trait = self.item.trait_.is_some();
-        let multitest_helpers = if cfg!(feature = "mt") {
-            MultitestHelpers::new(
-                self.item,
-                is_trait,
-                &self.error,
-                &self.generics,
-                &self.custom,
-                &self.override_entry_points,
-                &self.interfaces,
-            )
-            .emit()
-        } else {
-            quote! {}
-        };
-
-        let interfaces = Interfaces::new(self.item);
-        let variants = MsgVariants::new(self.item.as_variants(), &self.generics);
 
         match is_trait {
-            true => self.process_interface(&interfaces, variants, multitest_helpers),
-            false => self.process_contract(&interfaces, variants, multitest_helpers),
+            true => self.process_interface(),
+            false => self.process_contract(),
         }
     }
 
-    fn process_interface(
-        &self,
-        interfaces: &Interfaces,
-        variants: MsgVariants<'a>,
-        multitest_helpers: TokenStream,
-    ) -> TokenStream {
-        let querier_bound_for_impl = self.emit_querier_for_bound_impl(interfaces, variants);
+    fn process_interface(&self) -> TokenStream {
+        let interface_generics = self.extract_generic_argument();
+        let multitest_helpers = self.emit_multitest_helpers(&interface_generics);
+        let querier_bound_for_impl = self.emit_querier_for_bound_impl();
 
         #[cfg(not(tarpaulin_include))]
         quote! {
-            #multitest_helpers
+            pub mod sv {
+                use super::*;
 
-            #querier_bound_for_impl
+                #multitest_helpers
+
+                #querier_bound_for_impl
+            }
         }
     }
 
-    fn process_contract(
-        &self,
-        interfaces: &Interfaces,
-        variants: MsgVariants<'a>,
-        multitest_helpers: TokenStream,
-    ) -> TokenStream {
+    fn process_contract(&self) -> TokenStream {
+        let Self {
+            item,
+            generics,
+            custom,
+            ..
+        } = self;
+        let multitest_helpers = self.emit_multitest_helpers(generics);
+        let where_clause = &item.generics.where_clause;
+
+        let querier = MsgVariants::new(
+            self.item.as_variants(),
+            MsgType::Query,
+            generics,
+            where_clause,
+        )
+        .emit_querier();
         let messages = self.emit_messages();
-        let remote = Remote::new(interfaces).emit();
-        let querier = variants.emit_querier();
-        let querier_from_impl = interfaces.emit_querier_from_impl();
+        let remote = Remote::new(&self.interfaces).emit();
+        let querier_from_impl = self.interfaces.emit_querier_from_impl();
+        let contract_api = ContractApi::new(item, generics, custom).emit();
 
         #[cfg(not(tarpaulin_include))]
         {
             quote! {
-                #messages
+                pub mod sv {
+                    use super::*;
 
-                #multitest_helpers
+                    #messages
+
+                    #multitest_helpers
 
-                #remote
+                    #remote
 
-                #querier
+                    #querier
 
-                #(#querier_from_impl)*
+                    #(#querier_from_impl)*
+
+                    #contract_api
+                }
             }
         }
     }
@@ -214,12 +236,10 @@ impl<'a> ImplInput<'a> {
     fn emit_messages(&self) -> TokenStream {
         let instantiate = self.emit_struct_msg(MsgType::Instantiate);
         let migrate = self.emit_struct_msg(MsgType::Migrate);
-        let exec_impl =
-            self.emit_enum_msg(&Ident::new("ExecMsg", Span::mixed_site()), MsgType::Exec);
-        let query_impl =
-            self.emit_enum_msg(&Ident::new("QueryMsg", Span::mixed_site()), MsgType::Query);
-        let exec = self.emit_glue_msg(&Ident::new("ExecMsg", Span::mixed_site()), MsgType::Exec);
-        let query = self.emit_glue_msg(&Ident::new("QueryMsg", Span::mixed_site()), MsgType::Query);
+        let exec_impl = self.emit_enum_msg(MsgType::Exec);
+        let query_impl = self.emit_enum_msg(MsgType::Query);
+        let exec = self.emit_glue_msg(MsgType::Exec);
+        let query = self.emit_glue_msg(MsgType::Query);
 
         #[cfg(not(tarpaulin_include))]
         {
@@ -244,41 +264,83 @@ impl<'a> ImplInput<'a> {
             .map_or(quote! {}, |msg| msg.emit())
     }
 
-    fn emit_enum_msg(&self, name: &Ident, msg_ty: MsgType) -> TokenStream {
-        ContractEnumMessage::new(
-            name,
-            self.item,
-            msg_ty,
-            &self.generics,
-            &self.error,
-            &self.custom,
-        )
-        .emit()
+    fn emit_enum_msg(&self, msg_ty: MsgType) -> TokenStream {
+        ContractEnumMessage::new(self.item, msg_ty, &self.generics, &self.error, &self.custom)
+            .emit()
     }
 
-    fn emit_glue_msg(&self, name: &Ident, msg_ty: MsgType) -> TokenStream {
+    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(
-            name,
             self.item,
             msg_ty,
             &self.error,
             &self.custom,
             &self.interfaces,
+            variants,
         )
         .emit()
     }
 
-    fn emit_querier_for_bound_impl(
-        &self,
-        interfaces: &Interfaces,
-        variants: MsgVariants<'a>,
-    ) -> TokenStream {
-        let trait_module = interfaces
-            .interfaces()
-            .first()
+    /// This method should only be called for trait impl block
+    fn extract_generic_argument(&self) -> Vec<&GenericArgument> {
+        let interface_generics = &self.item.trait_.as_ref();
+        let args = match interface_generics {
+            Some((_, path, _)) => path.segments.last().map(|segment| &segment.arguments),
+            None => None,
+        };
+
+        match args {
+            Some(PathArguments::AngleBracketed(args)) => {
+                args.args.pairs().map(|pair| *pair.value()).collect()
+            }
+            _ => vec![],
+        }
+    }
+
+    fn emit_querier_for_bound_impl(&self) -> TokenStream {
+        let trait_module = self
+            .interfaces
+            .get_only_interface()
             .map(|interface| &interface.module);
         let contract_module = self.attributes.module.as_ref();
+        let generics = self.extract_generic_argument();
+
+        let variants = MsgVariants::new(self.item.as_variants(), MsgType::Query, &generics, &None);
 
         variants.emit_querier_for_bound_impl(trait_module, contract_module)
     }
+
+    fn emit_multitest_helpers<Generic>(&self, generics: &[&Generic]) -> TokenStream
+    where
+        Generic: ToTokens + PartialEq + GetPath,
+    {
+        let Self {
+            item,
+            error,
+            custom,
+            override_entry_points,
+            interfaces,
+            ..
+        } = self;
+
+        let is_trait = self.item.trait_.is_some();
+
+        if cfg!(feature = "mt") {
+            MultitestHelpers::new(
+                item,
+                is_trait,
+                error,
+                generics,
+                custom,
+                override_entry_points,
+                interfaces,
+            )
+            .emit()
+        } else {
+            quote! {}
+        }
+    }
 }
diff --git a/sylvia-derive/src/interfaces.rs b/sylvia-derive/src/interfaces.rs
index 94f0d57..fbfe971 100644
--- a/sylvia-derive/src/interfaces.rs
+++ b/sylvia-derive/src/interfaces.rs
@@ -15,13 +15,6 @@ pub struct Interfaces {
 }
 
 impl Interfaces {
-    fn merge_module_with_name(message_attr: &ContractMessageAttr, name: &syn::Ident) -> syn::Ident {
-        // ContractMessageAttr will fail to parse empty `#[messsages()]` attribute so we can safely unwrap here
-        let syn::PathSegment { ident, .. } = &message_attr.module.segments.last().unwrap();
-        let module_name = ident.to_string().to_case(Case::UpperCamel);
-        syn::Ident::new(&format!("{}{}", module_name, name), name.span())
-    }
-
     pub fn new(source: &ItemImpl) -> Self {
         let interfaces: Vec<_> = source
             .attrs
@@ -53,7 +46,7 @@ impl Interfaces {
         self.as_modules()
             .map(|module| {
                 quote! {
-                    impl<'a, C: #sylvia ::cw_std::CustomQuery> From<&'a BoundQuerier<'a, C>> for #module ::BoundQuerier<'a, C> {
+                    impl<'a, C: #sylvia ::cw_std::CustomQuery> From<&'a BoundQuerier<'a, C>> for #module ::sv::BoundQuerier<'a, C> {
                         fn from(querier: &'a BoundQuerier<'a, C>) -> Self {
                             Self::borrowed(querier.contract(),  querier.querier())
                         }
@@ -75,64 +68,70 @@ impl Interfaces {
                 );
 
                 quote! {
-                    pub fn #method_name (&self) -> #module ::trait_utils:: #proxy_name <'app, #mt_app> {
-                        #module ::trait_utils:: #proxy_name ::new(self.contract_addr.clone(), self.app)
+                    pub fn #method_name (&self) -> #module ::sv::trait_utils:: #proxy_name <'app, #mt_app> {
+                        #module ::sv::trait_utils:: #proxy_name ::new(self.contract_addr.clone(), self.app)
                     }
                 }
             })
             .collect()
     }
 
-    pub fn emit_glue_message_variants(
-        &self,
-        msg_ty: &MsgType,
-        msg_name: &Ident,
-    ) -> Vec<TokenStream> {
+    pub fn emit_glue_message_variants(&self, msg_ty: &MsgType) -> Vec<TokenStream> {
+        let sylvia = crate_module();
+
         self.interfaces
             .iter()
             .map(|interface| {
                 let ContractMessageAttr {
                     module,
-                    exec_generic_params,
-                    query_generic_params,
                     variant,
+                    generics,
                     ..
                 } = interface;
-
-                let generics = match msg_ty {
-                    MsgType::Exec => exec_generic_params.as_slice(),
-                    MsgType::Query => query_generic_params.as_slice(),
-                    _ => &[],
+                let generics = if !generics.is_empty() {
+                    quote! { < #generics > }
+                } else {
+                    quote! {}
                 };
 
-                let enum_name = Self::merge_module_with_name(interface, msg_name);
-                quote! { #variant(#module :: #enum_name<#(#generics,)*>) }
+                let interface_enum =
+                    quote! { <#module ::sv::Api #generics as #sylvia ::types::InterfaceApi> };
+                if msg_ty == &MsgType::Query {
+                    quote! { #variant ( #interface_enum :: Query) }
+                } else {
+                    quote! { #variant ( #interface_enum :: Exec)}
+                }
             })
             .collect()
     }
 
-    pub fn emit_messages_call(&self, msg_name: &Ident) -> Vec<TokenStream> {
+    pub fn emit_messages_call(&self, msg_ty: &MsgType) -> Vec<TokenStream> {
         self.interfaces
             .iter()
             .map(|interface| {
-                let enum_name = Self::merge_module_with_name(interface, msg_name);
-                let module = &interface.module;
-                quote! { &#module :: #enum_name :: messages()}
+                let ContractMessageAttr { module, .. } = interface;
+
+                let ep_name = msg_ty.emit_ep_name();
+                let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), module.span());
+                quote! {
+                    &#module ::sv:: #messages_fn_name()
+                }
             })
             .collect()
     }
 
-    pub fn emit_deserialization_attempts(&self, msg_name: &Ident) -> Vec<TokenStream> {
+    pub fn emit_deserialization_attempts(&self, msg_ty: &MsgType) -> Vec<TokenStream> {
         self.interfaces
             .iter()
             .map(|interface| {
                 let ContractMessageAttr {
                     module, variant, ..
                 } = interface;
-                let enum_name = Self::merge_module_with_name(interface, msg_name);
+                let ep_name = msg_ty.emit_ep_name();
+                let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), module.span());
 
                 quote! {
-                    let msgs = &#module :: #enum_name ::messages();
+                    let msgs = &#module ::sv:: #messages_fn_name();
                     if msgs.into_iter().any(|msg| msg == &recv_msg_name) {
                         match val.deserialize_into() {
                             Ok(msg) => return Ok(Self:: #variant (msg)),
@@ -144,13 +143,26 @@ impl Interfaces {
             .collect()
     }
 
-    pub fn emit_response_schemas_calls(&self, msg_name: &Ident) -> Vec<TokenStream> {
+    pub fn emit_response_schemas_calls(&self, msg_ty: &MsgType) -> Vec<TokenStream> {
+        let sylvia = crate_module();
+
         self.interfaces
             .iter()
             .map(|interface| {
-                let enum_name = Self::merge_module_with_name(interface, msg_name);
-                let module = &interface.module;
-                quote! { #module :: #enum_name :: response_schemas_impl()}
+                let ContractMessageAttr {
+                    module, generics, ..
+                } = interface;
+
+                let generics = if !generics.is_empty() {
+                    quote! { < #generics > }
+                } else {
+                    quote! {}
+                };
+
+                let type_name = msg_ty.as_accessor_name(false);
+                quote! {
+                    <#module ::sv::Api #generics as #sylvia ::types::InterfaceApi> :: #type_name :: response_schemas_impl()
+                }
             })
             .collect()
     }
@@ -158,4 +170,23 @@ impl Interfaces {
     pub fn as_modules(&self) -> impl Iterator<Item = &Path> {
         self.interfaces.iter().map(|interface| &interface.module)
     }
+
+    pub fn get_only_interface(&self) -> Option<&ContractMessageAttr> {
+        let interfaces = &self.interfaces;
+        match interfaces.len() {
+            0 => None,
+            1 => Some(&interfaces[0]),
+            _ => {
+                let first = &interfaces[0];
+                for redefined in &interfaces[1..] {
+                    emit_error!(
+                      redefined.module, "The attribute `messages` is redefined";
+                      note = first.module.span() => "Previous definition of the attribute `messsages`";
+                      note = "Only one `messages` attribute can exist on an interface implementation on contract"
+                    );
+                }
+                None
+            }
+        }
+    }
 }
diff --git a/sylvia-derive/src/lib.rs b/sylvia-derive/src/lib.rs
index a002c04..a5c6ca8 100644
--- a/sylvia-derive/src/lib.rs
+++ b/sylvia-derive/src/lib.rs
@@ -258,9 +258,10 @@ pub fn entry_points(attr: TokenStream, item: TokenStream) -> TokenStream {
 
 #[cfg(not(tarpaulin_include))]
 fn entry_points_impl(attr: TokenStream2, item: TokenStream2) -> TokenStream2 {
-    fn inner(_attr: TokenStream2, item: TokenStream2) -> syn::Result<TokenStream2> {
+    fn inner(attr: TokenStream2, item: TokenStream2) -> syn::Result<TokenStream2> {
+        let attrs: parser::EntryPointArgs = parse2(attr)?;
         let input: ItemImpl = parse2(item)?;
-        let expanded = EntryPoints::new(&input).emit();
+        let expanded = EntryPoints::new(&input, attrs).emit();
 
         Ok(quote! {
             #input
diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs
index 3bb14c5..9a67a37 100644
--- a/sylvia-derive/src/message.rs
+++ b/sylvia-derive/src/message.rs
@@ -1,24 +1,27 @@
-use crate::check_generics::CheckGenerics;
+use crate::check_generics::{CheckGenerics, GetPath};
 use crate::crate_module;
 use crate::interfaces::Interfaces;
 use crate::parser::{
     parse_associated_custom_type, parse_struct_message, ContractErrorAttr, ContractMessageAttr,
-    Custom, MsgAttr, MsgType, OverrideEntryPoint, OverrideEntryPoints,
+    Custom, EntryPointArgs, MsgAttr, MsgType, OverrideEntryPoints,
 };
 use crate::strip_generics::StripGenerics;
-use crate::utils::{extract_return_type, filter_wheres, process_fields};
+use crate::utils::{
+    as_where_clause, emit_bracketed_generics, extract_return_type, filter_wheres, process_fields,
+};
 use crate::variant_descs::{AsVariantDescs, VariantDescs};
 use convert_case::{Case, Casing};
 use proc_macro2::{Span, TokenStream};
 use proc_macro_error::emit_error;
-use quote::quote;
+use quote::{quote, ToTokens};
 use syn::fold::Fold;
 use syn::parse::{Parse, Parser};
+use syn::punctuated::Punctuated;
 use syn::spanned::Spanned;
 use syn::visit::Visit;
 use syn::{
-    parse_quote, Attribute, GenericParam, Ident, ImplItem, ItemImpl, ItemTrait, Pat, PatType, Path,
-    ReturnType, Signature, TraitItem, Type, WhereClause, WherePredicate,
+    parse_quote, Attribute, GenericArgument, GenericParam, Ident, ItemImpl, ItemTrait, Pat,
+    PatType, Path, ReturnType, Signature, Token, TraitItem, Type, WhereClause, WherePredicate,
 };
 
 /// Representation of single struct message
@@ -100,14 +103,6 @@ impl<'a> StructMessage<'a> {
             custom,
         } = self;
 
-        let where_clause = if !wheres.is_empty() {
-            quote! {
-                where #(#wheres,)*
-            }
-        } else {
-            quote! {}
-        };
-
         let ctx_type = msg_attr
             .msg_type()
             .emit_ctx_type(&custom.query_or_default());
@@ -119,21 +114,9 @@ impl<'a> StructMessage<'a> {
         });
         let fields = fields.iter().map(MsgField::emit);
 
-        let generics = if generics.is_empty() {
-            quote! {}
-        } else {
-            quote! {
-                <#(#generics,)*>
-            }
-        };
-
-        let unused_generics = if unused_generics.is_empty() {
-            quote! {}
-        } else {
-            quote! {
-                <#(#unused_generics,)*>
-            }
-        };
+        let where_clause = as_where_clause(wheres);
+        let generics = emit_bracketed_generics(generics);
+        let unused_generics = emit_bracketed_generics(unused_generics);
 
         #[cfg(not(tarpaulin_include))]
         {
@@ -141,7 +124,7 @@ impl<'a> StructMessage<'a> {
                 #[allow(clippy::derive_partial_eq_without_eq)]
                 #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)]
                 #[serde(rename_all="snake_case")]
-                pub struct #name #generics #where_clause {
+                pub struct #name #generics {
                     #(pub #fields,)*
                 }
 
@@ -260,9 +243,7 @@ impl<'a> EnumMessage<'a> {
             query_type,
         } = self;
 
-        let match_arms = variants
-            .iter()
-            .map(|variant| variant.emit_dispatch_leg(*msg_ty));
+        let match_arms = variants.iter().map(|variant| variant.emit_dispatch_leg());
         let mut msgs: Vec<String> = variants
             .iter()
             .map(|var| var.name.to_string().to_case(Case::Snake))
@@ -282,20 +263,40 @@ impl<'a> EnumMessage<'a> {
         let ctx_type = msg_ty.emit_ctx_type(query_type);
         let dispatch_type = msg_ty.emit_result_type(resp_type, &parse_quote!(C::Error));
 
-        let all_generics = if all_generics.is_empty() {
+        let all_generics = emit_bracketed_generics(all_generics);
+        let phantom = if generics.is_empty() {
             quote! {}
+        } else if MsgType::Query == *msg_ty {
+            quote! {
+                #[serde(skip)]
+                #[returns((#(#generics,)*))]
+                _Phantom(std::marker::PhantomData<( #(#generics,)* )>),
+            }
         } else {
-            quote! { <#(#all_generics,)*> }
+            quote! {
+                #[serde(skip)]
+                _Phantom(std::marker::PhantomData<( #(#generics,)* )>),
+            }
         };
 
-        let generics = if generics.is_empty() {
-            quote! {}
+        let match_arms = if !generics.is_empty() {
+            quote! {
+                #(#match_arms,)*
+                _Phantom(_) => Err(#sylvia ::cw_std::StdError::generic_err("Phantom message should not be constructed.")).map_err(Into::into),
+            }
         } else {
-            quote! { <#(#generics,)*> }
+            quote! {
+                #(#match_arms,)*
+            }
         };
 
+        let generics = emit_bracketed_generics(generics);
+
         let unique_enum_name = Ident::new(&format!("{}{}", trait_name, name), name.span());
 
+        let ep_name = msg_ty.emit_ep_name();
+        let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), name.span());
+
         #[cfg(not(tarpaulin_include))]
         let enum_declaration = match name.to_string().as_str() {
             "QueryMsg" => {
@@ -303,8 +304,9 @@ impl<'a> EnumMessage<'a> {
                     #[allow(clippy::derive_partial_eq_without_eq)]
                     #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema, cosmwasm_schema::QueryResponses)]
                     #[serde(rename_all="snake_case")]
-                    pub enum #unique_enum_name #generics #where_clause {
+                    pub enum #unique_enum_name #generics {
                         #(#variants,)*
+                        #phantom
                     }
                     pub type #name #generics = #unique_enum_name #generics;
                 }
@@ -314,8 +316,9 @@ impl<'a> EnumMessage<'a> {
                     #[allow(clippy::derive_partial_eq_without_eq)]
                     #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)]
                     #[serde(rename_all="snake_case")]
-                    pub enum #unique_enum_name #generics #where_clause {
+                    pub enum #unique_enum_name #generics {
                         #(#variants,)*
+                        #phantom
                     }
                     pub type #name #generics = #unique_enum_name #generics;
                 }
@@ -334,14 +337,15 @@ impl<'a> EnumMessage<'a> {
                         use #unique_enum_name::*;
 
                         match self {
-                            #(#match_arms,)*
+                            #match_arms
                         }
                     }
-                    pub const fn messages() -> [&'static str; #msgs_cnt] {
-                        [#(#msgs,)*]
-                    }
                     #(#variants_constructors)*
                 }
+
+                pub const fn #messages_fn_name () -> [&'static str; #msgs_cnt] {
+                    [#(#msgs,)*]
+                }
             }
         }
     }
@@ -349,55 +353,32 @@ impl<'a> EnumMessage<'a> {
 
 /// Representation of single enum message
 pub struct ContractEnumMessage<'a> {
-    name: &'a Ident,
-    variants: Vec<MsgVariant<'a>>,
+    variants: MsgVariants<'a, GenericParam>,
     msg_ty: MsgType,
     contract: &'a Type,
     error: &'a Type,
     custom: &'a Custom<'a>,
+    where_clause: &'a Option<WhereClause>,
 }
 
 impl<'a> ContractEnumMessage<'a> {
     pub fn new(
-        name: &'a Ident,
         source: &'a ItemImpl,
-        ty: MsgType,
+        msg_ty: MsgType,
         generics: &'a [&'a GenericParam],
         error: &'a Type,
         custom: &'a Custom,
     ) -> Self {
-        let mut generics_checker = CheckGenerics::new(generics);
-        let variants: Vec<_> = source
-            .items
-            .iter()
-            .filter_map(|item| match item {
-                ImplItem::Method(method) => {
-                    let msg_attr = method.attrs.iter().find(|attr| attr.path.is_ident("msg"))?;
-                    let attr = match MsgAttr::parse.parse2(msg_attr.tokens.clone()) {
-                        Ok(attr) => attr,
-                        Err(err) => {
-                            emit_error!(method.span(), err);
-                            return None;
-                        }
-                    };
-
-                    if attr == ty {
-                        Some(MsgVariant::new(&method.sig, &mut generics_checker, attr))
-                    } else {
-                        None
-                    }
-                }
-                _ => None,
-            })
-            .collect();
+        let where_clause = &source.generics.where_clause;
+        let variants = MsgVariants::new(source.as_variants(), msg_ty, generics, where_clause);
 
         Self {
-            name,
             variants,
-            msg_ty: ty,
+            msg_ty,
             contract: &source.self_ty,
             error,
             custom,
+            where_clause,
         }
     }
 
@@ -405,79 +386,71 @@ impl<'a> ContractEnumMessage<'a> {
         let sylvia = crate_module();
 
         let Self {
-            name,
             variants,
             msg_ty,
             contract,
             error,
             custom,
+            where_clause,
+            ..
         } = self;
 
-        let match_arms = variants
-            .iter()
-            .map(|variant| variant.emit_dispatch_leg(*msg_ty));
-        let mut msgs: Vec<String> = variants
-            .iter()
-            .map(|var| var.name.to_string().to_case(Case::Snake))
-            .collect();
-        msgs.sort();
-        let msgs_cnt = msgs.len();
-        let variants_constructors = variants.iter().map(MsgVariant::emit_variants_constructors);
-        let variants = variants.iter().map(MsgVariant::emit);
+        let enum_name = msg_ty.emit_msg_name(false);
+        let match_arms = variants.emit_dispatch_legs();
+        let unused_generics = variants.unused_generics();
+        let unused_generics = emit_bracketed_generics(unused_generics);
+        let used_generics = variants.used_generics();
+        let used_generics = emit_bracketed_generics(used_generics);
+
+        let mut variant_names = variants.as_names_snake_cased();
+        variant_names.sort();
+        let variants_cnt = variant_names.len();
+        let variants_constructors = variants.emit_constructors();
+        let variants = variants.emit();
 
         let ctx_type = msg_ty.emit_ctx_type(&custom.query_or_default());
-        let contract = StripGenerics.fold_type((*contract).clone());
         let ret_type = msg_ty.emit_result_type(&custom.msg_or_default(), error);
 
-        #[cfg(not(tarpaulin_include))]
-        let enum_declaration = match name.to_string().as_str() {
-            "QueryMsg" => {
-                quote! {
-                        #[allow(clippy::derive_partial_eq_without_eq)]
-                        #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema, cosmwasm_schema::QueryResponses)]
-                        #[serde(rename_all="snake_case")]
-                        pub enum #name {
-                            #(#variants,)*
-                        }
-                }
-            }
-            _ => {
-                quote! {
-                        #[allow(clippy::derive_partial_eq_without_eq)]
-                        #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)]
-                        #[serde(rename_all="snake_case")]
-                        pub enum #name {
-                            #(#variants,)*
-                    }
-                }
-            }
+        let derive_query = match msg_ty {
+            MsgType::Query => quote! { #sylvia ::cw_schema::QueryResponses },
+            _ => quote! {},
         };
 
+        let ep_name = msg_ty.emit_ep_name();
+        let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), contract.span());
+
         #[cfg(not(tarpaulin_include))]
         {
             quote! {
-                #enum_declaration
+                #[allow(clippy::derive_partial_eq_without_eq)]
+                #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema, #derive_query )]
+                #[serde(rename_all="snake_case")]
+                pub enum #enum_name #used_generics {
+                    #(#variants,)*
+                }
 
-                impl #name {
-                    pub fn dispatch(self, contract: &#contract, ctx: #ctx_type) -> #ret_type {
-                        use #name::*;
+                impl #used_generics #enum_name #used_generics {
+                    pub fn dispatch #unused_generics (self, contract: &#contract, ctx: #ctx_type) -> #ret_type #where_clause {
+                        use #enum_name::*;
 
                         match self {
                             #(#match_arms,)*
                         }
                     }
-                    pub const fn messages() -> [&'static str; #msgs_cnt] {
-                        [#(#msgs,)*]
-                    }
 
                     #(#variants_constructors)*
                 }
+
+                pub const fn #messages_fn_name () -> [&'static str; #variants_cnt] {
+                    [#(#variant_names,)*]
+                }
             }
         }
     }
 }
 
 /// Representation of whole message variant
+#[derive(Debug)]
 pub struct MsgVariant<'a> {
     name: Ident,
     function_name: &'a Ident,
@@ -490,11 +463,14 @@ pub struct MsgVariant<'a> {
 
 impl<'a> MsgVariant<'a> {
     /// Creates new message variant from trait method
-    pub fn new(
+    pub fn new<Generic>(
         sig: &'a Signature,
-        generics_checker: &mut CheckGenerics,
+        generics_checker: &mut CheckGenerics<Generic>,
         msg_attr: MsgAttr,
-    ) -> MsgVariant<'a> {
+    ) -> MsgVariant<'a>
+    where
+        Generic: GetPath + PartialEq,
+    {
         let function_name = &sig.ident;
 
         let name = Ident::new(
@@ -506,9 +482,13 @@ impl<'a> MsgVariant<'a> {
 
         let return_type = if let MsgAttr::Query { resp_type } = msg_attr {
             match resp_type {
-                Some(resp_type) => quote! {#resp_type},
+                Some(resp_type) => {
+                    generics_checker.visit_path(&parse_quote! { #resp_type });
+                    quote! {#resp_type}
+                }
                 None => {
                     let return_type = extract_return_type(&sig.output);
+                    generics_checker.visit_path(return_type);
                     quote! {#return_type}
                 }
             }
@@ -556,13 +536,14 @@ impl<'a> MsgVariant<'a> {
     /// Emits match leg dispatching against this variant. Assumes enum variants are imported into the
     /// scope. Dispatching is performed by calling the function this variant is build from on the
     /// `contract` variable, with `ctx` as its first argument - both of them should be in scope.
-    pub fn emit_dispatch_leg(&self, msg_type: MsgType) -> TokenStream {
+    pub fn emit_dispatch_leg(&self) -> TokenStream {
         use MsgType::*;
 
         let Self {
             name,
             fields,
             function_name,
+            msg_type,
             ..
         } = self;
 
@@ -619,7 +600,11 @@ impl<'a> MsgVariant<'a> {
         }
     }
 
-    pub fn emit_querier_impl(&self, trait_module: Option<&Path>) -> TokenStream {
+    pub fn emit_querier_impl<Generic: ToTokens>(
+        &self,
+        trait_module: Option<&Path>,
+        unbonded_generics: &Vec<&Generic>,
+    ) -> TokenStream {
         let sylvia = crate_module();
         let Self {
             name,
@@ -631,10 +616,21 @@ impl<'a> MsgVariant<'a> {
         let parameters = fields.iter().map(MsgField::emit_method_field);
         let fields_names = fields.iter().map(MsgField::name);
         let variant_name = Ident::new(&name.to_string().to_case(Case::Snake), name.span());
+
+        // This method is called for `impl Contract` and `impl Trait for Contract`
+        // In case of first one, `trait_module` will always be `None`.
+        // In case of the second one, `module` on interface is not an `Option` so it will always be
+        // `Some` or the compilation will fail earlier.
         let msg = trait_module
-            .map(|module| quote! { #module ::QueryMsg })
+            .map(|module| quote! { #module ::sv::QueryMsg })
             .unwrap_or_else(|| quote! { QueryMsg });
 
+        let msg = if !unbonded_generics.is_empty() {
+            quote! { #msg ::< #(#unbonded_generics,)* > }
+        } else {
+            quote! { #msg }
+        };
+
         #[cfg(not(tarpaulin_include))]
         {
             quote! {
@@ -665,14 +661,183 @@ impl<'a> MsgVariant<'a> {
             }
         }
     }
-}
 
-pub struct MsgVariants<'a>(Vec<MsgVariant<'a>>);
+    pub fn emit_multitest_proxy_methods<Generic>(
+        &self,
+        msg_ty: &MsgType,
+        custom_msg: &Type,
+        mt_app: &Type,
+        error_type: &Type,
+        generics: &[&Generic],
+    ) -> TokenStream
+    where
+        Generic: ToTokens,
+    {
+        let sylvia = crate_module();
+        let Self {
+            name,
+            fields,
+            return_type,
+            ..
+        } = self;
 
-impl<'a> MsgVariants<'a> {
-    pub fn new(source: VariantDescs<'a>, generics: &[&'a GenericParam]) -> Self {
-        let mut generics_checker = CheckGenerics::new(generics);
+        let params = fields.iter().map(|field| field.emit_method_field());
+        let arguments = fields.iter().map(MsgField::name);
+        let name = Ident::new(&name.to_string().to_case(Case::Snake), name.span());
+        let enum_name = msg_ty.emit_msg_name(false);
+        let enum_name: Type = if !generics.is_empty() {
+            parse_quote! { #enum_name ::< #(#generics,)* > }
+        } else {
+            parse_quote! { #enum_name }
+        };
+
+        match msg_ty {
+            MsgType::Exec => quote! {
+                #[track_caller]
+                pub fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #enum_name, #mt_app, #custom_msg> {
+                    let msg = #enum_name :: #name ( #(#arguments),* );
+
+                    #sylvia ::multitest::ExecProxy::new(&self.contract_addr, msg, &self.app)
+                }
+            },
+            MsgType::Migrate => quote! {
+                #[track_caller]
+                pub fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::MigrateProxy::<#error_type, #enum_name, #mt_app, #custom_msg> {
+                    let msg = #enum_name ::new( #(#arguments),* );
+
+                    #sylvia ::multitest::MigrateProxy::new(&self.contract_addr, msg, &self.app)
+                }
+            },
+            MsgType::Query => quote! {
+                pub fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type> {
+                    let msg = #enum_name :: #name ( #(#arguments),* );
+
+                    (*self.app)
+                        .app()
+                        .wrap()
+                        .query_wasm_smart(self.contract_addr.clone(), &msg)
+                        .map_err(Into::into)
+                }
+            },
+            _ => quote! {},
+        }
+    }
+
+    pub fn emit_interface_multitest_proxy_methods<Generics>(
+        &self,
+        msg_ty: &MsgType,
+        custom_msg: &Type,
+        mt_app: &Type,
+        error_type: &Type,
+        generics: &[&Generics],
+        module: &TokenStream,
+    ) -> TokenStream
+    where
+        Generics: ToTokens,
+    {
+        let sylvia = crate_module();
+        let Self {
+            name,
+            fields,
+            return_type,
+            ..
+        } = self;
+
+        let params = fields.iter().map(|field| field.emit_method_field());
+        let arguments = fields.iter().map(MsgField::name);
+        let bracketed_generics = emit_bracketed_generics(generics);
+        let interface_enum =
+            quote! { < #module sv::Api #bracketed_generics as #sylvia ::types::InterfaceApi> };
+        let type_name = msg_ty.as_accessor_name(false);
+        let name = Ident::new(&name.to_string().to_case(Case::Snake), name.span());
+
+        match msg_ty {
+            MsgType::Exec => quote! {
+                #[track_caller]
+                fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #interface_enum :: #type_name, #mt_app, #custom_msg> {
+                    let msg = #interface_enum :: #type_name :: #name ( #(#arguments),* );
+
+                    #sylvia ::multitest::ExecProxy::new(&self.contract_addr, msg, &self.app)
+                }
+            },
+            MsgType::Query => quote! {
+                fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type> {
+                    let msg = #interface_enum :: #type_name :: #name ( #(#arguments),* );
+
+                    (*self.app)
+                        .app()
+                        .wrap()
+                        .query_wasm_smart(self.contract_addr.clone(), &msg)
+                        .map_err(Into::into)
+                }
+            },
+            _ => quote! {},
+        }
+    }
+
+    pub fn emit_proxy_methods_declarations(
+        &self,
+        msg_ty: &MsgType,
+        custom_msg: &Type,
+        error_type: &Type,
+        interface_enum: &TokenStream,
+    ) -> TokenStream {
+        let sylvia = crate_module();
+        let Self {
+            name,
+            fields,
+            return_type,
+            ..
+        } = self;
+
+        let params = fields.iter().map(|field| field.emit_method_field());
+        let type_name = msg_ty.as_accessor_name(false);
+        let name = Ident::new(&name.to_string().to_case(Case::Snake), name.span());
 
+        match msg_ty {
+            MsgType::Exec => quote! {
+                fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #interface_enum :: #type_name, MtApp, #custom_msg>;
+            },
+            MsgType::Query => quote! {
+                fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type>;
+            },
+            _ => quote! {},
+        }
+    }
+
+    pub fn as_fields_names(&self) -> Vec<&Ident> {
+        self.fields.iter().map(MsgField::name).collect()
+    }
+
+    pub fn emit_fields(&self) -> Vec<TokenStream> {
+        self.fields.iter().map(MsgField::emit).collect()
+    }
+
+    pub fn name(&self) -> &Ident {
+        &self.name
+    }
+}
+
+#[derive(Debug)]
+pub struct MsgVariants<'a, Generic> {
+    variants: Vec<MsgVariant<'a>>,
+    used_generics: Vec<&'a Generic>,
+    unused_generics: Vec<&'a Generic>,
+    where_predicates: Vec<&'a WherePredicate>,
+    msg_ty: MsgType,
+}
+
+impl<'a, Generic> MsgVariants<'a, Generic>
+where
+    Generic: GetPath + PartialEq + ToTokens,
+{
+    pub fn new(
+        source: VariantDescs<'a>,
+        msg_ty: MsgType,
+        all_generics: &'a [&'a Generic],
+        unfiltered_where_clause: &'a Option<WhereClause>,
+    ) -> Self {
+        let mut generics_checker = CheckGenerics::new(all_generics);
         let variants: Vec<_> = source
             .filter_map(|variant_desc| {
                 let msg_attr = variant_desc.attr_msg()?;
@@ -684,6 +849,10 @@ impl<'a> MsgVariants<'a> {
                     }
                 };
 
+                if attr.msg_type() != msg_ty {
+                    return None;
+                }
+
                 Some(MsgVariant::new(
                     variant_desc.into_sig(),
                     &mut generics_checker,
@@ -691,23 +860,71 @@ impl<'a> MsgVariants<'a> {
                 ))
             })
             .collect();
-        Self(variants)
+
+        let (used_generics, unused_generics) = generics_checker.used_unused();
+        let where_predicates = filter_wheres(unfiltered_where_clause, all_generics, &used_generics);
+
+        Self {
+            variants,
+            used_generics,
+            unused_generics,
+            where_predicates,
+            msg_ty,
+        }
+    }
+
+    pub fn where_clause(&self) -> Option<WhereClause> {
+        let where_predicates = &self.where_predicates;
+        if !where_predicates.is_empty() {
+            Some(parse_quote! { where #(#where_predicates),* })
+        } else {
+            None
+        }
+    }
+
+    pub fn variants(&self) -> &Vec<MsgVariant<'a>> {
+        &self.variants
+    }
+
+    pub fn used_generics(&self) -> &Vec<&'a Generic> {
+        &self.used_generics
+    }
+
+    pub fn unused_generics(&self) -> &Vec<&'a Generic> {
+        &self.unused_generics
+    }
+
+    pub fn as_where_clause(&'a self) -> Option<WhereClause> {
+        let where_predicates = &self.where_predicates;
+        if !where_predicates.is_empty() {
+            Some(parse_quote!( where #(#where_predicates,)* ))
+        } else {
+            None
+        }
     }
 
     pub fn emit_querier(&self) -> TokenStream {
         let sylvia = crate_module();
-        let variants = &self.0;
+        let Self {
+            variants,
+            used_generics,
+            ..
+        } = self;
+        let where_clause = self.where_clause();
 
         let methods_impl = variants
             .iter()
             .filter(|variant| variant.msg_type == MsgType::Query)
-            .map(|variant| variant.emit_querier_impl(None));
+            .map(|variant| variant.emit_querier_impl(None, used_generics));
 
         let methods_declaration = variants
             .iter()
             .filter(|variant| variant.msg_type == MsgType::Query)
             .map(MsgVariant::emit_querier_declaration);
 
+        let braced_generics = emit_bracketed_generics(used_generics);
+        let querier = quote! { Querier #braced_generics };
+
         #[cfg(not(tarpaulin_include))]
         {
             quote! {
@@ -730,12 +947,11 @@ impl<'a> MsgVariants<'a> {
                     }
                 }
 
-                impl <'a, C: #sylvia ::cw_std::CustomQuery> Querier for BoundQuerier<'a, C> {
+                impl <'a, C: #sylvia ::cw_std::CustomQuery, #(#used_generics,)*> #querier for BoundQuerier<'a, C> #where_clause {
                     #(#methods_impl)*
                 }
 
-
-                pub trait Querier {
+                pub trait #querier {
                     #(#methods_declaration)*
                 }
             }
@@ -748,32 +964,189 @@ impl<'a> MsgVariants<'a> {
         contract_module: Option<&Path>,
     ) -> TokenStream {
         let sylvia = crate_module();
-        let variants = &self.0;
+        let Self {
+            variants,
+            used_generics,
+            ..
+        } = self;
+        let where_clause = self.where_clause();
 
         let methods_impl = variants
             .iter()
             .filter(|variant| variant.msg_type == MsgType::Query)
-            .map(|variant| variant.emit_querier_impl(trait_module));
+            .map(|variant| variant.emit_querier_impl(trait_module, used_generics));
 
         let querier = trait_module
-            .map(|module| quote! { #module ::Querier })
-            .unwrap_or_else(|| quote! { Querier });
+            .map(|module| quote! { #module ::sv::Querier })
+            .unwrap_or_else(|| quote! { sv::Querier });
         let bound_querier = contract_module
-            .map(|module| quote! { #module ::BoundQuerier})
-            .unwrap_or_else(|| quote! { BoundQuerier });
+            .map(|module| quote! { #module ::sv::BoundQuerier})
+            .unwrap_or_else(|| quote! { sv::BoundQuerier });
+
+        let querier = if !used_generics.is_empty() {
+            quote! { #querier < #(#used_generics,)* > }
+        } else {
+            quote! { #querier }
+        };
 
         #[cfg(not(tarpaulin_include))]
         {
             quote! {
-                impl <'a, C: #sylvia ::cw_std::CustomQuery> #querier for #bound_querier<'a, C> {
+                impl <'a, C: #sylvia ::cw_std::CustomQuery> #querier for #bound_querier<'a, C > #where_clause {
                     #(#methods_impl)*
                 }
             }
         }
     }
+
+    pub fn emit_multitest_default_dispatch(&self) -> TokenStream {
+        let sylvia = crate_module();
+        let Self {
+            msg_ty,
+            used_generics,
+            ..
+        } = self;
+
+        let values = msg_ty.emit_ctx_values();
+        let msg_name = msg_ty.emit_msg_name(true);
+        let bracketed_generics = emit_bracketed_generics(used_generics);
+
+        quote! {
+            #sylvia ::cw_std::from_slice::< #msg_name #bracketed_generics >(&msg)?
+                .dispatch(self, ( #values ))
+                .map_err(Into::into)
+        }
+    }
+
+    pub fn emit_default_entry_point(
+        &self,
+        custom_msg: &Type,
+        custom_query: &Type,
+        name: &Type,
+        error: &Type,
+        contract_generics: &Option<Punctuated<GenericArgument, Token![,]>>,
+    ) -> TokenStream {
+        let Self { msg_ty, .. } = self;
+        let sylvia = crate_module();
+
+        let resp_type = match msg_ty {
+            MsgType::Query => quote! { #sylvia ::cw_std::Binary },
+            _ => quote! { #sylvia ::cw_std::Response < #custom_msg > },
+        };
+        let params = msg_ty.emit_ctx_params(custom_query);
+        let values = msg_ty.emit_ctx_values();
+        let ep_name = msg_ty.emit_ep_name();
+        let bracketed_generics = match &contract_generics {
+            Some(generics) => quote! { ::< #generics > },
+            None => quote! {},
+        };
+        let associated_name = msg_ty.as_accessor_name(true);
+
+        quote! {
+            #[#sylvia ::cw_std::entry_point]
+            pub fn #ep_name (
+                #params ,
+                msg: < #name < #contract_generics > as #sylvia ::types::ContractApi> :: #associated_name,
+            ) -> Result<#resp_type, #error> {
+                msg.dispatch(&#name #bracketed_generics ::new() , ( #values )).map_err(Into::into)
+            }
+        }
+    }
+    pub fn emit_multitest_proxy_methods(
+        &self,
+        custom_msg: &Type,
+        mt_app: &Type,
+        error_type: &Type,
+    ) -> Vec<TokenStream> {
+        self.variants
+            .iter()
+            .map(|variant| {
+                variant.emit_multitest_proxy_methods(
+                    &self.msg_ty,
+                    custom_msg,
+                    mt_app,
+                    error_type,
+                    &self.used_generics,
+                )
+            })
+            .collect()
+    }
+
+    pub fn emit_interface_multitest_proxy_methods<Generics>(
+        &self,
+        custom_msg: &Type,
+        mt_app: &Type,
+        error_type: &Type,
+        generics: &[&Generics],
+        module: &TokenStream,
+    ) -> Vec<TokenStream>
+    where
+        Generics: ToTokens,
+    {
+        self.variants
+            .iter()
+            .map(|variant| {
+                variant.emit_interface_multitest_proxy_methods(
+                    &self.msg_ty,
+                    custom_msg,
+                    mt_app,
+                    error_type,
+                    generics,
+                    module,
+                )
+            })
+            .collect()
+    }
+
+    pub fn emit_proxy_methods_declarations(
+        &self,
+        custom_msg: &Type,
+        error_type: &Type,
+        interface_enum: &TokenStream,
+    ) -> Vec<TokenStream> {
+        self.variants
+            .iter()
+            .map(|variant| {
+                variant.emit_proxy_methods_declarations(
+                    &self.msg_ty,
+                    custom_msg,
+                    error_type,
+                    interface_enum,
+                )
+            })
+            .collect()
+    }
+
+    pub fn emit_dispatch_legs(&self) -> impl Iterator<Item = TokenStream> + '_ {
+        self.variants
+            .iter()
+            .map(|variant| variant.emit_dispatch_leg())
+    }
+
+    pub fn as_names_snake_cased(&self) -> Vec<String> {
+        self.variants
+            .iter()
+            .map(|variant| variant.name.to_string().to_case(Case::Snake))
+            .collect()
+    }
+
+    pub fn emit_constructors(&self) -> impl Iterator<Item = TokenStream> + '_ {
+        self.variants
+            .iter()
+            .map(MsgVariant::emit_variants_constructors)
+    }
+
+    pub fn emit(&self) -> impl Iterator<Item = TokenStream> + '_ {
+        self.variants.iter().map(MsgVariant::emit)
+    }
+
+    pub fn get_only_variant(&self) -> Option<&MsgVariant> {
+        self.variants.first()
+    }
 }
 
 /// Representation of single message variant field
+#[derive(Debug)]
 pub struct MsgField<'a> {
     name: &'a Ident,
     ty: &'a Type,
@@ -782,7 +1155,13 @@ pub struct MsgField<'a> {
 
 impl<'a> MsgField<'a> {
     /// Creates new field from trait method argument
-    pub fn new(item: &'a PatType, generics_checker: &mut CheckGenerics) -> Option<MsgField<'a>> {
+    pub fn new<Generic>(
+        item: &'a PatType,
+        generics_checker: &mut CheckGenerics<Generic>,
+    ) -> Option<MsgField<'a>>
+    where
+        Generic: GetPath + PartialEq,
+    {
         let name = match &*item.pat {
             Pat::Ident(p) => Some(&p.ident),
             pat => {
@@ -847,55 +1226,72 @@ impl<'a> MsgField<'a> {
 /// Glue message is the message composing Exec/Query messages from several traits
 #[derive(Debug)]
 pub struct GlueMessage<'a> {
-    name: &'a Ident,
+    source: &'a ItemImpl,
     contract: &'a Type,
     msg_ty: MsgType,
     error: &'a Type,
     custom: &'a Custom<'a>,
     interfaces: &'a Interfaces,
+    variants: MsgVariants<'a, GenericParam>,
 }
 
 impl<'a> GlueMessage<'a> {
     pub fn new(
-        name: &'a Ident,
         source: &'a ItemImpl,
         msg_ty: MsgType,
         error: &'a Type,
         custom: &'a Custom,
         interfaces: &'a Interfaces,
+        variants: MsgVariants<'a, GenericParam>,
     ) -> Self {
         GlueMessage {
-            name,
+            source,
             contract: &source.self_ty,
             msg_ty,
             error,
             custom,
             interfaces,
+            variants,
         }
     }
 
     pub fn emit(&self) -> TokenStream {
         let sylvia = crate_module();
-
         let Self {
-            name,
+            source,
             contract,
             msg_ty,
             error,
             custom,
             interfaces,
+            variants,
         } = self;
-        let contract = StripGenerics.fold_type((*contract).clone());
-        let contract_name = Ident::new(&format!("Contract{}", name), name.span());
-
-        let variants = interfaces.emit_glue_message_variants(msg_ty, name);
 
-        let msg_name = quote! {#contract ( #name)};
-        let mut messages_call_on_all_variants: Vec<TokenStream> =
-            interfaces.emit_messages_call(name);
-        messages_call_on_all_variants.push(quote! {&#name :: messages()});
+        let used_generics = variants.used_generics();
+        let unused_generics = variants.unused_generics();
+        let where_clause = variants.as_where_clause();
+        let full_where_clause = &source.generics.where_clause;
+
+        let contract_enum_name = msg_ty.emit_msg_name(true);
+        let enum_name = msg_ty.emit_msg_name(false);
+        let contract_name = StripGenerics.fold_type((*contract).clone());
+        let unused_generics = emit_bracketed_generics(unused_generics);
+        let bracketed_used_generics = emit_bracketed_generics(used_generics);
+
+        let variants = interfaces.emit_glue_message_variants(msg_ty);
+
+        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 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_on_all_variants.len();
+        let variants_cnt = messages_call.len();
 
         let dispatch_arms = interfaces.interfaces().iter().map(|interface| {
             let ContractMessageAttr {
@@ -916,25 +1312,26 @@ impl<'a> GlueMessage<'a> {
 
             match (msg_ty, customs.has_msg) {
                 (MsgType::Exec, true) => quote! {
-                    #contract_name :: #variant(msg) => #sylvia ::into_response::IntoResponse::into_response(msg.dispatch(contract, Into::into( #ctx ))?)
+                    #contract_enum_name:: #variant(msg) => #sylvia ::into_response::IntoResponse::into_response(msg.dispatch(contract, Into::into( #ctx ))?)
                 },
                 _ => quote! {
-                    #contract_name :: #variant(msg) => msg.dispatch(contract, Into::into( #ctx ))
+                    #contract_enum_name :: #variant(msg) => msg.dispatch(contract, Into::into( #ctx ))
                 },
             }
         });
 
-        let dispatch_arm = quote! {#contract_name :: #contract (msg) =>msg.dispatch(contract, ctx)};
+        let dispatch_arm =
+            quote! {#contract_enum_name :: #contract_name (msg) => msg.dispatch(contract, ctx)};
 
-        let interfaces_deserialization_attempts = interfaces.emit_deserialization_attempts(name);
+        let interfaces_deserialization_attempts = interfaces.emit_deserialization_attempts(msg_ty);
 
         #[cfg(not(tarpaulin_include))]
         let contract_deserialization_attempt = quote! {
-            let msgs = &#name :: messages();
+            let msgs = &#messages_fn_name();
             if msgs.into_iter().any(|msg| msg == &recv_msg_name) {
                 match val.deserialize_into() {
-                    Ok(msg) => return Ok(Self:: #contract (msg)),
-                    Err(err) => return Err(D::Error::custom(err)).map(Self:: #contract)
+                    Ok(msg) => return Ok(Self:: #contract_name (msg)),
+                    Err(err) => return Err(D::Error::custom(err)).map(Self:: #contract_name )
                 };
             }
         };
@@ -942,16 +1339,17 @@ 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);
 
-        let mut response_schemas_calls = interfaces.emit_response_schemas_calls(name);
-        response_schemas_calls.push(quote! {#name :: response_schemas_impl()});
+        let mut response_schemas_calls = interfaces.emit_response_schemas_calls(msg_ty);
+        response_schemas_calls
+            .push(quote! {#enum_name #prefixed_used_generics :: response_schemas_impl()});
 
-        let response_schemas = match name.to_string().as_str() {
-            "QueryMsg" => {
+        let response_schemas = match msg_ty {
+            MsgType::Query => {
                 #[cfg(not(tarpaulin_include))]
                 {
                     quote! {
                         #[cfg(not(target_arch = "wasm32"))]
-                        impl cosmwasm_schema::QueryResponses for #contract_name {
+                        impl #bracketed_used_generics #sylvia ::cw_schema::QueryResponses for #contract_enum_name #bracketed_used_generics #where_clause {
                             fn response_schemas_impl() -> std::collections::BTreeMap<String, #sylvia ::schemars::schema::RootSchema> {
                                 let responses = [#(#response_schemas_calls),*];
                                 responses.into_iter().flatten().collect()
@@ -971,19 +1369,19 @@ impl<'a> GlueMessage<'a> {
                 #[allow(clippy::derive_partial_eq_without_eq)]
                 #[derive(#sylvia ::serde::Serialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)]
                 #[serde(rename_all="snake_case", untagged)]
-                pub enum #contract_name {
+                pub enum #contract_enum_name #bracketed_used_generics {
                     #(#variants,)*
-                    #msg_name
+                    #contract_variant
                 }
 
-                impl #contract_name {
-                    pub fn dispatch(
+                impl #bracketed_used_generics #contract_enum_name #bracketed_used_generics {
+                    pub fn dispatch #unused_generics (
                         self,
                         contract: &#contract,
                         ctx: #ctx_type,
-                    ) -> #ret_type {
+                    ) -> #ret_type #full_where_clause {
                         const _: () = {
-                            let msgs: [&[&str]; #variants_cnt] = [#(#messages_call_on_all_variants),*];
+                            let msgs: [&[&str]; #variants_cnt] = [#(#messages_call),*];
                             #sylvia ::utils::assert_no_intersection(msgs);
                         };
 
@@ -996,7 +1394,7 @@ impl<'a> GlueMessage<'a> {
 
                 #response_schemas
 
-                impl<'de> serde::Deserialize<'de> for #contract_name {
+                impl<'de, #(#used_generics,)* > serde::Deserialize<'de> for #contract_enum_name #bracketed_used_generics #where_clause {
                     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
                         where D: serde::Deserializer<'de>,
                     {
@@ -1019,7 +1417,7 @@ impl<'a> GlueMessage<'a> {
                             #contract_deserialization_attempt
                         }
 
-                        let msgs: [&[&str]; #variants_cnt] = [#(#messages_call_on_all_variants),*];
+                        let msgs: [&[&str]; #variants_cnt] = [#(#messages_call),*];
                         let mut err_msg = msgs.into_iter().flatten().fold(
                             // It might be better to forward the error or serialization, but we just
                             // deserialized it from JSON, not reason to expect failure here.
@@ -1038,16 +1436,184 @@ impl<'a> GlueMessage<'a> {
     }
 }
 
+pub struct ContractApi<'a> {
+    source: &'a ItemImpl,
+    exec_variants: MsgVariants<'a, GenericParam>,
+    query_variants: MsgVariants<'a, GenericParam>,
+    instantiate_variants: MsgVariants<'a, GenericParam>,
+    migrate_variants: MsgVariants<'a, GenericParam>,
+    generics: &'a [&'a GenericParam],
+    custom: &'a Custom<'a>,
+}
+
+impl<'a> ContractApi<'a> {
+    pub fn new(
+        source: &'a ItemImpl,
+        generics: &'a [&'a GenericParam],
+        custom: &'a Custom<'a>,
+    ) -> Self {
+        let exec_variants = MsgVariants::new(
+            source.as_variants(),
+            MsgType::Exec,
+            generics,
+            &source.generics.where_clause,
+        );
+
+        let query_variants = MsgVariants::new(
+            source.as_variants(),
+            MsgType::Query,
+            generics,
+            &source.generics.where_clause,
+        );
+
+        let instantiate_variants = MsgVariants::new(
+            source.as_variants(),
+            MsgType::Instantiate,
+            generics,
+            &source.generics.where_clause,
+        );
+
+        let migrate_variants = MsgVariants::new(
+            source.as_variants(),
+            MsgType::Migrate,
+            generics,
+            &source.generics.where_clause,
+        );
+
+        Self {
+            source,
+            exec_variants,
+            query_variants,
+            instantiate_variants,
+            migrate_variants,
+            generics,
+            custom,
+        }
+    }
+
+    pub fn emit(&self) -> TokenStream {
+        let sylvia = crate_module();
+        let Self {
+            source,
+            exec_variants,
+            query_variants,
+            instantiate_variants,
+            migrate_variants,
+            generics,
+            custom,
+        } = self;
+
+        let contract_name = &source.self_ty;
+        let exec_generics = &exec_variants.used_generics;
+        let query_generics = &query_variants.used_generics;
+        let instantiate_generics = &instantiate_variants.used_generics;
+        let migrate_generics = &migrate_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 instantiate_bracketed_generics = emit_bracketed_generics(instantiate_generics);
+        let migrate_bracketed_generics = emit_bracketed_generics(migrate_generics);
+
+        let migrate_type = match !migrate_variants.variants().is_empty() {
+            true => quote! { type Migrate = MigrateMsg #migrate_bracketed_generics; },
+            false => quote! { type Migrate = #sylvia ::cw_std::Empty; },
+        };
+        let custom_query = custom.query_or_default();
+
+        quote! {
+            impl #bracket_generics #sylvia ::types::ContractApi for #contract_name {
+                type ContractExec = ContractExecMsg #exec_bracketed_generics;
+                type ContractQuery = ContractQueryMsg #query_bracketed_generics;
+                type Exec = ExecMsg #exec_bracketed_generics;
+                type Query = QueryMsg #query_bracketed_generics;
+                type Instantiate = InstantiateMsg #instantiate_bracketed_generics;
+                #migrate_type
+                type Remote<'remote> = Remote<'remote>;
+                type Querier<'querier> = BoundQuerier<'querier, #custom_query >;
+            }
+        }
+    }
+}
+
+pub struct InterfaceApi<'a> {
+    exec_variants: MsgVariants<'a, GenericParam>,
+    query_variants: MsgVariants<'a, GenericParam>,
+    generics: &'a [&'a GenericParam],
+}
+
+impl<'a> InterfaceApi<'a> {
+    pub fn new(source: &'a ItemTrait, generics: &'a [&'a GenericParam]) -> Self {
+        let exec_variants = MsgVariants::new(
+            source.as_variants(),
+            MsgType::Exec,
+            generics,
+            &source.generics.where_clause,
+        );
+
+        let query_variants = MsgVariants::new(
+            source.as_variants(),
+            MsgType::Query,
+            generics,
+            &source.generics.where_clause,
+        );
+
+        Self {
+            exec_variants,
+            query_variants,
+            generics,
+        }
+    }
+
+    pub fn emit(&self) -> TokenStream {
+        let sylvia = crate_module();
+        let Self {
+            exec_variants,
+            query_variants,
+            generics,
+        } = self;
+
+        let exec_generics = &exec_variants.used_generics;
+        let query_generics = &query_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 phantom = if !generics.is_empty() {
+            quote! {
+                _phantom: std::marker::PhantomData<( #(#generics,)* )>,
+            }
+        } else {
+            quote! {}
+        };
+
+        quote! {
+            pub struct Api #bracket_generics {
+                #phantom
+            }
+
+            impl #bracket_generics #sylvia ::types::InterfaceApi for Api #bracket_generics {
+                type Exec = ExecMsg #exec_bracketed_generics;
+                type Query = QueryMsg #query_bracketed_generics;
+            }
+        }
+    }
+}
+
 pub struct EntryPoints<'a> {
+    source: &'a ItemImpl,
     name: Type,
     error: Type,
     custom: Custom<'a>,
     override_entry_points: OverrideEntryPoints,
-    variants: MsgVariants<'a>,
+    generics: Vec<&'a GenericParam>,
+    where_clause: &'a Option<WhereClause>,
+    attrs: EntryPointArgs,
 }
 
 impl<'a> EntryPoints<'a> {
-    pub fn new(source: &'a ItemImpl) -> Self {
+    pub fn new(source: &'a ItemImpl, attrs: EntryPointArgs) -> Self {
         let sylvia = crate_module();
         let name = StripGenerics.fold_type(*source.self_ty.clone());
         let override_entry_points = OverrideEntryPoints::new(&source.attrs);
@@ -1068,50 +1634,77 @@ impl<'a> EntryPoints<'a> {
             .unwrap_or_else(|| parse_quote! { #sylvia ::cw_std::StdError });
 
         let generics: Vec<_> = source.generics.params.iter().collect();
-
-        let variants = MsgVariants::new(source.as_variants(), &generics);
+        let where_clause = &source.generics.where_clause;
         let custom = Custom::new(&source.attrs);
 
         Self {
+            source,
             name,
             error,
             custom,
             override_entry_points,
-            variants,
+            generics,
+            where_clause,
+            attrs,
         }
     }
 
     pub fn emit(&self) -> TokenStream {
         let Self {
+            source,
             name,
             error,
             custom,
             override_entry_points,
-            variants,
+            generics,
+            where_clause,
+            attrs,
         } = self;
         let sylvia = crate_module();
 
         let custom_msg = custom.msg_or_default();
         let custom_query = custom.query_or_default();
-        let reply = variants
-            .0
-            .iter()
-            .find(|variant| variant.msg_type == MsgType::Reply)
-            .map(|variant| variant.function_name.clone());
+
+        let instantiate_variants = MsgVariants::new(
+            source.as_variants(),
+            MsgType::Instantiate,
+            generics,
+            where_clause,
+        );
+        let exec_variants =
+            MsgVariants::new(source.as_variants(), MsgType::Exec, generics, where_clause);
+        let query_variants =
+            MsgVariants::new(source.as_variants(), MsgType::Query, generics, where_clause);
+        let migrate_variants = MsgVariants::new(
+            source.as_variants(),
+            MsgType::Migrate,
+            generics,
+            where_clause,
+        );
+        let reply =
+            MsgVariants::<GenericParam>::new(source.as_variants(), MsgType::Reply, &[], &None)
+                .variants()
+                .iter()
+                .map(|variant| variant.function_name.clone())
+                .next();
+        let contract_generics = match &attrs.generics {
+            Some(generics) => quote! { ::< #generics > },
+            None => quote! {},
+        };
 
         #[cfg(not(tarpaulin_include))]
         {
-            let entry_points = [MsgType::Instantiate, MsgType::Exec, MsgType::Query]
+            let entry_points = [instantiate_variants, exec_variants, query_variants]
                 .into_iter()
                 .map(
-                    |msg_type| match override_entry_points.get_entry_point(msg_type) {
+                    |variants| match override_entry_points.get_entry_point(variants.msg_ty) {
                         Some(_) => quote! {},
-                        None => OverrideEntryPoint::emit_default_entry_point(
+                        None => variants.emit_default_entry_point(
                             &custom_msg,
                             &custom_query,
                             name,
                             error,
-                            msg_type,
+                            &attrs.generics,
                         ),
                     },
                 );
@@ -1119,18 +1712,15 @@ impl<'a> EntryPoints<'a> {
             let migrate_not_overridden = override_entry_points
                 .get_entry_point(MsgType::Migrate)
                 .is_none();
-            let migrate_msg_defined = variants
-                .0
-                .iter()
-                .any(|variant| variant.msg_type == MsgType::Migrate);
 
-            let migrate = if migrate_not_overridden && migrate_msg_defined {
-                OverrideEntryPoint::emit_default_entry_point(
+            let migrate = if migrate_not_overridden && migrate_variants.get_only_variant().is_some()
+            {
+                migrate_variants.emit_default_entry_point(
                     &custom_msg,
                     &custom_query,
                     name,
                     error,
-                    MsgType::Migrate,
+                    &attrs.generics,
                 )
             } else {
                 quote! {}
@@ -1147,7 +1737,7 @@ impl<'a> EntryPoints<'a> {
                             env: #sylvia ::cw_std::Env,
                             msg: #sylvia ::cw_std::Reply,
                         ) -> Result<#sylvia ::cw_std::Response < #custom_msg >, #error> {
-                            #name ::new(). #reply((deps, env).into(), msg).map_err(Into::into)
+                            #name #contract_generics ::new(). #reply((deps, env).into(), msg).map_err(Into::into)
                         }
                     },
                     _ => quote! {},
diff --git a/sylvia-derive/src/parser.rs b/sylvia-derive/src/parser.rs
index dbf6440..569e9f8 100644
--- a/sylvia-derive/src/parser.rs
+++ b/sylvia-derive/src/parser.rs
@@ -1,18 +1,22 @@
 use proc_macro2::{Punct, TokenStream};
 use proc_macro_error::emit_error;
 use quote::quote;
+use syn::fold::Fold;
 use syn::parse::{Error, Nothing, Parse, ParseBuffer, ParseStream, Parser};
+use syn::punctuated::Punctuated;
 use syn::spanned::Spanned;
 use syn::{
-    parenthesized, parse_quote, Attribute, Ident, ImplItem, ImplItemMethod, ItemImpl, ItemTrait,
-    Path, Result, Token, TraitItem, Type,
+    parenthesized, parse_quote, Attribute, GenericArgument, Ident, ImplItem, ImplItemMethod,
+    ItemImpl, ItemTrait, Path, PathArguments, Result, Token, TraitItem, Type,
 };
 
 use crate::crate_module;
+use crate::strip_generics::StripGenerics;
 
-/// Parser arguments for `contract` macro
+/// Parsed arguments for `contract` macro
 pub struct ContractArgs {
-    /// Module name wrapping generated messages, by default no additional module is created
+    /// Module in which contract impl block is defined.
+    /// Used only while implementing `Interface` on `Contract`.
     pub module: Option<Path>,
 }
 
@@ -43,6 +47,31 @@ impl Parse for ContractArgs {
     }
 }
 
+/// Parsed arguments for `entry_points` macro
+pub struct EntryPointArgs {
+    /// Types used in place of contracts generics.
+    pub generics: Option<Punctuated<GenericArgument, Token![,]>>,
+}
+
+impl Parse for EntryPointArgs {
+    fn parse(input: ParseStream) -> Result<Self> {
+        if input.is_empty() {
+            return Ok(Self { generics: None });
+        }
+
+        let path: Path = input.parse()?;
+
+        let generics = match path.segments.last() {
+            Some(segment) if segment.ident == "generics" => Some(extract_generics_from_path(&path)),
+            _ => return Err(Error::new(path.span(), "Expected `generics`")),
+        };
+
+        let _: Nothing = input.parse()?;
+
+        Ok(Self { generics })
+    }
+}
+
 /// Type of message to be generated
 #[derive(PartialEq, Eq, Debug, Clone, Copy)]
 pub enum MsgType {
@@ -102,16 +131,16 @@ impl MsgType {
     }
 
     #[cfg(not(tarpaulin_include))]
-    pub fn emit_ep_name(self) -> TokenStream {
+    pub fn emit_ep_name(self) -> Ident {
         use MsgType::*;
 
         match self {
-            Exec => quote! { execute },
-            Instantiate => quote! { instantiate },
-            Migrate => quote! { migrate },
-            Sudo => quote! { sudo },
-            Reply => quote! { reply },
-            Query => quote! { query },
+            Exec => parse_quote! { execute },
+            Instantiate => parse_quote! { instantiate },
+            Migrate => parse_quote! { migrate },
+            Sudo => parse_quote! { sudo },
+            Reply => parse_quote! { reply },
+            Query => parse_quote! { query },
         }
     }
 
@@ -142,16 +171,31 @@ impl MsgType {
         }
     }
 
-    pub fn emit_msg_name(&self) -> Type {
+    pub fn emit_msg_name(&self, is_wrapper: bool) -> Type {
         match self {
-            MsgType::Exec => parse_quote! { ContractExecMsg },
-            MsgType::Query => parse_quote! { ContractQueryMsg },
+            MsgType::Exec if is_wrapper => parse_quote! { ContractExecMsg },
+            MsgType::Query if is_wrapper => parse_quote! { ContractQueryMsg },
+            MsgType::Exec => parse_quote! { ExecMsg },
+            MsgType::Query => parse_quote! { QueryMsg },
             MsgType::Instantiate => parse_quote! { InstantiateMsg },
             MsgType::Migrate => parse_quote! { MigrateMsg },
             MsgType::Reply => parse_quote! { ReplyMsg },
             MsgType::Sudo => todo!(),
         }
     }
+
+    pub fn as_accessor_name(&self, is_wrapper: bool) -> Option<Type> {
+        match self {
+            MsgType::Exec if is_wrapper => Some(parse_quote! { ContractExec }),
+            MsgType::Query if is_wrapper => Some(parse_quote! { ContractQuery }),
+            MsgType::Instantiate => Some(parse_quote! { Instantiate }),
+            MsgType::Exec => Some(parse_quote! { Exec }),
+            MsgType::Query => Some(parse_quote! { Query }),
+            MsgType::Migrate => Some(parse_quote! { Migrate }),
+            MsgType::Sudo => Some(parse_quote! { Sudo }),
+            MsgType::Reply => Some(parse_quote! { Reply }),
+        }
+    }
 }
 
 impl PartialEq<MsgType> for MsgAttr {
@@ -238,34 +282,9 @@ pub struct Customs {
 #[derive(Debug)]
 pub struct ContractMessageAttr {
     pub module: Path,
-    pub exec_generic_params: Vec<Path>,
-    pub query_generic_params: Vec<Path>,
     pub variant: Ident,
     pub customs: Customs,
-}
-
-#[cfg(not(tarpaulin_include))]
-// False negative. Called in function below
-fn parse_generics(content: &ParseBuffer) -> Result<Vec<Path>> {
-    let _: Token![<] = content.parse()?;
-    let mut params = vec![];
-
-    loop {
-        let param: Path = content.parse()?;
-        params.push(param);
-
-        let generics_close: Option<Token![>]> = content.parse()?;
-        if generics_close.is_some() {
-            break;
-        }
-
-        let comma: Option<Token![,]> = content.parse()?;
-        if comma.is_none() {
-            return Err(Error::new(content.span(), "Expected comma or `>`"));
-        }
-    }
-
-    Ok(params)
+    pub generics: Punctuated<GenericArgument, Token![,]>,
 }
 
 fn interface_has_custom(content: ParseStream) -> Result<Customs> {
@@ -307,6 +326,26 @@ fn interface_has_custom(content: ParseStream) -> Result<Customs> {
     Ok(customs)
 }
 
+fn extract_generics_from_path(module: &Path) -> Punctuated<GenericArgument, Token![,]> {
+    let generics = module.segments.last().map(|segment| {
+        match segment.arguments.clone(){
+            PathArguments::AngleBracketed(generics) => {
+                generics.args
+            },
+            PathArguments::None => Default::default(),
+            PathArguments::Parenthesized(generics) => {
+                emit_error!(
+                    generics.span(), "Found paranthesis wrapping generics in `messages` attribute.";
+                    note = "Expected `messages` attribute to be in form `#[messages(Path<generics> as Type)]`"
+                );
+               Default::default()
+            }
+        }
+    }).unwrap_or_default();
+
+    generics
+}
+
 #[cfg(not(tarpaulin_include))]
 // False negative. It is being called in closure
 impl Parse for ContractMessageAttr {
@@ -315,31 +354,8 @@ impl Parse for ContractMessageAttr {
         parenthesized!(content in input);
 
         let module = content.parse()?;
-
-        let generics_open: Option<Token![:]> = content.parse()?;
-        let mut exec_generic_params = vec![];
-        let mut query_generic_params = vec![];
-
-        if generics_open.is_some() {
-            loop {
-                let ty: Ident = content.parse()?;
-                let params = if ty == "exec" {
-                    &mut exec_generic_params
-                } else if ty == "query" {
-                    &mut query_generic_params
-                } else {
-                    return Err(Error::new(ty.span(), "Invalid message type"));
-                };
-
-                *params = parse_generics(&content)?;
-
-                if content.peek(Token![as]) {
-                    break;
-                }
-
-                let _: Token![,] = content.parse()?;
-            }
-        }
+        let generics = extract_generics_from_path(&module);
+        let module = StripGenerics.fold_path(module);
 
         let _: Token![as] = content.parse()?;
         let variant = content.parse()?;
@@ -355,10 +371,9 @@ impl Parse for ContractMessageAttr {
 
         Ok(Self {
             module,
-            exec_generic_params,
-            query_generic_params,
             variant,
             customs,
+            generics,
         })
     }
 }
@@ -523,6 +538,7 @@ impl OverrideEntryPoint {
             entry_point,
             msg_name,
             msg_type,
+            ..
         } = self;
 
         let sylvia = crate_module();
@@ -533,49 +549,6 @@ impl OverrideEntryPoint {
                 .map_err(Into::into)
         }
     }
-
-    pub fn emit_multitest_default_dispatch(ty: MsgType) -> TokenStream {
-        let sylvia = crate_module();
-
-        let values = ty.emit_ctx_values();
-        let msg_name = ty.emit_msg_name();
-
-        quote! {
-            #sylvia ::cw_std::from_slice::< #msg_name >(&msg)?
-                .dispatch(self, ( #values ))
-                .map_err(Into::into)
-        }
-    }
-
-    #[cfg(not(tarpaulin_include))]
-    pub fn emit_default_entry_point(
-        custom_msg: &Type,
-        custom_query: &Type,
-        name: &Type,
-        error: &Type,
-        msg_type: MsgType,
-    ) -> TokenStream {
-        let sylvia = crate_module();
-
-        let resp_type = match msg_type {
-            MsgType::Query => quote! { #sylvia ::cw_std::Binary },
-            _ => quote! { #sylvia ::cw_std::Response < #custom_msg > },
-        };
-        let params = msg_type.emit_ctx_params(custom_query);
-        let values = msg_type.emit_ctx_values();
-        let ep_name = msg_type.emit_ep_name();
-        let msg_name = msg_type.emit_msg_name();
-
-        quote! {
-            #[#sylvia ::cw_std::entry_point]
-            pub fn #ep_name (
-                #params ,
-                msg: #msg_name,
-            ) -> Result<#resp_type, #error> {
-                msg.dispatch(&#name ::new() , ( #values )).map_err(Into::into)
-            }
-        }
-    }
 }
 
 #[derive(Debug)]
diff --git a/sylvia-derive/src/remote.rs b/sylvia-derive/src/remote.rs
index 4d673f1..76fcdf1 100644
--- a/sylvia-derive/src/remote.rs
+++ b/sylvia-derive/src/remote.rs
@@ -21,9 +21,9 @@ impl<'a> Remote<'a> {
             let ContractMessageAttr { module, .. } = interface;
 
             quote! {
-                impl<'a> From<&'a Remote<'a>> for #module ::Remote<'a> {
+                impl<'a> From<&'a Remote<'a>> for #module ::sv::Remote<'a> {
                     fn from(remote: &'a Remote) -> Self {
-                        #module ::Remote::borrowed(remote.as_ref())
+                        #module ::sv::Remote::borrowed(remote.as_ref())
                     }
                 }
             }
@@ -33,13 +33,11 @@ impl<'a> Remote<'a> {
             #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)]
             pub struct Remote<'a>(std::borrow::Cow<'a, #sylvia ::cw_std::Addr>);
 
-            impl Remote<'static> {
+            impl<'a> Remote<'a> {
                 pub fn new(addr: #sylvia ::cw_std::Addr) -> Self {
                     Self(std::borrow::Cow::Owned(addr))
                 }
-            }
 
-            impl<'a> Remote<'a> {
                 pub fn borrowed(addr: &'a #sylvia ::cw_std::Addr) -> Self {
                     Self(std::borrow::Cow::Borrowed(addr))
                 }
diff --git a/sylvia-derive/src/utils.rs b/sylvia-derive/src/utils.rs
index 108e6db..276deb3 100644
--- a/sylvia-derive/src/utils.rs
+++ b/sylvia-derive/src/utils.rs
@@ -1,19 +1,21 @@
+use proc_macro2::TokenStream;
 use proc_macro_error::emit_error;
+use quote::{quote, ToTokens};
 use syn::spanned::Spanned;
 use syn::visit::Visit;
 use syn::{
-    FnArg, GenericArgument, GenericParam, PathArguments, PathSegment, ReturnType, Signature, Type,
+    parse_quote, FnArg, GenericArgument, Path, PathArguments, ReturnType, Signature, Type,
     WhereClause, WherePredicate,
 };
 
-use crate::check_generics::CheckGenerics;
+use crate::check_generics::{CheckGenerics, GetPath};
 use crate::message::MsgField;
 
 #[cfg(not(tarpaulin_include))]
-pub fn filter_wheres<'a>(
+pub fn filter_wheres<'a, Generic: GetPath + PartialEq>(
     clause: &'a Option<WhereClause>,
-    generics: &[&GenericParam],
-    used_generics: &[&GenericParam],
+    generics: &[&Generic],
+    used_generics: &[&Generic],
 ) -> Vec<&'a WherePredicate> {
     clause
         .as_ref()
@@ -34,10 +36,13 @@ pub fn filter_wheres<'a>(
         .unwrap_or_default()
 }
 
-pub fn process_fields<'s>(
+pub fn process_fields<'s, Generic>(
     sig: &'s Signature,
-    generics_checker: &mut CheckGenerics,
-) -> Vec<MsgField<'s>> {
+    generics_checker: &mut CheckGenerics<Generic>,
+) -> Vec<MsgField<'s>>
+where
+    Generic: GetPath + PartialEq,
+{
     sig.inputs
         .iter()
         .skip(2)
@@ -52,14 +57,14 @@ pub fn process_fields<'s>(
         .collect()
 }
 
-pub fn extract_return_type(ret_type: &ReturnType) -> &PathSegment {
-    let ReturnType::Type(_, ty) = ret_type  else {
-            unreachable!()
-        };
+pub fn extract_return_type(ret_type: &ReturnType) -> &Path {
+    let ReturnType::Type(_, ty) = ret_type else {
+        unreachable!()
+    };
 
-    let Type::Path(type_path) = ty.as_ref()  else {
-            unreachable!()
-        };
+    let Type::Path(type_path) = ty.as_ref() else {
+        unreachable!()
+    };
     let segments = &type_path.path.segments;
     assert!(!segments.is_empty());
     let segment = &segments[0];
@@ -73,16 +78,28 @@ pub fn extract_return_type(ret_type: &ReturnType) -> &PathSegment {
                     Please use #[msg(return_type=<your_return_type>)]"
         );
     }
-    let PathArguments::AngleBracketed(args) = &segments[0].arguments  else{
-            unreachable!()
-        };
+    let PathArguments::AngleBracketed(args) = &segments[0].arguments else {
+        unreachable!()
+    };
     let args = &args.args;
     assert!(!args.is_empty());
-    let GenericArgument::Type(Type::Path(type_path)) = &args[0] else{
-            unreachable!()
-        };
-    let segments = &type_path.path.segments;
-    assert!(!segments.is_empty());
+    let GenericArgument::Type(Type::Path(type_path)) = &args[0] else {
+        unreachable!()
+    };
+
+    &type_path.path
+}
 
-    &segments[0]
+pub fn as_where_clause(where_predicates: &[&WherePredicate]) -> Option<WhereClause> {
+    match where_predicates.is_empty() {
+        true => None,
+        false => Some(parse_quote! { where #(#where_predicates),* }),
+    }
+}
+
+pub fn emit_bracketed_generics<Generic: ToTokens>(unbonded_generics: &[&Generic]) -> TokenStream {
+    match unbonded_generics.is_empty() {
+        true => quote! {},
+        false => quote! { < #(#unbonded_generics,)* > },
+    }
 }
diff --git a/sylvia/examples/basic.rs b/sylvia/examples/basic.rs
index 07f04d7..404aa66 100644
--- a/sylvia/examples/basic.rs
+++ b/sylvia/examples/basic.rs
@@ -32,12 +32,11 @@ pub struct MemberResp {
 }
 
 mod group {
-    use anyhow::Error;
     use cosmwasm_std::{Response, StdError};
+    use sylvia::interface;
     use sylvia::types::{ExecCtx, QueryCtx};
-    use sylvia::{contract, interface};
 
-    use crate::{GroupContract, Member, MemberResp};
+    use crate::{Member, MemberResp};
 
     #[interface]
     pub trait Group {
@@ -61,9 +60,19 @@ mod group {
         #[msg(query)]
         fn member(&self, ctx: QueryCtx, addr: String) -> Result<MemberResp, Self::Error>;
     }
+}
+
+mod impl_group {
+    use anyhow::Error;
+    use cosmwasm_std::Response;
+    use sylvia::types::{ExecCtx, QueryCtx};
+    use sylvia_derive::contract;
+
+    use crate::{GroupContract, MemberResp};
 
-    #[contract(module=super)]
-    impl Group for GroupContract {
+    #[contract(module=crate)]
+    #[messages(crate::group as Group)]
+    impl crate::group::Group for GroupContract {
         type Error = Error;
 
         #[msg(exec)]
@@ -80,7 +89,7 @@ mod group {
             &self,
             _ctx: ExecCtx,
             _remove: Vec<String>,
-            _add: Vec<Member>,
+            _add: Vec<crate::Member>,
         ) -> Result<Response, Self::Error> {
             todo!()
         }
diff --git a/sylvia/examples/generics.rs b/sylvia/examples/generics.rs
deleted file mode 100644
index 75acbed..0000000
--- a/sylvia/examples/generics.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-use cosmwasm_std::{CosmosMsg, Response};
-
-use sylvia::types::ExecCtx;
-use sylvia_derive::interface;
-
-#[interface(module=msg)]
-pub trait Cw1<Msg>
-where
-    Msg: std::fmt::Debug + PartialEq + Clone + schemars::JsonSchema,
-{
-    type Error;
-
-    #[msg(exec)]
-    fn execute(&self, ctx: ExecCtx, msgs: Vec<CosmosMsg<Msg>>) -> Result<Response, Self::Error>;
-}
-
-fn main() {}
diff --git a/sylvia/src/into_response.rs b/sylvia/src/into_response.rs
index b3a3e73..06f974f 100644
--- a/sylvia/src/into_response.rs
+++ b/sylvia/src/into_response.rs
@@ -52,7 +52,7 @@ impl<T> IntoResponse<T> for Response<Empty> {
             .map(|msg| msg.into_msg())
             .collect::<StdResult<_>>()?;
         let mut resp = Response::new()
-            .add_submessages(messages.into_iter())
+            .add_submessages(messages)
             .add_events(self.events)
             .add_attributes(self.attributes);
         resp.data = self.data;
diff --git a/sylvia/src/lib.rs b/sylvia/src/lib.rs
index f3502c4..66afeb6 100644
--- a/sylvia/src/lib.rs
+++ b/sylvia/src/lib.rs
@@ -10,6 +10,7 @@ pub mod utils;
 
 #[cfg(feature = "mt")]
 pub use anyhow;
+pub use cosmwasm_schema as cw_schema;
 pub use cosmwasm_std as cw_std;
 #[cfg(feature = "mt")]
 pub use cw_multi_test;
diff --git a/sylvia/src/types.rs b/sylvia/src/types.rs
index 725d57d..d5c3d33 100644
--- a/sylvia/src/types.rs
+++ b/sylvia/src/types.rs
@@ -1,4 +1,6 @@
+use cosmwasm_schema::cw_serde;
 use cosmwasm_std::{CustomQuery, Deps, DepsMut, Empty, Env, MessageInfo};
+use serde::de::DeserializeOwned;
 
 pub struct ReplyCtx<'a, C: CustomQuery = Empty> {
     pub deps: DepsMut<'a, C>,
@@ -93,3 +95,28 @@ impl<'a, C: CustomQuery> From<(Deps<'a, C>, Env)> for QueryCtx<'a, C> {
         Self { deps, env }
     }
 }
+
+pub trait CustomMsg: cosmwasm_std::CustomMsg + DeserializeOwned {}
+
+impl<T> CustomMsg for T where T: cosmwasm_std::CustomMsg + DeserializeOwned {}
+
+#[cw_serde]
+pub struct SvCustomMsg;
+
+impl cosmwasm_std::CustomMsg for SvCustomMsg {}
+
+pub trait InterfaceApi {
+    type Exec;
+    type Query;
+}
+
+pub trait ContractApi {
+    type Instantiate;
+    type Query;
+    type Exec;
+    type ContractQuery;
+    type ContractExec;
+    type Migrate;
+    type Querier<'querier>;
+    type Remote<'remote>;
+}


Test patch:
diff --git a/examples/contracts/custom/src/multitest/tests.rs b/examples/contracts/custom/src/multitest/tests.rs
index 5b92208..566ee7f 100644
--- a/examples/contracts/custom/src/multitest/tests.rs
+++ b/examples/contracts/custom/src/multitest/tests.rs
@@ -1,6 +1,6 @@
 use sylvia::multitest::App;
 
-use crate::contract::multitest_utils::CodeId;
+use crate::contract::sv::multitest_utils::CodeId;
 
 use super::custom_module::CustomModule;
 
diff --git a/examples/contracts/cw1-subkeys/src/multitest/tests.rs b/examples/contracts/cw1-subkeys/src/multitest/tests.rs
index e0d6c61..a92fe84 100644
--- a/examples/contracts/cw1-subkeys/src/multitest/tests.rs
+++ b/examples/contracts/cw1-subkeys/src/multitest/tests.rs
@@ -3,7 +3,7 @@ use cw2::{query_contract_info, ContractVersion};
 use cw_utils::{Expiration, NativeBalance};
 use sylvia::multitest::App;
 
-use crate::contract::multitest_utils::CodeId;
+use crate::contract::sv::multitest_utils::CodeId;
 use crate::contract::{CONTRACT_NAME, CONTRACT_VERSION};
 
 const ATOM: &str = "atom";
@@ -370,7 +370,7 @@ mod permissions {
 }
 
 mod cw1_execute {
-    use crate::cw1::test_utils::Cw1;
+    use crate::cw1::sv::test_utils::Cw1;
     use cosmwasm_std::BankMsg;
 
     use super::*;
diff --git a/examples/contracts/cw1-whitelist/src/multitest.rs b/examples/contracts/cw1-whitelist/src/multitest.rs
index 9859b6c..a91713d 100644
--- a/examples/contracts/cw1-whitelist/src/multitest.rs
+++ b/examples/contracts/cw1-whitelist/src/multitest.rs
@@ -1,13 +1,12 @@
 #[cfg(test)]
 mod test {
     use cosmwasm_std::{to_binary, WasmMsg};
+    use whitelist::responses::AdminListResponse;
 
-    use crate::contract::multitest_utils::CodeId;
-    use crate::cw1::test_utils::Cw1;
+    use crate::contract::sv::multitest_utils::CodeId;
+    use crate::cw1::sv::test_utils::Cw1;
     use crate::error::ContractError;
-    use crate::responses::AdminListResponse;
-    use crate::whitelist;
-    use crate::whitelist::test_utils::Whitelist;
+    use crate::whitelist::sv::test_utils::Whitelist;
     use assert_matches::assert_matches;
     use sylvia::multitest::App;
 
@@ -30,7 +29,7 @@ mod test {
             .call(owner)
             .unwrap();
 
-        let freeze = whitelist::ExecMsg::Freeze {};
+        let freeze = whitelist::sv::ExecMsg::Freeze {};
         let freeze = WasmMsg::Execute {
             contract_addr: second_contract.contract_addr.to_string(),
             msg: to_binary(&freeze).unwrap(),
diff --git a/examples/contracts/cw20-base/src/multitest/allowances_tests.rs b/examples/contracts/cw20-base/src/multitest/allowances_tests.rs
index 005b010..29fdf0a 100644
--- a/examples/contracts/cw20-base/src/multitest/allowances_tests.rs
+++ b/examples/contracts/cw20-base/src/multitest/allowances_tests.rs
@@ -7,11 +7,11 @@ use cw_multi_test::next_block;
 use cw_utils::Expiration;
 use sylvia::multitest::App;
 
-use crate::allowances::test_utils::Cw20Allowances;
-use crate::contract::multitest_utils::CodeId;
+use crate::allowances::sv::test_utils::Cw20Allowances;
+use crate::contract::sv::multitest_utils::CodeId;
 use crate::contract::InstantiateMsgData;
 use crate::error::ContractError;
-use crate::multitest::receiver_contract::multitest_utils::CodeId as ReceiverCodeId;
+use crate::multitest::receiver_contract::sv::multitest_utils::CodeId as ReceiverCodeId;
 use crate::responses::Cw20Coin;
 
 #[test]
diff --git a/examples/contracts/cw20-base/src/multitest/base_tests.rs b/examples/contracts/cw20-base/src/multitest/base_tests.rs
index 30eca4e..f97a169 100644
--- a/examples/contracts/cw20-base/src/multitest/base_tests.rs
+++ b/examples/contracts/cw20-base/src/multitest/base_tests.rs
@@ -3,11 +3,11 @@ use cw20_allowances::responses::{AllAllowancesResponse, SpenderAllowanceInfo};
 use cw_utils::Expiration;
 use sylvia::multitest::App;
 
-use crate::allowances::test_utils::Cw20Allowances;
-use crate::contract::multitest_utils::CodeId;
+use crate::allowances::sv::test_utils::Cw20Allowances;
+use crate::contract::sv::multitest_utils::CodeId;
 use crate::contract::InstantiateMsgData;
 use crate::error::ContractError;
-use crate::multitest::receiver_contract::multitest_utils::CodeId as ReceiverCodeId;
+use crate::multitest::receiver_contract::sv::multitest_utils::CodeId as ReceiverCodeId;
 use crate::responses::{BalanceResponse, Cw20Coin, TokenInfoResponse};
 
 #[test]
diff --git a/examples/contracts/cw20-base/src/multitest/marketing_tests.rs b/examples/contracts/cw20-base/src/multitest/marketing_tests.rs
index a32fb8d..c5e2235 100644
--- a/examples/contracts/cw20-base/src/multitest/marketing_tests.rs
+++ b/examples/contracts/cw20-base/src/multitest/marketing_tests.rs
@@ -3,10 +3,10 @@ use cw20_marketing::responses::{DownloadLogoResponse, LogoInfo, MarketingInfoRes
 use cw20_marketing::{EmbeddedLogo, Logo};
 use sylvia::multitest::App;
 
-use crate::contract::multitest_utils::CodeId;
+use crate::contract::sv::multitest_utils::CodeId;
 use crate::contract::{InstantiateMarketingInfo, InstantiateMsgData};
 use crate::error::ContractError;
-use crate::marketing::test_utils::Cw20Marketing;
+use crate::marketing::sv::test_utils::Cw20Marketing;
 
 const PNG_HEADER: [u8; 8] = [0x89, b'P', b'N', b'G', 0x0d, 0x0a, 0x1a, 0x0a];
 
diff --git a/examples/contracts/cw20-base/src/multitest/minting_tests.rs b/examples/contracts/cw20-base/src/multitest/minting_tests.rs
index 9ae818d..50418d8 100644
--- a/examples/contracts/cw20-base/src/multitest/minting_tests.rs
+++ b/examples/contracts/cw20-base/src/multitest/minting_tests.rs
@@ -2,10 +2,10 @@ use cosmwasm_std::{StdError, Uint128};
 use cw20_minting::responses::MinterResponse;
 use sylvia::multitest::App;
 
-use crate::contract::multitest_utils::CodeId;
+use crate::contract::sv::multitest_utils::CodeId;
 use crate::contract::InstantiateMsgData;
 use crate::error::ContractError;
-use crate::minting::test_utils::Cw20Minting;
+use crate::minting::sv::test_utils::Cw20Minting;
 use crate::responses::{Cw20Coin, TokenInfoResponse};
 
 #[test]
diff --git a/examples/contracts/cw20-base/src/multitest/receiver.rs b/examples/contracts/cw20-base/src/multitest/receiver.rs
index b349461..3a12723 100644
--- a/examples/contracts/cw20-base/src/multitest/receiver.rs
+++ b/examples/contracts/cw20-base/src/multitest/receiver.rs
@@ -1,8 +1,6 @@
 use cosmwasm_std::{Binary, Response, StdError, Uint128};
 use sylvia::types::ExecCtx;
-use sylvia::{contract, interface, schemars};
-
-use super::receiver_contract::ReceiverContract;
+use sylvia::{interface, schemars};
 
 #[interface]
 pub trait Receiver {
@@ -18,18 +16,26 @@ pub trait Receiver {
     ) -> Result<Response, Self::Error>;
 }
 
-#[contract(module=crate::multitest::receiver_contract)]
-impl Receiver for ReceiverContract {
-    type Error = StdError;
+pub mod impl_receiver {
+    use crate::multitest::receiver_contract::ReceiverContract;
+    use cosmwasm_std::{Response, StdError};
+    use sylvia::contract;
+    use sylvia::types::ExecCtx;
 
-    #[msg(exec)]
-    fn receive(
-        &self,
-        _ctx: ExecCtx,
-        _sender: String,
-        _amount: cosmwasm_std::Uint128,
-        _msg: cosmwasm_std::Binary,
-    ) -> Result<Response, Self::Error> {
-        Ok(Response::default())
+    #[contract(module=crate::multitest::receiver_contract)]
+    #[messages(crate::multitest::receiver as Receiver)]
+    impl super::Receiver for ReceiverContract {
+        type Error = StdError;
+
+        #[msg(exec)]
+        fn receive(
+            &self,
+            _ctx: ExecCtx,
+            _sender: String,
+            _amount: cosmwasm_std::Uint128,
+            _msg: cosmwasm_std::Binary,
+        ) -> Result<Response, Self::Error> {
+            Ok(Response::default())
+        }
     }
 }
diff --git a/examples/contracts/entry-points-overriding/src/multitest.rs b/examples/contracts/entry-points-overriding/src/multitest.rs
index d822451..9efda4e 100644
--- a/examples/contracts/entry-points-overriding/src/multitest.rs
+++ b/examples/contracts/entry-points-overriding/src/multitest.rs
@@ -1,9 +1,8 @@
 #[cfg(test)]
 mod test {
-    use crate::{
-        contract::{multitest_utils::CodeId, ContractExecMsg, ExecMsg},
-        messages::{CustomExecMsg, SudoMsg, UserExecMsg},
-    };
+    use crate::contract::sv::multitest_utils::CodeId;
+    use crate::contract::sv::{ContractExecMsg, ExecMsg};
+    use crate::messages::{CustomExecMsg, SudoMsg, UserExecMsg};
     use cosmwasm_std::Addr;
     use cw_multi_test::Executor;
     use sylvia::multitest::App;
diff --git a/sylvia-derive/src/multitest.rs b/sylvia-derive/src/multitest.rs
index 8b871d3..15e788f 100644
--- a/sylvia-derive/src/multitest.rs
+++ b/sylvia-derive/src/multitest.rs
@@ -1,161 +1,92 @@
+use convert_case::{Case, Casing};
 use proc_macro2::{Ident, TokenStream};
-use proc_macro_error::emit_error;
-use quote::quote;
-use syn::parse::{Parse, Parser};
-use syn::spanned::Spanned;
-use syn::{
-    parse_quote, FnArg, GenericParam, ImplItem, ItemImpl, ItemTrait, Pat, PatType, Path, Type,
-};
-
-use crate::check_generics::CheckGenerics;
+use quote::{quote, ToTokens};
+use syn::{parse_quote, ImplItem, ItemImpl, ItemTrait, Path, Type};
+
+use crate::check_generics::GetPath;
 use crate::crate_module;
 use crate::interfaces::Interfaces;
-use crate::message::MsgField;
-use crate::parser::{
-    parse_struct_message, Custom, MsgAttr, MsgType, OverrideEntryPoint, OverrideEntryPoints,
-};
-use crate::utils::{extract_return_type, process_fields};
-
-struct MessageSignature<'a> {
-    pub name: &'a Ident,
-    pub params: Vec<TokenStream>,
-    pub arguments: Vec<&'a Ident>,
-    pub msg_ty: MsgType,
-    pub return_type: TokenStream,
-}
-
-pub struct MultitestHelpers<'a> {
-    messages: Vec<MessageSignature<'a>>,
-    error_type: TokenStream,
-    contract: &'a Type,
-    is_trait: bool,
-    is_migrate: bool,
-    reply: Option<Ident>,
-    source: &'a ItemImpl,
-    generics: &'a [&'a GenericParam],
-    contract_name: &'a Ident,
-    proxy_name: Ident,
-    custom: &'a Custom<'a>,
-    override_entry_points: &'a OverrideEntryPoints,
-    interfaces: &'a Interfaces,
-}
+use crate::message::{MsgVariant, MsgVariants};
+use crate::parser::{Custom, MsgType, OverrideEntryPoint, OverrideEntryPoints};
+use crate::utils::emit_bracketed_generics;
+use crate::variant_descs::AsVariantDescs;
 
 fn interface_name(source: &ItemImpl) -> &Ident {
     let trait_name = &source.trait_;
-    let Some(trait_name) = trait_name else {unreachable!()};
+    let Some(trait_name) = trait_name else {
+        unreachable!()
+    };
     let (_, Path { segments, .. }, _) = &trait_name;
-    assert!(!segments.is_empty());
 
-    &segments[0].ident
+    match segments.last() {
+        Some(segment) => &segment.ident,
+        None => unreachable!(),
+    }
 }
 
 fn extract_contract_name(contract: &Type) -> &Ident {
-    let Type::Path(type_path) = contract  else {
-            unreachable!()
-        };
+    let Type::Path(type_path) = contract else {
+        unreachable!()
+    };
     let segments = &type_path.path.segments;
     assert!(!segments.is_empty());
     let segment = &segments[0];
     &segment.ident
 }
 
-impl<'a> MultitestHelpers<'a> {
+pub struct MultitestHelpers<'a, Generics> {
+    error_type: Type,
+    contract: &'a Type,
+    is_trait: bool,
+    source: &'a ItemImpl,
+    generics: &'a [&'a Generics],
+    where_clause: &'a Option<syn::WhereClause>,
+    contract_name: &'a Ident,
+    proxy_name: Ident,
+    custom: &'a Custom<'a>,
+    override_entry_points: &'a OverrideEntryPoints,
+    interfaces: &'a Interfaces,
+    instantiate_variants: MsgVariants<'a, Generics>,
+    exec_variants: MsgVariants<'a, Generics>,
+    query_variants: MsgVariants<'a, Generics>,
+    migrate_variants: MsgVariants<'a, Generics>,
+    reply_variants: MsgVariants<'a, Generics>,
+}
+
+impl<'a, Generics> MultitestHelpers<'a, Generics>
+where
+    Generics: ToTokens + PartialEq + GetPath,
+{
     pub fn new(
         source: &'a ItemImpl,
         is_trait: bool,
         contract_error: &'a Type,
-        generics: &'a [&'a GenericParam],
+        generics: &'a [&'a Generics],
         custom: &'a Custom,
         override_entry_points: &'a OverrideEntryPoints,
         interfaces: &'a Interfaces,
     ) -> Self {
-        let mut is_migrate = false;
-        let mut reply = None;
-        let sylvia = crate_module();
-
-        let messages: Vec<_> = source
-            .items
-            .iter()
-            .filter_map(|item| match item {
-                ImplItem::Method(method) => {
-                    let msg_attr = method.attrs.iter().find(|attr| attr.path.is_ident("msg"))?;
-                    let attr = match MsgAttr::parse.parse2(msg_attr.tokens.clone()) {
-                        Ok(attr) => attr,
-                        Err(err) => {
-                            emit_error!(method.span(), err);
-                            return None;
-                        }
-                    };
-                    let msg_ty = attr.msg_type();
-
-                    if msg_ty == MsgType::Migrate {
-                        is_migrate = true;
-                    } else if msg_ty == MsgType::Reply {
-                        reply = Some(method.sig.ident.clone());
-                        return None;
-                    } else if msg_ty != MsgType::Query && msg_ty != MsgType::Exec {
-                        return None;
-                    }
-
-                    let sig = &method.sig;
-                    let return_type = if let MsgAttr::Query { resp_type } = attr {
-                        match resp_type {
-                            Some(resp_type) => quote! {#resp_type},
-                            None => {
-                                let return_type = extract_return_type(&sig.output);
-                                quote! {#return_type}
-                            }
-                        }
-                    } else {
-                        quote! { #sylvia ::cw_multi_test::AppResponse }
-                    };
-
-                    let name = &sig.ident;
-                    let params: Vec<_> = sig
-                        .inputs
-                        .iter()
-                        .skip(2)
-                        .filter_map(|arg| match arg {
-                            FnArg::Typed(ty) => {
-                                let name = match ty.pat.as_ref() {
-                                    Pat::Ident(ident) => &ident.ident,
-                                    _ => return None,
-                                };
-                                let ty = &ty.ty;
-                                Some(quote! {#name : #ty})
-                            }
-                            _ => None,
-                        })
-                        .collect();
-                    let arguments: Vec<_> = sig
-                        .inputs
-                        .iter()
-                        .skip(2)
-                        .filter_map(|arg| match arg {
-                            FnArg::Typed(item) => {
-                                let PatType { pat, .. } = item;
-                                let Pat::Ident(ident) = pat.as_ref() else {
-                                    unreachable!()
-                                };
-                                Some(&ident.ident)
-                            }
-                            _ => None,
-                        })
-                        .collect();
-
-                    Some(MessageSignature {
-                        name,
-                        params,
-                        arguments,
-                        msg_ty,
-                        return_type,
-                    })
-                }
-                _ => None,
-            })
-            .collect();
+        let where_clause = &source.generics.where_clause;
+        let instantiate_variants = MsgVariants::new(
+            source.as_variants(),
+            MsgType::Instantiate,
+            generics,
+            where_clause,
+        );
+        let exec_variants =
+            MsgVariants::new(source.as_variants(), MsgType::Exec, generics, where_clause);
+        let query_variants =
+            MsgVariants::new(source.as_variants(), MsgType::Query, generics, where_clause);
+        let migrate_variants = MsgVariants::new(
+            source.as_variants(),
+            MsgType::Migrate,
+            generics,
+            where_clause,
+        );
+        let reply_variants =
+            MsgVariants::new(source.as_variants(), MsgType::Reply, generics, where_clause);
 
-        let error_type = if is_trait {
+        let error_type: Type = if is_trait {
             let error_type: Vec<_> = source
                 .items
                 .iter()
@@ -178,9 +109,9 @@ impl<'a> MultitestHelpers<'a> {
 
             assert!(!error_type.is_empty());
             let error_type = error_type[0];
-            quote! {#error_type}
+            parse_quote! {#error_type}
         } else {
-            quote! {#contract_error}
+            parse_quote! {#contract_error}
         };
 
         let contract = &source.self_ty;
@@ -194,30 +125,37 @@ impl<'a> MultitestHelpers<'a> {
         };
 
         Self {
-            messages,
             error_type,
             contract,
             is_trait,
-            is_migrate,
-            reply,
             source,
             generics,
+            where_clause,
             contract_name,
             proxy_name,
             custom,
             override_entry_points,
             interfaces,
+            instantiate_variants,
+            exec_variants,
+            query_variants,
+            migrate_variants,
+            reply_variants,
         }
     }
 
     pub fn emit(&self) -> TokenStream {
         let Self {
-            messages,
             error_type,
             proxy_name,
             is_trait,
             custom,
             interfaces,
+            exec_variants,
+            query_variants,
+            migrate_variants,
+            generics,
+            where_clause,
             ..
         } = self;
         let sylvia = crate_module();
@@ -242,49 +180,15 @@ impl<'a> MultitestHelpers<'a> {
             >
         };
 
-        #[cfg(not(tarpaulin_include))]
-        let messages = messages.iter().map(|msg| {
-            let MessageSignature {
-                name,
-                params,
-                arguments,
-                msg_ty,
-                return_type,
-            } = msg;
-            if msg_ty == &MsgType::Exec {
-                    quote! {
-                        #[track_caller]
-                        pub fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, ExecMsg, #mt_app, #custom_msg> {
-                            let msg = ExecMsg:: #name ( #(#arguments),* );
-
-                            #sylvia ::multitest::ExecProxy::new(&self.contract_addr, msg, &self.app)
-                        }
-                }
-            } else if msg_ty == &MsgType::Migrate {
-                    quote! {
-                        #[track_caller]
-                        pub fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::MigrateProxy::<#error_type, MigrateMsg, #mt_app, #custom_msg> {
-                            let msg = MigrateMsg::new( #(#arguments),* );
-
-                            #sylvia ::multitest::MigrateProxy::new(&self.contract_addr, msg, &self.app)
-                    }
-                }
-            } else if msg_ty == &MsgType::Query {
-                    quote! {
-                        pub fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type> {
-                            let msg = QueryMsg:: #name ( #(#arguments),* );
-
-                            (*self.app)
-                                .app()
-                                .wrap()
-                                .query_wasm_smart(self.contract_addr.clone(), &msg)
-                                .map_err(Into::into)
-                        }
-                    }
-            } else {
-                quote! {}
-            }
-        });
+        let exec_methods =
+            exec_variants.emit_multitest_proxy_methods(&custom_msg, &mt_app, error_type);
+        let query_methods =
+            query_variants.emit_multitest_proxy_methods(&custom_msg, &mt_app, error_type);
+        let migrate_methods =
+            migrate_variants.emit_multitest_proxy_methods(&custom_msg, &mt_app, error_type);
+        let where_predicates = where_clause
+            .as_ref()
+            .map(|where_clause| &where_clause.predicates);
 
         let contract_block = self.generate_contract_helpers();
 
@@ -300,13 +204,14 @@ impl<'a> MultitestHelpers<'a> {
 
                     #[derive(Derivative)]
                     #[derivative(Debug)]
-                    pub struct #proxy_name <'app, MtApp> {
+                    pub struct #proxy_name <'app, MtApp, #(#generics,)* > {
                         pub contract_addr: #sylvia ::cw_std::Addr,
                         #[derivative(Debug="ignore")]
                         pub app: &'app #sylvia ::multitest::App<MtApp>,
+                        _phantom: std::marker::PhantomData<( #(#generics,)* )>,
                     }
 
-                    impl<'app, BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT> #proxy_name <'app, #mt_app >
+                    impl<'app, BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, #(#generics,)* > #proxy_name <'app, #mt_app, #(#generics,)* >
                         where
                             CustomT: #sylvia ::cw_multi_test::Module,
                             CustomT::ExecT: std::fmt::Debug
@@ -324,23 +229,25 @@ impl<'a> MultitestHelpers<'a> {
                             DistrT: #sylvia ::cw_multi_test::Distribution,
                             IbcT: #sylvia ::cw_multi_test::Ibc,
                             GovT: #sylvia ::cw_multi_test::Gov,
-                            #mt_app : Executor< #custom_msg >
+                            #mt_app : Executor< #custom_msg >,
+                            #where_predicates
                     {
                         pub fn new(contract_addr: #sylvia ::cw_std::Addr, app: &'app #sylvia ::multitest::App< #mt_app >) -> Self {
-                            #proxy_name{ contract_addr, app }
+                            #proxy_name { contract_addr, app, _phantom: std::marker::PhantomData::default() }
                         }
 
-                        #(#messages)*
-
-                        #(#proxy_accessors)*
+                        #( #exec_methods )*
+                        #( #migrate_methods )*
+                        #( #query_methods )*
+                        #( #proxy_accessors )*
                     }
 
-                    impl<'app, BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT>
+                    impl<'app, BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, #(#generics,)* >
                         From<(
                             #sylvia ::cw_std::Addr,
                             &'app #sylvia ::multitest::App<#mt_app>,
                         )>
-                        for #proxy_name <'app, #mt_app >
+                        for #proxy_name <'app, #mt_app, #(#generics,)* >
                     where
                         CustomT: #sylvia ::cw_multi_test::Module,
                         CustomT::ExecT: std::fmt::Debug
@@ -359,9 +266,10 @@ impl<'a> MultitestHelpers<'a> {
                         IbcT: #sylvia ::cw_multi_test::Ibc,
                         GovT: #sylvia ::cw_multi_test::Gov,
                         #mt_app : Executor< #custom_msg >,
+                        #where_predicates
                     {
                         fn from(input: (#sylvia ::cw_std::Addr, &'app #sylvia ::multitest::App< #mt_app >))
-                            -> #proxy_name<'app, #mt_app > {
+                            -> #proxy_name<'app, #mt_app, #(#generics,)* > {
                             #proxy_name::new(input.0, input.1)
                         }
                     }
@@ -374,10 +282,12 @@ impl<'a> MultitestHelpers<'a> {
 
     fn impl_trait_on_proxy(&self) -> TokenStream {
         let Self {
-            messages,
             error_type,
             custom,
             interfaces,
+            generics,
+            exec_variants,
+            query_variants,
             ..
         } = self;
 
@@ -387,34 +297,18 @@ impl<'a> MultitestHelpers<'a> {
         let proxy_name = &self.proxy_name;
         let trait_name = Ident::new(&format!("{}", interface_name), interface_name.span());
 
-        let modules: Vec<&Path> = interfaces.as_modules().collect();
-
-        #[cfg(not(tarpaulin_include))]
-        let module = match modules.len() {
-            0 => {
-                quote! {}
-            }
-            1 => {
-                let module = &modules[0];
-                quote! {#module ::}
-            }
-            _ => {
-                let first = &modules[0];
-                for redefined in &modules[1..] {
-                    emit_error!(
-                      redefined, "The attribute `messages` is redefined";
-                      note = first.span() => "Previous definition of the attribute `messsages`";
-                      note = "Only one `messages` attribute can exist on an interface implementation on contract"
-                    );
-                }
-                quote! {}
-            }
-        };
+        let module = interfaces
+            .get_only_interface()
+            .map(|interface| {
+                let module = &interface.module;
+                quote! { #module :: }
+            })
+            .unwrap_or(quote! {});
 
         let custom_msg = custom.msg_or_default();
 
         #[cfg(not(tarpaulin_include))]
-        let mt_app = quote! {
+        let mt_app = parse_quote! {
             #sylvia ::cw_multi_test::App<
                 BankT,
                 ApiT,
@@ -428,58 +322,31 @@ impl<'a> MultitestHelpers<'a> {
             >
         };
 
-        #[cfg(not(tarpaulin_include))]
-        let methods_definitions = messages.iter().map(|msg| {
-            let MessageSignature {
-                name,
-                params,
-                arguments,
-                msg_ty,
-                return_type,
-            } = msg;
-            if msg_ty == &MsgType::Exec {
-                quote! {
-                    #[track_caller]
-                    fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #module ExecMsg, #mt_app, #custom_msg> {
-                        let msg = #module ExecMsg:: #name ( #(#arguments),* );
-
-                        #sylvia ::multitest::ExecProxy::new(&self.contract_addr, msg, &self.app)
-                    }
-                }
-            } else {
-                quote! {
-                    fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type> {
-                        let msg = #module QueryMsg:: #name ( #(#arguments),* );
-
-                        (*self.app)
-                            .app()
-                            .wrap()
-                            .query_wasm_smart(self.contract_addr.clone(), &msg)
-                            .map_err(Into::into)
-                    }
-                }
-            }
-        });
+        let bracketed_generics = emit_bracketed_generics(generics);
+        let interface_enum =
+            quote! { < #module sv::Api #bracketed_generics as #sylvia ::types::InterfaceApi> };
 
-        #[cfg(not(tarpaulin_include))]
-        let methods_declarations = messages.iter().map(|msg| {
-            let MessageSignature {
-                name,
-                params,
-                msg_ty,
-                return_type,
-                ..
-            } = msg;
-            if msg_ty == &MsgType::Exec {
-                quote! {
-                    fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #module ExecMsg, MtApp, #custom_msg>;
-                }
-            } else {
-                quote! {
-                    fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type>;
-                }
-            }
-        });
+        let exec_methods = exec_variants.emit_interface_multitest_proxy_methods(
+            &custom_msg,
+            &mt_app,
+            error_type,
+            generics,
+            &module,
+        );
+        let query_methods = query_variants.emit_interface_multitest_proxy_methods(
+            &custom_msg,
+            &mt_app,
+            error_type,
+            generics,
+            &module,
+        );
+        let exec_methods_declarations =
+            exec_variants.emit_proxy_methods_declarations(&custom_msg, error_type, &interface_enum);
+        let query_methods_declarations = query_variants.emit_proxy_methods_declarations(
+            &custom_msg,
+            error_type,
+            &interface_enum,
+        );
 
         #[cfg(not(tarpaulin_include))]
         {
@@ -488,10 +355,11 @@ impl<'a> MultitestHelpers<'a> {
                     use super::*;
 
                     pub trait #trait_name<MtApp> {
-                        #(#methods_declarations)*
+                        #(#query_methods_declarations)*
+                        #(#exec_methods_declarations)*
                     }
 
-                    impl<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT> #trait_name< #mt_app > for #module trait_utils:: #proxy_name<'_, #mt_app >
+                    impl<BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT> #trait_name< #mt_app > for #module sv::trait_utils:: #proxy_name<'_, #mt_app >
                     where
                         CustomT: #sylvia ::cw_multi_test::Module,
                         WasmT: #sylvia ::cw_multi_test::Wasm<CustomT::ExecT, CustomT::QueryT>,
@@ -512,8 +380,8 @@ impl<'a> MultitestHelpers<'a> {
                         CustomT::QueryT: #sylvia:: cw_std::CustomQuery + #sylvia ::serde::de::DeserializeOwned + 'static,
                         #mt_app : #sylvia ::cw_multi_test::Executor< #custom_msg >
                     {
-
-                        #(#methods_definitions)*
+                        #(#query_methods)*
+                        #(#exec_methods)*
                     }
                 }
             }
@@ -521,13 +389,15 @@ impl<'a> MultitestHelpers<'a> {
     }
 
     fn generate_contract_helpers(&self) -> TokenStream {
+        let sylvia = crate_module();
         let Self {
             error_type,
             is_trait,
-            source,
             generics,
+            where_clause,
             contract_name,
             proxy_name,
+            instantiate_variants,
             ..
         } = self;
 
@@ -535,18 +405,33 @@ impl<'a> MultitestHelpers<'a> {
             return quote! {};
         }
 
-        let sylvia = crate_module();
+        let fields_names = instantiate_variants
+            .get_only_variant()
+            .map(MsgVariant::as_fields_names)
+            .unwrap_or(vec![]);
 
-        let mut generics_checker = CheckGenerics::new(generics);
+        let fields = instantiate_variants
+            .get_only_variant()
+            .map(MsgVariant::emit_fields)
+            .unwrap_or(vec![]);
 
-        let parsed = parse_struct_message(source, MsgType::Instantiate);
-        let Some((method,_)) = parsed else {
-            return quote! {};
+        let used_generics = instantiate_variants.used_generics();
+        let bracketed_used_generics = emit_bracketed_generics(used_generics);
+
+        let where_predicates = where_clause
+            .as_ref()
+            .map(|where_clause| &where_clause.predicates);
+        let contract = if !generics.is_empty() {
+            quote! { #contract_name ::< #(#generics,)* > }
+        } else {
+            quote! { #contract_name }
         };
 
-        let instantiate_fields = process_fields(&method.sig, &mut generics_checker);
-        let fields_names: Vec<_> = instantiate_fields.iter().map(MsgField::name).collect();
-        let fields = instantiate_fields.iter().map(MsgField::emit);
+        let instantiate_msg = if !used_generics.is_empty() {
+            quote! { InstantiateMsg::< #(#used_generics,)* > }
+        } else {
+            quote! { InstantiateMsg }
+        };
 
         let impl_contract = self.generate_impl_contract();
 
@@ -573,12 +458,14 @@ impl<'a> MultitestHelpers<'a> {
             quote! {
                 #impl_contract
 
-                pub struct CodeId<'app, MtApp> {
+                pub struct CodeId<'app, #(#generics,)* MtApp> {
                     code_id: u64,
                     app: &'app #sylvia ::multitest::App<MtApp>,
+                    _phantom: std::marker::PhantomData<( #(#generics,)* )>,
+
                 }
 
-                impl<'app, BankT, ApiT, StorageT, CustomT, StakingT, DistrT, IbcT, GovT> CodeId<'app, #mt_app>
+                impl<'app, BankT, ApiT, StorageT, CustomT, StakingT, DistrT, IbcT, GovT, #(#generics,)* > CodeId<'app, #(#generics,)* #mt_app >
                     where
                         BankT: #sylvia ::cw_multi_test::Bank,
                         ApiT: #sylvia ::cw_std::Api,
@@ -588,12 +475,13 @@ impl<'a> MultitestHelpers<'a> {
                         DistrT: #sylvia ::cw_multi_test::Distribution,
                         IbcT: #sylvia ::cw_multi_test::Ibc,
                         GovT: #sylvia ::cw_multi_test::Gov,
+                        #where_predicates
                 {
                     pub fn store_code(app: &'app #sylvia ::multitest::App< #mt_app >) -> Self {
                         let code_id = app
                             .app_mut()
-                            .store_code(Box::new(#contract_name ::new()));
-                        Self { code_id, app }
+                            .store_code(Box::new(#contract ::new()));
+                        Self { code_id, app, _phantom: std::marker::PhantomData::default() }
                     }
 
                     pub fn code_id(&self) -> u64 {
@@ -602,9 +490,9 @@ impl<'a> MultitestHelpers<'a> {
 
                     pub fn instantiate(
                         &self,#(#fields,)*
-                    ) -> InstantiateProxy<'_, 'app, #mt_app > {
-                        let msg = InstantiateMsg {#(#fields_names,)*};
-                        InstantiateProxy {
+                    ) -> InstantiateProxy<'_, 'app, #(#generics,)* #mt_app > {
+                        let msg = #instantiate_msg {#(#fields_names,)*};
+                        InstantiateProxy::< #(#generics,)* _> {
                             code_id: self,
                             funds: &[],
                             label: "Contract",
@@ -614,23 +502,24 @@ impl<'a> MultitestHelpers<'a> {
                     }
                 }
 
-                pub struct InstantiateProxy<'a, 'app, MtApp> {
-                    code_id: &'a CodeId <'app, MtApp>,
-                    funds: &'a [#sylvia ::cw_std::Coin],
-                    label: &'a str,
+                pub struct InstantiateProxy<'proxy, 'app, #(#generics,)* MtApp> {
+                    code_id: &'proxy CodeId <'app, #(#generics,)* MtApp>,
+                    funds: &'proxy [#sylvia ::cw_std::Coin],
+                    label: &'proxy str,
                     admin: Option<String>,
-                    msg: InstantiateMsg,
+                    msg: InstantiateMsg #bracketed_used_generics,
                 }
 
-                impl<'a, 'app, MtApp> InstantiateProxy<'a, 'app, MtApp>
+                impl<'proxy, 'app, #(#generics,)* MtApp> InstantiateProxy<'proxy, 'app, #(#generics,)* MtApp>
                     where
                         MtApp: Executor< #custom_msg >,
+                        #where_predicates
                 {
-                    pub fn with_funds(self, funds: &'a [#sylvia ::cw_std::Coin]) -> Self {
+                    pub fn with_funds(self, funds: &'proxy [#sylvia ::cw_std::Coin]) -> Self {
                         Self { funds, ..self }
                     }
 
-                    pub fn with_label(self, label: &'a str) -> Self {
+                    pub fn with_label(self, label: &'proxy str) -> Self {
                         Self { label, ..self }
                     }
 
@@ -640,7 +529,7 @@ impl<'a> MultitestHelpers<'a> {
                     }
 
                     #[track_caller]
-                    pub fn call(self, sender: &str) -> Result<#proxy_name<'app, MtApp>, #error_type> {
+                    pub fn call(self, sender: &str) -> Result<#proxy_name<'app, MtApp, #(#generics,)* >, #error_type> {
                         (*self.code_id.app)
                             .app_mut()
                             .instantiate_contract(
@@ -655,6 +544,7 @@ impl<'a> MultitestHelpers<'a> {
                             .map(|addr| #proxy_name {
                                 contract_addr: addr,
                                 app: self.code_id.app,
+                                _phantom: std::marker::PhantomData::default(),
                             })
                     }
                 }
@@ -664,29 +554,36 @@ impl<'a> MultitestHelpers<'a> {
 
     fn generate_impl_contract(&self) -> TokenStream {
         let Self {
+            source,
             contract,
             custom,
             override_entry_points,
+            generics,
+            instantiate_variants,
+            exec_variants,
+            query_variants,
+            migrate_variants,
+            reply_variants,
             ..
         } = self;
         let sylvia = crate_module();
 
+        let bracketed_generics = emit_bracketed_generics(generics);
+        let full_where_clause = &source.generics.where_clause;
         let instantiate_body = override_entry_points
             .get_entry_point(MsgType::Instantiate)
             .map(OverrideEntryPoint::emit_multitest_dispatch)
-            .unwrap_or_else(|| {
-                OverrideEntryPoint::emit_multitest_default_dispatch(MsgType::Instantiate)
-            });
+            .unwrap_or_else(|| instantiate_variants.emit_multitest_default_dispatch());
 
         let exec_body = override_entry_points
             .get_entry_point(MsgType::Exec)
             .map(OverrideEntryPoint::emit_multitest_dispatch)
-            .unwrap_or_else(|| OverrideEntryPoint::emit_multitest_default_dispatch(MsgType::Exec));
+            .unwrap_or_else(|| exec_variants.emit_multitest_default_dispatch());
 
         let query_body = override_entry_points
             .get_entry_point(MsgType::Query)
             .map(OverrideEntryPoint::emit_multitest_dispatch)
-            .unwrap_or_else(|| OverrideEntryPoint::emit_multitest_default_dispatch(MsgType::Query));
+            .unwrap_or_else(|| query_variants.emit_multitest_default_dispatch());
 
         let sudo_body = override_entry_points
             .get_entry_point(MsgType::Sudo)
@@ -699,8 +596,8 @@ impl<'a> MultitestHelpers<'a> {
 
         let migrate_body = match override_entry_points.get_entry_point(MsgType::Migrate) {
             Some(entry_point) => entry_point.emit_multitest_dispatch(),
-            None if self.is_migrate => {
-                OverrideEntryPoint::emit_multitest_default_dispatch(MsgType::Migrate)
+            None if migrate_variants.get_only_variant().is_some() => {
+                migrate_variants.emit_multitest_default_dispatch()
             }
             None => quote! {
                 #sylvia ::anyhow::bail!("migrate not implemented for contract")
@@ -709,12 +606,17 @@ impl<'a> MultitestHelpers<'a> {
 
         let reply_body = match override_entry_points.get_entry_point(MsgType::Reply) {
             Some(entry_point) => entry_point.emit_multitest_dispatch(),
-            None => self
-                .reply
+            None => reply_variants
+                .get_only_variant()
                 .as_ref()
                 .map(|reply| {
+                    let reply_name = reply.name();
+                    let reply_name = Ident::new(
+                        &reply_name.to_string().to_case(Case::Snake),
+                        reply_name.span(),
+                    );
                     quote! {
-                        self. #reply((deps, env).into(), msg).map_err(Into::into)
+                        self. #reply_name ((deps, env).into(), msg).map_err(Into::into)
                     }
                 })
                 .unwrap_or_else(|| {
@@ -730,7 +632,7 @@ impl<'a> MultitestHelpers<'a> {
         #[cfg(not(tarpaulin_include))]
         {
             quote! {
-                impl #sylvia ::cw_multi_test::Contract<#custom_msg, #custom_query> for #contract {
+                impl #bracketed_generics #sylvia ::cw_multi_test::Contract<#custom_msg, #custom_query> for #contract #full_where_clause {
                     fn execute(
                         &self,
                         deps: #sylvia ::cw_std::DepsMut< #custom_query >,
diff --git a/sylvia/tests/api.rs b/sylvia/tests/api.rs
new file mode 100644
index 0000000..e9c39a6
--- /dev/null
+++ b/sylvia/tests/api.rs
@@ -0,0 +1,127 @@
+use cosmwasm_std::{Response, StdResult};
+use std::marker::PhantomData;
+
+use sylvia::types::{CustomMsg, ExecCtx, InstantiateCtx, MigrateCtx, QueryCtx};
+use sylvia_derive::contract;
+
+pub struct SomeContract<Instantiate, Query, Exec, Migrate, Ret> {
+    _phantom: PhantomData<(Instantiate, Query, Exec, Migrate, Ret)>,
+}
+
+#[contract]
+impl<Instantiate, Query, Exec, Migrate, Ret> SomeContract<Instantiate, Query, Exec, Migrate, Ret>
+where
+    Instantiate: CustomMsg + 'static,
+    Query: CustomMsg + 'static,
+    Exec: CustomMsg + 'static,
+    Migrate: CustomMsg + 'static,
+    Ret: CustomMsg + 'static,
+{
+    pub const fn new() -> Self {
+        Self {
+            _phantom: PhantomData,
+        }
+    }
+
+    #[msg(instantiate)]
+    pub fn instantiate(&self, _ctx: InstantiateCtx, _param: Instantiate) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    #[msg(exec)]
+    pub fn exec(&self, _ctx: ExecCtx, _param: Exec) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    #[msg(query)]
+    pub fn query(&self, _ctx: QueryCtx, _param: Query) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    #[msg(migrate)]
+    pub fn migrate(&self, _ctx: MigrateCtx, _param: Migrate) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::SomeContract;
+    use cosmwasm_std::{testing::mock_dependencies, Addr, QuerierWrapper};
+    use sylvia::types::{ContractApi, SvCustomMsg};
+
+    #[test]
+    fn api() {
+        let owner = Addr::unchecked("owner");
+
+        let _: crate::sv::InstantiateMsg<SvCustomMsg> = <SomeContract<
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+        > as ContractApi>::Instantiate::new(
+            SvCustomMsg
+        );
+
+        let exec: crate::sv::ExecMsg<SvCustomMsg> = <SomeContract<
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+        > as ContractApi>::Exec::exec(
+            SvCustomMsg
+        );
+
+        let query: crate::sv::QueryMsg<SvCustomMsg> = <SomeContract<
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+        > as ContractApi>::Query::query(
+            SvCustomMsg
+        );
+
+        let _: crate::sv::ContractExecMsg<SvCustomMsg> = <SomeContract<
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+        > as ContractApi>::ContractExec::SomeContract(
+            exec
+        );
+
+        let _: crate::sv::ContractQueryMsg<SvCustomMsg> = <SomeContract<
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+        > as ContractApi>::ContractQuery::SomeContract(
+            query
+        );
+
+        let _: crate::sv::Remote<'_> = <SomeContract<
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+        > as ContractApi>::Remote::new(owner.clone());
+
+        let deps = mock_dependencies();
+        let querier_wrapper: QuerierWrapper = QuerierWrapper::new(&deps.querier);
+        let _: crate::sv::BoundQuerier<'_, cosmwasm_std::Empty> = <SomeContract<
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+            SvCustomMsg,
+        > as ContractApi>::Querier::borrowed(
+            &owner, &querier_wrapper
+        );
+    }
+}
diff --git a/sylvia/tests/custom_msg.rs b/sylvia/tests/custom_msg.rs
index 9365ba4..55e6693 100644
--- a/sylvia/tests/custom_msg.rs
+++ b/sylvia/tests/custom_msg.rs
@@ -21,8 +21,8 @@ pub struct SomeResponse;
 
 mod some_interface {
     use cosmwasm_std::{Response, StdError, StdResult};
+    use sylvia::interface;
     use sylvia::types::{ExecCtx, QueryCtx};
-    use sylvia::{contract, interface};
 
     use crate::{MyMsg, SomeResponse};
 
@@ -39,8 +39,18 @@ mod some_interface {
         #[msg(exec)]
         fn interface_exec(&self, ctx: ExecCtx) -> StdResult<Response<MyMsg>>;
     }
+}
+
+mod impl_some_interface {
+    use cosmwasm_std::{Response, StdError, StdResult};
+    use sylvia::contract;
+    use sylvia::types::{ExecCtx, QueryCtx};
+
+    use crate::some_interface::SomeInterface;
+    use crate::{MyMsg, SomeResponse};
 
-    #[contract(module=super)]
+    #[contract(module=crate)]
+    #[messages(crate::some_interface as SomeInterface)]
     #[sv::custom(msg=MyMsg)]
     impl SomeInterface for crate::MyContract {
         type Error = StdError;
@@ -59,10 +69,10 @@ mod some_interface {
 
 // Use `#[sv::custom(..)]` if both it and associated type defined
 mod interface {
-    use crate::{MyMsg, OtherMsg};
+    use crate::MyMsg;
     use cosmwasm_std::{CustomMsg, Response, StdError, StdResult};
+    use sylvia::interface;
     use sylvia::types::ExecCtx;
-    use sylvia::{contract, interface};
 
     #[interface]
     #[sv::custom(msg=MyMsg)]
@@ -74,8 +84,16 @@ mod interface {
         #[msg(exec)]
         fn exec(&self, ctx: ExecCtx) -> StdResult<Response<MyMsg>>;
     }
+}
+mod impl_interface {
+    use crate::interface::Interface;
+    use crate::{MyMsg, OtherMsg};
+    use cosmwasm_std::{Response, StdError, StdResult};
+    use sylvia::contract;
+    use sylvia::types::ExecCtx;
 
-    #[contract(module=super)]
+    #[contract(module=crate)]
+    #[messages(crate::interface as Interface)]
     #[sv::custom(msg=MyMsg)]
     impl Interface for crate::MyContract {
         type Error = StdError;
@@ -90,8 +108,8 @@ mod interface {
 
 mod other_interface {
     use cosmwasm_std::{Response, StdError, StdResult};
+    use sylvia::interface;
     use sylvia::types::ExecCtx;
-    use sylvia::{contract, interface};
 
     #[interface]
     pub trait OtherInterface {
@@ -101,8 +119,15 @@ mod other_interface {
         #[msg(exec)]
         fn other_interface_exec(&self, ctx: ExecCtx) -> StdResult<Response>;
     }
+}
+mod impl_other_interface {
+    use crate::other_interface::OtherInterface;
+    use cosmwasm_std::{Response, StdError, StdResult};
+    use sylvia::contract;
+    use sylvia::types::ExecCtx;
 
-    #[contract(module=super)]
+    #[contract(module=crate)]
+    #[messages(crate::other_interface as OtherInterface)]
     #[sv::custom(msg=crate::MyMsg)]
     impl OtherInterface for crate::MyContract {
         type Error = StdError;
@@ -115,10 +140,9 @@ mod other_interface {
 }
 
 mod associated_interface {
-    use crate::MyMsg;
     use cosmwasm_std::{CustomMsg, Response, StdError, StdResult};
+    use sylvia::interface;
     use sylvia::types::ExecCtx;
-    use sylvia::{contract, interface};
 
     #[interface]
     pub trait AssociatedInterface {
@@ -129,8 +153,16 @@ mod associated_interface {
         #[msg(exec)]
         fn associated_exec(&self, ctx: ExecCtx) -> StdResult<Response<Self::ExecC>>;
     }
+}
+mod impl_associated_interface {
+    use crate::associated_interface::AssociatedInterface;
+    use crate::MyMsg;
+    use cosmwasm_std::{Response, StdError, StdResult};
+    use sylvia::contract;
+    use sylvia::types::ExecCtx;
 
-    #[contract(module=super)]
+    #[contract(module=crate)]
+    #[messages(crate::associated_interface as AssociatedInterface)]
     #[sv::custom(msg=MyMsg)]
     impl AssociatedInterface for crate::MyContract {
         type Error = StdError;
@@ -179,20 +211,19 @@ impl MyContract {
 
 #[cfg(all(test, feature = "mt"))]
 mod tests {
-    use crate::interface::test_utils::Interface;
+    use crate::impl_associated_interface::sv::test_utils::AssociatedInterface;
+    use crate::impl_interface::sv::test_utils::Interface;
+    use crate::impl_other_interface::sv::test_utils::OtherInterface;
+    use crate::impl_some_interface::sv::test_utils::SomeInterface;
     use crate::MyContract;
-    use sylvia::multitest::App;
-
-    use crate::associated_interface::test_utils::AssociatedInterface;
-    use crate::other_interface::test_utils::OtherInterface;
-    use crate::some_interface::test_utils::SomeInterface;
     use crate::MyMsg;
+    use sylvia::multitest::App;
 
     #[test]
     fn test_custom() {
         let _ = MyContract::new();
         let app = App::<cw_multi_test::BasicApp<MyMsg>>::custom(|_, _, _| {});
-        let code_id = crate::multitest_utils::CodeId::store_code(&app);
+        let code_id = crate::sv::multitest_utils::CodeId::store_code(&app);
 
         let owner = "owner";
 
diff --git a/sylvia/tests/custom_query.rs b/sylvia/tests/custom_query.rs
index cb92e81..00c33d3 100644
--- a/sylvia/tests/custom_query.rs
+++ b/sylvia/tests/custom_query.rs
@@ -20,10 +20,10 @@ pub struct SomeResponse;
 
 mod interface {
     use cosmwasm_std::{CustomQuery, Response, StdError, StdResult};
+    use sylvia::interface;
     use sylvia::types::{ExecCtx, QueryCtx};
-    use sylvia::{contract, interface};
 
-    use crate::{MyQuery, OtherQuery, SomeResponse};
+    use crate::{MyQuery, SomeResponse};
 
     #[interface]
     #[sv::custom(query=MyQuery)]
@@ -39,10 +39,19 @@ mod interface {
         #[msg(exec)]
         fn interface_exec(&self, ctx: ExecCtx<MyQuery>) -> StdResult<Response>;
     }
+}
+
+mod impl_interface {
+    use cosmwasm_std::{Response, StdError, StdResult};
+    use sylvia::types::{ExecCtx, QueryCtx};
+    use sylvia_derive::contract;
 
-    #[contract(module=super)]
+    use crate::{MyQuery, OtherQuery, SomeResponse};
+
+    #[contract(module=crate)]
+    #[messages(crate::interface as Interface)]
     #[sv::custom(query=MyQuery)]
-    impl Interface for crate::MyContract {
+    impl crate::interface::Interface for crate::MyContract {
         type Error = StdError;
         type QueryC = OtherQuery;
 
@@ -60,8 +69,8 @@ mod interface {
 
 mod some_interface {
     use cosmwasm_std::{Response, StdError, StdResult};
+    use sylvia::interface;
     use sylvia::types::{ExecCtx, QueryCtx};
-    use sylvia::{contract, interface};
 
     use crate::{MyQuery, SomeResponse};
 
@@ -78,10 +87,19 @@ mod some_interface {
         #[msg(exec)]
         fn some_interface_exec(&self, ctx: ExecCtx<MyQuery>) -> StdResult<Response>;
     }
+}
+
+mod impl_some_interface {
+    use cosmwasm_std::{Response, StdError, StdResult};
+    use sylvia::types::{ExecCtx, QueryCtx};
+    use sylvia_derive::contract;
 
-    #[contract(module=super)]
+    use crate::{MyQuery, SomeResponse};
+
+    #[contract(module=crate)]
+    #[messages(crate::some_interface as SomeInterface)]
     #[sv::custom(query=MyQuery)]
-    impl SomeInterface for crate::MyContract {
+    impl super::some_interface::SomeInterface for crate::MyContract {
         type Error = StdError;
 
         #[msg(query)]
@@ -98,10 +116,10 @@ mod some_interface {
 
 mod associated_type_interface {
     use cosmwasm_std::{CustomQuery, Response, StdError, StdResult};
+    use sylvia::interface;
     use sylvia::types::{ExecCtx, QueryCtx};
-    use sylvia::{contract, interface};
 
-    use crate::{MyQuery, SomeResponse};
+    use crate::SomeResponse;
 
     #[interface]
     pub trait AssociatedTypeInterface {
@@ -116,8 +134,16 @@ mod associated_type_interface {
         #[msg(exec)]
         fn associated_exec(&self, ctx: ExecCtx<Self::QueryC>) -> StdResult<Response>;
     }
+}
+
+mod impl_associated_type_interface {
+    use crate::{associated_type_interface::AssociatedTypeInterface, MyQuery, SomeResponse};
+    use cosmwasm_std::{Response, StdError, StdResult};
+    use sylvia::types::{ExecCtx, QueryCtx};
+    use sylvia_derive::contract;
 
-    #[contract(module=super)]
+    #[contract(module=crate)]
+    #[messages(crate::associated_type_interface as AssociatedTypeInterface)]
     impl AssociatedTypeInterface for crate::MyContract {
         type Error = StdError;
         type QueryC = MyQuery;
@@ -136,8 +162,8 @@ mod associated_type_interface {
 
 mod default_query_interface {
     use cosmwasm_std::{Response, StdError, StdResult};
+    use sylvia::interface;
     use sylvia::types::{ExecCtx, QueryCtx};
-    use sylvia::{contract, interface};
 
     use crate::SomeResponse;
 
@@ -153,8 +179,16 @@ mod default_query_interface {
         #[msg(exec)]
         fn default_exec(&self, ctx: ExecCtx) -> StdResult<Response>;
     }
+}
+
+mod impl_default_query_interface {
+    use crate::{default_query_interface::DefaultQueryInterface, SomeResponse};
+    use cosmwasm_std::{Response, StdError, StdResult};
+    use sylvia::types::{ExecCtx, QueryCtx};
+    use sylvia_derive::contract;
 
-    #[contract(module=super)]
+    #[contract(module=crate)]
+    #[messages(crate::default_query_interface as DefaultQueryInterface)]
     #[sv::custom(query=MyQuery)]
     impl DefaultQueryInterface for crate::MyContract {
         type Error = StdError;
@@ -207,10 +241,11 @@ impl MyContract {
 
 #[cfg(all(test, feature = "mt"))]
 mod tests {
-    use crate::associated_type_interface::test_utils::AssociatedTypeInterface;
-    use crate::default_query_interface::test_utils::DefaultQueryInterface;
-    use crate::some_interface::test_utils::SomeInterface;
-    use crate::{interface::test_utils::Interface, MyContract, MyQuery};
+    use crate::impl_associated_type_interface::sv::test_utils::AssociatedTypeInterface;
+    use crate::impl_default_query_interface::sv::test_utils::DefaultQueryInterface;
+    use crate::impl_interface::sv::test_utils::Interface;
+    use crate::impl_some_interface::sv::test_utils::SomeInterface;
+    use crate::{MyContract, MyQuery};
 
     use cosmwasm_std::Empty;
     use sylvia::multitest::App;
@@ -219,7 +254,7 @@ mod tests {
     fn test_custom() {
         let _ = MyContract::new();
         let app = App::<cw_multi_test::BasicApp<Empty, MyQuery>>::custom(|_, _, _| {});
-        let code_id = crate::multitest_utils::CodeId::store_code(&app);
+        let code_id = crate::sv::multitest_utils::CodeId::store_code(&app);
 
         let owner = "owner";
 
diff --git a/sylvia/tests/dispatching.rs b/sylvia/tests/dispatching.rs
index 6858b55..155354c 100644
--- a/sylvia/tests/dispatching.rs
+++ b/sylvia/tests/dispatching.rs
@@ -1,12 +1,20 @@
 use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
-use cosmwasm_std::{from_binary, Addr, Decimal, Response, StdError, StdResult};
-use interface::Interface;
+use cosmwasm_std::{from_binary, Addr, Decimal, Response};
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
-use std::cell::RefCell;
-use std::collections::HashMap;
-use sylvia::contract;
-use sylvia::types::{ExecCtx, QueryCtx};
+
+use crate::contract::Contract;
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub struct EmptyQueryResponse {}
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub struct QueryResponse {
+    coef: Decimal,
+    desc: String,
+}
 
 mod interface {
     use cosmwasm_std::{Addr, Decimal, Response, StdError};
@@ -40,77 +48,83 @@ mod interface {
     }
 }
 
-#[derive(Default)]
-pub struct Contract {
-    execs: RefCell<u64>,
-    queries: RefCell<u64>,
+mod impl_interface {
+    use cosmwasm_std::{Addr, Decimal, Response, StdError};
+    use sylvia::types::{ExecCtx, QueryCtx};
 
-    data: RefCell<HashMap<Addr, QueryResponse>>,
-}
+    use crate::{EmptyQueryResponse, QueryResponse};
 
-#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
-#[serde(rename_all = "snake_case")]
-pub struct EmptyQueryResponse {}
+    #[sylvia::contract(module = crate::contract)]
+    #[messages(crate::interface as Interface)]
+    impl crate::interface::Interface for crate::contract::Contract {
+        type Error = StdError;
 
-#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
-#[serde(rename_all = "snake_case")]
-pub struct QueryResponse {
-    coef: Decimal,
-    desc: String,
-}
-
-#[contract]
-#[messages(interface as Interface)]
-impl Interface for Contract {
-    type Error = StdError;
+        #[msg(exec)]
+        fn no_args_execution(&self, _: ExecCtx) -> Result<Response, StdError> {
+            *self.execs.borrow_mut() += 1;
+            Ok(Response::new())
+        }
 
-    #[msg(exec)]
-    fn no_args_execution(&self, _: ExecCtx) -> Result<Response, StdError> {
-        *self.execs.borrow_mut() += 1;
-        Ok(Response::new())
-    }
+        #[msg(exec)]
+        fn argumented_execution(
+            &self,
+            _: ExecCtx,
+            addr: Addr,
+            coef: Decimal,
+            desc: String,
+        ) -> Result<Response, Self::Error> {
+            *self.execs.borrow_mut() += 1;
 
-    #[msg(exec)]
-    fn argumented_execution(
-        &self,
-        _: ExecCtx,
-        addr: Addr,
-        coef: Decimal,
-        desc: String,
-    ) -> Result<Response, Self::Error> {
-        *self.execs.borrow_mut() += 1;
-
-        self.data
-            .borrow_mut()
-            .insert(addr, QueryResponse { coef, desc });
-        Ok(Response::new())
-    }
+            self.data
+                .borrow_mut()
+                .insert(addr, QueryResponse { coef, desc });
+            Ok(Response::new())
+        }
 
-    #[msg(query)]
-    fn no_args_query(&self, _: QueryCtx) -> Result<EmptyQueryResponse, StdError> {
-        *self.queries.borrow_mut() += 1;
-        Ok(dbg!(EmptyQueryResponse {}))
-    }
+        #[msg(query)]
+        fn no_args_query(&self, _: QueryCtx) -> Result<EmptyQueryResponse, StdError> {
+            *self.queries.borrow_mut() += 1;
+            Ok(dbg!(EmptyQueryResponse {}))
+        }
 
-    #[msg(query)]
-    fn argumented_query(&self, _: QueryCtx, user: Addr) -> Result<QueryResponse, Self::Error> {
-        *self.queries.borrow_mut() += 1;
-        Ok(self.data.borrow().get(&user).unwrap().clone())
+        #[msg(query)]
+        fn argumented_query(&self, _: QueryCtx, user: Addr) -> Result<QueryResponse, Self::Error> {
+            *self.queries.borrow_mut() += 1;
+            Ok(self.data.borrow().get(&user).unwrap().clone())
+        }
     }
 }
 
-#[allow(dead_code)]
-#[cfg(not(tarpaulin_include))]
-#[contract]
-#[messages(interface as Interface)]
-impl Contract {
-    fn new() -> Self {
-        Self::default()
+mod contract {
+    use std::{cell::RefCell, collections::HashMap};
+
+    use cosmwasm_std::{Addr, Response, StdResult};
+    use sylvia::types::ExecCtx;
+    use sylvia_derive::contract;
+
+    use crate::QueryResponse;
+
+    #[derive(Default)]
+    pub struct Contract {
+        pub(crate) execs: RefCell<u64>,
+        pub(crate) queries: RefCell<u64>,
+
+        pub(crate) data: RefCell<HashMap<Addr, QueryResponse>>,
     }
 
-    #[msg(instantiate)]
-    fn instanciate(&self, _: ExecCtx) -> StdResult<Response> {
-        Ok(Response::new())
+    #[allow(dead_code)]
+    #[cfg(not(tarpaulin_include))]
+    #[contract]
+    #[messages(crate::interface as Interface)]
+    impl Contract {
+        fn new() -> Self {
+            Self::default()
+        }
+
+        #[msg(instantiate)]
+        fn instanciate(&self, _: ExecCtx) -> StdResult<Response> {
+            Ok(Response::new())
+        }
     }
 }
 
@@ -122,12 +136,12 @@ fn dispatch() {
     let env = mock_env();
     let info = mock_info("owner", &[]);
 
-    let resp = interface::ExecMsg::NoArgsExecution {}
+    let resp = interface::sv::ExecMsg::NoArgsExecution {}
         .dispatch(&contract, (deps.as_mut(), env.clone(), info.clone()))
         .unwrap();
     assert_eq!(resp, Response::new());
 
-    let resp = interface::ExecMsg::ArgumentedExecution {
+    let resp = interface::sv::ExecMsg::ArgumentedExecution {
         addr: Addr::unchecked("addr1"),
         coef: Decimal::percent(30),
         desc: "True".to_owned(),
@@ -136,7 +150,7 @@ fn dispatch() {
     .unwrap();
     assert_eq!(resp, Response::new());
 
-    let resp = interface::ExecMsg::ArgumentedExecution {
+    let resp = interface::sv::ExecMsg::ArgumentedExecution {
         addr: Addr::unchecked("addr2"),
         coef: Decimal::percent(70),
         desc: "False".to_owned(),
@@ -145,12 +159,12 @@ fn dispatch() {
     .unwrap();
     assert_eq!(resp, Response::new());
 
-    let resp = interface::QueryMsg::NoArgsQuery {}
+    let resp = interface::sv::QueryMsg::NoArgsQuery {}
         .dispatch(&contract, (deps.as_ref(), env.clone()))
         .unwrap();
     let _resp: EmptyQueryResponse = from_binary(&resp).unwrap();
 
-    let resp = interface::QueryMsg::ArgumentedQuery {
+    let resp = interface::sv::QueryMsg::ArgumentedQuery {
         user: Addr::unchecked("addr2"),
     }
     .dispatch(&contract, (deps.as_ref(), env))
diff --git a/sylvia/tests/entry_points.rs b/sylvia/tests/entry_points.rs
index d7bbb2e..951dfee 100644
--- a/sylvia/tests/entry_points.rs
+++ b/sylvia/tests/entry_points.rs
@@ -1,4 +1,4 @@
-use contract::ContractExecMsg;
+use contract::sv::ContractExecMsg;
 use cosmwasm_schema::cw_serde;
 use cosmwasm_std::Coin;
 use cw_storage_plus::Item;
@@ -145,8 +145,8 @@ mod tests {
     use cw_multi_test::Executor;
     use sylvia::multitest::App;
 
-    use crate::contract::multitest_utils::CodeId;
-    use crate::contract::{ContractExecMsg, ExecMsg};
+    use crate::contract::sv::multitest_utils::CodeId;
+    use crate::contract::sv::{ContractExecMsg, ExecMsg};
     use crate::exec::{CustomExecMsg, UserExecMsg};
     use crate::SudoMsg;
 
diff --git a/sylvia/tests/messages_generation.rs b/sylvia/tests/messages_generation.rs
index 2495fc6..6fe0b94 100644
--- a/sylvia/tests/messages_generation.rs
+++ b/sylvia/tests/messages_generation.rs
@@ -112,53 +112,53 @@ mod contract {
 
 #[test]
 fn interface_messages_constructible() {
-    let no_args_exec = interface::ExecMsg::NoArgsExecution {};
-    let _argumented_exec = interface::ExecMsg::ArgumentedExecution {
+    let no_args_exec = interface::sv::ExecMsg::NoArgsExecution {};
+    let _argumented_exec = interface::sv::ExecMsg::ArgumentedExecution {
         addr: Addr::unchecked("owner"),
         coef: Decimal::percent(10),
         desc: "Some description".to_owned(),
     };
-    let no_args_query = interface::QueryMsg::NoArgsQuery {};
-    let _argumented_query = interface::QueryMsg::ArgumentedQuery {
+    let no_args_query = interface::sv::QueryMsg::NoArgsQuery {};
+    let _argumented_query = interface::sv::QueryMsg::ArgumentedQuery {
         user: Addr::unchecked("owner"),
     };
 
     // Ensure no extra variants are generated
     match no_args_exec {
-        interface::ExecMsg::NoArgsExecution {} => (),
-        interface::ExecMsg::ArgumentedExecution { .. } => (),
+        interface::sv::ExecMsg::NoArgsExecution {} => (),
+        interface::sv::ExecMsg::ArgumentedExecution { .. } => (),
     }
 
     match no_args_query {
-        interface::QueryMsg::NoArgsQuery {} => (),
-        interface::QueryMsg::ArgumentedQuery { .. } => (),
+        interface::sv::QueryMsg::NoArgsQuery {} => (),
+        interface::sv::QueryMsg::ArgumentedQuery { .. } => (),
     }
 }
 
 #[test]
 fn contract_messages_constructible() {
-    let no_args_exec = contract::ExecMsg::NoArgsExecution {};
-    let _argumented_exec = contract::ExecMsg::ArgumentedExecution {
+    let no_args_exec = contract::sv::ExecMsg::NoArgsExecution {};
+    let _argumented_exec = contract::sv::ExecMsg::ArgumentedExecution {
         _addr: Addr::unchecked("owner"),
         _coef: Decimal::percent(10),
         _desc: "Some description".to_owned(),
     };
-    let no_args_query = contract::QueryMsg::NoArgsQuery {};
-    let _argumented_query = contract::QueryMsg::ArgumentedQuery {
+    let no_args_query = contract::sv::QueryMsg::NoArgsQuery {};
+    let _argumented_query = contract::sv::QueryMsg::ArgumentedQuery {
         _user: Addr::unchecked("owner"),
     };
-    let _ = contract::InstantiateMsg {};
-    let _ = contract::MigrateMsg {};
+    let _ = contract::sv::InstantiateMsg {};
+    let _ = contract::sv::MigrateMsg {};
 
     // Ensure no extra variants are generated
     match no_args_exec {
-        contract::ExecMsg::NoArgsExecution {} => (),
-        contract::ExecMsg::ArgumentedExecution { .. } => (),
+        contract::sv::ExecMsg::NoArgsExecution {} => (),
+        contract::sv::ExecMsg::ArgumentedExecution { .. } => (),
     }
 
     match no_args_query {
-        contract::QueryMsg::NoArgsQuery {} => (),
-        contract::QueryMsg::ArgumentedQuery { .. } => (),
+        contract::sv::QueryMsg::NoArgsQuery {} => (),
+        contract::sv::QueryMsg::ArgumentedQuery { .. } => (),
     }
 }
 
diff --git a/sylvia/tests/querier.rs b/sylvia/tests/querier.rs
index 191e880..9a78ed3 100644
--- a/sylvia/tests/querier.rs
+++ b/sylvia/tests/querier.rs
@@ -14,8 +14,8 @@ pub struct CountResponse {
 
 pub mod counter {
     use cosmwasm_std::{Response, StdError, StdResult};
+    use sylvia::interface;
     use sylvia::types::{ExecCtx, QueryCtx};
-    use sylvia::{contract, interface};
 
     use crate::CountResponse;
 
@@ -35,8 +35,17 @@ pub mod counter {
         #[msg(exec)]
         fn decrease_by_count(&self, ctx: ExecCtx) -> StdResult<Response>;
     }
+}
 
-    #[contract(module=super)]
+pub mod impl_counter {
+    use crate::counter::sv::Querier;
+    use crate::counter::Counter;
+    use crate::CountResponse;
+    use cosmwasm_std::{Response, StdError, StdResult};
+    use sylvia::contract;
+    use sylvia::types::{ExecCtx, QueryCtx};
+
+    #[contract(module=crate)]
     #[messages(crate::counter as Counter)]
     impl Counter for super::CounterContract<'_> {
         type Error = StdError;
@@ -69,9 +78,10 @@ pub mod counter {
         #[msg(exec)]
         fn decrease_by_count(&self, ctx: ExecCtx) -> StdResult<Response> {
             let remote = self.remote.load(ctx.deps.storage)?;
-            let other_count = BoundQuerier::borrowed(&remote.0, &ctx.deps.querier)
-                .count()?
-                .count;
+            let other_count =
+                crate::counter::sv::BoundQuerier::borrowed(remote.as_ref(), &ctx.deps.querier)
+                    .count()?
+                    .count;
             self.count.update(ctx.deps.storage, |count| {
                 let count = count.saturating_sub(other_count);
                 Ok::<_, StdError>(count)
@@ -84,7 +94,7 @@ pub mod counter {
 
 pub struct CounterContract<'a> {
     pub count: Item<'static, u64>,
-    pub remote: Item<'static, Remote<'a>>,
+    pub remote: Item<'static, sv::Remote<'a>>,
 }
 
 #[contract]
@@ -102,7 +112,7 @@ impl CounterContract<'_> {
     fn instantiate(&self, ctx: InstantiateCtx, remote_addr: Addr) -> StdResult<Response> {
         self.count.save(ctx.deps.storage, &0)?;
         self.remote
-            .save(ctx.deps.storage, &Remote::new(remote_addr))?;
+            .save(ctx.deps.storage, &sv::Remote::new(remote_addr))?;
         Ok(Response::new())
     }
 }
@@ -114,8 +124,8 @@ mod tests {
     use cosmwasm_std::{Addr, Empty, QuerierWrapper};
     use sylvia::multitest::App;
 
-    use crate::counter::test_utils::Counter;
-    use crate::multitest_utils::CodeId;
+    use crate::impl_counter::sv::test_utils::Counter;
+    use crate::sv::multitest_utils::CodeId;
 
     #[test]
     fn querier_generation() {
@@ -124,16 +134,16 @@ mod tests {
         let remote_addr = Addr::unchecked("remote");
 
         // Remote generation
-        let remote = super::counter::Remote::new(remote_addr.clone());
-        let _: super::counter::BoundQuerier<_> = remote.querier(&querier_wrapper);
-        let remote = super::Remote::new(remote_addr.clone());
-        let _: super::BoundQuerier<_> = remote.querier(&querier_wrapper);
+        let remote = super::counter::sv::Remote::new(remote_addr.clone());
+        let _: super::counter::sv::BoundQuerier<_> = remote.querier(&querier_wrapper);
+        let remote = super::sv::Remote::new(remote_addr.clone());
+        let _: super::sv::BoundQuerier<_> = remote.querier(&querier_wrapper);
 
         // Querier generation
-        let _ = super::counter::BoundQuerier::borrowed(&remote_addr, &querier_wrapper);
-        let querier = super::BoundQuerier::borrowed(&remote_addr, &querier_wrapper);
+        let _ = super::counter::sv::BoundQuerier::borrowed(&remote_addr, &querier_wrapper);
+        let querier = super::sv::BoundQuerier::borrowed(&remote_addr, &querier_wrapper);
 
-        let _ = super::counter::BoundQuerier::from(&querier);
+        let _ = super::counter::sv::BoundQuerier::from(&querier);
     }
 
     #[test]
diff --git a/sylvia/tests/query_returns.rs b/sylvia/tests/query_returns.rs
index f4dd6d0..b85dd6a 100644
--- a/sylvia/tests/query_returns.rs
+++ b/sylvia/tests/query_returns.rs
@@ -62,14 +62,14 @@ mod tests {
 
     #[test]
     fn generate_interface_query() {
-        let _ = msg::InterfaceQueryMsg::Query {
+        let _ = msg::sv::InterfaceQueryMsg::Query {
             name: "some_name".to_owned(),
         };
     }
 
     #[test]
     fn generate_contract_query() {
-        let _ = QueryMsg::ContractQuery {
+        let _ = sv::QueryMsg::ContractQuery {
             _name: "some_name".to_owned(),
         };
     }
diff --git a/sylvia/tests/remote.rs b/sylvia/tests/remote.rs
index 0b61719..4c93708 100644
--- a/sylvia/tests/remote.rs
+++ b/sylvia/tests/remote.rs
@@ -37,7 +37,7 @@ impl SomeContract {
 #[cw_serde]
 #[allow(dead_code)]
 struct CustomStorage {
-    remote: crate::Remote<'static>,
+    remote: crate::sv::Remote<'static>,
 }
 
 #[cfg(test)]
@@ -49,17 +49,17 @@ mod tests {
     #[test]
     fn remote_generation() {
         // interface
-        let _ = some_interface::Remote::new(Addr::unchecked("some_interface"));
+        let _ = some_interface::sv::Remote::new(Addr::unchecked("some_interface"));
         let addr = Addr::unchecked("some_interface");
-        let _ = some_interface::Remote::borrowed(&addr);
+        let _ = some_interface::sv::Remote::borrowed(&addr);
 
         // contract
-        let new_remote = crate::Remote::new(Addr::unchecked("some_contract"));
+        let new_remote = crate::sv::Remote::new(Addr::unchecked("some_contract"));
         let addr = Addr::unchecked("some_contract");
-        let borrowed_remote = crate::Remote::borrowed(&addr);
+        let borrowed_remote = crate::sv::Remote::borrowed(&addr);
         assert_eq!(&Addr::unchecked("some_contract"), borrowed_remote.as_ref());
 
-        let _ = some_interface::Remote::from(&borrowed_remote);
-        let _ = some_interface::Remote::from(&new_remote);
+        let _ = some_interface::sv::Remote::from(&borrowed_remote);
+        let _ = some_interface::sv::Remote::from(&new_remote);
     }
 }
diff --git a/sylvia/tests/replies.rs b/sylvia/tests/replies.rs
index a7bf502..979db3c 100644
--- a/sylvia/tests/replies.rs
+++ b/sylvia/tests/replies.rs
@@ -55,7 +55,7 @@ mod reply_contract {
 
         #[msg(exec)]
         fn poke(&self, _ctx: ExecCtx, noop: String) -> StdResult<Response> {
-            let msg = noop_contract::ExecMsg::Noop {};
+            let msg = noop_contract::sv::ExecMsg::Noop {};
             let msg = WasmMsg::Execute {
                 contract_addr: noop,
                 msg: to_binary(&msg)?,
@@ -93,16 +93,16 @@ fn entry_point_generation() {
     assert_eq!(data, "data");
 }
 
-#[cfg(feature = "mt")]
+#[cfg(all(test, feature = "mt"))]
 #[test]
 fn mt_helper_generation() {
     let app = sylvia::multitest::App::default();
     let owner = "owner";
 
-    let noop_contract_code = noop_contract::multitest_utils::CodeId::store_code(&app);
+    let noop_contract_code = noop_contract::sv::multitest_utils::CodeId::store_code(&app);
     let noop_contract = noop_contract_code.instantiate().call(owner).unwrap();
 
-    let reply_contract_code = reply_contract::multitest_utils::CodeId::store_code(&app);
+    let reply_contract_code = reply_contract::sv::multitest_utils::CodeId::store_code(&app);
     let reply_contract = reply_contract_code.instantiate().call(owner).unwrap();
 
     let resp = reply_contract