Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI queries for total supply info #3691

Merged
merged 6 commits into from
Aug 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1458,6 +1458,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
Loading