pub struct Client { /* private fields */ }Expand description
The Client.
Implementations§
Source§impl Client
impl Client
Sourcepub fn new(sender: RpcChannel) -> Self
pub fn new(sender: RpcChannel) -> Self
Creates a new Client.
Sourcepub fn send_bundle(
&self,
transactions: Vec<String>,
config: Option<RpcSendTransactionConfig>,
) -> impl Future<Output = RpcResult<String>>
pub fn send_bundle( &self, transactions: Vec<String>, config: Option<RpcSendTransactionConfig>, ) -> impl Future<Output = RpcResult<String>>
Sends a bundle of transactions to be processed atomically.
This RPC method accepts a bundle of transactions (Jito-compatible format) and processes them one by one in order against an isolated sandbox VM. The bundle is all-or-nothing: if any transaction in the bundle fails (simulation error, execution error, or verification error), every other transaction’s effects are discarded and the underlying VM is left byte-identical to its pre-bundle state. No Geyser event, no Simnet event, and no WebSocket subscriber notification is dispatched for a bundle that fails.
On full success, the sandbox’s state changes — account mutations, transaction storage
writes, token-account index updates, write-version increments, etc. — are atomically
committed onto the original VM under an exclusive writer guard, and Geyser/Simnet events
plus WebSocket subscriber notifications (account, program, signature, logs) are fired
onto the live event channels exactly as if each transaction had been submitted through
the regular sendTransaction RPC.
§Parameters
transactions: An array of serialized transaction data (base64 or base58 encoded).config: Optional configuration for encoding format.
§Returns
BoxFuture<Result<String>>: A future resolving to the bundle ID (SHA-256 hash of comma-separated signatures), or an error if any transaction in the bundle fails. Returning a future (rather than blocking) lets the JSON-RPC runtime drive the async sandbox execution without spawning a nested tokio runtime on an HTTP worker thread.
§Example Request (JSON-RPC)
{
"jsonrpc": "2.0",
"id": 1,
"method": "sendBundle",
"params": [
["base64EncodedTx1", "base64EncodedTx2"],
{ "encoding": "base64" }
]
}§Notes
- Bundles are limited to a maximum of 5 transactions, matching Jito’s limit.
- Transactions are processed sequentially in the order provided against a single sandbox.
- Atomicity is guaranteed: on any failure the original VM is unaffected.
- The bundle ID is calculated as SHA-256 hash of comma-separated transaction signatures.
Sourcepub fn get_bundle_statuses(
&self,
bundle_ids: Vec<String>,
) -> impl Future<Output = RpcResult<RpcResponse<Vec<Option<JitoBundleStatus>>>>>
pub fn get_bundle_statuses( &self, bundle_ids: Vec<String>, ) -> impl Future<Output = RpcResult<RpcResponse<Vec<Option<JitoBundleStatus>>>>>
Retrieves Jito-shaped status summaries for one or more previously submitted bundles.
Mirrors Jito’s wire protocol: the first positional parameter is an array of bundle IDs
(max [MAX_BUNDLES_PER_QUERY]). For each requested id we resolve per-signature status via
the same path as getSignatureStatuses and return a single aggregate status object per
bundle, or null at that index when the bundle is unknown locally.
§Parameters
bundle_ids: Array of bundle identifiers returned bysendBundle. May contain up to [MAX_BUNDLES_PER_QUERY] entries; the empty array is rejected withinvalid_params.
§Returns
On success, the JSON-RPC result is the standard Solana contextualized response shape:
context.slot: Context slot from the underlying status query (same idea asgetSignatureStatuses).value: An array with exactly one element per input bundle id, at the same index:nullwhen the bundle id is not known locally (no stored signatures — the id may be valid elsewhere, we simply have nothing to report).- A
surfpool_types::JitoBundleStatusobject otherwise, with:bundle_id: The requested bundle id (snake_case wire field).transactions: Base-58 signatures in bundle submission order (from localjito_bundlesstorage).slot: Slot from the first per-signature status entry (bundle txs share a landing slot), or0if none yet.confirmation_status: From that same first entry (defaults toprocessedwhen absent).err:Okif no transaction error was observed on any status; otherwise the firstErrencountered (JSON-serialized like other SolanaResultvalues, e.g.{"Ok": null}or{"Err": ...}).
§Example Request (JSON-RPC)
{
"jsonrpc": "2.0",
"id": 1,
"method": "getBundleStatuses",
"params": [
["bundleIdHere", "anotherBundleId"]
]
}§Example Response (JSON-RPC)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"context": { "slot": 242806119 },
"value": [
{
"bundle_id": "892b79ed49138bfb3aa5441f0df6e06ef34f9ee8f3976c15b323605bae0cf51d",
"transactions": [
"3bC2M9fiACSjkTXZDgeNAuQ4ScTsdKGwR42ytFdhUvikqTmBheUxfsR1fDVsM5ADCMMspuwGkdm1uKbU246x5aE3",
"8t9hKYEYNbLvNqiSzP96S13XF1C2f1ro271Kdf7bkZ6EpjPLuDff1ywRy4gfaGSTubsM2FeYGDoT64ZwPm1cQUt"
],
"slot": 242804011,
"confirmation_status": "finalized",
"err": { "Ok": null }
},
null
]
}
}§Notes
- Bundles are stored locally as a mapping from
bundle_idto a list of base-58 signatures. - Unknown bundle ids appear as
nullinside thevaluearray; the outerresultis nevernull(Jito-style: per-index reporting). - Per-signature status resolution uses the same logic as
getSignatureStatuses(local store and optional remote datasource).
Sourcepub fn simulate_bundle(
&self,
rpc_bundle_request: RpcBundleRequest,
config: Option<RpcSimulateBundleConfig>,
) -> impl Future<Output = RpcResult<RpcResponse<RpcSimulateBundleResult>>>
pub fn simulate_bundle( &self, rpc_bundle_request: RpcBundleRequest, config: Option<RpcSimulateBundleConfig>, ) -> impl Future<Output = RpcResult<RpcResponse<RpcSimulateBundleResult>>>
Simulates a bundle of transactions sequentially against an isolated sandbox VM, without committing any of the resulting state changes onto the live VM.
This is the read-only counterpart to Self::send_bundle. Where send_bundle is
all-or-nothing (every tx must succeed; on full success the sandbox is committed),
simulate_bundle is all-tx-attempted-or-fail-fast: each transaction is processed
in order against the sandbox, with subsequent transactions seeing the previous tx’s
state mutations, but the moment one transaction errors the loop exits and remaining
transactions are not simulated. The sandbox is always discarded — successful or
not — so the live VM is byte-identical to its pre-call state regardless of outcome.
Per-tx the response carries:
err:Noneon success, the typedTransactionErroron failure.logs: program logs the SVM emitted while executing the tx.units_consumed: compute units burned.pre_execution_accounts/post_execution_accounts: pre/post snapshot of the accounts whose pubkeys the caller listed inRpcSimulateBundleConfig.{pre,post}_execution_accounts_configs[i]. Returned inUiAccountEncoding::Base64(the only encodingsimulateBundlesupports — the request is rejected withinvalid_paramsfor any other encoding hint).replacement_blockhash: the bank’s latest blockhash, but only whenreplace_recent_blockhashis true.return_data: the program return data, when present.
The remaining fields on RpcSimulateBundleTransactionResult (pre_token_balances,
post_token_balances, loaded_addresses, loaded_accounts_data_size, fee,
pre_balances, post_balances) are returned as None. This matches Surfpool’s
existing single-tx simulateTransaction behavior; populating them requires layout
decoding of SPL Token accounts that has not yet been done on either path. Tracked
for a follow-up PR.
§Parameters
rpc_bundle_request: Wrapper struct carrying the array of base64-encoded transactions. Mirrors Jito’s wire format.config: Optional. When omitted the bundle simulates with no pre/post account snapshots, sigverify on, and the originally-encoded blockhashes preserved. Whenreplace_recent_blockhashis true,skip_sig_verifyMUST also be true (a resigned blockhash invalidates any pre-existing signature) — the request is otherwise rejected withinvalid_params.
§Returns
RpcResponse<RpcSimulateBundleResult> — the standard Solana contextualized response
shape. value.summary is Succeeded when every transaction in the bundle simulated
cleanly, otherwise Failed { error, tx_signature } where error carries the
typed RpcBundleExecutionError::TransactionFailure(signature, message) for the
first failing tx. value.transaction_results[i] corresponds to
rpc_bundle_request.encoded_transactions[i]; on early-exit failure the trailing
indices contain the empty/skipped tx result (err: None, logs: None, etc.) — they
were never simulated.
§Example Request (JSON-RPC)
{
"jsonrpc": "2.0",
"id": 1,
"method": "simulateBundle",
"params": [
{ "encodedTransactions": ["base64Tx1", "base64Tx2"] },
{
"preExecutionAccountsConfigs": [null, { "addresses": ["..."] }],
"postExecutionAccountsConfigs": [null, { "addresses": ["..."] }],
"skipSigVerify": true,
"replaceRecentBlockhash": true
}
]
}§Notes
- Bundles are limited to a maximum of [
MAX_BUNDLE_SIZE] (5) transactions, matchingsendBundleand Jito’s documented limit. pre_execution_accounts_configsandpost_execution_accounts_configs, when provided, MUST have the same length as the bundle.simulation_bankis accepted for API parity; Surfpool always simulates against the working SVM regardless of value.- The sandbox’s storage overlays, buffered Geyser/Simnet events, and cloned LiteSVM state are dropped at end-of-call. No notification fires on the live event channels; no signature/logs subscriber is woken; no chain state is touched.
Trait Implementations§
Source§impl From<RpcChannel> for Client
impl From<RpcChannel> for Client
Source§fn from(channel: RpcChannel) -> Self
fn from(channel: RpcChannel) -> Self
Auto Trait Implementations§
impl !RefUnwindSafe for Client
impl !UnwindSafe for Client
impl Freeze for Client
impl Send for Client
impl Sync for Client
impl Unpin for Client
impl UnsafeUnpin for Client
Blanket Implementations§
Source§impl<T> AggregateExpressionMethods for T
impl<T> AggregateExpressionMethods for T
Source§fn aggregate_distinct(self) -> Self::Outputwhere
Self: DistinctDsl,
fn aggregate_distinct(self) -> Self::Outputwhere
Self: DistinctDsl,
DISTINCT modifier for aggregate functions Read moreSource§fn aggregate_all(self) -> Self::Outputwhere
Self: AllDsl,
fn aggregate_all(self) -> Self::Outputwhere
Self: AllDsl,
ALL modifier for aggregate functions Read moreSource§fn aggregate_filter<P>(self, f: P) -> Self::Output
fn aggregate_filter<P>(self, f: P) -> Self::Output
Source§fn aggregate_order<O>(self, o: O) -> Self::Outputwhere
Self: OrderAggregateDsl<O>,
fn aggregate_order<O>(self, o: O) -> Self::Outputwhere
Self: OrderAggregateDsl<O>,
Source§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoSql for T
impl<T> IntoSql for T
Source§fn into_sql<T>(self) -> Self::Expression
fn into_sql<T>(self) -> Self::Expression
self to an expression for Diesel’s query builder. Read moreSource§fn as_sql<'a, T>(&'a self) -> <&'a Self as AsExpression<T>>::Expression
fn as_sql<'a, T>(&'a self) -> <&'a Self as AsExpression<T>>::Expression
&self to an expression for Diesel’s query builder. Read moreSource§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.Source§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.