Skip to content

Commit

Permalink
CLI query to estimate staking rewards rate
Browse files Browse the repository at this point in the history
  • Loading branch information
brentstone committed Sep 13, 2024
1 parent 24f9fba commit 3d889d1
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 1 deletion.
61 changes: 61 additions & 0 deletions crates/apps_lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ pub mod cmds {
.subcommand(QueryMetaData::def().display_order(5))
.subcommand(QueryTotalSupply::def().display_order(5))
.subcommand(QueryEffNativeSupply::def().display_order(5))
.subcommand(QueryStakingRewardsRate::def().display_order(5))
// Actions
.subcommand(SignTx::def().display_order(6))
.subcommand(ShieldedSync::def().display_order(6))
Expand Down Expand Up @@ -373,6 +374,8 @@ pub mod cmds {
Self::parse_with_ctx(matches, QueryTotalSupply);
let query_native_supply =
Self::parse_with_ctx(matches, QueryEffNativeSupply);
let query_staking_rewards_rate =
Self::parse_with_ctx(matches, QueryStakingRewardsRate);
let query_find_validator =
Self::parse_with_ctx(matches, QueryFindValidator);
let query_result = Self::parse_with_ctx(matches, QueryResult);
Expand Down Expand Up @@ -449,6 +452,7 @@ pub mod cmds {
.or(query_metadata)
.or(query_total_supply)
.or(query_native_supply)
.or(query_staking_rewards_rate)
.or(query_account)
.or(sign_tx)
.or(shielded_sync)
Expand Down Expand Up @@ -534,6 +538,7 @@ pub mod cmds {
QueryDelegations(QueryDelegations),
QueryTotalSupply(QueryTotalSupply),
QueryEffNativeSupply(QueryEffNativeSupply),
QueryStakingRewardsRate(QueryStakingRewardsRate),
QueryFindValidator(QueryFindValidator),
QueryRawBytes(QueryRawBytes),
QueryProposal(QueryProposal),
Expand Down Expand Up @@ -2118,6 +2123,36 @@ pub mod cmds {
}
}

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

impl SubCmd for QueryStakingRewardsRate {
const CMD: &'static str = "staking-rewards-rate";

fn parse(matches: &ArgMatches) -> Option<Self>
where
Self: Sized,
{
matches.subcommand_matches(Self::CMD).map(|matches| {
QueryStakingRewardsRate(args::QueryStakingRewardsRate::parse(
matches,
))
})
}

fn def() -> App {
App::new(Self::CMD)
.about(wrap!(
"Query the latest estimate of the staking rewards rate \
based on the most recent minted inflation amount at the \
last epoch change."
))
.add_args::<args::QueryStakingRewardsRate<args::CliTypes>>()
}
}

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

Expand Down Expand Up @@ -7155,6 +7190,32 @@ pub mod args {
}
}

impl Args for QueryStakingRewardsRate<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<QueryStakingRewardsRate<SdkTypes>>
for QueryStakingRewardsRate<CliTypes>
{
type Error = std::convert::Infallible;

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

impl Args for QueryFindValidator<CliTypes> {
fn parse(matches: &ArgMatches) -> Self {
let query = Query::parse(matches);
Expand Down
13 changes: 13 additions & 0 deletions crates/apps_lib/src/cli/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,19 @@ impl CliApi {
let namada = ctx.to_sdk(client, io);
rpc::query_effective_native_supply(&namada).await;
}
Sub::QueryStakingRewardsRate(QueryStakingRewardsRate(
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_staking_rewards_rate(&namada).await;
}
Sub::QueryFindValidator(QueryFindValidator(args)) => {
let chain_ctx = ctx.borrow_mut_chain_or_exit();
let ledger_address =
Expand Down
15 changes: 15 additions & 0 deletions crates/apps_lib/src/client/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1364,6 +1364,21 @@ pub async fn query_effective_native_supply<N: Namada>(context: &N) {
display_line!(context.io(), "nam: {}", native_supply.to_string_native());
}

/// Query the staking rewards rate estimate
pub async fn query_staking_rewards_rate<N: Namada>(context: &N) {
let rewards_rate = unwrap_client_response::<N::Client, Dec>(
RPC.vp()
.token()
.staking_rewards_rate(context.client())
.await,
);
display_line!(
context.io(),
"Current annual staking rewards rate: {}",
rewards_rate
);
}

/// Query a validator's state information
pub async fn query_and_print_validator_state(
context: &impl Namada,
Expand Down
35 changes: 35 additions & 0 deletions crates/proof_of_stake/src/rewards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,41 @@ where
Ok(storage.read::<token::Amount>(&key)?.unwrap_or_default())
}

/// Compute an estimation of the most recent staking rewards rate.
pub fn estimate_staking_reward_rate<S, Token, Parameters>(
storage: &S,
) -> Result<Dec>
where
S: StorageRead,
Parameters: parameters::Read<S>,
Token: trans_token::Read<S> + trans_token::Write<S>,
{
// Get needed data in desired form
let total_native_tokens =
Token::get_effective_total_native_supply(storage)?;
let last_staked_ratio = read_last_staked_ratio(storage)?
.expect("Last staked ratio should exist in PoS storage");
let last_inflation_amount = read_last_pos_inflation_amount(storage)?
.expect("Last inflation amount should exist in PoS storage");
let epochs_per_year: u64 = Parameters::epochs_per_year(storage)?;

let total_native_tokens =
Dec::try_from(total_native_tokens).into_storage_result()?;
let last_inflation_amount =
Dec::try_from(last_inflation_amount).into_storage_result()?;

// Estimate annual inflation rate
let est_inflation_rate = checked!(
last_inflation_amount * epochs_per_year / total_native_tokens
)?;

// Estimate annual staking rewards rate
let est_staking_reward_rate =
checked!(est_inflation_rate / last_staked_ratio)?;

Ok(est_staking_reward_rate)
}

#[cfg(test)]
mod tests {
use std::str::FromStr;
Expand Down
7 changes: 7 additions & 0 deletions crates/sdk/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2245,6 +2245,13 @@ pub struct QueryEffNativeSupply<C: NamadaTypes = SdkTypes> {
pub query: Query<C>,
}

/// Query estimate of staking rewards rate
#[derive(Clone, Debug)]
pub struct QueryStakingRewardsRate<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
19 changes: 18 additions & 1 deletion crates/sdk/src/queries/vp/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

use namada_core::address::Address;
use namada_core::token;
use namada_proof_of_stake::rewards::estimate_staking_reward_rate;
use namada_state::{DBIter, StorageHasher, DB};
use namada_token::{
get_effective_total_native_supply, read_denom, read_total_supply,
get_effective_total_native_supply, read_denom, read_total_supply, Dec,
};

use crate::queries::RequestCtx;
Expand All @@ -13,6 +14,7 @@ router! {TOKEN,
( "denomination" / [token: Address] ) -> Option<token::Denomination> = denomination,
( "total_supply" / [token: Address] ) -> token::Amount = total_supply,
( "effective_native_supply" ) -> token::Amount = effective_native_supply,
( "staking_rewards_rate" ) -> Dec = staking_rewards_rate,
}

/// Get the number of decimal places (in base 10) for a
Expand Down Expand Up @@ -51,6 +53,21 @@ where
get_effective_total_native_supply(ctx.state)
}

/// Get the effective total supply of the native token
fn staking_rewards_rate<D, H, V, T>(
ctx: RequestCtx<'_, D, H, V, T>,
) -> namada_storage::Result<Dec>
where
D: 'static + DB + for<'iter> DBIter<'iter> + Sync,
H: 'static + StorageHasher + Sync,
{
estimate_staking_reward_rate::<
_,
crate::token::Store<_>,
crate::parameters::Store<_>,
>(ctx.state)
}

pub mod client_only_methods {
use borsh::BorshDeserialize;
use namada_core::address::Address;
Expand Down
10 changes: 10 additions & 0 deletions crates/sdk/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ use namada_proof_of_stake::types::{
};
use namada_state::LastBlock;
use namada_token::masp::MaspTokenRewardData;
use namada_token::Dec;
use namada_tx::data::{BatchedTxResult, DryRunResult, ResultCode, TxResult};
use namada_tx::event::{Batch as BatchAttr, Code as CodeAttr};
use serde::Serialize;
Expand Down Expand Up @@ -235,6 +236,15 @@ pub async fn get_effective_native_supply<C: Client + Sync>(
)
}

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

/// Check if the given address is a known validator.
pub async fn is_validator<C: namada_io::Client + Sync>(
client: &C,
Expand Down

0 comments on commit 3d889d1

Please sign in to comment.