diff --git a/contracts/pre-propose/dao-pre-propose-approval-single/schema/dao-pre-propose-approval-single.json b/contracts/pre-propose/dao-pre-propose-approval-single/schema/dao-pre-propose-approval-single.json index f748e5ee3..8388c5f8d 100644 --- a/contracts/pre-propose/dao-pre-propose-approval-single/schema/dao-pre-propose-approval-single.json +++ b/contracts/pre-propose/dao-pre-propose-approval-single/schema/dao-pre-propose-approval-single.json @@ -88,7 +88,7 @@ "additionalProperties": false }, { - "description": "Use the token address of the associated DAO's voting module. NOTE: in order to use the token address of the voting module the voting module must (1) use a cw20 token and (2) implement the `TokenContract {}` query type defined by `dao_dao_macros::token_query`. Failing to implement that and using this option will cause instantiation to fail.", + "description": "Use the token native denom or cw20 contract address of the associated DAO's voting module. NOTE: in order to retrieve the token automatically via this variant, the voting module must either (1) use a native token and implement the `Denom {}` query type defined by `dao_dao_macros::native_token_query` OR (2) use a cw20 token and implement the `TokenContract {}` query type defined by `dao_dao_macros::cw20_token_query`. Failing to implement correctly will cause this option to fail to instantiate.", "type": "object", "required": [ "voting_module_token" @@ -609,7 +609,7 @@ "additionalProperties": false }, { - "description": "Use the token address of the associated DAO's voting module. NOTE: in order to use the token address of the voting module the voting module must (1) use a cw20 token and (2) implement the `TokenContract {}` query type defined by `dao_dao_macros::token_query`. Failing to implement that and using this option will cause instantiation to fail.", + "description": "Use the token native denom or cw20 contract address of the associated DAO's voting module. NOTE: in order to retrieve the token automatically via this variant, the voting module must either (1) use a native token and implement the `Denom {}` query type defined by `dao_dao_macros::native_token_query` OR (2) use a cw20 token and implement the `TokenContract {}` query type defined by `dao_dao_macros::cw20_token_query`. Failing to implement correctly will cause this option to fail to instantiate.", "type": "object", "required": [ "voting_module_token" diff --git a/contracts/pre-propose/dao-pre-propose-approver/schema/dao-pre-propose-approver.json b/contracts/pre-propose/dao-pre-propose-approver/schema/dao-pre-propose-approver.json index b0b52bbce..aa0850a12 100644 --- a/contracts/pre-propose/dao-pre-propose-approver/schema/dao-pre-propose-approver.json +++ b/contracts/pre-propose/dao-pre-propose-approver/schema/dao-pre-propose-approver.json @@ -283,7 +283,7 @@ "additionalProperties": false }, { - "description": "Use the token address of the associated DAO's voting module. NOTE: in order to use the token address of the voting module the voting module must (1) use a cw20 token and (2) implement the `TokenContract {}` query type defined by `dao_dao_macros::token_query`. Failing to implement that and using this option will cause instantiation to fail.", + "description": "Use the token native denom or cw20 contract address of the associated DAO's voting module. NOTE: in order to retrieve the token automatically via this variant, the voting module must either (1) use a native token and implement the `Denom {}` query type defined by `dao_dao_macros::native_token_query` OR (2) use a cw20 token and implement the `TokenContract {}` query type defined by `dao_dao_macros::cw20_token_query`. Failing to implement correctly will cause this option to fail to instantiate.", "type": "object", "required": [ "voting_module_token" diff --git a/contracts/pre-propose/dao-pre-propose-multiple/schema/dao-pre-propose-multiple.json b/contracts/pre-propose/dao-pre-propose-multiple/schema/dao-pre-propose-multiple.json index 6faed06ed..a1cb55d27 100644 --- a/contracts/pre-propose/dao-pre-propose-multiple/schema/dao-pre-propose-multiple.json +++ b/contracts/pre-propose/dao-pre-propose-multiple/schema/dao-pre-propose-multiple.json @@ -88,7 +88,7 @@ "additionalProperties": false }, { - "description": "Use the token address of the associated DAO's voting module. NOTE: in order to use the token address of the voting module the voting module must (1) use a cw20 token and (2) implement the `TokenContract {}` query type defined by `dao_dao_macros::token_query`. Failing to implement that and using this option will cause instantiation to fail.", + "description": "Use the token native denom or cw20 contract address of the associated DAO's voting module. NOTE: in order to retrieve the token automatically via this variant, the voting module must either (1) use a native token and implement the `Denom {}` query type defined by `dao_dao_macros::native_token_query` OR (2) use a cw20 token and implement the `TokenContract {}` query type defined by `dao_dao_macros::cw20_token_query`. Failing to implement correctly will cause this option to fail to instantiate.", "type": "object", "required": [ "voting_module_token" @@ -601,7 +601,7 @@ "additionalProperties": false }, { - "description": "Use the token address of the associated DAO's voting module. NOTE: in order to use the token address of the voting module the voting module must (1) use a cw20 token and (2) implement the `TokenContract {}` query type defined by `dao_dao_macros::token_query`. Failing to implement that and using this option will cause instantiation to fail.", + "description": "Use the token native denom or cw20 contract address of the associated DAO's voting module. NOTE: in order to retrieve the token automatically via this variant, the voting module must either (1) use a native token and implement the `Denom {}` query type defined by `dao_dao_macros::native_token_query` OR (2) use a cw20 token and implement the `TokenContract {}` query type defined by `dao_dao_macros::cw20_token_query`. Failing to implement correctly will cause this option to fail to instantiate.", "type": "object", "required": [ "voting_module_token" diff --git a/contracts/pre-propose/dao-pre-propose-single/schema/dao-pre-propose-single.json b/contracts/pre-propose/dao-pre-propose-single/schema/dao-pre-propose-single.json index b9a943395..1db69f76a 100644 --- a/contracts/pre-propose/dao-pre-propose-single/schema/dao-pre-propose-single.json +++ b/contracts/pre-propose/dao-pre-propose-single/schema/dao-pre-propose-single.json @@ -88,7 +88,7 @@ "additionalProperties": false }, { - "description": "Use the token address of the associated DAO's voting module. NOTE: in order to use the token address of the voting module the voting module must (1) use a cw20 token and (2) implement the `TokenContract {}` query type defined by `dao_dao_macros::token_query`. Failing to implement that and using this option will cause instantiation to fail.", + "description": "Use the token native denom or cw20 contract address of the associated DAO's voting module. NOTE: in order to retrieve the token automatically via this variant, the voting module must either (1) use a native token and implement the `Denom {}` query type defined by `dao_dao_macros::native_token_query` OR (2) use a cw20 token and implement the `TokenContract {}` query type defined by `dao_dao_macros::cw20_token_query`. Failing to implement correctly will cause this option to fail to instantiate.", "type": "object", "required": [ "voting_module_token" @@ -601,7 +601,7 @@ "additionalProperties": false }, { - "description": "Use the token address of the associated DAO's voting module. NOTE: in order to use the token address of the voting module the voting module must (1) use a cw20 token and (2) implement the `TokenContract {}` query type defined by `dao_dao_macros::token_query`. Failing to implement that and using this option will cause instantiation to fail.", + "description": "Use the token native denom or cw20 contract address of the associated DAO's voting module. NOTE: in order to retrieve the token automatically via this variant, the voting module must either (1) use a native token and implement the `Denom {}` query type defined by `dao_dao_macros::native_token_query` OR (2) use a cw20 token and implement the `TokenContract {}` query type defined by `dao_dao_macros::cw20_token_query`. Failing to implement correctly will cause this option to fail to instantiate.", "type": "object", "required": [ "voting_module_token" diff --git a/contracts/test/dao-voting-cw20-balance/src/msg.rs b/contracts/test/dao-voting-cw20-balance/src/msg.rs index 6a0a5cae3..c41590ee2 100644 --- a/contracts/test/dao-voting-cw20-balance/src/msg.rs +++ b/contracts/test/dao-voting-cw20-balance/src/msg.rs @@ -3,7 +3,7 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cw20::Cw20Coin; use cw20_base::msg::InstantiateMarketingInfo; -use dao_dao_macros::{token_query, voting_module_query}; +use dao_dao_macros::{cw20_token_query, voting_module_query}; #[cw_serde] pub enum TokenInfo { @@ -30,7 +30,7 @@ pub struct InstantiateMsg { #[cw_serde] pub enum ExecuteMsg {} -#[token_query] +#[cw20_token_query] #[voting_module_query] #[cw_serde] #[derive(QueryResponses)] diff --git a/contracts/voting/dao-voting-cw20-staked/src/msg.rs b/contracts/voting/dao-voting-cw20-staked/src/msg.rs index 33753fcae..bdb5e9f2a 100644 --- a/contracts/voting/dao-voting-cw20-staked/src/msg.rs +++ b/contracts/voting/dao-voting-cw20-staked/src/msg.rs @@ -4,7 +4,7 @@ use cw20::Cw20Coin; use cw20_base::msg::InstantiateMarketingInfo; use cw_utils::Duration; -use dao_dao_macros::{active_query, token_query, voting_module_query}; +use dao_dao_macros::{active_query, cw20_token_query, voting_module_query}; use dao_voting::threshold::{ActiveThreshold, ActiveThresholdResponse}; /// Information about the staking contract to be used with this voting @@ -71,7 +71,7 @@ pub enum ExecuteMsg { } #[voting_module_query] -#[token_query] +#[cw20_token_query] #[active_query] #[cw_serde] #[derive(QueryResponses)] diff --git a/contracts/voting/dao-voting-token-staked/schema/dao-voting-token-staked.json b/contracts/voting/dao-voting-token-staked/schema/dao-voting-token-staked.json index 281393190..12cfdea3a 100644 --- a/contracts/voting/dao-voting-token-staked/schema/dao-voting-token-staked.json +++ b/contracts/voting/dao-voting-token-staked/schema/dao-voting-token-staked.json @@ -587,19 +587,6 @@ }, "additionalProperties": false }, - { - "type": "object", - "required": [ - "denom" - ], - "properties": { - "denom": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - }, { "type": "object", "required": [ @@ -689,6 +676,19 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "denom" + ], + "properties": { + "denom": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ diff --git a/contracts/voting/dao-voting-token-staked/src/contract.rs b/contracts/voting/dao-voting-token-staked/src/contract.rs index ffcba6902..15f19fb6e 100644 --- a/contracts/voting/dao-voting-token-staked/src/contract.rs +++ b/contracts/voting/dao-voting-token-staked/src/contract.rs @@ -18,7 +18,9 @@ use dao_hooks::stake::{stake_hook_msgs, unstake_hook_msgs}; use dao_interface::{ state::ModuleInstantiateCallback, token::{InitialBalance, NewTokenInfo, TokenFactoryCallback}, - voting::{IsActiveResponse, TotalPowerAtHeightResponse, VotingPowerAtHeightResponse}, + voting::{ + DenomResponse, IsActiveResponse, TotalPowerAtHeightResponse, VotingPowerAtHeightResponse, + }, }; use dao_voting::{ duration::validate_duration, @@ -30,8 +32,8 @@ use dao_voting::{ use crate::error::ContractError; use crate::msg::{ - DenomResponse, ExecuteMsg, GetHooksResponse, InstantiateMsg, ListStakersResponse, MigrateMsg, - QueryMsg, StakerBalanceResponse, TokenInfo, + ExecuteMsg, GetHooksResponse, InstantiateMsg, ListStakersResponse, MigrateMsg, QueryMsg, + StakerBalanceResponse, TokenInfo, }; use crate::state::{ Config, ACTIVE_THRESHOLD, CLAIMS, CONFIG, DAO, DENOM, HOOKS, MAX_CLAIMS, STAKED_BALANCES, diff --git a/contracts/voting/dao-voting-token-staked/src/msg.rs b/contracts/voting/dao-voting-token-staked/src/msg.rs index 52c71baa9..98809ec9e 100644 --- a/contracts/voting/dao-voting-token-staked/src/msg.rs +++ b/contracts/voting/dao-voting-token-staked/src/msg.rs @@ -1,7 +1,7 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Binary, Uint128}; use cw_utils::Duration; -use dao_dao_macros::{active_query, voting_module_query}; +use dao_dao_macros::{active_query, native_token_query, voting_module_query}; use dao_interface::token::NewTokenInfo; use dao_voting::threshold::{ActiveThreshold, ActiveThresholdResponse}; @@ -57,6 +57,7 @@ pub enum ExecuteMsg { RemoveHook { addr: String }, } +#[native_token_query] #[active_query] #[voting_module_query] #[cw_serde] @@ -64,8 +65,6 @@ pub enum ExecuteMsg { pub enum QueryMsg { #[returns(crate::state::Config)] GetConfig {}, - #[returns(DenomResponse)] - Denom {}, #[returns(cw_controllers::ClaimsResponse)] Claims { address: String }, #[returns(ListStakersResponse)] @@ -95,11 +94,6 @@ pub struct StakerBalanceResponse { pub balance: Uint128, } -#[cw_serde] -pub struct DenomResponse { - pub denom: String, -} - #[cw_serde] pub struct GetHooksResponse { pub hooks: Vec, diff --git a/contracts/voting/dao-voting-token-staked/src/tests/multitest/tests.rs b/contracts/voting/dao-voting-token-staked/src/tests/multitest/tests.rs index 3498764ec..90b4fab28 100644 --- a/contracts/voting/dao-voting-token-staked/src/tests/multitest/tests.rs +++ b/contracts/voting/dao-voting-token-staked/src/tests/multitest/tests.rs @@ -1,7 +1,7 @@ use crate::contract::{migrate, CONTRACT_NAME, CONTRACT_VERSION}; use crate::msg::{ - DenomResponse, ExecuteMsg, GetHooksResponse, InstantiateMsg, ListStakersResponse, MigrateMsg, - QueryMsg, StakerBalanceResponse, TokenInfo, + ExecuteMsg, GetHooksResponse, InstantiateMsg, ListStakersResponse, MigrateMsg, QueryMsg, + StakerBalanceResponse, TokenInfo, }; use crate::state::Config; use cosmwasm_std::testing::{mock_dependencies, mock_env}; @@ -12,7 +12,8 @@ use cw_multi_test::{ }; use cw_utils::Duration; use dao_interface::voting::{ - InfoResponse, IsActiveResponse, TotalPowerAtHeightResponse, VotingPowerAtHeightResponse, + DenomResponse, InfoResponse, IsActiveResponse, TotalPowerAtHeightResponse, + VotingPowerAtHeightResponse, }; use dao_voting::threshold::{ActiveThreshold, ActiveThresholdResponse}; diff --git a/packages/dao-dao-macros/README.md b/packages/dao-dao-macros/README.md index 404f8acda..d65999b77 100644 --- a/packages/dao-dao-macros/README.md +++ b/packages/dao-dao-macros/README.md @@ -6,11 +6,11 @@ the voting module interface on an enum: ```rust use cosmwasm_schema::{cw_serde, QueryResponses}; -use dao_dao_macros::{token_query, voting_module_query}; +use dao_dao_macros::{cw20_token_query, voting_module_query}; use dao_interface::voting::TotalPowerAtHeightResponse; use dao_interface::voting::VotingPowerAtHeightResponse; -#[token_query] +#[cw20_token_query] #[voting_module_query] #[cw_serde] #[derive(QueryResponses)] diff --git a/packages/dao-dao-macros/src/lib.rs b/packages/dao-dao-macros/src/lib.rs index 8b2dddda9..48611fddf 100644 --- a/packages/dao-dao-macros/src/lib.rs +++ b/packages/dao-dao-macros/src/lib.rs @@ -152,16 +152,16 @@ pub fn voting_module_query(metadata: TokenStream, input: TokenStream) -> TokenSt } /// Adds the necessary fields to an enum such that it implements the -/// interface needed to be a voting module with a token. +/// interface needed to be a voting module with a cw20 token. /// /// For example: /// /// ``` -/// use dao_dao_macros::token_query; +/// use dao_dao_macros::cw20_token_query; /// use cosmwasm_schema::{cw_serde, QueryResponses}; /// use cosmwasm_std::Addr; /// -/// #[token_query] +/// #[cw20_token_query] /// #[cw_serde] /// #[derive(QueryResponses)] /// enum QueryMsg {} @@ -181,11 +181,11 @@ pub fn voting_module_query(metadata: TokenStream, input: TokenStream) -> TokenSt /// occurs before the addition of the field. /// /// ```compile_fail -/// use dao_dao_macros::token_query; +/// use dao_dao_macros::cw20_token_query; /// use cosmwasm_schema::{cw_serde, QueryResponses}; /// /// #[derive(Clone)] -/// #[token_query] +/// #[cw20_token_query] /// #[allow(dead_code)] /// #[cw_serde] /// #[derive(QueryResponses)] @@ -196,7 +196,7 @@ pub fn voting_module_query(metadata: TokenStream, input: TokenStream) -> TokenSt /// } /// ``` #[proc_macro_attribute] -pub fn token_query(metadata: TokenStream, input: TokenStream) -> TokenStream { +pub fn cw20_token_query(metadata: TokenStream, input: TokenStream) -> TokenStream { merge_variants( metadata, input, @@ -210,6 +210,65 @@ pub fn token_query(metadata: TokenStream, input: TokenStream) -> TokenStream { ) } +/// Adds the necessary fields to an enum such that it implements the +/// interface needed to be a voting module with a native token. +/// +/// For example: +/// +/// ``` +/// use dao_dao_macros::native_token_query; +/// use cosmwasm_schema::{cw_serde, QueryResponses}; +/// use cosmwasm_std::Addr; +/// +/// #[native_token_query] +/// #[cw_serde] +/// #[derive(QueryResponses)] +/// enum QueryMsg {} +/// ``` +/// +/// Will transform the enum to: +/// +/// ``` +/// enum QueryMsg { +/// Denom {}, +/// } +/// ``` +/// +/// Note that other derive macro invocations must occur after this +/// procedural macro as they may depend on the new fields. For +/// example, the following will fail becase the `Clone` derivation +/// occurs before the addition of the field. +/// +/// ```compile_fail +/// use dao_dao_macros::native_token_query; +/// use cosmwasm_schema::{cw_serde, QueryResponses}; +/// +/// #[derive(Clone)] +/// #[native_token_query] +/// #[allow(dead_code)] +/// #[cw_serde] +/// #[derive(QueryResponses)] +/// enum Test { +/// Foo, +/// Bar(u64), +/// Baz { foo: u64 }, +/// } +/// ``` +#[proc_macro_attribute] +pub fn native_token_query(metadata: TokenStream, input: TokenStream) -> TokenStream { + merge_variants( + metadata, + input, + quote! { + enum Right { + #[returns(::dao_interface::voting::DenomResponse)] + Denom {} + } + } + .into(), + ) +} + /// Adds the necessary fields to an enum such that it implements the /// interface needed to be a voting module that has an /// active check threshold. diff --git a/packages/dao-interface/src/voting.rs b/packages/dao-interface/src/voting.rs index cf6d43707..3ef95b971 100644 --- a/packages/dao-interface/src/voting.rs +++ b/packages/dao-interface/src/voting.rs @@ -8,6 +8,9 @@ pub enum Query { /// Returns the token contract address, if set. #[returns(::cosmwasm_std::Addr)] TokenContract {}, + /// Returns the native token denom, if used. + #[returns(DenomResponse)] + Denom {}, /// Returns the voting power for an address at a given height. #[returns(VotingPowerAtHeightResponse)] VotingPowerAtHeight { @@ -56,3 +59,8 @@ pub struct InfoResponse { pub struct IsActiveResponse { pub active: bool, } + +#[cw_serde] +pub struct DenomResponse { + pub denom: String, +} diff --git a/packages/dao-voting/src/deposit.rs b/packages/dao-voting/src/deposit.rs index 33fbb832c..5e87bc2a7 100644 --- a/packages/dao-voting/src/deposit.rs +++ b/packages/dao-voting/src/deposit.rs @@ -4,6 +4,7 @@ use cosmwasm_std::{ }; use cw_utils::{must_pay, PaymentError}; +use dao_interface::voting::DenomResponse; use thiserror::Error; use cw_denom::{CheckedDenom, DenomError, UncheckedDenom}; @@ -32,12 +33,14 @@ pub enum DepositError { pub enum DepositToken { /// Use a specific token address as the deposit token. Token { denom: UncheckedDenom }, - /// Use the token address of the associated DAO's voting - /// module. NOTE: in order to use the token address of the voting - /// module the voting module must (1) use a cw20 token and (2) + /// Use the token native denom or cw20 contract address of the associated + /// DAO's voting module. NOTE: in order to retrieve the token automatically + /// via this variant, the voting module must either (1) use a native token + /// and implement the `Denom {}` query type defined by + /// `dao_dao_macros::native_token_query` OR (2) use a cw20 token and /// implement the `TokenContract {}` query type defined by - /// `dao_dao_macros::token_query`. Failing to implement that - /// and using this option will cause instantiation to fail. + /// `dao_dao_macros::cw20_token_query`. Failing to implement correctly will + /// cause this option to fail to instantiate. VotingModuleToken {}, } @@ -102,17 +105,30 @@ impl UncheckedDepositInfo { let voting_module: Addr = deps .querier .query_wasm_smart(dao, &dao_interface::msg::QueryMsg::VotingModule {})?; - // If the voting module has no token this will - // error. This is desirable. - let token_addr: Addr = deps.querier.query_wasm_smart( - voting_module, - &dao_interface::voting::Query::TokenContract {}, - )?; - // We don't assume here that the voting module has - // returned a valid token. Conversion of the unchecked - // denom into a checked one will do a `TokenInfo {}` - // query. - UncheckedDenom::Cw20(token_addr.into_string()).into_checked(deps) + // If the voting module has no token denom, this will be None. + let denom: Option = deps + .querier + .query_wasm_smart( + voting_module.clone(), + &dao_interface::voting::Query::Denom {}, + ) + .ok(); + + if let Some(denom) = denom { + UncheckedDenom::Native(denom.denom).into_checked(deps) + } else { + // If the voting module has no token this will + // error. This is desirable. + let token_addr: Addr = deps.querier.query_wasm_smart( + voting_module, + &dao_interface::voting::Query::TokenContract {}, + )?; + // We don't assume here that the voting module has + // returned a valid token. Conversion of the unchecked + // denom into a checked one will do a `TokenInfo {}` + // query. + UncheckedDenom::Cw20(token_addr.into_string()).into_checked(deps) + } } }?;