diff --git a/budget.go b/budget.go index 9c823c6..347a623 100644 --- a/budget.go +++ b/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 } diff --git a/convert.go b/convert.go deleted file mode 100644 index 4ef4d31..0000000 --- a/convert.go +++ /dev/null @@ -1 +0,0 @@ -package pump_parser diff --git a/example/geyser/cmd/main.go b/example/geyser/cmd/main.go index bc6f3e1..1d115fb 100644 --- a/example/geyser/cmd/main.go +++ b/example/geyser/cmd/main.go @@ -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 diff --git a/example/geyser/message.go b/example/geyser/message.go index 227f4fb..23adbdb 100644 --- a/example/geyser/message.go +++ b/example/geyser/message.go @@ -18,5 +18,4 @@ type SubscriptionMessage struct { Block *BlockInfo Tx *pump_parser.Tx - RawTx *pump_parser.RawTx } diff --git a/example/geyser/pump.go b/example/geyser/pump.go index 1b6f325..f8c84a6 100644 --- a/example/geyser/pump.go +++ b/example/geyser/pump.go @@ -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) } } diff --git a/example/geyser/yellowstone.go b/example/geyser/yellowstone.go index 3c8e928..7978f2d 100644 --- a/example/geyser/yellowstone.go +++ b/example/geyser/yellowstone.go @@ -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 } diff --git a/example/tx.go b/example/tx.go index a8f0ed2..afb56ac 100644 --- a/example/tx.go +++ b/example/tx.go @@ -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) diff --git a/globals.go b/globals.go index 8e2d13a..f27fd02 100644 --- a/globals.go +++ b/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 { diff --git a/parser.go b/parser.go index e4466ca..bab757b 100644 --- a/parser.go +++ b/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 } diff --git a/pump.go b/pump.go index a3575d5..b5847c8 100644 --- a/pump.go +++ b/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, diff --git a/pumpamm.go b/pumpamm.go index b638e18..af2683f 100644 --- a/pumpamm.go +++ b/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, diff --git a/system.go b/system.go index ea82f71..2f36525 100644 --- a/system.go +++ b/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]) } diff --git a/tx.go b/tx.go index 052f731..e09c77f 100644 --- a/tx.go +++ b/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 {