1297 lines
51 KiB
Rust
1297 lines
51 KiB
Rust
// file: crates/ksp-onchain-transport-lib/src/rpc_transactions.rs
|
|
// version: 1
|
|
|
|
/// Binary encoding accepted for serialized transaction input payloads.
|
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
|
pub enum SolanaTransactionBinaryEncoding {
|
|
/// Base58 text encoding.
|
|
Base58,
|
|
/// Base64 text encoding.
|
|
Base64,
|
|
}
|
|
|
|
impl SolanaTransactionBinaryEncoding {
|
|
/// Returns the Solana JSON-RPC encoding string.
|
|
#[must_use]
|
|
pub const fn as_str(self) -> &'static str {
|
|
return match self {
|
|
Self::Base58 => "base58",
|
|
Self::Base64 => "base64",
|
|
};
|
|
}
|
|
|
|
fn from_wire(value: &str) -> std::option::Option<Self> {
|
|
return match value {
|
|
"base58" => std::option::Option::Some(Self::Base58),
|
|
"base64" => std::option::Option::Some(Self::Base64),
|
|
_ => std::option::Option::None,
|
|
};
|
|
}
|
|
}
|
|
|
|
/// Encoding accepted by `getTransaction`, including its retained legacy `binary` alias.
|
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
|
pub enum SolanaTransactionEncoding {
|
|
/// Legacy base58-compatible `binary` alias retained by Solana RPC for backwards compatibility.
|
|
Binary,
|
|
/// Base58 encoded transaction bytes.
|
|
Base58,
|
|
/// Base64 encoded transaction bytes.
|
|
Base64,
|
|
/// Raw JSON transaction representation.
|
|
Json,
|
|
/// Parsed JSON transaction representation.
|
|
JsonParsed,
|
|
}
|
|
|
|
impl SolanaTransactionEncoding {
|
|
/// 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::Json => "json",
|
|
Self::JsonParsed => "jsonParsed",
|
|
};
|
|
}
|
|
}
|
|
|
|
/// Three-state wire field used when Solana distinguishes omission from an explicit JSON `null`.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum SolanaWireField<T> {
|
|
/// The field was not present in the decoded wire object.
|
|
Omitted,
|
|
/// The field was explicitly present with JSON `null`.
|
|
Null,
|
|
/// The field was present with a concrete value.
|
|
Value(T),
|
|
}
|
|
|
|
impl<T> SolanaWireField<T> {
|
|
/// Returns whether the field was omitted from the wire object.
|
|
#[must_use]
|
|
pub const fn is_omitted(&self) -> bool {
|
|
return match self {
|
|
Self::Omitted => true,
|
|
Self::Null | Self::Value(_) => false,
|
|
};
|
|
}
|
|
|
|
/// Returns whether the field was explicitly JSON `null`.
|
|
#[must_use]
|
|
pub const fn is_null(&self) -> bool {
|
|
return match self {
|
|
Self::Null => true,
|
|
Self::Omitted | Self::Value(_) => false,
|
|
};
|
|
}
|
|
|
|
/// Returns the concrete value when the field was present and non-null.
|
|
#[must_use]
|
|
pub const fn value(&self) -> std::option::Option<&T> {
|
|
return match self {
|
|
Self::Value(value) => std::option::Option::Some(value),
|
|
Self::Omitted | Self::Null => std::option::Option::None,
|
|
};
|
|
}
|
|
}
|
|
|
|
impl<T> std::default::Default for SolanaWireField<T> {
|
|
fn default() -> Self {
|
|
return Self::Omitted;
|
|
}
|
|
}
|
|
|
|
impl<'de, T> serde::Deserialize<'de> for SolanaWireField<T>
|
|
where
|
|
T: serde::Deserialize<'de>,
|
|
{
|
|
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
let decoded = <std::option::Option<T> as serde::Deserialize>::deserialize(deserializer);
|
|
return match decoded {
|
|
std::result::Result::Ok(std::option::Option::Some(value)) => std::result::Result::Ok(Self::Value(value)),
|
|
std::result::Result::Ok(std::option::Option::None) => std::result::Result::Ok(Self::Null),
|
|
std::result::Result::Err(error) => std::result::Result::Err(error),
|
|
};
|
|
}
|
|
}
|
|
|
|
/// Modern configuration object accepted by `getTransaction`.
|
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
|
pub struct SolanaGetTransactionConfig {
|
|
commitment: std::option::Option<crate::SolanaCommitment>,
|
|
encoding: std::option::Option<crate::SolanaTransactionEncoding>,
|
|
max_supported_transaction_version: std::option::Option<u8>,
|
|
}
|
|
|
|
impl SolanaGetTransactionConfig {
|
|
/// Creates a modern `getTransaction` configuration.
|
|
#[must_use]
|
|
pub const fn new(
|
|
commitment: std::option::Option<crate::SolanaCommitment>,
|
|
encoding: std::option::Option<crate::SolanaTransactionEncoding>,
|
|
max_supported_transaction_version: std::option::Option<u8>,
|
|
) -> Self {
|
|
return Self { commitment, encoding, max_supported_transaction_version };
|
|
}
|
|
|
|
/// Returns the optional commitment level.
|
|
#[must_use]
|
|
pub const fn commitment(&self) -> std::option::Option<crate::SolanaCommitment> {
|
|
return self.commitment;
|
|
}
|
|
|
|
/// Returns the optional transaction response encoding.
|
|
#[must_use]
|
|
pub const fn encoding(&self) -> std::option::Option<crate::SolanaTransactionEncoding> {
|
|
return self.encoding;
|
|
}
|
|
|
|
/// 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 the modern 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.max_supported_transaction_version.is_none();
|
|
}
|
|
|
|
/// Serializes the modern config to its 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(encoding) = self.encoding {
|
|
object.insert("encoding".to_owned(), serde_json::Value::String(encoding.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()));
|
|
}
|
|
return serde_json::Value::Object(object);
|
|
}
|
|
}
|
|
|
|
/// Pagination and context configuration accepted by `getSignaturesForAddress`.
|
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
|
pub struct SolanaSignaturesForAddressConfig {
|
|
before: std::option::Option<std::string::String>,
|
|
until: std::option::Option<std::string::String>,
|
|
limit: std::option::Option<usize>,
|
|
context: crate::SolanaContextConfig,
|
|
}
|
|
|
|
impl SolanaSignaturesForAddressConfig {
|
|
/// Creates an address-signature pagination configuration.
|
|
#[must_use]
|
|
pub fn new(
|
|
before: std::option::Option<std::string::String>,
|
|
until: std::option::Option<std::string::String>,
|
|
limit: std::option::Option<usize>,
|
|
commitment: std::option::Option<crate::SolanaCommitment>,
|
|
min_context_slot: std::option::Option<u64>,
|
|
) -> Self {
|
|
return Self { before, until, limit, context: crate::SolanaContextConfig::new(commitment, min_context_slot) };
|
|
}
|
|
|
|
/// Returns the exclusive pagination signature preceding the requested page.
|
|
#[must_use]
|
|
pub fn before(&self) -> std::option::Option<&str> {
|
|
return self.before.as_deref();
|
|
}
|
|
|
|
/// Returns the exclusive pagination signature terminating the requested range.
|
|
#[must_use]
|
|
pub fn until(&self) -> std::option::Option<&str> {
|
|
return self.until.as_deref();
|
|
}
|
|
|
|
/// Returns the optional requested page size.
|
|
#[must_use]
|
|
pub const fn limit(&self) -> std::option::Option<usize> {
|
|
return self.limit;
|
|
}
|
|
|
|
/// 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 the pagination config would serialize to an empty object.
|
|
pub(crate) fn is_empty(&self) -> bool {
|
|
return self.before.is_none() && self.until.is_none() && self.limit.is_none() && self.commitment().is_none() && self.min_context_slot().is_none();
|
|
}
|
|
|
|
/// Serializes this pagination config to the Solana JSON-RPC wire object.
|
|
#[must_use]
|
|
pub(crate) fn to_json_value(&self) -> serde_json::Value {
|
|
let context = self.context.to_json_value();
|
|
let mut object = match context {
|
|
serde_json::Value::Object(object) => object,
|
|
_ => serde_json::Map::new(),
|
|
};
|
|
if let std::option::Option::Some(before) = self.before.as_ref() {
|
|
object.insert("before".to_owned(), serde_json::Value::String(before.clone()));
|
|
}
|
|
if let std::option::Option::Some(until) = self.until.as_ref() {
|
|
object.insert("until".to_owned(), serde_json::Value::String(until.clone()));
|
|
}
|
|
if let std::option::Option::Some(limit) = self.limit {
|
|
object.insert("limit".to_owned(), serde_json::json!(limit));
|
|
}
|
|
return serde_json::Value::Object(object);
|
|
}
|
|
}
|
|
|
|
/// Optional historical-search configuration accepted by `getSignatureStatuses`.
|
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
|
pub struct SolanaSignatureStatusesConfig {
|
|
search_transaction_history: std::option::Option<bool>,
|
|
}
|
|
|
|
impl SolanaSignatureStatusesConfig {
|
|
/// Creates a signature-status configuration.
|
|
#[must_use]
|
|
pub const fn new(search_transaction_history: std::option::Option<bool>) -> Self {
|
|
return Self { search_transaction_history };
|
|
}
|
|
|
|
/// Returns whether full transaction history should be searched when explicitly configured.
|
|
#[must_use]
|
|
pub const fn search_transaction_history(&self) -> std::option::Option<bool> {
|
|
return self.search_transaction_history;
|
|
}
|
|
|
|
/// Returns whether the status config would serialize to an empty object.
|
|
pub(crate) const fn is_empty(&self) -> bool {
|
|
return self.search_transaction_history.is_none();
|
|
}
|
|
|
|
/// 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(value) = self.search_transaction_history {
|
|
object.insert("searchTransactionHistory".to_owned(), serde_json::Value::Bool(value));
|
|
}
|
|
return serde_json::Value::Object(object);
|
|
}
|
|
}
|
|
|
|
/// Configuration accepted by `requestAirdrop`.
|
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
|
pub struct SolanaRequestAirdropConfig {
|
|
recent_blockhash: std::option::Option<std::string::String>,
|
|
commitment: std::option::Option<crate::SolanaCommitment>,
|
|
}
|
|
|
|
impl SolanaRequestAirdropConfig {
|
|
/// Creates an airdrop request configuration.
|
|
#[must_use]
|
|
pub fn new(recent_blockhash: std::option::Option<std::string::String>, commitment: std::option::Option<crate::SolanaCommitment>) -> Self {
|
|
return Self { recent_blockhash, commitment };
|
|
}
|
|
|
|
/// Returns the optional recent blockhash supplied to the faucet RPC.
|
|
#[must_use]
|
|
pub fn recent_blockhash(&self) -> std::option::Option<&str> {
|
|
return self.recent_blockhash.as_deref();
|
|
}
|
|
|
|
/// Returns the optional commitment level.
|
|
#[must_use]
|
|
pub const fn commitment(&self) -> std::option::Option<crate::SolanaCommitment> {
|
|
return self.commitment;
|
|
}
|
|
|
|
/// Returns whether the airdrop config would serialize to an empty object.
|
|
pub(crate) fn is_empty(&self) -> bool {
|
|
return self.recent_blockhash.is_none() && self.commitment.is_none();
|
|
}
|
|
|
|
/// 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(blockhash) = self.recent_blockhash.as_ref() {
|
|
object.insert("recentBlockhash".to_owned(), serde_json::Value::String(blockhash.clone()));
|
|
}
|
|
if let std::option::Option::Some(commitment) = self.commitment {
|
|
object.insert("commitment".to_owned(), serde_json::Value::String(commitment.as_str().to_owned()));
|
|
}
|
|
return serde_json::Value::Object(object);
|
|
}
|
|
}
|
|
|
|
/// Configuration accepted by `sendTransaction` without changing KSP transport retry semantics.
|
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
|
pub struct SolanaSendTransactionConfig {
|
|
skip_preflight: std::option::Option<bool>,
|
|
preflight_commitment: std::option::Option<crate::SolanaCommitment>,
|
|
encoding: std::option::Option<crate::SolanaTransactionBinaryEncoding>,
|
|
max_retries: std::option::Option<usize>,
|
|
min_context_slot: std::option::Option<u64>,
|
|
}
|
|
|
|
impl SolanaSendTransactionConfig {
|
|
/// Creates a serialized-transaction submission configuration.
|
|
#[must_use]
|
|
pub const fn new(
|
|
skip_preflight: std::option::Option<bool>,
|
|
preflight_commitment: std::option::Option<crate::SolanaCommitment>,
|
|
encoding: std::option::Option<crate::SolanaTransactionBinaryEncoding>,
|
|
max_retries: std::option::Option<usize>,
|
|
min_context_slot: std::option::Option<u64>,
|
|
) -> Self {
|
|
return Self { skip_preflight, preflight_commitment, encoding, max_retries, min_context_slot };
|
|
}
|
|
|
|
/// Returns the optional preflight-skip flag.
|
|
#[must_use]
|
|
pub const fn skip_preflight(&self) -> std::option::Option<bool> {
|
|
return self.skip_preflight;
|
|
}
|
|
|
|
/// Returns the optional preflight commitment.
|
|
#[must_use]
|
|
pub const fn preflight_commitment(&self) -> std::option::Option<crate::SolanaCommitment> {
|
|
return self.preflight_commitment;
|
|
}
|
|
|
|
/// Returns the optional serialized transaction encoding.
|
|
#[must_use]
|
|
pub const fn encoding(&self) -> std::option::Option<crate::SolanaTransactionBinaryEncoding> {
|
|
return self.encoding;
|
|
}
|
|
|
|
/// Returns the optional node-side retransmission limit.
|
|
#[must_use]
|
|
pub const fn max_retries(&self) -> std::option::Option<usize> {
|
|
return self.max_retries;
|
|
}
|
|
|
|
/// 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 the send config would serialize to an empty object.
|
|
pub(crate) const fn is_empty(&self) -> bool {
|
|
return self.skip_preflight.is_none()
|
|
&& self.preflight_commitment.is_none()
|
|
&& self.encoding.is_none()
|
|
&& self.max_retries.is_none()
|
|
&& self.min_context_slot.is_none();
|
|
}
|
|
|
|
/// 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(value) = self.skip_preflight {
|
|
object.insert("skipPreflight".to_owned(), serde_json::Value::Bool(value));
|
|
}
|
|
if let std::option::Option::Some(commitment) = self.preflight_commitment {
|
|
object.insert("preflightCommitment".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(max_retries) = self.max_retries {
|
|
object.insert("maxRetries".to_owned(), serde_json::json!(max_retries));
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// Account-return configuration nested under `simulateTransaction`.
|
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
|
pub struct SolanaSimulationAccountsConfig {
|
|
encoding: std::option::Option<crate::SolanaAccountEncoding>,
|
|
addresses: std::vec::Vec<ksp_core_lib::Pubkey>,
|
|
}
|
|
|
|
impl SolanaSimulationAccountsConfig {
|
|
/// Creates a simulation account-return configuration.
|
|
#[must_use]
|
|
pub fn new(encoding: std::option::Option<crate::SolanaAccountEncoding>, addresses: std::vec::Vec<ksp_core_lib::Pubkey>) -> Self {
|
|
return Self { encoding, addresses };
|
|
}
|
|
|
|
/// Returns the optional account-data encoding.
|
|
#[must_use]
|
|
pub const fn encoding(&self) -> std::option::Option<crate::SolanaAccountEncoding> {
|
|
return self.encoding;
|
|
}
|
|
|
|
/// Returns the requested account addresses in caller order.
|
|
#[must_use]
|
|
pub fn addresses(&self) -> &[ksp_core_lib::Pubkey] {
|
|
return self.addresses.as_slice();
|
|
}
|
|
|
|
/// Serializes the nested account 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(encoding) = self.encoding {
|
|
object.insert("encoding".to_owned(), serde_json::Value::String(encoding.as_str().to_owned()));
|
|
}
|
|
let addresses = self.addresses.iter().map(|address| address.to_string()).map(serde_json::Value::String).collect::<std::vec::Vec<_>>();
|
|
object.insert("addresses".to_owned(), serde_json::Value::Array(addresses));
|
|
return serde_json::Value::Object(object);
|
|
}
|
|
}
|
|
|
|
/// Configuration accepted by `simulateTransaction`.
|
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
|
pub struct SolanaSimulateTransactionConfig {
|
|
commitment: std::option::Option<crate::SolanaCommitment>,
|
|
encoding: std::option::Option<crate::SolanaTransactionBinaryEncoding>,
|
|
replace_recent_blockhash: std::option::Option<bool>,
|
|
sig_verify: std::option::Option<bool>,
|
|
min_context_slot: std::option::Option<u64>,
|
|
inner_instructions: std::option::Option<bool>,
|
|
accounts: std::option::Option<crate::SolanaSimulationAccountsConfig>,
|
|
}
|
|
|
|
impl SolanaSimulateTransactionConfig {
|
|
/// Creates a simulation configuration while leaving deterministic validation to the simulation wrapper.
|
|
#[must_use]
|
|
pub fn new(
|
|
commitment: std::option::Option<crate::SolanaCommitment>,
|
|
encoding: std::option::Option<crate::SolanaTransactionBinaryEncoding>,
|
|
replace_recent_blockhash: std::option::Option<bool>,
|
|
sig_verify: std::option::Option<bool>,
|
|
min_context_slot: std::option::Option<u64>,
|
|
inner_instructions: std::option::Option<bool>,
|
|
accounts: std::option::Option<crate::SolanaSimulationAccountsConfig>,
|
|
) -> Self {
|
|
return Self {
|
|
commitment,
|
|
encoding,
|
|
replace_recent_blockhash,
|
|
sig_verify,
|
|
min_context_slot,
|
|
inner_instructions,
|
|
accounts,
|
|
};
|
|
}
|
|
|
|
/// Returns the optional simulation commitment.
|
|
#[must_use]
|
|
pub const fn commitment(&self) -> std::option::Option<crate::SolanaCommitment> {
|
|
return self.commitment;
|
|
}
|
|
|
|
/// Returns the optional serialized transaction encoding.
|
|
#[must_use]
|
|
pub const fn encoding(&self) -> std::option::Option<crate::SolanaTransactionBinaryEncoding> {
|
|
return self.encoding;
|
|
}
|
|
|
|
/// Returns the optional recent-blockhash replacement flag.
|
|
#[must_use]
|
|
pub const fn replace_recent_blockhash(&self) -> std::option::Option<bool> {
|
|
return self.replace_recent_blockhash;
|
|
}
|
|
|
|
/// Returns the optional signature-verification flag.
|
|
#[must_use]
|
|
pub const fn sig_verify(&self) -> std::option::Option<bool> {
|
|
return self.sig_verify;
|
|
}
|
|
|
|
/// 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 the optional inner-instruction recording flag.
|
|
#[must_use]
|
|
pub const fn inner_instructions(&self) -> std::option::Option<bool> {
|
|
return self.inner_instructions;
|
|
}
|
|
|
|
/// Returns the optional post-simulation account-return configuration.
|
|
#[must_use]
|
|
pub const fn accounts(&self) -> std::option::Option<&crate::SolanaSimulationAccountsConfig> {
|
|
return self.accounts.as_ref();
|
|
}
|
|
|
|
/// Returns whether the simulation config would serialize to an empty object.
|
|
pub(crate) fn is_empty(&self) -> bool {
|
|
return self.commitment.is_none()
|
|
&& self.encoding.is_none()
|
|
&& self.replace_recent_blockhash.is_none()
|
|
&& self.sig_verify.is_none()
|
|
&& self.min_context_slot.is_none()
|
|
&& self.inner_instructions.is_none()
|
|
&& self.accounts.is_none();
|
|
}
|
|
|
|
/// 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(encoding) = self.encoding {
|
|
object.insert("encoding".to_owned(), serde_json::Value::String(encoding.as_str().to_owned()));
|
|
}
|
|
if let std::option::Option::Some(value) = self.replace_recent_blockhash {
|
|
object.insert("replaceRecentBlockhash".to_owned(), serde_json::Value::Bool(value));
|
|
}
|
|
if let std::option::Option::Some(value) = self.sig_verify {
|
|
object.insert("sigVerify".to_owned(), serde_json::Value::Bool(value));
|
|
}
|
|
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()));
|
|
}
|
|
if let std::option::Option::Some(value) = self.inner_instructions {
|
|
object.insert("innerInstructions".to_owned(), serde_json::Value::Bool(value));
|
|
}
|
|
if let std::option::Option::Some(accounts) = self.accounts.as_ref() {
|
|
object.insert("accounts".to_owned(), accounts.to_json_value());
|
|
}
|
|
return serde_json::Value::Object(object);
|
|
}
|
|
}
|
|
|
|
/// Blockhash information returned by `getLatestBlockhash` and optionally by simulation.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct SolanaLatestBlockhash {
|
|
blockhash: std::string::String,
|
|
last_valid_block_height: u64,
|
|
}
|
|
|
|
impl SolanaLatestBlockhash {
|
|
/// Returns the base58 blockhash string without locally decoding it.
|
|
#[must_use]
|
|
pub fn blockhash(&self) -> &str {
|
|
return self.blockhash.as_str();
|
|
}
|
|
|
|
/// Returns the last valid block height for this blockhash.
|
|
#[must_use]
|
|
pub const fn last_valid_block_height(&self) -> u64 {
|
|
return self.last_valid_block_height;
|
|
}
|
|
|
|
/// Decodes a latest-blockhash object 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::<WireLatestBlockhash>(method, value);
|
|
let wire = match decoded {
|
|
std::result::Result::Ok(wire) => wire,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
if wire.blockhash.is_empty() {
|
|
return std::result::Result::Err(invalid_transaction_wire(method, "blockhash", "latest blockhash must not be empty"));
|
|
}
|
|
return std::result::Result::Ok(Self { blockhash: wire.blockhash, last_valid_block_height: wire.last_valid_block_height });
|
|
}
|
|
}
|
|
|
|
/// One recent prioritization-fee sample returned by `getRecentPrioritizationFees`.
|
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
|
pub struct SolanaPrioritizationFee {
|
|
slot: u64,
|
|
prioritization_fee: u64,
|
|
}
|
|
|
|
impl SolanaPrioritizationFee {
|
|
/// Returns the sample slot.
|
|
#[must_use]
|
|
pub const fn slot(&self) -> u64 {
|
|
return self.slot;
|
|
}
|
|
|
|
/// Returns the minimum prioritization fee in micro-lamports reported for the sample.
|
|
#[must_use]
|
|
pub const fn prioritization_fee(&self) -> u64 {
|
|
return self.prioritization_fee;
|
|
}
|
|
|
|
/// Decodes one prioritization-fee sample 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::<WirePrioritizationFee>(method, value);
|
|
return match decoded {
|
|
std::result::Result::Ok(wire) => std::result::Result::Ok(Self { slot: wire.slot, prioritization_fee: wire.prioritization_fee }),
|
|
std::result::Result::Err(error) => std::result::Result::Err(error),
|
|
};
|
|
}
|
|
}
|
|
|
|
/// Confirmation state reported for a signature or transaction status.
|
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
|
pub enum SolanaTransactionConfirmationStatus {
|
|
/// The transaction has been processed by a node.
|
|
Processed,
|
|
/// The transaction has reached confirmed commitment.
|
|
Confirmed,
|
|
/// The transaction has reached finalized commitment.
|
|
Finalized,
|
|
}
|
|
|
|
/// One ordered signature record returned by `getSignaturesForAddress`.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct SolanaSignatureInfo {
|
|
signature: std::string::String,
|
|
slot: u64,
|
|
err: std::option::Option<serde_json::Value>,
|
|
memo: std::option::Option<std::string::String>,
|
|
block_time: std::option::Option<i64>,
|
|
confirmation_status: std::option::Option<crate::SolanaTransactionConfirmationStatus>,
|
|
transaction_index: std::option::Option<u32>,
|
|
}
|
|
|
|
impl SolanaSignatureInfo {
|
|
/// Returns the base58 transaction signature string without locally decoding it.
|
|
#[must_use]
|
|
pub fn signature(&self) -> &str {
|
|
return self.signature.as_str();
|
|
}
|
|
|
|
/// Returns the transaction slot.
|
|
#[must_use]
|
|
pub const fn slot(&self) -> u64 {
|
|
return self.slot;
|
|
}
|
|
|
|
/// Returns the nullable transaction error wire value.
|
|
#[must_use]
|
|
pub const fn err(&self) -> std::option::Option<&serde_json::Value> {
|
|
return self.err.as_ref();
|
|
}
|
|
|
|
/// Returns the nullable memo text.
|
|
#[must_use]
|
|
pub fn memo(&self) -> std::option::Option<&str> {
|
|
return self.memo.as_deref();
|
|
}
|
|
|
|
/// 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 optional confirmation status.
|
|
#[must_use]
|
|
pub const fn confirmation_status(&self) -> std::option::Option<crate::SolanaTransactionConfirmationStatus> {
|
|
return self.confirmation_status;
|
|
}
|
|
|
|
/// Returns the optional transaction index exposed by current Agave runtimes.
|
|
#[must_use]
|
|
pub const fn transaction_index(&self) -> std::option::Option<u32> {
|
|
return self.transaction_index;
|
|
}
|
|
|
|
/// Decodes one signature record 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::<WireSignatureInfo>(method, value);
|
|
let wire = match decoded {
|
|
std::result::Result::Ok(wire) => wire,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let status = decode_confirmation_status(method, "confirmationStatus", wire.confirmation_status);
|
|
let confirmation_status = match status {
|
|
std::result::Result::Ok(status) => status,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
return std::result::Result::Ok(Self {
|
|
signature: wire.signature,
|
|
slot: wire.slot,
|
|
err: wire.err,
|
|
memo: wire.memo,
|
|
block_time: wire.block_time,
|
|
confirmation_status,
|
|
transaction_index: wire.transaction_index,
|
|
});
|
|
}
|
|
}
|
|
|
|
/// One non-null position returned by `getSignatureStatuses`.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct SolanaSignatureStatus {
|
|
slot: u64,
|
|
confirmations: std::option::Option<u64>,
|
|
status: serde_json::Value,
|
|
err: std::option::Option<serde_json::Value>,
|
|
confirmation_status: std::option::Option<crate::SolanaTransactionConfirmationStatus>,
|
|
}
|
|
|
|
impl SolanaSignatureStatus {
|
|
/// Returns the slot at which the signature status was observed.
|
|
#[must_use]
|
|
pub const fn slot(&self) -> u64 {
|
|
return self.slot;
|
|
}
|
|
|
|
/// Returns remaining confirmations, or `None` when the transaction is rooted.
|
|
#[must_use]
|
|
pub const fn confirmations(&self) -> std::option::Option<u64> {
|
|
return self.confirmations;
|
|
}
|
|
|
|
/// Returns the legacy `status` result field losslessly.
|
|
#[must_use]
|
|
pub const fn status(&self) -> &serde_json::Value {
|
|
return &self.status;
|
|
}
|
|
|
|
/// Returns the nullable transaction error wire value.
|
|
#[must_use]
|
|
pub const fn err(&self) -> std::option::Option<&serde_json::Value> {
|
|
return self.err.as_ref();
|
|
}
|
|
|
|
/// Returns the optional confirmation status.
|
|
#[must_use]
|
|
pub const fn confirmation_status(&self) -> std::option::Option<crate::SolanaTransactionConfirmationStatus> {
|
|
return self.confirmation_status;
|
|
}
|
|
|
|
/// Decodes one present signature status 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::<WireSignatureStatus>(method, value);
|
|
let wire = match decoded {
|
|
std::result::Result::Ok(wire) => wire,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let status = decode_confirmation_status(method, "confirmationStatus", wire.confirmation_status);
|
|
let confirmation_status = match status {
|
|
std::result::Result::Ok(status) => status,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
return std::result::Result::Ok(Self {
|
|
slot: wire.slot,
|
|
confirmations: wire.confirmations,
|
|
status: wire.status,
|
|
err: wire.err,
|
|
confirmation_status,
|
|
});
|
|
}
|
|
}
|
|
|
|
/// Wire-preserving transaction payload returned by `getTransaction`.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum SolanaEncodedTransaction {
|
|
/// Legacy single-string base58 form retained for RPC backwards compatibility.
|
|
LegacyBinary(std::string::String),
|
|
/// Explicit encoded tuple `[data, "base58" | "base64"]`.
|
|
Binary {
|
|
/// Encoded transaction bytes.
|
|
data: std::string::String,
|
|
/// Explicit binary encoding label.
|
|
encoding: crate::SolanaTransactionBinaryEncoding,
|
|
},
|
|
/// JSON or `jsonParsed` object preserved without Program-specific decoding.
|
|
Json(serde_json::Value),
|
|
}
|
|
|
|
impl SolanaEncodedTransaction {
|
|
/// Decodes the untagged transaction wire union without decoding transaction bytes.
|
|
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
|
return match value {
|
|
serde_json::Value::String(data) => std::result::Result::Ok(Self::LegacyBinary(data)),
|
|
serde_json::Value::Object(object) => std::result::Result::Ok(Self::Json(serde_json::Value::Object(object))),
|
|
serde_json::Value::Array(values) => decode_binary_transaction_tuple(method, values),
|
|
_ => std::result::Result::Err(invalid_transaction_wire(method, "transaction", "transaction payload has an unsupported JSON shape")),
|
|
};
|
|
}
|
|
}
|
|
|
|
/// Transaction version reported by `getTransaction` when the version field is present.
|
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
|
pub enum SolanaTransactionVersion {
|
|
/// Legacy transaction without a versioned message prefix.
|
|
Legacy,
|
|
/// Numeric versioned-transaction identifier.
|
|
Number(u8),
|
|
}
|
|
|
|
impl SolanaTransactionVersion {
|
|
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) => {
|
|
let number = value.as_u64();
|
|
let number = match number {
|
|
std::option::Option::Some(number) => number,
|
|
std::option::Option::None => {
|
|
return std::result::Result::Err(invalid_transaction_wire(
|
|
method,
|
|
"version",
|
|
"transaction version must be an unsigned integer or legacy",
|
|
));
|
|
},
|
|
};
|
|
let converted = u8::try_from(number);
|
|
return match converted {
|
|
std::result::Result::Ok(number) => std::result::Result::Ok(Self::Number(number)),
|
|
std::result::Result::Err(_) => {
|
|
std::result::Result::Err(invalid_transaction_wire(method, "version", "transaction version is outside the supported wire integer range"))
|
|
},
|
|
};
|
|
},
|
|
_ => std::result::Result::Err(invalid_transaction_wire(method, "version", "transaction version must be an unsigned integer or legacy")),
|
|
};
|
|
}
|
|
}
|
|
|
|
/// Confirmed transaction result returned by `getTransaction` when the RPC result is non-null.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct SolanaConfirmedTransaction {
|
|
slot: u64,
|
|
block_time: std::option::Option<i64>,
|
|
transaction: crate::SolanaEncodedTransaction,
|
|
meta: crate::SolanaWireField<serde_json::Value>,
|
|
version: crate::SolanaWireField<crate::SolanaTransactionVersion>,
|
|
transaction_index: crate::SolanaWireField<u32>,
|
|
}
|
|
|
|
impl SolanaConfirmedTransaction {
|
|
/// Returns the containing slot.
|
|
#[must_use]
|
|
pub const fn slot(&self) -> u64 {
|
|
return self.slot;
|
|
}
|
|
|
|
/// 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 wire-preserving encoded/JSON transaction payload.
|
|
#[must_use]
|
|
pub const fn transaction(&self) -> &crate::SolanaEncodedTransaction {
|
|
return &self.transaction;
|
|
}
|
|
|
|
/// Returns the transaction metadata while preserving omitted/null/present states.
|
|
#[must_use]
|
|
pub const fn meta(&self) -> &crate::SolanaWireField<serde_json::Value> {
|
|
return &self.meta;
|
|
}
|
|
|
|
/// Returns the transaction version while preserving omitted/null/present states.
|
|
#[must_use]
|
|
pub const fn version(&self) -> &crate::SolanaWireField<crate::SolanaTransactionVersion> {
|
|
return &self.version;
|
|
}
|
|
|
|
/// Returns the current optional transaction index while preserving omitted/null/present states.
|
|
#[must_use]
|
|
pub const fn transaction_index(&self) -> &crate::SolanaWireField<u32> {
|
|
return &self.transaction_index;
|
|
}
|
|
|
|
/// Decodes one non-null confirmed transaction without decoding Program-specific transaction internals.
|
|
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
|
let decoded = crate::decode_wire_json::<WireConfirmedTransaction>(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_transaction_version_field(method, wire.version);
|
|
let version = match version {
|
|
std::result::Result::Ok(version) => version,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
return std::result::Result::Ok(Self {
|
|
slot: wire.slot,
|
|
block_time: wire.block_time,
|
|
transaction,
|
|
meta: wire.meta,
|
|
version,
|
|
transaction_index: wire.transaction_index,
|
|
});
|
|
}
|
|
}
|
|
|
|
/// Rich result payload returned inside the contextual `simulateTransaction` response.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct SolanaSimulateTransactionResult {
|
|
err: crate::SolanaWireField<serde_json::Value>,
|
|
logs: crate::SolanaWireField<std::vec::Vec<std::string::String>>,
|
|
accounts: crate::SolanaWireField<std::vec::Vec<std::option::Option<crate::SolanaAccount>>>,
|
|
units_consumed: crate::SolanaWireField<u64>,
|
|
loaded_accounts_data_size: crate::SolanaWireField<u32>,
|
|
return_data: crate::SolanaWireField<serde_json::Value>,
|
|
inner_instructions: crate::SolanaWireField<serde_json::Value>,
|
|
replacement_blockhash: crate::SolanaWireField<crate::SolanaLatestBlockhash>,
|
|
fee: crate::SolanaWireField<u64>,
|
|
pre_balances: crate::SolanaWireField<std::vec::Vec<u64>>,
|
|
post_balances: crate::SolanaWireField<std::vec::Vec<u64>>,
|
|
pre_token_balances: crate::SolanaWireField<serde_json::Value>,
|
|
post_token_balances: crate::SolanaWireField<serde_json::Value>,
|
|
loaded_addresses: crate::SolanaWireField<serde_json::Value>,
|
|
}
|
|
|
|
impl SolanaSimulateTransactionResult {
|
|
/// Returns the simulation error while preserving omission versus explicit `null`.
|
|
#[must_use]
|
|
pub const fn err(&self) -> &crate::SolanaWireField<serde_json::Value> {
|
|
return &self.err;
|
|
}
|
|
|
|
/// Returns simulation logs while preserving omission versus explicit `null`.
|
|
#[must_use]
|
|
pub const fn logs(&self) -> &crate::SolanaWireField<std::vec::Vec<std::string::String>> {
|
|
return &self.logs;
|
|
}
|
|
|
|
/// Returns requested post-simulation accounts with positional nulls preserved.
|
|
#[must_use]
|
|
pub const fn accounts(&self) -> &crate::SolanaWireField<std::vec::Vec<std::option::Option<crate::SolanaAccount>>> {
|
|
return &self.accounts;
|
|
}
|
|
|
|
/// Returns compute units consumed when the runtime reports them.
|
|
#[must_use]
|
|
pub const fn units_consumed(&self) -> &crate::SolanaWireField<u64> {
|
|
return &self.units_consumed;
|
|
}
|
|
|
|
/// Returns loaded-account data size when the runtime reports it.
|
|
#[must_use]
|
|
pub const fn loaded_accounts_data_size(&self) -> &crate::SolanaWireField<u32> {
|
|
return &self.loaded_accounts_data_size;
|
|
}
|
|
|
|
/// Returns Program return data losslessly at the transport boundary.
|
|
#[must_use]
|
|
pub const fn return_data(&self) -> &crate::SolanaWireField<serde_json::Value> {
|
|
return &self.return_data;
|
|
}
|
|
|
|
/// Returns inner instructions losslessly without Program-specific decoding.
|
|
#[must_use]
|
|
pub const fn inner_instructions(&self) -> &crate::SolanaWireField<serde_json::Value> {
|
|
return &self.inner_instructions;
|
|
}
|
|
|
|
/// Returns a replacement blockhash when the runtime produced one.
|
|
#[must_use]
|
|
pub const fn replacement_blockhash(&self) -> &crate::SolanaWireField<crate::SolanaLatestBlockhash> {
|
|
return &self.replacement_blockhash;
|
|
}
|
|
|
|
/// Returns the simulated fee when reported.
|
|
#[must_use]
|
|
pub const fn fee(&self) -> &crate::SolanaWireField<u64> {
|
|
return &self.fee;
|
|
}
|
|
|
|
/// Returns pre-simulation lamport balances when reported.
|
|
#[must_use]
|
|
pub const fn pre_balances(&self) -> &crate::SolanaWireField<std::vec::Vec<u64>> {
|
|
return &self.pre_balances;
|
|
}
|
|
|
|
/// Returns post-simulation lamport balances when reported.
|
|
#[must_use]
|
|
pub const fn post_balances(&self) -> &crate::SolanaWireField<std::vec::Vec<u64>> {
|
|
return &self.post_balances;
|
|
}
|
|
|
|
/// Returns pre-simulation token balances losslessly without SPL decoding.
|
|
#[must_use]
|
|
pub const fn pre_token_balances(&self) -> &crate::SolanaWireField<serde_json::Value> {
|
|
return &self.pre_token_balances;
|
|
}
|
|
|
|
/// Returns post-simulation token balances losslessly without SPL decoding.
|
|
#[must_use]
|
|
pub const fn post_token_balances(&self) -> &crate::SolanaWireField<serde_json::Value> {
|
|
return &self.post_token_balances;
|
|
}
|
|
|
|
/// Returns loaded-address metadata losslessly without transaction-message decoding.
|
|
#[must_use]
|
|
pub const fn loaded_addresses(&self) -> &crate::SolanaWireField<serde_json::Value> {
|
|
return &self.loaded_addresses;
|
|
}
|
|
|
|
/// Decodes a simulation result while preserving optional/nullable current Agave fields.
|
|
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
|
let decoded = crate::decode_wire_json::<WireSimulateTransactionResult>(method, value);
|
|
let wire = match decoded {
|
|
std::result::Result::Ok(wire) => wire,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let accounts = decode_simulation_accounts_field(method, wire.accounts);
|
|
let accounts = match accounts {
|
|
std::result::Result::Ok(accounts) => accounts,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let replacement_blockhash = decode_replacement_blockhash_field(method, wire.replacement_blockhash);
|
|
let replacement_blockhash = match replacement_blockhash {
|
|
std::result::Result::Ok(blockhash) => blockhash,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
return std::result::Result::Ok(Self {
|
|
err: wire.err,
|
|
logs: wire.logs,
|
|
accounts,
|
|
units_consumed: wire.units_consumed,
|
|
loaded_accounts_data_size: wire.loaded_accounts_data_size,
|
|
return_data: wire.return_data,
|
|
inner_instructions: wire.inner_instructions,
|
|
replacement_blockhash,
|
|
fee: wire.fee,
|
|
pre_balances: wire.pre_balances,
|
|
post_balances: wire.post_balances,
|
|
pre_token_balances: wire.pre_token_balances,
|
|
post_token_balances: wire.post_token_balances,
|
|
loaded_addresses: wire.loaded_addresses,
|
|
});
|
|
}
|
|
}
|
|
|
|
fn invalid_transaction_wire(method: &str, field: &str, message: &'static str) -> ksp_core_lib::Error {
|
|
return ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_RESPONSE, message).with_context("rpc_method", method).with_context("field", field);
|
|
}
|
|
|
|
fn decode_confirmation_status(
|
|
method: &str,
|
|
field: &str,
|
|
value: std::option::Option<std::string::String>,
|
|
) -> ksp_core_lib::Result<std::option::Option<crate::SolanaTransactionConfirmationStatus>> {
|
|
let value = match value {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return std::result::Result::Ok(std::option::Option::None),
|
|
};
|
|
let status = match value.as_str() {
|
|
"processed" => crate::SolanaTransactionConfirmationStatus::Processed,
|
|
"confirmed" => crate::SolanaTransactionConfirmationStatus::Confirmed,
|
|
"finalized" => crate::SolanaTransactionConfirmationStatus::Finalized,
|
|
_ => return std::result::Result::Err(invalid_transaction_wire(method, field, "transaction confirmation status is unknown")),
|
|
};
|
|
return std::result::Result::Ok(std::option::Option::Some(status));
|
|
}
|
|
|
|
fn decode_binary_transaction_tuple(method: &str, values: std::vec::Vec<serde_json::Value>) -> ksp_core_lib::Result<crate::SolanaEncodedTransaction> {
|
|
if values.len() != 2 {
|
|
return std::result::Result::Err(invalid_transaction_wire(method, "transaction", "encoded transaction tuple must contain data and encoding"));
|
|
}
|
|
let mut values = values.into_iter();
|
|
let data_value = match values.next() {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => {
|
|
return std::result::Result::Err(invalid_transaction_wire(method, "transaction", "encoded transaction tuple is missing data"));
|
|
},
|
|
};
|
|
let encoding_value = match values.next() {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => {
|
|
return std::result::Result::Err(invalid_transaction_wire(method, "transaction", "encoded transaction tuple is missing encoding"));
|
|
},
|
|
};
|
|
let data = match data_value.as_str() {
|
|
std::option::Option::Some(value) => value.to_owned(),
|
|
std::option::Option::None => {
|
|
return std::result::Result::Err(invalid_transaction_wire(method, "transaction", "encoded transaction tuple data must be a string"));
|
|
},
|
|
};
|
|
let encoding = match encoding_value.as_str() {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => {
|
|
return std::result::Result::Err(invalid_transaction_wire(method, "transaction", "encoded transaction tuple encoding must be a string"));
|
|
},
|
|
};
|
|
let encoding = crate::SolanaTransactionBinaryEncoding::from_wire(encoding);
|
|
let encoding = match encoding {
|
|
std::option::Option::Some(encoding) => encoding,
|
|
std::option::Option::None => {
|
|
return std::result::Result::Err(invalid_transaction_wire(method, "transaction", "encoded transaction tuple uses an unsupported binary encoding"));
|
|
},
|
|
};
|
|
return std::result::Result::Ok(crate::SolanaEncodedTransaction::Binary { data, encoding });
|
|
}
|
|
|
|
fn decode_transaction_version_field(
|
|
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 version = crate::SolanaTransactionVersion::decode_wire(method, value);
|
|
match version {
|
|
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_simulation_accounts_field(
|
|
method: &str,
|
|
field: crate::SolanaWireField<std::vec::Vec<std::option::Option<serde_json::Value>>>,
|
|
) -> ksp_core_lib::Result<crate::SolanaWireField<std::vec::Vec<std::option::Option<crate::SolanaAccount>>>> {
|
|
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 accounts = std::vec::Vec::with_capacity(values.len());
|
|
for value in values {
|
|
let value = match value {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => {
|
|
accounts.push(std::option::Option::None);
|
|
continue;
|
|
},
|
|
};
|
|
let account = crate::SolanaAccount::decode_wire(method, value);
|
|
match account {
|
|
std::result::Result::Ok(account) => accounts.push(std::option::Option::Some(account)),
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
}
|
|
}
|
|
return std::result::Result::Ok(crate::SolanaWireField::Value(accounts));
|
|
}
|
|
|
|
fn decode_replacement_blockhash_field(
|
|
method: &str,
|
|
field: crate::SolanaWireField<serde_json::Value>,
|
|
) -> ksp_core_lib::Result<crate::SolanaWireField<crate::SolanaLatestBlockhash>> {
|
|
let value = 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(value) => value,
|
|
};
|
|
let blockhash = crate::SolanaLatestBlockhash::decode_wire(method, value);
|
|
return match blockhash {
|
|
std::result::Result::Ok(blockhash) => std::result::Result::Ok(crate::SolanaWireField::Value(blockhash)),
|
|
std::result::Result::Err(error) => std::result::Result::Err(error),
|
|
};
|
|
}
|
|
|
|
#[derive(serde::Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
struct WireLatestBlockhash {
|
|
blockhash: std::string::String,
|
|
last_valid_block_height: u64,
|
|
}
|
|
|
|
#[derive(serde::Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
struct WirePrioritizationFee {
|
|
slot: u64,
|
|
prioritization_fee: u64,
|
|
}
|
|
|
|
#[derive(serde::Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
struct WireSignatureInfo {
|
|
signature: std::string::String,
|
|
slot: u64,
|
|
#[serde(default)]
|
|
err: std::option::Option<serde_json::Value>,
|
|
#[serde(default)]
|
|
memo: std::option::Option<std::string::String>,
|
|
#[serde(default)]
|
|
block_time: std::option::Option<i64>,
|
|
#[serde(default)]
|
|
confirmation_status: std::option::Option<std::string::String>,
|
|
#[serde(default)]
|
|
transaction_index: std::option::Option<u32>,
|
|
}
|
|
|
|
#[derive(serde::Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
struct WireSignatureStatus {
|
|
slot: u64,
|
|
#[serde(default)]
|
|
confirmations: std::option::Option<u64>,
|
|
status: serde_json::Value,
|
|
#[serde(default)]
|
|
err: std::option::Option<serde_json::Value>,
|
|
#[serde(default)]
|
|
confirmation_status: std::option::Option<std::string::String>,
|
|
}
|
|
|
|
#[derive(serde::Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
struct WireConfirmedTransaction {
|
|
slot: u64,
|
|
transaction: serde_json::Value,
|
|
#[serde(default)]
|
|
meta: crate::SolanaWireField<serde_json::Value>,
|
|
#[serde(default)]
|
|
block_time: std::option::Option<i64>,
|
|
#[serde(default)]
|
|
version: crate::SolanaWireField<serde_json::Value>,
|
|
#[serde(default)]
|
|
transaction_index: crate::SolanaWireField<u32>,
|
|
}
|
|
|
|
#[derive(serde::Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
struct WireSimulateTransactionResult {
|
|
#[serde(default)]
|
|
err: crate::SolanaWireField<serde_json::Value>,
|
|
#[serde(default)]
|
|
logs: crate::SolanaWireField<std::vec::Vec<std::string::String>>,
|
|
#[serde(default)]
|
|
accounts: crate::SolanaWireField<std::vec::Vec<std::option::Option<serde_json::Value>>>,
|
|
#[serde(default)]
|
|
units_consumed: crate::SolanaWireField<u64>,
|
|
#[serde(default)]
|
|
loaded_accounts_data_size: crate::SolanaWireField<u32>,
|
|
#[serde(default)]
|
|
return_data: crate::SolanaWireField<serde_json::Value>,
|
|
#[serde(default)]
|
|
inner_instructions: crate::SolanaWireField<serde_json::Value>,
|
|
#[serde(default)]
|
|
replacement_blockhash: crate::SolanaWireField<serde_json::Value>,
|
|
#[serde(default)]
|
|
fee: crate::SolanaWireField<u64>,
|
|
#[serde(default)]
|
|
pre_balances: crate::SolanaWireField<std::vec::Vec<u64>>,
|
|
#[serde(default)]
|
|
post_balances: crate::SolanaWireField<std::vec::Vec<u64>>,
|
|
#[serde(default)]
|
|
pre_token_balances: crate::SolanaWireField<serde_json::Value>,
|
|
#[serde(default)]
|
|
post_token_balances: crate::SolanaWireField<serde_json::Value>,
|
|
#[serde(default)]
|
|
loaded_addresses: crate::SolanaWireField<serde_json::Value>,
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[path = "../unit_tests/rpc_transactions.rs"]
|
|
mod tests;
|