use solana_client::client_error::ClientError as Error;
use solana_sdk::{
clock::Slot, commitment_config::CommitmentConfig, signature::Signature,
transaction::TransactionError,
};
use solana_transaction_status::TransactionStatus as SolanaTransactionStatus;
#[derive(Debug)]
pub enum TransactionOutcome<T> {
Success(SuccessfulTransaction<T>),
Unknown(UnknownTransaction<T>),
Failure(FailedTransaction<T>),
}
#[derive(Debug)]
pub struct SuccessfulTransaction<T> {
pub data: T,
pub slot: Slot,
pub signature: Signature,
}
#[derive(Debug)]
pub struct UnknownTransaction<T> {
pub data: T,
}
#[derive(Debug)]
pub struct FailedTransaction<T> {
pub data: T,
pub error: Error,
pub logs: Vec<String>,
}
impl<T> TransactionOutcome<T> {
pub fn successful(&self) -> bool {
match self {
TransactionOutcome::Success(_) => true,
TransactionOutcome::Unknown(_) | TransactionOutcome::Failure(_) => false,
}
}
pub fn into_successful(self) -> Option<SuccessfulTransaction<T>> {
match self {
TransactionOutcome::Success(s) => Some(s),
TransactionOutcome::Unknown(_) | TransactionOutcome::Failure(_) => None,
}
}
pub fn error(&self) -> Option<&FailedTransaction<T>> {
match self {
TransactionOutcome::Success(_) => None,
TransactionOutcome::Unknown(_) => None,
TransactionOutcome::Failure(f) => Some(f),
}
}
}
pub struct TransactionProgress<T> {
pub data: T,
pub landed_as: Option<(Slot, Signature)>,
pub status: TransactionStatus,
}
impl<T> TransactionProgress<T> {
pub fn new(data: T) -> Self {
Self {
data,
landed_as: None,
status: TransactionStatus::Pending,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TransactionStatus {
Pending,
Processing,
Committed,
Failed(TransactionError, Vec<String>),
}
impl TransactionStatus {
pub fn from_solana_status(
status: SolanaTransactionStatus,
logs: Vec<String>,
commitment: CommitmentConfig,
) -> Self {
if let Some(TransactionError::AlreadyProcessed) = status.err {
TransactionStatus::Committed
} else if let Some(err) = status.err {
TransactionStatus::Failed(err, logs)
} else if status.satisfies_commitment(commitment) {
TransactionStatus::Committed
} else {
TransactionStatus::Processing
}
}
pub fn should_be_reconfirmed(&self) -> bool {
match self {
TransactionStatus::Pending => true,
TransactionStatus::Processing => true,
TransactionStatus::Committed => false,
TransactionStatus::Failed(..) => false,
}
}
}
impl<T> From<TransactionProgress<T>> for TransactionOutcome<T> {
fn from(progress: TransactionProgress<T>) -> Self {
match progress.status {
TransactionStatus::Pending | TransactionStatus::Processing => {
TransactionOutcome::Unknown(UnknownTransaction {
data: progress.data,
})
}
TransactionStatus::Failed(err, logs) => {
TransactionOutcome::Failure(FailedTransaction {
data: progress.data,
error: err.into(),
logs,
})
}
TransactionStatus::Committed => TransactionOutcome::Success(SuccessfulTransaction {
data: progress.data,
slot: progress.landed_as.unwrap().0,
signature: progress.landed_as.unwrap().1,
}),
}
}
}