diff --git a/packages/errors/src/__tests__/instruction-error-test.ts b/packages/errors/src/__tests__/instruction-error-test.ts new file mode 100644 index 000000000000..d0231100d5c3 --- /dev/null +++ b/packages/errors/src/__tests__/instruction-error-test.ts @@ -0,0 +1,106 @@ +import { + SOLANA_ERROR__INSTRUCTION_ERROR_BORSH_IO_ERROR, + SOLANA_ERROR__INSTRUCTION_ERROR_CUSTOM, + SOLANA_ERROR__INSTRUCTION_ERROR_UNKNOWN, + SolanaErrorCode, +} from '../codes'; +import { SolanaError } from '../error'; +import { getSolanaErrorFromInstructionError } from '../instruction-error'; + +describe('getSolanaErrorFromInstructionError', () => { + it.each([ + ['GenericError', 4615001], + ['InvalidArgument', 4615002], + ['InvalidInstructionData', 4615003], + ['InvalidAccountData', 4615004], + ['AccountDataTooSmall', 4615005], + ['InsufficientFunds', 4615006], + ['IncorrectProgramId', 4615007], + ['MissingRequiredSignature', 4615008], + ['AccountAlreadyInitialized', 4615009], + ['UninitializedAccount', 4615010], + ['UnbalancedInstruction', 4615011], + ['ModifiedProgramId', 4615012], + ['ExternalAccountLamportSpend', 4615013], + ['ExternalAccountDataModified', 4615014], + ['ReadonlyLamportChange', 4615015], + ['ReadonlyDataModified', 4615016], + ['DuplicateAccountIndex', 4615017], + ['ExecutableModified', 4615018], + ['RentEpochModified', 4615019], + ['NotEnoughAccountKeys', 4615020], + ['AccountDataSizeChanged', 4615021], + ['AccountNotExecutable', 4615022], + ['AccountBorrowFailed', 4615023], + ['AccountBorrowOutstanding', 4615024], + ['DuplicateAccountOutOfSync', 4615025], + ['InvalidError', 4615027], + ['ExecutableDataModified', 4615028], + ['ExecutableLamportChange', 4615029], + ['ExecutableAccountNotRentExempt', 4615030], + ['UnsupportedProgramId', 4615031], + ['CallDepth', 4615032], + ['MissingAccount', 4615033], + ['ReentrancyNotAllowed', 4615034], + ['MaxSeedLengthExceeded', 4615035], + ['InvalidSeeds', 4615036], + ['InvalidRealloc', 4615037], + ['ComputationalBudgetExceeded', 4615038], + ['PrivilegeEscalation', 4615039], + ['ProgramEnvironmentSetupFailure', 4615040], + ['ProgramFailedToComplete', 4615041], + ['ProgramFailedToCompile', 4615042], + ['Immutable', 4615043], + ['IncorrectAuthority', 4615044], + ['AccountNotRentExempt', 4615046], + ['InvalidAccountOwner', 4615047], + ['ArithmeticOverflow', 4615048], + ['UnsupportedSysvar', 4615049], + ['IllegalOwner', 4615050], + ['MaxAccountsDataAllocationsExceeded', 4615051], + ['MaxAccountsExceeded', 4615052], + ['MaxInstructionTraceLengthExceeded', 4615053], + ['BuiltinProgramsMustConsumeComputeUnits', 4615054], + ])('produces the correct `SolanaError` for a `%s` error', (transactionError, expectedCode) => { + const error = getSolanaErrorFromInstructionError(123, transactionError); + expect(error).toEqual(new SolanaError(expectedCode as SolanaErrorCode, { index: 123 })); + }); + it('produces the correct `SolanaError` for a `Custom` error', () => { + const error = getSolanaErrorFromInstructionError(123, { Custom: 789 }); + expect(error).toEqual( + new SolanaError(SOLANA_ERROR__INSTRUCTION_ERROR_CUSTOM, { + code: 789, + index: 123, + }), + ); + }); + it('produces the correct `SolanaError` for a `BorshIoError` error', () => { + const error = getSolanaErrorFromInstructionError(123, { BorshIoError: 'abc' }); + expect(error).toEqual( + new SolanaError(SOLANA_ERROR__INSTRUCTION_ERROR_BORSH_IO_ERROR, { + encodedData: 'abc', + index: 123, + }), + ); + }); + it("returns the unknown error when encountering an enum name that's missing from the map", () => { + const error = getSolanaErrorFromInstructionError(123, 'ThisDoesNotExist'); + expect(error).toEqual( + new SolanaError(SOLANA_ERROR__INSTRUCTION_ERROR_UNKNOWN, { + errorName: 'ThisDoesNotExist', + index: 123, + }), + ); + }); + it("returns the unknown error when encountering an enum struct that's missing from the map", () => { + const expectedContext = {} as const; + const error = getSolanaErrorFromInstructionError(123, { ThisDoesNotExist: expectedContext }); + expect(error).toEqual( + new SolanaError(SOLANA_ERROR__INSTRUCTION_ERROR_UNKNOWN, { + errorName: 'ThisDoesNotExist', + index: 123, + instructionErrorContext: expectedContext, + }), + ); + }); +}); diff --git a/packages/errors/src/__tests__/transaction-error-test.ts b/packages/errors/src/__tests__/transaction-error-test.ts index 82f7c07c398c..0d8e019bd6a7 100644 --- a/packages/errors/src/__tests__/transaction-error-test.ts +++ b/packages/errors/src/__tests__/transaction-error-test.ts @@ -6,8 +6,11 @@ import { SolanaErrorCode, } from '../codes'; import { SolanaError } from '../error'; +import { getSolanaErrorFromInstructionError } from '../instruction-error'; import { getSolanaErrorFromTransactionError } from '../transaction-error'; +jest.mock('../instruction-error.ts'); + describe('getSolanaErrorFromTransactionError', () => { it.each([ ['AccountInUse', 7050001], @@ -91,4 +94,12 @@ describe('getSolanaErrorFromTransactionError', () => { }), ); }); + it('delegates `InstructionError` to the instruction error getter', () => { + const instructionError = Symbol(); + const mockErrorResult = Symbol() as unknown as SolanaError; + jest.mocked(getSolanaErrorFromInstructionError).mockReturnValue(mockErrorResult); + const error = getSolanaErrorFromTransactionError({ InstructionError: [123, instructionError] }); + expect(getSolanaErrorFromInstructionError).toHaveBeenCalledWith(123, instructionError); + expect(error).toBe(mockErrorResult); + }); }); diff --git a/packages/errors/src/codes.ts b/packages/errors/src/codes.ts index b403434b0637..b6bd5c401573 100644 --- a/packages/errors/src/codes.ts +++ b/packages/errors/src/codes.ts @@ -13,6 +13,62 @@ export const SOLANA_ERROR__INVALID_KEYPAIR_BYTES = 4 as const; export const SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED = 5 as const; export const SOLANA_ERROR__NONCE_INVALID = 6 as const; export const SOLANA_ERROR__NONCE_ACCOUNT_NOT_FOUND = 7 as const; +// Reserve error codes starting with [4615000-4615999] for the Rust enum `InstructionError` +export const SOLANA_ERROR__INSTRUCTION_ERROR_UNKNOWN = 4615000 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_GENERIC_ERROR = 4615001 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ARGUMENT = 4615002 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_INSTRUCTION_DATA = 4615003 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ACCOUNT_DATA = 4615004 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_DATA_TOO_SMALL = 4615005 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_INSUFFICIENT_FUNDS = 4615006 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_INCORRECT_PROGRAM_ID = 4615007 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_MISSING_REQUIRED_SIGNATURE = 4615008 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_ALREADY_INITIALIZED = 4615009 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_UNINITIALIZED_ACCOUNT = 4615010 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_UNBALANCED_INSTRUCTION = 4615011 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_MODIFIED_PROGRAM_ID = 4615012 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_EXTERNAL_ACCOUNT_LAMPORT_SPEND = 4615013 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_EXTERNAL_ACCOUNT_DATA_MODIFIED = 4615014 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_READONLY_LAMPORT_CHANGE = 4615015 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_READONLY_DATA_MODIFIED = 4615016 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_DUPLICATE_ACCOUNT_INDEX = 4615017 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_MODIFIED = 4615018 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_RENT_EPOCH_MODIFIED = 4615019 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_NOT_ENOUGH_ACCOUNT_KEYS = 4615020 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_DATA_SIZE_CHANGED = 4615021 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_NOT_EXECUTABLE = 4615022 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_BORROW_FAILED = 4615023 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_BORROW_OUTSTANDING = 4615024 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_DUPLICATE_ACCOUNT_OUT_OF_SYNC = 4615025 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_CUSTOM = 4615026 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ERROR = 4615027 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_DATA_MODIFIED = 4615028 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_LAMPORT_CHANGE = 4615029 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_ACCOUNT_NOT_RENT_EXEMPT = 4615030 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_UNSUPPORTED_PROGRAM_ID = 4615031 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_CALL_DEPTH = 4615032 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_MISSING_ACCOUNT = 4615033 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_REENTRANCY_NOT_ALLOWED = 4615034 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_MAX_SEED_LENGTH_EXCEEDED = 4615035 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_SEEDS = 4615036 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_REALLOC = 4615037 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_COMPUTATIONAL_BUDGET_EXCEEDED = 4615038 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_PRIVILEGE_ESCALATION = 4615039 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_PROGRAM_ENVIRONMENT_SETUP_FAILURE = 4615040 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_PROGRAM_FAILED_TO_COMPLETE = 4615041 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_PROGRAM_FAILED_TO_COMPILE = 4615042 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_IMMUTABLE = 4615043 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_INCORRECT_AUTHORITY = 4615044 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_BORSH_IO_ERROR = 4615045 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_NOT_RENT_EXEMPT = 4615046 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ACCOUNT_OWNER = 4615047 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_ARITHMETIC_OVERFLOW = 4615048 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_UNSUPPORTED_SYSVAR = 4615049 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_ILLEGAL_OWNER = 4615050 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED = 4615051 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_MAX_ACCOUNTS_EXCEEDED = 4615052 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED = 4615053 as const; +export const SOLANA_ERROR__INSTRUCTION_ERROR_BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS = 4615054 as const; // Reserve error codes starting with [7050000-7050999] for the Rust enum `TransactionError` export const SOLANA_ERROR__TRANSACTION_ERROR_UNKNOWN = 7050000 as const; export const SOLANA_ERROR__TRANSACTION_ERROR_ACCOUNT_IN_USE = 7050001 as const; @@ -76,6 +132,61 @@ export type SolanaErrorCode = | typeof SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED | typeof SOLANA_ERROR__NONCE_INVALID | typeof SOLANA_ERROR__NONCE_ACCOUNT_NOT_FOUND + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_UNKNOWN + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_GENERIC_ERROR + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ARGUMENT + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_INSTRUCTION_DATA + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ACCOUNT_DATA + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_DATA_TOO_SMALL + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INSUFFICIENT_FUNDS + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INCORRECT_PROGRAM_ID + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_MISSING_REQUIRED_SIGNATURE + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_ALREADY_INITIALIZED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_UNINITIALIZED_ACCOUNT + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_UNBALANCED_INSTRUCTION + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_MODIFIED_PROGRAM_ID + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_EXTERNAL_ACCOUNT_LAMPORT_SPEND + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_EXTERNAL_ACCOUNT_DATA_MODIFIED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_READONLY_LAMPORT_CHANGE + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_READONLY_DATA_MODIFIED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_DUPLICATE_ACCOUNT_INDEX + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_MODIFIED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_RENT_EPOCH_MODIFIED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_NOT_ENOUGH_ACCOUNT_KEYS + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_DATA_SIZE_CHANGED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_NOT_EXECUTABLE + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_BORROW_FAILED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_BORROW_OUTSTANDING + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_DUPLICATE_ACCOUNT_OUT_OF_SYNC + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_CUSTOM + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ERROR + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_DATA_MODIFIED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_LAMPORT_CHANGE + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_ACCOUNT_NOT_RENT_EXEMPT + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_UNSUPPORTED_PROGRAM_ID + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_CALL_DEPTH + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_MISSING_ACCOUNT + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_REENTRANCY_NOT_ALLOWED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_MAX_SEED_LENGTH_EXCEEDED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_SEEDS + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_REALLOC + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_COMPUTATIONAL_BUDGET_EXCEEDED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_PRIVILEGE_ESCALATION + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_PROGRAM_ENVIRONMENT_SETUP_FAILURE + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_PROGRAM_FAILED_TO_COMPLETE + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_PROGRAM_FAILED_TO_COMPILE + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_IMMUTABLE + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INCORRECT_AUTHORITY + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_BORSH_IO_ERROR + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_NOT_RENT_EXEMPT + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ACCOUNT_OWNER + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_ARITHMETIC_OVERFLOW + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_UNSUPPORTED_SYSVAR + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_ILLEGAL_OWNER + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_MAX_ACCOUNTS_EXCEEDED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS | typeof SOLANA_ERROR__TRANSACTION_ERROR_UNKNOWN | typeof SOLANA_ERROR__TRANSACTION_ERROR_ACCOUNT_IN_USE | typeof SOLANA_ERROR__TRANSACTION_ERROR_ACCOUNT_LOADED_TWICE diff --git a/packages/errors/src/context.ts b/packages/errors/src/context.ts index 2355b8ae39a9..a0b699737d95 100644 --- a/packages/errors/src/context.ts +++ b/packages/errors/src/context.ts @@ -1,5 +1,60 @@ import { SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, + SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_ALREADY_INITIALIZED, + SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_BORROW_FAILED, + SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_BORROW_OUTSTANDING, + SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_DATA_SIZE_CHANGED, + SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_DATA_TOO_SMALL, + SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_NOT_EXECUTABLE, + SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_NOT_RENT_EXEMPT, + SOLANA_ERROR__INSTRUCTION_ERROR_ARITHMETIC_OVERFLOW, + SOLANA_ERROR__INSTRUCTION_ERROR_BORSH_IO_ERROR, + SOLANA_ERROR__INSTRUCTION_ERROR_BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS, + SOLANA_ERROR__INSTRUCTION_ERROR_CALL_DEPTH, + SOLANA_ERROR__INSTRUCTION_ERROR_COMPUTATIONAL_BUDGET_EXCEEDED, + SOLANA_ERROR__INSTRUCTION_ERROR_CUSTOM, + SOLANA_ERROR__INSTRUCTION_ERROR_DUPLICATE_ACCOUNT_INDEX, + SOLANA_ERROR__INSTRUCTION_ERROR_DUPLICATE_ACCOUNT_OUT_OF_SYNC, + SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_ACCOUNT_NOT_RENT_EXEMPT, + SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_DATA_MODIFIED, + SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_LAMPORT_CHANGE, + SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_MODIFIED, + SOLANA_ERROR__INSTRUCTION_ERROR_EXTERNAL_ACCOUNT_DATA_MODIFIED, + SOLANA_ERROR__INSTRUCTION_ERROR_EXTERNAL_ACCOUNT_LAMPORT_SPEND, + SOLANA_ERROR__INSTRUCTION_ERROR_GENERIC_ERROR, + SOLANA_ERROR__INSTRUCTION_ERROR_ILLEGAL_OWNER, + SOLANA_ERROR__INSTRUCTION_ERROR_IMMUTABLE, + SOLANA_ERROR__INSTRUCTION_ERROR_INCORRECT_AUTHORITY, + SOLANA_ERROR__INSTRUCTION_ERROR_INCORRECT_PROGRAM_ID, + SOLANA_ERROR__INSTRUCTION_ERROR_INSUFFICIENT_FUNDS, + SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ACCOUNT_DATA, + SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ACCOUNT_OWNER, + SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ARGUMENT, + SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ERROR, + SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_INSTRUCTION_DATA, + SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_REALLOC, + SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_SEEDS, + SOLANA_ERROR__INSTRUCTION_ERROR_MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED, + SOLANA_ERROR__INSTRUCTION_ERROR_MAX_ACCOUNTS_EXCEEDED, + SOLANA_ERROR__INSTRUCTION_ERROR_MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED, + SOLANA_ERROR__INSTRUCTION_ERROR_MAX_SEED_LENGTH_EXCEEDED, + SOLANA_ERROR__INSTRUCTION_ERROR_MISSING_ACCOUNT, + SOLANA_ERROR__INSTRUCTION_ERROR_MISSING_REQUIRED_SIGNATURE, + SOLANA_ERROR__INSTRUCTION_ERROR_MODIFIED_PROGRAM_ID, + SOLANA_ERROR__INSTRUCTION_ERROR_NOT_ENOUGH_ACCOUNT_KEYS, + SOLANA_ERROR__INSTRUCTION_ERROR_PRIVILEGE_ESCALATION, + SOLANA_ERROR__INSTRUCTION_ERROR_PROGRAM_ENVIRONMENT_SETUP_FAILURE, + SOLANA_ERROR__INSTRUCTION_ERROR_PROGRAM_FAILED_TO_COMPILE, + SOLANA_ERROR__INSTRUCTION_ERROR_PROGRAM_FAILED_TO_COMPLETE, + SOLANA_ERROR__INSTRUCTION_ERROR_READONLY_DATA_MODIFIED, + SOLANA_ERROR__INSTRUCTION_ERROR_READONLY_LAMPORT_CHANGE, + SOLANA_ERROR__INSTRUCTION_ERROR_REENTRANCY_NOT_ALLOWED, + SOLANA_ERROR__INSTRUCTION_ERROR_RENT_EPOCH_MODIFIED, + SOLANA_ERROR__INSTRUCTION_ERROR_UNBALANCED_INSTRUCTION, + SOLANA_ERROR__INSTRUCTION_ERROR_UNINITIALIZED_ACCOUNT, + SOLANA_ERROR__INSTRUCTION_ERROR_UNKNOWN, + SOLANA_ERROR__INSTRUCTION_ERROR_UNSUPPORTED_PROGRAM_ID, + SOLANA_ERROR__INSTRUCTION_ERROR_UNSUPPORTED_SYSVAR, SOLANA_ERROR__INVALID_KEYPAIR_BYTES, SOLANA_ERROR__NONCE_ACCOUNT_NOT_FOUND, SOLANA_ERROR__NONCE_INVALID, @@ -12,6 +67,8 @@ import { SolanaErrorCode, } from './codes'; +type BasicInstructionErrorContext = Readonly<{ [P in T]: { index: number } }>; + export type DefaultUnspecifiedErrorContextToUndefined = { [P in SolanaErrorCode]: P extends keyof T ? T[P] : undefined; }; @@ -23,43 +80,123 @@ export type DefaultUnspecifiedErrorContextToUndefined = { * WARNING: * - Don't change or remove members of an error's context. */ -export type SolanaErrorContext = DefaultUnspecifiedErrorContextToUndefined<{ - [SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED]: { - currentBlockHeight: bigint; - lastValidBlockHeight: bigint; - }; - [SOLANA_ERROR__TRANSACTION_MISSING_SIGNATURES]: { - addresses: string[]; - }; - [SOLANA_ERROR__RPC_INTEGER_OVERFLOW]: { - argumentLabel: string; - keyPath: readonly (string | number | symbol)[]; - methodName: string; - optionalPathLabel: string; - path?: string; - value: bigint; - }; - [SOLANA_ERROR__INVALID_KEYPAIR_BYTES]: { - byteLength: number; - }; - [SOLANA_ERROR__NONCE_ACCOUNT_NOT_FOUND]: { - nonceAccountAddress: string; - }; - [SOLANA_ERROR__NONCE_INVALID]: { - actualNonceValue: string; - expectedNonceValue: string; - }; - [SOLANA_ERROR__TRANSACTION_ERROR_UNKNOWN]: { - errorName: string; - transactionErrorContext?: unknown; - }; - [SOLANA_ERROR__TRANSACTION_ERROR_DUPLICATE_INSTRUCTION]: { - index: number; - }; - [SOLANA_ERROR__TRANSACTION_ERROR_INSUFFICIENT_FUNDS_FOR_RENT]: { - accountIndex: number; - }; - [SOLANA_ERROR__TRANSACTION_ERROR_PROGRAM_EXECUTION_TEMPORARILY_RESTRICTED]: { - accountIndex: number; - }; -}>; +export type SolanaErrorContext = DefaultUnspecifiedErrorContextToUndefined< + BasicInstructionErrorContext< + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_UNKNOWN + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_GENERIC_ERROR + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ARGUMENT + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_INSTRUCTION_DATA + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ACCOUNT_DATA + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_DATA_TOO_SMALL + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INSUFFICIENT_FUNDS + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INCORRECT_PROGRAM_ID + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_MISSING_REQUIRED_SIGNATURE + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_ALREADY_INITIALIZED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_UNINITIALIZED_ACCOUNT + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_UNBALANCED_INSTRUCTION + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_MODIFIED_PROGRAM_ID + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_EXTERNAL_ACCOUNT_LAMPORT_SPEND + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_EXTERNAL_ACCOUNT_DATA_MODIFIED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_READONLY_LAMPORT_CHANGE + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_READONLY_DATA_MODIFIED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_DUPLICATE_ACCOUNT_INDEX + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_MODIFIED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_RENT_EPOCH_MODIFIED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_NOT_ENOUGH_ACCOUNT_KEYS + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_DATA_SIZE_CHANGED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_NOT_EXECUTABLE + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_BORROW_FAILED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_BORROW_OUTSTANDING + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_DUPLICATE_ACCOUNT_OUT_OF_SYNC + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_CUSTOM + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ERROR + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_DATA_MODIFIED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_LAMPORT_CHANGE + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_ACCOUNT_NOT_RENT_EXEMPT + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_UNSUPPORTED_PROGRAM_ID + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_CALL_DEPTH + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_MISSING_ACCOUNT + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_REENTRANCY_NOT_ALLOWED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_MAX_SEED_LENGTH_EXCEEDED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_SEEDS + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_REALLOC + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_COMPUTATIONAL_BUDGET_EXCEEDED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_PRIVILEGE_ESCALATION + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_PROGRAM_ENVIRONMENT_SETUP_FAILURE + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_PROGRAM_FAILED_TO_COMPLETE + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_PROGRAM_FAILED_TO_COMPILE + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_IMMUTABLE + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INCORRECT_AUTHORITY + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_BORSH_IO_ERROR + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_NOT_RENT_EXEMPT + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ACCOUNT_OWNER + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_ARITHMETIC_OVERFLOW + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_UNSUPPORTED_SYSVAR + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_ILLEGAL_OWNER + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_MAX_ACCOUNTS_EXCEEDED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED + | typeof SOLANA_ERROR__INSTRUCTION_ERROR_BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS + > & { + [SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED]: { + currentBlockHeight: bigint; + lastValidBlockHeight: bigint; + }; + [SOLANA_ERROR__TRANSACTION_MISSING_SIGNATURES]: { + addresses: string[]; + }; + [SOLANA_ERROR__RPC_INTEGER_OVERFLOW]: { + argumentLabel: string; + keyPath: readonly (string | number | symbol)[]; + methodName: string; + optionalPathLabel: string; + path?: string; + value: bigint; + }; + [SOLANA_ERROR__INVALID_KEYPAIR_BYTES]: { + byteLength: number; + }; + [SOLANA_ERROR__NONCE_ACCOUNT_NOT_FOUND]: { + nonceAccountAddress: string; + }; + [SOLANA_ERROR__NONCE_INVALID]: { + actualNonceValue: string; + expectedNonceValue: string; + }; + [SOLANA_ERROR__TRANSACTION_ERROR_UNKNOWN]: { + errorName: string; + transactionErrorContext?: unknown; + }; + [SOLANA_ERROR__TRANSACTION_ERROR_DUPLICATE_INSTRUCTION]: { + index: number; + }; + [SOLANA_ERROR__TRANSACTION_ERROR_INSUFFICIENT_FUNDS_FOR_RENT]: { + accountIndex: number; + }; + [SOLANA_ERROR__TRANSACTION_ERROR_PROGRAM_EXECUTION_TEMPORARILY_RESTRICTED]: { + accountIndex: number; + }; + [SOLANA_ERROR__TRANSACTION_ERROR_DUPLICATE_INSTRUCTION]: { + index: number; + }; + [SOLANA_ERROR__TRANSACTION_ERROR_INSUFFICIENT_FUNDS_FOR_RENT]: { + accountIndex: number; + }; + [SOLANA_ERROR__TRANSACTION_ERROR_PROGRAM_EXECUTION_TEMPORARILY_RESTRICTED]: { + accountIndex: number; + }; + [SOLANA_ERROR__INSTRUCTION_ERROR_UNKNOWN]: { + errorName: string; + index: number; + instructionErrorContext?: unknown; + }; + [SOLANA_ERROR__INSTRUCTION_ERROR_BORSH_IO_ERROR]: { + encodedData: string; + index: number; + }; + [SOLANA_ERROR__INSTRUCTION_ERROR_CUSTOM]: { + code: number; + index: number; + }; + } +>; diff --git a/packages/errors/src/index.ts b/packages/errors/src/index.ts index fa7fcbc32aba..bba62edff7a4 100644 --- a/packages/errors/src/index.ts +++ b/packages/errors/src/index.ts @@ -1,3 +1,4 @@ export * from './codes'; export * from './error'; +export * from './instruction-error'; export * from './transaction-error'; diff --git a/packages/errors/src/instruction-error.ts b/packages/errors/src/instruction-error.ts new file mode 100644 index 000000000000..969581b32aa2 --- /dev/null +++ b/packages/errors/src/instruction-error.ts @@ -0,0 +1,101 @@ +import { + SOLANA_ERROR__INSTRUCTION_ERROR_BORSH_IO_ERROR, + SOLANA_ERROR__INSTRUCTION_ERROR_CUSTOM, + SOLANA_ERROR__INSTRUCTION_ERROR_UNKNOWN, +} from './codes'; +import { SolanaError } from './error'; +import { getSolanaErrorFromRpcError } from './rpc-enum-errors'; + +const ORDERED_ERROR_NAMES = [ + // Keep synced with RPC source: https://github.com/anza-xyz/agave/blob/master/sdk/program/src/instruction.rs + // If this list ever gets too large, consider implementing a compression strategy like this: + // https://gist.github.com/steveluscher/aaa7cbbb5433b1197983908a40860c47 + 'GenericError', + 'InvalidArgument', + 'InvalidInstructionData', + 'InvalidAccountData', + 'AccountDataTooSmall', + 'InsufficientFunds', + 'IncorrectProgramId', + 'MissingRequiredSignature', + 'AccountAlreadyInitialized', + 'UninitializedAccount', + 'UnbalancedInstruction', + 'ModifiedProgramId', + 'ExternalAccountLamportSpend', + 'ExternalAccountDataModified', + 'ReadonlyLamportChange', + 'ReadonlyDataModified', + 'DuplicateAccountIndex', + 'ExecutableModified', + 'RentEpochModified', + 'NotEnoughAccountKeys', + 'AccountDataSizeChanged', + 'AccountNotExecutable', + 'AccountBorrowFailed', + 'AccountBorrowOutstanding', + 'DuplicateAccountOutOfSync', + 'Custom', + 'InvalidError', + 'ExecutableDataModified', + 'ExecutableLamportChange', + 'ExecutableAccountNotRentExempt', + 'UnsupportedProgramId', + 'CallDepth', + 'MissingAccount', + 'ReentrancyNotAllowed', + 'MaxSeedLengthExceeded', + 'InvalidSeeds', + 'InvalidRealloc', + 'ComputationalBudgetExceeded', + 'PrivilegeEscalation', + 'ProgramEnvironmentSetupFailure', + 'ProgramFailedToComplete', + 'ProgramFailedToCompile', + 'Immutable', + 'IncorrectAuthority', + 'BorshIoError', + 'AccountNotRentExempt', + 'InvalidAccountOwner', + 'ArithmeticOverflow', + 'UnsupportedSysvar', + 'IllegalOwner', + 'MaxAccountsDataAllocationsExceeded', + 'MaxAccountsExceeded', + 'MaxInstructionTraceLengthExceeded', + 'BuiltinProgramsMustConsumeComputeUnits', +]; + +export function getSolanaErrorFromInstructionError( + index: number, + instructionError: string | { [key: string]: unknown }, +): SolanaError { + return getSolanaErrorFromRpcError( + { + errorCodeBaseOffset: 4615001, + getErrorContext(errorCode, rpcErrorName, rpcErrorContext) { + if (errorCode === SOLANA_ERROR__INSTRUCTION_ERROR_UNKNOWN) { + return { + errorName: rpcErrorName, + index, + ...(rpcErrorContext !== undefined ? { instructionErrorContext: rpcErrorContext } : null), + }; + } else if (errorCode === SOLANA_ERROR__INSTRUCTION_ERROR_CUSTOM) { + return { + code: rpcErrorContext as number, + index, + }; + } else if (errorCode === SOLANA_ERROR__INSTRUCTION_ERROR_BORSH_IO_ERROR) { + return { + encodedData: rpcErrorContext as string, + index, + }; + } + return { index }; + }, + orderedErrorNames: ORDERED_ERROR_NAMES, + rpcEnumError: instructionError, + }, + getSolanaErrorFromInstructionError, + ); +} diff --git a/packages/errors/src/messages.ts b/packages/errors/src/messages.ts index 317808116e7a..f96c09f89382 100644 --- a/packages/errors/src/messages.ts +++ b/packages/errors/src/messages.ts @@ -1,5 +1,60 @@ import { SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, + SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_ALREADY_INITIALIZED, + SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_BORROW_FAILED, + SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_BORROW_OUTSTANDING, + SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_DATA_SIZE_CHANGED, + SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_DATA_TOO_SMALL, + SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_NOT_EXECUTABLE, + SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_NOT_RENT_EXEMPT, + SOLANA_ERROR__INSTRUCTION_ERROR_ARITHMETIC_OVERFLOW, + SOLANA_ERROR__INSTRUCTION_ERROR_BORSH_IO_ERROR, + SOLANA_ERROR__INSTRUCTION_ERROR_BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS, + SOLANA_ERROR__INSTRUCTION_ERROR_CALL_DEPTH, + SOLANA_ERROR__INSTRUCTION_ERROR_COMPUTATIONAL_BUDGET_EXCEEDED, + SOLANA_ERROR__INSTRUCTION_ERROR_CUSTOM, + SOLANA_ERROR__INSTRUCTION_ERROR_DUPLICATE_ACCOUNT_INDEX, + SOLANA_ERROR__INSTRUCTION_ERROR_DUPLICATE_ACCOUNT_OUT_OF_SYNC, + SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_ACCOUNT_NOT_RENT_EXEMPT, + SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_DATA_MODIFIED, + SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_LAMPORT_CHANGE, + SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_MODIFIED, + SOLANA_ERROR__INSTRUCTION_ERROR_EXTERNAL_ACCOUNT_DATA_MODIFIED, + SOLANA_ERROR__INSTRUCTION_ERROR_EXTERNAL_ACCOUNT_LAMPORT_SPEND, + SOLANA_ERROR__INSTRUCTION_ERROR_GENERIC_ERROR, + SOLANA_ERROR__INSTRUCTION_ERROR_ILLEGAL_OWNER, + SOLANA_ERROR__INSTRUCTION_ERROR_IMMUTABLE, + SOLANA_ERROR__INSTRUCTION_ERROR_INCORRECT_AUTHORITY, + SOLANA_ERROR__INSTRUCTION_ERROR_INCORRECT_PROGRAM_ID, + SOLANA_ERROR__INSTRUCTION_ERROR_INSUFFICIENT_FUNDS, + SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ACCOUNT_DATA, + SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ACCOUNT_OWNER, + SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ARGUMENT, + SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ERROR, + SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_INSTRUCTION_DATA, + SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_REALLOC, + SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_SEEDS, + SOLANA_ERROR__INSTRUCTION_ERROR_MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED, + SOLANA_ERROR__INSTRUCTION_ERROR_MAX_ACCOUNTS_EXCEEDED, + SOLANA_ERROR__INSTRUCTION_ERROR_MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED, + SOLANA_ERROR__INSTRUCTION_ERROR_MAX_SEED_LENGTH_EXCEEDED, + SOLANA_ERROR__INSTRUCTION_ERROR_MISSING_ACCOUNT, + SOLANA_ERROR__INSTRUCTION_ERROR_MISSING_REQUIRED_SIGNATURE, + SOLANA_ERROR__INSTRUCTION_ERROR_MODIFIED_PROGRAM_ID, + SOLANA_ERROR__INSTRUCTION_ERROR_NOT_ENOUGH_ACCOUNT_KEYS, + SOLANA_ERROR__INSTRUCTION_ERROR_PRIVILEGE_ESCALATION, + SOLANA_ERROR__INSTRUCTION_ERROR_PROGRAM_ENVIRONMENT_SETUP_FAILURE, + SOLANA_ERROR__INSTRUCTION_ERROR_PROGRAM_FAILED_TO_COMPILE, + SOLANA_ERROR__INSTRUCTION_ERROR_PROGRAM_FAILED_TO_COMPLETE, + SOLANA_ERROR__INSTRUCTION_ERROR_READONLY_DATA_MODIFIED, + SOLANA_ERROR__INSTRUCTION_ERROR_READONLY_LAMPORT_CHANGE, + SOLANA_ERROR__INSTRUCTION_ERROR_REENTRANCY_NOT_ALLOWED, + SOLANA_ERROR__INSTRUCTION_ERROR_RENT_EPOCH_MODIFIED, + SOLANA_ERROR__INSTRUCTION_ERROR_UNBALANCED_INSTRUCTION, + SOLANA_ERROR__INSTRUCTION_ERROR_UNINITIALIZED_ACCOUNT, + SOLANA_ERROR__INSTRUCTION_ERROR_UNKNOWN, + SOLANA_ERROR__INSTRUCTION_ERROR_UNSUPPORTED_PROGRAM_ID, + SOLANA_ERROR__INSTRUCTION_ERROR_UNSUPPORTED_SYSVAR, SOLANA_ERROR__INVALID_KEYPAIR_BYTES, SOLANA_ERROR__NONCE_ACCOUNT_NOT_FOUND, SOLANA_ERROR__NONCE_INVALID, @@ -60,6 +115,76 @@ export const SolanaErrorMessages: Readonly<{ }> = { [SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED]: 'The network has progressed past the last block for which this transaction could have been committed.', + [SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_ALREADY_INITIALIZED]: 'instruction requires an uninitialized account', + [SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_BORROW_FAILED]: + 'instruction tries to borrow reference for an account which is already borrowed', + [SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_BORROW_OUTSTANDING]: + 'instruction left account with an outstanding borrowed reference', + [SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_DATA_SIZE_CHANGED]: + "program other than the account's owner changed the size of the account data", + [SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_DATA_TOO_SMALL]: 'account data too small for instruction', + [SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_NOT_EXECUTABLE]: 'instruction expected an executable account', + [SOLANA_ERROR__INSTRUCTION_ERROR_ACCOUNT_NOT_RENT_EXEMPT]: + 'An account does not have enough lamports to be rent-exempt', + [SOLANA_ERROR__INSTRUCTION_ERROR_ARITHMETIC_OVERFLOW]: 'Program arithmetic overflowed', + [SOLANA_ERROR__INSTRUCTION_ERROR_BORSH_IO_ERROR]: 'Failed to serialize or deserialize account data: $encodedData', + [SOLANA_ERROR__INSTRUCTION_ERROR_BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS]: + 'Builtin programs must consume compute units', + [SOLANA_ERROR__INSTRUCTION_ERROR_CALL_DEPTH]: 'Cross-program invocation call depth too deep', + [SOLANA_ERROR__INSTRUCTION_ERROR_COMPUTATIONAL_BUDGET_EXCEEDED]: 'Computational budget exceeded', + [SOLANA_ERROR__INSTRUCTION_ERROR_CUSTOM]: 'custom program error: #$code', + [SOLANA_ERROR__INSTRUCTION_ERROR_DUPLICATE_ACCOUNT_INDEX]: 'instruction contains duplicate accounts', + [SOLANA_ERROR__INSTRUCTION_ERROR_DUPLICATE_ACCOUNT_OUT_OF_SYNC]: + 'instruction modifications of multiply-passed account differ', + [SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_ACCOUNT_NOT_RENT_EXEMPT]: 'executable accounts must be rent exempt', + [SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_DATA_MODIFIED]: 'instruction changed executable accounts data', + [SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_LAMPORT_CHANGE]: + 'instruction changed the balance of an executable account', + [SOLANA_ERROR__INSTRUCTION_ERROR_EXECUTABLE_MODIFIED]: 'instruction changed executable bit of an account', + [SOLANA_ERROR__INSTRUCTION_ERROR_EXTERNAL_ACCOUNT_DATA_MODIFIED]: + 'instruction modified data of an account it does not own', + [SOLANA_ERROR__INSTRUCTION_ERROR_EXTERNAL_ACCOUNT_LAMPORT_SPEND]: + 'instruction spent from the balance of an account it does not own', + [SOLANA_ERROR__INSTRUCTION_ERROR_GENERIC_ERROR]: 'generic instruction error', + [SOLANA_ERROR__INSTRUCTION_ERROR_ILLEGAL_OWNER]: 'Provided owner is not allowed', + [SOLANA_ERROR__INSTRUCTION_ERROR_IMMUTABLE]: 'Account is immutable', + [SOLANA_ERROR__INSTRUCTION_ERROR_INCORRECT_AUTHORITY]: 'Incorrect authority provided', + [SOLANA_ERROR__INSTRUCTION_ERROR_INCORRECT_PROGRAM_ID]: 'incorrect program id for instruction', + [SOLANA_ERROR__INSTRUCTION_ERROR_INSUFFICIENT_FUNDS]: 'insufficient funds for instruction', + [SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ACCOUNT_DATA]: 'invalid account data for instruction', + [SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ACCOUNT_OWNER]: 'Invalid account owner', + [SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ARGUMENT]: 'invalid program argument', + [SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_ERROR]: 'program returned invalid error code', + [SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_INSTRUCTION_DATA]: 'invalid instruction data', + [SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_REALLOC]: 'Failed to reallocate account data', + [SOLANA_ERROR__INSTRUCTION_ERROR_INVALID_SEEDS]: 'Provided seeds do not result in a valid address', + [SOLANA_ERROR__INSTRUCTION_ERROR_MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED]: + 'Accounts data allocations exceeded the maximum allowed per transaction', + [SOLANA_ERROR__INSTRUCTION_ERROR_MAX_ACCOUNTS_EXCEEDED]: 'Max accounts exceeded', + [SOLANA_ERROR__INSTRUCTION_ERROR_MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED]: 'Max instruction trace length exceeded', + [SOLANA_ERROR__INSTRUCTION_ERROR_MAX_SEED_LENGTH_EXCEEDED]: 'Length of the seed is too long for address generation', + [SOLANA_ERROR__INSTRUCTION_ERROR_MISSING_ACCOUNT]: 'An account required by the instruction is missing', + [SOLANA_ERROR__INSTRUCTION_ERROR_MISSING_REQUIRED_SIGNATURE]: 'missing required signature for instruction', + [SOLANA_ERROR__INSTRUCTION_ERROR_MODIFIED_PROGRAM_ID]: + 'instruction illegally modified the program id of an account', + [SOLANA_ERROR__INSTRUCTION_ERROR_NOT_ENOUGH_ACCOUNT_KEYS]: 'insufficient account keys for instruction', + [SOLANA_ERROR__INSTRUCTION_ERROR_PRIVILEGE_ESCALATION]: + 'Cross-program invocation with unauthorized signer or writable account', + [SOLANA_ERROR__INSTRUCTION_ERROR_PROGRAM_ENVIRONMENT_SETUP_FAILURE]: + 'Failed to create program execution environment', + [SOLANA_ERROR__INSTRUCTION_ERROR_PROGRAM_FAILED_TO_COMPILE]: 'Program failed to compile', + [SOLANA_ERROR__INSTRUCTION_ERROR_PROGRAM_FAILED_TO_COMPLETE]: 'Program failed to complete', + [SOLANA_ERROR__INSTRUCTION_ERROR_READONLY_DATA_MODIFIED]: 'instruction modified data of a read-only account', + [SOLANA_ERROR__INSTRUCTION_ERROR_READONLY_LAMPORT_CHANGE]: 'instruction changed the balance of a read-only account', + [SOLANA_ERROR__INSTRUCTION_ERROR_REENTRANCY_NOT_ALLOWED]: + 'Cross-program invocation reentrancy not allowed for this instruction', + [SOLANA_ERROR__INSTRUCTION_ERROR_RENT_EPOCH_MODIFIED]: 'instruction modified rent epoch of an account', + [SOLANA_ERROR__INSTRUCTION_ERROR_UNBALANCED_INSTRUCTION]: + 'sum of account balances before and after instruction do not match', + [SOLANA_ERROR__INSTRUCTION_ERROR_UNINITIALIZED_ACCOUNT]: 'instruction requires an initialized account', + [SOLANA_ERROR__INSTRUCTION_ERROR_UNKNOWN]: '', + [SOLANA_ERROR__INSTRUCTION_ERROR_UNSUPPORTED_PROGRAM_ID]: 'Unsupported program id', + [SOLANA_ERROR__INSTRUCTION_ERROR_UNSUPPORTED_SYSVAR]: 'Unsupported sysvar', [SOLANA_ERROR__INVALID_KEYPAIR_BYTES]: 'Key pair bytes must be of length 64, got $byteLength.', [SOLANA_ERROR__NONCE_ACCOUNT_NOT_FOUND]: 'No nonce account could be found at address `$nonceAccountAddress`', [SOLANA_ERROR__NONCE_INVALID]: diff --git a/packages/errors/src/transaction-error.ts b/packages/errors/src/transaction-error.ts index 8a9e9a3cf870..939dfba9cb70 100644 --- a/packages/errors/src/transaction-error.ts +++ b/packages/errors/src/transaction-error.ts @@ -5,6 +5,7 @@ import { SOLANA_ERROR__TRANSACTION_ERROR_UNKNOWN, } from './codes'; import { SolanaError } from './error'; +import { getSolanaErrorFromInstructionError } from './instruction-error'; import { getSolanaErrorFromRpcError } from './rpc-enum-errors'; /** @@ -26,7 +27,7 @@ const ORDERED_ERROR_NAMES = [ 'InvalidAccountForFee', 'AlreadyProcessed', 'BlockhashNotFound', - // `InstructionError` intentionally omitted + // `InstructionError` intentionally omitted; delegated to `getSolanaErrorFromInstructionError` 'CallChainTooDeep', 'MissingSignatureForFee', 'InvalidAccountIndex', @@ -58,6 +59,11 @@ const ORDERED_ERROR_NAMES = [ ]; 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,