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
}
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 {
return increaseOffset(offset), nil
}
decode := result.Transaction.Message.Instructions[offset[0]].Data
decode := instr.Data
discriminator := decode[0]
switch discriminator {
case setComputeUnitLimitDiscriminator:
return computeUnitLimitParser(result, offset, tx, decode[1:])
return computeUnitLimitParser(offset, tx, decode[1:])
case setComputeUnitPriceDiscriminator:
return computeUnitPriceParser(result, offset, tx, decode[1:])
return computeUnitPriceParser(offset, tx, decode[1:])
default:
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 {
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 {
return increaseOffset(offset), nil
}

View File

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

View File

@@ -54,7 +54,7 @@ func main() {
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)
} else {
txs := example.FromTx(ptx, msg.RawTx)
txs := example.FromTx(ptx)
if len(txs) == 0 {
fmt.Printf("tx is empty, block: %d, tx %s \n", ptx.Block, ptx.GetTxHash())
continue

View File

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

View File

@@ -8,14 +8,14 @@ import (
)
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{
callback: func(tx *types.Tx, tx2 *types.RawTx) {
callback: func(tx *types.Tx) {
//tx.Check(tx2)
cb(tx, tx2)
cb(tx)
},
}
}
@@ -40,11 +40,13 @@ func (h *PumpHandler) HandleMessage(rawTx *types.RawTx) {
BeforeSolBalance: beforeSolBalance,
AfterSOLBalance: afterSolBalance,
}, rawTx)
})
return
}
parsedTx, err := types.Parser(rawTx)
var parsedTx = &types.Tx{}
parsedTx.SetRawTx(rawTx)
err := parsedTx.Parser()
if err != nil {
fmt.Printf("parser error: %s, block: %d tx: %s\n", err, rawTx.Slot, rawTx.TxHash())
return
@@ -55,6 +57,6 @@ func (h *PumpHandler) HandleMessage(rawTx *types.RawTx) {
}
// fmt.Println(parsedTx.GetTxHash(), len(parsedTx.Swaps))
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,
subscription: &subscription,
}
c.handler = NewPumpHandler(func(tx *types.Tx, tx2 *types.RawTx) {
c.sendTx(tx, tx2)
c.handler = NewPumpHandler(func(tx *types.Tx) {
c.sendTx(tx)
})
return c
}
@@ -107,8 +107,8 @@ func NewClientWithLaunchLab(endpoint string, ch chan SubscriptionMessage) *Clien
subStatus: false,
subscription: &subscription,
}
c.handler = NewPumpHandler(func(tx *types.Tx, tx2 *types.RawTx) {
c.sendTx(tx, tx2)
c.handler = NewPumpHandler(func(tx *types.Tx) {
c.sendTx(tx)
})
return c
}
@@ -262,13 +262,12 @@ func (c *Client) computeDelay(slot uint64) int64 {
return delay
}
func (c *Client) sendTx(t *types.Tx, tx *types.RawTx) {
func (c *Client) sendTx(t *types.Tx) {
c.ch <- SubscriptionMessage{
Reconnect: c.firstMessage,
EstimateDelaySecond: c.computeDelay(tx.Slot),
EstimateDelaySecond: c.computeDelay(t.Block),
Block: nil,
Tx: t,
RawTx: tx,
}
c.firstMessage = false
}
@@ -287,8 +286,7 @@ func (c *Client) sendBlock(blockMeta *pb.SubscribeUpdateBlockMeta) {
BlockHash: c.leastBlock.BlockHash,
Height: c.leastBlock.Height,
},
Tx: nil,
RawTx: nil,
Tx: nil,
}
c.firstMessage = false
}

View File

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

View File

@@ -2,6 +2,7 @@ package pump_parser
import (
"errors"
"fmt"
"github.com/gagliardetto/solana-go"
)
@@ -20,42 +21,22 @@ var (
InstructionIgnoredError = errors.New("instruction ignored")
)
type swapParser func(result *RawTx, 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 swapParser func(tx *Tx, instr Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error)
type actionParser func(tx *Tx, instr Instruction, innerInstructions InnerInstructions, offset [2]uint) ([2]uint, error)
//type PumpComplete struct {
// IsMayhem bool
// IsToken2022 bool
//}
//type PumpStatusCache map[solana.PublicKey]PumpComplete
//
//var (
// pumpCompleteCache PumpStatusCache = make(map[solana.PublicKey]PumpComplete)
// pumpCompleteCacheLock sync.Mutex
//)
//
//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 getInnerInstructions(innerInstructions InnerInstructions, offset uint) ([]Instruction, error) {
var inners []Instruction
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 isMayhemPump(feeAccount solana.PublicKey) bool {
for _, mayhemFeeAccount := range mayhemFeeAccounts {

View File

@@ -17,40 +17,45 @@ var actionPrograms = map[solana.PublicKey]actionParser{
budgGetProgram: budgetParser,
}
func Parser(rawTx *RawTx) (*Tx, error) {
accountList := rawTx.getAccountList()
var tx = &Tx{
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)),
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.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 rawTx.Meta.InnerInstructions {
for _, inner := range tx.rawTx.Meta.InnerInstructions {
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]
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 errors.Is(err, InstructionIgnoredError) {
continue
}
return nil, err
return err
}
tx.Swaps = append(tx.Swaps, swaps...)
} 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 errors.Is(err, InstructionIgnoredError) {
continue
}
return nil, err
return err
}
} else {
ii := i
@@ -61,25 +66,25 @@ func Parser(rawTx *RawTx) (*Tx, error) {
innerProgramAccount := accountList[innerInstr.ProgramIDIndex]
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 errors.Is(err, InstructionIgnoredError) {
j = int(offset[1])
continue
}
return nil, err
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(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 errors.Is(err, InstructionIgnoredError) {
j = int(offset[1])
continue
}
return nil, err
return err
}
j = int(offset[1])
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,
// 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.
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])
}
@@ -35,11 +35,11 @@ func pumpParser(result *RawTx, instruction Instruction, innerInstructions InnerI
switch discriminator {
case pumpBuyV2Discriminator, pumpBuyDiscriminator, pumpSellDiscriminator:
return BuyOrSellParser(result, instruction, innerInstructions, offset)
return BuyOrSellParser(tx, instruction, innerInstructions, offset)
case pumpCreateDiscriminator, pumpCreateV2Discriminator:
return CreateParser(result, instruction, innerInstructions, offset)
return CreateParser(tx, instruction, innerInstructions, offset)
case pumpMigrateDiscriminator:
return MigrateParser(result, instruction, innerInstructions, offset)
return MigrateParser(tx, instruction, innerInstructions, offset)
default:
return nil, increaseOffset(offset), InstructionIgnoredError
}
@@ -82,21 +82,8 @@ type PumpCreateEvent struct {
IsMayhemMode bool
}
func getInnerInstructions(innerInstructions InnerInstructions, offset uint) ([]Instruction, error) {
var inners []Instruction
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) {
func CreateParser(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 programIndex = instr.ProgramIDIndex
@@ -134,6 +121,16 @@ func CreateParser(result *RawTx, instr Instruction, innerInstructions InnerInstr
userBase := getAccountBalanceAfterTx(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{
{
Program: SolProgramPump,
@@ -186,7 +183,8 @@ type CompleteEvent struct {
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 err error
var programIndex = instruction.ProgramIDIndex
@@ -255,6 +253,13 @@ func BuyOrSellParser(result *RawTx, instruction Instruction, innerInstructions I
event = "sell"
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{
{
Program: SolProgramPump,
@@ -314,7 +319,8 @@ type MigrateEvent struct {
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 err error
programIndex := instr.ProgramIDIndex
@@ -389,6 +395,13 @@ func MigrateParser(result *RawTx, instr Instruction, innerInstructions InnerInst
}
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{
{
Program: SolProgramPump,

View File

@@ -123,9 +123,8 @@ type ammWithdrawEvent struct {
UserPoolTokenAccount solana.PublicKey
}
func pumpAmmParser(result *RawTx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
if !result.accountList[instruction.ProgramIDIndex].Equals(pumpAmmProgram) {
func pumpAmmParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
if !tx.rawTx.accountList[instruction.ProgramIDIndex].Equals(pumpAmmProgram) {
return nil, increaseOffset(offset), fmt.Errorf("pump amm program instruction not found, offset: %d, %d", offset[0], offset[1])
}
decode := instruction.Data
@@ -137,21 +136,22 @@ func pumpAmmParser(result *RawTx, instruction Instruction, innerInstructions Inn
discriminator := *(*[8]byte)(decode[:8])
switch discriminator {
case pumpAmmCreateDiscriminator:
return ammCreatePoolParser(result, instruction, innerInstructions, offset)
return ammCreatePoolParser(tx, instruction, innerInstructions, offset)
case pumpAmmBuyDiscriminator:
return ammBuyParser(result, instruction, innerInstructions, offset)
return ammBuyParser(tx, instruction, innerInstructions, offset)
case pumpAmmSellDiscriminator:
return ammSellParser(result, instruction, innerInstructions, offset)
return ammSellParser(tx, instruction, innerInstructions, offset)
case pumpAmmDepositDiscriminator:
return depositParse(result, instruction, innerInstructions, offset)
return depositParse(tx, instruction, innerInstructions, offset)
case pumpAmmWithdrawDiscriminator:
return withdrawParse(result, instruction, innerInstructions, offset)
return withdrawParse(tx, instruction, innerInstructions, offset)
default:
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 err error
var prefixLen = offset[1]
@@ -182,6 +182,22 @@ func ammCreatePoolParser(result *RawTx, instruction Instruction, innerInstructio
baseTokenProgram := result.accountList[instruction.Accounts[13]]
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{
{
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 err error
var prefixLen = offset[1]
@@ -254,6 +271,21 @@ func ammBuyParser(result *RawTx, instruction Instruction, innerInstructions Inne
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{
{
Program: SolProgramPumpAMM,
@@ -279,7 +311,8 @@ func ammBuyParser(result *RawTx, instruction Instruction, innerInstructions Inne
}, 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 err error
var prefixLen = offset[1]
@@ -325,6 +358,21 @@ func ammSellParser(result *RawTx, instruction Instruction, innerInstructions Inn
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{
{
Program: SolProgramPumpAMM,
@@ -350,7 +398,8 @@ func ammSellParser(result *RawTx, instruction Instruction, innerInstructions Inn
}, 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 err error
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{
{
Program: SolProgramPumpAMM,
Event: "deposit",
Pool: event.Pool,
BaseMint: result.accountList[instruction.Accounts[3]],
QuoteMint: result.accountList[instruction.Accounts[4]],
BaseMint: baseMint,
QuoteMint: quoteMint,
BaseTokenProgram: baseMintProgram,
QuoteTokenProgram: quoteMintProgram,
//Creator: solana.PublicKey{},
@@ -431,7 +496,8 @@ func depositParse(result *RawTx, instruction Instruction, innerInstructions Inne
}, 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 err error
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{
{
Program: SolProgramPumpAMM,

View File

@@ -7,23 +7,10 @@ import (
"github.com/shopspring/decimal"
)
func systemParser(result *RawTx, instr Instruction, innerInstructions InnerInstructions, offset [2]uint, tx *Tx) ([2]uint, error) {
var instruction Instruction
func systemParser(tx *Tx, instruction Instruction, _ InnerInstructions, offset [2]uint) ([2]uint, error) {
var found bool
if offset[1] == 0 {
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 {
result := tx.rawTx
if !result.accountList[instruction.ProgramIDIndex].Equals(systemProgram) {
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])
}

30
tx.go
View File

@@ -48,6 +48,7 @@ type mevInfo struct {
}
type Tx struct {
rawTx *RawTx
Signer solana.PublicKey
Err interface{} `json:"err,omitempty"`
Swaps []Swap `json:"swaps,omitempty"`
@@ -65,20 +66,30 @@ type Tx struct {
BeforeSolBalance decimal.Decimal `json:"-"`
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 {
Address solana.PublicKey `json:"address"`
Mint solana.PublicKey `json:"address"`
TokenProgram solana.PublicKey `json:"token_program"`
Decimals uint8 `json:"decimals"`
Name string
Symbol string
Decimal uint8
Url string
Name string
Symbol string
Url string
TotalSupply *decimal.Decimal
SignerBalance *decimal.Decimal
TotalSupply *decimal.Decimal
}
func (tx *Tx) GetTxHash() string {
@@ -92,8 +103,9 @@ func (tx *Tx) GetTxHash() string {
return tx.cachedTxHash
}
func (tx *Tx) CheckPlatform(swap Swap, rawTx *RawTx) (string, decimal.Decimal) {
func (tx *Tx) CheckPlatform(swap Swap) (string, decimal.Decimal) {
// hasSolProgramRaydiumLaunchLabBonk
rawTx := tx.rawTx
var platform string
var platformFee decimal.Decimal
if len(tx.Platform) == 0 {