import { SOLANA_ERROR__TRANSACTION_ERROR__DUPLICATE_INSTRUCTION, SOLANA_ERROR__TRANSACTION_ERROR__INSUFFICIENT_FUNDS_FOR_RENT, SOLANA_ERROR__TRANSACTION_ERROR__PROGRAM_EXECUTION_TEMPORARILY_RESTRICTED, SOLANA_ERROR__TRANSACTION_ERROR__UNKNOWN, } from './codes'; import { SolanaError } from './error'; import { getSolanaErrorFromInstructionError } from './instruction-error'; import { getSolanaErrorFromRpcError } from './rpc-enum-errors'; /** * How to add an error when an entry is added to the RPC `TransactionError` enum: * * 1. Follow the instructions in `./codes.ts` to add a corresponding Solana error code * 2. Add the `TransactionError` enum name in the same order as it appears in `./codes.ts` * 3. Add the new error name/code mapping to `./__tests__/transaction-error-test.ts` */ const ORDERED_ERROR_NAMES = [ // Keep synced with RPC source: https://github.com/anza-xyz/agave/blob/master/sdk/src/transaction/error.rs // If this list ever gets too large, consider implementing a compression strategy like this: // https://gist.github.com/steveluscher/aaa7cbbb5433b1197983908a40860c47 'AccountInUse', 'AccountLoadedTwice', 'AccountNotFound', 'ProgramAccountNotFound', 'InsufficientFundsForFee', 'InvalidAccountForFee', 'AlreadyProcessed', 'BlockhashNotFound', // `InstructionError` intentionally omitted; delegated to `getSolanaErrorFromInstructionError` 'CallChainTooDeep', 'MissingSignatureForFee', 'InvalidAccountIndex', 'SignatureFailure', 'InvalidProgramForExecution', 'SanitizeFailure', 'ClusterMaintenance', 'AccountBorrowOutstanding', 'WouldExceedMaxBlockCostLimit', 'UnsupportedVersion', 'InvalidWritableAccount', 'WouldExceedMaxAccountCostLimit', 'WouldExceedAccountDataBlockLimit', 'TooManyAccountLocks', 'AddressLookupTableNotFound', 'InvalidAddressLookupTableOwner', 'InvalidAddressLookupTableData', 'InvalidAddressLookupTableIndex', 'InvalidRentPayingAccount', 'WouldExceedMaxVoteCostLimit', 'WouldExceedAccountDataTotalLimit', 'DuplicateInstruction', 'InsufficientFundsForRent', 'MaxLoadedAccountsDataSizeExceeded', 'InvalidLoadedAccountsDataSizeLimit', 'ResanitizationNeeded', 'ProgramExecutionTemporarilyRestricted', 'UnbalancedTransaction', ]; export function getSolanaErrorFromTransactionError(transactionError: string | { [key: string]: unknown }): SolanaError { if (typeof transactionError === 'object' && 'InstructionError' in transactionError) { return getSolanaErrorFromInstructionError( ...(transactionError.InstructionError as Parameters), ); } return getSolanaErrorFromRpcError( { errorCodeBaseOffset: 7050001, getErrorContext(errorCode, rpcErrorName, rpcErrorContext) { if (errorCode === SOLANA_ERROR__TRANSACTION_ERROR__UNKNOWN) { return { errorName: rpcErrorName, ...(rpcErrorContext !== undefined ? { transactionErrorContext: rpcErrorContext } : null), }; } else if (errorCode === SOLANA_ERROR__TRANSACTION_ERROR__DUPLICATE_INSTRUCTION) { return { index: Number(rpcErrorContext as bigint | number), }; } else if ( errorCode === SOLANA_ERROR__TRANSACTION_ERROR__INSUFFICIENT_FUNDS_FOR_RENT || errorCode === SOLANA_ERROR__TRANSACTION_ERROR__PROGRAM_EXECUTION_TEMPORARILY_RESTRICTED ) { return { accountIndex: Number((rpcErrorContext as { account_index: bigint | number }).account_index), }; } }, orderedErrorNames: ORDERED_ERROR_NAMES, rpcEnumError: transactionError, }, getSolanaErrorFromTransactionError, ); }