punm parser
This commit is contained in:
12
budget.go
12
budget.go
@@ -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
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
package pump_parser
|
||||
@@ -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
|
||||
|
||||
@@ -18,5 +18,4 @@ type SubscriptionMessage struct {
|
||||
|
||||
Block *BlockInfo
|
||||
Tx *pump_parser.Tx
|
||||
RawTx *pump_parser.RawTx
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
51
globals.go
51
globals.go
@@ -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 {
|
||||
|
||||
49
parser.go
49
parser.go
@@ -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
57
pump.go
@@ -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,
|
||||
|
||||
112
pumpamm.go
112
pumpamm.go
@@ -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,
|
||||
|
||||
19
system.go
19
system.go
@@ -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
30
tx.go
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user