use crate::tmp_account_decoder::UiTokenAmount;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use solana_sdk::{
clock::UnixTimestamp,
commitment_config::CommitmentConfig,
message::MessageHeader,
slot_history::Slot,
transaction::{
Result as TransactionResult, TransactionError, TransactionVersion, VersionedTransaction,
},
transaction_context::TransactionReturnData,
};
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum TransactionBinaryEncoding {
Base58,
Base64,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UiInnerInstructions {
pub index: u8,
pub instructions: Vec<UiInstruction>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UiTransactionTokenBalance {
pub account_index: u8,
pub mint: String,
pub ui_token_amount: UiTokenAmount,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub owner: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub program_id: Option<String>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UiLoadedAddresses {
pub writable: Vec<String>,
pub readonly: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UiCompiledInstruction {
pub program_id_index: u8,
pub accounts: Vec<u8>,
pub data: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UiAddressTableLookup {
pub account_key: String,
pub writable_indexes: Vec<u8>,
pub readonly_indexes: Vec<u8>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UiRawMessage {
pub header: MessageHeader,
pub account_keys: Vec<String>,
pub recent_blockhash: String,
pub instructions: Vec<UiCompiledInstruction>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub address_table_lookups: Option<Vec<UiAddressTableLookup>>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct ParsedAccount {
pub pubkey: String,
pub writable: bool,
pub signer: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UiPartiallyDecodedInstruction {
pub program_id: String,
pub accounts: Vec<String>,
pub data: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", untagged)]
pub enum UiParsedInstruction {
Parsed(ParsedInstruction),
PartiallyDecoded(UiPartiallyDecodedInstruction),
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct ParsedInstruction {
pub program: String,
pub program_id: String,
pub parsed: Value,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", untagged)]
pub enum UiInstruction {
Compiled(UiCompiledInstruction),
Parsed(UiParsedInstruction),
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UiParsedMessage {
pub account_keys: Vec<ParsedAccount>,
pub recent_blockhash: String,
pub instructions: Vec<UiInstruction>,
pub address_table_lookups: Option<Vec<UiAddressTableLookup>>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", untagged)]
pub enum UiMessage {
Parsed(UiParsedMessage),
Raw(UiRawMessage),
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UiTransaction {
pub signatures: Vec<String>,
pub message: UiMessage,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", untagged)]
pub enum EncodedTransaction {
LegacyBinary(String), Binary(String, TransactionBinaryEncoding),
Json(UiTransaction),
}
impl EncodedTransaction {
pub fn decode(&self) -> Option<VersionedTransaction> {
let (blob, encoding) = match self {
Self::Json(_) => return None,
Self::LegacyBinary(blob) => (blob, TransactionBinaryEncoding::Base58),
Self::Binary(blob, encoding) => (blob, *encoding),
};
let transaction: Option<VersionedTransaction> = match encoding {
TransactionBinaryEncoding::Base58 => bs58::decode(blob)
.into_vec()
.ok()
.and_then(|bytes| bincode::deserialize(&bytes).ok()),
TransactionBinaryEncoding::Base64 => base64::decode(blob)
.ok()
.and_then(|bytes| bincode::deserialize(&bytes).ok()),
};
transaction.filter(|transaction| {
transaction
.sanitize(
true, )
.is_ok()
})
}
}
pub type Rewards = Vec<Reward>;
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
pub enum RewardType {
Fee,
Rent,
Staking,
Voting,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Reward {
pub pubkey: String,
pub lamports: i64,
pub post_balance: u64, pub reward_type: Option<RewardType>,
pub commission: Option<u8>, }
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UiTransactionStatusMeta {
pub err: Option<TransactionError>,
pub status: TransactionResult<()>, pub fee: u64,
pub pre_balances: Vec<u64>,
pub post_balances: Vec<u64>,
pub inner_instructions: Option<Vec<UiInnerInstructions>>,
pub log_messages: Option<Vec<String>>,
pub pre_token_balances: Option<Vec<UiTransactionTokenBalance>>,
pub post_token_balances: Option<Vec<UiTransactionTokenBalance>>,
pub rewards: Option<Rewards>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub loaded_addresses: Option<UiLoadedAddresses>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub return_data: Option<UiTransactionReturnData>,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct UiTransactionReturnData {
pub program_id: String,
pub data: (String, UiReturnDataEncoding),
}
impl From<TransactionReturnData> for UiTransactionReturnData {
fn from(return_data: TransactionReturnData) -> Self {
Self {
program_id: return_data.program_id.to_string(),
data: (
base64::encode(return_data.data),
UiReturnDataEncoding::Base64,
),
}
}
}
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum UiReturnDataEncoding {
Base64,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EncodedTransactionWithStatusMeta {
pub transaction: EncodedTransaction,
pub meta: Option<UiTransactionStatusMeta>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<TransactionVersion>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum TransactionConfirmationStatus {
Processed,
Confirmed,
Finalized,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionStatus {
pub slot: Slot,
pub confirmations: Option<usize>, pub status: TransactionResult<()>, pub err: Option<TransactionError>,
pub confirmation_status: Option<TransactionConfirmationStatus>,
}
impl TransactionStatus {
pub fn satisfies_commitment(&self, commitment_config: CommitmentConfig) -> bool {
if commitment_config.is_finalized() {
self.confirmations.is_none()
} else if commitment_config.is_confirmed() {
if let Some(status) = &self.confirmation_status {
*status != TransactionConfirmationStatus::Processed
} else {
self.confirmations.is_some() && self.confirmations.unwrap() > 1
|| self.confirmations.is_none()
}
} else {
true
}
}
pub fn confirmation_status(&self) -> TransactionConfirmationStatus {
match &self.confirmation_status {
Some(status) => status.clone(),
None => {
if self.confirmations.is_none() {
TransactionConfirmationStatus::Finalized
} else if self.confirmations.unwrap() > 0 {
TransactionConfirmationStatus::Confirmed
} else {
TransactionConfirmationStatus::Processed
}
}
}
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct EncodedConfirmedTransactionWithStatusMeta {
pub slot: Slot,
#[serde(flatten)]
pub transaction: EncodedTransactionWithStatusMeta,
pub block_time: Option<UnixTimestamp>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct UiConfirmedBlock {
pub previous_blockhash: String,
pub blockhash: String,
pub parent_slot: Slot,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub transactions: Option<Vec<EncodedTransactionWithStatusMeta>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub signatures: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rewards: Option<Rewards>,
pub block_time: Option<UnixTimestamp>,
pub block_height: Option<u64>,
}