anchor/lang/src/error.rs

505 lines
18 KiB
Rust
Raw Normal View History

use anchor_lang::error_code;
2022-02-20 14:28:24 -08:00
use borsh::maybestd::io::Error as BorshIoError;
use solana_program::{program_error::ProgramError, pubkey::Pubkey};
2022-02-20 14:28:24 -08:00
use std::fmt::{Debug, Display};
2021-01-15 23:05:26 -08:00
2022-02-10 12:54:12 -08:00
/// The starting point for user defined error codes.
pub const ERROR_CODE_OFFSET: u32 = 6000;
2021-12-30 17:46:49 -08:00
/// Error codes that can be returned by internal framework code.
///
/// - >= 100 Instruction error codes
/// - >= 1000 IDL error codes
/// - >= 2000 constraint error codes
/// - >= 3000 account error codes
2022-02-16 19:07:56 -08:00
/// - >= 4100 misc error codes
2021-12-30 17:46:49 -08:00
/// - = 5000 deprecated error code
///
/// The starting point for user-defined errors is defined
2022-02-10 12:54:12 -08:00
/// by the [ERROR_CODE_OFFSET](crate::error::ERROR_CODE_OFFSET).
2022-02-20 14:28:24 -08:00
#[error_code(offset = 0)]
2021-01-15 23:05:26 -08:00
pub enum ErrorCode {
2021-12-30 17:46:49 -08:00
// Instructions
/// 100 - 8 byte instruction identifier not provided
#[msg("8 byte instruction identifier not provided")]
InstructionMissing = 100,
2021-12-30 17:46:49 -08:00
/// 101 - Fallback functions are not supported
#[msg("Fallback functions are not supported")]
InstructionFallbackNotFound,
2021-12-30 17:46:49 -08:00
/// 102 - The program could not deserialize the given instruction
#[msg("The program could not deserialize the given instruction")]
InstructionDidNotDeserialize,
2021-12-30 17:46:49 -08:00
/// 103 - The program could not serialize the given instruction
#[msg("The program could not serialize the given instruction")]
InstructionDidNotSerialize,
2021-01-15 23:05:26 -08:00
2021-12-30 17:46:49 -08:00
// IDL instructions
/// 1000 - The program was compiled without idl instructions
#[msg("The program was compiled without idl instructions")]
IdlInstructionStub = 1000,
2021-12-30 17:46:49 -08:00
/// 1001 - Invalid program given to the IDL instruction
#[msg("Invalid program given to the IDL instruction")]
IdlInstructionInvalidProgram,
/// 1002 - IDL Account must be empty in order to resize
#[msg("IDL account must be empty in order to resize, try closing first")]
IdlAccountNotEmpty,
2021-12-30 17:46:49 -08:00
// Constraints
/// 2000 - A mut constraint was violated
#[msg("A mut constraint was violated")]
ConstraintMut = 2000,
2021-12-30 17:46:49 -08:00
/// 2001 - A has one constraint was violated
#[msg("A has one constraint was violated")]
ConstraintHasOne,
2021-12-30 17:46:49 -08:00
/// 2002 - A signer constraint was violated
2021-12-21 11:13:51 -08:00
#[msg("A signer constraint was violated")]
ConstraintSigner,
2021-12-30 17:46:49 -08:00
/// 2003 - A raw constraint was violated
#[msg("A raw constraint was violated")]
ConstraintRaw,
2021-12-30 17:46:49 -08:00
/// 2004 - An owner constraint was violated
#[msg("An owner constraint was violated")]
ConstraintOwner,
2021-12-30 17:46:49 -08:00
/// 2005 - A rent exemption constraint was violated
#[msg("A rent exemption constraint was violated")]
ConstraintRentExempt,
2021-12-30 17:46:49 -08:00
/// 2006 - A seeds constraint was violated
#[msg("A seeds constraint was violated")]
ConstraintSeeds,
2021-12-30 17:46:49 -08:00
/// 2007 - An executable constraint was violated
#[msg("An executable constraint was violated")]
ConstraintExecutable,
/// 2008 - Deprecated Error, feel free to replace with something else
#[msg("Deprecated Error, feel free to replace with something else")]
ConstraintState,
2021-12-30 17:46:49 -08:00
/// 2009 - An associated constraint was violated
#[msg("An associated constraint was violated")]
ConstraintAssociated,
2021-12-30 17:46:49 -08:00
/// 2010 - An associated init constraint was violated
#[msg("An associated init constraint was violated")]
ConstraintAssociatedInit,
2021-12-30 17:46:49 -08:00
/// 2011 - A close constraint was violated
#[msg("A close constraint was violated")]
ConstraintClose,
2021-12-30 17:46:49 -08:00
/// 2012 - An address constraint was violated
2021-06-27 13:17:05 -07:00
#[msg("An address constraint was violated")]
ConstraintAddress,
2021-12-30 17:46:49 -08:00
/// 2013 - Expected zero account discriminant
#[msg("Expected zero account discriminant")]
ConstraintZero,
2021-12-30 17:46:49 -08:00
/// 2014 - A token mint constraint was violated
#[msg("A token mint constraint was violated")]
ConstraintTokenMint,
2021-12-30 17:46:49 -08:00
/// 2015 - A token owner constraint was violated
#[msg("A token owner constraint was violated")]
ConstraintTokenOwner,
2021-12-30 17:46:49 -08:00
/// The mint mint is intentional -> a mint authority for the mint.
///
/// 2016 - A mint mint authority constraint was violated
#[msg("A mint mint authority constraint was violated")]
ConstraintMintMintAuthority,
2021-12-30 17:46:49 -08:00
/// 2017 - A mint freeze authority constraint was violated
#[msg("A mint freeze authority constraint was violated")]
ConstraintMintFreezeAuthority,
2021-12-30 17:46:49 -08:00
/// 2018 - A mint decimals constraint was violated
#[msg("A mint decimals constraint was violated")]
ConstraintMintDecimals,
2021-12-30 17:46:49 -08:00
/// 2019 - A space constraint was violated
#[msg("A space constraint was violated")]
ConstraintSpace,
Feat: Optional Positional Accounts (#2101) * optional accounts initial implementation * cargo fmt * panic if Account related traits are run on none * Allow empty accounts to deserialize to None for optional accounts * implement constraints for optional accounts * optional accounts to idl gen * accountstruct helper method * implemented to_account_metas and infos * add test program * Rename optional to is_optional * added more traits * added TryKey error * fix has_one * update prelude * is_optional * add is_optional helper method * Add TryAccountInfos trait * improve constraint parser * initial work on TryToAccountInfo * Rename to TryToAccountInfo * finished implementing tryToAccountInfo * Using program method * Formatting * Fix program function call * Remove function return borrow * Fix access to program field * finished implementing tryToAccountInfo * add exit try_to_account_infos * descriptive ID path * try_to_account_info * fix close constraint * update test files * completed typescript optional accounts implementation * fix try accounts for init * update tests * fix to_account_metas * update tests * fix linting * remove types/node * update yarn.lock maybe? * update optional test * update optional test * update optional rust cli test * fix linting and tests * fix tests * update try_accounts to pass in accs during constraint gen * Add default impl for TryToAccountInfos * Removed TryToAccountInfos trait * Formatting * remove unneccesary traits and improve constraint gen drastically * fix exit generation * clippy * improve cross check error message * improve comments * more comments * update constraints hopefully good now? * add new errors to ts client * add new errors to ts client * update optional test * update anchor ts client * update misc crate * linting * temporarily comment out optional rs tests * update ts * remove local test files * linting * optional client tests * fix other lints to make the test pass * remove comments * remove misc-optional for now * update optional program * update optional program and client tests again * update optional program and client tests again again * added initialize tests that should pass * undo unrelated anchor.toml change * update close on optional program and improve tests * update optional program again. * update optional program and optional tests * fix has one error message * fix client example tests * update lockfile * update lockfile * regenerate lockfile * reset lockfile * reset ts yarn lockfile * update no caching tests * update exit codegen to use generate_optional_check * remove `try_to_account_infos` * update parser to ignore method calls in constraints * refactor and improve optional checks in constraints * add misc-optional program and tests * enable cpi for optional tests * Revert "enable cpi for optional tests" This reverts commit c864cd5d4f019e6bd5f93641e01bd82fc74041d4. * simplify misc tests * update version * fix rust version and resolve merge conflicts * prevent Option on composite accounts * hopefully fixed ts stuff? * hopefully fixed ts stuff? * testing * hopefully done? * update misc test * fix optional tests * fix ts * fix ts again! * linting urg * allow-missing-optionals feature * fix client tests * add bnjs types to tests Co-authored-by: febo <febo@kent.ac.uk> Co-authored-by: Henry-E <henry.elder@adaptcentre.ie>
2022-12-12 07:32:59 -08:00
/// 2020 - A required account for the constraint is None
#[msg("A required account for the constraint is None")]
ConstraintAccountIsNone,
// Require
/// 2500 - A require expression was violated
#[msg("A require expression was violated")]
RequireViolated = 2500,
/// 2501 - A require_eq expression was violated
#[msg("A require_eq expression was violated")]
RequireEqViolated,
/// 2502 - A require_keys_eq expression was violated
#[msg("A require_keys_eq expression was violated")]
RequireKeysEqViolated,
/// 2503 - A require_neq expression was violated
#[msg("A require_neq expression was violated")]
RequireNeqViolated,
/// 2504 - A require_keys_neq expression was violated
#[msg("A require_keys_neq expression was violated")]
RequireKeysNeqViolated,
/// 2505 - A require_gt expression was violated
#[msg("A require_gt expression was violated")]
RequireGtViolated,
/// 2506 - A require_gte expression was violated
#[msg("A require_gte expression was violated")]
RequireGteViolated,
// Accounts.
2021-12-30 17:46:49 -08:00
/// 3000 - The account discriminator was already set on this account
#[msg("The account discriminator was already set on this account")]
AccountDiscriminatorAlreadySet = 3000,
2021-12-30 17:46:49 -08:00
/// 3001 - No 8 byte discriminator was found on the account
#[msg("No 8 byte discriminator was found on the account")]
AccountDiscriminatorNotFound,
2021-12-30 17:46:49 -08:00
/// 3002 - 8 byte discriminator did not match what was expected
#[msg("8 byte discriminator did not match what was expected")]
AccountDiscriminatorMismatch,
2021-12-30 17:46:49 -08:00
/// 3003 - Failed to deserialize the account
#[msg("Failed to deserialize the account")]
AccountDidNotDeserialize,
2021-12-30 17:46:49 -08:00
/// 3004 - Failed to serialize the account
#[msg("Failed to serialize the account")]
AccountDidNotSerialize,
2021-12-30 17:46:49 -08:00
/// 3005 - Not enough account keys given to the instruction
#[msg("Not enough account keys given to the instruction")]
AccountNotEnoughKeys,
2021-12-30 17:46:49 -08:00
/// 3006 - The given account is not mutable
#[msg("The given account is not mutable")]
AccountNotMutable,
2021-12-30 17:46:49 -08:00
/// 3007 - The given account is owned by a different program than expected
#[msg("The given account is owned by a different program than expected")]
AccountOwnedByWrongProgram,
2021-12-30 17:46:49 -08:00
/// 3008 - Program ID was not as expected
#[msg("Program ID was not as expected")]
InvalidProgramId,
2021-12-30 17:46:49 -08:00
/// 3009 - Program account is not executable
#[msg("Program account is not executable")]
InvalidProgramExecutable,
2021-12-30 17:46:49 -08:00
/// 3010 - The given account did not sign
#[msg("The given account did not sign")]
AccountNotSigner,
2021-12-30 17:46:49 -08:00
/// 3011 - The given account is not owned by the system program
#[msg("The given account is not owned by the system program")]
AccountNotSystemOwned,
2021-12-30 17:46:49 -08:00
/// 3012 - The program expected this account to be already initialized
#[msg("The program expected this account to be already initialized")]
AccountNotInitialized,
2021-12-30 17:46:49 -08:00
/// 3013 - The given account is not a program data account
2021-12-05 11:14:16 -08:00
#[msg("The given account is not a program data account")]
AccountNotProgramData,
/// 3014 - The given account is not the associated token account
#[msg("The given account is not the associated token account")]
AccountNotAssociatedTokenAccount,
/// 3015 - The given public key does not match the required sysvar
#[msg("The given public key does not match the required sysvar")]
AccountSysvarMismatch,
/// 3016 - The account reallocation exceeds the MAX_PERMITTED_DATA_INCREASE limit
#[msg("The account reallocation exceeds the MAX_PERMITTED_DATA_INCREASE limit")]
AccountReallocExceedsLimit,
/// 3017 - The account was duplicated for more than one reallocation
#[msg("The account was duplicated for more than one reallocation")]
AccountDuplicateReallocs,
2021-01-15 23:05:26 -08:00
// Miscellaneous
/// 4100 - The declared program id does not match actual program id
#[msg("The declared program id does not match the actual program id")]
DeclaredProgramIdMismatch = 4100,
/// 4101 - You cannot/should not initialize the payer account as a program account
#[msg("You cannot/should not initialize the payer account as a program account")]
TryingToInitPayerAsProgramAccount = 4101,
2021-12-30 17:46:49 -08:00
// Deprecated
/// 5000 - The API being used is deprecated and should no longer be used
#[msg("The API being used is deprecated and should no longer be used")]
Deprecated = 5000,
2021-01-15 23:05:26 -08:00
}
2022-02-20 14:28:24 -08:00
2022-04-20 16:12:50 -07:00
#[derive(Debug, PartialEq, Eq)]
2022-02-20 14:28:24 -08:00
pub enum Error {
AnchorError(AnchorError),
ProgramError(ProgramErrorWithOrigin),
}
impl std::error::Error for Error {}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::AnchorError(ae) => Display::fmt(&ae, f),
Error::ProgramError(pe) => Display::fmt(&pe, f),
}
}
}
impl From<AnchorError> for Error {
fn from(ae: AnchorError) -> Self {
Self::AnchorError(ae)
}
}
impl From<ProgramError> for Error {
fn from(program_error: ProgramError) -> Self {
Self::ProgramError(program_error.into())
}
}
impl From<BorshIoError> for Error {
fn from(error: BorshIoError) -> Self {
Error::ProgramError(ProgramError::from(error).into())
}
}
impl From<ProgramErrorWithOrigin> for Error {
fn from(pe: ProgramErrorWithOrigin) -> Self {
Self::ProgramError(pe)
}
}
impl Error {
pub fn log(&self) {
match self {
Error::ProgramError(program_error) => program_error.log(),
Error::AnchorError(anchor_error) => anchor_error.log(),
}
}
pub fn with_account_name(mut self, account_name: impl ToString) -> Self {
match &mut self {
Error::AnchorError(ae) => {
ae.error_origin = Some(ErrorOrigin::AccountName(account_name.to_string()));
}
Error::ProgramError(pe) => {
pe.error_origin = Some(ErrorOrigin::AccountName(account_name.to_string()));
}
2022-02-20 14:28:24 -08:00
};
self
}
pub fn with_source(mut self, source: Source) -> Self {
match &mut self {
Error::AnchorError(ae) => {
ae.error_origin = Some(ErrorOrigin::Source(source));
}
Error::ProgramError(pe) => {
pe.error_origin = Some(ErrorOrigin::Source(source));
}
};
self
}
pub fn with_pubkeys(mut self, pubkeys: (Pubkey, Pubkey)) -> Self {
let pubkeys = Some(ComparedValues::Pubkeys((pubkeys.0, pubkeys.1)));
match &mut self {
Error::AnchorError(ae) => ae.compared_values = pubkeys,
Error::ProgramError(pe) => pe.compared_values = pubkeys,
};
self
}
pub fn with_values(mut self, values: (impl ToString, impl ToString)) -> Self {
match &mut self {
Error::AnchorError(ae) => {
ae.compared_values = Some(ComparedValues::Values((
values.0.to_string(),
values.1.to_string(),
)))
}
Error::ProgramError(pe) => {
pe.compared_values = Some(ComparedValues::Values((
values.0.to_string(),
values.1.to_string(),
)))
}
2022-02-20 14:28:24 -08:00
};
self
}
}
#[derive(Debug)]
pub struct ProgramErrorWithOrigin {
pub program_error: ProgramError,
pub error_origin: Option<ErrorOrigin>,
pub compared_values: Option<ComparedValues>,
2022-02-20 14:28:24 -08:00
}
2022-04-20 16:12:50 -07:00
// Two ProgramErrors are equal when they have the same error code
impl PartialEq for ProgramErrorWithOrigin {
fn eq(&self, other: &Self) -> bool {
self.program_error == other.program_error
}
}
impl Eq for ProgramErrorWithOrigin {}
2022-02-20 14:28:24 -08:00
impl Display for ProgramErrorWithOrigin {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.program_error, f)
}
}
impl ProgramErrorWithOrigin {
pub fn log(&self) {
match &self.error_origin {
None => {
anchor_lang::solana_program::msg!(
"ProgramError occurred. Error Code: {:?}. Error Number: {}. Error Message: {}.",
self.program_error,
u64::from(self.program_error.clone()),
self.program_error
);
}
Some(ErrorOrigin::Source(source)) => {
anchor_lang::solana_program::msg!(
"ProgramError thrown in {}:{}. Error Code: {:?}. Error Number: {}. Error Message: {}.",
source.filename,
source.line,
self.program_error,
u64::from(self.program_error.clone()),
self.program_error
);
}
Some(ErrorOrigin::AccountName(account_name)) => {
// using sol_log because msg! wrongly interprets 5 inputs as u64
anchor_lang::solana_program::log::sol_log(&format!(
"ProgramError caused by account: {}. Error Code: {:?}. Error Number: {}. Error Message: {}.",
account_name,
self.program_error,
u64::from(self.program_error.clone()),
self.program_error
));
}
}
match &self.compared_values {
Some(ComparedValues::Pubkeys((left, right))) => {
anchor_lang::solana_program::msg!("Left:");
left.log();
anchor_lang::solana_program::msg!("Right:");
right.log();
}
Some(ComparedValues::Values((left, right))) => {
anchor_lang::solana_program::msg!("Left: {}", left);
anchor_lang::solana_program::msg!("Right: {}", right);
}
None => (),
2022-02-20 14:28:24 -08:00
}
}
pub fn with_source(mut self, source: Source) -> Self {
self.error_origin = Some(ErrorOrigin::Source(source));
self
}
pub fn with_account_name(mut self, account_name: impl ToString) -> Self {
self.error_origin = Some(ErrorOrigin::AccountName(account_name.to_string()));
self
}
2022-02-20 14:28:24 -08:00
}
impl From<ProgramError> for ProgramErrorWithOrigin {
fn from(program_error: ProgramError) -> Self {
Self {
program_error,
error_origin: None,
compared_values: None,
2022-02-20 14:28:24 -08:00
}
}
}
#[derive(Debug)]
pub enum ComparedValues {
Values((String, String)),
Pubkeys((Pubkey, Pubkey)),
}
#[derive(Debug)]
pub enum ErrorOrigin {
Source(Source),
AccountName(String),
}
2022-02-20 14:28:24 -08:00
#[derive(Debug)]
pub struct AnchorError {
pub error_name: String,
pub error_code_number: u32,
pub error_msg: String,
pub error_origin: Option<ErrorOrigin>,
pub compared_values: Option<ComparedValues>,
2022-02-20 14:28:24 -08:00
}
impl AnchorError {
pub fn log(&self) {
match &self.error_origin {
None => {
anchor_lang::solana_program::log::sol_log(&format!(
"AnchorError occurred. Error Code: {}. Error Number: {}. Error Message: {}.",
self.error_name, self.error_code_number, self.error_msg
));
}
Some(ErrorOrigin::Source(source)) => {
anchor_lang::solana_program::msg!(
"AnchorError thrown in {}:{}. Error Code: {}. Error Number: {}. Error Message: {}.",
source.filename,
source.line,
self.error_name,
self.error_code_number,
self.error_msg
);
}
Some(ErrorOrigin::AccountName(account_name)) => {
anchor_lang::solana_program::log::sol_log(&format!(
"AnchorError caused by account: {}. Error Code: {}. Error Number: {}. Error Message: {}.",
account_name,
self.error_name,
self.error_code_number,
self.error_msg
));
}
2022-02-20 14:28:24 -08:00
}
match &self.compared_values {
Some(ComparedValues::Pubkeys((left, right))) => {
anchor_lang::solana_program::msg!("Left:");
left.log();
anchor_lang::solana_program::msg!("Right:");
right.log();
}
Some(ComparedValues::Values((left, right))) => {
anchor_lang::solana_program::msg!("Left: {}", left);
anchor_lang::solana_program::msg!("Right: {}", right);
}
None => (),
}
}
pub fn with_source(mut self, source: Source) -> Self {
self.error_origin = Some(ErrorOrigin::Source(source));
self
}
pub fn with_account_name(mut self, account_name: impl ToString) -> Self {
self.error_origin = Some(ErrorOrigin::AccountName(account_name.to_string()));
self
}
}
impl Display for AnchorError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(&self, f)
2022-02-20 14:28:24 -08:00
}
}
2022-04-20 16:12:50 -07:00
/// Two `AnchorError`s are equal when they have the same error code
impl PartialEq for AnchorError {
fn eq(&self, other: &Self) -> bool {
self.error_code_number == other.error_code_number
}
}
impl Eq for AnchorError {}
2022-02-20 14:28:24 -08:00
impl std::convert::From<Error> for anchor_lang::solana_program::program_error::ProgramError {
fn from(e: Error) -> anchor_lang::solana_program::program_error::ProgramError {
match e {
Error::AnchorError(AnchorError {
error_code_number, ..
2022-02-20 14:28:24 -08:00
}) => {
anchor_lang::solana_program::program_error::ProgramError::Custom(error_code_number)
}
Error::ProgramError(program_error) => program_error.program_error,
}
}
}
#[derive(Debug)]
pub struct Source {
pub filename: &'static str,
pub line: u32,
}