Files
pump-parser/error.go
2026-02-27 16:37:15 +08:00

330 lines
10 KiB
Go

package pump_parser
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
// InvalidArgument / The arguments provided to a program were invalid
InvalidArgument
// InvalidInstructionData / An instruction's data contents were invalid
InvalidInstructionData
// InvalidAccountData / An account's data contents was invalid
InvalidAccountData
// AccountDataTooSmall / An account's data was too small
AccountDataTooSmall
// InsufficientFunds / An account's balance was too small to complete the instruction
InsufficientFunds
// IncorrectProgramId / The account did not have the expected program id
IncorrectProgramId
// MissingRequiredSignature / A signature was required but not found
MissingRequiredSignature
// AccountAlreadyInitialized / An initialize instruction was sent to an account that has already been initialized.
AccountAlreadyInitialized
// UninitializedAccount / An attempt to operate on an account that hasn't been initialized.
UninitializedAccount
// UnbalancedInstruction / Program's instruction lamport balance does not equal the balance after the instruction
UnbalancedInstruction
// ModifiedProgramId / Program illegally modified an account's program id
ModifiedProgramId
// ExternalAccountLamportSpend / Program spent the lamports of an account that doesn't belong to it
ExternalAccountLamportSpend
// ExternalAccountDataModified / Program modified the data of an account that doesn't belong to it
ExternalAccountDataModified
// ReadonlyLamportChange / Read-only account's lamports modified
ReadonlyLamportChange
// ReadonlyDataModified / Read-only account's data was modified
ReadonlyDataModified
// DuplicateAccountIndex / An account was referenced more than once in a single instruction
// Deprecated, instructions can now contain duplicate accounts
DuplicateAccountIndex
// ExecutableModified / Executable bit on account changed, but shouldn't have
ExecutableModified
// RentEpochModified / Rent_epoch account changed, but shouldn't have
RentEpochModified
// NotEnoughAccountKeys / The instruction expected additional account keys
NotEnoughAccountKeys
// AccountDataSizeChanged / Program other than the account's owner changed the size of the account data
AccountDataSizeChanged
// AccountNotExecutable / The instruction expected an executable account
AccountNotExecutable
// AccountBorrowFailed / Failed to borrow a reference to account data, already borrowed
AccountBorrowFailed
// InstructionAccountBorrowOutstanding / Account data has an outstanding reference after a program's execution
InstructionAccountBorrowOutstanding
// DuplicateAccountOutOfSync / The same account was multiply passed to an on-chain program's entrypoint, but the program
/// 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),
// InvalidError / The return value from the program was invalid. Valid errors are either a defined builtin
/// error value or a user-defined error in the lower 32 bits.
InvalidError
// ExecutableDataModified / Executable account's data was modified
ExecutableDataModified
// ExecutableLamportChange / Executable account's lamports modified
ExecutableLamportChange
// ExecutableAccountNotRentExempt / Executable accounts must be rent exempt
ExecutableAccountNotRentExempt
// UnsupportedProgramId / Unsupported program id
UnsupportedProgramId
// CallDepth / Cross-program invocation call depth too deep
CallDepth
// MissingAccount / An account required by the instruction is missing
MissingAccount
// ReentrancyNotAllowed / Cross-program invocation reentrancy not allowed for this instruction
ReentrancyNotAllowed
// MaxSeedLengthExceeded / Length of the seed is too long for address generation
MaxSeedLengthExceeded
// InvalidSeeds / Provided seeds do not result in a valid address
InvalidSeeds
// InvalidRealloc / Failed to reallocate account data of this length
InvalidRealloc
// ComputationalBudgetExceeded / Computational budget exceeded
ComputationalBudgetExceeded
// PrivilegeEscalation / Cross-program invocation with unauthorized signer or writable account
PrivilegeEscalation
// ProgramEnvironmentSetupFailure / Failed to create program execution environment
ProgramEnvironmentSetupFailure
// ProgramFailedToComplete / Program failed to complete
ProgramFailedToComplete
// ProgramFailedToCompile / Program failed to compile
ProgramFailedToCompile
// Immutable / Account is immutable
Immutable
// IncorrectAuthority / Incorrect authority provided
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)
// AccountNotRentExempt An account does not have enough lamports to be rent-exempt
AccountNotRentExempt
// InvalidAccountOwner Invalid account owner
InvalidAccountOwner
// ArithmeticOverflow Program arithmetic overflowed
ArithmeticOverflow
// UnsupportedSysvar Unsupported sysvar
UnsupportedSysvar
// IllegalOwner Illegal account owner
IllegalOwner
// MaxAccountsDataAllocationsExceeded / Accounts data allocations exceeded the maximum allowed per transaction
MaxAccountsDataAllocationsExceeded
// MaxAccountsExceeded Max accounts exceeded
MaxAccountsExceeded
// MaxInstructionTraceLengthExceeded Max instruction trace length exceeded
MaxInstructionTraceLengthExceeded
// BuiltinProgramsMustConsumeComputeUnits Builtin programs must consume compute units
BuiltinProgramsMustConsumeComputeUnits
)
type TransactionError struct {
Variant TransactionErrorVariant
rest []byte
}
type TransactionParsedError struct {
Index uint8
Variant TransactionErrorVariant
Enum InstructionErrorVariant
CustomCode uint32
UnKnown string
}
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
}
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,
}
}
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
}