Files
khadhroony-bot3/kb-onchain-transport/src/standard_http_economics.rs
2026-07-25 00:48:29 +02:00

306 lines
12 KiB
Rust

// file: kb-onchain-transport/src/standard_http_economics.rs
// version: 2
//! Configurable standard inflation, supply and stake-economics HTTP requests.
/// Inflation governor parameters.
#[derive(Clone, Copy, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcInflationGovernor {
/// Initial inflation rate.
pub initial: f64,
/// Terminal inflation rate.
pub terminal: f64,
/// Annual taper rate.
pub taper: f64,
/// Foundation allocation rate.
pub foundation: f64,
/// Foundation allocation term in years.
pub foundation_term: f64,
}
/// Inflation rates for the current epoch.
#[derive(Clone, Copy, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcInflationRate {
/// Total inflation rate.
pub total: f64,
/// Validator inflation rate.
pub validator: f64,
/// Foundation inflation rate.
pub foundation: f64,
/// Epoch represented by the rates.
pub epoch: u64,
}
/// Inflation reward credited to one requested address.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcInflationReward {
/// Reward epoch.
pub epoch: u64,
/// First effective slot of the rewarded epoch.
pub effective_slot: u64,
/// Reward amount in lamports.
pub amount: u64,
/// Account balance after the reward.
pub post_balance: u64,
/// Legacy vote commission percentage when applicable.
pub commission: std::option::Option<u8>,
/// Vote commission in basis points when exposed by the node.
#[serde(default, skip_serializing_if = "std::option::Option::is_none")]
pub commission_bps: std::option::Option<u16>,
}
/// Current lamport supply breakdown.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcSupply {
/// Total lamport supply.
pub total: u64,
/// Circulating lamport supply.
pub circulating: u64,
/// Non-circulating lamport supply.
pub non_circulating: u64,
/// Non-circulating account list when requested.
#[serde(default)]
pub non_circulating_accounts: std::vec::Vec<std::string::String>,
}
/// Optional commitment accepted by `getInflationGovernor`.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcInflationGovernorConfig {
/// Optional commitment level.
#[serde(skip_serializing_if = "std::option::Option::is_none")]
pub commitment: std::option::Option<crate::RpcCommitmentLevel>,
}
/// Epoch and contextual options accepted by `getInflationReward`.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcEpochConfig {
/// Optional reward epoch.
#[serde(skip_serializing_if = "std::option::Option::is_none")]
pub epoch: std::option::Option<u64>,
/// Optional commitment level.
#[serde(skip_serializing_if = "std::option::Option::is_none")]
pub commitment: std::option::Option<crate::RpcCommitmentLevel>,
/// Optional minimum context slot.
#[serde(skip_serializing_if = "std::option::Option::is_none")]
pub min_context_slot: std::option::Option<u64>,
}
/// Options accepted by `getSupply`.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcSupplyConfig {
/// Optional commitment level.
#[serde(skip_serializing_if = "std::option::Option::is_none")]
pub commitment: std::option::Option<crate::RpcCommitmentLevel>,
/// Optional omission of the potentially large non-circulating account list.
#[serde(skip_serializing_if = "std::option::Option::is_none")]
pub exclude_non_circulating_accounts_list: std::option::Option<bool>,
}
/// Typed `getInflationGovernor` request.
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct GetInflationGovernorRequest {
/// Optional commitment configuration.
pub config: std::option::Option<crate::RpcInflationGovernorConfig>,
}
impl crate::StandardHttpRequest for crate::GetInflationGovernorRequest {
type Response = crate::RpcInflationGovernor;
const METHOD: &'static str = "getInflationGovernor";
fn params(&self) -> kb_core::Result<std::vec::Vec<serde_json::Value>> {
if let std::option::Option::Some(config) = &self.config {
let value = match crate::serialize_parameter(Self::METHOD, config) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(std::vec![value]);
}
return std::result::Result::Ok(std::vec::Vec::new());
}
}
/// Typed `getInflationRate` request.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct GetInflationRateRequest;
impl crate::StandardHttpRequest for crate::GetInflationRateRequest {
type Response = crate::RpcInflationRate;
const METHOD: &'static str = "getInflationRate";
fn params(&self) -> kb_core::Result<std::vec::Vec<serde_json::Value>> {
return std::result::Result::Ok(std::vec::Vec::new());
}
}
/// Typed `getInflationReward` request.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct GetInflationRewardRequest {
/// Account public keys whose rewards are requested, in response order.
pub addresses: std::vec::Vec<std::string::String>,
/// Optional epoch, commitment and minimum-context options.
pub config: std::option::Option<crate::RpcEpochConfig>,
}
impl crate::StandardHttpRequest for crate::GetInflationRewardRequest {
type Response = std::vec::Vec<std::option::Option<crate::RpcInflationReward>>;
const METHOD: &'static str = "getInflationReward";
fn params(&self) -> kb_core::Result<std::vec::Vec<serde_json::Value>> {
let validation_result =
crate::validate_pubkey_list(&self.addresses, "getInflationReward address", usize::MAX);
if let std::result::Result::Err(error) = validation_result {
return std::result::Result::Err(error);
}
let addresses = match crate::serialize_parameter(Self::METHOD, &self.addresses) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let mut params = std::vec![addresses];
if let std::option::Option::Some(config) = &self.config {
let value = match crate::serialize_parameter(Self::METHOD, config) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
params.push(value);
}
return std::result::Result::Ok(params);
}
}
/// Typed `getStakeMinimumDelegation` request.
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct GetStakeMinimumDelegationRequest {
/// Optional commitment and minimum-context options.
pub config: std::option::Option<crate::RpcContextConfig>,
}
impl crate::StandardHttpRequest for crate::GetStakeMinimumDelegationRequest {
type Response = crate::RpcResponse<u64>;
const METHOD: &'static str = "getStakeMinimumDelegation";
fn params(&self) -> kb_core::Result<std::vec::Vec<serde_json::Value>> {
if let std::option::Option::Some(config) = &self.config {
let value = match crate::serialize_parameter(Self::METHOD, config) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(std::vec![value]);
}
return std::result::Result::Ok(std::vec::Vec::new());
}
}
/// Typed `getSupply` request.
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct GetSupplyRequest {
/// Optional commitment and non-circulating-list options.
pub config: std::option::Option<crate::RpcSupplyConfig>,
}
impl crate::StandardHttpRequest for crate::GetSupplyRequest {
type Response = crate::RpcResponse<crate::RpcSupply>;
const METHOD: &'static str = "getSupply";
fn params(&self) -> kb_core::Result<std::vec::Vec<serde_json::Value>> {
if let std::option::Option::Some(config) = &self.config {
let value = match crate::serialize_parameter(Self::METHOD, config) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(std::vec![value]);
}
return std::result::Result::Ok(std::vec::Vec::new());
}
}
#[cfg(test)]
mod tests {
fn pubkey(seed: u8) -> std::string::String {
return bs58::encode([seed; 32]).into_string();
}
#[test]
fn supply_distinguishes_omitted_and_explicit_false_option() {
let omitted = crate::GetSupplyRequest {
config: std::option::Option::Some(crate::RpcSupplyConfig {
commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Finalized),
exclude_non_circulating_accounts_list: std::option::Option::None,
}),
};
let explicit = crate::GetSupplyRequest {
config: std::option::Option::Some(crate::RpcSupplyConfig {
commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Finalized),
exclude_non_circulating_accounts_list: std::option::Option::Some(false),
}),
};
let omitted_params = match crate::StandardHttpRequest::params(&omitted) {
std::result::Result::Ok(params) => params,
std::result::Result::Err(error) => panic!("params failed: {error}"),
};
let explicit_params = match crate::StandardHttpRequest::params(&explicit) {
std::result::Result::Ok(params) => params,
std::result::Result::Err(error) => panic!("params failed: {error}"),
};
assert!(omitted_params[0].get("excludeNonCirculatingAccountsList").is_none());
assert_eq!(
explicit_params[0]["excludeNonCirculatingAccountsList"],
serde_json::Value::Bool(false)
);
}
#[test]
fn inflation_reward_preserves_epoch_commitment_and_minimum_context() {
let request = crate::GetInflationRewardRequest {
addresses: std::vec![pubkey(1), pubkey(2)],
config: std::option::Option::Some(crate::RpcEpochConfig {
epoch: std::option::Option::Some(44),
commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Confirmed),
min_context_slot: std::option::Option::Some(99),
}),
};
let params = match crate::StandardHttpRequest::params(&request) {
std::result::Result::Ok(params) => params,
std::result::Result::Err(error) => panic!("params failed: {error}"),
};
assert_eq!(params[1]["epoch"], serde_json::Value::from(44_u64));
assert_eq!(params[1]["commitment"], serde_json::Value::String("confirmed".to_string()));
assert_eq!(params[1]["minContextSlot"], serde_json::Value::from(99_u64));
let reward = match serde_json::from_value::<crate::RpcInflationReward>(serde_json::json!({
"epoch": 44,
"effectiveSlot": 100,
"amount": 200,
"postBalance": 300,
"commission": 5,
"commissionBps": 575
})) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("inflation reward parsing failed: {error}"),
};
assert_eq!(reward.commission, std::option::Option::Some(5));
assert_eq!(reward.commission_bps, std::option::Option::Some(575));
let empty = crate::GetInflationRewardRequest {
addresses: std::vec::Vec::new(),
config: std::option::Option::None,
};
let empty_params = match crate::StandardHttpRequest::params(&empty) {
std::result::Result::Ok(params) => params,
std::result::Result::Err(error) => panic!("empty params failed: {error}"),
};
assert_eq!(empty_params, std::vec![serde_json::json!([])]);
}
}