Repository: CosmWasm/sylvia
Pull request #403 (2024-07-22 13:16:47)

feat: Deduce generic custom types in entry_points macro

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/CosmWasm/sylvia/pull/403?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) Report
Attention: Patch coverage is `17.64706%` with `14 lines` in your changes missing coverage. Please review.
> Project coverage is 85.14%. Comparing base [(`e8ad3f5`)](https://app.codecov.io/gh/CosmWasm/sylvia/commit/e8ad3f5d4e14928c70d0c10eb7f3bd592ccea672?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) to head [(`caf1ce3`)](https://app.codecov.io/gh/CosmWasm/sylvia/commit/caf1ce3481f399f78837fe9abfc073d8bad13ad3?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm).

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

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


```diff
@@            Coverage Diff             @@
##             main     #403      +/-   ##
==========================================
+ Coverage   84.83%   85.14%   +0.30%     
==========================================
  Files          41       41              
  Lines        2921     2914       -7     
==========================================
+ Hits         2478     2481       +3     
+ Misses        443      433      -10     
```

</details>

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


Patch:
diff --git a/examples/contracts/cw1-subkeys/src/contract.rs b/examples/contracts/cw1-subkeys/src/contract.rs
index 131c887..f25192c 100644
--- a/examples/contracts/cw1-subkeys/src/contract.rs
+++ b/examples/contracts/cw1-subkeys/src/contract.rs
@@ -25,10 +25,10 @@ pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
 const MAX_LIMIT: u32 = 30;
 const DEFAULT_LIMIT: u32 = 10;
 
-pub struct Cw1SubkeysContract<'a> {
-    pub(crate) whitelist: Cw1WhitelistContract<'a>,
-    pub(crate) permissions: Map<&'a Addr, Permissions>,
-    pub(crate) allowances: Map<&'a Addr, Allowance>,
+pub struct Cw1SubkeysContract {
+    pub(crate) whitelist: Cw1WhitelistContract,
+    pub(crate) permissions: Map<&'static Addr, Permissions>,
+    pub(crate) allowances: Map<&'static Addr, Allowance>,
 }
 
 #[cfg_attr(not(feature = "library"), entry_points)]
@@ -36,7 +36,7 @@ pub struct Cw1SubkeysContract<'a> {
 #[sv::error(ContractError)]
 #[sv::messages(cw1 as Cw1)]
 #[sv::messages(whitelist as Whitelist)]
-impl<'abcd> Cw1SubkeysContract<'abcd> {
+impl Cw1SubkeysContract {
     pub const fn new() -> Self {
         Self {
             whitelist: Cw1WhitelistContract::new(),
diff --git a/examples/contracts/cw1-subkeys/src/cw1.rs b/examples/contracts/cw1-subkeys/src/cw1.rs
index d8a1b06..a349deb 100644
--- a/examples/contracts/cw1-subkeys/src/cw1.rs
+++ b/examples/contracts/cw1-subkeys/src/cw1.rs
@@ -5,7 +5,7 @@ use sylvia::types::{ExecCtx, QueryCtx};
 use crate::contract::Cw1SubkeysContract;
 use crate::error::ContractError;
 
-impl Cw1 for Cw1SubkeysContract<'_> {
+impl Cw1 for Cw1SubkeysContract {
     type Error = ContractError;
 
     fn execute(
diff --git a/examples/contracts/cw1-subkeys/src/whitelist.rs b/examples/contracts/cw1-subkeys/src/whitelist.rs
index 7a67b93..0817282 100644
--- a/examples/contracts/cw1-subkeys/src/whitelist.rs
+++ b/examples/contracts/cw1-subkeys/src/whitelist.rs
@@ -6,7 +6,7 @@ use whitelist::Whitelist;
 use crate::contract::Cw1SubkeysContract;
 use crate::error::ContractError;
 
-impl Whitelist for Cw1SubkeysContract<'_> {
+impl Whitelist for Cw1SubkeysContract {
     type Error = ContractError;
 
     fn freeze(&self, ctx: ExecCtx) -> Result<Response, Self::Error> {
diff --git a/examples/contracts/cw1-whitelist/src/contract.rs b/examples/contracts/cw1-whitelist/src/contract.rs
index 123e899..8797c0d 100644
--- a/examples/contracts/cw1-whitelist/src/contract.rs
+++ b/examples/contracts/cw1-whitelist/src/contract.rs
@@ -12,8 +12,8 @@ use sylvia::entry_points;
 const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME");
 const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
 
-pub struct Cw1WhitelistContract<'a> {
-    pub(crate) admins: Map<&'a Addr, Empty>,
+pub struct Cw1WhitelistContract {
+    pub(crate) admins: Map<&'static Addr, Empty>,
     pub(crate) mutable: Item<bool>,
 }
 
@@ -22,7 +22,7 @@ pub struct Cw1WhitelistContract<'a> {
 #[sv::error(ContractError)]
 #[sv::messages(cw1 as Cw1)]
 #[sv::messages(whitelist as Whitelist)]
-impl<'a> Cw1WhitelistContract<'a> {
+impl Cw1WhitelistContract {
     pub const fn new() -> Self {
         Self {
             admins: Map::new("admins"),
diff --git a/examples/contracts/cw1-whitelist/src/cw1.rs b/examples/contracts/cw1-whitelist/src/cw1.rs
index 4dd3b55..0c94b65 100644
--- a/examples/contracts/cw1-whitelist/src/cw1.rs
+++ b/examples/contracts/cw1-whitelist/src/cw1.rs
@@ -5,7 +5,7 @@ use sylvia::types::{ExecCtx, QueryCtx};
 use crate::contract::Cw1WhitelistContract;
 use crate::error::ContractError;
 
-impl Cw1 for Cw1WhitelistContract<'_> {
+impl Cw1 for Cw1WhitelistContract {
     type Error = ContractError;
 
     fn execute(&self, ctx: ExecCtx, msgs: Vec<CosmosMsg>) -> Result<Response, ContractError> {
diff --git a/examples/contracts/cw1-whitelist/src/whitelist.rs b/examples/contracts/cw1-whitelist/src/whitelist.rs
index d96f549..0d9d224 100644
--- a/examples/contracts/cw1-whitelist/src/whitelist.rs
+++ b/examples/contracts/cw1-whitelist/src/whitelist.rs
@@ -6,7 +6,7 @@ use whitelist::Whitelist;
 use crate::contract::Cw1WhitelistContract;
 use crate::error::ContractError;
 
-impl Whitelist for Cw1WhitelistContract<'_> {
+impl Whitelist for Cw1WhitelistContract {
     type Error = ContractError;
 
     fn freeze(&self, ctx: ExecCtx) -> Result<Response, ContractError> {
diff --git a/examples/contracts/cw20-base/src/allowances.rs b/examples/contracts/cw20-base/src/allowances.rs
index 15ac5f8..deb2d4d 100644
--- a/examples/contracts/cw20-base/src/allowances.rs
+++ b/examples/contracts/cw20-base/src/allowances.rs
@@ -16,7 +16,7 @@ use crate::responses::Cw20ReceiveMsg;
 const MAX_LIMIT: u32 = 30;
 const DEFAULT_LIMIT: u32 = 10;
 
-impl Cw20Allowances for Cw20Base<'_> {
+impl Cw20Allowances for Cw20Base {
     type Error = ContractError;
 
     /// Allows spender to access an additional amount tokens from the owner's (env.sender) account.
diff --git a/examples/contracts/cw20-base/src/contract.rs b/examples/contracts/cw20-base/src/contract.rs
index c594fe2..35d57f9 100644
--- a/examples/contracts/cw20-base/src/contract.rs
+++ b/examples/contracts/cw20-base/src/contract.rs
@@ -62,14 +62,14 @@ pub struct InstantiateMsgData {
     pub marketing: Option<InstantiateMarketingInfo>,
 }
 
-pub struct Cw20Base<'a> {
+pub struct Cw20Base {
     pub(crate) token_info: Item<TokenInfo>,
     pub(crate) marketing_info: Item<MarketingInfoResponse>,
     pub(crate) logo: Item<Logo>,
-    pub(crate) balances: Map<&'a Addr, Uint128>,
-    pub(crate) allowances: Map<(&'a Addr, &'a Addr), AllowanceResponse>,
+    pub(crate) balances: Map<&'static Addr, Uint128>,
+    pub(crate) allowances: Map<(&'static Addr, &'static Addr), AllowanceResponse>,
     // TODO: After https://github.com/CosmWasm/cw-plus/issues/670 is implemented, replace this with a `MultiIndex` over `ALLOWANCES`
-    pub(crate) allowances_spender: Map<(&'a Addr, &'a Addr), AllowanceResponse>,
+    pub(crate) allowances_spender: Map<(&'static Addr, &'static Addr), AllowanceResponse>,
 }
 
 #[cfg_attr(not(feature = "library"), entry_points)]
@@ -78,7 +78,7 @@ pub struct Cw20Base<'a> {
 #[sv::messages(cw20_allowances as Allowances)]
 #[sv::messages(cw20_marketing as Marketing)]
 #[sv::messages(cw20_minting as Minting)]
-impl<'abcd> Cw20Base<'abcd> {
+impl Cw20Base {
     pub const fn new() -> Self {
         Self {
             token_info: Item::new("token_info"),
diff --git a/examples/contracts/cw20-base/src/marketing.rs b/examples/contracts/cw20-base/src/marketing.rs
index 845c1c2..ed95836 100644
--- a/examples/contracts/cw20-base/src/marketing.rs
+++ b/examples/contracts/cw20-base/src/marketing.rs
@@ -6,7 +6,7 @@ use cw20_marketing::responses::{DownloadLogoResponse, LogoInfo, MarketingInfoRes
 use cw20_marketing::{Cw20Marketing, EmbeddedLogo, Logo};
 use sylvia::types::{ExecCtx, QueryCtx};
 
-impl Cw20Marketing for Cw20Base<'_> {
+impl Cw20Marketing for Cw20Base {
     type Error = ContractError;
 
     fn update_marketing(
diff --git a/examples/contracts/cw20-base/src/minting.rs b/examples/contracts/cw20-base/src/minting.rs
index f13585c..a6e4e41 100644
--- a/examples/contracts/cw20-base/src/minting.rs
+++ b/examples/contracts/cw20-base/src/minting.rs
@@ -5,7 +5,7 @@ use cw20_minting::responses::MinterResponse;
 use cw20_minting::Cw20Minting;
 use sylvia::types::{ExecCtx, QueryCtx};
 
-impl Cw20Minting for Cw20Base<'_> {
+impl Cw20Minting for Cw20Base {
     type Error = ContractError;
 
     fn mint(
diff --git a/examples/contracts/generics_forwarded/src/contract.rs b/examples/contracts/generics_forwarded/src/contract.rs
index 945ff61..f6ac52d 100644
--- a/examples/contracts/generics_forwarded/src/contract.rs
+++ b/examples/contracts/generics_forwarded/src/contract.rs
@@ -50,7 +50,7 @@ pub struct GenericsForwardedContract<
     )>,
 }
 
-#[cfg_attr(not(feature = "library"), sylvia::entry_points(generics<SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomQuery, String>, custom(msg=SvCustomMsg, query=SvCustomQuery)))]
+#[cfg_attr(not(feature = "library"), sylvia::entry_points(generics<SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomQuery, String>))]
 #[contract]
 #[sv::error(ContractError)]
 #[sv::messages(generic as Generic: custom(msg, query))]
diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs
index e2a3397..1259887 100644
--- a/sylvia-derive/src/message.rs
+++ b/sylvia-derive/src/message.rs
@@ -1175,6 +1175,7 @@ impl<'a> ContractApi<'a> {
         } else {
             quote! { type Migrate = #sylvia ::cw_std::Empty; }
         };
+        let custom_msg = custom.msg_or_default();
         let custom_query = custom.query_or_default();
 
         quote! {
@@ -1189,6 +1190,8 @@ impl<'a> ContractApi<'a> {
                 #migrate_type
                 type Remote<'remote> = #sylvia ::types::Remote<'remote, Self >;
                 type Querier<'querier> = #sylvia ::types::BoundQuerier<'querier, #custom_query, Self >;
+                type CustomMsg = #custom_msg;
+                type CustomQuery = #custom_query;
             }
         }
     }
@@ -1293,7 +1296,6 @@ pub struct EntryPoints<'a> {
     source: &'a ItemImpl,
     name: Type,
     error: Type,
-    custom: Custom,
     override_entry_points: Vec<OverrideEntryPoint>,
     generics: Vec<&'a GenericParam>,
     where_clause: &'a Option<WhereClause>,
@@ -1310,13 +1312,11 @@ impl<'a> EntryPoints<'a> {
 
         let generics: Vec<_> = source.generics.params.iter().collect();
         let where_clause = &source.generics.where_clause;
-        let custom = parsed_attrs.custom_attr.unwrap_or_default();
 
         Self {
             source,
             name,
             error,
-            custom,
             override_entry_points,
             generics,
             where_clause,
@@ -1329,7 +1329,6 @@ impl<'a> EntryPoints<'a> {
             source,
             name,
             error,
-            custom,
             override_entry_points,
             generics,
             where_clause,
@@ -1337,13 +1336,17 @@ impl<'a> EntryPoints<'a> {
         } = self;
         let sylvia = crate_module();
 
-        let custom = match &attrs.custom {
-            Some(custom) => custom,
-            None => custom,
-        };
+        let bracketed_generics = attrs
+            .generics
+            .as_ref()
+            .map(|generics| match generics.is_empty() {
+                true => quote! {},
+                false => quote! { < #generics > },
+            })
+            .unwrap_or(quote! {});
 
-        let custom_msg = custom.msg_or_default();
-        let custom_query = custom.query_or_default();
+        let custom_msg = parse_quote! { < #name #bracketed_generics as #sylvia ::types::ContractApi > :: CustomMsg };
+        let custom_query = parse_quote! { < #name #bracketed_generics as #sylvia ::types::ContractApi > :: CustomQuery };
 
         let instantiate_variants = MsgVariants::new(
             source.as_variants(),
diff --git a/sylvia-derive/src/parser/entry_point.rs b/sylvia-derive/src/parser/entry_point.rs
index 8c32b56..231bb0b 100644
--- a/sylvia-derive/src/parser/entry_point.rs
+++ b/sylvia-derive/src/parser/entry_point.rs
@@ -1,9 +1,8 @@
-use syn::parse::{Error, Nothing, Parse, ParseStream, Parser};
+use syn::parse::{Error, Nothing, Parse, ParseStream};
 use syn::punctuated::Punctuated;
 use syn::spanned::Spanned;
-use syn::{parenthesized, GenericArgument, Path, Result, Token};
+use syn::{GenericArgument, Path, Result, Token};
 
-use super::attributes::custom::Custom;
 use super::extract_generics_from_path;
 
 /// Parsed arguments for `entry_points` macro
@@ -11,8 +10,6 @@ use super::extract_generics_from_path;
 pub struct EntryPointArgs {
     /// Types used in place of contracts generics.
     pub generics: Option<Punctuated<GenericArgument, Token![,]>>,
-    /// Concrete custom msg/query used in place of contracts generic ones.
-    pub custom: Option<Custom>,
 }
 
 impl Parse for EntryPointArgs {
@@ -30,25 +27,6 @@ impl Parse for EntryPointArgs {
             _ => return Err(Error::new(generics.span(), "Expected `generics`.")),
         };
 
-        let comma: Option<Token![,]> = input.parse().ok();
-        if comma.is_none() {
-            return Ok(entry_points_args);
-        }
-
-        let custom: Option<Path> = input.parse().ok();
-        match custom {
-            Some(custom)
-                if custom.get_ident().map(|custom| custom.to_string())
-                    == Some("custom".to_owned()) =>
-            {
-                let content;
-                parenthesized!(content in input);
-                entry_points_args.custom = Some(Custom::parse.parse2(content.parse()?)?);
-            }
-            Some(attr) => return Err(Error::new(attr.span(), "Expected `custom`.")),
-            _ => (),
-        };
-
         let _: Nothing = input.parse()?;
 
         Ok(entry_points_args)
diff --git a/sylvia/src/types.rs b/sylvia/src/types.rs
index 45cc70c..c4f7ec1 100644
--- a/sylvia/src/types.rs
+++ b/sylvia/src/types.rs
@@ -582,4 +582,6 @@ pub trait ContractApi {
     type Sudo;
     type Querier<'querier>;
     type Remote<'remote>;
+    type CustomMsg: CustomMsg;
+    type CustomQuery: CustomQuery;
 }


Test patch:
diff --git a/sylvia/tests/api.rs b/sylvia/tests/api.rs
index ba2b3eb..5e44151 100644
--- a/sylvia/tests/api.rs
+++ b/sylvia/tests/api.rs
@@ -1,21 +1,30 @@
 use cosmwasm_std::{Response, StdResult};
 use std::marker::PhantomData;
 
-use sylvia::types::{CustomMsg, ExecCtx, InstantiateCtx, MigrateCtx, QueryCtx};
+use sylvia::types::{
+    CustomMsg, CustomQuery, ExecCtx, InstantiateCtx, MigrateCtx, QueryCtx, SudoCtx,
+};
 use sylvia_derive::contract;
 
-pub struct SomeContract<Instantiate, Query, Exec, Migrate, Ret> {
-    _phantom: PhantomData<(Instantiate, Query, Exec, Migrate, Ret)>,
+pub struct SomeContract<Instantiate, Query, Exec, Migrate, Sudo, Ret, CMsg, CQuery> {
+    #[allow(clippy::complexity)]
+    _phantom: PhantomData<(Instantiate, Query, Exec, Migrate, Sudo, Ret, CMsg, CQuery)>,
 }
 
+#[allow(dead_code)]
 #[contract]
-impl<Instantiate, Query, Exec, Migrate, Ret> SomeContract<Instantiate, Query, Exec, Migrate, Ret>
+#[sv::custom(msg=CMsg, query=CQuery)]
+impl<Instantiate, Query, Exec, Migrate, Sudo, Ret, CMsg, CQuery>
+    SomeContract<Instantiate, Query, Exec, Migrate, Sudo, Ret, CMsg, CQuery>
 where
     Instantiate: CustomMsg + 'static,
     Query: CustomMsg + 'static,
     Exec: CustomMsg + 'static,
     Migrate: CustomMsg + 'static,
+    Sudo: CustomMsg + 'static,
     Ret: CustomMsg + 'static,
+    CMsg: CustomMsg + 'static,
+    CQuery: CustomQuery + 'static,
 {
     pub const fn new() -> Self {
         Self {
@@ -24,22 +33,31 @@ where
     }
 
     #[sv::msg(instantiate)]
-    pub fn instantiate(&self, _ctx: InstantiateCtx, _param: Instantiate) -> StdResult<Response> {
+    pub fn instantiate(
+        &self,
+        _ctx: InstantiateCtx<CQuery>,
+        _param: Instantiate,
+    ) -> StdResult<Response<CMsg>> {
         Ok(Response::new())
     }
 
     #[sv::msg(exec)]
-    pub fn exec(&self, _ctx: ExecCtx, _param: Exec) -> StdResult<Response> {
+    pub fn exec(&self, _ctx: ExecCtx<CQuery>, _param: Exec) -> StdResult<Response<CMsg>> {
         Ok(Response::new())
     }
 
     #[sv::msg(query)]
-    pub fn query(&self, _ctx: QueryCtx, _param: Query) -> StdResult<Response> {
+    pub fn query(&self, _ctx: QueryCtx<CQuery>, _param: Query) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    #[sv::msg(sudo)]
+    pub fn sudo(&self, _ctx: SudoCtx<CQuery>, _param: Sudo) -> StdResult<Response<CMsg>> {
         Ok(Response::new())
     }
 
     #[sv::msg(migrate)]
-    pub fn migrate(&self, _ctx: MigrateCtx, _param: Migrate) -> StdResult<Response> {
+    pub fn migrate(&self, _ctx: MigrateCtx<CQuery>, _param: Migrate) -> StdResult<Response<CMsg>> {
         Ok(Response::new())
     }
 }
@@ -47,86 +65,66 @@ where
 #[cfg(test)]
 mod tests {
     use crate::SomeContract;
+    use cosmwasm_schema::cw_serde;
     use cosmwasm_std::testing::mock_dependencies;
     use cosmwasm_std::{Addr, QuerierWrapper};
     use sylvia::types::ContractApi;
 
-    #[cosmwasm_schema::cw_serde]
+    #[cw_serde]
     pub struct SvCustomMsg;
     impl cosmwasm_std::CustomMsg for SvCustomMsg {}
+    #[cw_serde]
+    pub struct SvCustomQuery;
+    impl cosmwasm_std::CustomQuery for SvCustomQuery {}
+
+    pub type ConcreteContract = SomeContract<
+        SvCustomMsg,
+        SvCustomMsg,
+        SvCustomMsg,
+        SvCustomMsg,
+        SvCustomMsg,
+        SvCustomMsg,
+        SvCustomMsg,
+        SvCustomQuery,
+    >;
 
     #[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<_, _, _, _, _> = <SomeContract<
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-        > as ContractApi>::ContractExec::SomeContract(
-            exec
-        );
-
-        let _: crate::sv::ContractQueryMsg<_, _, _, _, _> = <SomeContract<
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-        > as ContractApi>::ContractQuery::SomeContract(
-            query
-        );
-
-        let _: sylvia::types::Remote<'_, _> = <SomeContract<
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-        > as ContractApi>::Remote::new(owner.clone());
+        // Messages
+        let _: crate::sv::InstantiateMsg<SvCustomMsg> =
+            <ConcreteContract as ContractApi>::Instantiate::new(SvCustomMsg);
+
+        let exec: crate::sv::ExecMsg<SvCustomMsg> =
+            <ConcreteContract as ContractApi>::Exec::exec(SvCustomMsg);
+
+        let query: crate::sv::QueryMsg<SvCustomMsg> =
+            <ConcreteContract as ContractApi>::Query::query(SvCustomMsg);
+
+        let sudo: crate::sv::SudoMsg<SvCustomMsg> =
+            <ConcreteContract as ContractApi>::Sudo::sudo(SvCustomMsg);
+
+        let _: crate::sv::ContractExecMsg<_, _, _, _, _, _, _, _> =
+            <ConcreteContract as ContractApi>::ContractExec::SomeContract(exec);
+
+        let _: crate::sv::ContractQueryMsg<_, _, _, _, _, _, _, _> =
+            <ConcreteContract as ContractApi>::ContractQuery::SomeContract(query);
+
+        let _: crate::sv::ContractSudoMsg<_, _, _, _, _, _, _, _> =
+            <ConcreteContract as ContractApi>::ContractSudo::SomeContract(sudo);
+
+        // Communication
+        let _: sylvia::types::Remote<'_, _> =
+            <ConcreteContract as ContractApi>::Remote::new(owner.clone());
 
         let deps = mock_dependencies();
-        let querier_wrapper: QuerierWrapper = QuerierWrapper::new(&deps.querier);
-        let _: sylvia::types::BoundQuerier<'_, cosmwasm_std::Empty, _> = <SomeContract<
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-        > as ContractApi>::Querier::borrowed(
-            &owner, &querier_wrapper
-        );
+        let querier_wrapper = QuerierWrapper::new(&deps.querier);
+        let _: sylvia::types::BoundQuerier<'_, SvCustomQuery, _> =
+            <ConcreteContract as ContractApi>::Querier::borrowed(&owner, &querier_wrapper);
+
+        // Customs
+        let _: <ConcreteContract as ContractApi>::CustomMsg = SvCustomMsg {};
+        let _: <ConcreteContract as ContractApi>::CustomQuery = SvCustomQuery {};
     }
 }
diff --git a/sylvia/tests/custom_msg.rs b/sylvia/tests/custom_msg.rs
index dffc02d..3d00c61 100644
--- a/sylvia/tests/custom_msg.rs
+++ b/sylvia/tests/custom_msg.rs
@@ -1,8 +1,8 @@
 use cosmwasm_std::{CustomMsg, Response, StdResult};
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
-use sylvia::contract;
 use sylvia::types::{ExecCtx, InstantiateCtx, MigrateCtx, QueryCtx, SudoCtx};
+use sylvia::{contract, entry_points};
 
 #[derive(Clone, PartialEq, Serialize, Deserialize, Debug, JsonSchema)]
 pub struct MyMsg;
@@ -201,6 +201,7 @@ mod impl_associated_interface {
     }
 }
 
+#[entry_points]
 #[contract]
 #[sv::messages(some_interface)]
 #[sv::messages(other_interface: custom(msg))]
diff --git a/sylvia/tests/custom_query.rs b/sylvia/tests/custom_query.rs
index 8d9113b..53a9b20 100644
--- a/sylvia/tests/custom_query.rs
+++ b/sylvia/tests/custom_query.rs
@@ -1,7 +1,7 @@
 use cosmwasm_schema::cw_serde;
 use cosmwasm_std::{CustomQuery, Response, StdResult};
-use sylvia::contract;
 use sylvia::types::{ExecCtx, InstantiateCtx, MigrateCtx, QueryCtx, SudoCtx};
+use sylvia::{contract, entry_points};
 
 #[cw_serde]
 pub struct MyQuery;
@@ -206,6 +206,7 @@ mod impl_default_query_interface {
     }
 }
 
+#[entry_points]
 #[contract]
 #[sv::messages(some_interface)]
 #[sv::messages(associated_type_interface)]
diff --git a/sylvia/tests/entry_points.rs b/sylvia/tests/entry_points.rs
index c65a26b..27332b0 100644
--- a/sylvia/tests/entry_points.rs
+++ b/sylvia/tests/entry_points.rs
@@ -135,8 +135,8 @@ pub mod entry_points {
 mod contract {
     use cosmwasm_std::{Response, StdError, StdResult};
     use cw_storage_plus::Item;
-    use sylvia::contract;
     use sylvia::types::{ExecCtx, InstantiateCtx, MigrateCtx, QueryCtx, SudoCtx};
+    use sylvia::{contract, entry_points};
 
     use crate::CountResponse;
 
@@ -146,6 +146,7 @@ mod contract {
         pub(crate) migrates: Item<u32>,
     }
 
+    #[entry_points]
     #[contract]
     #[sv::override_entry_point(sudo=crate::entry_points::sudo(crate::sudo::SudoWrapperMsg))]
     #[sv::override_entry_point(migrate=crate::entry_points::migrate(crate::migrate::MigrateMsg))]
diff --git a/sylvia/tests/executor.rs b/sylvia/tests/executor.rs
index fa7687a..712bdc7 100644
--- a/sylvia/tests/executor.rs
+++ b/sylvia/tests/executor.rs
@@ -4,8 +4,8 @@ use cosmwasm_std::{Addr, Response, StdResult};
 use cw_storage_plus::Item;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
-use sylvia::contract;
 use sylvia::types::InstantiateCtx;
+use sylvia::{contract, entry_points};
 
 #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug, Default)]
 pub struct CountResponse {
@@ -38,7 +38,7 @@ use counter::Counter;
 use sv::Executor as ContractExecutor;
 use sylvia::types::{ExecCtx, QueryCtx};
 
-impl Counter for CounterContract<'_> {
+impl Counter for CounterContract {
     type Error = StdError;
 
     fn count(&self, ctx: QueryCtx) -> StdResult<CountResponse> {
@@ -56,15 +56,16 @@ impl Counter for CounterContract<'_> {
     }
 }
 
-pub struct CounterContract<'a> {
+pub struct CounterContract {
     pub count: Item<u64>,
-    pub remote_contract: Item<sylvia::types::Remote<'a, CounterContract<'a>>>,
-    pub remote_interface: Item<sylvia::types::Remote<'a, dyn Counter<Error = StdError>>>,
+    pub remote_contract: Item<sylvia::types::Remote<'static, CounterContract>>,
+    pub remote_interface: Item<sylvia::types::Remote<'static, dyn Counter<Error = StdError>>>,
 }
 
+#[entry_points]
 #[contract]
 #[sv::messages(counter)]
-impl<'a> CounterContract<'a> {
+impl CounterContract {
     #[allow(clippy::new_without_default)]
     pub fn new() -> Self {
         Self {
diff --git a/sylvia/tests/interface_impl.rs b/sylvia/tests/interface_impl.rs
index 6bd6615..88d5427 100644
--- a/sylvia/tests/interface_impl.rs
+++ b/sylvia/tests/interface_impl.rs
@@ -1,6 +1,6 @@
 use cosmwasm_std::{Response, StdError, StdResult};
-use sylvia::contract;
 use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx, SudoCtx};
+use sylvia::{contract, entry_points};
 
 mod interface1 {
     use cosmwasm_std::{Response, StdError, StdResult};
@@ -46,6 +46,7 @@ mod interface2 {
 
 pub struct Contract;
 
+#[entry_points]
 #[contract]
 #[sv::messages(interface1)]
 #[sv::messages(interface2)]
diff --git a/sylvia/tests/multitest.rs b/sylvia/tests/multitest.rs
index f20a8c2..166067e 100644
--- a/sylvia/tests/multitest.rs
+++ b/sylvia/tests/multitest.rs
@@ -3,6 +3,7 @@
 use cosmwasm_std::{CodeInfoResponse, Empty, Response, StdResult};
 use cw_multi_test::IntoBech32;
 use std::marker::PhantomData;
+use sylvia::entry_points;
 use sylvia::multitest::App;
 use sylvia::types::InstantiateCtx;
 use sylvia_derive::contract;
@@ -11,6 +12,7 @@ pub struct SomeContract<ParamT> {
     _phantom: PhantomData<ParamT>,
 }
 
+#[entry_points(generics<Empty>)]
 #[contract]
 impl<ParamT> SomeContract<ParamT>
 where
diff --git a/sylvia/tests/querier.rs b/sylvia/tests/querier.rs
index fe39632..cd48971 100644
--- a/sylvia/tests/querier.rs
+++ b/sylvia/tests/querier.rs
@@ -4,8 +4,8 @@ use cosmwasm_std::{Addr, Response, StdResult};
 use cw_storage_plus::Item;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
-use sylvia::contract;
 use sylvia::types::InstantiateCtx;
+use sylvia::{contract, entry_points};
 
 #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug, Default)]
 pub struct CountResponse {
@@ -45,7 +45,7 @@ pub mod impl_counter {
     use cosmwasm_std::{Response, StdError, StdResult};
     use sylvia::types::{ExecCtx, QueryCtx};
 
-    impl Counter for super::CounterContract<'_> {
+    impl Counter for super::CounterContract {
         type Error = StdError;
 
         fn count(&self, ctx: QueryCtx) -> StdResult<CountResponse> {
@@ -88,14 +88,15 @@ pub mod impl_counter {
     }
 }
 
-pub struct CounterContract<'a> {
+pub struct CounterContract {
     pub count: Item<u64>,
-    pub remote: Item<sylvia::types::Remote<'a, CounterContract<'a>>>,
+    pub remote: Item<sylvia::types::Remote<'static, CounterContract>>,
 }
 
+#[entry_points]
 #[contract]
 #[sv::messages(counter)]
-impl<'asdf> CounterContract<'asdf> {
+impl CounterContract {
     #[allow(clippy::new_without_default)]
     pub fn new() -> Self {
         Self {
@@ -130,19 +131,19 @@ mod tests {
         let remote_addr = Addr::unchecked("remote");
 
         // Remote generation
-        let remote = sylvia::types::Remote::<super::CounterContract<'_>>::new(remote_addr.clone());
+        let remote = sylvia::types::Remote::<super::CounterContract>::new(remote_addr.clone());
         let _: sylvia::types::BoundQuerier<_, _> = remote.querier(&querier_wrapper);
-        let remote = sylvia::types::Remote::<super::CounterContract<'_>>::new(remote_addr.clone());
+        let remote = sylvia::types::Remote::<super::CounterContract>::new(remote_addr.clone());
         let _: sylvia::types::BoundQuerier<_, _> = remote.querier(&querier_wrapper);
 
         // Querier generation
-        let _ = sylvia::types::BoundQuerier::<_, super::CounterContract<'_>>::borrowed(
+        let _ = sylvia::types::BoundQuerier::<_, super::CounterContract>::borrowed(
             &remote_addr,
             &querier_wrapper,
         );
         let querier = sylvia::types::BoundQuerier::borrowed(&remote_addr, &querier_wrapper);
 
-        let _ = sylvia::types::BoundQuerier::<_, super::CounterContract<'_>>::from(&querier);
+        let _ = sylvia::types::BoundQuerier::<_, super::CounterContract>::from(&querier);
     }
 
     #[test]
diff --git a/sylvia/tests/query_returns.rs b/sylvia/tests/query_returns.rs
index adbdc85..8543059 100644
--- a/sylvia/tests/query_returns.rs
+++ b/sylvia/tests/query_returns.rs
@@ -2,8 +2,8 @@ use cosmwasm_std::{Response, StdError, StdResult};
 
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
-use sylvia::contract;
 use sylvia::types::{InstantiateCtx, QueryCtx};
+use sylvia::{contract, entry_points};
 use thiserror::Error;
 
 #[derive(Error, Debug, PartialEq)]
@@ -36,6 +36,7 @@ pub mod msg {
 
 pub struct SomeContract {}
 
+#[entry_points]
 #[contract]
 #[sv::error(ContractError)]
 impl SomeContract {
diff --git a/sylvia/tests/remote.rs b/sylvia/tests/remote.rs
index 96b94d4..2da8e30 100644
--- a/sylvia/tests/remote.rs
+++ b/sylvia/tests/remote.rs
@@ -159,14 +159,14 @@ pub mod unsigned_contract {
 // Making sure `Remote` can be stored in `#[cw_serde]` types
 #[cw_serde]
 #[allow(dead_code)]
-pub struct ContractStorage<'a, Contract> {
-    remote: Remote<'a, Contract>,
+pub struct ContractStorage<Contract> {
+    remote: Remote<'static, Contract>,
 }
 
 #[derive(Serialize, Deserialize)]
-pub struct InterfaceStorage<'a, CounterT> {
+pub struct InterfaceStorage<CounterT> {
     interface_remote: Remote<
-        'a,
+        'static,
         dyn counter::Counter<
             Error = StdError,
             ExecC = ExampleMsg,
@@ -176,7 +176,7 @@ pub struct InterfaceStorage<'a, CounterT> {
     >,
 }
 
-impl<CounterT> InterfaceStorage<'_, CounterT>
+impl<CounterT> InterfaceStorage<CounterT>
 where
     CounterT: Serialize + DeserializeOwned,
 {
@@ -193,22 +193,20 @@ pub mod manager {
     use schemars::JsonSchema;
     use serde::de::DeserializeOwned;
     use serde::Serialize;
-    use sylvia::contract;
     use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx};
+    use sylvia::{contract, entry_points};
 
     use crate::counter::sv::{Executor, Querier};
     use crate::{ExampleMsg, ExampleQuery, InterfaceStorage};
 
-    pub struct ManagerContract<'a, CounterT> {
-        remote_counter: Item<InterfaceStorage<'a, CounterT>>,
+    pub struct ManagerContract<CounterT> {
+        remote_counter: Item<InterfaceStorage<CounterT>>,
     }
 
+    #[entry_points(generics<i32>)]
     #[contract]
-    // Due to how `cw_multi_test::App` is constructed to test two contracts they have
-    // to use the same custom types.
-    // Not sure if this limits testing of some real life scenarios.
     #[sv::custom(msg=ExampleMsg, query=ExampleQuery)]
-    impl<'a, CounterT> ManagerContract<'a, CounterT>
+    impl<CounterT> ManagerContract<CounterT>
     where
         CounterT: Serialize + DeserializeOwned + std::fmt::Debug + JsonSchema + 'static,
     {
diff --git a/sylvia/tests/replies.rs b/sylvia/tests/replies.rs
index 89bc92d..663b255 100644
--- a/sylvia/tests/replies.rs
+++ b/sylvia/tests/replies.rs
@@ -5,13 +5,14 @@ use sylvia::cw_std::{from_json, Reply, SubMsgResponse, SubMsgResult};
 
 #[allow(dead_code)]
 mod noop_contract {
-    use sylvia::contract;
     use sylvia::types::{ExecCtx, InstantiateCtx};
+    use sylvia::{contract, entry_points};
 
     use sylvia::cw_std::{Response, StdResult};
 
     pub struct NoopContract;
 
+    #[entry_points]
     #[contract]
     impl NoopContract {
         pub const fn new() -> Self {