Skip to content

Commit

Permalink
Merge of #3691
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] committed Aug 29, 2024
2 parents cc0a448 + f57a1a9 commit 941efd5
Show file tree
Hide file tree
Showing 9 changed files with 271 additions and 13 deletions.
3 changes: 3 additions & 0 deletions .changelog/unreleased/improvements/3691-cli-commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- Include some CLI commands for querying the total supply of any token
and the effective total circulating supply of the native token.
([\#3691](https://github.com/anoma/namada/pull/3691))
118 changes: 118 additions & 0 deletions crates/apps_lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -523,6 +531,8 @@ pub mod cmds {
QueryMetaData(QueryMetaData),
QuerySlashes(QuerySlashes),
QueryDelegations(QueryDelegations),
QueryTotalSupply(QueryTotalSupply),
QueryEffNativeSupply(QueryEffNativeSupply),
QueryFindValidator(QueryFindValidator),
QueryRawBytes(QueryRawBytes),
QueryProposal(QueryProposal),
Expand Down Expand Up @@ -2052,6 +2062,61 @@ pub mod cmds {
}
}

#[derive(Clone, Debug)]
pub struct QueryTotalSupply(pub args::QueryTotalSupply<args::CliTypes>);

impl SubCmd for QueryTotalSupply {
const CMD: &'static str = "total-supply";

fn parse(matches: &ArgMatches) -> Option<Self>
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::<args::QueryTotalSupply<args::CliTypes>>()
}
}

#[derive(Clone, Debug)]
pub struct QueryEffNativeSupply(
pub args::QueryEffNativeSupply<args::CliTypes>,
);

impl SubCmd for QueryEffNativeSupply {
const CMD: &'static str = "native-supply";

fn parse(matches: &ArgMatches) -> Option<Self>
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 effective total circulating supply of the \
native token NAM. This excludes illquid NAM tokens held \
in places such as the PGF account. This is the token \
amount used in inflation calculations."
))
.add_args::<args::QueryEffNativeSupply<args::CliTypes>>()
}
}

#[derive(Clone, Debug)]
pub struct QueryFindValidator(pub args::QueryFindValidator<args::CliTypes>);

Expand Down Expand Up @@ -6957,6 +7022,59 @@ pub mod args {
}
}

impl Args for QueryTotalSupply<CliTypes> {
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::<Query<CliTypes>>()
.arg(TOKEN.def().help(wrap!("The token address.")))
}
}

impl CliToSdk<QueryTotalSupply<SdkTypes>> for QueryTotalSupply<CliTypes> {
type Error = std::convert::Infallible;

fn to_sdk(
self,
ctx: &mut Context,
) -> Result<QueryTotalSupply<SdkTypes>, Self::Error> {
Ok(QueryTotalSupply::<SdkTypes> {
query: self.query.to_sdk(ctx)?,
token: ctx.borrow_chain_or_exit().get(&self.token),
})
}
}

impl Args for QueryEffNativeSupply<CliTypes> {
fn parse(matches: &ArgMatches) -> Self {
let query = Query::parse(matches);
Self { query }
}

fn def(app: App) -> App {
app.add_args::<Query<CliTypes>>()
}
}

impl CliToSdk<QueryEffNativeSupply<SdkTypes>>
for QueryEffNativeSupply<CliTypes>
{
type Error = std::convert::Infallible;

fn to_sdk(
self,
ctx: &mut Context,
) -> Result<QueryEffNativeSupply<SdkTypes>, Self::Error> {
Ok(QueryEffNativeSupply::<SdkTypes> {
query: self.query.to_sdk(ctx)?,
})
}
}

impl Args for QueryFindValidator<CliTypes> {
fn parse(matches: &ArgMatches) -> Self {
let query = Query::parse(matches);
Expand Down
23 changes: 23 additions & 0 deletions crates/apps_lib/src/cli/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
45 changes: 44 additions & 1 deletion crates/apps_lib/src/client/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ use namada_sdk::proof_of_stake::types::{
use namada_sdk::proof_of_stake::PosParams;
use namada_sdk::queries::{Client, RPC};
use namada_sdk::rpc::{
self, enriched_bonds_and_unbonds, query_epoch, TxResponse,
self, enriched_bonds_and_unbonds, format_denominated_amount, query_epoch,
TxResponse,
};
use namada_sdk::storage::BlockResults;
use namada_sdk::tendermint_rpc::endpoint::status;
Expand Down Expand Up @@ -1176,6 +1177,40 @@ pub async fn query_rewards<C: namada_sdk::queries::Client + Sync>(
)
}

/// Query token total supply.
pub async fn query_total_supply<N: Namada>(
context: &N,
args: args::QueryTotalSupply,
) {
let token = args.token;
let supply = unwrap_sdk_result(
rpc::get_token_total_supply(context.client(), &token).await,
);
let amount_str = format_denominated_amount(
context.client(),
context.io(),
&token,
supply,
)
.await;
display_line!(
context.io(),
"Total supply of token {token}: {}",
amount_str
);
}

/// Query the effective total supply of the native token
pub async fn query_effective_native_supply<N: Namada>(context: &N) {
let native_supply = unwrap_client_response::<N::Client, token::Amount>(
RPC.vp()
.token()
.effective_native_supply(context.client())
.await,
);
display_line!(context.io(), "nam: {}", native_supply.to_string_native());
}

/// Query a validator's state information
pub async fn query_and_print_validator_state(
context: &impl Namada,
Expand Down Expand Up @@ -2008,6 +2043,14 @@ fn unwrap_client_response<C: namada_sdk::queries::Client, T>(
})
}

/// A helper to unwrap an SDK query result. Will shut down process on error.
fn unwrap_sdk_result<T>(response: Result<T, namada_sdk::error::Error>) -> T {
response.unwrap_or_else(|err| {
eprintln!("Error in the query: {:?}", err);
cli::safe_exit(1)
})
}

pub async fn compute_proposal_votes<C: namada_sdk::queries::Client + Sync>(
client: &C,
proposal_id: u64,
Expand Down
16 changes: 16 additions & 0 deletions crates/sdk/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2220,6 +2220,22 @@ pub struct QueryDelegations<C: NamadaTypes = SdkTypes> {
pub owner: C::Address,
}

/// Query token total supply
#[derive(Clone, Debug)]
pub struct QueryTotalSupply<C: NamadaTypes = SdkTypes> {
/// Common query args
pub query: Query<C>,
/// Token address
pub token: C::Address,
}

/// Query effective native supply
#[derive(Clone, Debug)]
pub struct QueryEffNativeSupply<C: NamadaTypes = SdkTypes> {
/// Common query args
pub query: Query<C>,
}

/// Query PoS to find a validator
#[derive(Clone, Debug)]
pub struct QueryFindValidator<C: NamadaTypes = SdkTypes> {
Expand Down
29 changes: 18 additions & 11 deletions crates/sdk/src/queries/vp/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,45 @@ use namada_token::{
use crate::queries::RequestCtx;

router! {TOKEN,
( "denomination" / [addr: Address] ) -> Option<token::Denomination> = denomination,
( "total_supply" / [addr: Address] ) -> token::Amount = total_supply,
( "denomination" / [token: Address] ) -> Option<token::Denomination> = 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<D, H, V, T>(
ctx: RequestCtx<'_, D, H, V, T>,
addr: Address,
token: Address,
) -> namada_storage::Result<Option<token::Denomination>>
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<D, H, V, T>(
ctx: RequestCtx<'_, D, H, V, T>,
addr: Address,
token: Address,
) -> namada_storage::Result<token::Amount>
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<D, H, V, T>(
ctx: RequestCtx<'_, D, H, V, T>,
) -> namada_storage::Result<token::Amount>
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 {
Expand Down
9 changes: 9 additions & 0 deletions crates/sdk/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,15 @@ pub async fn get_token_total_supply<C: crate::queries::Client + Sync>(
convert_response::<C, _>(RPC.vp().token().total_supply(client, token).await)
}

/// Query the effective total supply of the native token
pub async fn get_effective_native_supply<C: crate::queries::Client + Sync>(
client: &C,
) -> Result<token::Amount, error::Error> {
convert_response::<C, _>(
RPC.vp().token().effective_native_supply(client).await,
)
}

/// Check if the given address is a known validator.
pub async fn is_validator<C: crate::queries::Client + Sync>(
client: &C,
Expand Down
1 change: 1 addition & 0 deletions crates/tests/src/e2e/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1469,6 +1469,7 @@ pub mod constants {
// Native VP aliases
pub const GOVERNANCE_ADDRESS: &str = "governance";
pub const MASP: &str = "masp";
pub const PGF_ADDRESS: &str = "pgf";

// Fungible token addresses
pub const NAM: &str = "NAM";
Expand Down
Loading

0 comments on commit 941efd5

Please sign in to comment.