Files
pump-parser/parser.go

162 lines
4.1 KiB
Go
Raw Normal View History

2025-11-20 17:56:45 +08:00
package pump_parser
import (
"errors"
2026-01-02 10:42:44 +08:00
"log"
2025-11-20 17:56:45 +08:00
"github.com/gagliardetto/solana-go"
"github.com/shopspring/decimal"
)
2026-01-08 12:00:59 +08:00
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
}
2025-11-20 17:56:45 +08:00
}
var actionPrograms = map[solana.PublicKey]actionParser{
systemProgram: systemParser,
budgGetProgram: budgetParser,
}
2025-11-27 16:39:01 +08:00
func ParseRawTx(rawTx *RawTx) (*Tx, error) {
tx := &Tx{
rawTx: rawTx,
}
err := tx.Parser()
if err != nil {
return nil, err
}
return tx, nil
}
2025-11-21 12:01:44 +08:00
func (tx *Tx) Parser() error {
if tx.rawTx == nil {
return errors.New("rawTx is nil")
2025-11-20 17:56:45 +08:00
}
2025-11-21 12:01:44 +08:00
accountList := tx.rawTx.getAccountList()
2026-02-02 14:13:00 +08:00
if tx.rawTx.Meta.Err == nil {
for _, acc := range tx.rawTx.Transaction.Message.Instructions {
if accountList[acc.ProgramIDIndex] == solana.VoteProgramID {
tx.Vote = true
}
}
}
2025-11-21 12:01:44 +08:00
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
2026-01-05 11:46:54 +08:00
tx.CuFee = decimal.NewFromUint64(tx.rawTx.Meta.Fee)
2025-11-21 12:01:44 +08:00
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)
2025-11-20 17:56:45 +08:00
var innersMap = make(map[int]InnerInstructions)
2025-11-21 12:01:44 +08:00
for _, inner := range tx.rawTx.Meta.InnerInstructions {
2025-11-20 17:56:45 +08:00
innersMap[inner.Index] = inner
}
2025-11-21 12:01:44 +08:00
for i, instr := range tx.rawTx.Transaction.Message.Instructions {
2025-11-20 17:56:45 +08:00
programAccount := accountList[instr.ProgramIDIndex]
if p, exists := swapPrograms[programAccount]; exists {
2025-11-21 12:01:44 +08:00
swaps, _, err := p(tx, instr, innersMap[i], [2]uint{uint(i), uint(0)})
2025-11-20 17:56:45 +08:00
if err != nil {
if errors.Is(err, InstructionIgnoredError) {
continue
}
2025-11-21 12:01:44 +08:00
return err
2025-11-20 17:56:45 +08:00
}
tx.Swaps = append(tx.Swaps, swaps...)
} else if p, exists := actionPrograms[programAccount]; exists {
2025-11-21 12:01:44 +08:00
_, err := p(tx, instr, innersMap[i], [2]uint{uint(i), uint(0)})
2025-11-20 17:56:45 +08:00
if err != nil {
if errors.Is(err, InstructionIgnoredError) {
continue
}
2025-11-21 12:01:44 +08:00
return err
2025-11-20 17:56:45 +08:00
}
} else {
ii := i
// unknown program, parser inner instructions
innerLength := len(innersMap[i].Instructions)
for j := 1; j <= innerLength; {
2026-01-02 10:42:44 +08:00
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
}
2025-11-20 17:56:45 +08:00
innerInstr := innersMap[i].Instructions[j-1]
innerProgramAccount := accountList[innerInstr.ProgramIDIndex]
if p, exists := swapPrograms[innerProgramAccount]; exists {
2025-11-21 12:01:44 +08:00
swaps, offset, err := p(tx, innerInstr, innersMap[i], [2]uint{uint(i), uint(j)})
2025-11-20 17:56:45 +08:00
if err != nil {
if errors.Is(err, InstructionIgnoredError) {
j = int(offset[1])
continue
}
2025-11-21 12:01:44 +08:00
return err
2025-11-20 17:56:45 +08:00
}
tx.Swaps = append(tx.Swaps, swaps...)
j = int(offset[1])
ii = int(offset[0])
} else if p, exists := actionPrograms[innerProgramAccount]; exists {
2025-11-21 12:01:44 +08:00
offset, err := p(tx, innerInstr, innersMap[i], [2]uint{uint(i), uint(j)})
2025-11-20 17:56:45 +08:00
if err != nil {
if errors.Is(err, InstructionIgnoredError) {
j = int(offset[1])
continue
}
2025-11-21 12:01:44 +08:00
return err
2025-11-20 17:56:45 +08:00
}
j = int(offset[1])
ii = int(offset[0])
} else {
j++
}
if j > innerLength || ii > i {
break
}
}
}
}
2025-11-21 12:01:44 +08:00
return nil
2025-11-20 17:56:45 +08:00
}
2026-01-08 12:00:59 +08:00
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
}