Skip to content

Commit c39f64c

Browse files
committed
CLI query to estimate staking rewards rate
1 parent f7830f6 commit c39f64c

File tree

7 files changed

+160
-1
lines changed

7 files changed

+160
-1
lines changed

crates/apps_lib/src/cli.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ pub mod cmds {
298298
.subcommand(QueryMetaData::def().display_order(5))
299299
.subcommand(QueryTotalSupply::def().display_order(5))
300300
.subcommand(QueryEffNativeSupply::def().display_order(5))
301+
.subcommand(QueryStakingRewardsRate::def().display_order(5))
301302
// Actions
302303
.subcommand(SignTx::def().display_order(6))
303304
.subcommand(ShieldedSync::def().display_order(6))
@@ -373,6 +374,8 @@ pub mod cmds {
373374
Self::parse_with_ctx(matches, QueryTotalSupply);
374375
let query_native_supply =
375376
Self::parse_with_ctx(matches, QueryEffNativeSupply);
377+
let query_staking_rewards_rate =
378+
Self::parse_with_ctx(matches, QueryStakingRewardsRate);
376379
let query_find_validator =
377380
Self::parse_with_ctx(matches, QueryFindValidator);
378381
let query_result = Self::parse_with_ctx(matches, QueryResult);
@@ -449,6 +452,7 @@ pub mod cmds {
449452
.or(query_metadata)
450453
.or(query_total_supply)
451454
.or(query_native_supply)
455+
.or(query_staking_rewards_rate)
452456
.or(query_account)
453457
.or(sign_tx)
454458
.or(shielded_sync)
@@ -534,6 +538,7 @@ pub mod cmds {
534538
QueryDelegations(QueryDelegations),
535539
QueryTotalSupply(QueryTotalSupply),
536540
QueryEffNativeSupply(QueryEffNativeSupply),
541+
QueryStakingRewardsRate(QueryStakingRewardsRate),
537542
QueryFindValidator(QueryFindValidator),
538543
QueryRawBytes(QueryRawBytes),
539544
QueryProposal(QueryProposal),
@@ -2118,6 +2123,36 @@ pub mod cmds {
21182123
}
21192124
}
21202125

2126+
#[derive(Clone, Debug)]
2127+
pub struct QueryStakingRewardsRate(
2128+
pub args::QueryStakingRewardsRate<args::CliTypes>,
2129+
);
2130+
2131+
impl SubCmd for QueryStakingRewardsRate {
2132+
const CMD: &'static str = "staking-rewards-rate";
2133+
2134+
fn parse(matches: &ArgMatches) -> Option<Self>
2135+
where
2136+
Self: Sized,
2137+
{
2138+
matches.subcommand_matches(Self::CMD).map(|matches| {
2139+
QueryStakingRewardsRate(args::QueryStakingRewardsRate::parse(
2140+
matches,
2141+
))
2142+
})
2143+
}
2144+
2145+
fn def() -> App {
2146+
App::new(Self::CMD)
2147+
.about(wrap!(
2148+
"Query the latest estimate of the staking rewards rate \
2149+
based on the most recent minted inflation amount at the \
2150+
last epoch change."
2151+
))
2152+
.add_args::<args::QueryStakingRewardsRate<args::CliTypes>>()
2153+
}
2154+
}
2155+
21212156
#[derive(Clone, Debug)]
21222157
pub struct QueryFindValidator(pub args::QueryFindValidator<args::CliTypes>);
21232158

@@ -7157,6 +7192,32 @@ pub mod args {
71577192
}
71587193
}
71597194

7195+
impl Args for QueryStakingRewardsRate<CliTypes> {
7196+
fn parse(matches: &ArgMatches) -> Self {
7197+
let query = Query::parse(matches);
7198+
Self { query }
7199+
}
7200+
7201+
fn def(app: App) -> App {
7202+
app.add_args::<Query<CliTypes>>()
7203+
}
7204+
}
7205+
7206+
impl CliToSdk<QueryStakingRewardsRate<SdkTypes>>
7207+
for QueryStakingRewardsRate<CliTypes>
7208+
{
7209+
type Error = std::convert::Infallible;
7210+
7211+
fn to_sdk(
7212+
self,
7213+
ctx: &mut Context,
7214+
) -> Result<QueryStakingRewardsRate<SdkTypes>, Self::Error> {
7215+
Ok(QueryStakingRewardsRate::<SdkTypes> {
7216+
query: self.query.to_sdk(ctx)?,
7217+
})
7218+
}
7219+
}
7220+
71607221
impl Args for QueryFindValidator<CliTypes> {
71617222
fn parse(matches: &ArgMatches) -> Self {
71627223
let query = Query::parse(matches);

crates/apps_lib/src/cli/client.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,19 @@ impl CliApi {
661661
let namada = ctx.to_sdk(client, io);
662662
rpc::query_effective_native_supply(&namada).await;
663663
}
664+
Sub::QueryStakingRewardsRate(QueryStakingRewardsRate(
665+
args,
666+
)) => {
667+
let chain_ctx = ctx.borrow_mut_chain_or_exit();
668+
let ledger_address =
669+
chain_ctx.get(&args.query.ledger_address);
670+
let client = client.unwrap_or_else(|| {
671+
C::from_tendermint_address(&ledger_address)
672+
});
673+
client.wait_until_node_is_synced(&io).await?;
674+
let namada = ctx.to_sdk(client, io);
675+
rpc::query_staking_rewards_rate(&namada).await;
676+
}
664677
Sub::QueryFindValidator(QueryFindValidator(args)) => {
665678
let chain_ctx = ctx.borrow_mut_chain_or_exit();
666679
let ledger_address =

crates/apps_lib/src/client/rpc.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use namada_sdk::address::{Address, InternalAddress, MASP};
1515
use namada_sdk::chain::{BlockHeight, Epoch};
1616
use namada_sdk::collections::{HashMap, HashSet};
1717
use namada_sdk::control_flow::time::{Duration, Instant};
18+
use namada_sdk::dec::Dec;
1819
use namada_sdk::events::Event;
1920
use namada_sdk::governance::parameters::GovernanceParameters;
2021
use namada_sdk::governance::pgf::parameters::PgfParameters;
@@ -1364,6 +1365,21 @@ pub async fn query_effective_native_supply<N: Namada>(context: &N) {
13641365
display_line!(context.io(), "nam: {}", native_supply.to_string_native());
13651366
}
13661367

1368+
/// Query the staking rewards rate estimate
1369+
pub async fn query_staking_rewards_rate<N: Namada>(context: &N) {
1370+
let rewards_rate = unwrap_client_response::<N::Client, Dec>(
1371+
RPC.vp()
1372+
.token()
1373+
.staking_rewards_rate(context.client())
1374+
.await,
1375+
);
1376+
display_line!(
1377+
context.io(),
1378+
"Current annual staking rewards rate: {}",
1379+
rewards_rate
1380+
);
1381+
}
1382+
13671383
/// Query a validator's state information
13681384
pub async fn query_and_print_validator_state(
13691385
context: &impl Namada,

crates/proof_of_stake/src/rewards.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,41 @@ where
656656
Ok(storage.read::<token::Amount>(&key)?.unwrap_or_default())
657657
}
658658

659+
/// Compute an estimation of the most recent staking rewards rate.
660+
pub fn estimate_staking_reward_rate<S, Token, Parameters>(
661+
storage: &S,
662+
) -> Result<Dec>
663+
where
664+
S: StorageRead,
665+
Parameters: parameters::Read<S>,
666+
Token: trans_token::Read<S> + trans_token::Write<S>,
667+
{
668+
// Get needed data in desired form
669+
let total_native_tokens =
670+
Token::get_effective_total_native_supply(storage)?;
671+
let last_staked_ratio = read_last_staked_ratio(storage)?
672+
.expect("Last staked ratio should exist in PoS storage");
673+
let last_inflation_amount = read_last_pos_inflation_amount(storage)?
674+
.expect("Last inflation amount should exist in PoS storage");
675+
let epochs_per_year: u64 = Parameters::epochs_per_year(storage)?;
676+
677+
let total_native_tokens =
678+
Dec::try_from(total_native_tokens).into_storage_result()?;
679+
let last_inflation_amount =
680+
Dec::try_from(last_inflation_amount).into_storage_result()?;
681+
682+
// Estimate annual inflation rate
683+
let est_inflation_rate = checked!(
684+
last_inflation_amount * epochs_per_year / total_native_tokens
685+
)?;
686+
687+
// Estimate annual staking rewards rate
688+
let est_staking_reward_rate =
689+
checked!(est_inflation_rate / last_staked_ratio)?;
690+
691+
Ok(est_staking_reward_rate)
692+
}
693+
659694
#[cfg(test)]
660695
mod tests {
661696
use std::str::FromStr;

crates/sdk/src/args.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2245,6 +2245,13 @@ pub struct QueryEffNativeSupply<C: NamadaTypes = SdkTypes> {
22452245
pub query: Query<C>,
22462246
}
22472247

2248+
/// Query estimate of staking rewards rate
2249+
#[derive(Clone, Debug)]
2250+
pub struct QueryStakingRewardsRate<C: NamadaTypes = SdkTypes> {
2251+
/// Common query args
2252+
pub query: Query<C>,
2253+
}
2254+
22482255
/// Query PoS to find a validator
22492256
#[derive(Clone, Debug)]
22502257
pub struct QueryFindValidator<C: NamadaTypes = SdkTypes> {

crates/sdk/src/queries/vp/token.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
33
use namada_core::address::Address;
44
use namada_core::token;
5+
use namada_proof_of_stake::rewards::estimate_staking_reward_rate;
56
use namada_state::{DBIter, StorageHasher, DB};
67
use namada_token::{
7-
get_effective_total_native_supply, read_denom, read_total_supply,
8+
get_effective_total_native_supply, read_denom, read_total_supply, Dec,
89
};
910

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

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

56+
/// Get the effective total supply of the native token
57+
fn staking_rewards_rate<D, H, V, T>(
58+
ctx: RequestCtx<'_, D, H, V, T>,
59+
) -> namada_storage::Result<Dec>
60+
where
61+
D: 'static + DB + for<'iter> DBIter<'iter> + Sync,
62+
H: 'static + StorageHasher + Sync,
63+
{
64+
estimate_staking_reward_rate::<
65+
_,
66+
crate::token::Store<_>,
67+
crate::parameters::Store<_>,
68+
>(ctx.state)
69+
}
70+
5471
pub mod client_only_methods {
5572
use borsh::BorshDeserialize;
5673
use namada_core::address::Address;

crates/sdk/src/rpc.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ use namada_proof_of_stake::types::{
4646
};
4747
use namada_state::LastBlock;
4848
use namada_token::masp::MaspTokenRewardData;
49+
use namada_token::Dec;
4950
use namada_tx::data::{BatchedTxResult, DryRunResult, ResultCode, TxResult};
5051
use namada_tx::event::{Batch as BatchAttr, Code as CodeAttr};
5152
use serde::Serialize;
@@ -237,6 +238,15 @@ pub async fn get_effective_native_supply<C: Client + Sync>(
237238
)
238239
}
239240

241+
/// Query the effective total supply of the native token
242+
pub async fn get_staking_rewards_rate<C: Client + Sync>(
243+
client: &C,
244+
) -> Result<Dec, error::Error> {
245+
convert_response::<C, _>(
246+
RPC.vp().token().staking_rewards_rate(client).await,
247+
)
248+
}
249+
240250
/// Check if the given address is a known validator.
241251
pub async fn is_validator<C: namada_io::Client + Sync>(
242252
client: &C,

0 commit comments

Comments
 (0)