@solana/errors
Version:
Throw, identify, and decode Solana JavaScript errors
95 lines (92 loc) • 4.04 kB
text/typescript
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<typeof getSolanaErrorFromInstructionError>),
);
}
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,
);
}