soroban_client/
soroban_rpc.rs

1#![allow(non_snake_case)]
2use std::ops::Deref;
3
4use stellar_baselib::{
5    soroban_data_builder::{SorobanDataBuilder, SorobanDataBuilderBehavior},
6    xdr::{
7        ContractEvent, DiagnosticEvent, LedgerCloseMeta, LedgerEntry, LedgerEntryData,
8        LedgerEntryExt, LedgerHeaderHistoryEntry, LedgerKey, Limits, ReadXdr, ScVal,
9        SorobanAuthorizationEntry, SorobanTransactionData, TransactionEnvelope, TransactionEvent,
10        TransactionMeta, TransactionResult,
11    },
12};
13
14use serde::{Deserialize, Serialize};
15
16/// Response to [get_health](crate::Server::get_health) RPC method
17#[derive(Debug, Deserialize, PartialEq, Eq)]
18#[serde(rename_all = "camelCase")]
19pub struct GetHealthResponse {
20    /// Health status, typically 'healthy'
21    pub status: String,
22    /// Most recent known ledger sequence
23    pub latest_ledger: u32,
24    /// Oldest ledger sequence kept in history
25    pub oldest_ledger: u32,
26    /// Maximum retention window configured. A full window state can be determined via:
27    /// ledgerRetentionWindow = latestLedger - oldestLedger + 1
28    pub ledger_retention_window: u32,
29}
30
31/// A pair of LedgerKey and LedgerEntryData
32#[derive(Clone, Debug, Deserialize)]
33#[serde(rename_all = "camelCase")]
34pub struct LedgerEntryResult {
35    /// The ledger sequence number of the last time this entry was updated.
36    pub last_modified_ledger_seq: Option<u32>,
37    /// Sequence number of the ledger.
38    pub live_until_ledger_seq: Option<u32>,
39    key: String,
40    xdr: String,
41    ext_xdr: Option<String>,
42}
43
44impl LedgerEntryResult {
45    /// The key of the ledger entry
46    pub fn to_key(&self) -> LedgerKey {
47        LedgerKey::from_xdr_base64(&self.key, Limits::none()).expect("Invalid LedgerKey from RPC")
48    }
49    /// The current value of the given ledger entry
50    pub fn to_data(&self) -> LedgerEntryData {
51        LedgerEntryData::from_xdr_base64(&self.xdr, Limits::none())
52            .expect("Invalid LedgerEntryData from RPC")
53    }
54    /// The extension of the ledger entry
55    pub fn to_ext(&self) -> Option<LedgerEntryExt> {
56        self.ext_xdr.as_ref().map(|ext| {
57            LedgerEntryExt::from_xdr_base64(ext, Limits::none())
58                .expect("Invalid LedgerEntryData from RPC")
59        })
60    }
61}
62
63/// Response to [get_ledger_entries](crate::Server::get_ledger_entries)
64#[derive(Deserialize, Debug, Clone)]
65pub struct GetLedgerEntriesResponse {
66    /// Array of objects containing all found ledger entries
67    pub entries: Option<Vec<LedgerEntryResult>>,
68    /// The sequence number of the latest ledger known to Stellar RPC at the time it handled the request.
69    pub latestLedger: u32,
70}
71
72/// Response to [get_network](crate::Server::get_network)
73#[derive(Deserialize, Debug, PartialEq, Eq)]
74#[serde(rename_all = "camelCase")]
75pub struct GetNetworkResponse {
76    /// Network passphrase configured for this Stellar RPC node.
77    pub passphrase: Option<String>,
78    /// Stellar Core protocol version associated with the latest ledger.
79    pub protocol_version: Option<i32>,
80    /// (optional) The URL of this network's "friendbot" faucet
81    pub friendbot_url: Option<String>,
82}
83
84/// Response to [get_latest_ledger](crate::Server::get_latest_ledger)
85#[derive(Deserialize, Debug, PartialEq, Eq)]
86#[serde(rename_all = "camelCase")]
87pub struct GetLatestLedgerResponse {
88    /// Hash identifier of the latest ledger (as a hex-encoded string) known to Stellar RPC at the time it handled the request.
89    pub id: String,
90    /// Stellar Core protocol version associated with the latest ledger.
91    pub protocol_version: u32,
92    /// The sequence number of the latest ledger known to Stellar RPC at the time it handled the request.
93    pub sequence: u32,
94}
95
96/// Status of [GetTransactionResponse] or [GetTransactionsResponse] transactions
97#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
98#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
99pub enum TransactionStatus {
100    /// Transaction succeeded
101    Success,
102    /// NotFound, may not exist yet
103    NotFound,
104    /// Transaction failed
105    Failed,
106}
107
108/// Response to [get_transaction](crate::Server::get_transaction)
109///
110/// See [TransactionDetails] for additionnal fields
111#[derive(Debug, Deserialize)]
112#[serde(rename_all = "camelCase")]
113pub struct GetTransactionResponse {
114    /// The sequence number of the latest ledger known to Stellar RPC at the time it handled the request.
115    pub latest_ledger: u32,
116    /// The unix timestamp of the close time of the latest ledger known to Stellar RPC at the time it handled the request.
117    pub latest_ledger_close_time: String,
118    /// The sequence number of the oldest ledger ingested by Stellar RPC at the time it handled the request.
119    pub oldest_ledger: u32,
120    /// The unix timestamp of the close time of the oldest ledger ingested by Stellar RPC at the time it handled the request.
121    pub oldest_ledger_close_time: String,
122    /// (optional) The unix timestamp of when the transaction was included in the ledger. This field is only present if status is [TransactionStatus::Success] or [TransactionStatus::Failed].
123    pub created_at: Option<String>,
124    /// Transaction details
125    #[serde(flatten)]
126    transaction: TransactionDetails,
127}
128// Flatten the transaction in the struct
129impl Deref for GetTransactionResponse {
130    type Target = TransactionDetails;
131
132    fn deref(&self) -> &Self::Target {
133        &self.transaction
134    }
135}
136
137/// Event types (system, contract, or diagnostic) used to filter events
138#[derive(PartialEq, Eq, Deserialize, Debug)]
139#[serde(rename_all = "lowercase")]
140pub enum EventType {
141    /// Only contract type events
142    Contract,
143    /// Only system type events
144    System,
145    /// Only diagnostic events
146    /// Since protocol 23: Diagnostic events are no longer returned by [get_events](crate::Server::get_events)
147    Diagnostic,
148    /// Any event type, contract, system and diagnostic
149    All,
150}
151
152/// Response to [get_events](crate::Server::get_events)
153#[derive(Debug, Deserialize)]
154#[serde(rename_all = "camelCase")]
155pub struct GetEventsResponse {
156    /// Events found for the filter
157    pub events: Vec<EventResponse>,
158    /// The last populated event ID if total events reach the limit or end of the search window.
159    pub cursor: Option<String>,
160    /// The sequence number of the latest ledger known to Stellar RPC at the time it handled the request.
161    pub latest_ledger: u64,
162    /// The sequence number of the oldest ledger stored in Stellar-RPC
163    pub oldest_ledger: Option<u64>,
164    /// The unix timestamp of when the latest ledger was closed
165    pub latest_ledger_close_time: Option<String>,
166    /// The unix timestamp of when the oldest ledger was closed
167    pub oldest_ledger_close_time: Option<String>,
168}
169
170/// Event data
171#[derive(Debug, Deserialize)]
172#[serde(rename_all = "camelCase")]
173pub struct EventResponse {
174    /// The type of event emission.
175    #[serde(rename = "type")]
176    pub event_type: EventType,
177    /// Sequence number of the ledger in which this event was emitted.
178    pub ledger: u64,
179    /// ISO-8601 timestamp of the ledger closing time
180    pub ledger_closed_at: String,
181    /// `StrKey` representation of the contract address that emitted this event.
182    pub contract_id: String,
183    /// Unique identifier for this event.
184    ///
185    /// The event's unique id field is based on a toid from Horizon as used in
186    /// Horizon's [/effects endpoint](https://github.com/stellar/go/blob/master/services/horizon/internal/db2/history/effect.go#L58).
187    ///
188    /// Specifically, it is a string containing:
189    /// - bigint(32 bit ledger sequence + 20 bit txn number + 12 bit operation) + &lt;hyphen&gt; + number for the event within the operation.
190    ///   For example: 1234-1
191    pub id: String,
192    /// The index of the operation in the transaction
193    pub operation_index: Option<u32>,
194    /// The index of the transaction in the ledger
195    pub transaction_index: Option<u32>,
196    /// The transaction which triggered this event.
197    pub tx_hash: String,
198    /// Duplicate of `id` field, but in the standard place for pagination tokens.
199    /// Since protocol 23: This field is no longer present
200    pub paging_token: Option<String>,
201    /// If true the event was emitted during a successful contract call.
202    ///
203    /// Deprecated: will be remove in protocol 24
204    pub in_successful_contract_call: bool,
205    topic: Vec<String>,
206    value: String,
207}
208
209impl EventResponse {
210    /// List containing the topic this event was emitted with.
211    pub fn topic(&self) -> Vec<ScVal> {
212        self.topic
213            .iter()
214            .map(|t| ScVal::from_xdr_base64(t, Limits::none()).expect("Invalid XDR from RPC"))
215            .collect()
216    }
217
218    /// The emitted body value of the event (serialized in a base64 string).
219    pub fn value(&self) -> ScVal {
220        ScVal::from_xdr_base64(&self.value, Limits::none()).expect("Invalid XDR from RPC")
221    }
222}
223
224/// Status of the transaction
225#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
226#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
227pub enum SendTransactionStatus {
228    /// Transaction submitted
229    Pending,
230    /// Transaction already submitted
231    Duplicate,
232    /// Transaction in error
233    Error,
234    /// Transaction cannot be submitted, try again
235    TryAgainLater,
236}
237
238/// Response to [send_transaction](crate::Server::send_transaction)
239#[derive(Debug, Serialize, Deserialize)]
240#[serde(rename_all = "camelCase")]
241pub struct SendTransactionResponse {
242    /// The current status of the transaction by hash.
243    pub status: SendTransactionStatus,
244    /// Transaction hash (as a hex-encoded string)
245    pub hash: String,
246    /// The sequence number of the latest ledger known to Stellar RPC at the time it handled the request.
247    pub latest_ledger: u32,
248    /// The unix timestamp of the close time of the latest ledger known to Stellar RPC at the
249    /// time it handled the request.
250    pub latest_ledger_close_time: String,
251    error_result_xdr: Option<String>, // Base64 encoded TransactionResult
252    diagnostic_events_xdr: Option<Vec<String>>, // Base64 encoded DiagnosticEvent
253}
254
255impl SendTransactionResponse {
256    /// (optional) If the transaction status is [SendTransactionStatus::Error], this will be a
257    /// TransactionResult struct containing details on why stellar-core rejected the transaction.
258    pub fn to_error_result(&self) -> Option<TransactionResult> {
259        self.error_result_xdr.as_ref().map(|e| {
260            TransactionResult::from_xdr_base64(e, Limits::none()).expect("Invalid XDR from RPC")
261        })
262    }
263
264    /// (optional) If the transaction status is [SendTransactionStatus::Error], this field may
265    /// be present with [`Vec<DiagnosticEvent>`]. Each [DiagnosticEvent] is containing details on
266    /// why stellar-core rejected the transaction.
267    pub fn to_diagnostic_events(&self) -> Option<Vec<DiagnosticEvent>> {
268        if let Some(events) = self.diagnostic_events_xdr.as_ref() {
269            events
270                .iter()
271                .map(|e| DiagnosticEvent::from_xdr_base64(e, Limits::none()).ok())
272                .collect()
273        } else {
274            None
275        }
276    }
277}
278
279#[derive(Clone, Debug, Serialize, Deserialize)]
280#[serde(rename_all = "camelCase")]
281struct RestorePreamble {
282    min_resource_fee: String,
283    transaction_data: String,
284}
285
286#[derive(Clone, Debug, Serialize, Deserialize)]
287struct RawSimulateHostFunctionResult {
288    auth: Vec<String>,
289    xdr: String,
290}
291
292#[derive(Clone, Debug, Serialize, Deserialize)]
293struct RawStateChanges {
294    #[serde(rename = "type")]
295    kind: StateChangeKind,
296    key: String,
297    before: Option<String>,
298    after: Option<String>,
299}
300
301/// Indicates if the entry was created (1), updated (2), or deleted (3)
302#[derive(Clone, Debug, Serialize, Deserialize, Copy)]
303#[serde(rename_all = "lowercase")]
304pub enum StateChangeKind {
305    /// Entry has been created
306    Created = 1,
307    /// Entry has been updated
308    Updated = 2,
309    /// Entry has been deleted
310    Deleted = 3,
311}
312
313/// On successful simulation of InvokeHostFunction operations, this field will be an array of
314/// [LedgerEntry]s before and after simulation occurred. Note that at least one of before or after
315/// will be present: before and no after indicates a deletion event, the inverse is a creation
316/// event, and both present indicates an update event. Or just check the type.
317pub struct StateChange {
318    /// Type of change
319    pub kind: StateChangeKind,
320    /// The [LedgerKey] for this delta
321    pub key: LedgerKey,
322    /// If present, [LedgerEntry] state prior to simulation
323    pub before: Option<LedgerEntry>,
324    /// If present, [LedgerEntry] state after simulation
325    pub after: Option<LedgerEntry>,
326}
327
328/// Response to [simulate_transaction](crate::Server::simulate_transaction)
329#[derive(Clone, Debug, Serialize, Deserialize)]
330#[serde(rename_all = "camelCase")]
331pub struct SimulateTransactionResponse {
332    /// The sequence number of the latest ledger known to Stellar RPC at the time it handled the request.
333    pub latest_ledger: u32,
334    /// (optional) Stringified number - Recommended minimum resource fee to add when submitting
335    /// the transaction. This fee is to be added on top of the Stellar network fee.
336    /// Not present in case of error.
337    pub min_resource_fee: Option<String>,
338    /// (optional) - This field will include details about why the invoke host function call
339    /// failed. Only present if the transaction simulation failed.
340    pub error: Option<String>,
341    results: Option<Vec<RawSimulateHostFunctionResult>>,
342    transaction_data: Option<String>,
343    restore_preamble: Option<RestorePreamble>,
344    events: Option<Vec<String>>,
345    state_changes: Option<Vec<RawStateChanges>>,
346}
347
348impl SimulateTransactionResponse {
349    /// (optional) - This array will only have one element: the result for the Host Function
350    /// invocation.
351    /// Only present on successful simulation (i.e. no error) of InvokeHostFunction op
352    pub fn to_result(&self) -> Option<(ScVal, Vec<SorobanAuthorizationEntry>)> {
353        if let Some(r) = self.results.as_ref() {
354            let auth: Vec<SorobanAuthorizationEntry> = r[0]
355                .auth
356                .iter()
357                .map(|e| SorobanAuthorizationEntry::from_xdr_base64(e, Limits::none()).unwrap())
358                .collect();
359            let ret_val = ScVal::from_xdr_base64(&r[0].xdr, Limits::none())
360                .expect("Xdr from RPC should be valid");
361
362            Some((ret_val, auth))
363        } else {
364            None
365        }
366    }
367
368    /// (optional) - The recommended Soroban Transaction Data to use when
369    /// submitting the simulated transaction. This data contains the refundable fee and resource
370    /// usage information such as the ledger footprint and IO access data.
371    /// Not present in case of error.
372    pub fn to_transaction_data(&self) -> Option<SorobanTransactionData> {
373        self.transaction_data.as_ref().map(|data| {
374            SorobanDataBuilder::new(Some(stellar_baselib::soroban_data_builder::Either::Left(
375                data.to_owned(),
376            )))
377            .build()
378        })
379    }
380
381    /// (optional) - It can only be present on successful simulation (i.e. no error) of
382    /// InvokeHostFunction operations. If present, it indicates that the simulation detected
383    /// archived ledger entries which need to be restored before the submission of the
384    /// InvokeHostFunction operation.
385    /// The minResourceFee and transactionData fields should be used to submit a transaction
386    /// containing a RestoreFootprint operation.
387    ///```rust
388    /// # use soroban_client::*;
389    /// # use soroban_client::account::Account;
390    /// # use soroban_client::address::Address;
391    /// # use soroban_client::address::AddressTrait;
392    /// # use soroban_client::contract::Contracts;
393    /// # use soroban_client::contract::ContractBehavior;
394    /// # use soroban_client::network::Networks;
395    /// # use soroban_client::network::NetworkPassphrase;
396    /// # use soroban_client::xdr::Int128Parts;
397    /// # use soroban_client::xdr::ScVal;
398    /// # use soroban_client::xdr::int128_helpers::i128_from_pieces;
399    /// # use soroban_client::keypair::Keypair;
400    /// # use soroban_client::keypair::KeypairBehavior;
401    /// # use soroban_client::transaction::AccountBehavior;
402    /// # use soroban_client::transaction::TransactionBuilderBehavior;
403    /// # use soroban_client::transaction_builder::TransactionBuilder;
404    /// # #[tokio::main]
405    /// # async fn main() {
406    /// #   let rpc = Server::new("https://soroban-testnet.stellar.org", Options::default()).unwrap();
407    /// #   let native_id = "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC";
408    /// #   let native_sac = Contracts::new(native_id).unwrap();
409    /// #   let kp = Keypair::random().unwrap();
410    /// #   let account = rpc.request_airdrop(&kp.public_key()).await.unwrap();
411    /// #   let mut source_account = Account::new(&kp.public_key(), &account.sequence_number()).unwrap();
412    /// #   let account_address = Address::new(&kp.public_key()).unwrap();
413    /// #   let tx = TransactionBuilder::new(&mut source_account, Networks::testnet(), None)
414    /// #       .fee(1000u32)
415    /// #       .add_operation(
416    /// #           native_sac.call(
417    /// #               "balance",
418    /// #               Some(vec![account_address.to_sc_val().unwrap()])))
419    /// #       .build();
420    ///
421    ///     let simulation = rpc.simulate_transaction(&tx, None).await.unwrap();
422    ///     if let Some((min_resource_fee, transaction_data)) =
423    ///         simulation.to_restore_transaction_data() {
424    ///         // Build a RestoreFootprint transaction
425    ///     }
426    /// # }
427    /// ```
428    pub fn to_restore_transaction_data(&self) -> Option<(i64, SorobanTransactionData)> {
429        if let Some(restore) = self.restore_preamble.clone() {
430            Some((
431                restore
432                    .min_resource_fee
433                    .parse()
434                    .expect("Invalid i64 for min_resource_fee"),
435                SorobanDataBuilder::new(Some(stellar_baselib::soroban_data_builder::Either::Left(
436                    restore.transaction_data,
437                )))
438                .build(),
439            ))
440        } else {
441            None
442        }
443    }
444
445    /// (optional) - Array of the events emitted during the
446    /// contract invocation. The events are ordered by their emission time.
447    /// Only present when simulating of InvokeHostFunction operations,
448    /// note that it can be present on error, providing extra context about what failed.
449    pub fn to_events(&self) -> Option<Vec<DiagnosticEvent>> {
450        if let Some(events) = self.events.as_ref() {
451            events
452                .iter()
453                .map(|e| DiagnosticEvent::from_xdr_base64(e, Limits::none()).ok())
454                .collect()
455        } else {
456            None
457        }
458    }
459
460    /// (optional) - On successful simulation of InvokeHostFunction operations, this field will be
461    /// an array of LedgerEntrys before and after simulation occurred. Note that at least one of
462    /// before or after will be present: before and no after indicates a deletion event, the
463    /// inverse is a creation event, and both present indicates an update event. Or just check the
464    /// type.
465    pub fn to_state_changes(&self) -> Vec<StateChange> {
466        if let Some(changes) = self.state_changes.as_ref() {
467            changes
468                .iter()
469                .map(|c| StateChange {
470                    kind: c.kind,
471                    key: LedgerKey::from_xdr_base64(&c.key, Limits::none())
472                        .expect("Invalid LedgerKey"),
473                    before: c.before.as_ref().map(|e| {
474                        LedgerEntry::from_xdr_base64(e, Limits::none())
475                            .expect("Invalid LedgerEntry")
476                    }),
477                    after: c.after.as_ref().map(|e| {
478                        LedgerEntry::from_xdr_base64(e, Limits::none())
479                            .expect("Invalid LedgerEntry")
480                    }),
481                })
482                .collect()
483        } else {
484            Default::default()
485        }
486    }
487}
488
489/// Response to [get_fee_stats](crate::Server::get_fee_stats)
490#[derive(Debug, Deserialize)]
491#[serde(rename_all = "camelCase")]
492pub struct GetFeeStatsResponse {
493    /// Inclusion fee distribution statistics for Soroban transactions
494    pub soroban_inclusion_fee: FeeDistribution,
495    /// Fee distribution statistics for Stellar (i.e. non-Soroban) transactions.
496    /// Statistics are normalized per operation.
497    pub inclusion_fee: FeeDistribution,
498    /// The sequence number of the latest ledger known to Stellar RPC at the time it handled
499    /// the request.
500    pub latest_ledger: u32,
501}
502
503/// Fee distribution
504#[derive(Debug, Deserialize)]
505#[serde(rename_all = "camelCase")]
506pub struct FeeDistribution {
507    /// Maximum fee
508    pub max: String,
509    /// Minimum fee
510    pub min: String,
511    /// Fee value which occurs the most often
512    pub mode: String,
513    /// 10th nearest-rank fee percentile
514    pub p10: String,
515    /// 20th nearest-rank fee percentile
516    pub p20: String,
517    /// 30th nearest-rank fee percentile
518    pub p30: String,
519    /// 40th nearest-rank fee percentile
520    pub p40: String,
521    /// 50th nearest-rank fee percentile
522    pub p50: String,
523    /// 60th nearest-rank fee percentile
524    pub p60: String,
525    /// 70th nearest-rank fee percentile
526    pub p70: String,
527    /// 80th nearest-rank fee percentile
528    pub p80: String,
529    /// 90th nearest-rank fee percentile
530    pub p90: String,
531    /// 95th nearest-rank fee percentile
532    pub p95: String,
533    /// 99th nearest-rank fee percentile
534    pub p99: String,
535    /// How many transactions are part of the distribution
536    pub transaction_count: String,
537    /// How many consecutive ledgers form the distribution
538    pub ledger_count: u32,
539}
540
541/// Response to [get_version_info](crate::Server::get_version_info)
542#[derive(Debug, Deserialize)]
543#[serde(rename_all = "camelCase")]
544pub struct GetVersionInfoResponse {
545    /// The version of the RPC server.
546    pub version: String,
547    /// The commit hash of the RPC server.
548    pub commit_hash: String,
549    /// The build timestamp of the RPC server.
550    pub build_timestamp: String,
551    /// The version of the Captive Core.
552    pub captive_core_version: String,
553    /// The protocol version.
554    pub protocol_version: u32,
555}
556
557/// Response to [get_transactions](crate::Server::get_transactions)
558#[derive(Debug, Deserialize)]
559#[serde(rename_all = "camelCase")]
560pub struct GetTransactionsResponse {
561    /// The sequence number of the latest ledger known to Stellar RPC at the time it handled the request.
562    pub latest_ledger: u32,
563    /// The unix timestamp of the close time of the latest ledger known to Stellar RPC at the time it handled the request.
564    pub latest_ledger_close_timestamp: i64,
565    /// The sequence number of the oldest ledger ingested by Stellar RPC at the time it handled the request.
566    pub oldest_ledger: u32,
567    /// The unix timestamp of the close time of the oldest ledger ingested by Stellar RPC at the time it handled the request.
568    pub oldest_ledger_close_timestamp: i64,
569    /// Cursor reference
570    pub cursor: String,
571    /// The transactions found
572    pub transactions: Vec<TransactionInfo>,
573}
574
575/// Representation of a transaction returned by stellar RPC
576///
577/// Specific type for [GetTransactionsResponse]
578#[derive(Debug, Deserialize)]
579#[serde(rename_all = "camelCase")]
580pub struct TransactionInfo {
581    /// The unix timestamp of when the transaction was included in the ledger.
582    pub created_at: Option<i64>,
583    #[serde(flatten)]
584    transaction: TransactionDetails,
585}
586// Flatten the transaction in the struct
587impl Deref for TransactionInfo {
588    type Target = TransactionDetails;
589
590    fn deref(&self) -> &Self::Target {
591        &self.transaction
592    }
593}
594
595/// Representation of a transaction returned by stellar RPC
596#[derive(Debug, Deserialize)]
597#[serde(rename_all = "camelCase")]
598pub struct TransactionDetails {
599    /// The current status of the transaction by hash
600    pub status: TransactionStatus,
601    /// The sequence number of the latest ledger known to Stellar RPC at the time it handled the request.
602    /// (optional) The sequence number of the ledger which included the transaction. This field is only present if status is [TransactionStatus::Success] or [TransactionStatus::Failed].
603    pub ledger: Option<u32>,
604    /// (optional) The index of the transaction among all transactions included in the ledger. This field is only present if status is [TransactionStatus::Success] or [TransactionStatus::Failed].
605    pub application_order: Option<i32>,
606    /// (optional) Indicates whether the transaction was fee bumped. This field is only present if status is [TransactionStatus::Success] or [TransactionStatus::Failed].
607    pub fee_bump: Option<bool>,
608    envelope_xdr: Option<String>,
609    result_xdr: Option<String>,
610    result_meta_xdr: Option<String>,
611    diagnostic_events_xdr: Option<Vec<String>>,
612    events: Option<Events>,
613}
614
615#[derive(Debug, Deserialize)]
616#[serde(rename_all = "camelCase")]
617struct Events {
618    transaction_events_xdr: Option<Vec<String>>,
619    contract_events_xdr: Option<Vec<Vec<String>>>,
620}
621
622impl TransactionDetails {
623    /// (optional) The [TransactionEnvelope] struct for this transaction.
624    pub fn to_envelope(&self) -> Option<TransactionEnvelope> {
625        if let Some(result) = &self.envelope_xdr {
626            let r = TransactionEnvelope::from_xdr_base64(result, Limits::none());
627            r.ok()
628        } else {
629            None
630        }
631    }
632
633    /// (optional) The [TransactionResult] struct for this transaction. This field is only present if status is [TransactionStatus::Success] or [TransactionStatus::Failed].
634    pub fn to_result(&self) -> Option<TransactionResult> {
635        if let Some(result) = &self.result_xdr {
636            let r = TransactionResult::from_xdr_base64(result, Limits::none());
637            r.ok()
638        } else {
639            None
640        }
641    }
642
643    /// (optional) The [TransactionMeta] struct of this transaction. Also return the optional
644    /// return value of the transaction.
645    pub fn to_result_meta(&self) -> Option<(TransactionMeta, Option<ScVal>)> {
646        if let Some(result) = &self.result_meta_xdr {
647            let r = TransactionMeta::from_xdr_base64(result, Limits::none());
648            if let Ok(e) = r {
649                let mut return_value = None;
650                match &e {
651                    TransactionMeta::V3(v3) => {
652                        if let Some(v) = &v3.soroban_meta {
653                            return_value = Some(v.return_value.clone());
654                        }
655                    }
656                    TransactionMeta::V4(v4) => {
657                        if let Some(v) = &v4.soroban_meta {
658                            return_value = v.return_value.clone();
659                        }
660                    }
661                    _ => {}
662                };
663                Some((e, return_value))
664            } else {
665                None
666            }
667        } else {
668            None
669        }
670    }
671
672    /// (optional) A base64 encoded slice of xdr.DiagnosticEvent. This is only present if the
673    /// ENABLE_SOROBAN_DIAGNOSTIC_EVENTS has been enabled in the stellar-core config.
674    ///
675    /// Deprecated: will be removed in protocol 24
676    pub fn to_diagnostic_events(&self) -> Option<Vec<DiagnosticEvent>> {
677        if let Some(events) = &self.diagnostic_events_xdr {
678            events
679                .iter()
680                .map(|e| DiagnosticEvent::from_xdr_base64(e, Limits::none()).ok())
681                .collect()
682        } else {
683            None
684        }
685    }
686
687    /// Events contains all events related to the transaction: transaction and contract events.
688    pub fn to_events(&self) -> Option<(Vec<TransactionEvent>, Vec<Vec<ContractEvent>>)> {
689        if let Some(events) = &self.events {
690            let tx_events = match &events.transaction_events_xdr {
691                Some(te) => {
692                    let v: Option<Vec<TransactionEvent>> = te
693                        .iter()
694                        .map(|e| TransactionEvent::from_xdr_base64(e, Limits::none()).ok())
695                        .collect();
696                    v.unwrap_or_default()
697                }
698                None => Vec::default(),
699            };
700            let cx_events = match &events.contract_events_xdr {
701                Some(te) => {
702                    let v: Option<Vec<Vec<ContractEvent>>> = te
703                        .iter()
704                        .map(|row| {
705                            row.iter()
706                                .map(|e| ContractEvent::from_xdr_base64(e, Limits::none()).ok())
707                                .collect()
708                        })
709                        .collect();
710                    v.unwrap_or_default()
711                }
712                None => Vec::default(),
713            };
714            Some((tx_events, cx_events))
715        } else {
716            None
717        }
718    }
719}
720
721/// Response to [get_ledgers](crate::Server::get_ledgers)
722#[derive(Debug, Deserialize)]
723#[serde(rename_all = "camelCase")]
724pub struct GetLedgersResponse {
725    /// The sequence number of the latest ledger known to Stellar RPC at the time it handled the request.
726    pub latest_ledger: u32,
727    /// The unix timestamp of the close time of the latest ledger known to Stellar RPC at the time it handled the request.
728    pub latest_ledger_close_time: i64,
729    /// The sequence number of the oldest ledger ingested by Stellar RPC at the time it handled the request.
730    pub oldest_ledger: u32,
731    /// The unix timestamp of the close time of the oldest ledger ingested by Stellar RPC at the time it handled the request.
732    pub oldest_ledger_close_time: i64,
733    /// Cursor reference
734    pub cursor: String,
735    /// Ledgers returned
736    pub ledgers: Vec<LedgerInfo>,
737}
738
739/// Representation of the ledger
740#[derive(Debug, Clone, Deserialize)]
741#[serde(rename_all = "camelCase")]
742pub struct LedgerInfo {
743    /// The hash of the ledger header which was included in the chain
744    pub hash: String,
745    /// The sequence number of the ledger (sometimes called the 'block height').
746    pub sequence: u32,
747    /// The timestamp at which the ledger was closed.
748    pub ledger_close_time: String,
749    header_xdr: Option<String>,
750    metadataXdr: Option<String>,
751}
752
753impl LedgerInfo {
754    /// LedgerHeader for this ledger
755    pub fn to_header(&self) -> Option<LedgerHeaderHistoryEntry> {
756        self.header_xdr.as_ref().map(|header| {
757            LedgerHeaderHistoryEntry::from_xdr_base64(header, Limits::none())
758                .expect("Invalid XDR from RPC")
759        })
760    }
761    /// LedgerCloseMeta for this ledger
762    pub fn to_metadata(&self) -> Option<LedgerCloseMeta> {
763        self.metadataXdr.as_ref().map(|meta| {
764            LedgerCloseMeta::from_xdr_base64(meta, Limits::none()).expect("Invalid XDR from RPC")
765        })
766    }
767}