#![cfg_attr(feature = "frozen-abi", feature(min_specialization))]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
use core::fmt;
#[cfg(feature = "frozen-abi")]
use rialo_frozen_abi_macro::{AbiEnumVisitor, AbiExample};
use rialo_s_instruction::error::InstructionError;
use rialo_sanitize::SanitizeError;
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
pub type TransactionResult<T> = Result<T, TransactionError>;
#[cfg_attr(feature = "frozen-abi", derive(AbiExample, AbiEnumVisitor))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum TransactionError {
AccountInUse,
AccountLoadedTwice,
AccountNotFound,
ProgramAccountNotFound,
InsufficientFundsForFee,
InvalidAccountForFee,
AlreadyProcessed,
TimestampTooStale,
TimestampInFuture,
InstructionError(u8, InstructionError),
InvalidAccountIndex,
SignatureFailure,
InvalidProgramForExecution,
SanitizeFailure,
AccountBorrowOutstanding,
WouldExceedMaxBlockCostLimit,
UnsupportedVersion,
InvalidWritableAccount,
WouldExceedMaxAccountCostLimit,
WouldExceedAccountDataBlockLimit,
TooManyAccountLocks,
InvalidRentPayingAccount,
WouldExceedMaxVoteCostLimit,
DuplicateInstruction(u8),
InsufficientFundsForRent { account_index: u8 },
MaxLoadedAccountsDataSizeExceeded,
InvalidLoadedAccountsDataSizeLimit,
ResanitizationNeeded,
UnbalancedTransaction,
ProgramCacheHitMaxLimit,
SerializationFailure,
InvalidConfigHashPrefix,
}
impl std::error::Error for TransactionError {}
impl fmt::Display for TransactionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::AccountInUse
=> f.write_str("Account in use"),
Self::AccountLoadedTwice
=> f.write_str("Account loaded twice"),
Self::AccountNotFound
=> f.write_str("Attempt to debit an account but found no record of a prior credit."),
Self::ProgramAccountNotFound
=> f.write_str("Attempt to load a program that does not exist"),
Self::InsufficientFundsForFee
=> f.write_str("Insufficient funds for fee"),
Self::InvalidAccountForFee
=> f.write_str("This account may not be used to pay transaction fees"),
Self::AlreadyProcessed
=> f.write_str("This transaction has already been processed"),
Self::TimestampTooStale
=> f.write_str("Transaction timestamp in seconds is too stale"),
Self::TimestampInFuture
=> f.write_str("Transaction timestamp in seconds is in the future"),
Self::InstructionError(idx, err) => write!(f, "Error processing Instruction {idx}: {err}"),
Self::InvalidAccountIndex
=> f.write_str("Transaction contains an invalid account reference"),
Self::SignatureFailure
=> f.write_str("Transaction did not pass signature verification"),
Self::InvalidProgramForExecution
=> f.write_str("This program may not be used for executing instructions"),
Self::SanitizeFailure
=> f.write_str("Transaction failed to sanitize accounts offsets correctly"),
Self::AccountBorrowOutstanding
=> f.write_str("Transaction processing left an account with an outstanding borrowed reference"),
Self::WouldExceedMaxBlockCostLimit
=> f.write_str("Transaction would exceed max Block Cost Limit"),
Self::UnsupportedVersion
=> f.write_str("Transaction version is unsupported"),
Self::InvalidWritableAccount
=> f.write_str("Transaction loads a writable account that cannot be written"),
Self::WouldExceedMaxAccountCostLimit
=> f.write_str("Transaction would exceed max account limit within the block"),
Self::WouldExceedAccountDataBlockLimit
=> f.write_str("Transaction would exceed account data limit within the block"),
Self::TooManyAccountLocks
=> f.write_str("Transaction locked too many accounts"),
Self::InvalidRentPayingAccount
=> f.write_str("Transaction leaves an account with a lower balance than rent-exempt minimum"),
Self::WouldExceedMaxVoteCostLimit
=> f.write_str("Transaction would exceed max Vote Cost Limit"),
Self::DuplicateInstruction(idx) => write!(f, "Transaction contains a duplicate instruction ({idx}) that is not allowed"),
Self::InsufficientFundsForRent {
account_index
} => write!(f,"Transaction results in an account ({account_index}) with insufficient funds for rent"),
Self::MaxLoadedAccountsDataSizeExceeded
=> f.write_str("Transaction exceeded max loaded accounts data size cap"),
Self::InvalidLoadedAccountsDataSizeLimit
=> f.write_str("LoadedAccountsDataSizeLimit set for transaction must be greater than 0."),
Self::ResanitizationNeeded
=> f.write_str("ResanitizationNeeded"),
Self::UnbalancedTransaction
=> f.write_str("Sum of account balances before and after transaction do not match"),
Self::ProgramCacheHitMaxLimit
=> f.write_str("Program cache hit max limit"),
Self::SerializationFailure => {
f.write_str("Serialization failure")
}
Self::InvalidConfigHashPrefix => {
f.write_str(
"Transaction has an invalid config hash prefix. \
The config hash may have changed or you may be connecting to a different network. \
Fetch the current config hash using getRecentValidatorConfigHash and retry."
)
}
}
}
}
impl From<SanitizeError> for TransactionError {
fn from(_: SanitizeError) -> Self {
Self::SanitizeFailure
}
}
#[cfg(not(target_os = "solana"))]
impl From<SanitizeMessageError> for TransactionError {
fn from(err: SanitizeMessageError) -> Self {
#[allow(clippy::match_single_binding)]
match err {
_ => Self::SanitizeFailure,
}
}
}
#[cfg(not(target_os = "solana"))]
#[derive(PartialEq, Debug, Eq, Clone)]
pub enum SanitizeMessageError {
IndexOutOfBounds,
ValueOutOfBounds,
InvalidValue,
}
#[cfg(not(target_os = "solana"))]
impl std::error::Error for SanitizeMessageError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::IndexOutOfBounds => None,
Self::ValueOutOfBounds => None,
Self::InvalidValue => None,
}
}
}
#[cfg(not(target_os = "solana"))]
impl fmt::Display for SanitizeMessageError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IndexOutOfBounds => f.write_str("index out of bounds"),
Self::ValueOutOfBounds => f.write_str("value out of bounds"),
Self::InvalidValue => f.write_str("invalid value"),
}
}
}
#[cfg(not(target_os = "solana"))]
impl From<SanitizeError> for SanitizeMessageError {
fn from(err: SanitizeError) -> Self {
match err {
SanitizeError::IndexOutOfBounds => Self::IndexOutOfBounds,
SanitizeError::ValueOutOfBounds => Self::ValueOutOfBounds,
SanitizeError::InvalidValue => Self::InvalidValue,
}
}
}
#[cfg(not(target_os = "solana"))]
#[derive(Debug)]
pub enum TransportError {
IoError(std::io::Error),
TransactionError(TransactionError),
Custom(String),
}
#[cfg(not(target_os = "solana"))]
impl std::error::Error for TransportError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
TransportError::IoError(e) => Some(e),
TransportError::TransactionError(e) => Some(e),
TransportError::Custom(_) => None,
}
}
}
#[cfg(not(target_os = "solana"))]
impl fmt::Display for TransportError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> ::core::fmt::Result {
match self {
Self::IoError(e) => f.write_fmt(format_args!("transport io error: {e}")),
Self::TransactionError(e) => {
f.write_fmt(format_args!("transport transaction error: {e}"))
}
Self::Custom(s) => f.write_fmt(format_args!("transport custom error: {s}")),
}
}
}
#[cfg(not(target_os = "solana"))]
impl From<std::io::Error> for TransportError {
fn from(e: std::io::Error) -> Self {
TransportError::IoError(e)
}
}
#[cfg(not(target_os = "solana"))]
impl From<TransactionError> for TransportError {
fn from(e: TransactionError) -> Self {
TransportError::TransactionError(e)
}
}
#[cfg(not(target_os = "solana"))]
impl TransportError {
pub fn unwrap(&self) -> TransactionError {
if let TransportError::TransactionError(err) = self {
err.clone()
} else {
panic!("unexpected transport error")
}
}
}
#[cfg(not(target_os = "solana"))]
pub type TransportResult<T> = std::result::Result<T, TransportError>;