v0.2.2-pre.002
This commit is contained in:
632
crates/ksp-onchain-transport-lib/src/rpc_accounts.rs
Normal file
632
crates/ksp-onchain-transport-lib/src/rpc_accounts.rs
Normal file
@@ -0,0 +1,632 @@
|
||||
// file: crates/ksp-onchain-transport-lib/src/rpc_accounts.rs
|
||||
// version: 1
|
||||
|
||||
/// Account-data encoding accepted by Solana HTTP account methods.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum SolanaAccountEncoding {
|
||||
/// Legacy binary/base58 request encoding.
|
||||
Binary,
|
||||
/// Base58 text encoding.
|
||||
Base58,
|
||||
/// Base64 text encoding.
|
||||
Base64,
|
||||
/// Parsed JSON representation when the RPC node has a parser for the account owner.
|
||||
JsonParsed,
|
||||
/// Base64 text containing zstd-compressed bytes.
|
||||
Base64Zstd,
|
||||
}
|
||||
|
||||
impl SolanaAccountEncoding {
|
||||
/// Returns the Solana JSON-RPC encoding string.
|
||||
#[must_use]
|
||||
pub const fn as_str(self) -> &'static str {
|
||||
return match self {
|
||||
Self::Binary => "binary",
|
||||
Self::Base58 => "base58",
|
||||
Self::Base64 => "base64",
|
||||
Self::JsonParsed => "jsonParsed",
|
||||
Self::Base64Zstd => "base64+zstd",
|
||||
};
|
||||
}
|
||||
|
||||
fn from_wire(value: &str) -> std::option::Option<Self> {
|
||||
return match value {
|
||||
"binary" => std::option::Option::Some(Self::Binary),
|
||||
"base58" => std::option::Option::Some(Self::Base58),
|
||||
"base64" => std::option::Option::Some(Self::Base64),
|
||||
"jsonParsed" => std::option::Option::Some(Self::JsonParsed),
|
||||
"base64+zstd" => std::option::Option::Some(Self::Base64Zstd),
|
||||
_ => std::option::Option::None,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Byte range requested from account data without decoding it locally.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct SolanaDataSliceConfig {
|
||||
offset: usize,
|
||||
length: usize,
|
||||
}
|
||||
|
||||
impl SolanaDataSliceConfig {
|
||||
/// Creates an account-data slice configuration.
|
||||
#[must_use]
|
||||
pub const fn new(offset: usize, length: usize) -> Self {
|
||||
return Self { offset, length };
|
||||
}
|
||||
|
||||
/// Returns the byte offset.
|
||||
#[must_use]
|
||||
pub const fn offset(&self) -> usize {
|
||||
return self.offset;
|
||||
}
|
||||
|
||||
/// Returns the requested byte length.
|
||||
#[must_use]
|
||||
pub const fn length(&self) -> usize {
|
||||
return self.length;
|
||||
}
|
||||
|
||||
fn to_json_value(self) -> serde_json::Value {
|
||||
return serde_json::json!({"offset": self.offset, "length": self.length});
|
||||
}
|
||||
}
|
||||
|
||||
/// Shared account configuration used by account-info and token-account list methods.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct SolanaAccountInfoConfig {
|
||||
encoding: std::option::Option<crate::SolanaAccountEncoding>,
|
||||
data_slice: std::option::Option<crate::SolanaDataSliceConfig>,
|
||||
context: crate::SolanaContextConfig,
|
||||
}
|
||||
|
||||
impl SolanaAccountInfoConfig {
|
||||
/// Creates an explicit account-info configuration.
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
encoding: std::option::Option<crate::SolanaAccountEncoding>,
|
||||
data_slice: std::option::Option<crate::SolanaDataSliceConfig>,
|
||||
commitment: std::option::Option<crate::SolanaCommitment>,
|
||||
min_context_slot: std::option::Option<u64>,
|
||||
) -> Self {
|
||||
return Self { encoding, data_slice, context: crate::SolanaContextConfig::new(commitment, min_context_slot) };
|
||||
}
|
||||
|
||||
/// Returns the optional account-data encoding.
|
||||
#[must_use]
|
||||
pub const fn encoding(&self) -> std::option::Option<crate::SolanaAccountEncoding> {
|
||||
return self.encoding;
|
||||
}
|
||||
|
||||
/// Returns the optional account-data slice.
|
||||
#[must_use]
|
||||
pub const fn data_slice(&self) -> std::option::Option<crate::SolanaDataSliceConfig> {
|
||||
return self.data_slice;
|
||||
}
|
||||
|
||||
/// Returns the optional commitment level.
|
||||
#[must_use]
|
||||
pub const fn commitment(&self) -> std::option::Option<crate::SolanaCommitment> {
|
||||
return self.context.commitment();
|
||||
}
|
||||
|
||||
/// Returns the optional minimum context slot.
|
||||
#[must_use]
|
||||
pub const fn min_context_slot(&self) -> std::option::Option<u64> {
|
||||
return self.context.min_context_slot();
|
||||
}
|
||||
|
||||
/// Returns whether this config serializes to an empty JSON object.
|
||||
#[must_use]
|
||||
pub(crate) const fn is_empty(&self) -> bool {
|
||||
return self.encoding.is_none() && self.data_slice.is_none() && self.context.is_empty();
|
||||
}
|
||||
|
||||
/// Serializes this config to the Solana JSON-RPC wire object.
|
||||
#[must_use]
|
||||
pub(crate) fn to_json_value(&self) -> serde_json::Value {
|
||||
let context_value = self.context.to_json_value();
|
||||
let mut object = match context_value {
|
||||
serde_json::Value::Object(object) => object,
|
||||
_ => serde_json::Map::new(),
|
||||
};
|
||||
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(data_slice) = self.data_slice {
|
||||
object.insert("dataSlice".to_owned(), data_slice.to_json_value());
|
||||
}
|
||||
return serde_json::Value::Object(object);
|
||||
}
|
||||
}
|
||||
|
||||
/// Filter accepted by `getLargestAccounts`.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum SolanaLargestAccountsFilter {
|
||||
/// Return only circulating accounts.
|
||||
Circulating,
|
||||
/// Return only non-circulating accounts.
|
||||
NonCirculating,
|
||||
}
|
||||
|
||||
impl SolanaLargestAccountsFilter {
|
||||
fn as_str(self) -> &'static str {
|
||||
return match self {
|
||||
Self::Circulating => "circulating",
|
||||
Self::NonCirculating => "nonCirculating",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Optional configuration for `getLargestAccounts`.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct SolanaLargestAccountsConfig {
|
||||
commitment: std::option::Option<crate::SolanaCommitment>,
|
||||
filter: std::option::Option<crate::SolanaLargestAccountsFilter>,
|
||||
sort_results: std::option::Option<bool>,
|
||||
}
|
||||
|
||||
impl SolanaLargestAccountsConfig {
|
||||
/// Creates a largest-accounts configuration.
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
commitment: std::option::Option<crate::SolanaCommitment>,
|
||||
filter: std::option::Option<crate::SolanaLargestAccountsFilter>,
|
||||
sort_results: std::option::Option<bool>,
|
||||
) -> Self {
|
||||
return Self { commitment, filter, sort_results };
|
||||
}
|
||||
|
||||
/// Returns the optional commitment.
|
||||
#[must_use]
|
||||
pub const fn commitment(&self) -> std::option::Option<crate::SolanaCommitment> {
|
||||
return self.commitment;
|
||||
}
|
||||
|
||||
/// Returns the optional circulating-account filter.
|
||||
#[must_use]
|
||||
pub const fn filter(&self) -> std::option::Option<crate::SolanaLargestAccountsFilter> {
|
||||
return self.filter;
|
||||
}
|
||||
|
||||
/// Returns the optional server-side result-sorting request.
|
||||
#[must_use]
|
||||
pub const fn sort_results(&self) -> std::option::Option<bool> {
|
||||
return self.sort_results;
|
||||
}
|
||||
|
||||
/// Serializes this config to the Solana JSON-RPC wire 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(filter) = self.filter {
|
||||
object.insert("filter".to_owned(), serde_json::Value::String(filter.as_str().to_owned()));
|
||||
}
|
||||
if let std::option::Option::Some(sort_results) = self.sort_results {
|
||||
object.insert("sortResults".to_owned(), serde_json::Value::Bool(sort_results));
|
||||
}
|
||||
return serde_json::Value::Object(object);
|
||||
}
|
||||
}
|
||||
|
||||
/// Bytes used by a `memcmp` program-account filter.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum SolanaMemcmpBytes {
|
||||
/// Base58-encoded bytes.
|
||||
Base58(std::string::String),
|
||||
/// Base64-encoded bytes.
|
||||
Base64(std::string::String),
|
||||
/// Raw byte array.
|
||||
Bytes(std::vec::Vec<u8>),
|
||||
}
|
||||
|
||||
/// One `memcmp` filter applied to account data.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct SolanaMemcmpFilter {
|
||||
offset: usize,
|
||||
bytes: crate::SolanaMemcmpBytes,
|
||||
}
|
||||
|
||||
impl SolanaMemcmpFilter {
|
||||
/// Creates a `memcmp` filter without locally decoding encoded string data.
|
||||
#[must_use]
|
||||
pub fn new(offset: usize, bytes: crate::SolanaMemcmpBytes) -> Self {
|
||||
return Self { offset, bytes };
|
||||
}
|
||||
|
||||
/// Returns the account-data byte offset.
|
||||
#[must_use]
|
||||
pub const fn offset(&self) -> usize {
|
||||
return self.offset;
|
||||
}
|
||||
|
||||
/// Returns the encoded or raw bytes.
|
||||
#[must_use]
|
||||
pub const fn bytes(&self) -> &crate::SolanaMemcmpBytes {
|
||||
return &self.bytes;
|
||||
}
|
||||
|
||||
fn to_json_value(&self) -> serde_json::Value {
|
||||
return match &self.bytes {
|
||||
crate::SolanaMemcmpBytes::Base58(bytes) => serde_json::json!({"offset": self.offset, "bytes": bytes, "encoding": "base58"}),
|
||||
crate::SolanaMemcmpBytes::Base64(bytes) => serde_json::json!({"offset": self.offset, "bytes": bytes, "encoding": "base64"}),
|
||||
crate::SolanaMemcmpBytes::Bytes(bytes) => serde_json::json!({"offset": self.offset, "bytes": bytes, "encoding": "bytes"}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Filter accepted by the current `getProgramAccounts` implementation.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum SolanaProgramAccountFilter {
|
||||
/// Require an exact account data size.
|
||||
DataSize(u64),
|
||||
/// Compare bytes at one account-data offset.
|
||||
Memcmp(crate::SolanaMemcmpFilter),
|
||||
/// Require a valid SPL Token account-state layout according to the RPC implementation.
|
||||
TokenAccountState,
|
||||
}
|
||||
|
||||
impl SolanaProgramAccountFilter {
|
||||
fn to_json_value(&self) -> serde_json::Value {
|
||||
return match self {
|
||||
Self::DataSize(size) => serde_json::json!({"dataSize": size}),
|
||||
Self::Memcmp(filter) => serde_json::json!({"memcmp": filter.to_json_value()}),
|
||||
Self::TokenAccountState => serde_json::Value::String("tokenAccountState".to_owned()),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for `getProgramAccounts` built from the shared account config plus program filters.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct SolanaProgramAccountsConfig {
|
||||
account_config: crate::SolanaAccountInfoConfig,
|
||||
filters: std::vec::Vec<crate::SolanaProgramAccountFilter>,
|
||||
with_context: std::option::Option<bool>,
|
||||
sort_results: std::option::Option<bool>,
|
||||
}
|
||||
|
||||
impl SolanaProgramAccountsConfig {
|
||||
/// Creates a program-accounts configuration.
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
account_config: crate::SolanaAccountInfoConfig,
|
||||
filters: std::vec::Vec<crate::SolanaProgramAccountFilter>,
|
||||
with_context: std::option::Option<bool>,
|
||||
sort_results: std::option::Option<bool>,
|
||||
) -> Self {
|
||||
return Self { account_config, filters, with_context, sort_results };
|
||||
}
|
||||
|
||||
/// Returns the shared account configuration.
|
||||
#[must_use]
|
||||
pub const fn account_config(&self) -> &crate::SolanaAccountInfoConfig {
|
||||
return &self.account_config;
|
||||
}
|
||||
|
||||
/// Returns the ordered program-account filters.
|
||||
#[must_use]
|
||||
pub fn filters(&self) -> &[crate::SolanaProgramAccountFilter] {
|
||||
return self.filters.as_slice();
|
||||
}
|
||||
|
||||
/// Returns the optional context-wrapper request.
|
||||
#[must_use]
|
||||
pub const fn with_context(&self) -> std::option::Option<bool> {
|
||||
return self.with_context;
|
||||
}
|
||||
|
||||
/// Returns the optional server-side sorting request.
|
||||
#[must_use]
|
||||
pub const fn sort_results(&self) -> std::option::Option<bool> {
|
||||
return self.sort_results;
|
||||
}
|
||||
|
||||
/// Serializes this config to the Solana JSON-RPC wire object.
|
||||
#[must_use]
|
||||
pub(crate) fn to_json_value(&self) -> serde_json::Value {
|
||||
let account_value = self.account_config.to_json_value();
|
||||
let mut object = match account_value {
|
||||
serde_json::Value::Object(object) => object,
|
||||
_ => serde_json::Map::new(),
|
||||
};
|
||||
if !self.filters.is_empty() {
|
||||
let values = self.filters.iter().map(crate::SolanaProgramAccountFilter::to_json_value).collect::<std::vec::Vec<_>>();
|
||||
object.insert("filters".to_owned(), serde_json::Value::Array(values));
|
||||
}
|
||||
if let std::option::Option::Some(with_context) = self.with_context {
|
||||
object.insert("withContext".to_owned(), serde_json::Value::Bool(with_context));
|
||||
}
|
||||
if let std::option::Option::Some(sort_results) = self.sort_results {
|
||||
object.insert("sortResults".to_owned(), serde_json::Value::Bool(sort_results));
|
||||
}
|
||||
return serde_json::Value::Object(object);
|
||||
}
|
||||
}
|
||||
|
||||
/// Parsed account payload returned by the RPC node for `jsonParsed` account data.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SolanaParsedAccountData {
|
||||
program: std::string::String,
|
||||
parsed: serde_json::Value,
|
||||
space: u64,
|
||||
}
|
||||
|
||||
impl SolanaParsedAccountData {
|
||||
/// Returns the parser/program label reported by the RPC node.
|
||||
#[must_use]
|
||||
pub fn program(&self) -> &str {
|
||||
return self.program.as_str();
|
||||
}
|
||||
|
||||
/// Returns the parsed JSON payload without converting it to a Program/SPL domain model.
|
||||
#[must_use]
|
||||
pub const fn parsed(&self) -> &serde_json::Value {
|
||||
return &self.parsed;
|
||||
}
|
||||
|
||||
/// Returns the account-data space reported inside the parsed payload.
|
||||
#[must_use]
|
||||
pub const fn space(&self) -> u64 {
|
||||
return self.space;
|
||||
}
|
||||
}
|
||||
|
||||
/// Wire-preserving account data returned by Solana HTTP account methods.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum SolanaAccountData {
|
||||
/// Legacy single-string binary form retained for backwards compatibility.
|
||||
LegacyBinary(std::string::String),
|
||||
/// Encoded tuple `[data, encoding]`.
|
||||
Encoded {
|
||||
/// Encoded account bytes.
|
||||
data: std::string::String,
|
||||
/// Encoding label returned by the RPC node.
|
||||
encoding: crate::SolanaAccountEncoding,
|
||||
},
|
||||
/// Parsed JSON object returned by the RPC node.
|
||||
JsonParsed(crate::SolanaParsedAccountData),
|
||||
}
|
||||
|
||||
impl SolanaAccountData {
|
||||
fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
||||
let decoded = crate::decode_wire_json::<WireAccountData>(method, value);
|
||||
let wire = match decoded {
|
||||
std::result::Result::Ok(wire) => wire,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return match wire {
|
||||
WireAccountData::LegacyBinary(value) => std::result::Result::Ok(Self::LegacyBinary(value)),
|
||||
WireAccountData::JsonParsed(value) => std::result::Result::Ok(Self::JsonParsed(crate::SolanaParsedAccountData {
|
||||
program: value.program,
|
||||
parsed: value.parsed,
|
||||
space: value.space,
|
||||
})),
|
||||
WireAccountData::Encoded((data, encoding)) => {
|
||||
let parsed = crate::SolanaAccountEncoding::from_wire(encoding.as_str());
|
||||
let encoding = match parsed {
|
||||
std::option::Option::Some(encoding) => encoding,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_RESPONSE, "account data tuple uses an unknown encoding")
|
||||
.with_context("rpc_method", method),
|
||||
);
|
||||
}
|
||||
};
|
||||
if encoding == crate::SolanaAccountEncoding::Binary || encoding == crate::SolanaAccountEncoding::JsonParsed {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_RESPONSE, "account data tuple uses an invalid tuple encoding")
|
||||
.with_context("rpc_method", method),
|
||||
);
|
||||
}
|
||||
std::result::Result::Ok(Self::Encoded { data, encoding })
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Typed transport-level Solana account without Program/SPL decoding.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SolanaAccount {
|
||||
lamports: u64,
|
||||
data: crate::SolanaAccountData,
|
||||
owner: ksp_core_lib::Pubkey,
|
||||
executable: bool,
|
||||
rent_epoch: u64,
|
||||
space: std::option::Option<u64>,
|
||||
}
|
||||
|
||||
impl SolanaAccount {
|
||||
/// Returns the account balance in lamports.
|
||||
#[must_use]
|
||||
pub const fn lamports(&self) -> u64 {
|
||||
return self.lamports;
|
||||
}
|
||||
|
||||
/// Returns the wire-preserving account data.
|
||||
#[must_use]
|
||||
pub const fn data(&self) -> &crate::SolanaAccountData {
|
||||
return &self.data;
|
||||
}
|
||||
|
||||
/// Returns the account owner program public key.
|
||||
#[must_use]
|
||||
pub const fn owner(&self) -> &ksp_core_lib::Pubkey {
|
||||
return &self.owner;
|
||||
}
|
||||
|
||||
/// Returns whether the account is executable.
|
||||
#[must_use]
|
||||
pub const fn executable(&self) -> bool {
|
||||
return self.executable;
|
||||
}
|
||||
|
||||
/// Returns the rent epoch reported by the RPC node.
|
||||
#[must_use]
|
||||
pub const fn rent_epoch(&self) -> u64 {
|
||||
return self.rent_epoch;
|
||||
}
|
||||
|
||||
/// Returns the optional account data-space field.
|
||||
#[must_use]
|
||||
pub const fn space(&self) -> std::option::Option<u64> {
|
||||
return self.space;
|
||||
}
|
||||
|
||||
/// Decodes one account DTO from the 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::<WireAccount>(method, value);
|
||||
let wire = match decoded {
|
||||
std::result::Result::Ok(wire) => wire,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let owner = crate::parse_wire_pubkey(method, "owner", wire.owner.as_str());
|
||||
let owner = match owner {
|
||||
std::result::Result::Ok(owner) => owner,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let data = crate::SolanaAccountData::decode_wire(method, wire.data);
|
||||
let data = match data {
|
||||
std::result::Result::Ok(data) => data,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(Self {
|
||||
lamports: wire.lamports,
|
||||
data,
|
||||
owner,
|
||||
executable: wire.executable,
|
||||
rent_epoch: wire.rent_epoch,
|
||||
space: wire.space,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// One public key plus its account returned by account-list RPC methods.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SolanaKeyedAccount {
|
||||
pubkey: ksp_core_lib::Pubkey,
|
||||
account: crate::SolanaAccount,
|
||||
}
|
||||
|
||||
impl SolanaKeyedAccount {
|
||||
/// Returns the account public key.
|
||||
#[must_use]
|
||||
pub const fn pubkey(&self) -> &ksp_core_lib::Pubkey {
|
||||
return &self.pubkey;
|
||||
}
|
||||
|
||||
/// Returns the account payload.
|
||||
#[must_use]
|
||||
pub const fn account(&self) -> &crate::SolanaAccount {
|
||||
return &self.account;
|
||||
}
|
||||
|
||||
/// Decodes one keyed account from the 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::<WireKeyedAccount>(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, "pubkey", wire.pubkey.as_str());
|
||||
let pubkey = match pubkey {
|
||||
std::result::Result::Ok(pubkey) => pubkey,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let account = crate::SolanaAccount::decode_wire(method, wire.account);
|
||||
let account = match account {
|
||||
std::result::Result::Ok(account) => account,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(Self { pubkey, account });
|
||||
}
|
||||
}
|
||||
|
||||
/// Address and lamport balance returned by `getLargestAccounts`.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct SolanaAccountBalance {
|
||||
address: ksp_core_lib::Pubkey,
|
||||
lamports: u64,
|
||||
}
|
||||
|
||||
impl SolanaAccountBalance {
|
||||
/// Returns the account address.
|
||||
#[must_use]
|
||||
pub const fn address(&self) -> &ksp_core_lib::Pubkey {
|
||||
return &self.address;
|
||||
}
|
||||
|
||||
/// Returns the balance in lamports.
|
||||
#[must_use]
|
||||
pub const fn lamports(&self) -> u64 {
|
||||
return self.lamports;
|
||||
}
|
||||
|
||||
/// Decodes one account-balance entry from the 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::<WireAccountBalance>(method, value);
|
||||
let wire = match decoded {
|
||||
std::result::Result::Ok(wire) => wire,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let address = crate::parse_wire_pubkey(method, "address", wire.address.as_str());
|
||||
return match address {
|
||||
std::result::Result::Ok(address) => std::result::Result::Ok(Self { address, lamports: wire.lamports }),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Result union returned by `getProgramAccounts` with or without an RPC context.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum SolanaProgramAccountsResult {
|
||||
/// Bare account list returned when `withContext` is false or absent.
|
||||
Accounts(std::vec::Vec<crate::SolanaKeyedAccount>),
|
||||
/// Contextual account list returned when `withContext` is true.
|
||||
Context(crate::SolanaRpcResponse<std::vec::Vec<crate::SolanaKeyedAccount>>),
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum WireAccountData {
|
||||
LegacyBinary(std::string::String),
|
||||
JsonParsed(WireParsedAccountData),
|
||||
Encoded((std::string::String, std::string::String)),
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct WireParsedAccountData {
|
||||
program: std::string::String,
|
||||
parsed: serde_json::Value,
|
||||
space: u64,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct WireAccount {
|
||||
lamports: u64,
|
||||
data: serde_json::Value,
|
||||
owner: std::string::String,
|
||||
executable: bool,
|
||||
#[serde(rename = "rentEpoch")]
|
||||
rent_epoch: u64,
|
||||
#[serde(default)]
|
||||
space: std::option::Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct WireKeyedAccount {
|
||||
pubkey: std::string::String,
|
||||
account: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct WireAccountBalance {
|
||||
address: std::string::String,
|
||||
lamports: u64,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/rpc_accounts.rs"]
|
||||
mod tests;
|
||||
Reference in New Issue
Block a user