package pump_parser import ( "errors" "log" "github.com/gagliardetto/solana-go" "github.com/shopspring/decimal" ) var defaultSwapPrograms = map[solana.PublicKey]swapParser{ pumpAmmProgram: pumpAmmParser, pumpProgram: pumpParser, } var swapPrograms = cloneSwapPrograms(defaultSwapPrograms) type ParserOption func(*parserConfig) type parserConfig struct { enableMeteoraDlmm bool } func InitParser(opts ...ParserOption) { cfg := parserConfig{} for _, opt := range opts { opt(&cfg) } programs := cloneSwapPrograms(defaultSwapPrograms) if cfg.enableMeteoraDlmm { programs[meteoraDlmmProgram] = metaoradlmmParser } swapPrograms = programs } func WithMeteoraDlmm() ParserOption { return func(cfg *parserConfig) { cfg.enableMeteoraDlmm = true } } var actionPrograms = map[solana.PublicKey]actionParser{ systemProgram: systemParser, budgGetProgram: budgetParser, } func ParseRawTx(rawTx *RawTx) (*Tx, error) { tx := &Tx{ rawTx: rawTx, } err := tx.Parser() if err != nil { return nil, err } return tx, nil } func (tx *Tx) Parser() error { if tx.rawTx == nil { return errors.New("rawTx is nil") } accountList := tx.rawTx.getAccountList() tx.TxHash = (*[64]byte)((tx.rawTx.Transaction.Signatures[0][:])) tx.Signer = tx.rawTx.GetSigner() tx.Block = tx.rawTx.Slot tx.BlockIndex = uint64(tx.rawTx.IndexWithinBlock) tx.BlockAt = tx.rawTx.BlockTime tx.CuFee = decimal.NewFromUint64(tx.rawTx.Meta.Fee) tx.BeforeSolBalance = decimal.NewFromUint64(tx.rawTx.Meta.PreBalances[0]).Div(decimal.NewFromInt(1e9)) tx.AfterSOLBalance = decimal.NewFromUint64(tx.rawTx.Meta.PostBalances[0]).Div(decimal.NewFromInt(1e9)) tx.Token = make(map[solana.PublicKey]TokenMeta) var innersMap = make(map[int]InnerInstructions) for _, inner := range tx.rawTx.Meta.InnerInstructions { innersMap[inner.Index] = inner } for i, instr := range tx.rawTx.Transaction.Message.Instructions { programAccount := accountList[instr.ProgramIDIndex] if p, exists := swapPrograms[programAccount]; exists { swaps, _, err := p(tx, instr, innersMap[i], [2]uint{uint(i), uint(0)}) if err != nil { if errors.Is(err, InstructionIgnoredError) { continue } return err } tx.Swaps = append(tx.Swaps, swaps...) } else if p, exists := actionPrograms[programAccount]; exists { _, err := p(tx, instr, innersMap[i], [2]uint{uint(i), uint(0)}) if err != nil { if errors.Is(err, InstructionIgnoredError) { continue } return err } } else { ii := i // unknown program, parser inner instructions innerLength := len(innersMap[i].Instructions) for j := 1; j <= innerLength; { if j <= 0 || j > innerLength { log.Printf("inner instruction index is out if range, block: %d, tx: %s, outerIndex: %d, innerIndex: %d", tx.Block, tx.GetTxHash(), ii, j) break } innerInstr := innersMap[i].Instructions[j-1] innerProgramAccount := accountList[innerInstr.ProgramIDIndex] if p, exists := swapPrograms[innerProgramAccount]; exists { swaps, offset, err := p(tx, innerInstr, innersMap[i], [2]uint{uint(i), uint(j)}) if err != nil { if errors.Is(err, InstructionIgnoredError) { j = int(offset[1]) continue } return err } tx.Swaps = append(tx.Swaps, swaps...) j = int(offset[1]) ii = int(offset[0]) } else if p, exists := actionPrograms[innerProgramAccount]; exists { offset, err := p(tx, innerInstr, innersMap[i], [2]uint{uint(i), uint(j)}) if err != nil { if errors.Is(err, InstructionIgnoredError) { j = int(offset[1]) continue } return err } j = int(offset[1]) ii = int(offset[0]) } else { j++ } if j > innerLength || ii > i { break } } } } return nil } func cloneSwapPrograms(src map[solana.PublicKey]swapParser) map[solana.PublicKey]swapParser { dst := make(map[solana.PublicKey]swapParser, len(src)) for k, v := range src { dst[k] = v } return dst }