v0.2.4-pre.002

This commit is contained in:
2026-08-18 20:12:46 +02:00
parent cb19742cf7
commit cf8d79567d
19 changed files with 1869 additions and 8 deletions

View File

@@ -0,0 +1,377 @@
// file: crates/ksp-onchain-transport-lib/src/rpc_economics.rs
// version: 1
/// Inflation-governor values returned by `getInflationGovernor`.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SolanaInflationGovernor {
initial: f64,
terminal: f64,
taper: f64,
foundation: f64,
foundation_term: f64,
}
impl SolanaInflationGovernor {
/// Returns the initial inflation rate.
#[must_use]
pub const fn initial(&self) -> f64 {
return self.initial;
}
/// Returns the terminal inflation rate.
#[must_use]
pub const fn terminal(&self) -> f64 {
return self.terminal;
}
/// Returns the taper/disinflation parameter reported by the runtime.
#[must_use]
pub const fn taper(&self) -> f64 {
return self.taper;
}
/// Returns the foundation allocation rate.
#[must_use]
pub const fn foundation(&self) -> f64 {
return self.foundation;
}
/// Returns the foundation allocation term.
#[must_use]
pub const fn foundation_term(&self) -> f64 {
return self.foundation_term;
}
/// Decodes an inflation-governor result from its Solana JSON wire shape.
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
let decoded = crate::decode_wire_json::<WireInflationGovernor>(method, value);
return match decoded {
std::result::Result::Ok(wire) => std::result::Result::Ok(Self {
initial: wire.initial,
terminal: wire.terminal,
taper: wire.taper,
foundation: wire.foundation,
foundation_term: wire.foundation_term,
}),
std::result::Result::Err(error) => std::result::Result::Err(error),
};
}
}
/// Current inflation-rate values returned by `getInflationRate`.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SolanaInflationRate {
total: f64,
validator: f64,
foundation: f64,
epoch: u64,
}
impl SolanaInflationRate {
/// Returns the total inflation rate.
#[must_use]
pub const fn total(&self) -> f64 {
return self.total;
}
/// Returns the validator inflation rate.
#[must_use]
pub const fn validator(&self) -> f64 {
return self.validator;
}
/// Returns the foundation inflation rate.
#[must_use]
pub const fn foundation(&self) -> f64 {
return self.foundation;
}
/// Returns the epoch associated with the rate.
#[must_use]
pub const fn epoch(&self) -> u64 {
return self.epoch;
}
/// Decodes an inflation-rate result from its Solana JSON wire shape.
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
let decoded = crate::decode_wire_json::<WireInflationRate>(method, value);
return match decoded {
std::result::Result::Ok(wire) => {
std::result::Result::Ok(Self { total: wire.total, validator: wire.validator, foundation: wire.foundation, epoch: wire.epoch })
},
std::result::Result::Err(error) => std::result::Result::Err(error),
};
}
}
/// Optional epoch/context configuration accepted by `getInflationReward`.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct SolanaInflationRewardConfig {
epoch: std::option::Option<u64>,
commitment: std::option::Option<crate::SolanaCommitment>,
min_context_slot: std::option::Option<u64>,
}
impl SolanaInflationRewardConfig {
/// Creates an inflation-reward configuration.
#[must_use]
pub const fn new(
epoch: std::option::Option<u64>,
commitment: std::option::Option<crate::SolanaCommitment>,
min_context_slot: std::option::Option<u64>,
) -> Self {
return Self { epoch, commitment, min_context_slot };
}
/// Returns the optional target epoch.
#[must_use]
pub const fn epoch(&self) -> std::option::Option<u64> {
return self.epoch;
}
/// Returns the optional commitment level.
#[must_use]
pub const fn commitment(&self) -> std::option::Option<crate::SolanaCommitment> {
return self.commitment;
}
/// Returns the optional minimum context slot.
#[must_use]
pub const fn min_context_slot(&self) -> std::option::Option<u64> {
return self.min_context_slot;
}
/// Returns whether this config would serialize to an empty object.
pub(crate) const fn is_empty(&self) -> bool {
return self.epoch.is_none() && self.commitment.is_none() && self.min_context_slot.is_none();
}
/// Serializes this config to the exact Solana JSON-RPC object.
#[must_use]
pub(crate) fn to_json_value(self) -> serde_json::Value {
let mut object = serde_json::Map::new();
if let std::option::Option::Some(epoch) = self.epoch {
object.insert("epoch".to_owned(), serde_json::Value::Number(epoch.into()));
}
if let std::option::Option::Some(commitment) = self.commitment {
object.insert("commitment".to_owned(), serde_json::Value::String(commitment.as_str().to_owned()));
}
if let std::option::Option::Some(min_context_slot) = self.min_context_slot {
object.insert("minContextSlot".to_owned(), serde_json::Value::Number(min_context_slot.into()));
}
return serde_json::Value::Object(object);
}
}
/// One non-null positional reward returned by `getInflationReward`.
#[derive(Clone, Debug, PartialEq)]
pub struct SolanaInflationReward {
epoch: u64,
effective_slot: u64,
amount: u64,
post_balance: u64,
commission: std::option::Option<u8>,
commission_bps: crate::SolanaWireField<u16>,
}
impl SolanaInflationReward {
/// Returns the rewarded epoch.
#[must_use]
pub const fn epoch(&self) -> u64 {
return self.epoch;
}
/// Returns the slot at which the reward became effective.
#[must_use]
pub const fn effective_slot(&self) -> u64 {
return self.effective_slot;
}
/// Returns the reward amount in lamports.
#[must_use]
pub const fn amount(&self) -> u64 {
return self.amount;
}
/// Returns the post-reward account balance in lamports.
#[must_use]
pub const fn post_balance(&self) -> u64 {
return self.post_balance;
}
/// Returns the nullable legacy/effective commission percentage.
#[must_use]
pub const fn commission(&self) -> std::option::Option<u8> {
return self.commission;
}
/// Returns the basis-point commission while preserving omitted/null/present wire states.
#[must_use]
pub const fn commission_bps(&self) -> &crate::SolanaWireField<u16> {
return &self.commission_bps;
}
/// Decodes one non-null inflation reward from its Solana JSON wire shape.
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
let decoded = crate::decode_wire_json::<WireInflationReward>(method, value);
return match decoded {
std::result::Result::Ok(wire) => std::result::Result::Ok(Self {
epoch: wire.epoch,
effective_slot: wire.effective_slot,
amount: wire.amount,
post_balance: wire.post_balance,
commission: wire.commission,
commission_bps: wire.commission_bps,
}),
std::result::Result::Err(error) => std::result::Result::Err(error),
};
}
}
/// Optional configuration accepted by `getSupply`.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct SolanaSupplyConfig {
commitment: std::option::Option<crate::SolanaCommitment>,
exclude_non_circulating_accounts_list: std::option::Option<bool>,
}
impl SolanaSupplyConfig {
/// Creates a supply configuration.
#[must_use]
pub const fn new(commitment: std::option::Option<crate::SolanaCommitment>, exclude_non_circulating_accounts_list: std::option::Option<bool>) -> Self {
return Self { commitment, exclude_non_circulating_accounts_list };
}
/// Returns the optional commitment level.
#[must_use]
pub const fn commitment(&self) -> std::option::Option<crate::SolanaCommitment> {
return self.commitment;
}
/// Returns whether the request explicitly controls omission of the non-circulating account list.
#[must_use]
pub const fn exclude_non_circulating_accounts_list(&self) -> std::option::Option<bool> {
return self.exclude_non_circulating_accounts_list;
}
/// Returns whether this config would serialize to an empty object.
pub(crate) const fn is_empty(&self) -> bool {
return self.commitment.is_none() && self.exclude_non_circulating_accounts_list.is_none();
}
/// Serializes this config to the exact Solana JSON-RPC object.
#[must_use]
pub(crate) fn to_json_value(self) -> serde_json::Value {
let mut object = serde_json::Map::new();
if let std::option::Option::Some(commitment) = self.commitment {
object.insert("commitment".to_owned(), serde_json::Value::String(commitment.as_str().to_owned()));
}
if let std::option::Option::Some(exclude) = self.exclude_non_circulating_accounts_list {
object.insert("excludeNonCirculatingAccountsList".to_owned(), serde_json::Value::Bool(exclude));
}
return serde_json::Value::Object(object);
}
}
/// Supply totals returned inside the contextual `getSupply` response.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SolanaSupply {
total: u64,
circulating: u64,
non_circulating: u64,
non_circulating_accounts: std::vec::Vec<ksp_core_lib::Pubkey>,
}
impl SolanaSupply {
/// Returns the total supply in lamports.
#[must_use]
pub const fn total(&self) -> u64 {
return self.total;
}
/// Returns the circulating supply in lamports.
#[must_use]
pub const fn circulating(&self) -> u64 {
return self.circulating;
}
/// Returns the non-circulating supply in lamports.
#[must_use]
pub const fn non_circulating(&self) -> u64 {
return self.non_circulating;
}
/// Returns the ordered non-circulating account list supplied by the runtime.
#[must_use]
pub fn non_circulating_accounts(&self) -> &[ksp_core_lib::Pubkey] {
return self.non_circulating_accounts.as_slice();
}
/// Decodes a supply result from its Solana JSON wire shape.
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
let decoded = crate::decode_wire_json::<WireSupply>(method, value);
let wire = match decoded {
std::result::Result::Ok(wire) => wire,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let mut accounts = std::vec::Vec::with_capacity(wire.non_circulating_accounts.len());
for account in wire.non_circulating_accounts {
let parsed = crate::parse_wire_pubkey(method, "nonCirculatingAccounts", account.as_str());
match parsed {
std::result::Result::Ok(pubkey) => accounts.push(pubkey),
std::result::Result::Err(error) => return std::result::Result::Err(error),
}
}
return std::result::Result::Ok(Self {
total: wire.total,
circulating: wire.circulating,
non_circulating: wire.non_circulating,
non_circulating_accounts: accounts,
});
}
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct WireInflationGovernor {
initial: f64,
terminal: f64,
taper: f64,
foundation: f64,
foundation_term: f64,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct WireInflationRate {
total: f64,
validator: f64,
foundation: f64,
epoch: u64,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct WireInflationReward {
epoch: u64,
effective_slot: u64,
amount: u64,
post_balance: u64,
#[serde(default)]
commission: std::option::Option<u8>,
#[serde(default)]
commission_bps: crate::SolanaWireField<u16>,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct WireSupply {
total: u64,
circulating: u64,
non_circulating: u64,
non_circulating_accounts: std::vec::Vec<std::string::String>,
}
#[cfg(test)]
#[path = "../unit_tests/rpc_economics.rs"]
mod tests;