punm parser

This commit is contained in:
thloyi
2025-11-21 12:01:44 +08:00
parent a945f3b45d
commit f86c5591c1
13 changed files with 228 additions and 148 deletions

View File

@@ -10,31 +10,31 @@ type setComputeData struct {
Units uint64 Units uint64
} }
func budgetParser(result *RawTx, instr Instruction, innerInstructions InnerInstructions, offset [2]uint, tx *Tx) ([2]uint, error) { func budgetParser(tx *Tx, instr Instruction, _ InnerInstructions, offset [2]uint) ([2]uint, error) {
if offset[1] != 0 { if offset[1] != 0 {
return increaseOffset(offset), nil return increaseOffset(offset), nil
} }
decode := result.Transaction.Message.Instructions[offset[0]].Data decode := instr.Data
discriminator := decode[0] discriminator := decode[0]
switch discriminator { switch discriminator {
case setComputeUnitLimitDiscriminator: case setComputeUnitLimitDiscriminator:
return computeUnitLimitParser(result, offset, tx, decode[1:]) return computeUnitLimitParser(offset, tx, decode[1:])
case setComputeUnitPriceDiscriminator: case setComputeUnitPriceDiscriminator:
return computeUnitPriceParser(result, offset, tx, decode[1:]) return computeUnitPriceParser(offset, tx, decode[1:])
default: default:
return increaseOffset(offset), nil return increaseOffset(offset), nil
} }
} }
func computeUnitLimitParser(_ *RawTx, offset [2]uint, _ *Tx, decodedData []byte) ([2]uint, error) { func computeUnitLimitParser(offset [2]uint, _ *Tx, decodedData []byte) ([2]uint, error) {
if len(decodedData) < 8 { if len(decodedData) < 8 {
return increaseOffset(offset), nil return increaseOffset(offset), nil
} }
return increaseOffset(offset), nil return increaseOffset(offset), nil
} }
func computeUnitPriceParser(_ *RawTx, offset [2]uint, tx *Tx, decodedData []byte) ([2]uint, error) { func computeUnitPriceParser(offset [2]uint, tx *Tx, decodedData []byte) ([2]uint, error) {
if len(decodedData) < 8 { if len(decodedData) < 8 {
return increaseOffset(offset), nil return increaseOffset(offset), nil
} }

View File

@@ -1 +0,0 @@
package pump_parser

View File

@@ -54,7 +54,7 @@ func main() {
instructorErrIndex, customerErrCode, _ = txErr.GetCustomErrorCode() instructorErrIndex, customerErrCode, _ = txErr.GetCustomErrorCode()
fmt.Printf("now: %s, block: %d, tx: %s, errInstr Code: %d, errInstrIndex: %d, err: %v\n", time.Now().Format("2006-01-02 15:04:05"), ptx.Block, ptx.GetTxHash(), customerErrCode, instructorErrIndex, ptx.Err) fmt.Printf("now: %s, block: %d, tx: %s, errInstr Code: %d, errInstrIndex: %d, err: %v\n", time.Now().Format("2006-01-02 15:04:05"), ptx.Block, ptx.GetTxHash(), customerErrCode, instructorErrIndex, ptx.Err)
} else { } else {
txs := example.FromTx(ptx, msg.RawTx) txs := example.FromTx(ptx)
if len(txs) == 0 { if len(txs) == 0 {
fmt.Printf("tx is empty, block: %d, tx %s \n", ptx.Block, ptx.GetTxHash()) fmt.Printf("tx is empty, block: %d, tx %s \n", ptx.Block, ptx.GetTxHash())
continue continue

View File

@@ -18,5 +18,4 @@ type SubscriptionMessage struct {
Block *BlockInfo Block *BlockInfo
Tx *pump_parser.Tx Tx *pump_parser.Tx
RawTx *pump_parser.RawTx
} }

View File

@@ -8,14 +8,14 @@ import (
) )
type PumpHandler struct { type PumpHandler struct {
callback func(*types.Tx, *types.RawTx) callback func(*types.Tx)
} }
func NewPumpHandler(cb func(*types.Tx, *types.RawTx)) *PumpHandler { func NewPumpHandler(cb func(*types.Tx)) *PumpHandler {
return &PumpHandler{ return &PumpHandler{
callback: func(tx *types.Tx, tx2 *types.RawTx) { callback: func(tx *types.Tx) {
//tx.Check(tx2) //tx.Check(tx2)
cb(tx, tx2) cb(tx)
}, },
} }
} }
@@ -40,11 +40,13 @@ func (h *PumpHandler) HandleMessage(rawTx *types.RawTx) {
BeforeSolBalance: beforeSolBalance, BeforeSolBalance: beforeSolBalance,
AfterSOLBalance: afterSolBalance, AfterSOLBalance: afterSolBalance,
}, rawTx) })
return return
} }
parsedTx, err := types.Parser(rawTx) var parsedTx = &types.Tx{}
parsedTx.SetRawTx(rawTx)
err := parsedTx.Parser()
if err != nil { if err != nil {
fmt.Printf("parser error: %s, block: %d tx: %s\n", err, rawTx.Slot, rawTx.TxHash()) fmt.Printf("parser error: %s, block: %d tx: %s\n", err, rawTx.Slot, rawTx.TxHash())
return return
@@ -55,6 +57,6 @@ func (h *PumpHandler) HandleMessage(rawTx *types.RawTx) {
} }
// fmt.Println(parsedTx.GetTxHash(), len(parsedTx.Swaps)) // fmt.Println(parsedTx.GetTxHash(), len(parsedTx.Swaps))
if h.callback != nil { if h.callback != nil {
h.callback(parsedTx, rawTx) h.callback(parsedTx)
} }
} }

View File

@@ -74,8 +74,8 @@ func NewClientWithPumpSwap(endpoint string, ch chan SubscriptionMessage) *Client
subStatus: false, subStatus: false,
subscription: &subscription, subscription: &subscription,
} }
c.handler = NewPumpHandler(func(tx *types.Tx, tx2 *types.RawTx) { c.handler = NewPumpHandler(func(tx *types.Tx) {
c.sendTx(tx, tx2) c.sendTx(tx)
}) })
return c return c
} }
@@ -107,8 +107,8 @@ func NewClientWithLaunchLab(endpoint string, ch chan SubscriptionMessage) *Clien
subStatus: false, subStatus: false,
subscription: &subscription, subscription: &subscription,
} }
c.handler = NewPumpHandler(func(tx *types.Tx, tx2 *types.RawTx) { c.handler = NewPumpHandler(func(tx *types.Tx) {
c.sendTx(tx, tx2) c.sendTx(tx)
}) })
return c return c
} }
@@ -262,13 +262,12 @@ func (c *Client) computeDelay(slot uint64) int64 {
return delay return delay
} }
func (c *Client) sendTx(t *types.Tx, tx *types.RawTx) { func (c *Client) sendTx(t *types.Tx) {
c.ch <- SubscriptionMessage{ c.ch <- SubscriptionMessage{
Reconnect: c.firstMessage, Reconnect: c.firstMessage,
EstimateDelaySecond: c.computeDelay(tx.Slot), EstimateDelaySecond: c.computeDelay(t.Block),
Block: nil, Block: nil,
Tx: t, Tx: t,
RawTx: tx,
} }
c.firstMessage = false c.firstMessage = false
} }
@@ -288,7 +287,6 @@ func (c *Client) sendBlock(blockMeta *pb.SubscribeUpdateBlockMeta) {
Height: c.leastBlock.Height, Height: c.leastBlock.Height,
}, },
Tx: nil, Tx: nil,
RawTx: nil,
} }
c.firstMessage = false c.firstMessage = false
} }

View File

@@ -64,14 +64,15 @@ func (tx *Tx) GetTxHash() string {
return tx.CachedTxHash return tx.CachedTxHash
} }
func FromTx(tx *parser.Tx, raw *parser.RawTx) []*Tx { func FromTx(tx *parser.Tx) []*Tx {
var txs []*Tx = make([]*Tx, 0, len(tx.Swaps)) var txs = make([]*Tx, 0, len(tx.Swaps))
mev, mevFee := tx.CheckMevAgent() mev, mevFee := tx.CheckMevAgent()
for i, s := range tx.Swaps { for i, s := range tx.Swaps {
var newTx *Tx var newTx *Tx
platform, platformFee := tx.CheckPlatform(s, raw) platform, platformFee := tx.CheckPlatform(s)
token0Program := s.BaseTokenProgram token0Program := s.BaseTokenProgram
token0Address := s.BaseMint token0Address := s.BaseMint
token0Decimals := s.BaseMintDecimals
if s.Program == "Pump" { if s.Program == "Pump" {
newTx = &Tx{ newTx = &Tx{
Err: nil, Err: nil,
@@ -126,6 +127,7 @@ func FromTx(tx *parser.Tx, raw *parser.RawTx) []*Tx {
} }
token0Program = s.QuoteTokenProgram token0Program = s.QuoteTokenProgram
token0Address = s.QuoteMint token0Address = s.QuoteMint
token0Decimals = s.QuoteMintDecimals
newTx = &Tx{ newTx = &Tx{
Err: nil, Err: nil,
//BondingCurve: s.Pool.String(), //BondingCurve: s.Pool.String(),
@@ -220,7 +222,7 @@ func FromTx(tx *parser.Tx, raw *parser.RawTx) []*Tx {
} }
if newTx.Maker == "HV1KXxWFaSeriyFvXyx48FqG9BoFbfinB8njCJonqP7K" && newTx.EntryContract == "oKXAggregatorV2" { if newTx.Maker == "HV1KXxWFaSeriyFvXyx48FqG9BoFbfinB8njCJonqP7K" && newTx.EntryContract == "oKXAggregatorV2" {
newTx.Maker = tx.Signer.String() newTx.Maker = tx.Signer.String()
newTx.AfterSignerToken0Balance = parser.GetTokenBalanceAfterTx(raw, 0, token0Program, token0Address) newTx.AfterSignerToken0Balance = tx.GetSignerTokenBalanceAfterTx(token0Program, token0Address).Div(decimal.New(1, int32(token0Decimals)))
} }
txs = append(txs, newTx) txs = append(txs, newTx)

View File

@@ -2,6 +2,7 @@ package pump_parser
import ( import (
"errors" "errors"
"fmt"
"github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go"
) )
@@ -20,42 +21,22 @@ var (
InstructionIgnoredError = errors.New("instruction ignored") InstructionIgnoredError = errors.New("instruction ignored")
) )
type swapParser func(result *RawTx, instr Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) type swapParser func(tx *Tx, instr Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error)
type actionParser func(result *RawTx, instr Instruction, innerInstructions InnerInstructions, offset [2]uint, tx *Tx) ([2]uint, error) type actionParser func(tx *Tx, instr Instruction, innerInstructions InnerInstructions, offset [2]uint) ([2]uint, error)
//type PumpComplete struct { func getInnerInstructions(innerInstructions InnerInstructions, offset uint) ([]Instruction, error) {
// IsMayhem bool var inners []Instruction
// IsToken2022 bool var prefixLen = offset
//} if prefixLen > uint(len(innerInstructions.Instructions)) {
return nil, fmt.Errorf("error inner instruction index out of range")
//type PumpStatusCache map[solana.PublicKey]PumpComplete }
// if prefixLen == 0 {
//var ( inners = innerInstructions.Instructions
// pumpCompleteCache PumpStatusCache = make(map[solana.PublicKey]PumpComplete) } else {
// pumpCompleteCacheLock sync.Mutex inners = innerInstructions.Instructions[prefixLen:]
//) }
// return inners, nil
//func getPumpCompleteStatus(pumpToken solana.PublicKey) (PumpComplete, bool) { }
// pumpCompleteCacheLock.Lock()
// defer pumpCompleteCacheLock.Unlock()
// status, exists := pumpCompleteCache[pumpToken]
// return status, exists
//}
//
//func setPumpCompleteStatus(pumpToken solana.PublicKey, mayhem bool, token2022 bool) {
// pumpCompleteCacheLock.Lock()
// defer pumpCompleteCacheLock.Unlock()
// pumpCompleteCache[pumpToken] = PumpComplete{
// IsMayhem: mayhem,
// IsToken2022: token2022,
// }
//}
//
//func removePumpCompleteStatus(pumpToken solana.PublicKey) {
// pumpCompleteCacheLock.Lock()
// defer pumpCompleteCacheLock.Unlock()
// delete(pumpCompleteCache, pumpToken)
//}
func isMayhemPump(feeAccount solana.PublicKey) bool { func isMayhemPump(feeAccount solana.PublicKey) bool {
for _, mayhemFeeAccount := range mayhemFeeAccounts { for _, mayhemFeeAccount := range mayhemFeeAccounts {

View File

@@ -17,40 +17,45 @@ var actionPrograms = map[solana.PublicKey]actionParser{
budgGetProgram: budgetParser, budgGetProgram: budgetParser,
} }
func Parser(rawTx *RawTx) (*Tx, error) { func (tx *Tx) Parser() error {
accountList := rawTx.getAccountList() if tx.rawTx == nil {
var tx = &Tx{ return errors.New("rawTx is nil")
TxHash: (*[64]byte)((rawTx.Transaction.Signatures[0][:])),
Signer: rawTx.GetSigner(),
Block: rawTx.Slot,
BlockIndex: uint64(rawTx.IndexWithinBlock),
BlockAt: rawTx.BlockTime,
BeforeSolBalance: decimal.NewFromUint64(rawTx.Meta.PreBalances[0]).Div(decimal.NewFromInt(1e9)),
AfterSOLBalance: decimal.NewFromUint64(rawTx.Meta.PostBalances[0]).Div(decimal.NewFromInt(1e9)),
} }
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.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) var innersMap = make(map[int]InnerInstructions)
for _, inner := range rawTx.Meta.InnerInstructions { for _, inner := range tx.rawTx.Meta.InnerInstructions {
innersMap[inner.Index] = inner innersMap[inner.Index] = inner
} }
for i, instr := range rawTx.Transaction.Message.Instructions { for i, instr := range tx.rawTx.Transaction.Message.Instructions {
programAccount := accountList[instr.ProgramIDIndex] programAccount := accountList[instr.ProgramIDIndex]
if p, exists := swapPrograms[programAccount]; exists { if p, exists := swapPrograms[programAccount]; exists {
swaps, _, err := p(rawTx, instr, innersMap[i], [2]uint{uint(i), uint(0)}) swaps, _, err := p(tx, instr, innersMap[i], [2]uint{uint(i), uint(0)})
if err != nil { if err != nil {
if errors.Is(err, InstructionIgnoredError) { if errors.Is(err, InstructionIgnoredError) {
continue continue
} }
return nil, err return err
} }
tx.Swaps = append(tx.Swaps, swaps...) tx.Swaps = append(tx.Swaps, swaps...)
} else if p, exists := actionPrograms[programAccount]; exists { } else if p, exists := actionPrograms[programAccount]; exists {
_, err := p(rawTx, instr, innersMap[i], [2]uint{uint(i), uint(0)}, tx) _, err := p(tx, instr, innersMap[i], [2]uint{uint(i), uint(0)})
if err != nil { if err != nil {
if errors.Is(err, InstructionIgnoredError) { if errors.Is(err, InstructionIgnoredError) {
continue continue
} }
return nil, err return err
} }
} else { } else {
ii := i ii := i
@@ -61,25 +66,25 @@ func Parser(rawTx *RawTx) (*Tx, error) {
innerProgramAccount := accountList[innerInstr.ProgramIDIndex] innerProgramAccount := accountList[innerInstr.ProgramIDIndex]
if p, exists := swapPrograms[innerProgramAccount]; exists { if p, exists := swapPrograms[innerProgramAccount]; exists {
swaps, offset, err := p(rawTx, innerInstr, innersMap[i], [2]uint{uint(i), uint(j)}) swaps, offset, err := p(tx, innerInstr, innersMap[i], [2]uint{uint(i), uint(j)})
if err != nil { if err != nil {
if errors.Is(err, InstructionIgnoredError) { if errors.Is(err, InstructionIgnoredError) {
j = int(offset[1]) j = int(offset[1])
continue continue
} }
return nil, err return err
} }
tx.Swaps = append(tx.Swaps, swaps...) tx.Swaps = append(tx.Swaps, swaps...)
j = int(offset[1]) j = int(offset[1])
ii = int(offset[0]) ii = int(offset[0])
} else if p, exists := actionPrograms[innerProgramAccount]; exists { } else if p, exists := actionPrograms[innerProgramAccount]; exists {
offset, err := p(rawTx, innerInstr, innersMap[i], [2]uint{uint(i), uint(j)}, tx) offset, err := p(tx, innerInstr, innersMap[i], [2]uint{uint(i), uint(j)})
if err != nil { if err != nil {
if errors.Is(err, InstructionIgnoredError) { if errors.Is(err, InstructionIgnoredError) {
j = int(offset[1]) j = int(offset[1])
continue continue
} }
return nil, err return err
} }
j = int(offset[1]) j = int(offset[1])
ii = int(offset[0]) ii = int(offset[0])
@@ -93,5 +98,5 @@ func Parser(rawTx *RawTx) (*Tx, error) {
} }
} }
return tx, nil return nil
} }

57
pump.go
View File

@@ -19,9 +19,9 @@ func increaseOffset(offset [2]uint) [2]uint {
// pumpParser // routes pump program instructions to their respective parsers, // pumpParser // routes pump program instructions to their respective parsers,
// offset is [outerIndex, innerIndex] index of instructions in the transaction, // offset is [outerIndex, innerIndex] index of instructions in the transaction,
// if innerIndex == 0 this is outer instruction,if it's an inner instruction, outerIndex is the index of the parent instruction. // if innerIndex == 0 this is outer instruction,if it's an inner instruction, outerIndex is the index of the parent instruction.
func pumpParser(result *RawTx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) { func pumpParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
if !result.accountList[instruction.ProgramIDIndex].Equals(pumpProgram) { if !tx.rawTx.accountList[instruction.ProgramIDIndex].Equals(pumpProgram) {
return nil, increaseOffset(offset), fmt.Errorf("pump program instruction not found, offset, %d, %d", offset[0], offset[1]) return nil, increaseOffset(offset), fmt.Errorf("pump program instruction not found, offset, %d, %d", offset[0], offset[1])
} }
@@ -35,11 +35,11 @@ func pumpParser(result *RawTx, instruction Instruction, innerInstructions InnerI
switch discriminator { switch discriminator {
case pumpBuyV2Discriminator, pumpBuyDiscriminator, pumpSellDiscriminator: case pumpBuyV2Discriminator, pumpBuyDiscriminator, pumpSellDiscriminator:
return BuyOrSellParser(result, instruction, innerInstructions, offset) return BuyOrSellParser(tx, instruction, innerInstructions, offset)
case pumpCreateDiscriminator, pumpCreateV2Discriminator: case pumpCreateDiscriminator, pumpCreateV2Discriminator:
return CreateParser(result, instruction, innerInstructions, offset) return CreateParser(tx, instruction, innerInstructions, offset)
case pumpMigrateDiscriminator: case pumpMigrateDiscriminator:
return MigrateParser(result, instruction, innerInstructions, offset) return MigrateParser(tx, instruction, innerInstructions, offset)
default: default:
return nil, increaseOffset(offset), InstructionIgnoredError return nil, increaseOffset(offset), InstructionIgnoredError
} }
@@ -82,21 +82,8 @@ type PumpCreateEvent struct {
IsMayhemMode bool IsMayhemMode bool
} }
func getInnerInstructions(innerInstructions InnerInstructions, offset uint) ([]Instruction, error) { func CreateParser(tx *Tx, instr Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
var inners []Instruction result := tx.rawTx
var prefixLen = offset
if prefixLen > uint(len(innerInstructions.Instructions)) {
return nil, fmt.Errorf("error inner instruction index out of range")
}
if prefixLen == 0 {
inners = innerInstructions.Instructions
} else {
inners = innerInstructions.Instructions[prefixLen:]
}
return inners, nil
}
func CreateParser(result *RawTx, instr Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex] var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
var programIndex = instr.ProgramIDIndex var programIndex = instr.ProgramIDIndex
@@ -134,6 +121,16 @@ func CreateParser(result *RawTx, instr Instruction, innerInstructions InnerInstr
userBase := getAccountBalanceAfterTx(result, userIndex) userBase := getAccountBalanceAfterTx(result, userIndex)
userQuote, _ := GetSolAfterTx(result, userIndex) userQuote, _ := GetSolAfterTx(result, userIndex)
totalSupply := decimal.NewFromUint64(createEvent.TokenTotalSupply).Div(decimal.New(1, 6))
tx.Token[createEvent.Mint] = TokenMeta{
Mint: createEvent.Mint,
TokenProgram: createEvent.TokenProgram,
Decimals: 6,
Name: createEvent.Name,
Symbol: createEvent.Symbol,
Url: createEvent.Uri,
TotalSupply: &totalSupply,
}
return []Swap{ return []Swap{
{ {
Program: SolProgramPump, Program: SolProgramPump,
@@ -186,7 +183,8 @@ type CompleteEvent struct {
Timestamp int64 Timestamp int64
} }
func BuyOrSellParser(result *RawTx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) { func BuyOrSellParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
result := tx.rawTx
var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex] var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
var err error var err error
var programIndex = instruction.ProgramIDIndex var programIndex = instruction.ProgramIDIndex
@@ -255,6 +253,13 @@ func BuyOrSellParser(result *RawTx, instruction Instruction, innerInstructions I
event = "sell" event = "sell"
baseTokenProgram = result.accountList[instruction.Accounts[9]] baseTokenProgram = result.accountList[instruction.Accounts[9]]
} }
if _, exists := tx.Token[tradeEvent.Mint]; !exists {
tx.Token[tradeEvent.Mint] = TokenMeta{
Mint: tradeEvent.Mint,
TokenProgram: baseTokenProgram,
Decimals: 6,
}
}
swaps := []Swap{ swaps := []Swap{
{ {
Program: SolProgramPump, Program: SolProgramPump,
@@ -314,7 +319,8 @@ type MigrateEvent struct {
Pool solana.PublicKey Pool solana.PublicKey
} }
func MigrateParser(result *RawTx, instr Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) { func MigrateParser(tx *Tx, instr Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
result := tx.rawTx
var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex] var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
var err error var err error
programIndex := instr.ProgramIDIndex programIndex := instr.ProgramIDIndex
@@ -389,6 +395,13 @@ func MigrateParser(result *RawTx, instr Instruction, innerInstructions InnerInst
} }
userQuote, _ := GetSolAfterTx(result, userIndex) userQuote, _ := GetSolAfterTx(result, userIndex)
if _, exists := tx.Token[migrateEvent.Mint]; !exists {
tx.Token[migrateEvent.Mint] = TokenMeta{
Mint: migrateEvent.Mint,
TokenProgram: baseTokenProgram,
Decimals: 6,
}
}
swaps := []Swap{ swaps := []Swap{
{ {
Program: SolProgramPump, Program: SolProgramPump,

View File

@@ -123,9 +123,8 @@ type ammWithdrawEvent struct {
UserPoolTokenAccount solana.PublicKey UserPoolTokenAccount solana.PublicKey
} }
func pumpAmmParser(result *RawTx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) { func pumpAmmParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
if !tx.rawTx.accountList[instruction.ProgramIDIndex].Equals(pumpAmmProgram) {
if !result.accountList[instruction.ProgramIDIndex].Equals(pumpAmmProgram) {
return nil, increaseOffset(offset), fmt.Errorf("pump amm program instruction not found, offset: %d, %d", offset[0], offset[1]) return nil, increaseOffset(offset), fmt.Errorf("pump amm program instruction not found, offset: %d, %d", offset[0], offset[1])
} }
decode := instruction.Data decode := instruction.Data
@@ -137,21 +136,22 @@ func pumpAmmParser(result *RawTx, instruction Instruction, innerInstructions Inn
discriminator := *(*[8]byte)(decode[:8]) discriminator := *(*[8]byte)(decode[:8])
switch discriminator { switch discriminator {
case pumpAmmCreateDiscriminator: case pumpAmmCreateDiscriminator:
return ammCreatePoolParser(result, instruction, innerInstructions, offset) return ammCreatePoolParser(tx, instruction, innerInstructions, offset)
case pumpAmmBuyDiscriminator: case pumpAmmBuyDiscriminator:
return ammBuyParser(result, instruction, innerInstructions, offset) return ammBuyParser(tx, instruction, innerInstructions, offset)
case pumpAmmSellDiscriminator: case pumpAmmSellDiscriminator:
return ammSellParser(result, instruction, innerInstructions, offset) return ammSellParser(tx, instruction, innerInstructions, offset)
case pumpAmmDepositDiscriminator: case pumpAmmDepositDiscriminator:
return depositParse(result, instruction, innerInstructions, offset) return depositParse(tx, instruction, innerInstructions, offset)
case pumpAmmWithdrawDiscriminator: case pumpAmmWithdrawDiscriminator:
return withdrawParse(result, instruction, innerInstructions, offset) return withdrawParse(tx, instruction, innerInstructions, offset)
default: default:
return nil, increaseOffset(offset), InstructionIgnoredError return nil, increaseOffset(offset), InstructionIgnoredError
} }
} }
func ammCreatePoolParser(result *RawTx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) { func ammCreatePoolParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
result := tx.rawTx
var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex] var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
var err error var err error
var prefixLen = offset[1] var prefixLen = offset[1]
@@ -182,6 +182,22 @@ func ammCreatePoolParser(result *RawTx, instruction Instruction, innerInstructio
baseTokenProgram := result.accountList[instruction.Accounts[13]] baseTokenProgram := result.accountList[instruction.Accounts[13]]
quoteTokenProgram := result.accountList[instruction.Accounts[14]] quoteTokenProgram := result.accountList[instruction.Accounts[14]]
if _, exists := tx.Token[createEvent.BaseMint]; !exists && !createEvent.BaseMint.Equals(wSolMint) {
tx.Token[createEvent.BaseMint] = TokenMeta{
Mint: createEvent.BaseMint,
Decimals: createEvent.BaseMintDecimals,
TokenProgram: baseTokenProgram,
}
}
if _, exists := tx.Token[createEvent.QuoteMint]; !exists && !createEvent.QuoteMint.Equals(wSolMint) {
tx.Token[createEvent.QuoteMint] = TokenMeta{
Mint: createEvent.QuoteMint,
Decimals: createEvent.QuoteMintDecimals,
TokenProgram: quoteTokenProgram,
}
}
return []Swap{ return []Swap{
{ {
Program: SolProgramPumpAMM, Program: SolProgramPumpAMM,
@@ -208,7 +224,8 @@ func ammCreatePoolParser(result *RawTx, instruction Instruction, innerInstructio
} }
func ammBuyParser(result *RawTx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) { func ammBuyParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
result := tx.rawTx
var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex] var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
var err error var err error
var prefixLen = offset[1] var prefixLen = offset[1]
@@ -254,6 +271,21 @@ func ammBuyParser(result *RawTx, instruction Instruction, innerInstructions Inne
quoteMintDecimals = uint8(meta.UITokenAmount.Decimals) quoteMintDecimals = uint8(meta.UITokenAmount.Decimals)
} }
} }
if _, exists := tx.Token[baseMint]; !exists && !baseMint.Equals(wSolMint) {
tx.Token[baseMint] = TokenMeta{
Mint: baseMint,
Decimals: baseMintDecimals,
TokenProgram: baseTokenProgram,
}
}
if _, exists := tx.Token[quoteMint]; !exists && !quoteMint.Equals(wSolMint) {
tx.Token[quoteMint] = TokenMeta{
Mint: quoteMint,
Decimals: quoteMintDecimals,
TokenProgram: quoteTokenProgram,
}
}
return []Swap{ return []Swap{
{ {
Program: SolProgramPumpAMM, Program: SolProgramPumpAMM,
@@ -279,7 +311,8 @@ func ammBuyParser(result *RawTx, instruction Instruction, innerInstructions Inne
}, offset, nil }, offset, nil
} }
func ammSellParser(result *RawTx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) { func ammSellParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
result := tx.rawTx
var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex] var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
var err error var err error
var prefixLen = offset[1] var prefixLen = offset[1]
@@ -325,6 +358,21 @@ func ammSellParser(result *RawTx, instruction Instruction, innerInstructions Inn
quoteMintDecimals = uint8(meta.UITokenAmount.Decimals) quoteMintDecimals = uint8(meta.UITokenAmount.Decimals)
} }
} }
if _, exists := tx.Token[baseMint]; !exists && !baseMint.Equals(wSolMint) {
tx.Token[baseMint] = TokenMeta{
Mint: baseMint,
Decimals: baseMintDecimals,
TokenProgram: baseTokenProgram,
}
}
if _, exists := tx.Token[quoteMint]; !exists && !quoteMint.Equals(wSolMint) {
tx.Token[quoteMint] = TokenMeta{
Mint: quoteMint,
Decimals: quoteMintDecimals,
TokenProgram: quoteTokenProgram,
}
}
return []Swap{ return []Swap{
{ {
Program: SolProgramPumpAMM, Program: SolProgramPumpAMM,
@@ -350,7 +398,8 @@ func ammSellParser(result *RawTx, instruction Instruction, innerInstructions Inn
}, offset, nil }, offset, nil
} }
func depositParse(result *RawTx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) { func depositParse(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
result := tx.rawTx
var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex] var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
var err error var err error
var prefixLen = offset[1] var prefixLen = offset[1]
@@ -405,14 +454,30 @@ func depositParse(result *RawTx, instruction Instruction, innerInstructions Inne
} }
} }
} }
baseMint := result.accountList[instruction.Accounts[3]]
quoteMint := result.accountList[instruction.Accounts[4]]
if _, exists := tx.Token[baseMint]; !exists && !baseMint.Equals(wSolMint) {
tx.Token[baseMint] = TokenMeta{
Mint: baseMint,
Decimals: baseMintDecimals,
TokenProgram: baseMintProgram,
}
}
if _, exists := tx.Token[quoteMint]; !exists && !quoteMint.Equals(wSolMint) {
tx.Token[quoteMint] = TokenMeta{
Mint: quoteMint,
Decimals: quoteMintDecimals,
TokenProgram: quoteMintProgram,
}
}
return []Swap{ return []Swap{
{ {
Program: SolProgramPumpAMM, Program: SolProgramPumpAMM,
Event: "deposit", Event: "deposit",
Pool: event.Pool, Pool: event.Pool,
BaseMint: result.accountList[instruction.Accounts[3]], BaseMint: baseMint,
QuoteMint: result.accountList[instruction.Accounts[4]], QuoteMint: quoteMint,
BaseTokenProgram: baseMintProgram, BaseTokenProgram: baseMintProgram,
QuoteTokenProgram: quoteMintProgram, QuoteTokenProgram: quoteMintProgram,
//Creator: solana.PublicKey{}, //Creator: solana.PublicKey{},
@@ -431,7 +496,8 @@ func depositParse(result *RawTx, instruction Instruction, innerInstructions Inne
}, offset, nil }, offset, nil
} }
func withdrawParse(result *RawTx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) { func withdrawParse(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
result := tx.rawTx
var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex] var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
var err error var err error
var prefixLen = offset[1] var prefixLen = offset[1]
@@ -486,7 +552,23 @@ func withdrawParse(result *RawTx, instruction Instruction, innerInstructions Inn
} }
} }
} }
baseMint := result.accountList[instruction.Accounts[3]]
quoteMint := result.accountList[instruction.Accounts[4]]
if _, exists := tx.Token[baseMint]; !exists && !baseMint.Equals(wSolMint) {
tx.Token[baseMint] = TokenMeta{
Mint: baseMint,
Decimals: baseMintDecimals,
TokenProgram: baseMintProgram,
}
}
if _, exists := tx.Token[quoteMint]; !exists && !quoteMint.Equals(wSolMint) {
tx.Token[quoteMint] = TokenMeta{
Mint: quoteMint,
Decimals: quoteMintDecimals,
TokenProgram: quoteMintProgram,
}
}
return []Swap{ return []Swap{
{ {
Program: SolProgramPumpAMM, Program: SolProgramPumpAMM,

View File

@@ -7,23 +7,10 @@ import (
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
) )
func systemParser(result *RawTx, instr Instruction, innerInstructions InnerInstructions, offset [2]uint, tx *Tx) ([2]uint, error) { func systemParser(tx *Tx, instruction Instruction, _ InnerInstructions, offset [2]uint) ([2]uint, error) {
var instruction Instruction
var found bool result := tx.rawTx
if offset[1] == 0 { if !result.accountList[instruction.ProgramIDIndex].Equals(systemProgram) {
instruction = result.Transaction.Message.Instructions[offset[0]]
found = true
} else {
for _, innerInstruction := range result.Meta.InnerInstructions {
if innerInstruction.Index == int(offset[0]) {
instruction = innerInstruction.Instructions[offset[1]-1]
found = true
break
}
}
}
if !found {
return increaseOffset(offset), fmt.Errorf("system program instruction not found, block: %d, tx: %s, outerIndex: %d, innerIndex: %d", result.Slot, result.TxHash(), offset[0], offset[1]) return increaseOffset(offset), fmt.Errorf("system program instruction not found, block: %d, tx: %s, outerIndex: %d, innerIndex: %d", result.Slot, result.TxHash(), offset[0], offset[1])
} }

22
tx.go
View File

@@ -48,6 +48,7 @@ type mevInfo struct {
} }
type Tx struct { type Tx struct {
rawTx *RawTx
Signer solana.PublicKey Signer solana.PublicKey
Err interface{} `json:"err,omitempty"` Err interface{} `json:"err,omitempty"`
Swaps []Swap `json:"swaps,omitempty"` Swaps []Swap `json:"swaps,omitempty"`
@@ -65,20 +66,30 @@ type Tx struct {
BeforeSolBalance decimal.Decimal `json:"-"` BeforeSolBalance decimal.Decimal `json:"-"`
AfterSOLBalance decimal.Decimal `json:"after_sol_balance"` AfterSOLBalance decimal.Decimal `json:"after_sol_balance"`
Token []TokenMeta `gorm:"-"` // update tokenInfo
Token map[solana.PublicKey]TokenMeta `gorm:"-"`
// todo pool info ??
}
func (tx *Tx) SetRawTx(t *RawTx) {
tx.rawTx = t
}
func (tx *Tx) GetSignerTokenBalanceAfterTx(tokenProgram, tokenMint solana.PublicKey) decimal.Decimal {
return GetTokenBalanceAfterTx(tx.rawTx, 0, tokenProgram, tokenMint)
} }
type TokenMeta struct { type TokenMeta struct {
Address solana.PublicKey `json:"address"` Mint solana.PublicKey `json:"address"`
TokenProgram solana.PublicKey `json:"token_program"` TokenProgram solana.PublicKey `json:"token_program"`
Decimals uint8 `json:"decimals"`
Name string Name string
Symbol string Symbol string
Decimal uint8
Url string Url string
TotalSupply *decimal.Decimal TotalSupply *decimal.Decimal
SignerBalance *decimal.Decimal
} }
func (tx *Tx) GetTxHash() string { func (tx *Tx) GetTxHash() string {
@@ -92,8 +103,9 @@ func (tx *Tx) GetTxHash() string {
return tx.cachedTxHash return tx.cachedTxHash
} }
func (tx *Tx) CheckPlatform(swap Swap, rawTx *RawTx) (string, decimal.Decimal) { func (tx *Tx) CheckPlatform(swap Swap) (string, decimal.Decimal) {
// hasSolProgramRaydiumLaunchLabBonk // hasSolProgramRaydiumLaunchLabBonk
rawTx := tx.rawTx
var platform string var platform string
var platformFee decimal.Decimal var platformFee decimal.Decimal
if len(tx.Platform) == 0 { if len(tx.Platform) == 0 {