v0.2.2-pre.004
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-onchain-transport-lib/src/lib.rs
|
||||
// version: 9
|
||||
// version: 10
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
#![forbid(unsafe_code)]
|
||||
@@ -9,8 +9,8 @@
|
||||
//! This crate owns runtime HTTP transport settings, Solana HTTP JSON-RPC envelopes and the audited standard method registry. It deliberately remains
|
||||
//! independent from `ksp-config-lib`, Store and Program layers. `ksp-config-lib` now constructs these public settings through its one-way Config ->
|
||||
//! Transport adapter without creating a reverse dependency. Logical endpoint clients, priority-aware pools, bounded admission limits and retry/no-resend policy
|
||||
//! are available. The four typed Solana HTTP foundation canaries and the `0.2.2` Accounts wrappers execute real JSON-RPC requests through the shared
|
||||
//! transport path while the remaining audited methods stay staged by subsequent `0.2.x` prereleases.
|
||||
//! are available. The four typed Solana HTTP foundation canaries plus the `0.2.2` Accounts and Tokens wrappers execute real JSON-RPC requests through
|
||||
//! the shared transport path while the remaining audited methods stay staged by subsequent `0.2.x` prereleases.
|
||||
|
||||
mod client;
|
||||
mod constants;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-onchain-transport-lib/src/rpc_accounts.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
/// Account-data encoding accepted by Solana HTTP account methods.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
@@ -116,7 +116,7 @@ impl SolanaAccountInfoConfig {
|
||||
return self.context.min_context_slot();
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
return self.encoding.is_none() && self.data_slice.is_none() && self.commitment().is_none() && self.min_context_slot().is_none();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-onchain-transport-lib/src/rpc_tokens.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
/// Exclusive selector accepted by token-account list RPC methods.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
@@ -13,7 +13,6 @@ pub enum SolanaTokenAccountSelector {
|
||||
impl SolanaTokenAccountSelector {
|
||||
/// Serializes the exclusive selector to the Solana JSON-RPC wire object.
|
||||
#[must_use]
|
||||
#[cfg(test)]
|
||||
pub(crate) fn to_json_value(&self) -> serde_json::Value {
|
||||
return match self {
|
||||
Self::Mint(pubkey) => serde_json::json!({"mint": pubkey.to_string()}),
|
||||
@@ -57,7 +56,6 @@ impl SolanaTokenAmount {
|
||||
}
|
||||
|
||||
/// Decodes one token amount from the Solana JSON wire shape.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
||||
let decoded = crate::decode_wire_json::<WireTokenAmount>(method, value);
|
||||
return match decoded {
|
||||
@@ -93,7 +91,6 @@ impl SolanaTokenAccountBalance {
|
||||
}
|
||||
|
||||
/// Decodes one token-account balance entry from the Solana JSON wire shape.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
||||
let decoded = crate::decode_wire_json::<WireTokenAccountBalance>(method, value);
|
||||
let wire = match decoded {
|
||||
@@ -115,7 +112,6 @@ impl SolanaTokenAccountBalance {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[derive(serde::Deserialize)]
|
||||
struct WireTokenAmount {
|
||||
amount: std::string::String,
|
||||
@@ -126,7 +122,6 @@ struct WireTokenAmount {
|
||||
ui_amount_string: std::string::String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[derive(serde::Deserialize)]
|
||||
struct WireTokenAccountBalance {
|
||||
address: std::string::String,
|
||||
@@ -138,6 +133,222 @@ struct WireTokenAccountBalance {
|
||||
ui_amount_string: std::string::String,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct WireRpcResponse<T> {
|
||||
context: serde_json::Value,
|
||||
value: T,
|
||||
}
|
||||
|
||||
impl crate::HttpTransportPool {
|
||||
/// Executes typed `getTokenAccountBalance` through the common KSP HTTP transport path.
|
||||
pub async fn get_token_account_balance(
|
||||
&self,
|
||||
role: &crate::HttpRoleName,
|
||||
token_account: &ksp_core_lib::Pubkey,
|
||||
config: std::option::Option<&crate::SolanaCommitmentConfig>,
|
||||
) -> ksp_core_lib::Result<crate::SolanaRpcResponse<crate::SolanaTokenAmount>> {
|
||||
let method_result = token_descriptor("getTokenAccountBalance");
|
||||
let method = match method_result {
|
||||
std::result::Result::Ok(method) => method,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut params = std::vec![serde_json::Value::String(token_account.to_string())];
|
||||
push_commitment_config(&mut params, config);
|
||||
let result = self.execute_standard_rpc(role, method, params).await;
|
||||
let value = match result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return decode_token_amount_response("getTokenAccountBalance", value);
|
||||
}
|
||||
|
||||
/// Executes typed `getTokenAccountsByDelegate` through the common KSP HTTP transport path.
|
||||
pub async fn get_token_accounts_by_delegate(
|
||||
&self,
|
||||
role: &crate::HttpRoleName,
|
||||
delegate: &ksp_core_lib::Pubkey,
|
||||
selector: &crate::SolanaTokenAccountSelector,
|
||||
config: std::option::Option<&crate::SolanaAccountInfoConfig>,
|
||||
) -> ksp_core_lib::Result<crate::SolanaRpcResponse<std::vec::Vec<crate::SolanaKeyedAccount>>> {
|
||||
return self.get_token_accounts_list("getTokenAccountsByDelegate", role, delegate, selector, config).await;
|
||||
}
|
||||
|
||||
/// Executes typed `getTokenAccountsByOwner` through the common KSP HTTP transport path.
|
||||
pub async fn get_token_accounts_by_owner(
|
||||
&self,
|
||||
role: &crate::HttpRoleName,
|
||||
owner: &ksp_core_lib::Pubkey,
|
||||
selector: &crate::SolanaTokenAccountSelector,
|
||||
config: std::option::Option<&crate::SolanaAccountInfoConfig>,
|
||||
) -> ksp_core_lib::Result<crate::SolanaRpcResponse<std::vec::Vec<crate::SolanaKeyedAccount>>> {
|
||||
return self.get_token_accounts_list("getTokenAccountsByOwner", role, owner, selector, config).await;
|
||||
}
|
||||
|
||||
/// Executes typed `getTokenLargestAccounts` through the common KSP HTTP transport path.
|
||||
pub async fn get_token_largest_accounts(
|
||||
&self,
|
||||
role: &crate::HttpRoleName,
|
||||
mint: &ksp_core_lib::Pubkey,
|
||||
config: std::option::Option<&crate::SolanaCommitmentConfig>,
|
||||
) -> ksp_core_lib::Result<crate::SolanaRpcResponse<std::vec::Vec<crate::SolanaTokenAccountBalance>>> {
|
||||
let method_result = token_descriptor("getTokenLargestAccounts");
|
||||
let method = match method_result {
|
||||
std::result::Result::Ok(method) => method,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut params = std::vec![serde_json::Value::String(mint.to_string())];
|
||||
push_commitment_config(&mut params, config);
|
||||
let result = self.execute_standard_rpc(role, method, params).await;
|
||||
let value = match result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return decode_token_account_balances_response("getTokenLargestAccounts", value);
|
||||
}
|
||||
|
||||
/// Executes typed `getTokenSupply` through the common KSP HTTP transport path.
|
||||
pub async fn get_token_supply(
|
||||
&self,
|
||||
role: &crate::HttpRoleName,
|
||||
mint: &ksp_core_lib::Pubkey,
|
||||
config: std::option::Option<&crate::SolanaCommitmentConfig>,
|
||||
) -> ksp_core_lib::Result<crate::SolanaRpcResponse<crate::SolanaTokenAmount>> {
|
||||
let method_result = token_descriptor("getTokenSupply");
|
||||
let method = match method_result {
|
||||
std::result::Result::Ok(method) => method,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut params = std::vec![serde_json::Value::String(mint.to_string())];
|
||||
push_commitment_config(&mut params, config);
|
||||
let result = self.execute_standard_rpc(role, method, params).await;
|
||||
let value = match result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return decode_token_amount_response("getTokenSupply", value);
|
||||
}
|
||||
|
||||
async fn get_token_accounts_list(
|
||||
&self,
|
||||
method_name: &'static str,
|
||||
role: &crate::HttpRoleName,
|
||||
address: &ksp_core_lib::Pubkey,
|
||||
selector: &crate::SolanaTokenAccountSelector,
|
||||
config: std::option::Option<&crate::SolanaAccountInfoConfig>,
|
||||
) -> ksp_core_lib::Result<crate::SolanaRpcResponse<std::vec::Vec<crate::SolanaKeyedAccount>>> {
|
||||
let method_result = token_descriptor(method_name);
|
||||
let method = match method_result {
|
||||
std::result::Result::Ok(method) => method,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut params = std::vec![serde_json::Value::String(address.to_string()), selector.to_json_value()];
|
||||
if let std::option::Option::Some(config) = config
|
||||
&& !config.is_empty()
|
||||
{
|
||||
params.push(config.to_json_value());
|
||||
}
|
||||
let result = self.execute_standard_rpc(role, method, params).await;
|
||||
let value = match result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return decode_keyed_accounts_response(method_name, value);
|
||||
}
|
||||
}
|
||||
|
||||
fn token_descriptor(method: &str) -> ksp_core_lib::Result<&'static crate::HttpRpcMethodDescriptor> {
|
||||
let descriptor = crate::find_http_rpc_method(method);
|
||||
return match descriptor {
|
||||
std::option::Option::Some(descriptor)
|
||||
if descriptor.category() == crate::HttpRpcCategory::Tokens && descriptor.coverage_release() == crate::HttpRpcCoverageRelease::V0_2_2 =>
|
||||
{
|
||||
std::result::Result::Ok(descriptor)
|
||||
},
|
||||
_ => std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_RESPONSE, "typed Tokens descriptor is missing from the audited 0.2.2 registry")
|
||||
.with_context("rpc_method", method),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
fn push_commitment_config(params: &mut std::vec::Vec<serde_json::Value>, config: std::option::Option<&crate::SolanaCommitmentConfig>) {
|
||||
if let std::option::Option::Some(config) = config
|
||||
&& config.commitment().is_some()
|
||||
{
|
||||
params.push(config.to_json_value());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
fn decode_token_amount_response(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<crate::SolanaRpcResponse<crate::SolanaTokenAmount>> {
|
||||
let decoded = crate::decode_wire_json::<WireRpcResponse<serde_json::Value>>(method, value);
|
||||
let wire = match decoded {
|
||||
std::result::Result::Ok(wire) => wire,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let context = crate::SolanaRpcContext::decode_wire(method, wire.context);
|
||||
let context = match context {
|
||||
std::result::Result::Ok(context) => context,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let amount = crate::SolanaTokenAmount::decode_wire(method, wire.value);
|
||||
let amount = match amount {
|
||||
std::result::Result::Ok(amount) => amount,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(crate::SolanaRpcResponse::new(context, amount));
|
||||
}
|
||||
|
||||
fn decode_keyed_accounts_response(
|
||||
method: &str,
|
||||
value: serde_json::Value,
|
||||
) -> ksp_core_lib::Result<crate::SolanaRpcResponse<std::vec::Vec<crate::SolanaKeyedAccount>>> {
|
||||
let decoded = crate::decode_wire_json::<WireRpcResponse<std::vec::Vec<serde_json::Value>>>(method, value);
|
||||
let wire = match decoded {
|
||||
std::result::Result::Ok(wire) => wire,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let context = crate::SolanaRpcContext::decode_wire(method, wire.context);
|
||||
let context = match context {
|
||||
std::result::Result::Ok(context) => context,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut accounts = std::vec::Vec::with_capacity(wire.value.len());
|
||||
for value in wire.value {
|
||||
let account = crate::SolanaKeyedAccount::decode_wire(method, value);
|
||||
match account {
|
||||
std::result::Result::Ok(account) => accounts.push(account),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
return std::result::Result::Ok(crate::SolanaRpcResponse::new(context, accounts));
|
||||
}
|
||||
|
||||
fn decode_token_account_balances_response(
|
||||
method: &str,
|
||||
value: serde_json::Value,
|
||||
) -> ksp_core_lib::Result<crate::SolanaRpcResponse<std::vec::Vec<crate::SolanaTokenAccountBalance>>> {
|
||||
let decoded = crate::decode_wire_json::<WireRpcResponse<std::vec::Vec<serde_json::Value>>>(method, value);
|
||||
let wire = match decoded {
|
||||
std::result::Result::Ok(wire) => wire,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let context = crate::SolanaRpcContext::decode_wire(method, wire.context);
|
||||
let context = match context {
|
||||
std::result::Result::Ok(context) => context,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut accounts = std::vec::Vec::with_capacity(wire.value.len());
|
||||
for value in wire.value {
|
||||
let account = crate::SolanaTokenAccountBalance::decode_wire(method, value);
|
||||
match account {
|
||||
std::result::Result::Ok(account) => accounts.push(account),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
return std::result::Result::Ok(crate::SolanaRpcResponse::new(context, accounts));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/rpc_tokens.rs"]
|
||||
mod tests;
|
||||
|
||||
Reference in New Issue
Block a user