diff --git a/crates/apps_lib/src/cli.rs b/crates/apps_lib/src/cli.rs index 51eacc4cc3d..cc593628d60 100644 --- a/crates/apps_lib/src/cli.rs +++ b/crates/apps_lib/src/cli.rs @@ -295,6 +295,8 @@ pub mod cmds { .subcommand(QueryCommissionRate::def().display_order(5)) .subcommand(QueryRewards::def().display_order(5)) .subcommand(QueryMetaData::def().display_order(5)) + .subcommand(QueryTotalSupply::def().display_order(5)) + .subcommand(QueryEffNativeSupply::def().display_order(5)) // Actions .subcommand(SignTx::def().display_order(6)) .subcommand(ShieldedSync::def().display_order(6)) @@ -366,6 +368,10 @@ pub mod cmds { let query_rewards = Self::parse_with_ctx(matches, QueryRewards); let query_delegations = Self::parse_with_ctx(matches, QueryDelegations); + let query_total_supply = + Self::parse_with_ctx(matches, QueryTotalSupply); + let query_native_supply = + Self::parse_with_ctx(matches, QueryEffNativeSupply); let query_find_validator = Self::parse_with_ctx(matches, QueryFindValidator); let query_result = Self::parse_with_ctx(matches, QueryResult); @@ -440,6 +446,8 @@ pub mod cmds { .or(query_validator_state) .or(query_commission) .or(query_metadata) + .or(query_total_supply) + .or(query_native_supply) .or(query_account) .or(sign_tx) .or(shielded_sync) @@ -523,6 +531,8 @@ pub mod cmds { QueryMetaData(QueryMetaData), QuerySlashes(QuerySlashes), QueryDelegations(QueryDelegations), + QueryTotalSupply(QueryTotalSupply), + QueryEffNativeSupply(QueryEffNativeSupply), QueryFindValidator(QueryFindValidator), QueryRawBytes(QueryRawBytes), QueryProposal(QueryProposal), @@ -2052,6 +2062,60 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct QueryTotalSupply(pub args::QueryTotalSupply); + + impl SubCmd for QueryTotalSupply { + const CMD: &'static str = "total-supply"; + + fn parse(matches: &ArgMatches) -> Option + where + Self: Sized, + { + matches.subcommand_matches(Self::CMD).map(|matches| { + QueryTotalSupply(args::QueryTotalSupply::parse(matches)) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about(wrap!( + "Query the total supply in the network of the given \ + token. For the native token, this will query the raw \ + total supply and not the effective total supply." + )) + .add_args::>() + } + } + + #[derive(Clone, Debug)] + pub struct QueryEffNativeSupply( + pub args::QueryEffNativeSupply, + ); + + impl SubCmd for QueryEffNativeSupply { + const CMD: &'static str = "native-supply"; + + fn parse(matches: &ArgMatches) -> Option + where + Self: Sized, + { + matches.subcommand_matches(Self::CMD).map(|matches| { + QueryEffNativeSupply(args::QueryEffNativeSupply::parse(matches)) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about(wrap!( + "Query the total supply in the network of the given \ + token. For the native token, this will query the raw \ + total supply and not the effective total supply." + )) + .add_args::>() + } + } + #[derive(Clone, Debug)] pub struct QueryFindValidator(pub args::QueryFindValidator); @@ -6957,6 +7021,59 @@ pub mod args { } } + impl Args for QueryTotalSupply { + fn parse(matches: &ArgMatches) -> Self { + let query = Query::parse(matches); + let token = TOKEN.parse(matches); + Self { query, token } + } + + fn def(app: App) -> App { + app.add_args::>() + .arg(TOKEN.def().help(wrap!("The token address."))) + } + } + + impl CliToSdk> for QueryTotalSupply { + type Error = std::convert::Infallible; + + fn to_sdk( + self, + ctx: &mut Context, + ) -> Result, Self::Error> { + Ok(QueryTotalSupply:: { + query: self.query.to_sdk(ctx)?, + token: ctx.borrow_chain_or_exit().get(&self.token), + }) + } + } + + impl Args for QueryEffNativeSupply { + fn parse(matches: &ArgMatches) -> Self { + let query = Query::parse(matches); + Self { query } + } + + fn def(app: App) -> App { + app + } + } + + impl CliToSdk> + for QueryEffNativeSupply + { + type Error = std::convert::Infallible; + + fn to_sdk( + self, + ctx: &mut Context, + ) -> Result, Self::Error> { + Ok(QueryEffNativeSupply:: { + query: self.query.to_sdk(ctx)?, + }) + } + } + impl Args for QueryFindValidator { fn parse(matches: &ArgMatches) -> Self { let query = Query::parse(matches); diff --git a/crates/apps_lib/src/cli/client.rs b/crates/apps_lib/src/cli/client.rs index 1d9ce012032..6724f24a733 100644 --- a/crates/apps_lib/src/cli/client.rs +++ b/crates/apps_lib/src/cli/client.rs @@ -637,6 +637,29 @@ impl CliApi { let namada = ctx.to_sdk(client, io); rpc::query_delegations(&namada, args).await; } + Sub::QueryTotalSupply(QueryTotalSupply(args)) => { + let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let ledger_address = + chain_ctx.get(&args.query.ledger_address); + let client = client.unwrap_or_else(|| { + C::from_tendermint_address(&ledger_address) + }); + client.wait_until_node_is_synced(&io).await?; + let args = args.to_sdk(&mut ctx)?; + let namada = ctx.to_sdk(client, io); + rpc::query_total_supply(&namada, args).await; + } + Sub::QueryEffNativeSupply(QueryEffNativeSupply(args)) => { + let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let ledger_address = + chain_ctx.get(&args.query.ledger_address); + let client = client.unwrap_or_else(|| { + C::from_tendermint_address(&ledger_address) + }); + client.wait_until_node_is_synced(&io).await?; + let namada = ctx.to_sdk(client, io); + rpc::query_effective_native_supply(&namada).await; + } Sub::QueryFindValidator(QueryFindValidator(args)) => { let chain_ctx = ctx.borrow_mut_chain_or_exit(); let ledger_address = diff --git a/crates/apps_lib/src/client/rpc.rs b/crates/apps_lib/src/client/rpc.rs index 9bb20e99c8d..15126d1652c 100644 --- a/crates/apps_lib/src/client/rpc.rs +++ b/crates/apps_lib/src/client/rpc.rs @@ -1176,6 +1176,32 @@ pub async fn query_rewards( ) } +/// Query token total supply. +pub async fn query_total_supply( + context: &N, + args: args::QueryTotalSupply, +) { + let token = args.token; + let supply = unwrap_client_response::( + RPC.vp() + .token() + .total_supply(context.client(), &token) + .await, + ); + display_line!(context.io(), "Total supply of {token}: {supply}"); +} + +/// Query the effective total supply of the native token +pub async fn query_effective_native_supply(context: &N) { + let native_supply = unwrap_client_response::( + RPC.vp() + .token() + .effective_native_supply(context.client()) + .await, + ); + display_line!(context.io(), "{native_supply} NAM"); +} + /// Query a validator's state information pub async fn query_and_print_validator_state( context: &impl Namada, diff --git a/crates/sdk/src/args.rs b/crates/sdk/src/args.rs index 00bf1675d81..5949a16ba4c 100644 --- a/crates/sdk/src/args.rs +++ b/crates/sdk/src/args.rs @@ -2220,6 +2220,22 @@ pub struct QueryDelegations { pub owner: C::Address, } +/// Query token total supply +#[derive(Clone, Debug)] +pub struct QueryTotalSupply { + /// Common query args + pub query: Query, + /// Token address + pub token: C::Address, +} + +/// Query effective native supply +#[derive(Clone, Debug)] +pub struct QueryEffNativeSupply { + /// Common query args + pub query: Query, +} + /// Query PoS to find a validator #[derive(Clone, Debug)] pub struct QueryFindValidator { diff --git a/crates/sdk/src/queries/vp/token.rs b/crates/sdk/src/queries/vp/token.rs index 7bae2619a01..3904f1cd857 100644 --- a/crates/sdk/src/queries/vp/token.rs +++ b/crates/sdk/src/queries/vp/token.rs @@ -10,38 +10,45 @@ use namada_token::{ use crate::queries::RequestCtx; router! {TOKEN, - ( "denomination" / [addr: Address] ) -> Option = denomination, - ( "total_supply" / [addr: Address] ) -> token::Amount = total_supply, + ( "denomination" / [token: Address] ) -> Option = denomination, + ( "total_supply" / [token: Address] ) -> token::Amount = total_supply, + ( "effective_native_supply" ) -> token::Amount = effective_native_supply, } /// Get the number of decimal places (in base 10) for a /// token specified by `addr`. fn denomination( ctx: RequestCtx<'_, D, H, V, T>, - addr: Address, + token: Address, ) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - read_denom(ctx.state, &addr) + read_denom(ctx.state, &token) } /// Get the total supply for a token address fn total_supply( ctx: RequestCtx<'_, D, H, V, T>, - addr: Address, + token: Address, ) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - let native_token = ctx.state.in_mem().native_token.clone(); - if addr == native_token { - get_effective_total_native_supply(ctx.state) - } else { - read_total_supply(ctx.state, &addr) - } + read_total_supply(ctx.state, &token) +} + +/// Get the effective total supply of the native token +fn effective_native_supply( + ctx: RequestCtx<'_, D, H, V, T>, +) -> namada_storage::Result +where + D: 'static + DB + for<'iter> DBIter<'iter> + Sync, + H: 'static + StorageHasher + Sync, +{ + get_effective_total_native_supply(ctx.state) } pub mod client_only_methods { diff --git a/crates/sdk/src/rpc.rs b/crates/sdk/src/rpc.rs index 44a2ab554a4..96e82e76866 100644 --- a/crates/sdk/src/rpc.rs +++ b/crates/sdk/src/rpc.rs @@ -224,6 +224,15 @@ pub async fn get_token_total_supply( convert_response::(RPC.vp().token().total_supply(client, token).await) } +/// Query the effective total supply of the native token +pub async fn get_effective_native_supply( + client: &C, +) -> Result { + convert_response::( + RPC.vp().token().effective_native_supply(client).await, + ) +} + /// Check if the given address is a known validator. pub async fn is_validator( client: &C,