#![allow(non_snake_case)]
use std::ops::Deref;
use stellar_baselib::{
soroban_data_builder::{SorobanDataBuilder, SorobanDataBuilderBehavior},
xdr::{
ContractEvent, DiagnosticEvent, LedgerCloseMeta, LedgerEntry, LedgerEntryData,
LedgerEntryExt, LedgerHeaderHistoryEntry, LedgerKey, Limits, ReadXdr, ScVal,
SorobanAuthorizationEntry, SorobanTransactionData, TransactionEnvelope, TransactionEvent,
TransactionMeta, TransactionResult,
},
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct GetHealthResponse {
pub status: String,
pub latest_ledger: u32,
pub oldest_ledger: u32,
pub ledger_retention_window: u32,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LedgerEntryResult {
pub last_modified_ledger_seq: Option<u32>,
pub live_until_ledger_seq: Option<u32>,
key: String,
xdr: String,
ext_xdr: Option<String>,
}
impl LedgerEntryResult {
pub fn to_key(&self) -> LedgerKey {
LedgerKey::from_xdr_base64(&self.key, Limits::none()).expect("Invalid LedgerKey from RPC")
}
pub fn to_data(&self) -> LedgerEntryData {
LedgerEntryData::from_xdr_base64(&self.xdr, Limits::none())
.expect("Invalid LedgerEntryData from RPC")
}
pub fn to_ext(&self) -> Option<LedgerEntryExt> {
self.ext_xdr.as_ref().map(|ext| {
LedgerEntryExt::from_xdr_base64(ext, Limits::none())
.expect("Invalid LedgerEntryExt from RPC")
})
}
}
#[derive(Deserialize, Debug, Clone)]
pub struct GetLedgerEntriesResponse {
pub entries: Option<Vec<LedgerEntryResult>>,
pub latestLedger: u32,
}
#[derive(Deserialize, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct GetNetworkResponse {
pub passphrase: Option<String>,
pub protocol_version: Option<i32>,
pub friendbot_url: Option<String>,
}
#[derive(Deserialize, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct GetLatestLedgerResponse {
pub id: String,
pub protocol_version: u32,
pub sequence: u32,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum TransactionStatus {
Success,
NotFound,
Failed,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetTransactionResponse {
pub latest_ledger: u32,
pub latest_ledger_close_time: String,
pub oldest_ledger: u32,
pub oldest_ledger_close_time: String,
pub created_at: Option<String>,
#[serde(flatten)]
transaction: TransactionDetails,
}
impl Deref for GetTransactionResponse {
type Target = TransactionDetails;
fn deref(&self) -> &Self::Target {
&self.transaction
}
}
#[derive(PartialEq, Eq, Deserialize, Debug)]
#[serde(rename_all = "lowercase")]
pub enum EventType {
Contract,
System,
Diagnostic,
All,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetEventsResponse {
pub events: Vec<EventResponse>,
pub cursor: Option<String>,
pub latest_ledger: u64,
pub oldest_ledger: Option<u64>,
pub latest_ledger_close_time: Option<String>,
pub oldest_ledger_close_time: Option<String>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EventResponse {
#[serde(rename = "type")]
pub event_type: EventType,
pub ledger: u64,
pub ledger_closed_at: String,
pub contract_id: String,
pub id: String,
pub operation_index: Option<u32>,
pub transaction_index: Option<u32>,
pub tx_hash: String,
pub paging_token: Option<String>,
pub in_successful_contract_call: bool,
topic: Vec<String>,
value: String,
}
impl EventResponse {
pub fn topic(&self) -> Vec<ScVal> {
self.topic
.iter()
.map(|t| ScVal::from_xdr_base64(t, Limits::none()).expect("Invalid XDR from RPC"))
.collect()
}
pub fn value(&self) -> ScVal {
ScVal::from_xdr_base64(&self.value, Limits::none()).expect("Invalid XDR from RPC")
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum SendTransactionStatus {
Pending,
Duplicate,
Error,
TryAgainLater,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SendTransactionResponse {
pub status: SendTransactionStatus,
pub hash: String,
pub latest_ledger: u32,
pub latest_ledger_close_time: String,
error_result_xdr: Option<String>, diagnostic_events_xdr: Option<Vec<String>>, }
impl SendTransactionResponse {
pub fn to_error_result(&self) -> Option<TransactionResult> {
self.error_result_xdr.as_ref().map(|e| {
TransactionResult::from_xdr_base64(e, Limits::none()).expect("Invalid XDR from RPC")
})
}
pub fn to_diagnostic_events(&self) -> Option<Vec<DiagnosticEvent>> {
if let Some(events) = self.diagnostic_events_xdr.as_ref() {
events
.iter()
.map(|e| DiagnosticEvent::from_xdr_base64(e, Limits::none()).ok())
.collect()
} else {
None
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct RestorePreamble {
min_resource_fee: String,
transaction_data: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct RawSimulateHostFunctionResult {
auth: Vec<String>,
xdr: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct RawStateChanges {
#[serde(rename = "type")]
kind: StateChangeKind,
key: String,
before: Option<String>,
after: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize, Copy)]
#[serde(rename_all = "lowercase")]
pub enum StateChangeKind {
Created = 1,
Updated = 2,
Deleted = 3,
}
pub struct StateChange {
pub kind: StateChangeKind,
pub key: LedgerKey,
pub before: Option<LedgerEntry>,
pub after: Option<LedgerEntry>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SimulateTransactionResponse {
pub latest_ledger: u32,
pub min_resource_fee: Option<String>,
pub error: Option<String>,
results: Option<Vec<RawSimulateHostFunctionResult>>,
transaction_data: Option<String>,
restore_preamble: Option<RestorePreamble>,
events: Option<Vec<String>>,
state_changes: Option<Vec<RawStateChanges>>,
}
impl SimulateTransactionResponse {
pub fn to_result(&self) -> Option<(ScVal, Vec<SorobanAuthorizationEntry>)> {
if let Some(r) = self.results.as_ref() {
let auth: Vec<SorobanAuthorizationEntry> = r[0]
.auth
.iter()
.map(|e| SorobanAuthorizationEntry::from_xdr_base64(e, Limits::none()).unwrap())
.collect();
let ret_val = ScVal::from_xdr_base64(&r[0].xdr, Limits::none())
.expect("Xdr from RPC should be valid");
Some((ret_val, auth))
} else {
None
}
}
pub fn to_transaction_data(&self) -> Option<SorobanTransactionData> {
self.transaction_data.as_ref().map(|data| {
SorobanDataBuilder::new(Some(stellar_baselib::soroban_data_builder::Either::Left(
data.to_owned(),
)))
.build()
})
}
pub fn to_restore_transaction_data(&self) -> Option<(i64, SorobanTransactionData)> {
if let Some(restore) = self.restore_preamble.clone() {
Some((
restore
.min_resource_fee
.parse()
.expect("Invalid i64 for min_resource_fee"),
SorobanDataBuilder::new(Some(stellar_baselib::soroban_data_builder::Either::Left(
restore.transaction_data,
)))
.build(),
))
} else {
None
}
}
pub fn to_events(&self) -> Option<Vec<DiagnosticEvent>> {
if let Some(events) = self.events.as_ref() {
events
.iter()
.map(|e| DiagnosticEvent::from_xdr_base64(e, Limits::none()).ok())
.collect()
} else {
None
}
}
pub fn to_state_changes(&self) -> Vec<StateChange> {
if let Some(changes) = self.state_changes.as_ref() {
changes
.iter()
.map(|c| StateChange {
kind: c.kind,
key: LedgerKey::from_xdr_base64(&c.key, Limits::none())
.expect("Invalid LedgerKey"),
before: c.before.as_ref().map(|e| {
LedgerEntry::from_xdr_base64(e, Limits::none())
.expect("Invalid LedgerEntry")
}),
after: c.after.as_ref().map(|e| {
LedgerEntry::from_xdr_base64(e, Limits::none())
.expect("Invalid LedgerEntry")
}),
})
.collect()
} else {
Default::default()
}
}
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetFeeStatsResponse {
pub soroban_inclusion_fee: FeeDistribution,
pub inclusion_fee: FeeDistribution,
pub latest_ledger: u32,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FeeDistribution {
pub max: String,
pub min: String,
pub mode: String,
pub p10: String,
pub p20: String,
pub p30: String,
pub p40: String,
pub p50: String,
pub p60: String,
pub p70: String,
pub p80: String,
pub p90: String,
pub p95: String,
pub p99: String,
pub transaction_count: String,
pub ledger_count: u32,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetVersionInfoResponse {
pub version: String,
pub commit_hash: String,
pub build_timestamp: String,
pub captive_core_version: String,
pub protocol_version: u32,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetTransactionsResponse {
pub latest_ledger: u32,
pub latest_ledger_close_timestamp: i64,
pub oldest_ledger: u32,
pub oldest_ledger_close_timestamp: i64,
pub cursor: String,
pub transactions: Vec<TransactionInfo>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionInfo {
pub created_at: Option<i64>,
#[serde(flatten)]
transaction: TransactionDetails,
}
impl Deref for TransactionInfo {
type Target = TransactionDetails;
fn deref(&self) -> &Self::Target {
&self.transaction
}
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionDetails {
pub status: TransactionStatus,
pub ledger: Option<u32>,
pub application_order: Option<i32>,
pub fee_bump: Option<bool>,
envelope_xdr: Option<String>,
result_xdr: Option<String>,
result_meta_xdr: Option<String>,
diagnostic_events_xdr: Option<Vec<String>>,
events: Option<Events>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Events {
transaction_events_xdr: Option<Vec<String>>,
contract_events_xdr: Option<Vec<Vec<String>>>,
}
impl TransactionDetails {
pub fn to_envelope(&self) -> Option<TransactionEnvelope> {
if let Some(result) = &self.envelope_xdr {
let r = TransactionEnvelope::from_xdr_base64(result, Limits::none());
r.ok()
} else {
None
}
}
pub fn to_result(&self) -> Option<TransactionResult> {
if let Some(result) = &self.result_xdr {
let r = TransactionResult::from_xdr_base64(result, Limits::none());
r.ok()
} else {
None
}
}
pub fn to_result_meta(&self) -> Option<(TransactionMeta, Option<ScVal>)> {
if let Some(result) = &self.result_meta_xdr {
let r = TransactionMeta::from_xdr_base64(result, Limits::none());
if let Ok(e) = r {
let mut return_value = None;
match &e {
TransactionMeta::V3(v3) => {
if let Some(v) = &v3.soroban_meta {
return_value = Some(v.return_value.clone());
}
}
TransactionMeta::V4(v4) => {
if let Some(v) = &v4.soroban_meta {
return_value = v.return_value.clone();
}
}
_ => {}
};
Some((e, return_value))
} else {
None
}
} else {
None
}
}
pub fn to_diagnostic_events(&self) -> Option<Vec<DiagnosticEvent>> {
if let Some(events) = &self.diagnostic_events_xdr {
events
.iter()
.map(|e| DiagnosticEvent::from_xdr_base64(e, Limits::none()).ok())
.collect()
} else {
None
}
}
pub fn to_events(&self) -> Option<(Vec<TransactionEvent>, Vec<Vec<ContractEvent>>)> {
if let Some(events) = &self.events {
let tx_events = match &events.transaction_events_xdr {
Some(te) => {
let v: Option<Vec<TransactionEvent>> = te
.iter()
.map(|e| TransactionEvent::from_xdr_base64(e, Limits::none()).ok())
.collect();
v.unwrap_or_default()
}
None => Vec::default(),
};
let cx_events = match &events.contract_events_xdr {
Some(te) => {
let v: Option<Vec<Vec<ContractEvent>>> = te
.iter()
.map(|row| {
row.iter()
.map(|e| ContractEvent::from_xdr_base64(e, Limits::none()).ok())
.collect()
})
.collect();
v.unwrap_or_default()
}
None => Vec::default(),
};
Some((tx_events, cx_events))
} else {
None
}
}
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetLedgersResponse {
pub latest_ledger: u32,
pub latest_ledger_close_time: i64,
pub oldest_ledger: u32,
pub oldest_ledger_close_time: i64,
pub cursor: String,
pub ledgers: Vec<LedgerInfo>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LedgerInfo {
pub hash: String,
pub sequence: u32,
pub ledger_close_time: String,
header_xdr: Option<String>,
metadataXdr: Option<String>,
}
impl LedgerInfo {
pub fn to_header(&self) -> Option<LedgerHeaderHistoryEntry> {
self.header_xdr.as_ref().map(|header| {
LedgerHeaderHistoryEntry::from_xdr_base64(header, Limits::none())
.expect("Invalid XDR from RPC")
})
}
pub fn to_metadata(&self) -> Option<LedgerCloseMeta> {
self.metadataXdr.as_ref().map(|meta| {
LedgerCloseMeta::from_xdr_base64(meta, Limits::none()).expect("Invalid XDR from RPC")
})
}
}