v0.1.0-pre.024
This commit is contained in:
305
kb-onchain-transport/src/standard_http_economics.rs
Normal file
305
kb-onchain-transport/src/standard_http_economics.rs
Normal file
@@ -0,0 +1,305 @@
|
||||
// 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!([])]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user