Files
pump-parser/error.go

330 lines
10 KiB
Go
Raw Permalink Normal View History

2025-12-22 17:56:40 +08:00
package pump_parser
2025-11-20 17:56:45 +08:00
import (
"encoding/binary"
"errors"
)
type TransactionErrorVariant uint32
type InstructionErrorVariant uint32
type InstructionErrorEnum struct {
Index uint8
Variant InstructionErrorVariant
rest []byte
}
type CustomInstructionErrorEnum struct {
Code uint32
}
const (
AccountInUse TransactionErrorVariant = iota
AccountLoadedTwice
AccountNotFound
ProgramAccountNotFound
InsufficientFundsForFee
InvalidAccountForFee
AlreadyProcessed
BlockhashNotFound
InstructionError //InstructionError(u8, InstructionError),
CallChainTooDeep
MissingSignatureForFee
InvalidAccountIndex
SignatureFailure
InvalidProgramForExecution
SanitizeFailure
ClusterMaintenance
AccountBorrowOutstanding
WouldExceedMaxBlockCostLimit
UnsupportedVersion
InvalidWritableAccount
WouldExceedMaxAccountCostLimit
WouldExceedAccountDataBlockLimit
TooManyAccountLocks
AddressLookupTableNotFound
InvalidAddressLookupTableOwner
InvalidAddressLookupTableData
InvalidAddressLookupTableIndex
InvalidRentPayingAccount
WouldExceedMaxVoteCostLimit
WouldExceedAccountDataTotalLimit
DuplicateInstruction //DuplicateInstruction(u8),
/*
InsufficientFundsForRent {
account_index: u8,
},
*/
InsufficientFundsForRent
MaxLoadedAccountsDataSizeExceeded
InvalidLoadedAccountsDataSizeLimit
ResanitizationNeeded
/*
ProgramExecutionTemporarilyRestricted {
account_index: u8,
},
*/
ProgramExecutionTemporarilyRestricted
UnbalancedTransaction
ProgramCacheHitMaxLimit
CommitCancelled
)
const (
GenericError InstructionErrorVariant = iota
2026-02-26 16:11:34 +08:00
// InvalidArgument / The arguments provided to a program were invalid
2025-11-20 17:56:45 +08:00
InvalidArgument
2026-02-26 16:11:34 +08:00
// InvalidInstructionData / An instruction's data contents were invalid
2025-11-20 17:56:45 +08:00
InvalidInstructionData
2026-02-26 16:11:34 +08:00
// InvalidAccountData / An account's data contents was invalid
2025-11-20 17:56:45 +08:00
InvalidAccountData
2026-02-26 16:11:34 +08:00
// AccountDataTooSmall / An account's data was too small
2025-11-20 17:56:45 +08:00
AccountDataTooSmall
2026-02-26 16:11:34 +08:00
// InsufficientFunds / An account's balance was too small to complete the instruction
2025-11-20 17:56:45 +08:00
InsufficientFunds
2026-02-26 16:11:34 +08:00
// IncorrectProgramId / The account did not have the expected program id
2025-11-20 17:56:45 +08:00
IncorrectProgramId
2026-02-26 16:11:34 +08:00
// MissingRequiredSignature / A signature was required but not found
2025-11-20 17:56:45 +08:00
MissingRequiredSignature
2026-02-26 16:11:34 +08:00
// AccountAlreadyInitialized / An initialize instruction was sent to an account that has already been initialized.
2025-11-20 17:56:45 +08:00
AccountAlreadyInitialized
2026-02-26 16:11:34 +08:00
// UninitializedAccount / An attempt to operate on an account that hasn't been initialized.
2025-11-20 17:56:45 +08:00
UninitializedAccount
2026-02-26 16:11:34 +08:00
// UnbalancedInstruction / Program's instruction lamport balance does not equal the balance after the instruction
2025-11-20 17:56:45 +08:00
UnbalancedInstruction
2026-02-26 16:11:34 +08:00
// ModifiedProgramId / Program illegally modified an account's program id
2025-11-20 17:56:45 +08:00
ModifiedProgramId
2026-02-26 16:11:34 +08:00
// ExternalAccountLamportSpend / Program spent the lamports of an account that doesn't belong to it
2025-11-20 17:56:45 +08:00
ExternalAccountLamportSpend
2026-02-26 16:11:34 +08:00
// ExternalAccountDataModified / Program modified the data of an account that doesn't belong to it
2025-11-20 17:56:45 +08:00
ExternalAccountDataModified
2026-02-26 16:11:34 +08:00
// ReadonlyLamportChange / Read-only account's lamports modified
2025-11-20 17:56:45 +08:00
ReadonlyLamportChange
2026-02-26 16:11:34 +08:00
// ReadonlyDataModified / Read-only account's data was modified
2025-11-20 17:56:45 +08:00
ReadonlyDataModified
2026-02-26 16:11:34 +08:00
// DuplicateAccountIndex / An account was referenced more than once in a single instruction
2025-11-20 17:56:45 +08:00
// Deprecated, instructions can now contain duplicate accounts
DuplicateAccountIndex
2026-02-26 16:11:34 +08:00
// ExecutableModified / Executable bit on account changed, but shouldn't have
2025-11-20 17:56:45 +08:00
ExecutableModified
2026-02-26 16:11:34 +08:00
// RentEpochModified / Rent_epoch account changed, but shouldn't have
2025-11-20 17:56:45 +08:00
RentEpochModified
2026-02-26 16:11:34 +08:00
// NotEnoughAccountKeys / The instruction expected additional account keys
2025-11-20 17:56:45 +08:00
NotEnoughAccountKeys
2026-02-26 16:11:34 +08:00
// AccountDataSizeChanged / Program other than the account's owner changed the size of the account data
2025-11-20 17:56:45 +08:00
AccountDataSizeChanged
2026-02-26 16:11:34 +08:00
// AccountNotExecutable / The instruction expected an executable account
2025-11-20 17:56:45 +08:00
AccountNotExecutable
2026-02-26 16:11:34 +08:00
// AccountBorrowFailed / Failed to borrow a reference to account data, already borrowed
2025-11-20 17:56:45 +08:00
AccountBorrowFailed
2026-02-26 16:11:34 +08:00
// InstructionAccountBorrowOutstanding / Account data has an outstanding reference after a program's execution
2025-11-20 17:56:45 +08:00
InstructionAccountBorrowOutstanding
2026-02-26 16:11:34 +08:00
// DuplicateAccountOutOfSync / The same account was multiply passed to an on-chain program's entrypoint, but the program
2025-11-20 17:56:45 +08:00
/// modified them differently. A program can only modify one instance of the account because
/// the runtime cannot determine which changes to pick or how to merge them if both are modified
DuplicateAccountOutOfSync
/// Allows on-chain programs to implement program-specific error types and see them returned
/// by the Solana runtime. A program-specific error may be any type that is represented as
/// or serialized to a u32 integer.
Custom // Custom(u32),
2026-02-26 16:11:34 +08:00
// InvalidError / The return value from the program was invalid. Valid errors are either a defined builtin
2025-11-20 17:56:45 +08:00
/// error value or a user-defined error in the lower 32 bits.
InvalidError
2026-02-26 16:11:34 +08:00
// ExecutableDataModified / Executable account's data was modified
2025-11-20 17:56:45 +08:00
ExecutableDataModified
2026-02-26 16:11:34 +08:00
// ExecutableLamportChange / Executable account's lamports modified
2025-11-20 17:56:45 +08:00
ExecutableLamportChange
2026-02-26 16:11:34 +08:00
// ExecutableAccountNotRentExempt / Executable accounts must be rent exempt
2025-11-20 17:56:45 +08:00
ExecutableAccountNotRentExempt
2026-02-26 16:11:34 +08:00
// UnsupportedProgramId / Unsupported program id
2025-11-20 17:56:45 +08:00
UnsupportedProgramId
2026-02-26 16:11:34 +08:00
// CallDepth / Cross-program invocation call depth too deep
2025-11-20 17:56:45 +08:00
CallDepth
2026-02-26 16:11:34 +08:00
// MissingAccount / An account required by the instruction is missing
2025-11-20 17:56:45 +08:00
MissingAccount
2026-02-26 16:11:34 +08:00
// ReentrancyNotAllowed / Cross-program invocation reentrancy not allowed for this instruction
2025-11-20 17:56:45 +08:00
ReentrancyNotAllowed
2026-02-26 16:11:34 +08:00
// MaxSeedLengthExceeded / Length of the seed is too long for address generation
2025-11-20 17:56:45 +08:00
MaxSeedLengthExceeded
2026-02-26 16:11:34 +08:00
// InvalidSeeds / Provided seeds do not result in a valid address
2025-11-20 17:56:45 +08:00
InvalidSeeds
2026-02-26 16:11:34 +08:00
// InvalidRealloc / Failed to reallocate account data of this length
2025-11-20 17:56:45 +08:00
InvalidRealloc
2026-02-26 16:11:34 +08:00
// ComputationalBudgetExceeded / Computational budget exceeded
2025-11-20 17:56:45 +08:00
ComputationalBudgetExceeded
2026-02-26 16:11:34 +08:00
// PrivilegeEscalation / Cross-program invocation with unauthorized signer or writable account
2025-11-20 17:56:45 +08:00
PrivilegeEscalation
2026-02-26 16:11:34 +08:00
// ProgramEnvironmentSetupFailure / Failed to create program execution environment
2025-11-20 17:56:45 +08:00
ProgramEnvironmentSetupFailure
2026-02-26 16:11:34 +08:00
// ProgramFailedToComplete / Program failed to complete
2025-11-20 17:56:45 +08:00
ProgramFailedToComplete
2026-02-26 16:11:34 +08:00
// ProgramFailedToCompile / Program failed to compile
2025-11-20 17:56:45 +08:00
ProgramFailedToCompile
2026-02-26 16:11:34 +08:00
// Immutable / Account is immutable
2025-11-20 17:56:45 +08:00
Immutable
2026-02-26 16:11:34 +08:00
// IncorrectAuthority / Incorrect authority provided
2025-11-20 17:56:45 +08:00
IncorrectAuthority
/// Failed to serialize or deserialize account data
///
/// Warning: This error should never be emitted by the runtime.
///
/// This error includes strings from the underlying 3rd party Borsh crate
/// which can be dangerous because the error strings could change across
/// Borsh versions. Only programs can use this error because they are
/// consistent across Solana software versions.
/// string values from this error should not be used in
BorshIoError // BorshIoError(String)
2026-02-26 16:11:34 +08:00
// AccountNotRentExempt An account does not have enough lamports to be rent-exempt
2025-11-20 17:56:45 +08:00
AccountNotRentExempt
2026-02-26 16:11:34 +08:00
// InvalidAccountOwner Invalid account owner
2025-11-20 17:56:45 +08:00
InvalidAccountOwner
2026-02-26 16:11:34 +08:00
// ArithmeticOverflow Program arithmetic overflowed
2025-11-20 17:56:45 +08:00
ArithmeticOverflow
2026-02-26 16:11:34 +08:00
// UnsupportedSysvar Unsupported sysvar
2025-11-20 17:56:45 +08:00
UnsupportedSysvar
2026-02-26 16:11:34 +08:00
// IllegalOwner Illegal account owner
2025-11-20 17:56:45 +08:00
IllegalOwner
2026-02-26 16:11:34 +08:00
// MaxAccountsDataAllocationsExceeded / Accounts data allocations exceeded the maximum allowed per transaction
2025-11-20 17:56:45 +08:00
MaxAccountsDataAllocationsExceeded
2026-02-26 16:11:34 +08:00
// MaxAccountsExceeded Max accounts exceeded
2025-11-20 17:56:45 +08:00
MaxAccountsExceeded
2026-02-26 16:11:34 +08:00
// MaxInstructionTraceLengthExceeded Max instruction trace length exceeded
2025-11-20 17:56:45 +08:00
MaxInstructionTraceLengthExceeded
2026-02-26 16:11:34 +08:00
// BuiltinProgramsMustConsumeComputeUnits Builtin programs must consume compute units
2025-11-20 17:56:45 +08:00
BuiltinProgramsMustConsumeComputeUnits
)
type TransactionError struct {
Variant TransactionErrorVariant
rest []byte
}
2026-02-26 16:11:34 +08:00
type TransactionParsedError struct {
Index uint8
Variant TransactionErrorVariant
Enum InstructionErrorVariant
CustomCode uint32
UnKnown string
}
2025-11-20 17:56:45 +08:00
var (
ErrInvalidTransactionError = errors.New("invalid transaction error")
NotAnInstructionError = errors.New("not an instruction error")
NotACustomInstructionError = errors.New("not a custom instruction error")
UnsupportedInstructionError = errors.New("unsupported instruction error")
)
func DecodeTransactionError(data []byte) (*TransactionError, error) {
if len(data) < 4 {
return nil, ErrInvalidTransactionError
}
var err TransactionError
variant := binary.LittleEndian.Uint32(data[:4])
if variant > uint32(CommitCancelled) {
return nil, UnsupportedInstructionError
}
err.Variant = TransactionErrorVariant(variant)
err.rest = data[4:]
return &err, nil
}
2026-02-26 16:11:34 +08:00
func ParseTransactionErrorFromGeyser(data []byte) *TransactionParsedError {
if len(data) == 0 {
return nil
}
transactionError, err := DecodeTransactionError(data)
if err != nil {
return &TransactionParsedError{
UnKnown: string(data),
}
}
enumErr, err := transactionError.GetInstructionError()
if err != nil {
return &TransactionParsedError{
Variant: transactionError.Variant,
UnKnown: string(data),
}
}
if enumErr.Variant != Custom {
return &TransactionParsedError{
Index: enumErr.Index,
Variant: transactionError.Variant,
Enum: enumErr.Variant,
}
}
customCode, err := enumErr.Custom()
if err != nil {
return &TransactionParsedError{
Index: enumErr.Index,
Variant: transactionError.Variant,
Enum: enumErr.Variant,
UnKnown: string(data),
}
}
return &TransactionParsedError{
Index: enumErr.Index,
Variant: transactionError.Variant,
Enum: enumErr.Variant,
CustomCode: customCode.Code,
}
}
2025-11-20 17:56:45 +08:00
func (e *TransactionError) GetCustomErrorCode() (uint8, uint32, error) {
instr, err := e.GetInstructionError()
if err != nil {
return 0, 0, err
}
custom, err := instr.Custom()
if err != nil {
return 0, 0, err
}
return instr.Index, custom.Code, nil
}
func (e *TransactionError) GetInstructionError() (*InstructionErrorEnum, error) {
if e.Variant != InstructionError {
return nil, NotAnInstructionError
}
if len(e.rest) < 5 {
return nil, NotAnInstructionError
}
var err InstructionErrorEnum
err.Index = e.rest[0]
variant := binary.LittleEndian.Uint32(e.rest[1:5])
if variant > uint32(BuiltinProgramsMustConsumeComputeUnits) {
return nil, UnsupportedInstructionError
}
err.Variant = InstructionErrorVariant(binary.LittleEndian.Uint32(e.rest[1:5]))
err.rest = e.rest[5:]
return &err, nil
}
func (e *InstructionErrorEnum) Custom() (*CustomInstructionErrorEnum, error) {
if e.Variant != Custom {
return nil, NotACustomInstructionError
}
if len(e.rest) < 4 {
return nil, NotACustomInstructionError
}
var custom CustomInstructionErrorEnum
custom.Code = binary.LittleEndian.Uint32(e.rest[:4])
return &custom, nil
}