v0.2.4-pre.002
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-onchain-transport-lib/src/lib.rs
|
||||
// version: 16
|
||||
// version: 17
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
#![forbid(unsafe_code)]
|
||||
@@ -12,7 +12,8 @@
|
||||
//! are available. The four typed Solana HTTP foundation canaries plus all 22 typed `0.2.2` Accounts, Tokens and Cluster wrappers execute real JSON-RPC
|
||||
//! requests through the shared transport path. `0.2.3` exposes its shared Transaction wire/config primitives and all eleven Transaction wrappers through
|
||||
//! `pre.007`: eight reads, two write submissions with centralized no-resend protection, and retry-safe `simulateTransaction`, including complete
|
||||
//! modern/legacy `getTransaction` coverage. The `0.2.4` family remains staged.
|
||||
//! modern/legacy `getTransaction` coverage. `0.2.4-pre.002` adds the shared Blocks/Economics wire, config and result primitives without yet advancing
|
||||
//! any `V0_2_4` method to a typed wrapper.
|
||||
|
||||
mod client;
|
||||
mod constants;
|
||||
@@ -22,9 +23,11 @@ mod json_rpc;
|
||||
mod pool;
|
||||
mod resilience;
|
||||
mod rpc_accounts;
|
||||
mod rpc_blocks;
|
||||
mod rpc_canary;
|
||||
mod rpc_cluster;
|
||||
mod rpc_common;
|
||||
mod rpc_economics;
|
||||
mod rpc_method;
|
||||
mod rpc_tokens;
|
||||
mod rpc_transactions;
|
||||
@@ -126,6 +129,28 @@ pub use self::rpc_accounts::SolanaProgramAccountFilter;
|
||||
pub use self::rpc_accounts::SolanaProgramAccountsConfig;
|
||||
/// Result union returned by `getProgramAccounts` with or without an RPC context.
|
||||
pub use self::rpc_accounts::SolanaProgramAccountsResult;
|
||||
/// Commitment distribution returned by `getBlockCommitment`.
|
||||
pub use self::rpc_blocks::SolanaBlockCommitment;
|
||||
/// Block-production counts returned by `getBlockProduction` before the shared RPC context wrapper is applied.
|
||||
pub use self::rpc_blocks::SolanaBlockProduction;
|
||||
/// Optional configuration accepted by `getBlockProduction`.
|
||||
pub use self::rpc_blocks::SolanaBlockProductionConfig;
|
||||
/// Slot range accepted inside `getBlockProduction` configuration.
|
||||
pub use self::rpc_blocks::SolanaBlockProductionRange;
|
||||
/// Effective range reported inside `getBlockProduction` results.
|
||||
pub use self::rpc_blocks::SolanaBlockProductionResultRange;
|
||||
/// One reward entry returned in a confirmed block.
|
||||
pub use self::rpc_blocks::SolanaBlockReward;
|
||||
/// One transaction entry returned inside a confirmed block.
|
||||
pub use self::rpc_blocks::SolanaBlockTransaction;
|
||||
/// Confirmed block wire result returned by `getBlock` when the RPC result is non-null.
|
||||
pub use self::rpc_blocks::SolanaConfirmedBlock;
|
||||
/// Modern configuration object accepted by `getBlock`.
|
||||
pub use self::rpc_blocks::SolanaGetBlockConfig;
|
||||
/// One entry returned by `getRecentPerformanceSamples`.
|
||||
pub use self::rpc_blocks::SolanaPerformanceSample;
|
||||
/// Transaction detail level accepted by modern `getBlock` requests.
|
||||
pub use self::rpc_blocks::SolanaTransactionDetails;
|
||||
/// Optional typed configuration for the `getBalance` canary.
|
||||
pub use self::rpc_canary::GetBalanceConfig;
|
||||
/// Typed lamport balance returned by the `getBalance` canary.
|
||||
@@ -172,6 +197,18 @@ pub use self::rpc_common::SolanaRpcResponse;
|
||||
pub(crate) use self::rpc_common::decode_wire_json;
|
||||
/// Parses a base58 public key without echoing its wire value into diagnostics for typed RPC adapters.
|
||||
pub(crate) use self::rpc_common::parse_wire_pubkey;
|
||||
/// Inflation-governor values returned by `getInflationGovernor`.
|
||||
pub use self::rpc_economics::SolanaInflationGovernor;
|
||||
/// Current inflation-rate values returned by `getInflationRate`.
|
||||
pub use self::rpc_economics::SolanaInflationRate;
|
||||
/// One non-null positional reward returned by `getInflationReward`.
|
||||
pub use self::rpc_economics::SolanaInflationReward;
|
||||
/// Optional epoch/context configuration accepted by `getInflationReward`.
|
||||
pub use self::rpc_economics::SolanaInflationRewardConfig;
|
||||
/// Supply totals returned inside the contextual `getSupply` response.
|
||||
pub use self::rpc_economics::SolanaSupply;
|
||||
/// Optional configuration accepted by `getSupply`.
|
||||
pub use self::rpc_economics::SolanaSupplyConfig;
|
||||
/// Functional category used by the audited Solana HTTP JSON-RPC registry.
|
||||
pub use self::rpc_method::HttpRpcCategory;
|
||||
/// Release that owns typed KSP coverage for one audited HTTP RPC method.
|
||||
|
||||
712
crates/ksp-onchain-transport-lib/src/rpc_blocks.rs
Normal file
712
crates/ksp-onchain-transport-lib/src/rpc_blocks.rs
Normal file
@@ -0,0 +1,712 @@
|
||||
// file: crates/ksp-onchain-transport-lib/src/rpc_blocks.rs
|
||||
// version: 1
|
||||
|
||||
/// Transaction detail level accepted by modern `getBlock` requests.
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
|
||||
pub enum SolanaTransactionDetails {
|
||||
/// Return complete transaction payloads and metadata.
|
||||
#[default]
|
||||
Full,
|
||||
/// Return transaction signatures only.
|
||||
Signatures,
|
||||
/// Return no transaction payloads.
|
||||
None,
|
||||
/// Return the account-list transaction representation.
|
||||
Accounts,
|
||||
}
|
||||
|
||||
impl SolanaTransactionDetails {
|
||||
/// Returns the Solana JSON-RPC wire string.
|
||||
#[must_use]
|
||||
pub const fn as_str(self) -> &'static str {
|
||||
return match self {
|
||||
Self::Full => "full",
|
||||
Self::Signatures => "signatures",
|
||||
Self::None => "none",
|
||||
Self::Accounts => "accounts",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Modern configuration object accepted by `getBlock`.
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub struct SolanaGetBlockConfig {
|
||||
commitment: std::option::Option<crate::SolanaCommitment>,
|
||||
encoding: std::option::Option<crate::SolanaTransactionEncoding>,
|
||||
transaction_details: std::option::Option<crate::SolanaTransactionDetails>,
|
||||
max_supported_transaction_version: std::option::Option<u8>,
|
||||
rewards: std::option::Option<bool>,
|
||||
}
|
||||
|
||||
impl SolanaGetBlockConfig {
|
||||
/// Creates a modern `getBlock` configuration.
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
commitment: std::option::Option<crate::SolanaCommitment>,
|
||||
encoding: std::option::Option<crate::SolanaTransactionEncoding>,
|
||||
transaction_details: std::option::Option<crate::SolanaTransactionDetails>,
|
||||
max_supported_transaction_version: std::option::Option<u8>,
|
||||
rewards: std::option::Option<bool>,
|
||||
) -> Self {
|
||||
return Self { commitment, encoding, transaction_details, max_supported_transaction_version, rewards };
|
||||
}
|
||||
|
||||
/// Returns the optional commitment level.
|
||||
#[must_use]
|
||||
pub const fn commitment(&self) -> std::option::Option<crate::SolanaCommitment> {
|
||||
return self.commitment;
|
||||
}
|
||||
|
||||
/// Returns the optional transaction encoding.
|
||||
#[must_use]
|
||||
pub const fn encoding(&self) -> std::option::Option<crate::SolanaTransactionEncoding> {
|
||||
return self.encoding;
|
||||
}
|
||||
|
||||
/// Returns the optional transaction detail level.
|
||||
#[must_use]
|
||||
pub const fn transaction_details(&self) -> std::option::Option<crate::SolanaTransactionDetails> {
|
||||
return self.transaction_details;
|
||||
}
|
||||
|
||||
/// Returns the highest transaction version the caller declares it can consume.
|
||||
#[must_use]
|
||||
pub const fn max_supported_transaction_version(&self) -> std::option::Option<u8> {
|
||||
return self.max_supported_transaction_version;
|
||||
}
|
||||
|
||||
/// Returns whether rewards were explicitly requested or suppressed.
|
||||
#[must_use]
|
||||
pub const fn rewards(&self) -> std::option::Option<bool> {
|
||||
return self.rewards;
|
||||
}
|
||||
|
||||
/// Returns whether this config would serialize to an empty object.
|
||||
pub(crate) const fn is_empty(&self) -> bool {
|
||||
return self.commitment.is_none()
|
||||
&& self.encoding.is_none()
|
||||
&& self.transaction_details.is_none()
|
||||
&& self.max_supported_transaction_version.is_none()
|
||||
&& self.rewards.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(encoding) = self.encoding {
|
||||
object.insert("encoding".to_owned(), serde_json::Value::String(encoding.as_str().to_owned()));
|
||||
}
|
||||
if let std::option::Option::Some(details) = self.transaction_details {
|
||||
object.insert("transactionDetails".to_owned(), serde_json::Value::String(details.as_str().to_owned()));
|
||||
}
|
||||
if let std::option::Option::Some(version) = self.max_supported_transaction_version {
|
||||
object.insert("maxSupportedTransactionVersion".to_owned(), serde_json::Value::Number(version.into()));
|
||||
}
|
||||
if let std::option::Option::Some(rewards) = self.rewards {
|
||||
object.insert("rewards".to_owned(), serde_json::Value::Bool(rewards));
|
||||
}
|
||||
return serde_json::Value::Object(object);
|
||||
}
|
||||
}
|
||||
|
||||
/// Slot range accepted inside `getBlockProduction` configuration.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct SolanaBlockProductionRange {
|
||||
first_slot: u64,
|
||||
last_slot: std::option::Option<u64>,
|
||||
}
|
||||
|
||||
impl SolanaBlockProductionRange {
|
||||
/// Creates a block-production range.
|
||||
#[must_use]
|
||||
pub const fn new(first_slot: u64, last_slot: std::option::Option<u64>) -> Self {
|
||||
return Self { first_slot, last_slot };
|
||||
}
|
||||
|
||||
/// Returns the first slot in the requested range.
|
||||
#[must_use]
|
||||
pub const fn first_slot(&self) -> u64 {
|
||||
return self.first_slot;
|
||||
}
|
||||
|
||||
/// Returns the optional last slot in the requested range.
|
||||
#[must_use]
|
||||
pub const fn last_slot(&self) -> std::option::Option<u64> {
|
||||
return self.last_slot;
|
||||
}
|
||||
|
||||
/// Serializes this range 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();
|
||||
object.insert("firstSlot".to_owned(), serde_json::Value::Number(self.first_slot.into()));
|
||||
if let std::option::Option::Some(last_slot) = self.last_slot {
|
||||
object.insert("lastSlot".to_owned(), serde_json::Value::Number(last_slot.into()));
|
||||
}
|
||||
return serde_json::Value::Object(object);
|
||||
}
|
||||
}
|
||||
|
||||
/// Optional configuration accepted by `getBlockProduction`.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct SolanaBlockProductionConfig {
|
||||
commitment: std::option::Option<crate::SolanaCommitment>,
|
||||
identity: std::option::Option<ksp_core_lib::Pubkey>,
|
||||
range: std::option::Option<crate::SolanaBlockProductionRange>,
|
||||
}
|
||||
|
||||
impl SolanaBlockProductionConfig {
|
||||
/// Creates a block-production configuration.
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
commitment: std::option::Option<crate::SolanaCommitment>,
|
||||
identity: std::option::Option<ksp_core_lib::Pubkey>,
|
||||
range: std::option::Option<crate::SolanaBlockProductionRange>,
|
||||
) -> Self {
|
||||
return Self { commitment, identity, range };
|
||||
}
|
||||
|
||||
/// Returns the optional commitment level.
|
||||
#[must_use]
|
||||
pub const fn commitment(&self) -> std::option::Option<crate::SolanaCommitment> {
|
||||
return self.commitment;
|
||||
}
|
||||
|
||||
/// Returns the optional validator identity filter.
|
||||
#[must_use]
|
||||
pub const fn identity(&self) -> std::option::Option<&ksp_core_lib::Pubkey> {
|
||||
return self.identity.as_ref();
|
||||
}
|
||||
|
||||
/// Returns the optional slot range.
|
||||
#[must_use]
|
||||
pub const fn range(&self) -> std::option::Option<&crate::SolanaBlockProductionRange> {
|
||||
return self.range.as_ref();
|
||||
}
|
||||
|
||||
/// Returns whether this config would serialize to an empty object.
|
||||
pub(crate) const fn is_empty(&self) -> bool {
|
||||
return self.commitment.is_none() && self.identity.is_none() && self.range.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(identity) = self.identity.as_ref() {
|
||||
object.insert("identity".to_owned(), serde_json::Value::String(identity.to_string()));
|
||||
}
|
||||
if let std::option::Option::Some(range) = self.range {
|
||||
object.insert("range".to_owned(), range.to_json_value());
|
||||
}
|
||||
return serde_json::Value::Object(object);
|
||||
}
|
||||
}
|
||||
|
||||
/// Commitment distribution returned by `getBlockCommitment`.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct SolanaBlockCommitment {
|
||||
commitment: std::option::Option<std::vec::Vec<u64>>,
|
||||
total_stake: u64,
|
||||
}
|
||||
|
||||
impl SolanaBlockCommitment {
|
||||
/// Returns the nullable commitment stake distribution.
|
||||
#[must_use]
|
||||
pub fn commitment(&self) -> std::option::Option<&[u64]> {
|
||||
return self.commitment.as_deref();
|
||||
}
|
||||
|
||||
/// Returns the total active stake considered by the response.
|
||||
#[must_use]
|
||||
pub const fn total_stake(&self) -> u64 {
|
||||
return self.total_stake;
|
||||
}
|
||||
|
||||
/// Decodes a block-commitment 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::<WireBlockCommitment>(method, value);
|
||||
return match decoded {
|
||||
std::result::Result::Ok(wire) => std::result::Result::Ok(Self { commitment: wire.commitment, total_stake: wire.total_stake }),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Effective range reported inside `getBlockProduction` results.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct SolanaBlockProductionResultRange {
|
||||
first_slot: u64,
|
||||
last_slot: u64,
|
||||
}
|
||||
|
||||
impl SolanaBlockProductionResultRange {
|
||||
/// Returns the first slot in the effective range.
|
||||
#[must_use]
|
||||
pub const fn first_slot(&self) -> u64 {
|
||||
return self.first_slot;
|
||||
}
|
||||
|
||||
/// Returns the last slot in the effective range.
|
||||
#[must_use]
|
||||
pub const fn last_slot(&self) -> u64 {
|
||||
return self.last_slot;
|
||||
}
|
||||
}
|
||||
|
||||
/// Block-production counts returned by `getBlockProduction` before the shared RPC context wrapper is applied.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct SolanaBlockProduction {
|
||||
by_identity: std::collections::BTreeMap<ksp_core_lib::Pubkey, (usize, usize)>,
|
||||
range: crate::SolanaBlockProductionResultRange,
|
||||
}
|
||||
|
||||
impl SolanaBlockProduction {
|
||||
/// Returns validator identities mapped to `(leader slots, blocks produced)` counts.
|
||||
#[must_use]
|
||||
pub const fn by_identity(&self) -> &std::collections::BTreeMap<ksp_core_lib::Pubkey, (usize, usize)> {
|
||||
return &self.by_identity;
|
||||
}
|
||||
|
||||
/// Returns the effective slot range reported by the runtime.
|
||||
#[must_use]
|
||||
pub const fn range(&self) -> &crate::SolanaBlockProductionResultRange {
|
||||
return &self.range;
|
||||
}
|
||||
|
||||
/// Decodes a block-production 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::<WireBlockProduction>(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 by_identity = std::collections::BTreeMap::new();
|
||||
for (identity, counts) in wire.by_identity {
|
||||
let parsed = crate::parse_wire_pubkey(method, "byIdentity", identity.as_str());
|
||||
let identity = match parsed {
|
||||
std::result::Result::Ok(identity) => identity,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
by_identity.insert(identity, counts);
|
||||
}
|
||||
return std::result::Result::Ok(Self {
|
||||
by_identity,
|
||||
range: crate::SolanaBlockProductionResultRange { first_slot: wire.range.first_slot, last_slot: wire.range.last_slot },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// One reward entry returned in a confirmed block.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SolanaBlockReward {
|
||||
pubkey: ksp_core_lib::Pubkey,
|
||||
lamports: i64,
|
||||
post_balance: u64,
|
||||
reward_type: std::option::Option<std::string::String>,
|
||||
commission: std::option::Option<u8>,
|
||||
commission_bps: crate::SolanaWireField<u16>,
|
||||
}
|
||||
|
||||
impl SolanaBlockReward {
|
||||
/// Returns the rewarded account public key.
|
||||
#[must_use]
|
||||
pub const fn pubkey(&self) -> &ksp_core_lib::Pubkey {
|
||||
return &self.pubkey;
|
||||
}
|
||||
|
||||
/// Returns the signed lamport delta applied by the reward.
|
||||
#[must_use]
|
||||
pub const fn lamports(&self) -> i64 {
|
||||
return self.lamports;
|
||||
}
|
||||
|
||||
/// Returns the post-reward account balance in lamports.
|
||||
#[must_use]
|
||||
pub const fn post_balance(&self) -> u64 {
|
||||
return self.post_balance;
|
||||
}
|
||||
|
||||
/// Returns the runtime reward type text without imposing a local economic taxonomy.
|
||||
#[must_use]
|
||||
pub fn reward_type(&self) -> std::option::Option<&str> {
|
||||
return self.reward_type.as_deref();
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
||||
let decoded = crate::decode_wire_json::<WireBlockReward>(method, value);
|
||||
let wire = match decoded {
|
||||
std::result::Result::Ok(wire) => wire,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let pubkey = crate::parse_wire_pubkey(method, "rewards.pubkey", wire.pubkey.as_str());
|
||||
return match pubkey {
|
||||
std::result::Result::Ok(pubkey) => std::result::Result::Ok(Self {
|
||||
pubkey,
|
||||
lamports: wire.lamports,
|
||||
post_balance: wire.post_balance,
|
||||
reward_type: wire.reward_type,
|
||||
commission: wire.commission,
|
||||
commission_bps: wire.commission_bps,
|
||||
}),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// One transaction entry returned inside a confirmed block.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SolanaBlockTransaction {
|
||||
transaction: crate::SolanaEncodedTransaction,
|
||||
meta: crate::SolanaWireField<serde_json::Value>,
|
||||
version: crate::SolanaWireField<crate::SolanaTransactionVersion>,
|
||||
}
|
||||
|
||||
impl SolanaBlockTransaction {
|
||||
/// Returns the encoded or JSON transaction payload without Program-specific decoding.
|
||||
#[must_use]
|
||||
pub const fn transaction(&self) -> &crate::SolanaEncodedTransaction {
|
||||
return &self.transaction;
|
||||
}
|
||||
|
||||
/// Returns transaction metadata while preserving omission versus explicit `null`.
|
||||
#[must_use]
|
||||
pub const fn meta(&self) -> &crate::SolanaWireField<serde_json::Value> {
|
||||
return &self.meta;
|
||||
}
|
||||
|
||||
/// Returns the transaction version while preserving omission versus explicit `null`.
|
||||
#[must_use]
|
||||
pub const fn version(&self) -> &crate::SolanaWireField<crate::SolanaTransactionVersion> {
|
||||
return &self.version;
|
||||
}
|
||||
|
||||
fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
||||
let decoded = crate::decode_wire_json::<WireBlockTransaction>(method, value);
|
||||
let wire = match decoded {
|
||||
std::result::Result::Ok(wire) => wire,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let transaction = crate::SolanaEncodedTransaction::decode_wire(method, wire.transaction);
|
||||
let transaction = match transaction {
|
||||
std::result::Result::Ok(transaction) => transaction,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let version = decode_block_transaction_version(method, wire.version);
|
||||
return match version {
|
||||
std::result::Result::Ok(version) => std::result::Result::Ok(Self { transaction, meta: wire.meta, version }),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Confirmed block wire result returned by `getBlock` when the RPC result is non-null.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SolanaConfirmedBlock {
|
||||
previous_blockhash: std::string::String,
|
||||
blockhash: std::string::String,
|
||||
parent_slot: u64,
|
||||
transactions: crate::SolanaWireField<std::vec::Vec<crate::SolanaBlockTransaction>>,
|
||||
signatures: crate::SolanaWireField<std::vec::Vec<std::string::String>>,
|
||||
rewards: crate::SolanaWireField<std::vec::Vec<crate::SolanaBlockReward>>,
|
||||
num_reward_partitions: crate::SolanaWireField<u64>,
|
||||
block_time: std::option::Option<i64>,
|
||||
block_height: std::option::Option<u64>,
|
||||
}
|
||||
|
||||
impl SolanaConfirmedBlock {
|
||||
/// Returns the previous blockhash exactly as reported by the RPC wire.
|
||||
#[must_use]
|
||||
pub fn previous_blockhash(&self) -> &str {
|
||||
return self.previous_blockhash.as_str();
|
||||
}
|
||||
|
||||
/// Returns this block's blockhash exactly as reported by the RPC wire.
|
||||
#[must_use]
|
||||
pub fn blockhash(&self) -> &str {
|
||||
return self.blockhash.as_str();
|
||||
}
|
||||
|
||||
/// Returns the parent slot.
|
||||
#[must_use]
|
||||
pub const fn parent_slot(&self) -> u64 {
|
||||
return self.parent_slot;
|
||||
}
|
||||
|
||||
/// Returns block transactions while preserving omitted/null/present states.
|
||||
#[must_use]
|
||||
pub const fn transactions(&self) -> &crate::SolanaWireField<std::vec::Vec<crate::SolanaBlockTransaction>> {
|
||||
return &self.transactions;
|
||||
}
|
||||
|
||||
/// Returns block signatures while preserving omitted/null/present states.
|
||||
#[must_use]
|
||||
pub const fn signatures(&self) -> &crate::SolanaWireField<std::vec::Vec<std::string::String>> {
|
||||
return &self.signatures;
|
||||
}
|
||||
|
||||
/// Returns block rewards while preserving omitted/null/present states.
|
||||
#[must_use]
|
||||
pub const fn rewards(&self) -> &crate::SolanaWireField<std::vec::Vec<crate::SolanaBlockReward>> {
|
||||
return &self.rewards;
|
||||
}
|
||||
|
||||
/// Returns the SIMD-0118 reward-partition count while preserving omission versus explicit `null`.
|
||||
#[must_use]
|
||||
pub const fn num_reward_partitions(&self) -> &crate::SolanaWireField<u64> {
|
||||
return &self.num_reward_partitions;
|
||||
}
|
||||
|
||||
/// Returns the nullable block time as a Unix timestamp.
|
||||
#[must_use]
|
||||
pub const fn block_time(&self) -> std::option::Option<i64> {
|
||||
return self.block_time;
|
||||
}
|
||||
|
||||
/// Returns the nullable block height.
|
||||
#[must_use]
|
||||
pub const fn block_height(&self) -> std::option::Option<u64> {
|
||||
return self.block_height;
|
||||
}
|
||||
|
||||
/// Decodes a confirmed block 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::<WireConfirmedBlock>(method, value);
|
||||
let wire = match decoded {
|
||||
std::result::Result::Ok(wire) => wire,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let transactions = decode_block_transactions(method, wire.transactions);
|
||||
let transactions = match transactions {
|
||||
std::result::Result::Ok(transactions) => transactions,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let rewards = decode_block_rewards(method, wire.rewards);
|
||||
let rewards = match rewards {
|
||||
std::result::Result::Ok(rewards) => rewards,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(Self {
|
||||
previous_blockhash: wire.previous_blockhash,
|
||||
blockhash: wire.blockhash,
|
||||
parent_slot: wire.parent_slot,
|
||||
transactions,
|
||||
signatures: wire.signatures,
|
||||
rewards,
|
||||
num_reward_partitions: wire.num_reward_partitions,
|
||||
block_time: wire.block_time,
|
||||
block_height: wire.block_height,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// One entry returned by `getRecentPerformanceSamples`.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct SolanaPerformanceSample {
|
||||
slot: u64,
|
||||
num_transactions: u64,
|
||||
num_non_vote_transactions: std::option::Option<u64>,
|
||||
num_slots: u64,
|
||||
sample_period_secs: u16,
|
||||
}
|
||||
|
||||
impl SolanaPerformanceSample {
|
||||
/// Returns the sample's highest slot.
|
||||
#[must_use]
|
||||
pub const fn slot(&self) -> u64 {
|
||||
return self.slot;
|
||||
}
|
||||
|
||||
/// Returns the total transaction count in the sample.
|
||||
#[must_use]
|
||||
pub const fn num_transactions(&self) -> u64 {
|
||||
return self.num_transactions;
|
||||
}
|
||||
|
||||
/// Returns the optional non-vote transaction count exposed by newer runtimes.
|
||||
#[must_use]
|
||||
pub const fn num_non_vote_transactions(&self) -> std::option::Option<u64> {
|
||||
return self.num_non_vote_transactions;
|
||||
}
|
||||
|
||||
/// Returns the number of slots covered by the sample.
|
||||
#[must_use]
|
||||
pub const fn num_slots(&self) -> u64 {
|
||||
return self.num_slots;
|
||||
}
|
||||
|
||||
/// Returns the sample duration in seconds.
|
||||
#[must_use]
|
||||
pub const fn sample_period_secs(&self) -> u16 {
|
||||
return self.sample_period_secs;
|
||||
}
|
||||
|
||||
/// Decodes one performance sample 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::<WirePerformanceSample>(method, value);
|
||||
return match decoded {
|
||||
std::result::Result::Ok(wire) => std::result::Result::Ok(Self {
|
||||
slot: wire.slot,
|
||||
num_transactions: wire.num_transactions,
|
||||
num_non_vote_transactions: wire.num_non_vote_transactions,
|
||||
num_slots: wire.num_slots,
|
||||
sample_period_secs: wire.sample_period_secs,
|
||||
}),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_block_transaction_version(
|
||||
method: &str,
|
||||
field: crate::SolanaWireField<serde_json::Value>,
|
||||
) -> ksp_core_lib::Result<crate::SolanaWireField<crate::SolanaTransactionVersion>> {
|
||||
return match field {
|
||||
crate::SolanaWireField::Omitted => std::result::Result::Ok(crate::SolanaWireField::Omitted),
|
||||
crate::SolanaWireField::Null => std::result::Result::Ok(crate::SolanaWireField::Null),
|
||||
crate::SolanaWireField::Value(value) => {
|
||||
let decoded = crate::SolanaTransactionVersion::decode_wire(method, value);
|
||||
match decoded {
|
||||
std::result::Result::Ok(version) => std::result::Result::Ok(crate::SolanaWireField::Value(version)),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn decode_block_transactions(
|
||||
method: &str,
|
||||
field: crate::SolanaWireField<std::vec::Vec<serde_json::Value>>,
|
||||
) -> ksp_core_lib::Result<crate::SolanaWireField<std::vec::Vec<crate::SolanaBlockTransaction>>> {
|
||||
let values = match field {
|
||||
crate::SolanaWireField::Omitted => return std::result::Result::Ok(crate::SolanaWireField::Omitted),
|
||||
crate::SolanaWireField::Null => return std::result::Result::Ok(crate::SolanaWireField::Null),
|
||||
crate::SolanaWireField::Value(values) => values,
|
||||
};
|
||||
let mut transactions = std::vec::Vec::with_capacity(values.len());
|
||||
for value in values {
|
||||
let decoded = crate::SolanaBlockTransaction::decode_wire(method, value);
|
||||
match decoded {
|
||||
std::result::Result::Ok(transaction) => transactions.push(transaction),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
return std::result::Result::Ok(crate::SolanaWireField::Value(transactions));
|
||||
}
|
||||
|
||||
fn decode_block_rewards(
|
||||
method: &str,
|
||||
field: crate::SolanaWireField<std::vec::Vec<serde_json::Value>>,
|
||||
) -> ksp_core_lib::Result<crate::SolanaWireField<std::vec::Vec<crate::SolanaBlockReward>>> {
|
||||
let values = match field {
|
||||
crate::SolanaWireField::Omitted => return std::result::Result::Ok(crate::SolanaWireField::Omitted),
|
||||
crate::SolanaWireField::Null => return std::result::Result::Ok(crate::SolanaWireField::Null),
|
||||
crate::SolanaWireField::Value(values) => values,
|
||||
};
|
||||
let mut rewards = std::vec::Vec::with_capacity(values.len());
|
||||
for value in values {
|
||||
let decoded = crate::SolanaBlockReward::decode_wire(method, value);
|
||||
match decoded {
|
||||
std::result::Result::Ok(reward) => rewards.push(reward),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
return std::result::Result::Ok(crate::SolanaWireField::Value(rewards));
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WireBlockCommitment {
|
||||
commitment: std::option::Option<std::vec::Vec<u64>>,
|
||||
total_stake: u64,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WireBlockProductionRange {
|
||||
first_slot: u64,
|
||||
last_slot: u64,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WireBlockProduction {
|
||||
by_identity: std::collections::BTreeMap<std::string::String, (usize, usize)>,
|
||||
range: WireBlockProductionRange,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WireBlockReward {
|
||||
pubkey: std::string::String,
|
||||
lamports: i64,
|
||||
post_balance: u64,
|
||||
#[serde(default)]
|
||||
reward_type: std::option::Option<std::string::String>,
|
||||
#[serde(default)]
|
||||
commission: std::option::Option<u8>,
|
||||
#[serde(default)]
|
||||
commission_bps: crate::SolanaWireField<u16>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WireBlockTransaction {
|
||||
transaction: serde_json::Value,
|
||||
#[serde(default)]
|
||||
meta: crate::SolanaWireField<serde_json::Value>,
|
||||
#[serde(default)]
|
||||
version: crate::SolanaWireField<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WireConfirmedBlock {
|
||||
previous_blockhash: std::string::String,
|
||||
blockhash: std::string::String,
|
||||
parent_slot: u64,
|
||||
#[serde(default)]
|
||||
transactions: crate::SolanaWireField<std::vec::Vec<serde_json::Value>>,
|
||||
#[serde(default)]
|
||||
signatures: crate::SolanaWireField<std::vec::Vec<std::string::String>>,
|
||||
#[serde(default)]
|
||||
rewards: crate::SolanaWireField<std::vec::Vec<serde_json::Value>>,
|
||||
#[serde(default)]
|
||||
num_reward_partitions: crate::SolanaWireField<u64>,
|
||||
block_time: std::option::Option<i64>,
|
||||
block_height: std::option::Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WirePerformanceSample {
|
||||
slot: u64,
|
||||
num_transactions: u64,
|
||||
#[serde(default)]
|
||||
num_non_vote_transactions: std::option::Option<u64>,
|
||||
num_slots: u64,
|
||||
sample_period_secs: u16,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/rpc_blocks.rs"]
|
||||
mod tests;
|
||||
377
crates/ksp-onchain-transport-lib/src/rpc_economics.rs
Normal file
377
crates/ksp-onchain-transport-lib/src/rpc_economics.rs
Normal 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;
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-onchain-transport-lib/src/rpc_transactions.rs
|
||||
// version: 7
|
||||
// version: 8
|
||||
|
||||
/// Binary encoding accepted for serialized transaction input payloads.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
@@ -838,7 +838,8 @@ pub enum SolanaTransactionVersion {
|
||||
}
|
||||
|
||||
impl SolanaTransactionVersion {
|
||||
fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
||||
/// Decodes a transaction version from the shared Solana JSON wire representation.
|
||||
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
||||
return match value {
|
||||
serde_json::Value::String(value) if value == "legacy" => std::result::Result::Ok(Self::Legacy),
|
||||
serde_json::Value::Number(value) => {
|
||||
|
||||
Reference in New Issue
Block a user