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 }