use num_traits::FromPrimitive;
use thiserror::Error;
use crate::{decode_error::DecodeError, msg};
#[derive(Clone, Debug, Eq, Error, PartialEq)]
pub enum ProgramError {
#[error("Custom program error: {0:#x}")]
Custom(u32),
#[error("The arguments provided to a program instruction were invalid")]
InvalidArgument,
#[error("An instruction's data contents was invalid")]
InvalidInstructionData,
#[error("An account's data contents was invalid")]
InvalidAccountData,
#[error("An account's data was too small")]
AccountDataTooSmall,
#[error("An account's balance was too small to complete the instruction")]
InsufficientFunds,
#[error("The account did not have the expected program id")]
IncorrectProgramId,
#[error("A signature was required but not found")]
MissingRequiredSignature,
#[error("An initialize instruction was sent to an account that has already been initialized")]
AccountAlreadyInitialized,
#[error("An attempt to operate on an account that hasn't been initialized")]
UninitializedAccount,
#[error("The instruction expected additional account keys")]
NotEnoughAccountKeys,
#[error("Failed to borrow a reference to account data, already borrowed")]
AccountBorrowFailed,
#[error("Length of the seed is too long for address generation")]
MaxSeedLengthExceeded,
#[error("The number of seeds is greater than the maximum allowed")]
MaxSeedsExceeded,
#[error("Provided seeds do not result in a valid address")]
InvalidSeeds,
#[error("IO Error: {0}")]
BorshIoError(String),
#[error("Unsupported sysvar")]
IllegalOwner,
#[error("Accounts data allocations exceeded the maximum allowed per transaction")]
MaxAccountsDataAllocationsExceeded,
#[error("Account data reallocation was invalid")]
InvalidRealloc,
#[error("Instruction trace length exceeded the maximum allowed per transaction")]
MaxInstructionTraceLengthExceeded,
#[error("Builtin programs must consume compute units")]
BuiltinProgramsMustConsumeComputeUnits,
#[error("Invalid account owner")]
InvalidAccountOwner,
#[error("Program arithmetic overflowed")]
ArithmeticOverflow,
#[error("Account is immutable")]
Immutable,
#[error("Incorrect authority provided")]
IncorrectAuthority,
#[error("From hex error")]
FromHexError,
#[error("Account lamports cannot be negative")]
NegativeAccountLamports,
#[error("Readonly lamport change")]
ReadonlyLamportChange,
#[error("Executable lamport change")]
ExecutableLamportChange,
#[error("Account is not anchored")]
AccountNotAnchored,
#[error("Not enough compute units available to complete the instruction")]
NotEnoughComputeUnits,
#[error("Insufficient data length")]
InsufficientDataLength,
#[error("Incorrect length")]
IncorrectLength,
#[error("Transcript verification failed")]
TranscriptVerificationFailed,
#[error("Invalid chunk: {0}")]
InvalidChunk(String),
#[error("Transaction to sign empty")]
TransactionToSignEmpty,
#[error("Invalid utxo id")]
InvalidUtxoId,
#[error("Invalid utxo signer")]
InvalidUtxoSigner,
#[error("Invalid state transition: {0}")]
InvalidStateTransition(String),
}
pub trait PrintProgramError {
fn print<E>(&self)
where
E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive;
}
impl PrintProgramError for ProgramError {
fn print<E>(&self)
where
E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive,
{
match self {
Self::Custom(error) => {
if let Some(custom_error) = E::decode_custom_error_to_enum(*error) {
custom_error.print::<E>();
} else {
msg!("Error: Unknown");
}
}
Self::InvalidArgument => msg!("Error: InvalidArgument"),
Self::InvalidInstructionData => msg!("Error: InvalidInstructionData"),
Self::InvalidAccountData => msg!("Error: InvalidAccountData"),
Self::AccountDataTooSmall => msg!("Error: AccountDataTooSmall"),
Self::InsufficientFunds => msg!("Error: InsufficientFunds"),
Self::IncorrectProgramId => msg!("Error: IncorrectProgramId"),
Self::MissingRequiredSignature => msg!("Error: MissingRequiredSignature"),
Self::AccountAlreadyInitialized => msg!("Error: AccountAlreadyInitialized"),
Self::UninitializedAccount => msg!("Error: UninitializedAccount"),
Self::NotEnoughAccountKeys => msg!("Error: NotEnoughAccountKeys"),
Self::AccountBorrowFailed => msg!("Error: AccountBorrowFailed"),
Self::MaxSeedLengthExceeded => msg!("Error: MaxSeedLengthExceeded"),
Self::MaxSeedsExceeded => msg!("Error: MaxSeedsExceeded"),
Self::InvalidSeeds => msg!("Error: InvalidSeeds"),
Self::BorshIoError(_) => msg!("Error: BorshIoError"),
Self::IllegalOwner => msg!("Error: IllegalOwner"),
Self::MaxAccountsDataAllocationsExceeded => {
msg!("Error: MaxAccountsDataAllocationsExceeded")
}
Self::InvalidRealloc => msg!("Error: InvalidRealloc"),
Self::MaxInstructionTraceLengthExceeded => {
msg!("Error: MaxInstructionTraceLengthExceeded")
}
Self::BuiltinProgramsMustConsumeComputeUnits => {
msg!("Error: BuiltinProgramsMustConsumeComputeUnits")
}
Self::InvalidAccountOwner => msg!("Error: InvalidAccountOwner"),
Self::ArithmeticOverflow => msg!("Error: ArithmeticOverflow"),
Self::Immutable => msg!("Error: Immutable"),
Self::IncorrectAuthority => msg!("Error: IncorrectAuthority"),
Self::FromHexError => msg!("Error: FromHexError"),
Self::NegativeAccountLamports => msg!("Error: NegativeAccountLamports"),
Self::ReadonlyLamportChange => msg!("Error: ReadonlyLamportChange"),
Self::ExecutableLamportChange => msg!("Error: ExecutableLamportChange"),
Self::AccountNotAnchored => msg!("Error: AccountNotAnchored"),
Self::NotEnoughComputeUnits => msg!("Error: NotEnoughComputeUnits"),
Self::InsufficientDataLength => msg!("Error: InsufficientDataLength"),
Self::IncorrectLength => msg!("Error: IncorrectLength"),
Self::TranscriptVerificationFailed => msg!("Error: TranscriptVerificationFailed"),
Self::InvalidChunk(_) => msg!("Error: invalid chunk"),
Self::TransactionToSignEmpty => msg!("Error: TransactionToSignEmpty"),
Self::InvalidUtxoId => msg!("Error: InvalidUtxoId"),
Self::InvalidUtxoSigner => msg!("Error: InvalidUtxoSigner"),
Self::InvalidStateTransition(_) => msg!("Error: InvalidStateTransition"),
}
}
}
pub const BUILTIN_BIT_SHIFT: usize = 32;
macro_rules! to_builtin {
($error:expr) => {
($error as u64) << BUILTIN_BIT_SHIFT
};
}
pub const CUSTOM_ZERO: u64 = to_builtin!(1);
pub const INVALID_ARGUMENT: u64 = to_builtin!(2);
pub const INVALID_INSTRUCTION_DATA: u64 = to_builtin!(3);
pub const INVALID_ACCOUNT_DATA: u64 = to_builtin!(4);
pub const ACCOUNT_DATA_TOO_SMALL: u64 = to_builtin!(5);
pub const INSUFFICIENT_FUNDS: u64 = to_builtin!(6);
pub const INCORRECT_PROGRAM_ID: u64 = to_builtin!(7);
pub const MISSING_REQUIRED_SIGNATURES: u64 = to_builtin!(8);
pub const ACCOUNT_ALREADY_INITIALIZED: u64 = to_builtin!(9);
pub const UNINITIALIZED_ACCOUNT: u64 = to_builtin!(10);
pub const NOT_ENOUGH_ACCOUNT_KEYS: u64 = to_builtin!(11);
pub const ACCOUNT_BORROW_FAILED: u64 = to_builtin!(12);
pub const MAX_SEED_LENGTH_EXCEEDED: u64 = to_builtin!(13);
pub const MAX_SEEDS_EXCEEDED: u64 = to_builtin!(14);
pub const INVALID_SEEDS: u64 = to_builtin!(15);
pub const BORSH_IO_ERROR: u64 = to_builtin!(16);
pub const ACCOUNT_NOT_RENT_EXEMPT: u64 = to_builtin!(17);
pub const UNSUPPORTED_SYSVAR: u64 = to_builtin!(18);
pub const ILLEGAL_OWNER: u64 = to_builtin!(19);
pub const MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED: u64 = to_builtin!(20);
pub const INVALID_ACCOUNT_DATA_REALLOC: u64 = to_builtin!(21);
pub const MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED: u64 = to_builtin!(22);
pub const BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS: u64 = to_builtin!(23);
pub const INVALID_ACCOUNT_OWNER: u64 = to_builtin!(24);
pub const ARITHMETIC_OVERFLOW: u64 = to_builtin!(25);
pub const IMMUTABLE: u64 = to_builtin!(26);
pub const INCORRECT_AUTHORITY: u64 = to_builtin!(27);
pub const FROM_HEX_ERROR: u64 = to_builtin!(28);
pub const NEGATIVE_ACCOUNT_LAMPORTS: u64 = to_builtin!(29);
pub const READONLY_LAMPORT_CHANGE: u64 = to_builtin!(30);
pub const EXECUTABLE_LAMPORT_CHANGE: u64 = to_builtin!(31);
pub const ACCOUNT_NOT_ANCHORED: u64 = to_builtin!(32);
pub const NOT_ENOUGH_COMPUTE_UNITS: u64 = to_builtin!(33);
pub const INSUFFICIENT_DATA_LENGTH: u64 = to_builtin!(35);
pub const INCORRECT_LENGTH: u64 = to_builtin!(36);
pub const TRANSCRIPT_VERIFICATION_FAILED: u64 = to_builtin!(37);
pub const INVALID_CHUNK: u64 = to_builtin!(38);
pub const TRANSACTION_TO_SIGN_EMPTY: u64 = to_builtin!(39);
pub const INVALID_UTXO_ID: u64 = to_builtin!(40);
pub const INVALID_UTXO_SIGNER: u64 = to_builtin!(41);
pub const INVALID_STATE_TRANSITION: u64 = to_builtin!(42);
impl From<ProgramError> for u64 {
fn from(error: ProgramError) -> Self {
match error {
ProgramError::InvalidArgument => INVALID_ARGUMENT,
ProgramError::InvalidInstructionData => INVALID_INSTRUCTION_DATA,
ProgramError::InvalidAccountData => INVALID_ACCOUNT_DATA,
ProgramError::AccountDataTooSmall => ACCOUNT_DATA_TOO_SMALL,
ProgramError::InsufficientFunds => INSUFFICIENT_FUNDS,
ProgramError::IncorrectProgramId => INCORRECT_PROGRAM_ID,
ProgramError::MissingRequiredSignature => MISSING_REQUIRED_SIGNATURES,
ProgramError::AccountAlreadyInitialized => ACCOUNT_ALREADY_INITIALIZED,
ProgramError::UninitializedAccount => UNINITIALIZED_ACCOUNT,
ProgramError::NotEnoughAccountKeys => NOT_ENOUGH_ACCOUNT_KEYS,
ProgramError::AccountBorrowFailed => ACCOUNT_BORROW_FAILED,
ProgramError::MaxSeedLengthExceeded => MAX_SEED_LENGTH_EXCEEDED,
ProgramError::MaxSeedsExceeded => MAX_SEEDS_EXCEEDED,
ProgramError::InvalidSeeds => INVALID_SEEDS,
ProgramError::BorshIoError(_) => BORSH_IO_ERROR,
ProgramError::IllegalOwner => ILLEGAL_OWNER,
ProgramError::MaxAccountsDataAllocationsExceeded => {
MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED
}
ProgramError::InvalidRealloc => INVALID_ACCOUNT_DATA_REALLOC,
ProgramError::MaxInstructionTraceLengthExceeded => {
MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED
}
ProgramError::BuiltinProgramsMustConsumeComputeUnits => {
BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS
}
ProgramError::InvalidAccountOwner => INVALID_ACCOUNT_OWNER,
ProgramError::ArithmeticOverflow => ARITHMETIC_OVERFLOW,
ProgramError::Immutable => IMMUTABLE,
ProgramError::IncorrectAuthority => INCORRECT_AUTHORITY,
ProgramError::FromHexError => FROM_HEX_ERROR,
ProgramError::NegativeAccountLamports => NEGATIVE_ACCOUNT_LAMPORTS,
ProgramError::ReadonlyLamportChange => READONLY_LAMPORT_CHANGE,
ProgramError::ExecutableLamportChange => EXECUTABLE_LAMPORT_CHANGE,
ProgramError::AccountNotAnchored => ACCOUNT_NOT_ANCHORED,
ProgramError::NotEnoughComputeUnits => NOT_ENOUGH_COMPUTE_UNITS,
ProgramError::InsufficientDataLength => INSUFFICIENT_DATA_LENGTH,
ProgramError::IncorrectLength => INCORRECT_LENGTH,
ProgramError::TranscriptVerificationFailed => TRANSCRIPT_VERIFICATION_FAILED,
ProgramError::InvalidChunk(_) => INVALID_CHUNK,
ProgramError::TransactionToSignEmpty => TRANSACTION_TO_SIGN_EMPTY,
ProgramError::InvalidUtxoId => INVALID_UTXO_ID,
ProgramError::InvalidUtxoSigner => INVALID_UTXO_SIGNER,
ProgramError::InvalidStateTransition(_) => INVALID_STATE_TRANSITION,
ProgramError::Custom(error) => {
if error == 0 {
CUSTOM_ZERO
} else {
error as u64
}
}
}
}
}
impl From<u64> for ProgramError {
fn from(error: u64) -> Self {
match error {
CUSTOM_ZERO => Self::Custom(0),
INVALID_ARGUMENT => Self::InvalidArgument,
INVALID_INSTRUCTION_DATA => Self::InvalidInstructionData,
INVALID_ACCOUNT_DATA => Self::InvalidAccountData,
ACCOUNT_DATA_TOO_SMALL => Self::AccountDataTooSmall,
INSUFFICIENT_FUNDS => Self::InsufficientFunds,
INCORRECT_PROGRAM_ID => Self::IncorrectProgramId,
MISSING_REQUIRED_SIGNATURES => Self::MissingRequiredSignature,
ACCOUNT_ALREADY_INITIALIZED => Self::AccountAlreadyInitialized,
UNINITIALIZED_ACCOUNT => Self::UninitializedAccount,
NOT_ENOUGH_ACCOUNT_KEYS => Self::NotEnoughAccountKeys,
ACCOUNT_BORROW_FAILED => Self::AccountBorrowFailed,
MAX_SEED_LENGTH_EXCEEDED => Self::MaxSeedLengthExceeded,
INVALID_SEEDS => Self::InvalidSeeds,
BORSH_IO_ERROR => Self::BorshIoError("Unknown".to_string()),
ILLEGAL_OWNER => Self::IllegalOwner,
MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED => Self::MaxAccountsDataAllocationsExceeded,
INVALID_ACCOUNT_DATA_REALLOC => Self::InvalidRealloc,
MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED => Self::MaxInstructionTraceLengthExceeded,
BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS => {
Self::BuiltinProgramsMustConsumeComputeUnits
}
INVALID_ACCOUNT_OWNER => Self::InvalidAccountOwner,
ARITHMETIC_OVERFLOW => Self::ArithmeticOverflow,
IMMUTABLE => Self::Immutable,
INCORRECT_AUTHORITY => Self::IncorrectAuthority,
FROM_HEX_ERROR => Self::FromHexError,
ACCOUNT_NOT_ANCHORED => Self::AccountNotAnchored,
NOT_ENOUGH_COMPUTE_UNITS => Self::NotEnoughComputeUnits,
INSUFFICIENT_DATA_LENGTH => Self::InsufficientDataLength,
INCORRECT_LENGTH => Self::IncorrectLength,
TRANSCRIPT_VERIFICATION_FAILED => Self::TranscriptVerificationFailed,
INVALID_CHUNK => Self::InvalidChunk("Unknown".to_string()),
TRANSACTION_TO_SIGN_EMPTY => Self::TransactionToSignEmpty,
INVALID_UTXO_ID => Self::InvalidUtxoId,
INVALID_UTXO_SIGNER => Self::InvalidUtxoSigner,
_ => Self::Custom(error as u32),
}
}
}
impl From<hex::FromHexError> for ProgramError {
fn from(_: hex::FromHexError) -> Self {
Self::FromHexError
}
}
impl From<std::io::Error> for ProgramError {
fn from(error: std::io::Error) -> Self {
Self::BorshIoError(error.to_string())
}
}