#derive-error #macro-derive #solana #attributes #thiserror #mint

jpl-program-error

Library for Solana Program error attributes and derive macro for creating them

1 unstable release

new 0.6.0 Dec 18, 2024

#19 in #mint


Used in 3 crates (2 directly)

Apache-2.0

14KB

SPL Program Error

Macros for implementing error-based traits on enums.

  • #[derive(IntoProgramError)]: automatically derives the trait From<Self> for solana_program::program_error::ProgramError.
  • #[derive(DecodeError)]: automatically derives the trait solana_program::decode_error::DecodeError<T>.
  • #[derive(PrintProgramError)]: automatically derives the trait solana_program::program_error::PrintProgramError.
  • #[jpl_program_error]: Automatically derives all below traits:
    • Clone
    • Debug
    • Eq
    • DecodeError
    • IntoProgramError
    • PrintProgramError
    • thiserror::Error
    • num_derive::FromPrimitive
    • PartialEq

#[derive(IntoProgramError)]

This derive macro automatically derives the trait From<Self> for solana_program::program_error::ProgramError.

Your enum must implement the following traits in order for this macro to work:

  • Clone
  • Debug
  • Eq
  • thiserror::Error
  • num_derive::FromPrimitive
  • PartialEq

Sample code:

/// Example error
#[derive(
    Clone, Debug, Eq, IntoProgramError, thiserror::Error, num_derive::FromPrimitive, PartialEq,
)]
pub enum ExampleError {
    /// Mint has no mint authority
    #[error("Mint has no mint authority")]
    MintHasNoMintAuthority,
    /// Incorrect mint authority has signed the instruction
    #[error("Incorrect mint authority has signed the instruction")]
    IncorrectMintAuthority,
}

#[derive(DecodeError)]

This derive macro automatically derives the trait solana_program::decode_error::DecodeError<T>.

Your enum must implement the following traits in order for this macro to work:

  • Clone
  • Debug
  • Eq
  • IntoProgramError (above)
  • thiserror::Error
  • num_derive::FromPrimitive
  • PartialEq

Sample code:

/// Example error
#[derive(
    Clone,
    Debug,
    DecodeError,
    Eq,
    IntoProgramError,
    thiserror::Error,
    num_derive::FromPrimitive,
    PartialEq,
)]
pub enum ExampleError {
    /// Mint has no mint authority
    #[error("Mint has no mint authority")]
    MintHasNoMintAuthority,
    /// Incorrect mint authority has signed the instruction
    #[error("Incorrect mint authority has signed the instruction")]
    IncorrectMintAuthority,
}

#[derive(PrintProgramError)]

This derive macro automatically derives the trait solana_program::program_error::PrintProgramError.

Your enum must implement the following traits in order for this macro to work:

  • Clone
  • Debug
  • DecodeError<T> (above)
  • Eq
  • IntoProgramError (above)
  • thiserror::Error
  • num_derive::FromPrimitive
  • PartialEq

Sample code:

/// Example error
#[derive(
    Clone,
    Debug,
    DecodeError,
    Eq,
    IntoProgramError,
    thiserror::Error,
    num_derive::FromPrimitive,
    PartialEq,
)]
pub enum ExampleError {
    /// Mint has no mint authority
    #[error("Mint has no mint authority")]
    MintHasNoMintAuthority,
    /// Incorrect mint authority has signed the instruction
    #[error("Incorrect mint authority has signed the instruction")]
    IncorrectMintAuthority,
}

#[jpl_program_error]

It can be cumbersome to ensure your program's defined errors - typically represented in an enum - implement the required traits and will print to the program's logs when they're invoked.

This procedural macro will give you all of the required implementations out of the box:

  • Clone
  • Debug
  • Eq
  • thiserror::Error
  • num_derive::FromPrimitive
  • PartialEq

It also imports the required crates so you don't have to in your program:

  • num_derive
  • num_traits
  • thiserror

Just annotate your enum...

use solana_program_error_derive::*;

/// Example error
#[solana_program_error]
pub enum ExampleError {
    /// Mint has no mint authority
    #[error("Mint has no mint authority")]
    MintHasNoMintAuthority,
    /// Incorrect mint authority has signed the instruction
    #[error("Incorrect mint authority has signed the instruction")]
    IncorrectMintAuthority,
}

...and get:

/// Example error
pub enum ExampleError {
    /// Mint has no mint authority
    #[error("Mint has no mint authority")]
    MintHasNoMintAuthority,
    /// Incorrect mint authority has signed the instruction
    #[error("Incorrect mint authority has signed the instruction")]
    IncorrectMintAuthority,
}
#[automatically_derived]
impl ::core::clone::Clone for ExampleError {
    #[inline]
    fn clone(&self) -> ExampleError {
        match self {
            ExampleError::MintHasNoMintAuthority => ExampleError::MintHasNoMintAuthority,
            ExampleError::IncorrectMintAuthority => ExampleError::IncorrectMintAuthority,
        }
    }
}
#[automatically_derived]
impl ::core::fmt::Debug for ExampleError {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(
            f,
            match self {
                ExampleError::MintHasNoMintAuthority => "MintHasNoMintAuthority",
                ExampleError::IncorrectMintAuthority => "IncorrectMintAuthority",
            },
        )
    }
}
#[automatically_derived]
impl ::core::marker::StructuralEq for ExampleError {}
#[automatically_derived]
impl ::core::cmp::Eq for ExampleError {
    #[inline]
    #[doc(hidden)]
    #[no_coverage]
    fn assert_receiver_is_total_eq(&self) -> () {}
}
#[allow(unused_qualifications)]
impl std::error::Error for ExampleError {}
#[allow(unused_qualifications)]
impl std::fmt::Display for ExampleError {
    fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        #[allow(unused_variables, deprecated, clippy::used_underscore_binding)]
        match self {
            ExampleError::MintHasNoMintAuthority {} => {
                __formatter.write_fmt(format_args!("Mint has no mint authority"))
            }
            ExampleError::IncorrectMintAuthority {} => {
                __formatter
                    .write_fmt(
                        format_args!(
                            "Incorrect mint authority has signed the instruction"
                        ),
                    )
            }
        }
    }
}
#[allow(non_upper_case_globals, unused_qualifications)]
const _IMPL_NUM_FromPrimitive_FOR_ExampleError: () = {
    #[allow(clippy::useless_attribute)]
    #[allow(rust_2018_idioms)]
    extern crate num_traits as _num_traits;
    impl _num_traits::FromPrimitive for ExampleError {
        #[allow(trivial_numeric_casts)]
        #[inline]
        fn from_i64(n: i64) -> Option<Self> {
            if n == ExampleError::MintHasNoMintAuthority as i64 {
                Some(ExampleError::MintHasNoMintAuthority)
            } else if n == ExampleError::IncorrectMintAuthority as i64 {
                Some(ExampleError::IncorrectMintAuthority)
            } else {
                None
            }
        }
        #[inline]
        fn from_u64(n: u64) -> Option<Self> {
            Self::from_i64(n as i64)
        }
    }
};
#[automatically_derived]
impl ::core::marker::StructuralPartialEq for ExampleError {}
#[automatically_derived]
impl ::core::cmp::PartialEq for ExampleError {
    #[inline]
    fn eq(&self, other: &ExampleError) -> bool {
        let __self_tag = ::core::intrinsics::discriminant_value(self);
        let __arg1_tag = ::core::intrinsics::discriminant_value(other);
        __self_tag == __arg1_tag
    }
}
impl From<ExampleError> for solana_program::program_error::ProgramError {
    fn from(e: ExampleError) -> Self {
        solana_program::program_error::ProgramError::Custom(e as u32)
    }
}
impl<T> solana_program::decode_error::DecodeError<T> for ExampleError {
    fn type_of() -> &'static str {
        "ExampleError"
    }
}
impl solana_program::program_error::PrintProgramError for ExampleError {
    fn print<E>(&self)
    where
        E: 'static + std::error::Error + solana_program::decode_error::DecodeError<E>
            + solana_program::program_error::PrintProgramError
            + num_traits::FromPrimitive,
    {
        match self {
            ExampleError::MintHasNoMintAuthority => {
                ::solana_program::log::sol_log("Mint has no mint authority")
            }
            ExampleError::IncorrectMintAuthority => {
                ::solana_program::log::sol_log(
                    "Incorrect mint authority has signed the instruction",
                )
            }
        }
    }
}

Dependencies

~10–18MB
~264K SLoC