punm parser
This commit is contained in:
46
budget.go
Normal file
46
budget.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package pump_parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type setComputeData struct {
|
||||||
|
Units uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func budgetParser(result *RawTx, instr Instruction, innerInstructions InnerInstructions, offset [2]uint, tx *Tx) ([2]uint, error) {
|
||||||
|
if offset[1] != 0 {
|
||||||
|
return increaseOffset(offset), nil
|
||||||
|
}
|
||||||
|
decode := result.Transaction.Message.Instructions[offset[0]].Data
|
||||||
|
discriminator := decode[0]
|
||||||
|
|
||||||
|
switch discriminator {
|
||||||
|
case setComputeUnitLimitDiscriminator:
|
||||||
|
return computeUnitLimitParser(result, offset, tx, decode[1:])
|
||||||
|
case setComputeUnitPriceDiscriminator:
|
||||||
|
return computeUnitPriceParser(result, offset, tx, decode[1:])
|
||||||
|
default:
|
||||||
|
return increaseOffset(offset), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeUnitLimitParser(_ *RawTx, 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) {
|
||||||
|
if len(decodedData) < 8 {
|
||||||
|
return increaseOffset(offset), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cuPrice := binary.LittleEndian.Uint64(decodedData[:8])
|
||||||
|
tx.CUPrice = decimal.NewFromInt(int64(cuPrice)).Div(decimal.NewFromInt(1000000)) // cu decimals
|
||||||
|
|
||||||
|
return increaseOffset(offset), nil
|
||||||
|
}
|
||||||
368
checking.go
Normal file
368
checking.go
Normal file
@@ -0,0 +1,368 @@
|
|||||||
|
package pump_parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gagliardetto/solana-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkBonkGmgnBuy(rawTx *RawTx) bool {
|
||||||
|
|
||||||
|
// 检查交易版本
|
||||||
|
var version, _ = rawTx.Version.(solana.MessageVersion)
|
||||||
|
if version != solana.MessageVersionLegacy {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查交易指令数量
|
||||||
|
if len(rawTx.Transaction.Message.Instructions) != 10 && len(rawTx.Transaction.Message.Instructions) != 9 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
accountList := rawTx.getAccountList()
|
||||||
|
// 检查 cu limit
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[0]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != solana.ComputeBudget {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(instruction.Accounts) != 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
accountId := accountList[instruction.Accounts[0]].String()
|
||||||
|
if !strings.HasPrefix(accountId, "jitodontfront1111111111151111111111111655") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 cu price
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[1]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != solana.ComputeBudget {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 ata.createIdempotent
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[2]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != solana.SPLAssociatedTokenAccountProgramID {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if instruction.Data.String() != "2" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(instruction.Accounts) < 4 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// gmgn 会先创建 wsol 账户, 而不是 token 账户
|
||||||
|
accountId := accountList[instruction.Accounts[3]]
|
||||||
|
if accountId != solana.WrappedSol {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 transfer
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[3]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != solana.SystemProgramID {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(instruction.Data) == 0 || instruction.Data[0] != 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 token.syncNative
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[4]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != solana.TokenProgramID {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if instruction.Data.String() != "J" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset := 5
|
||||||
|
if len(rawTx.Transaction.Message.Instructions) == 10 {
|
||||||
|
// 检查 ata.create
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[offset]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != solana.SPLAssociatedTokenAccountProgramID {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if instruction.Data.String() != "1" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset++
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 bonk.buy
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[offset]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != raydiumLaunchLabProgramID {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset++
|
||||||
|
|
||||||
|
// 检查 token.closeAccount
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[offset]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != solana.TokenProgramID {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if instruction.Data.String() != "A" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset++
|
||||||
|
|
||||||
|
// 检查 transfer
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[offset]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != solana.SystemProgramID {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(instruction.Data) == 0 || instruction.Data[0] != 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset++
|
||||||
|
|
||||||
|
// 检查 transfer
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[offset]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != solana.SystemProgramID {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(instruction.Data) == 0 || instruction.Data[0] != 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
axiomTxLoopupTable = solana.MustPublicKeyFromBase58("7RKtfATWCe98ChuwecNq8XCzAzfoK3DtZTprFsPMGtio")
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkBonkAxiomBuy(rawTx *RawTx) bool {
|
||||||
|
|
||||||
|
// 检查交易版本
|
||||||
|
var version, _ = rawTx.Version.(solana.MessageVersion)
|
||||||
|
if version == solana.MessageVersionLegacy || len(rawTx.Transaction.Message.AddressTableLookups) != 1 {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid transaction version or address table lookups: %v %v %v", rawTx.Transaction.Signatures[0].String(), version, len(rawTx.Transaction.Message.AddressTableLookups))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 addressLookupTable 是否是 Axiom 的
|
||||||
|
if rawTx.Transaction.Message.AddressTableLookups[0].AccountKey != axiomTxLoopupTable {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid address lookup table: %v", rawTx.Transaction.Signatures[0].String())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查交易指令数量
|
||||||
|
if len(rawTx.Transaction.Message.Instructions) != 10 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
accountList := rawTx.getAccountList()
|
||||||
|
// 检查 cu limit
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[0]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != solana.ComputeBudget {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid program ID for ComputeBudget: %v", programId)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(instruction.Accounts) != 1 {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid number of accounts for ComputeBudget: %v", len(instruction.Accounts))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
accountId := accountList[instruction.Accounts[0]].String()
|
||||||
|
if !strings.HasPrefix(accountId, "jitodontfront") || !strings.HasSuffix(accountId, "TradeWithAxiomDotTrade") {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid account ID for ComputeBudget: %v", accountId)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 cu price
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[1]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != solana.ComputeBudget {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid program ID for ComputeBudget: %v", programId)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 ata.createIdempotent
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[2]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != solana.SPLAssociatedTokenAccountProgramID {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid program ID for SPLAssociatedTokenAccount: %v", programId)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if instruction.Data.String() != "2" {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid data for SPLAssociatedTokenAccount: %v", instruction.Data.String())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(instruction.Accounts) < 4 {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid number of accounts for SPLAssociatedTokenAccount: %v", len(instruction.Accounts))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// axiom 会先创建 token 账户, 而不是 wsol 账户
|
||||||
|
accountId := accountList[instruction.Accounts[3]]
|
||||||
|
if accountId == solana.WrappedSol {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid account ID for SPLAssociatedTokenAccount, expected token account but got wsol: %v", accountId)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 ata.createIdempotent
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[3]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != solana.SPLAssociatedTokenAccountProgramID {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid program ID for SPLAssociatedTokenAccount2: %v", programId)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if instruction.Data.String() != "2" {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid data for SPLAssociatedTokenAccount2: %v", instruction.Data.String())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(instruction.Accounts) < 4 {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid number of accounts for SPLAssociatedTokenAccount2: %v", len(instruction.Accounts))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// axiom 会先创建 token 账户, 而不是 wsol 账户
|
||||||
|
accountId := accountList[instruction.Accounts[3]]
|
||||||
|
if accountId != solana.WrappedSol {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid account ID for SPLAssociatedTokenAccount2, expected wsol but got: %v", accountId)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 transfer
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[4]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != solana.SystemProgramID {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid program ID for SystemProgram3: %v", programId)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(instruction.Data) == 0 || instruction.Data[0] != 2 {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid data for SystemProgram transfer3: %v", instruction.Data)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 token.syncNative
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[5]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != solana.TokenProgramID {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid program ID for TokenProgram3: %v", programId)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if instruction.Data.String() != "J" {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid data for TokenProgram syncNative3: %v", instruction.Data.String())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 bonk.buy
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[6]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != raydiumLaunchLabProgramID {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 token.closeAccount
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[7]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != solana.TokenProgramID {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid program ID for TokenProgram closeAccount: %v", programId)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if instruction.Data.String() != "A" {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid data for TokenProgram closeAccount: %v", instruction.Data.String())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 transfer
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[8]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != solana.SystemProgramID {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid program ID for SystemProgram transfer4: %v", programId)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(instruction.Data) == 0 || instruction.Data[0] != 2 {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid data for SystemProgram transfer4: %v", instruction.Data)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 transfer
|
||||||
|
{
|
||||||
|
instruction := rawTx.Transaction.Message.Instructions[9]
|
||||||
|
programId := accountList[instruction.ProgramIDIndex]
|
||||||
|
if programId != solana.SystemProgramID {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid program ID for SystemProgram transfer5: %v", programId)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(instruction.Data) == 0 || instruction.Data[0] != 2 {
|
||||||
|
log.Printf("CheckBonkAxiomBuy: Invalid data for SystemProgram transfer5: %v", instruction.Data)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
180
consts.go
Normal file
180
consts.go
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
package pump_parser
|
||||||
|
|
||||||
|
import "github.com/gagliardetto/solana-go"
|
||||||
|
|
||||||
|
var platformFeeAddresses = map[solana.PublicKey]string{
|
||||||
|
solana.MustPublicKeyFromBase58("BB5dnY55FXS1e1NXqZDwCzgdYJdMCj3B92PU6Q5Fb6DT"): PlatformGMGN,
|
||||||
|
solana.MustPublicKeyFromBase58("AVUCZyuT35YSuj4RH7fwiyPu82Djn2Hfg7y2ND2XcnZH"): PlatformPhoton,
|
||||||
|
solana.MustPublicKeyFromBase58("7LCZckF6XXGQ1hDY6HFXBKWAtiUgL9QY5vj1C4Bn1Qjj"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("4V65jvcDG9DSQioUVqVPiUcUY9v6sb6HKtMnsxSKEz5S"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("CeA3sPZfWWToFEBmw5n1Y93tnV66Vmp8LacLzsVprgxZ"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("AaG6of1gbj1pbDumvbSiTuJhRCRkkUNaWVxijSbWvTJW"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("7oi1L8U9MRu5zDz5syFahsiLUric47LzvJBQX6r827ws"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("9kPrgLggBJ69tx1czYAbp7fezuUmL337BsqQTKETUEhP"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("DKyUs1xXMDy8Z11zNsLnUg3dy9HZf6hYZidB6WodcaGy"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("4FobGn5ZWYquoJkxMzh2VUAWvV36xMgxQ3M7uG1pGGhd"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("76sxKrPtgoJHDJvxwFHqb3cAXWfRHFLe3VpKcLCAHSEf"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("H2cDR3EkJjtTKDQKk8SJS48du9mhsdzQhy8xJx5UMqQK"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("8m5GkL7nVy95G4YVUbs79z873oVKqg2afgKRmqxsiiRm"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("4kuG6NsAFJNwqEkac8GFDMMheCGKUPEbaRVHHyFHSwWz"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("8vFGAKdwpn4hk7kc1cBgfWZzpyW3MEMDATDzVZhddeQb"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("86Vh4XGLW2b6nvWbRyDs4ScgMXbuvRCHT7WbUT3RFxKG"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("DZfEurFKFtSbdWZsKSDTqpqsQgvXxmESpvRtXkAdgLwM"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("5L2QKqDn5ukJSWGyqR4RPvFvwnBabKWqAqMzH4heaQNB"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("DYVeNgXGLAhZdeLMMYnCw1nPnMxkBN7fJnNpHmizTrrF"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("Hbj6XdxX6eV4nfbYTseysibp4zZJtVRRPn2J3BhGRuK9"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("846ah7iBSu9ApuCyEhA5xpnjHHX7d4QJKetWLbwzmJZ8"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("5BqYhuD4q1YD3DMAYkc1FeTu9vqQVYYdfBAmkZjamyZg"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("G9PhF9C9H83mAjjkdJz4MDqkufiTPMJkx7TnKE1kFyCp"): PlatformPepe,
|
||||||
|
solana.MustPublicKeyFromBase58("9RYJ3qr5eU5xAooqVcbmdeusjcViL5Nkiq7Gske3tiKq"): PlatformBullX,
|
||||||
|
solana.MustPublicKeyFromBase58("F4hJ3Ee3c5UuaorKAMfELBjYCjiiLH75haZTKqTywRP3"): PlatformBullX,
|
||||||
|
solana.MustPublicKeyFromBase58("47hEzz83VFR23rLTEeVm9A7eFzjJwjvdupPPmX3cePqF"): PlatformBanana,
|
||||||
|
solana.MustPublicKeyFromBase58("9yMwSPk9mrXSN7yDHUuZurAh1sjbJsfpUqjZ7SvVtdco"): PlatformTrojan,
|
||||||
|
solana.MustPublicKeyFromBase58("4mih95RmBqfHYvEfqq6uGGLp1Fr3gVS3VNSEa3JVRfQK"): PlatformRaybot,
|
||||||
|
solana.MustPublicKeyFromBase58("3udvfL24waJcLhskRAsStNMoNUvtyXdxrWQz4hgi953N"): PlatformMoonshot,
|
||||||
|
solana.MustPublicKeyFromBase58("3kxSQybWEeQZsMuNWMRJH4TxrhwoDwfv41TNMLRzFP5A"): PlatformMEVX,
|
||||||
|
solana.MustPublicKeyFromBase58("BS3CyJ9rRC4Tp8G7f86r6hGvuu3XdrVGNVpbNM9U5WRZ"): PlatformMEVX,
|
||||||
|
solana.MustPublicKeyFromBase58("97VmzkjX9w8gMFS2RnHTSjtMEDbifGXBq9pgosFdFnM"): PlatformTradeWiz,
|
||||||
|
solana.MustPublicKeyFromBase58("F34kcgMgCF7mYWkwLN3WN7KrFprr2NbwxuLvXx4fbztj"): PlatformSolTradingBot,
|
||||||
|
solana.MustPublicKeyFromBase58("96aFQc9qyqpjMfqdUeurZVYRrrwPJG2uPV6pceu4B1yb"): PlatformSolTradingBot,
|
||||||
|
solana.MustPublicKeyFromBase58("5wkyL2FLEcyUUgc3UeGntHTAfWfzDrVuxMnaMm7792Gk"): PlatformMoonshotMoney,
|
||||||
|
solana.MustPublicKeyFromBase58("MaestroUL88UBnZr3wfoN7hqmNWFi3ZYCGqZoJJHE36"): PlatformMaestro,
|
||||||
|
solana.MustPublicKeyFromBase58("ZG98FUCjb8mJ824Gbs6RsgVmr1FhXb2oNiJHa2dwmPd"): PlatformBonkBot,
|
||||||
|
}
|
||||||
|
|
||||||
|
var mevAgentFeeAddresses = map[solana.PublicKey]string{
|
||||||
|
solana.MustPublicKeyFromBase58("96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5"): MevAgentJito,
|
||||||
|
solana.MustPublicKeyFromBase58("HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe"): MevAgentJito,
|
||||||
|
solana.MustPublicKeyFromBase58("Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY"): MevAgentJito,
|
||||||
|
solana.MustPublicKeyFromBase58("ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49"): MevAgentJito,
|
||||||
|
solana.MustPublicKeyFromBase58("DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh"): MevAgentJito,
|
||||||
|
solana.MustPublicKeyFromBase58("ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt"): MevAgentJito,
|
||||||
|
solana.MustPublicKeyFromBase58("DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL"): MevAgentJito,
|
||||||
|
solana.MustPublicKeyFromBase58("3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT"): MevAgentJito,
|
||||||
|
solana.MustPublicKeyFromBase58("6fQaVhYZA4w3MBSXjJ81Vf6W1EDYeUPXpgVQ6UQyU1Av"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("4HiwLEP2Bzqj3hM2ENxJuzhcPCdsafwiet3oGkMkuQY4"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("7toBU3inhmrARGngC7z6SjyP85HgGMmCTEwGNRAcYnEK"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("8mR3wB1nh4D6J9RUCugxUpc6ya8w38LPxZ3ZjcBhgzws"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("6SiVU5WEwqfFapRuYCndomztEwDjvS5xgtEof3PLEGm9"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("TpdxgNJBWZRL8UXF5mrEsyWxDWx9HQexA9P1eTWQ42p"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("D8f3WkQu6dCF33cZxuAsrKHrGsqGP2yvAHf8mX6RXnwf"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("GQPFicsy3P3NXxB5piJohoxACqTvWE9fKpLgdsMduoHE"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("Ey2JEr8hDkgN8qKJGrLf2yFjRhW7rab99HVxwi5rcvJE"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("4iUgjMT8q2hNZnLuhpqZ1QtiV8deFPy2ajvvjEpKKgsS"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("3Rz8uD83QsU8wKvZbgWAPvCNDU6Fy8TSZTMcPm3RB6zt"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("DiTmWENJsHQdawVUUKnUXkconcpW4Jv52TnMWhkncF6t"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("HRyRhQ86t3H4aAtgvHVpUJmw64BDrb61gRiKcdKUXs5c"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("7y4whZmw388w1ggjToDLSBLv47drw5SUXcLk6jtmwixd"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("J9BMEWFbCBEjtQ1fG5Lo9kouX1HfrKQxeUxetwXrifBw"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("8U1JPQh3mVQ4F5jwRdFTBzvNRQaYFQppHQYoH38DJGSQ"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("Eb2KpSC8uMt9GmzyAEm5Eb1AAAgTjRaXWFjKyFXHZxF3"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("FCjUJZ1qozm1e8romw216qyfQMaaWKxWsuySnumVCCNe"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("ENxTEjSQ1YabmUpXAdCgevnHQ9MHdLv8tzFiuiYJqa13"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("6rYLG55Q9RpsPGvqdPNJs4z5WTxJVatMB8zV3WJhs5EK"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("Cix2bHfqPcKcM233mzxbLk14kSggUUiz2A87fJtGivXr"): MevAgent0slot,
|
||||||
|
solana.MustPublicKeyFromBase58("HWEoBxYs7ssKuudEjzjmpfJVX7Dvi7wescFsVx2L5yoY"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("7ks326H4LbMVaUC8nW5FpC5EoAf5eK5pf4Dsx4HDQLpq"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("95cfoy472fcQHaw4tPGBTKpn6ZQnfEPfBgDQx6gcRmRg"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("6GZVKMaoWry4UFiydjeQU9nmAxj3hEARAStQ7Hc2z6TB"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("HZTmLyC683y74TW3HtGbNX5orxjm2sPuZBEYwwSgAM8v"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("FogxVNs6Mm2w9rnGL1vkARSwJxvLE8mujTv3LK8RnUhF"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("3UQUKjhMKaY2S6bjcQD6yHB7utcZt5bfarRCmctpRtUd"): MevAgentBlocxRoute,
|
||||||
|
solana.MustPublicKeyFromBase58("TEMPaMeCRFAS9EKF53Jd6KpHxgL47uWLcpFArU1Fanq"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("noz3jAjPiHuBPqiSPkkugaJDkJscPuRhYnSpbi8UvC4"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("noz3str9KXfpKknefHji8L1mPgimezaiUyCHYMDv1GE"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("noz6uoYCDijhu1V7cutCpwxNiSovEwLdRHPwmgCGDNo"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("noz9EPNcT7WH6Sou3sr3GGjHQYVkN3DNirpbvDkv9YJ"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("nozc5yT15LazbLTFVZzoNZCwjh3yUtW86LoUyqsBu4L"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("nozFrhfnNGoyqwVuwPAW4aaGqempx4PU6g6D9CJMv7Z"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("nozievPk7HyK1Rqy1MPJwVQ7qQg2QoJGyP71oeDwbsu"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("noznbgwYnBLDHu8wcQVCEw6kDrXkPdKkydGJGNXGvL7"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("nozNVWs5N8mgzuD3qigrCG2UoKxZttxzZ85pvAQVrbP"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("nozpEGbwx4BcGp6pvEdAh1JoC2CQGZdU6HbNP1v2p6P"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("nozrhjhkCr3zXT3BiT4WCodYCUFeQvcdUkM7MqhKqge"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("nozrwQtWhEdrA6W8dkbt9gnUaMs52PdAv5byipnadq3"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("nozUacTVWub3cL4mJmGCYjKZTnE9RbdY5AP46iQgbPJ"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("nozWCyTPppJjRuw2fpzDhhWbW355fzosWSzrrMYB1Qk"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("nozWNju6dY353eMkMqURqwQEoM3SFgEKC6psLCSfUne"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("nozxNBgWohjR75vdspfxR5H9ceC7XXH99xpxhVGt3Bb"): MevAgentNozomi,
|
||||||
|
solana.MustPublicKeyFromBase58("NextbLoCkVtMGcV47JzewQdvBpLqT9TxQFozQkN98pE"): MevAgentNextBlock,
|
||||||
|
solana.MustPublicKeyFromBase58("NexTbLoCkWykbLuB1NkjXgFWkX9oAtcoagQegygXXA2"): MevAgentNextBlock,
|
||||||
|
solana.MustPublicKeyFromBase58("NeXTBLoCKs9F1y5PJS9CKrFNNLU1keHW71rfh7KgA1X"): MevAgentNextBlock,
|
||||||
|
solana.MustPublicKeyFromBase58("NexTBLockJYZ7QD7p2byrUa6df8ndV2WSd8GkbWqfbb"): MevAgentNextBlock,
|
||||||
|
solana.MustPublicKeyFromBase58("neXtBLock1LeC67jYd1QdAa32kbVeubsfPNTJC1V5At"): MevAgentNextBlock,
|
||||||
|
solana.MustPublicKeyFromBase58("nEXTBLockYgngeRmRrjDV31mGSekVPqZoMGhQEZtPVG"): MevAgentNextBlock,
|
||||||
|
solana.MustPublicKeyFromBase58("NEXTbLoCkB51HpLBLojQfpyVAMorm3zzKg7w9NFdqid"): MevAgentNextBlock,
|
||||||
|
solana.MustPublicKeyFromBase58("nextBLoCkPMgmG8ZgJtABeScP35qLa2AMCNKntAP7Xc"): MevAgentNextBlock,
|
||||||
|
solana.MustPublicKeyFromBase58("4ACfpUFoaSD9bfPdeu6DBt89gB6ENTeHBXCAi87NhDEE"): MevAgentHelius,
|
||||||
|
solana.MustPublicKeyFromBase58("D2L6yPZ2FmmmTKPgzaMKdhu6EWZcTpLy1Vhx8uvZe7NZ"): MevAgentHelius,
|
||||||
|
solana.MustPublicKeyFromBase58("9bnz4RShgq1hAnLnZbP8kbgBg1kEmcJBYQq3gQbmnSta"): MevAgentHelius,
|
||||||
|
solana.MustPublicKeyFromBase58("5VY91ws6B2hMmBFRsXkoAAdsPHBJwRfBht4DXox3xkwn"): MevAgentHelius,
|
||||||
|
solana.MustPublicKeyFromBase58("2nyhqdwKcJZR2vcqCyrYsaPVdAnFoJjiksCXJ7hfEYgD"): MevAgentHelius,
|
||||||
|
solana.MustPublicKeyFromBase58("2q5pghRs6arqVjRvT5gfgWfWcHWmw1ZuCzphgd5KfWGJ"): MevAgentHelius,
|
||||||
|
solana.MustPublicKeyFromBase58("wyvPkWjVZz1M8fHQnMMCDTQDbkManefNNhweYk5WkcF"): MevAgentHelius,
|
||||||
|
solana.MustPublicKeyFromBase58("3KCKozbAaF75qEU33jtzozcJ29yJuaLJTy2jFdzUY8bT"): MevAgentHelius,
|
||||||
|
solana.MustPublicKeyFromBase58("4vieeGHPYPG2MmyPRcYjdiDmmhN3ww7hsFNap8pVN3Ey"): MevAgentHelius,
|
||||||
|
solana.MustPublicKeyFromBase58("4TQLFNWK8AovT1gFvda5jfw2oJeRMKEmw7aH6MGBJ3or"): MevAgentHelius,
|
||||||
|
solana.MustPublicKeyFromBase58("node1PqAa3BWWzUnTHVbw8NJHC874zn9ngAkXjgWEej"): MevAgentNode1,
|
||||||
|
solana.MustPublicKeyFromBase58("node1UzzTxAAeBTpfZkQPJXBAqixsbdth11ba1NXLBG"): MevAgentNode1,
|
||||||
|
solana.MustPublicKeyFromBase58("node1Qm1bV4fwYnCurP8otJ9s5yrkPq7SPZ5uhj3Tsv"): MevAgentNode1,
|
||||||
|
solana.MustPublicKeyFromBase58("node1PUber6SFmSQgvf2ECmXsHP5o3boRSGhvJyPMX1"): MevAgentNode1,
|
||||||
|
solana.MustPublicKeyFromBase58("node1AyMbeqiVN6eoQzEAwCA6Pk826hrdqdAHR7cdJ3"): MevAgentNode1,
|
||||||
|
solana.MustPublicKeyFromBase58("node1YtWCoTwwVYTFLfS19zquRQzYX332hs1HEuRBjC"): MevAgentNode1,
|
||||||
|
solana.MustPublicKeyFromBase58("FLasHstqx11M8W56zrSEqkCyhMCCpr6ze6Mjdvqope5s"): MevAgentFlashBlock,
|
||||||
|
solana.MustPublicKeyFromBase58("FLasHXTqrbNvpWFB6grN47HGZfK6pze9HLNTgbukfPSk"): MevAgentFlashBlock,
|
||||||
|
solana.MustPublicKeyFromBase58("FLashhsorBmM9dLpuq6qATawcpqk1Y2aqaZfkd48iT3W"): MevAgentFlashBlock,
|
||||||
|
solana.MustPublicKeyFromBase58("FLASHRzANfcAKDuQ3RXv9hbkBy4WVEKDzoAgxJ56DiE4"): MevAgentFlashBlock,
|
||||||
|
solana.MustPublicKeyFromBase58("FLAsHZTRcf3Dy1APaz6j74ebdMC6Xx4g6i9YxjyrDybR"): MevAgentFlashBlock,
|
||||||
|
solana.MustPublicKeyFromBase58("FLAshyAyBcKb39KPxSzXcepiS8iDYUhDGwJcJDPX4g2B"): MevAgentFlashBlock,
|
||||||
|
solana.MustPublicKeyFromBase58("FLaSHJNm5dWYzEgnHJWWJP5ccu128Mu61NJLxUf7mUXU"): MevAgentFlashBlock,
|
||||||
|
solana.MustPublicKeyFromBase58("FLaSHR4Vv7sttd6TyDF4yR1bJyAxRwWKbohDytEMu3wL"): MevAgentFlashBlock,
|
||||||
|
solana.MustPublicKeyFromBase58("FLaShB3iXXTWE1vu9wQsChUKq3HFtpMAhb8kAh1pf1wi"): MevAgentFlashBlock,
|
||||||
|
solana.MustPublicKeyFromBase58("FLAShWTjcweNT4NSotpjpxAkwxUr2we3eXQGhpTVzRwy"): MevAgentFlashBlock,
|
||||||
|
solana.MustPublicKeyFromBase58("FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9"): MevAgentBlockRazor,
|
||||||
|
solana.MustPublicKeyFromBase58("6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG"): MevAgentBlockRazor,
|
||||||
|
solana.MustPublicKeyFromBase58("A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22"): MevAgentBlockRazor,
|
||||||
|
solana.MustPublicKeyFromBase58("Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm"): MevAgentBlockRazor,
|
||||||
|
solana.MustPublicKeyFromBase58("68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o"): MevAgentBlockRazor,
|
||||||
|
solana.MustPublicKeyFromBase58("4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9"): MevAgentBlockRazor,
|
||||||
|
solana.MustPublicKeyFromBase58("B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM"): MevAgentBlockRazor,
|
||||||
|
solana.MustPublicKeyFromBase58("5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf"): MevAgentBlockRazor,
|
||||||
|
solana.MustPublicKeyFromBase58("5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61"): MevAgentBlockRazor,
|
||||||
|
solana.MustPublicKeyFromBase58("295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV"): MevAgentBlockRazor,
|
||||||
|
solana.MustPublicKeyFromBase58("EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK"): MevAgentBlockRazor,
|
||||||
|
solana.MustPublicKeyFromBase58("BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6"): MevAgentBlockRazor,
|
||||||
|
solana.MustPublicKeyFromBase58("Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq"): MevAgentBlockRazor,
|
||||||
|
solana.MustPublicKeyFromBase58("AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S"): MevAgentBlockRazor,
|
||||||
|
}
|
||||||
|
|
||||||
|
var entryContractAddresses = map[solana.PublicKey]string{
|
||||||
|
solana.MustPublicKeyFromBase58("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"): EntryContractPumpFunAMM,
|
||||||
|
solana.MustPublicKeyFromBase58("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"): EntryContractPumpFun,
|
||||||
|
solana.MustPublicKeyFromBase58("GMgnVFR8Jb39LoXsEVzb3DvBy3ywCmdmJquHUy1Lrkqb"): EntryContractGMGN,
|
||||||
|
solana.MustPublicKeyFromBase58("Axiom3a2w1UbMt2SMgqSvRiuJFTPusDhwKamNgPTeNQ9"): EntryContractAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("AxiomfHaWDemCFBLBayqnEnNwE6b7B2Qz3UmzMpgbMG6"): EntryContractAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("AxiomxSitiyXyPjKgJ9XSrdhsydtZsskZTEDam3PxKcC"): EntryContractAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("BANANAjs7FJiPQqJTGFzkZJndT9o7UmKiYYGaJz6frGu"): EntryContractBananaGun,
|
||||||
|
solana.MustPublicKeyFromBase58("MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG"): EntryContractMoonshot,
|
||||||
|
solana.MustPublicKeyFromBase58("troY36YiPGqMyAYCNbEqYCdN2tb91Zf7bHcQt7KUi61"): EntryContractTrojan,
|
||||||
|
solana.MustPublicKeyFromBase58("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"): EntryContractRaydiumAMMV4,
|
||||||
|
solana.MustPublicKeyFromBase58("routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS"): EntryContractRaydiumAMMRouting,
|
||||||
|
solana.MustPublicKeyFromBase58("CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK"): EntryContractRaydiumConcetratedLiquidity,
|
||||||
|
solana.MustPublicKeyFromBase58("CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"): EntryContractRaydiumCPMM,
|
||||||
|
solana.MustPublicKeyFromBase58("LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo"): EntryContractMeteoraDLMM,
|
||||||
|
solana.MustPublicKeyFromBase58("Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB"): EntryContractMeteoraPools,
|
||||||
|
solana.MustPublicKeyFromBase58("JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"): EntryContractJupiterAggregatorV6,
|
||||||
|
solana.MustPublicKeyFromBase58("PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu"): EntryContractJupiterPerps,
|
||||||
|
solana.MustPublicKeyFromBase58("obriQD1zbpyLz95G5n7nJe6a4DPjpFwa5XYPoNm113y"): EntryContractObricV2,
|
||||||
|
solana.MustPublicKeyFromBase58("1oopBoJG58DgkUVKkEzKgyG9dvRmpgeEm1AVjoHkF78"): EntryContractLoopscale,
|
||||||
|
solana.MustPublicKeyFromBase58("fragnAis7Bp6FTsMoa6YcH8UffhEw43Ph79qAiK3iF3"): EntryContractFragmetricLiquidStaking,
|
||||||
|
solana.MustPublicKeyFromBase58("BSfD6SHZigAfDWSjzD5Q41jw8LmKwtmjskPH9XW1mrRW"): EntryContractPhoton,
|
||||||
|
solana.MustPublicKeyFromBase58("2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c"): EntryContractLfinitySwapV2,
|
||||||
|
solana.MustPublicKeyFromBase58("SoLFiHG9TfgtdUXUjWAxi3LtvYuFyDLVhBWxdMZxyCe"): EntryContractSolFi,
|
||||||
|
solana.MustPublicKeyFromBase58("NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P"): EntryContractNumeraire,
|
||||||
|
solana.MustPublicKeyFromBase58("b1oomGGqPKGD6errbyfbVMBuzSC8WtAAYo8MwNafWW1"): EntryContractBloomRouter,
|
||||||
|
solana.MustPublicKeyFromBase58("6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma"): EntryContractOKXAggregatorV2,
|
||||||
|
solana.MustPublicKeyFromBase58("FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X"): EntryContractFluxbeamDEX,
|
||||||
|
solana.MustPublicKeyFromBase58("NoVA1TmDUqksaj2hB1nayFkPysjJbFiU76dT4qPw2wm"): EntryContractNovaBotsProgram,
|
||||||
|
solana.MustPublicKeyFromBase58("E6YoRP3adE5XYneSseLee15wJshDxCsmyD2WtLvAmfLi"): EntryContractTaggedSearcher,
|
||||||
|
solana.MustPublicKeyFromBase58("MAyhSmzXzV1pTf7LsNkrNwkWKTo4ougAJ1PPg47MD4e"): EntryContractMayhem,
|
||||||
|
}
|
||||||
1
convert.go
Normal file
1
convert.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package pump_parser
|
||||||
10
discriminator.go
Normal file
10
discriminator.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package pump_parser
|
||||||
|
|
||||||
|
import "crypto/sha256"
|
||||||
|
|
||||||
|
func calculateDiscriminator(instructionName string) [8]byte {
|
||||||
|
hash := sha256.Sum256([]byte(instructionName))
|
||||||
|
var discriminator [8]byte
|
||||||
|
copy(discriminator[:], hash[:8])
|
||||||
|
return discriminator
|
||||||
|
}
|
||||||
106
enum.go
Normal file
106
enum.go
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package pump_parser
|
||||||
|
|
||||||
|
const (
|
||||||
|
MevAgentJito = "jito"
|
||||||
|
MevAgent0slot = "0slot"
|
||||||
|
MevAgentBlocxRoute = "blocxroute"
|
||||||
|
MevAgentNozomi = "nozomi"
|
||||||
|
MevAgentNextBlock = "nextblock"
|
||||||
|
MevAgentHelius = "helius"
|
||||||
|
MevAgentNode1 = "node1"
|
||||||
|
MevAgentFlashBlock = "flashBlock"
|
||||||
|
MevAgentUnknown = "unknown"
|
||||||
|
MevAgentBlockRazor = "blockrazor"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
EntryContractPumpFunAMM = "pumpFunAMM"
|
||||||
|
EntryContractPumpFun = "pumpFun"
|
||||||
|
EntryContractGMGN = "gmgn"
|
||||||
|
EntryContractAxiom = "axiom"
|
||||||
|
EntryContractBananaGun = "bananaGun"
|
||||||
|
EntryContractMoonshot = "moonshot"
|
||||||
|
EntryContractTrojan = "trojan"
|
||||||
|
EntryContractRaydiumAMMV4 = "raydiumAMMV4"
|
||||||
|
EntryContractRaydiumAMMRouting = "raydiumAMMRouting"
|
||||||
|
EntryContractRaydiumConcetratedLiquidity = "raydiumConcetratedLiquidity"
|
||||||
|
EntryContractRaydiumCPMM = "raydiumCPMM"
|
||||||
|
EntryContractMeteoraDLMM = "meteoraDLMM"
|
||||||
|
EntryContractMeteoraPools = "meteoraPools"
|
||||||
|
EntryContractJupiterAggregatorV6 = "jupiterAggregatorV6"
|
||||||
|
EntryContractJupiterPerps = "jupiterPerps"
|
||||||
|
EntryContractObricV2 = "obricV2"
|
||||||
|
EntryContractLoopscale = "loopscale"
|
||||||
|
EntryContractFragmetricLiquidStaking = "fragmetricLiquidStaking"
|
||||||
|
EntryContractPhoton = "photon"
|
||||||
|
EntryContractLfinitySwapV2 = "lfinitySwapV2"
|
||||||
|
EntryContractSolFi = "solFi"
|
||||||
|
EntryContractNumeraire = "numeraire"
|
||||||
|
EntryContractBloomRouter = "bloomRouter"
|
||||||
|
EntryContractOKXAggregatorV2 = "oKXAggregatorV2"
|
||||||
|
EntryContractFluxbeamDEX = "fluxbeamDEX"
|
||||||
|
EntryContractNovaBotsProgram = "novaBotsProgram"
|
||||||
|
EntryContractTaggedSearcher = "taggedSearcher"
|
||||||
|
EntryContractMayhem = "pumpMayhem"
|
||||||
|
EntryContractUnknown = "unknown"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PlatformGMGN = "gmgn"
|
||||||
|
PlatformPhoton = "photon"
|
||||||
|
PlatformAxiom = "axiom"
|
||||||
|
PlatformPepe = "pepe" // pepeboost & xxyy
|
||||||
|
PlatformBullX = "bullx"
|
||||||
|
PlatformBanana = "banana"
|
||||||
|
PlatformTrojan = "trojan"
|
||||||
|
PlatformRaybot = "raybot"
|
||||||
|
PlatformMoonshot = "moonshot"
|
||||||
|
PlatformMEVX = "mevx"
|
||||||
|
PlatformTradeWiz = "tradewiz"
|
||||||
|
PlatformSolTradingBot = "soltradingbot"
|
||||||
|
PlatformMoonshotMoney = "moonshot.money"
|
||||||
|
PlatformMaestro = "maestro"
|
||||||
|
PlatformBonkBot = "bonkbot"
|
||||||
|
|
||||||
|
// used to flag transactions impersonating platform users
|
||||||
|
PlatformFake = "fake"
|
||||||
|
PlatformNone = "none"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SolProgramPump = "Pump"
|
||||||
|
SolProgramRaydiumV4 = "RaydiumV4"
|
||||||
|
SolProgramRaydiumCLMM = "RaydiumCLMM"
|
||||||
|
SolProgramRaydiumCPMM = "RaydiumCPMM"
|
||||||
|
SolProgramMeteoraDLMM = "MeteoraDLMM"
|
||||||
|
SolProgramOrcaWhirPool = "OrcaWhirPool"
|
||||||
|
SolProgramPumpAMM = "PumpAMM"
|
||||||
|
SolProgramMeteoraAmmV2 = "MeteoraAmmV2"
|
||||||
|
SolProgramMeteoraBondingCurve = "MeteoraBondingCurve"
|
||||||
|
SolProgramMeteoraPools = "MeteoraPools"
|
||||||
|
SolProgramRaydiumLaunchLab = "RaydiumLaunchLab"
|
||||||
|
SolProgramRaydiumLaunchLabBonk = "RaydiumLaunchLab-Bonk"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PUMP
|
||||||
|
const (
|
||||||
|
NewPump = "pump"
|
||||||
|
NewRaydium = "raydium"
|
||||||
|
GraduatedSoon = "graduatedsoon"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetConditionByProgram(program string) []string {
|
||||||
|
if program == SolProgramRaydiumLaunchLabBonk {
|
||||||
|
return []string{SolProgramRaydiumLaunchLabBonk, SolProgramRaydiumV4, SolProgramRaydiumCPMM}
|
||||||
|
}
|
||||||
|
|
||||||
|
return []string{SolProgramPump, SolProgramPumpAMM}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
TxEventAddLP = "add"
|
||||||
|
TxEventRemoveLP = "remove"
|
||||||
|
TxEventBuy = "buy"
|
||||||
|
TxEventSell = "sell"
|
||||||
|
TxEventBurn = "burn"
|
||||||
|
)
|
||||||
7
example/geyser/Makefile
Normal file
7
example/geyser/Makefile
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
protoc:
|
||||||
|
protoc \
|
||||||
|
--go_out=./proto \
|
||||||
|
--go_opt=paths=source_relative \
|
||||||
|
--go-grpc_out=./proto \
|
||||||
|
--go-grpc_opt=paths=source_relative \
|
||||||
|
--proto_path ./proto/ ./proto/*.proto
|
||||||
94
example/geyser/cmd/main.go
Normal file
94
example/geyser/cmd/main.go
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
parser "github.com/thloyi/pump-parser"
|
||||||
|
example "github.com/thloyi/pump-parser/example"
|
||||||
|
"github.com/thloyi/pump-parser/example/geyser"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
//pool, err := ants.NewPool(100, ants.WithPreAlloc(true), ants.WithNonblocking(true))
|
||||||
|
//if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
//}
|
||||||
|
//xt := tracker.NewTwitterTracker(nil) // Initialize Twitter tracker if needed
|
||||||
|
// laserstream-mainnet-slc.helius-rpc.com:80
|
||||||
|
|
||||||
|
ch := make(chan geyser.SubscriptionMessage, 1)
|
||||||
|
go geyser.RunLoopWithReConnect(context.Background(), "127.0.0.1:10001", parser.SolProgramPump, ch)
|
||||||
|
// var tokenTxs = make(map[string]*types.Tx)
|
||||||
|
currentBlock := uint64(0)
|
||||||
|
for msg := range ch {
|
||||||
|
if msg.Tx == nil {
|
||||||
|
block := msg.Block
|
||||||
|
if block.Slot%100 == 0 {
|
||||||
|
fmt.Printf("slot: %d, hash: %s, time: %s, height: %d, estimate delay second: %d\n",
|
||||||
|
block.Slot, block.BlockHash, time.Unix(block.BlockTime, 0).Format("2006-01-02 15:04:05"), block.Height, msg.EstimateDelaySecond)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ptx := msg.Tx
|
||||||
|
//data, _ := json.Marshal(tx)
|
||||||
|
//fmt.Println(string(data))
|
||||||
|
//continue
|
||||||
|
|
||||||
|
//if tx.Token0Address != "HRHLDjqFBhNeyTXUuZQE9gTy5z2112qeQBS9U79NHyyp" {
|
||||||
|
// continue
|
||||||
|
//}
|
||||||
|
//if tx.Program != parser.SolProgramPump {
|
||||||
|
// continue
|
||||||
|
//}
|
||||||
|
if currentBlock == ptx.Block {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理交易
|
||||||
|
txErr, ok := ptx.Err.(*geyser.TransactionError)
|
||||||
|
var customerErrCode uint32
|
||||||
|
var instructorErrIndex uint8
|
||||||
|
if ok {
|
||||||
|
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)
|
||||||
|
if len(txs) == 0 {
|
||||||
|
fmt.Printf("tx is empty, block: %d, tx %s \n", ptx.Block, ptx.GetTxHash())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
printed := false
|
||||||
|
for _, tx := range txs {
|
||||||
|
if tx.Program != parser.SolProgramPump {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
printed = true
|
||||||
|
fmt.Printf("t: %s, block: %d, hash: %s, signer: %s, program: %s, event: %s, token1: %s, cuPrice: %s, mevAgent: %s, mevFee: %s, platform: %s, platformFee: %s, entryContract: %s, mayhem: %t\n",
|
||||||
|
time.Now().Format(time.RFC3339Nano),
|
||||||
|
tx.Block, tx.GetTxHash(), tx.Maker, tx.Program, tx.Event, tx.Token1Amount, tx.CUPrice, tx.MevAgent, tx.MevAgentFee, tx.Platform, tx.PlatformFee, tx.EntryContract, tx.Mayhem)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !printed {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
//fmt.Printf("t: %s, block: %d, hash: %s, signer: %s, program: %s, event: %s, token0: %s, token1: %s, signer before sol :%s, after sol: %s, after token: %s, tokencreator: %s, tokenprogram: %s, mayhem: %t\n",
|
||||||
|
// time.Now().Format(time.RFC3339Nano),
|
||||||
|
// tx.Block, tx.GetTxHash(), tx.Maker, tx.Program, tx.Event, tx.Token0Amount.String(), tx.Token1Amount.String(),
|
||||||
|
// tx.BeforeSolBalance, tx.AfterSOLBalance, tx.AfterSignerToken0Balance, tx.TokenCreator, tx.Token0Program, tx.Mayhem)
|
||||||
|
|
||||||
|
}
|
||||||
|
currentBlock = ptx.Block
|
||||||
|
//
|
||||||
|
//if tx.Event == "create" {
|
||||||
|
// if err := pool.Submit(func() {
|
||||||
|
// now := time.Now()
|
||||||
|
// xt.AddToken(tx.Token)
|
||||||
|
// log.Printf("Add token %s, cost: %s %s %v %v", tx.Token.Address, time.Since(now), tx.Token.Twitter, xt.DuplicateCount(tx.Token.Address), xt.HasTwitter(tx.Token.Address))
|
||||||
|
// }); err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
277
example/geyser/error.go
Normal file
277
example/geyser/error.go
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
package geyser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TransactionErrorVariant uint32
|
||||||
|
type InstructionErrorVariant uint32
|
||||||
|
|
||||||
|
type InstructionErrorEnum struct {
|
||||||
|
Index uint8
|
||||||
|
Variant InstructionErrorVariant
|
||||||
|
rest []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomInstructionErrorEnum struct {
|
||||||
|
Code uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
AccountInUse TransactionErrorVariant = iota
|
||||||
|
AccountLoadedTwice
|
||||||
|
AccountNotFound
|
||||||
|
ProgramAccountNotFound
|
||||||
|
InsufficientFundsForFee
|
||||||
|
InvalidAccountForFee
|
||||||
|
AlreadyProcessed
|
||||||
|
BlockhashNotFound
|
||||||
|
|
||||||
|
InstructionError //InstructionError(u8, InstructionError),
|
||||||
|
|
||||||
|
CallChainTooDeep
|
||||||
|
MissingSignatureForFee
|
||||||
|
InvalidAccountIndex
|
||||||
|
SignatureFailure
|
||||||
|
InvalidProgramForExecution
|
||||||
|
SanitizeFailure
|
||||||
|
ClusterMaintenance
|
||||||
|
AccountBorrowOutstanding
|
||||||
|
WouldExceedMaxBlockCostLimit
|
||||||
|
UnsupportedVersion
|
||||||
|
InvalidWritableAccount
|
||||||
|
WouldExceedMaxAccountCostLimit
|
||||||
|
WouldExceedAccountDataBlockLimit
|
||||||
|
TooManyAccountLocks
|
||||||
|
AddressLookupTableNotFound
|
||||||
|
InvalidAddressLookupTableOwner
|
||||||
|
InvalidAddressLookupTableData
|
||||||
|
InvalidAddressLookupTableIndex
|
||||||
|
InvalidRentPayingAccount
|
||||||
|
WouldExceedMaxVoteCostLimit
|
||||||
|
WouldExceedAccountDataTotalLimit
|
||||||
|
|
||||||
|
DuplicateInstruction //DuplicateInstruction(u8),
|
||||||
|
|
||||||
|
/*
|
||||||
|
InsufficientFundsForRent {
|
||||||
|
account_index: u8,
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
InsufficientFundsForRent
|
||||||
|
|
||||||
|
MaxLoadedAccountsDataSizeExceeded
|
||||||
|
InvalidLoadedAccountsDataSizeLimit
|
||||||
|
ResanitizationNeeded
|
||||||
|
|
||||||
|
/*
|
||||||
|
ProgramExecutionTemporarilyRestricted {
|
||||||
|
account_index: u8,
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
ProgramExecutionTemporarilyRestricted
|
||||||
|
|
||||||
|
UnbalancedTransaction
|
||||||
|
ProgramCacheHitMaxLimit
|
||||||
|
CommitCancelled
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
GenericError InstructionErrorVariant = iota
|
||||||
|
/// The arguments provided to a program were invalid
|
||||||
|
InvalidArgument
|
||||||
|
/// An instruction's data contents were invalid
|
||||||
|
InvalidInstructionData
|
||||||
|
/// An account's data contents was invalid
|
||||||
|
InvalidAccountData
|
||||||
|
/// An account's data was too small
|
||||||
|
AccountDataTooSmall
|
||||||
|
/// An account's balance was too small to complete the instruction
|
||||||
|
InsufficientFunds
|
||||||
|
/// The account did not have the expected program id
|
||||||
|
IncorrectProgramId
|
||||||
|
/// A signature was required but not found
|
||||||
|
MissingRequiredSignature
|
||||||
|
/// An initialize instruction was sent to an account that has already been initialized.
|
||||||
|
AccountAlreadyInitialized
|
||||||
|
/// An attempt to operate on an account that hasn't been initialized.
|
||||||
|
UninitializedAccount
|
||||||
|
/// Program's instruction lamport balance does not equal the balance after the instruction
|
||||||
|
UnbalancedInstruction
|
||||||
|
/// Program illegally modified an account's program id
|
||||||
|
ModifiedProgramId
|
||||||
|
/// Program spent the lamports of an account that doesn't belong to it
|
||||||
|
ExternalAccountLamportSpend
|
||||||
|
/// Program modified the data of an account that doesn't belong to it
|
||||||
|
ExternalAccountDataModified
|
||||||
|
/// Read-only account's lamports modified
|
||||||
|
ReadonlyLamportChange
|
||||||
|
/// Read-only account's data was modified
|
||||||
|
ReadonlyDataModified
|
||||||
|
/// An account was referenced more than once in a single instruction
|
||||||
|
// Deprecated, instructions can now contain duplicate accounts
|
||||||
|
DuplicateAccountIndex
|
||||||
|
/// Executable bit on account changed, but shouldn't have
|
||||||
|
ExecutableModified
|
||||||
|
/// Rent_epoch account changed, but shouldn't have
|
||||||
|
RentEpochModified
|
||||||
|
/// The instruction expected additional account keys
|
||||||
|
NotEnoughAccountKeys
|
||||||
|
/// Program other than the account's owner changed the size of the account data
|
||||||
|
AccountDataSizeChanged
|
||||||
|
/// The instruction expected an executable account
|
||||||
|
AccountNotExecutable
|
||||||
|
/// Failed to borrow a reference to account data, already borrowed
|
||||||
|
AccountBorrowFailed
|
||||||
|
/// Account data has an outstanding reference after a program's execution
|
||||||
|
InstructionAccountBorrowOutstanding
|
||||||
|
/// The same account was multiply passed to an on-chain program's entrypoint, but the program
|
||||||
|
/// modified them differently. A program can only modify one instance of the account because
|
||||||
|
/// the runtime cannot determine which changes to pick or how to merge them if both are modified
|
||||||
|
DuplicateAccountOutOfSync
|
||||||
|
/// Allows on-chain programs to implement program-specific error types and see them returned
|
||||||
|
/// by the Solana runtime. A program-specific error may be any type that is represented as
|
||||||
|
/// or serialized to a u32 integer.
|
||||||
|
|
||||||
|
Custom // Custom(u32),
|
||||||
|
|
||||||
|
/// The return value from the program was invalid. Valid errors are either a defined builtin
|
||||||
|
/// error value or a user-defined error in the lower 32 bits.
|
||||||
|
InvalidError
|
||||||
|
/// Executable account's data was modified
|
||||||
|
ExecutableDataModified
|
||||||
|
/// Executable account's lamports modified
|
||||||
|
ExecutableLamportChange
|
||||||
|
/// Executable accounts must be rent exempt
|
||||||
|
ExecutableAccountNotRentExempt
|
||||||
|
/// Unsupported program id
|
||||||
|
UnsupportedProgramId
|
||||||
|
/// Cross-program invocation call depth too deep
|
||||||
|
CallDepth
|
||||||
|
/// An account required by the instruction is missing
|
||||||
|
MissingAccount
|
||||||
|
/// Cross-program invocation reentrancy not allowed for this instruction
|
||||||
|
ReentrancyNotAllowed
|
||||||
|
/// Length of the seed is too long for address generation
|
||||||
|
MaxSeedLengthExceeded
|
||||||
|
/// Provided seeds do not result in a valid address
|
||||||
|
InvalidSeeds
|
||||||
|
/// Failed to reallocate account data of this length
|
||||||
|
InvalidRealloc
|
||||||
|
/// Computational budget exceeded
|
||||||
|
ComputationalBudgetExceeded
|
||||||
|
/// Cross-program invocation with unauthorized signer or writable account
|
||||||
|
PrivilegeEscalation
|
||||||
|
/// Failed to create program execution environment
|
||||||
|
ProgramEnvironmentSetupFailure
|
||||||
|
/// Program failed to complete
|
||||||
|
ProgramFailedToComplete
|
||||||
|
/// Program failed to compile
|
||||||
|
ProgramFailedToCompile
|
||||||
|
/// Account is immutable
|
||||||
|
Immutable
|
||||||
|
/// Incorrect authority provided
|
||||||
|
IncorrectAuthority
|
||||||
|
/// Failed to serialize or deserialize account data
|
||||||
|
///
|
||||||
|
/// Warning: This error should never be emitted by the runtime.
|
||||||
|
///
|
||||||
|
/// This error includes strings from the underlying 3rd party Borsh crate
|
||||||
|
/// which can be dangerous because the error strings could change across
|
||||||
|
/// Borsh versions. Only programs can use this error because they are
|
||||||
|
/// consistent across Solana software versions.
|
||||||
|
/// string values from this error should not be used in
|
||||||
|
|
||||||
|
BorshIoError // BorshIoError(String)
|
||||||
|
|
||||||
|
// An account does not have enough lamports to be rent-exempt
|
||||||
|
AccountNotRentExempt
|
||||||
|
/// Invalid account owner
|
||||||
|
InvalidAccountOwner
|
||||||
|
/// Program arithmetic overflowed
|
||||||
|
ArithmeticOverflow
|
||||||
|
/// Unsupported sysvar
|
||||||
|
UnsupportedSysvar
|
||||||
|
/// Illegal account owner
|
||||||
|
IllegalOwner
|
||||||
|
/// Accounts data allocations exceeded the maximum allowed per transaction
|
||||||
|
MaxAccountsDataAllocationsExceeded
|
||||||
|
/// Max accounts exceeded
|
||||||
|
MaxAccountsExceeded
|
||||||
|
/// Max instruction trace length exceeded
|
||||||
|
MaxInstructionTraceLengthExceeded
|
||||||
|
/// Builtin programs must consume compute units
|
||||||
|
BuiltinProgramsMustConsumeComputeUnits
|
||||||
|
)
|
||||||
|
|
||||||
|
type TransactionError struct {
|
||||||
|
Variant TransactionErrorVariant
|
||||||
|
rest []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidTransactionError = errors.New("invalid transaction error")
|
||||||
|
NotAnInstructionError = errors.New("not an instruction error")
|
||||||
|
NotACustomInstructionError = errors.New("not a custom instruction error")
|
||||||
|
UnsupportedInstructionError = errors.New("unsupported instruction error")
|
||||||
|
)
|
||||||
|
|
||||||
|
func DecodeTransactionError(data []byte) (*TransactionError, error) {
|
||||||
|
if len(data) < 4 {
|
||||||
|
return nil, ErrInvalidTransactionError
|
||||||
|
}
|
||||||
|
|
||||||
|
var err TransactionError
|
||||||
|
variant := binary.LittleEndian.Uint32(data[:4])
|
||||||
|
if variant > uint32(CommitCancelled) {
|
||||||
|
return nil, UnsupportedInstructionError
|
||||||
|
}
|
||||||
|
err.Variant = TransactionErrorVariant(variant)
|
||||||
|
err.rest = data[4:]
|
||||||
|
|
||||||
|
return &err, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *TransactionError) GetCustomErrorCode() (uint8, uint32, error) {
|
||||||
|
instr, err := e.GetInstructionError()
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
custom, err := instr.Custom()
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
return instr.Index, custom.Code, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *TransactionError) GetInstructionError() (*InstructionErrorEnum, error) {
|
||||||
|
if e.Variant != InstructionError {
|
||||||
|
return nil, NotAnInstructionError
|
||||||
|
}
|
||||||
|
if len(e.rest) < 5 {
|
||||||
|
return nil, NotAnInstructionError
|
||||||
|
}
|
||||||
|
|
||||||
|
var err InstructionErrorEnum
|
||||||
|
err.Index = e.rest[0]
|
||||||
|
variant := binary.LittleEndian.Uint32(e.rest[1:5])
|
||||||
|
if variant > uint32(BuiltinProgramsMustConsumeComputeUnits) {
|
||||||
|
return nil, UnsupportedInstructionError
|
||||||
|
}
|
||||||
|
err.Variant = InstructionErrorVariant(binary.LittleEndian.Uint32(e.rest[1:5]))
|
||||||
|
err.rest = e.rest[5:]
|
||||||
|
return &err, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *InstructionErrorEnum) Custom() (*CustomInstructionErrorEnum, error) {
|
||||||
|
if e.Variant != Custom {
|
||||||
|
return nil, NotACustomInstructionError
|
||||||
|
}
|
||||||
|
if len(e.rest) < 4 {
|
||||||
|
return nil, NotACustomInstructionError
|
||||||
|
}
|
||||||
|
var custom CustomInstructionErrorEnum
|
||||||
|
custom.Code = binary.LittleEndian.Uint32(e.rest[:4])
|
||||||
|
return &custom, nil
|
||||||
|
}
|
||||||
22
example/geyser/message.go
Normal file
22
example/geyser/message.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package geyser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/thloyi/pump-parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BlockInfo struct {
|
||||||
|
Slot uint64
|
||||||
|
BlockTime int64
|
||||||
|
BlockHash string
|
||||||
|
Height uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubscriptionMessage struct {
|
||||||
|
EstimateDelaySecond int64
|
||||||
|
|
||||||
|
Reconnect bool
|
||||||
|
|
||||||
|
Block *BlockInfo
|
||||||
|
Tx *pump_parser.Tx
|
||||||
|
RawTx *pump_parser.RawTx
|
||||||
|
}
|
||||||
3113
example/geyser/proto/geyser.pb.go
Normal file
3113
example/geyser/proto/geyser.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
272
example/geyser/proto/geyser.proto
Normal file
272
example/geyser/proto/geyser.proto
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
import public "solana-storage.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/rpcpool/yellowstone-grpc/examples/golang/proto";
|
||||||
|
|
||||||
|
package geyser;
|
||||||
|
|
||||||
|
service Geyser {
|
||||||
|
rpc Subscribe(stream SubscribeRequest) returns (stream SubscribeUpdate) {}
|
||||||
|
rpc Ping(PingRequest) returns (PongResponse) {}
|
||||||
|
rpc GetLatestBlockhash(GetLatestBlockhashRequest) returns (GetLatestBlockhashResponse) {}
|
||||||
|
rpc GetBlockHeight(GetBlockHeightRequest) returns (GetBlockHeightResponse) {}
|
||||||
|
rpc GetSlot(GetSlotRequest) returns (GetSlotResponse) {}
|
||||||
|
rpc IsBlockhashValid(IsBlockhashValidRequest) returns (IsBlockhashValidResponse) {}
|
||||||
|
rpc GetVersion(GetVersionRequest) returns (GetVersionResponse) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CommitmentLevel {
|
||||||
|
PROCESSED = 0;
|
||||||
|
CONFIRMED = 1;
|
||||||
|
FINALIZED = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SlotStatus {
|
||||||
|
SLOT_PROCESSED = 0;
|
||||||
|
SLOT_CONFIRMED = 1;
|
||||||
|
SLOT_FINALIZED = 2;
|
||||||
|
SLOT_FIRST_SHRED_RECEIVED = 3;
|
||||||
|
SLOT_COMPLETED = 4;
|
||||||
|
SLOT_CREATED_BANK = 5;
|
||||||
|
SLOT_DEAD = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequest {
|
||||||
|
map<string, SubscribeRequestFilterAccounts> accounts = 1;
|
||||||
|
map<string, SubscribeRequestFilterSlots> slots = 2;
|
||||||
|
map<string, SubscribeRequestFilterTransactions> transactions = 3;
|
||||||
|
map<string, SubscribeRequestFilterTransactions> transactions_status = 10;
|
||||||
|
map<string, SubscribeRequestFilterBlocks> blocks = 4;
|
||||||
|
map<string, SubscribeRequestFilterBlocksMeta> blocks_meta = 5;
|
||||||
|
map<string, SubscribeRequestFilterEntry> entry = 8;
|
||||||
|
optional CommitmentLevel commitment = 6;
|
||||||
|
repeated SubscribeRequestAccountsDataSlice accounts_data_slice = 7;
|
||||||
|
optional SubscribeRequestPing ping = 9;
|
||||||
|
optional uint64 from_slot = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequestFilterAccounts {
|
||||||
|
repeated string account = 2;
|
||||||
|
repeated string owner = 3;
|
||||||
|
repeated SubscribeRequestFilterAccountsFilter filters = 4;
|
||||||
|
optional bool nonempty_txn_signature = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequestFilterAccountsFilter {
|
||||||
|
oneof filter {
|
||||||
|
SubscribeRequestFilterAccountsFilterMemcmp memcmp = 1;
|
||||||
|
uint64 datasize = 2;
|
||||||
|
bool token_account_state = 3;
|
||||||
|
SubscribeRequestFilterAccountsFilterLamports lamports = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequestFilterAccountsFilterMemcmp {
|
||||||
|
uint64 offset = 1;
|
||||||
|
oneof data {
|
||||||
|
bytes bytes = 2;
|
||||||
|
string base58 = 3;
|
||||||
|
string base64 = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequestFilterAccountsFilterLamports {
|
||||||
|
oneof cmp {
|
||||||
|
uint64 eq = 1;
|
||||||
|
uint64 ne = 2;
|
||||||
|
uint64 lt = 3;
|
||||||
|
uint64 gt = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequestFilterSlots {
|
||||||
|
optional bool filter_by_commitment = 1;
|
||||||
|
optional bool interslot_updates = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequestFilterTransactions {
|
||||||
|
optional bool vote = 1;
|
||||||
|
optional bool failed = 2;
|
||||||
|
optional string signature = 5;
|
||||||
|
repeated string account_include = 3;
|
||||||
|
repeated string account_exclude = 4;
|
||||||
|
repeated string account_required = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequestFilterBlocks {
|
||||||
|
repeated string account_include = 1;
|
||||||
|
optional bool include_transactions = 2;
|
||||||
|
optional bool include_accounts = 3;
|
||||||
|
optional bool include_entries = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequestFilterBlocksMeta {}
|
||||||
|
|
||||||
|
message SubscribeRequestFilterEntry {}
|
||||||
|
|
||||||
|
message SubscribeRequestAccountsDataSlice {
|
||||||
|
uint64 offset = 1;
|
||||||
|
uint64 length = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequestPing {
|
||||||
|
int32 id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdate {
|
||||||
|
repeated string filters = 1;
|
||||||
|
oneof update_oneof {
|
||||||
|
SubscribeUpdateAccount account = 2;
|
||||||
|
SubscribeUpdateSlot slot = 3;
|
||||||
|
SubscribeUpdateTransaction transaction = 4;
|
||||||
|
SubscribeUpdateTransactionStatus transaction_status = 10;
|
||||||
|
SubscribeUpdateBlock block = 5;
|
||||||
|
SubscribeUpdatePing ping = 6;
|
||||||
|
SubscribeUpdatePong pong = 9;
|
||||||
|
SubscribeUpdateBlockMeta block_meta = 7;
|
||||||
|
SubscribeUpdateEntry entry = 8;
|
||||||
|
}
|
||||||
|
google.protobuf.Timestamp created_at = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdateAccount {
|
||||||
|
SubscribeUpdateAccountInfo account = 1;
|
||||||
|
uint64 slot = 2;
|
||||||
|
bool is_startup = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdateAccountInfo {
|
||||||
|
bytes pubkey = 1;
|
||||||
|
uint64 lamports = 2;
|
||||||
|
bytes owner = 3;
|
||||||
|
bool executable = 4;
|
||||||
|
uint64 rent_epoch = 5;
|
||||||
|
bytes data = 6;
|
||||||
|
uint64 write_version = 7;
|
||||||
|
optional bytes txn_signature = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdateSlot {
|
||||||
|
uint64 slot = 1;
|
||||||
|
optional uint64 parent = 2;
|
||||||
|
SlotStatus status = 3;
|
||||||
|
optional string dead_error = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdateTransaction {
|
||||||
|
SubscribeUpdateTransactionInfo transaction = 1;
|
||||||
|
uint64 slot = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdateTransactionInfo {
|
||||||
|
bytes signature = 1;
|
||||||
|
bool is_vote = 2;
|
||||||
|
solana.storage.ConfirmedBlock.Transaction transaction = 3;
|
||||||
|
solana.storage.ConfirmedBlock.TransactionStatusMeta meta = 4;
|
||||||
|
uint64 index = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdateTransactionStatus {
|
||||||
|
uint64 slot = 1;
|
||||||
|
bytes signature = 2;
|
||||||
|
bool is_vote = 3;
|
||||||
|
uint64 index = 4;
|
||||||
|
solana.storage.ConfirmedBlock.TransactionError err = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdateBlock {
|
||||||
|
uint64 slot = 1;
|
||||||
|
string blockhash = 2;
|
||||||
|
solana.storage.ConfirmedBlock.Rewards rewards = 3;
|
||||||
|
solana.storage.ConfirmedBlock.UnixTimestamp block_time = 4;
|
||||||
|
solana.storage.ConfirmedBlock.BlockHeight block_height = 5;
|
||||||
|
uint64 parent_slot = 7;
|
||||||
|
string parent_blockhash = 8;
|
||||||
|
uint64 executed_transaction_count = 9;
|
||||||
|
repeated SubscribeUpdateTransactionInfo transactions = 6;
|
||||||
|
uint64 updated_account_count = 10;
|
||||||
|
repeated SubscribeUpdateAccountInfo accounts = 11;
|
||||||
|
uint64 entries_count = 12;
|
||||||
|
repeated SubscribeUpdateEntry entries = 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdateBlockMeta {
|
||||||
|
uint64 slot = 1;
|
||||||
|
string blockhash = 2;
|
||||||
|
solana.storage.ConfirmedBlock.Rewards rewards = 3;
|
||||||
|
solana.storage.ConfirmedBlock.UnixTimestamp block_time = 4;
|
||||||
|
solana.storage.ConfirmedBlock.BlockHeight block_height = 5;
|
||||||
|
uint64 parent_slot = 6;
|
||||||
|
string parent_blockhash = 7;
|
||||||
|
uint64 executed_transaction_count = 8;
|
||||||
|
uint64 entries_count = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdateEntry {
|
||||||
|
uint64 slot = 1;
|
||||||
|
uint64 index = 2;
|
||||||
|
uint64 num_hashes = 3;
|
||||||
|
bytes hash = 4;
|
||||||
|
uint64 executed_transaction_count = 5;
|
||||||
|
uint64 starting_transaction_index = 6; // added in v1.18, for solana 1.17 value is always 0
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdatePing {}
|
||||||
|
|
||||||
|
message SubscribeUpdatePong {
|
||||||
|
int32 id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// non-streaming methods
|
||||||
|
|
||||||
|
message PingRequest {
|
||||||
|
int32 count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PongResponse {
|
||||||
|
int32 count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetLatestBlockhashRequest {
|
||||||
|
optional CommitmentLevel commitment = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetLatestBlockhashResponse {
|
||||||
|
uint64 slot = 1;
|
||||||
|
string blockhash = 2;
|
||||||
|
uint64 last_valid_block_height = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetBlockHeightRequest {
|
||||||
|
optional CommitmentLevel commitment = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetBlockHeightResponse {
|
||||||
|
uint64 block_height = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetSlotRequest {
|
||||||
|
optional CommitmentLevel commitment = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetSlotResponse {
|
||||||
|
uint64 slot = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetVersionRequest {}
|
||||||
|
|
||||||
|
message GetVersionResponse {
|
||||||
|
string version = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message IsBlockhashValidRequest {
|
||||||
|
string blockhash = 1;
|
||||||
|
optional CommitmentLevel commitment = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message IsBlockhashValidResponse {
|
||||||
|
uint64 slot = 1;
|
||||||
|
bool valid = 2;
|
||||||
|
}
|
||||||
344
example/geyser/proto/geyser_grpc.pb.go
Normal file
344
example/geyser/proto/geyser_grpc.pb.go
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// - protoc-gen-go-grpc v1.5.1
|
||||||
|
// - protoc v5.29.3
|
||||||
|
// source: geyser.proto
|
||||||
|
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
// Requires gRPC-Go v1.64.0 or later.
|
||||||
|
const _ = grpc.SupportPackageIsVersion9
|
||||||
|
|
||||||
|
const (
|
||||||
|
Geyser_Subscribe_FullMethodName = "/geyser.Geyser/Subscribe"
|
||||||
|
Geyser_Ping_FullMethodName = "/geyser.Geyser/Ping"
|
||||||
|
Geyser_GetLatestBlockhash_FullMethodName = "/geyser.Geyser/GetLatestBlockhash"
|
||||||
|
Geyser_GetBlockHeight_FullMethodName = "/geyser.Geyser/GetBlockHeight"
|
||||||
|
Geyser_GetSlot_FullMethodName = "/geyser.Geyser/GetSlot"
|
||||||
|
Geyser_IsBlockhashValid_FullMethodName = "/geyser.Geyser/IsBlockhashValid"
|
||||||
|
Geyser_GetVersion_FullMethodName = "/geyser.Geyser/GetVersion"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GeyserClient is the client API for Geyser service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
type GeyserClient interface {
|
||||||
|
Subscribe(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[SubscribeRequest, SubscribeUpdate], error)
|
||||||
|
Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PongResponse, error)
|
||||||
|
GetLatestBlockhash(ctx context.Context, in *GetLatestBlockhashRequest, opts ...grpc.CallOption) (*GetLatestBlockhashResponse, error)
|
||||||
|
GetBlockHeight(ctx context.Context, in *GetBlockHeightRequest, opts ...grpc.CallOption) (*GetBlockHeightResponse, error)
|
||||||
|
GetSlot(ctx context.Context, in *GetSlotRequest, opts ...grpc.CallOption) (*GetSlotResponse, error)
|
||||||
|
IsBlockhashValid(ctx context.Context, in *IsBlockhashValidRequest, opts ...grpc.CallOption) (*IsBlockhashValidResponse, error)
|
||||||
|
GetVersion(ctx context.Context, in *GetVersionRequest, opts ...grpc.CallOption) (*GetVersionResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type geyserClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGeyserClient(cc grpc.ClientConnInterface) GeyserClient {
|
||||||
|
return &geyserClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *geyserClient) Subscribe(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[SubscribeRequest, SubscribeUpdate], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &Geyser_ServiceDesc.Streams[0], Geyser_Subscribe_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[SubscribeRequest, SubscribeUpdate]{ClientStream: stream}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type Geyser_SubscribeClient = grpc.BidiStreamingClient[SubscribeRequest, SubscribeUpdate]
|
||||||
|
|
||||||
|
func (c *geyserClient) Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PongResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(PongResponse)
|
||||||
|
err := c.cc.Invoke(ctx, Geyser_Ping_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *geyserClient) GetLatestBlockhash(ctx context.Context, in *GetLatestBlockhashRequest, opts ...grpc.CallOption) (*GetLatestBlockhashResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(GetLatestBlockhashResponse)
|
||||||
|
err := c.cc.Invoke(ctx, Geyser_GetLatestBlockhash_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *geyserClient) GetBlockHeight(ctx context.Context, in *GetBlockHeightRequest, opts ...grpc.CallOption) (*GetBlockHeightResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(GetBlockHeightResponse)
|
||||||
|
err := c.cc.Invoke(ctx, Geyser_GetBlockHeight_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *geyserClient) GetSlot(ctx context.Context, in *GetSlotRequest, opts ...grpc.CallOption) (*GetSlotResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(GetSlotResponse)
|
||||||
|
err := c.cc.Invoke(ctx, Geyser_GetSlot_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *geyserClient) IsBlockhashValid(ctx context.Context, in *IsBlockhashValidRequest, opts ...grpc.CallOption) (*IsBlockhashValidResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(IsBlockhashValidResponse)
|
||||||
|
err := c.cc.Invoke(ctx, Geyser_IsBlockhashValid_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *geyserClient) GetVersion(ctx context.Context, in *GetVersionRequest, opts ...grpc.CallOption) (*GetVersionResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(GetVersionResponse)
|
||||||
|
err := c.cc.Invoke(ctx, Geyser_GetVersion_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GeyserServer is the server API for Geyser service.
|
||||||
|
// All implementations must embed UnimplementedGeyserServer
|
||||||
|
// for forward compatibility.
|
||||||
|
type GeyserServer interface {
|
||||||
|
Subscribe(grpc.BidiStreamingServer[SubscribeRequest, SubscribeUpdate]) error
|
||||||
|
Ping(context.Context, *PingRequest) (*PongResponse, error)
|
||||||
|
GetLatestBlockhash(context.Context, *GetLatestBlockhashRequest) (*GetLatestBlockhashResponse, error)
|
||||||
|
GetBlockHeight(context.Context, *GetBlockHeightRequest) (*GetBlockHeightResponse, error)
|
||||||
|
GetSlot(context.Context, *GetSlotRequest) (*GetSlotResponse, error)
|
||||||
|
IsBlockhashValid(context.Context, *IsBlockhashValidRequest) (*IsBlockhashValidResponse, error)
|
||||||
|
GetVersion(context.Context, *GetVersionRequest) (*GetVersionResponse, error)
|
||||||
|
mustEmbedUnimplementedGeyserServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedGeyserServer must be embedded to have
|
||||||
|
// forward compatible implementations.
|
||||||
|
//
|
||||||
|
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||||
|
// pointer dereference when methods are called.
|
||||||
|
type UnimplementedGeyserServer struct{}
|
||||||
|
|
||||||
|
func (UnimplementedGeyserServer) Subscribe(grpc.BidiStreamingServer[SubscribeRequest, SubscribeUpdate]) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method Subscribe not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedGeyserServer) Ping(context.Context, *PingRequest) (*PongResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedGeyserServer) GetLatestBlockhash(context.Context, *GetLatestBlockhashRequest) (*GetLatestBlockhashResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetLatestBlockhash not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedGeyserServer) GetBlockHeight(context.Context, *GetBlockHeightRequest) (*GetBlockHeightResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetBlockHeight not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedGeyserServer) GetSlot(context.Context, *GetSlotRequest) (*GetSlotResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetSlot not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedGeyserServer) IsBlockhashValid(context.Context, *IsBlockhashValidRequest) (*IsBlockhashValidResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method IsBlockhashValid not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedGeyserServer) GetVersion(context.Context, *GetVersionRequest) (*GetVersionResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetVersion not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedGeyserServer) mustEmbedUnimplementedGeyserServer() {}
|
||||||
|
func (UnimplementedGeyserServer) testEmbeddedByValue() {}
|
||||||
|
|
||||||
|
// UnsafeGeyserServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to GeyserServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeGeyserServer interface {
|
||||||
|
mustEmbedUnimplementedGeyserServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterGeyserServer(s grpc.ServiceRegistrar, srv GeyserServer) {
|
||||||
|
// If the following call pancis, it indicates UnimplementedGeyserServer was
|
||||||
|
// embedded by pointer and is nil. This will cause panics if an
|
||||||
|
// unimplemented method is ever invoked, so we test this at initialization
|
||||||
|
// time to prevent it from happening at runtime later due to I/O.
|
||||||
|
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||||
|
t.testEmbeddedByValue()
|
||||||
|
}
|
||||||
|
s.RegisterService(&Geyser_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Geyser_Subscribe_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
return srv.(GeyserServer).Subscribe(&grpc.GenericServerStream[SubscribeRequest, SubscribeUpdate]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type Geyser_SubscribeServer = grpc.BidiStreamingServer[SubscribeRequest, SubscribeUpdate]
|
||||||
|
|
||||||
|
func _Geyser_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(PingRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(GeyserServer).Ping(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Geyser_Ping_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(GeyserServer).Ping(ctx, req.(*PingRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Geyser_GetLatestBlockhash_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(GetLatestBlockhashRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(GeyserServer).GetLatestBlockhash(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Geyser_GetLatestBlockhash_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(GeyserServer).GetLatestBlockhash(ctx, req.(*GetLatestBlockhashRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Geyser_GetBlockHeight_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(GetBlockHeightRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(GeyserServer).GetBlockHeight(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Geyser_GetBlockHeight_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(GeyserServer).GetBlockHeight(ctx, req.(*GetBlockHeightRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Geyser_GetSlot_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(GetSlotRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(GeyserServer).GetSlot(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Geyser_GetSlot_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(GeyserServer).GetSlot(ctx, req.(*GetSlotRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Geyser_IsBlockhashValid_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(IsBlockhashValidRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(GeyserServer).IsBlockhashValid(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Geyser_IsBlockhashValid_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(GeyserServer).IsBlockhashValid(ctx, req.(*IsBlockhashValidRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Geyser_GetVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(GetVersionRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(GeyserServer).GetVersion(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Geyser_GetVersion_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(GeyserServer).GetVersion(ctx, req.(*GetVersionRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Geyser_ServiceDesc is the grpc.ServiceDesc for Geyser service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var Geyser_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "geyser.Geyser",
|
||||||
|
HandlerType: (*GeyserServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "Ping",
|
||||||
|
Handler: _Geyser_Ping_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetLatestBlockhash",
|
||||||
|
Handler: _Geyser_GetLatestBlockhash_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetBlockHeight",
|
||||||
|
Handler: _Geyser_GetBlockHeight_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetSlot",
|
||||||
|
Handler: _Geyser_GetSlot_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "IsBlockhashValid",
|
||||||
|
Handler: _Geyser_IsBlockhashValid_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetVersion",
|
||||||
|
Handler: _Geyser_GetVersion_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{
|
||||||
|
{
|
||||||
|
StreamName: "Subscribe",
|
||||||
|
Handler: _Geyser_Subscribe_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
ClientStreams: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Metadata: "geyser.proto",
|
||||||
|
}
|
||||||
1547
example/geyser/proto/solana-storage.pb.go
Normal file
1547
example/geyser/proto/solana-storage.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
149
example/geyser/proto/solana-storage.proto
Normal file
149
example/geyser/proto/solana-storage.proto
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package solana.storage.ConfirmedBlock;
|
||||||
|
|
||||||
|
option go_package = "github.com/rpcpool/yellowstone-grpc/examples/golang/proto";
|
||||||
|
|
||||||
|
message ConfirmedBlock {
|
||||||
|
string previous_blockhash = 1;
|
||||||
|
string blockhash = 2;
|
||||||
|
uint64 parent_slot = 3;
|
||||||
|
repeated ConfirmedTransaction transactions = 4;
|
||||||
|
repeated Reward rewards = 5;
|
||||||
|
UnixTimestamp block_time = 6;
|
||||||
|
BlockHeight block_height = 7;
|
||||||
|
NumPartitions num_partitions = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ConfirmedTransaction {
|
||||||
|
Transaction transaction = 1;
|
||||||
|
TransactionStatusMeta meta = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Transaction {
|
||||||
|
repeated bytes signatures = 1;
|
||||||
|
Message message = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Message {
|
||||||
|
MessageHeader header = 1;
|
||||||
|
repeated bytes account_keys = 2;
|
||||||
|
bytes recent_blockhash = 3;
|
||||||
|
repeated CompiledInstruction instructions = 4;
|
||||||
|
bool versioned = 5;
|
||||||
|
repeated MessageAddressTableLookup address_table_lookups = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MessageHeader {
|
||||||
|
uint32 num_required_signatures = 1;
|
||||||
|
uint32 num_readonly_signed_accounts = 2;
|
||||||
|
uint32 num_readonly_unsigned_accounts = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MessageAddressTableLookup {
|
||||||
|
bytes account_key = 1;
|
||||||
|
bytes writable_indexes = 2;
|
||||||
|
bytes readonly_indexes = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TransactionStatusMeta {
|
||||||
|
TransactionError err = 1;
|
||||||
|
uint64 fee = 2;
|
||||||
|
repeated uint64 pre_balances = 3;
|
||||||
|
repeated uint64 post_balances = 4;
|
||||||
|
repeated InnerInstructions inner_instructions = 5;
|
||||||
|
bool inner_instructions_none = 10;
|
||||||
|
repeated string log_messages = 6;
|
||||||
|
bool log_messages_none = 11;
|
||||||
|
repeated TokenBalance pre_token_balances = 7;
|
||||||
|
repeated TokenBalance post_token_balances = 8;
|
||||||
|
repeated Reward rewards = 9;
|
||||||
|
repeated bytes loaded_writable_addresses = 12;
|
||||||
|
repeated bytes loaded_readonly_addresses = 13;
|
||||||
|
ReturnData return_data = 14;
|
||||||
|
bool return_data_none = 15;
|
||||||
|
|
||||||
|
// Sum of compute units consumed by all instructions.
|
||||||
|
// Available since Solana v1.10.35 / v1.11.6.
|
||||||
|
// Set to `None` for txs executed on earlier versions.
|
||||||
|
optional uint64 compute_units_consumed = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TransactionError {
|
||||||
|
bytes err = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message InnerInstructions {
|
||||||
|
uint32 index = 1;
|
||||||
|
repeated InnerInstruction instructions = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message InnerInstruction {
|
||||||
|
uint32 program_id_index = 1;
|
||||||
|
bytes accounts = 2;
|
||||||
|
bytes data = 3;
|
||||||
|
|
||||||
|
// Invocation stack height of an inner instruction.
|
||||||
|
// Available since Solana v1.14.6
|
||||||
|
// Set to `None` for txs executed on earlier versions.
|
||||||
|
optional uint32 stack_height = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CompiledInstruction {
|
||||||
|
uint32 program_id_index = 1;
|
||||||
|
bytes accounts = 2;
|
||||||
|
bytes data = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TokenBalance {
|
||||||
|
uint32 account_index = 1;
|
||||||
|
string mint = 2;
|
||||||
|
UiTokenAmount ui_token_amount = 3;
|
||||||
|
string owner = 4;
|
||||||
|
string program_id = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UiTokenAmount {
|
||||||
|
double ui_amount = 1;
|
||||||
|
uint32 decimals = 2;
|
||||||
|
string amount = 3;
|
||||||
|
string ui_amount_string = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ReturnData {
|
||||||
|
bytes program_id = 1;
|
||||||
|
bytes data = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum RewardType {
|
||||||
|
Unspecified = 0;
|
||||||
|
Fee = 1;
|
||||||
|
Rent = 2;
|
||||||
|
Staking = 3;
|
||||||
|
Voting = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Reward {
|
||||||
|
string pubkey = 1;
|
||||||
|
int64 lamports = 2;
|
||||||
|
uint64 post_balance = 3;
|
||||||
|
RewardType reward_type = 4;
|
||||||
|
string commission = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Rewards {
|
||||||
|
repeated Reward rewards = 1;
|
||||||
|
NumPartitions num_partitions = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UnixTimestamp {
|
||||||
|
int64 timestamp = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BlockHeight {
|
||||||
|
uint64 block_height = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message NumPartitions {
|
||||||
|
uint64 num_partitions = 1;
|
||||||
|
}
|
||||||
60
example/geyser/pump.go
Normal file
60
example/geyser/pump.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package geyser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
types "github.com/thloyi/pump-parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PumpHandler struct {
|
||||||
|
callback func(*types.Tx, *types.RawTx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPumpHandler(cb func(*types.Tx, *types.RawTx)) *PumpHandler {
|
||||||
|
return &PumpHandler{
|
||||||
|
callback: func(tx *types.Tx, tx2 *types.RawTx) {
|
||||||
|
//tx.Check(tx2)
|
||||||
|
cb(tx, tx2)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *PumpHandler) HandleMessage(rawTx *types.RawTx) {
|
||||||
|
if rawTx.Meta.Err != nil {
|
||||||
|
// Notify the channel about the failed transaction
|
||||||
|
beforeSolBalance := decimal.Zero
|
||||||
|
afterSolBalance := decimal.Zero
|
||||||
|
if rawTx.Meta.PreBalances != nil && len(rawTx.Meta.PreBalances) > 0 {
|
||||||
|
beforeSolBalance = decimal.NewFromUint64(rawTx.Meta.PreBalances[0]).Div(decimal.NewFromInt(1e9))
|
||||||
|
}
|
||||||
|
if rawTx.Meta.PostBalances != nil && len(rawTx.Meta.PostBalances) > 0 {
|
||||||
|
afterSolBalance = decimal.NewFromUint64(rawTx.Meta.PostBalances[0]).Div(decimal.NewFromInt(1e9))
|
||||||
|
}
|
||||||
|
h.callback(&types.Tx{
|
||||||
|
TxHash: (*[64]byte)((rawTx.Transaction.Signatures[0][:])),
|
||||||
|
Err: rawTx.Meta.Err,
|
||||||
|
Signer: rawTx.GetSigner(),
|
||||||
|
Block: rawTx.Slot,
|
||||||
|
BlockIndex: uint64(rawTx.IndexWithinBlock),
|
||||||
|
|
||||||
|
BeforeSolBalance: beforeSolBalance,
|
||||||
|
AfterSOLBalance: afterSolBalance,
|
||||||
|
}, rawTx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedTx, err := types.Parser(rawTx)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("parser error: %s, block: %d tx: %s\n", err, rawTx.Slot, rawTx.TxHash())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(parsedTx.Swaps) == 0 {
|
||||||
|
// no swap, ignore
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// fmt.Println(parsedTx.GetTxHash(), len(parsedTx.Swaps))
|
||||||
|
if h.callback != nil {
|
||||||
|
h.callback(parsedTx, rawTx)
|
||||||
|
}
|
||||||
|
}
|
||||||
527
example/geyser/yellowstone.go
Normal file
527
example/geyser/yellowstone.go
Normal file
@@ -0,0 +1,527 @@
|
|||||||
|
package geyser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
solana2 "github.com/gagliardetto/solana-go"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
"google.golang.org/grpc/keepalive"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
|
||||||
|
types "github.com/thloyi/pump-parser"
|
||||||
|
pb "github.com/thloyi/pump-parser/example/geyser/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handler interface {
|
||||||
|
HandleMessage(rawTx *types.RawTx)
|
||||||
|
}
|
||||||
|
|
||||||
|
var kacp = keepalive.ClientParameters{
|
||||||
|
Time: 10 * time.Second, // send pings every 10 seconds if there is no activity
|
||||||
|
Timeout: time.Second, // wait 1 second for ping ack before considering the connection dead
|
||||||
|
PermitWithoutStream: true, // send pings even without active streams
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
ch chan SubscriptionMessage
|
||||||
|
endpoint string
|
||||||
|
conn *grpc.ClientConn
|
||||||
|
ctx context.Context
|
||||||
|
lastReceiveTime time.Time
|
||||||
|
backoffFactor float64
|
||||||
|
|
||||||
|
subscription *pb.SubscribeRequest
|
||||||
|
subStatus bool
|
||||||
|
|
||||||
|
leastBlock BlockInfo
|
||||||
|
|
||||||
|
firstMessage bool
|
||||||
|
|
||||||
|
handler Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientWithPumpSwap(endpoint string, ch chan SubscriptionMessage) *Client {
|
||||||
|
var subscription pb.SubscribeRequest
|
||||||
|
|
||||||
|
var failed = false
|
||||||
|
var vote = false
|
||||||
|
subscription.Transactions = make(map[string]*pb.SubscribeRequestFilterTransactions)
|
||||||
|
subscription.Transactions["transactions_sub"] = &pb.SubscribeRequestFilterTransactions{
|
||||||
|
Failed: &failed,
|
||||||
|
Vote: &vote,
|
||||||
|
}
|
||||||
|
|
||||||
|
subscription.Transactions["transactions_sub"].AccountInclude = []string{
|
||||||
|
"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA", //Pump AMM
|
||||||
|
"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", //Pump
|
||||||
|
}
|
||||||
|
subscription.BlocksMeta = make(map[string]*pb.SubscribeRequestFilterBlocksMeta)
|
||||||
|
subscription.BlocksMeta["block_meta"] = &pb.SubscribeRequestFilterBlocksMeta{}
|
||||||
|
|
||||||
|
c := &Client{
|
||||||
|
backoffFactor: 1.5,
|
||||||
|
ch: ch,
|
||||||
|
endpoint: endpoint,
|
||||||
|
lastReceiveTime: time.Now(),
|
||||||
|
subStatus: false,
|
||||||
|
subscription: &subscription,
|
||||||
|
}
|
||||||
|
c.handler = NewPumpHandler(func(tx *types.Tx, tx2 *types.RawTx) {
|
||||||
|
c.sendTx(tx, tx2)
|
||||||
|
})
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientWithLaunchLab(endpoint string, ch chan SubscriptionMessage) *Client {
|
||||||
|
var subscription pb.SubscribeRequest
|
||||||
|
|
||||||
|
var failed = false
|
||||||
|
var vote = false
|
||||||
|
subscription.Transactions = make(map[string]*pb.SubscribeRequestFilterTransactions)
|
||||||
|
subscription.Transactions["transactions_sub"] = &pb.SubscribeRequestFilterTransactions{
|
||||||
|
Failed: &failed,
|
||||||
|
Vote: &vote,
|
||||||
|
}
|
||||||
|
|
||||||
|
subscription.Transactions["transactions_sub"].AccountInclude = []string{
|
||||||
|
"LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj", //LaunchLab
|
||||||
|
"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C", //CPMM
|
||||||
|
//"675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8", //V4
|
||||||
|
}
|
||||||
|
subscription.BlocksMeta = make(map[string]*pb.SubscribeRequestFilterBlocksMeta)
|
||||||
|
subscription.BlocksMeta["block_meta"] = &pb.SubscribeRequestFilterBlocksMeta{}
|
||||||
|
|
||||||
|
c := &Client{
|
||||||
|
backoffFactor: 1.5,
|
||||||
|
ch: ch,
|
||||||
|
endpoint: endpoint,
|
||||||
|
lastReceiveTime: time.Now(),
|
||||||
|
subStatus: false,
|
||||||
|
subscription: &subscription,
|
||||||
|
}
|
||||||
|
c.handler = NewPumpHandler(func(tx *types.Tx, tx2 *types.RawTx) {
|
||||||
|
c.sendTx(tx, tx2)
|
||||||
|
})
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunLoopWithReConnect(ctx context.Context, endpoint, program string, ch chan SubscriptionMessage) {
|
||||||
|
var client *Client
|
||||||
|
if program == types.SolProgramRaydiumLaunchLab {
|
||||||
|
client = NewClientWithLaunchLab(endpoint, ch)
|
||||||
|
} else {
|
||||||
|
client = NewClientWithPumpSwap(endpoint, ch)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
log.Println("Context done, exiting loop")
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
err := client.Connect(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to connect: %v", err)
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// should not reach here, because Connect will block
|
||||||
|
panic("geyser already connected, waiting for messages...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) SetSubscribe(subscription *pb.SubscribeRequest) {
|
||||||
|
c.subscription = subscription
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Connect(ctx context.Context) error {
|
||||||
|
|
||||||
|
c.ctx = ctx
|
||||||
|
if c.conn == nil {
|
||||||
|
// 连接到 geyser
|
||||||
|
conn, err := c.grpcConnect(c.endpoint, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.conn = conn
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.subStatus {
|
||||||
|
return nil // 已经订阅了
|
||||||
|
}
|
||||||
|
// 订阅交易
|
||||||
|
err := c.grpcSubscribe(ctx, c.conn)
|
||||||
|
if err != nil {
|
||||||
|
if c.conn != nil {
|
||||||
|
c.conn.Close()
|
||||||
|
}
|
||||||
|
c.conn = nil
|
||||||
|
c.subStatus = false
|
||||||
|
log.Printf("Failed to subscribe: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) grpcConnect(address string, plaintext bool) (*grpc.ClientConn, error) {
|
||||||
|
var opts []grpc.DialOption
|
||||||
|
if plaintext {
|
||||||
|
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||||
|
} else {
|
||||||
|
pool, _ := x509.SystemCertPool()
|
||||||
|
creds := credentials.NewClientTLSFromCert(pool, "")
|
||||||
|
opts = append(opts, grpc.WithTransportCredentials(creds))
|
||||||
|
}
|
||||||
|
|
||||||
|
opts = append(opts, grpc.WithKeepaliveParams(kacp))
|
||||||
|
|
||||||
|
log.Println("Starting grpc client, connecting to", address)
|
||||||
|
conn, err := grpc.NewClient(address, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("fail to dial: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) grpcSubscribe(ctx context.Context, conn *grpc.ClientConn) error {
|
||||||
|
var err error
|
||||||
|
client := pb.NewGeyserClient(conn)
|
||||||
|
|
||||||
|
//subscription.Transactions["transactions_sub"].AccountExclude = transactionsAccountsExclude
|
||||||
|
|
||||||
|
subscriptionJson, err := json.Marshal(c.subscription)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to marshal subscription request: %v", subscriptionJson)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("Subscription request: %s", string(subscriptionJson))
|
||||||
|
|
||||||
|
// Set up the subscription request
|
||||||
|
//if *token != "" {
|
||||||
|
// md := metadata.New(map[string]string{"x-token": *token})
|
||||||
|
// ctx = metadata.NewOutgoingContext(ctx, md)
|
||||||
|
//}
|
||||||
|
md := metadata.New(map[string]string{"x-token": "5adcf1f9-5719-43d1-bf3f-c2d4e1e5f94d"})
|
||||||
|
ctx = metadata.NewOutgoingContext(ctx, md)
|
||||||
|
|
||||||
|
stream, err := client.Subscribe(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = stream.Send(c.subscription)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.subStatus = true
|
||||||
|
c.firstMessage = true
|
||||||
|
for {
|
||||||
|
resp, err := stream.Recv()
|
||||||
|
|
||||||
|
if err == io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error occurred in receiving update: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
txn := resp.GetTransaction()
|
||||||
|
if txn == nil {
|
||||||
|
blockMeta := resp.GetBlockMeta()
|
||||||
|
if blockMeta != nil && c.ch != nil {
|
||||||
|
c.sendBlock(blockMeta)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rawTx, err := ConvertYellowstoneGrpcTransactionToSolanaTransaction(txn, resp.GetCreatedAt().Seconds)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to convert transaction: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c.handler.HandleMessage(rawTx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) computeDelay(slot uint64) int64 {
|
||||||
|
if c.leastBlock.Slot == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if slot < c.leastBlock.Slot {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
delay := time.Now().Unix() - c.leastBlock.BlockTime - (4 * int64(slot-c.leastBlock.Slot) / 10)
|
||||||
|
return delay
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) sendTx(t *types.Tx, tx *types.RawTx) {
|
||||||
|
c.ch <- SubscriptionMessage{
|
||||||
|
Reconnect: c.firstMessage,
|
||||||
|
EstimateDelaySecond: c.computeDelay(tx.Slot),
|
||||||
|
Block: nil,
|
||||||
|
Tx: t,
|
||||||
|
RawTx: tx,
|
||||||
|
}
|
||||||
|
c.firstMessage = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) sendBlock(blockMeta *pb.SubscribeUpdateBlockMeta) {
|
||||||
|
c.leastBlock.Slot = blockMeta.GetSlot()
|
||||||
|
c.leastBlock.BlockTime = blockMeta.GetBlockTime().Timestamp
|
||||||
|
c.leastBlock.BlockHash = blockMeta.Blockhash
|
||||||
|
c.leastBlock.Height = blockMeta.BlockHeight.BlockHeight
|
||||||
|
c.ch <- SubscriptionMessage{
|
||||||
|
EstimateDelaySecond: time.Now().Unix() - blockMeta.GetBlockTime().Timestamp,
|
||||||
|
Reconnect: c.firstMessage,
|
||||||
|
Block: &BlockInfo{
|
||||||
|
Slot: c.leastBlock.Slot,
|
||||||
|
BlockTime: c.leastBlock.BlockTime,
|
||||||
|
BlockHash: c.leastBlock.BlockHash,
|
||||||
|
Height: c.leastBlock.Height,
|
||||||
|
},
|
||||||
|
Tx: nil,
|
||||||
|
RawTx: nil,
|
||||||
|
}
|
||||||
|
c.firstMessage = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertYellowstoneGrpcTransactionToSolanaTransaction(y *pb.SubscribeUpdateTransaction, created int64) (*types.RawTx, error) {
|
||||||
|
sTx := &types.RawTx{
|
||||||
|
BlockTime: created,
|
||||||
|
Slot: y.Slot,
|
||||||
|
IndexWithinBlock: int64(y.Transaction.Index),
|
||||||
|
Meta: types.Meta{
|
||||||
|
Err: nil,
|
||||||
|
Fee: 0,
|
||||||
|
InnerInstructions: nil,
|
||||||
|
LoadedAddresses: types.LoadedAddresses{},
|
||||||
|
LogMessages: nil,
|
||||||
|
PostBalances: nil,
|
||||||
|
PostTokenBalances: nil,
|
||||||
|
PreBalances: nil,
|
||||||
|
PreTokenBalances: nil,
|
||||||
|
Rewards: nil,
|
||||||
|
},
|
||||||
|
//Transaction: types.Transaction{
|
||||||
|
// Message: types.Message{
|
||||||
|
// AccountKeys: nil,
|
||||||
|
// AddressTableLookups: nil,
|
||||||
|
// Header: types.Header{},
|
||||||
|
// Instructions: nil,
|
||||||
|
// RecentBlockHash: "",
|
||||||
|
// },
|
||||||
|
// Signatures: nil,
|
||||||
|
//},
|
||||||
|
//Version: nil,
|
||||||
|
}
|
||||||
|
meta := y.Transaction.GetMeta()
|
||||||
|
yTx := y.Transaction.Transaction
|
||||||
|
|
||||||
|
if meta.Err != nil && len(meta.Err.GetErr()) > 0 {
|
||||||
|
// If the transaction has an error, we set the error in the Meta
|
||||||
|
transError, err := DecodeTransactionError(meta.Err.GetErr())
|
||||||
|
if err != nil {
|
||||||
|
sTx.Meta.Err = err
|
||||||
|
} else {
|
||||||
|
sTx.Meta.Err = transError
|
||||||
|
}
|
||||||
|
// sTx.Meta.Err = meta.Err.GetErr()
|
||||||
|
}
|
||||||
|
sTx.Meta.Fee = meta.Fee
|
||||||
|
//sTx.Meta.InnerInstructions = meta.InnerInstructions
|
||||||
|
|
||||||
|
for _, innerInstr := range meta.InnerInstructions {
|
||||||
|
var instrs []types.Instruction
|
||||||
|
for _, instr := range innerInstr.Instructions {
|
||||||
|
instrs = append(instrs, types.Instruction{
|
||||||
|
ProgramIDIndex: int(instr.ProgramIdIndex),
|
||||||
|
Accounts: func() []int {
|
||||||
|
var out []int
|
||||||
|
for i := range instr.Accounts {
|
||||||
|
out = append(out, int(instr.Accounts[i]))
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}(),
|
||||||
|
Data: instr.Data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sTx.Meta.InnerInstructions = append(sTx.Meta.InnerInstructions, types.InnerInstructions{
|
||||||
|
Index: int(innerInstr.Index),
|
||||||
|
Instructions: instrs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sTx.Meta.LogMessages = meta.LogMessages
|
||||||
|
sTx.Meta.PostBalances = meta.PostBalances
|
||||||
|
sTx.Meta.PostTokenBalances = grpcTokenBalance(meta.PostTokenBalances)
|
||||||
|
sTx.Meta.PreBalances = meta.PreBalances
|
||||||
|
sTx.Meta.PreTokenBalances = grpcTokenBalance(meta.PreTokenBalances)
|
||||||
|
sTx.Meta.Rewards = nil
|
||||||
|
sTx.Meta.LoadedAddresses.Readonly = byteSlicesToKeySlices(meta.LoadedReadonlyAddresses)
|
||||||
|
sTx.Meta.LoadedAddresses.Writable = byteSlicesToKeySlices(meta.LoadedWritableAddresses)
|
||||||
|
|
||||||
|
// copy signatures
|
||||||
|
for i := range yTx.Signatures {
|
||||||
|
sTx.Transaction.Signatures = append(sTx.Transaction.Signatures, solana2.SignatureFromBytes(yTx.Signatures[i]))
|
||||||
|
}
|
||||||
|
// copy message
|
||||||
|
sTx.Transaction.Message = types.Message{
|
||||||
|
RecentBlockHash: solana2.HashFromBytes(yTx.Message.RecentBlockhash).String(),
|
||||||
|
}
|
||||||
|
// copy message.AccountKeys
|
||||||
|
//stopAt := len(yTx.Message.AccountKeys) - sTx.Message.NumLookups()
|
||||||
|
stopAt := len(yTx.Message.AccountKeys)
|
||||||
|
for accIndex, acc := range yTx.Message.AccountKeys {
|
||||||
|
sTx.Transaction.Message.AccountKeys = append(sTx.Transaction.Message.AccountKeys, solana2.PublicKeyFromBytes(acc))
|
||||||
|
if accIndex == stopAt-1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// copy message.Header
|
||||||
|
sTx.Transaction.Message.Header = types.Header{
|
||||||
|
NumRequiredSignatures: int(yTx.Message.Header.NumRequiredSignatures),
|
||||||
|
NumReadonlySignedAccounts: int(yTx.Message.Header.NumReadonlySignedAccounts),
|
||||||
|
NumReadonlyUnsignedAccounts: int(yTx.Message.Header.NumReadonlyUnsignedAccounts),
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy message.versioned
|
||||||
|
if yTx.Message.Versioned {
|
||||||
|
sTx.Version = solana2.MessageVersionV0
|
||||||
|
} else {
|
||||||
|
sTx.Version = solana2.MessageVersionLegacy
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy address table lookups
|
||||||
|
{
|
||||||
|
tables := map[solana2.PublicKey]solana2.PublicKeySlice{}
|
||||||
|
writable := byteSlicesToKeySlices(meta.LoadedWritableAddresses)
|
||||||
|
readonly := byteSlicesToKeySlices(meta.LoadedReadonlyAddresses)
|
||||||
|
for _, addr := range yTx.Message.AddressTableLookups {
|
||||||
|
sTx.Transaction.Message.AddressTableLookups = append(sTx.Transaction.Message.AddressTableLookups, solana2.MessageAddressTableLookup{
|
||||||
|
AccountKey: solana2.PublicKeyFromBytes(addr.AccountKey),
|
||||||
|
WritableIndexes: addr.WritableIndexes,
|
||||||
|
ReadonlyIndexes: addr.ReadonlyIndexes,
|
||||||
|
})
|
||||||
|
numTakeWritable := len(addr.WritableIndexes)
|
||||||
|
numTakeReadonly := len(addr.ReadonlyIndexes)
|
||||||
|
tableKey := solana2.PublicKeyFromBytes(addr.AccountKey)
|
||||||
|
{
|
||||||
|
// now need to rebuild the address table taking into account the indexes, and put the keys into the tables
|
||||||
|
maxIndex := 0
|
||||||
|
for _, indexB := range addr.WritableIndexes {
|
||||||
|
index := int(indexB)
|
||||||
|
if index > maxIndex {
|
||||||
|
maxIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, indexB := range addr.ReadonlyIndexes {
|
||||||
|
index := int(indexB)
|
||||||
|
if index > maxIndex {
|
||||||
|
maxIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tables[tableKey] = make([]solana2.PublicKey, maxIndex+1)
|
||||||
|
}
|
||||||
|
if numTakeWritable > 0 {
|
||||||
|
writableForTable := writable[:numTakeWritable]
|
||||||
|
for i, indexB := range addr.WritableIndexes {
|
||||||
|
index := int(indexB)
|
||||||
|
tables[tableKey][index] = writableForTable[i]
|
||||||
|
}
|
||||||
|
writable = writable[numTakeWritable:]
|
||||||
|
}
|
||||||
|
if numTakeReadonly > 0 {
|
||||||
|
readableForTable := readonly[:numTakeReadonly]
|
||||||
|
for i, indexB := range addr.ReadonlyIndexes {
|
||||||
|
index := int(indexB)
|
||||||
|
tables[tableKey][index] = readableForTable[i]
|
||||||
|
}
|
||||||
|
readonly = readonly[numTakeReadonly:]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy instructions
|
||||||
|
for _, instr := range yTx.Message.Instructions {
|
||||||
|
sTx.Transaction.Message.Instructions = append(sTx.Transaction.Message.Instructions, types.Instruction{
|
||||||
|
ProgramIDIndex: int(instr.ProgramIdIndex),
|
||||||
|
Accounts: func() []int {
|
||||||
|
var out []int
|
||||||
|
for i := range instr.Accounts {
|
||||||
|
out = append(out, int(instr.Accounts[i]))
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}(),
|
||||||
|
Data: instr.Data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve the lookups
|
||||||
|
//{
|
||||||
|
// if sTx.Transaction.Message.IsVersioned() {
|
||||||
|
// // only versioned transactions have address table lookups
|
||||||
|
// err := sTx.Transaction.Message.ResolveLookups()
|
||||||
|
// if err != nil {
|
||||||
|
// return sTx, fmt.Errorf("failed to resolve lookups: %w", err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
return sTx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func byteSlicesToKeySlices(keys [][]byte) []solana2.PublicKey {
|
||||||
|
var out []solana2.PublicKey
|
||||||
|
for _, key := range keys {
|
||||||
|
var k solana2.PublicKey
|
||||||
|
copy(k[:], key)
|
||||||
|
out = append(out, k)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func grpcTokenBalance(src []*pb.TokenBalance) []types.TokenBalance {
|
||||||
|
out := make([]types.TokenBalance, len(src))
|
||||||
|
for i, tb := range src {
|
||||||
|
var (
|
||||||
|
mintAccount solana2.PublicKey
|
||||||
|
ownerAccount solana2.PublicKey
|
||||||
|
programIDAccount solana2.PublicKey
|
||||||
|
)
|
||||||
|
|
||||||
|
if tb.Mint != "" {
|
||||||
|
mintAccount, _ = solana2.PublicKeyFromBase58(tb.Mint)
|
||||||
|
}
|
||||||
|
if tb.Owner != "" {
|
||||||
|
ownerAccount, _ = solana2.PublicKeyFromBase58(tb.Owner)
|
||||||
|
}
|
||||||
|
if tb.ProgramId != "" {
|
||||||
|
programIDAccount, _ = solana2.PublicKeyFromBase58(tb.ProgramId)
|
||||||
|
}
|
||||||
|
|
||||||
|
out[i] = types.TokenBalance{
|
||||||
|
AccountIndex: int(tb.AccountIndex),
|
||||||
|
MintAccount: mintAccount,
|
||||||
|
OwnerAccount: &ownerAccount,
|
||||||
|
ProgramIDAccount: programIDAccount,
|
||||||
|
Mint: tb.Mint,
|
||||||
|
Owner: tb.Owner,
|
||||||
|
ProgramID: tb.ProgramId,
|
||||||
|
UITokenAmount: types.UITokenAmount{
|
||||||
|
Amount: tb.UiTokenAmount.Amount,
|
||||||
|
Decimals: uint64(tb.UiTokenAmount.Decimals),
|
||||||
|
UIAmount: tb.UiTokenAmount.UiAmount,
|
||||||
|
UIAmountString: tb.UiTokenAmount.UiAmountString,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
229
example/tx.go
Normal file
229
example/tx.go
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
package example
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gagliardetto/solana-go"
|
||||||
|
"github.com/jackc/pgtype"
|
||||||
|
"github.com/mr-tron/base58"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
parser "github.com/thloyi/pump-parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Tx struct {
|
||||||
|
Err interface{} `json:"err,omitempty" gorm:"-"`
|
||||||
|
BondingCurve string `json:"bonding_curve" gorm:"-"`
|
||||||
|
PairAddress string `json:"pair_address"`
|
||||||
|
Maker string `json:"maker"`
|
||||||
|
Token0Address string `json:"token0_address"`
|
||||||
|
Token0Program string `json:"token0_program" gorm:"-"`
|
||||||
|
Token0Decimals uint8 `json:"token0_decimals" gorm:"-"`
|
||||||
|
Token1Address string `json:"token1_address"`
|
||||||
|
Token0Amount decimal.Decimal `json:"token0Amount" gorm:"column:token0_amount;type:numeric"`
|
||||||
|
Token1Amount decimal.Decimal `json:"token1Amount" gorm:"column:token1_amount;type:numeric"`
|
||||||
|
PriceUsd decimal.Decimal `json:"price_usd" gorm:"column:price_usd;type:numeric"`
|
||||||
|
AmountUsd decimal.Decimal `json:"amount_usd" gorm:"column:amount_usd;type:numeric"`
|
||||||
|
Block uint64 `json:"block"`
|
||||||
|
BlockIndex uint64 `json:"index"`
|
||||||
|
Event string `json:"event"`
|
||||||
|
CachedTxHash string `json:"tx_hash" gorm:"column:tx_hash"`
|
||||||
|
txHash *[64]byte `gorm:"-"`
|
||||||
|
TxIndex uint64 `json:"topic_index"`
|
||||||
|
Program string `json:"program"`
|
||||||
|
BlockAt pgtype.Timestamptz `gorm:"column:block_at;default:NULL" json:"block_at"`
|
||||||
|
CreatedAt *pgtype.Timestamptz `gorm:"autoCreateTime" json:"-"`
|
||||||
|
TotalSupply string `gorm:"total_supply"`
|
||||||
|
AfterReserve0 string `gorm:"after_reserve0"`
|
||||||
|
AfterReserve1 string `gorm:"after_reserve1"`
|
||||||
|
PositionChange int64 `gorm:"position_change"`
|
||||||
|
QuoteIsToken0 bool `gorm:"-"`
|
||||||
|
CurrentPrice decimal.Decimal `gorm:"-"`
|
||||||
|
TokenCreator string `gorm:"-"`
|
||||||
|
Platform string `gorm:"column:tx_platform;type:platform;default:'none'" json:"platform"`
|
||||||
|
MevAgent string `gorm:"column:tx_mev_agent;type:mev_agent;default:'none'" json:"tx_mev_agent"`
|
||||||
|
MevAgentFee decimal.Decimal `gorm:"column:tx_mev_agent_fee;type:numeric" json:"tx_mev_agent_fee"`
|
||||||
|
CUPrice decimal.Decimal `gorm:"column:tx_cu_price;type:numeric" json:"tx_cu_price"`
|
||||||
|
PlatformFee decimal.Decimal `gorm:"-"`
|
||||||
|
|
||||||
|
AfterSignerToken0Balance decimal.Decimal `gorm:"-" json:"-"`
|
||||||
|
BeforeSolBalance decimal.Decimal `gorm:"-" json:"-"`
|
||||||
|
AfterSOLBalance decimal.Decimal `gorm:"column:after_sol_balance;type:numeric" json:"after_sol_balance"`
|
||||||
|
EntryContract string `gorm:"column:tx_entry_contract;type:entry_contract;default:'none'" json:"tx_entry_contract"`
|
||||||
|
|
||||||
|
Mayhem bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *Tx) GetTxHash() string {
|
||||||
|
if tx.CachedTxHash != "" {
|
||||||
|
return tx.CachedTxHash
|
||||||
|
}
|
||||||
|
if tx.txHash == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
tx.CachedTxHash = base58.Encode(tx.txHash[:])
|
||||||
|
return tx.CachedTxHash
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromTx(tx *parser.Tx, raw *parser.RawTx) []*Tx {
|
||||||
|
var txs []*Tx = 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)
|
||||||
|
token0Program := s.BaseTokenProgram
|
||||||
|
token0Address := s.BaseMint
|
||||||
|
if s.Program == "Pump" {
|
||||||
|
newTx = &Tx{
|
||||||
|
Err: nil,
|
||||||
|
BondingCurve: s.Pool.String(),
|
||||||
|
//PairAddress: s.Pool.String(),
|
||||||
|
Maker: s.User.String(),
|
||||||
|
Token0Address: s.BaseMint.String(),
|
||||||
|
Token0Program: s.BaseTokenProgram.String(),
|
||||||
|
Token0Decimals: s.BaseMintDecimals,
|
||||||
|
Token1Address: "",
|
||||||
|
Token0Amount: s.BaseAmount.Div(decimal.NewFromInt(1e6)),
|
||||||
|
Token1Amount: s.QuoteAmount.Div(decimal.NewFromInt(1e9)),
|
||||||
|
Block: tx.Block,
|
||||||
|
BlockIndex: tx.BlockIndex,
|
||||||
|
Event: s.Event,
|
||||||
|
CachedTxHash: "",
|
||||||
|
txHash: tx.TxHash,
|
||||||
|
TxIndex: uint64(i),
|
||||||
|
Program: s.Program,
|
||||||
|
BlockAt: pgtype.Timestamptz{
|
||||||
|
Time: time.Unix(tx.BlockAt, 0),
|
||||||
|
},
|
||||||
|
//CreatedAt: nil,
|
||||||
|
TotalSupply: "1000000000",
|
||||||
|
AfterReserve0: s.BaseReserve.Div(decimal.New(1, int32(s.BaseMintDecimals))).String(),
|
||||||
|
AfterReserve1: s.QuoteReserve.Div(decimal.New(1, int32(s.QuoteMintDecimals))).String(),
|
||||||
|
QuoteIsToken0: false,
|
||||||
|
// CurrentPrice: decimal.Decimal{},
|
||||||
|
TokenCreator: s.Creator.String(),
|
||||||
|
Platform: platform,
|
||||||
|
PlatformFee: platformFee,
|
||||||
|
|
||||||
|
MevAgent: mev,
|
||||||
|
MevAgentFee: mevFee,
|
||||||
|
CUPrice: tx.CUPrice,
|
||||||
|
|
||||||
|
AfterSignerToken0Balance: s.UserBaseBalance.Div(decimal.New(1, int32(s.BaseMintDecimals))),
|
||||||
|
|
||||||
|
BeforeSolBalance: tx.BeforeSolBalance,
|
||||||
|
AfterSOLBalance: tx.AfterSOLBalance,
|
||||||
|
|
||||||
|
EntryContract: s.CheckEntryContract(),
|
||||||
|
Mayhem: s.Mayhem,
|
||||||
|
}
|
||||||
|
} else if s.Program == "PumpAMM" {
|
||||||
|
if s.BaseMint.Equals(solana.WrappedSol) {
|
||||||
|
eventName := s.Event
|
||||||
|
if s.Event == "buy" {
|
||||||
|
eventName = "sell"
|
||||||
|
} else if s.Event == "sell" {
|
||||||
|
eventName = "buy"
|
||||||
|
}
|
||||||
|
token0Program = s.QuoteTokenProgram
|
||||||
|
token0Address = s.QuoteMint
|
||||||
|
newTx = &Tx{
|
||||||
|
Err: nil,
|
||||||
|
//BondingCurve: s.Pool.String(),
|
||||||
|
PairAddress: s.Pool.String(),
|
||||||
|
Maker: s.User.String(),
|
||||||
|
Token1Address: s.BaseMint.String(),
|
||||||
|
Token0Program: s.QuoteTokenProgram.String(),
|
||||||
|
Token0Decimals: s.QuoteMintDecimals,
|
||||||
|
Token0Address: s.QuoteMint.String(),
|
||||||
|
Token1Amount: s.BaseAmount.Div(decimal.New(1, int32(s.BaseMintDecimals))),
|
||||||
|
Token0Amount: s.QuoteAmount.Div(decimal.New(1, int32(s.QuoteMintDecimals))),
|
||||||
|
Block: tx.Block,
|
||||||
|
BlockIndex: tx.BlockIndex,
|
||||||
|
Event: eventName,
|
||||||
|
CachedTxHash: "",
|
||||||
|
txHash: tx.TxHash,
|
||||||
|
TxIndex: uint64(i),
|
||||||
|
Program: s.Program,
|
||||||
|
BlockAt: pgtype.Timestamptz{
|
||||||
|
Time: time.Unix(tx.BlockAt, 0),
|
||||||
|
},
|
||||||
|
//CreatedAt: nil,
|
||||||
|
TotalSupply: "1000000000",
|
||||||
|
AfterReserve1: s.BaseReserve.Div(decimal.New(1, int32(s.BaseMintDecimals))).String(),
|
||||||
|
AfterReserve0: s.QuoteReserve.Div(decimal.New(1, int32(s.QuoteMintDecimals))).String(),
|
||||||
|
QuoteIsToken0: true,
|
||||||
|
// CurrentPrice: decimal.Decimal{},
|
||||||
|
TokenCreator: s.Creator.String(),
|
||||||
|
Platform: platform,
|
||||||
|
PlatformFee: platformFee,
|
||||||
|
|
||||||
|
MevAgent: mev,
|
||||||
|
MevAgentFee: mevFee,
|
||||||
|
CUPrice: tx.CUPrice,
|
||||||
|
|
||||||
|
AfterSignerToken0Balance: s.UserQuoteBalance.Div(decimal.New(1, int32(s.QuoteMintDecimals))),
|
||||||
|
|
||||||
|
BeforeSolBalance: tx.BeforeSolBalance,
|
||||||
|
AfterSOLBalance: tx.AfterSOLBalance,
|
||||||
|
|
||||||
|
EntryContract: s.CheckEntryContract(),
|
||||||
|
Mayhem: s.Mayhem,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newTx = &Tx{
|
||||||
|
Err: nil,
|
||||||
|
//BondingCurve: s.Pool.String(),
|
||||||
|
PairAddress: s.Pool.String(),
|
||||||
|
Maker: s.User.String(),
|
||||||
|
Token0Address: s.BaseMint.String(),
|
||||||
|
Token0Program: s.BaseTokenProgram.String(),
|
||||||
|
Token0Decimals: s.BaseMintDecimals,
|
||||||
|
Token1Address: s.QuoteMint.String(),
|
||||||
|
Token0Amount: s.BaseAmount.Div(decimal.New(1, int32(s.BaseMintDecimals))),
|
||||||
|
Token1Amount: s.QuoteAmount.Div(decimal.New(1, int32(s.QuoteMintDecimals))),
|
||||||
|
Block: tx.Block,
|
||||||
|
BlockIndex: tx.BlockIndex,
|
||||||
|
Event: s.Event,
|
||||||
|
CachedTxHash: "",
|
||||||
|
txHash: tx.TxHash,
|
||||||
|
TxIndex: uint64(i),
|
||||||
|
Program: s.Program,
|
||||||
|
BlockAt: pgtype.Timestamptz{
|
||||||
|
Time: time.Unix(tx.BlockAt, 0),
|
||||||
|
},
|
||||||
|
//CreatedAt: nil,
|
||||||
|
TotalSupply: "1000000000",
|
||||||
|
AfterReserve0: s.BaseReserve.Div(decimal.New(1, int32(s.BaseMintDecimals))).String(),
|
||||||
|
AfterReserve1: s.QuoteReserve.Div(decimal.New(1, int32(s.QuoteMintDecimals))).String(),
|
||||||
|
QuoteIsToken0: false,
|
||||||
|
// CurrentPrice: decimal.Decimal{},
|
||||||
|
TokenCreator: s.Creator.String(),
|
||||||
|
Platform: platform,
|
||||||
|
PlatformFee: platformFee,
|
||||||
|
|
||||||
|
MevAgent: mev,
|
||||||
|
MevAgentFee: mevFee,
|
||||||
|
CUPrice: tx.CUPrice,
|
||||||
|
|
||||||
|
AfterSignerToken0Balance: s.UserBaseBalance.Div(decimal.New(1, int32(s.BaseMintDecimals))),
|
||||||
|
|
||||||
|
BeforeSolBalance: tx.BeforeSolBalance,
|
||||||
|
AfterSOLBalance: tx.AfterSOLBalance,
|
||||||
|
|
||||||
|
EntryContract: s.CheckEntryContract(),
|
||||||
|
Mayhem: s.Mayhem,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if newTx == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if newTx.Maker == "HV1KXxWFaSeriyFvXyx48FqG9BoFbfinB8njCJonqP7K" && newTx.EntryContract == "oKXAggregatorV2" {
|
||||||
|
newTx.Maker = tx.Signer.String()
|
||||||
|
newTx.AfterSignerToken0Balance = parser.GetTokenBalanceAfterTx(raw, 0, token0Program, token0Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
txs = append(txs, newTx)
|
||||||
|
}
|
||||||
|
return txs
|
||||||
|
}
|
||||||
67
globals.go
Normal file
67
globals.go
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package pump_parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/gagliardetto/solana-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
WSOLString = "So11111111111111111111111111111111111111112"
|
||||||
|
WSOL = solana.MustPublicKeyFromBase58("So11111111111111111111111111111111111111112")
|
||||||
|
USDC = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SOLDecimals = 9
|
||||||
|
)
|
||||||
|
|
||||||
|
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 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 isMayhemPump(feeAccount solana.PublicKey) bool {
|
||||||
|
for _, mayhemFeeAccount := range mayhemFeeAccounts {
|
||||||
|
if feeAccount.Equals(mayhemFeeAccount) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
46
go.mod
Normal file
46
go.mod
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
module github.com/thloyi/pump-parser
|
||||||
|
|
||||||
|
go 1.25.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/gagliardetto/binary v0.8.0
|
||||||
|
github.com/gagliardetto/solana-go v1.14.0
|
||||||
|
github.com/jackc/pgtype v1.14.4
|
||||||
|
github.com/mr-tron/base58 v1.2.0
|
||||||
|
github.com/shopspring/decimal v1.4.0
|
||||||
|
google.golang.org/grpc v1.77.0
|
||||||
|
google.golang.org/protobuf v1.36.10
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
filippo.io/edwards25519 v1.0.0 // indirect
|
||||||
|
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
|
||||||
|
github.com/blendle/zapdriver v1.3.1 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/fatih/color v1.9.0 // indirect
|
||||||
|
github.com/gagliardetto/treeout v0.1.4 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/jackc/pgio v1.0.0 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/compress v1.13.6 // indirect
|
||||||
|
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect
|
||||||
|
github.com/streamingfast/logging v0.0.0-20250918142248-ac5a1e292845 // indirect
|
||||||
|
go.mongodb.org/mongo-driver v1.12.2 // indirect
|
||||||
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
|
go.uber.org/ratelimit v0.2.0 // indirect
|
||||||
|
go.uber.org/zap v1.21.0 // indirect
|
||||||
|
golang.org/x/crypto v0.43.0 // indirect
|
||||||
|
golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 // indirect
|
||||||
|
golang.org/x/sys v0.37.0 // indirect
|
||||||
|
golang.org/x/term v0.36.0 // indirect
|
||||||
|
golang.org/x/text v0.30.0 // indirect
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect
|
||||||
|
)
|
||||||
344
go.sum
Normal file
344
go.sum
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
|
||||||
|
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||||
|
github.com/AlekSi/pointer v1.1.0 h1:SSDMPcXD9jSl8FPy9cRzoRaMJtm9g9ggGTxecRUbQoI=
|
||||||
|
github.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj48UJIZE=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
|
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI=
|
||||||
|
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
|
||||||
|
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||||
|
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
|
github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE=
|
||||||
|
github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc=
|
||||||
|
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
||||||
|
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||||
|
github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg=
|
||||||
|
github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c=
|
||||||
|
github.com/gagliardetto/solana-go v1.14.0 h1:3WfAi70jOOjAJ0deFMjdhFYlLXATF4tOQXsDNWJtOLw=
|
||||||
|
github.com/gagliardetto/solana-go v1.14.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k=
|
||||||
|
github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw=
|
||||||
|
github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89VZhmMOyCyqok=
|
||||||
|
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||||
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
|
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
|
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
||||||
|
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||||
|
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||||
|
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||||
|
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||||
|
github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=
|
||||||
|
github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
|
||||||
|
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||||
|
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
|
||||||
|
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||||
|
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
||||||
|
github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||||
|
github.com/jackc/pgtype v1.14.4 h1:fKuNiCumbKTAIxQwXfB/nsrnkEI6bPJrrSiMKgbJ2j8=
|
||||||
|
github.com/jackc/pgtype v1.14.4/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||||
|
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||||
|
github.com/jackc/pgx/v4 v4.18.2 h1:xVpYkNR5pk5bMCZGfClbO962UIqVABcAGt7ha1s/FeU=
|
||||||
|
github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
|
||||||
|
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
|
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||||
|
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
||||||
|
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||||
|
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||||
|
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||||
|
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
|
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
|
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||||
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||||
|
github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 h1:mPMvm6X6tf4w8y7j9YIt6V9jfWhL6QlbEc7CCmeQlWk=
|
||||||
|
github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1/go.mod h1:ye2e/VUEtE2BHE+G/QcKkcLQVAEJoYRFj5VUOQatCRE=
|
||||||
|
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||||
|
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||||
|
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
||||||
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||||
|
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||||
|
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||||
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
|
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||||
|
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
|
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
|
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||||
|
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||||
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU=
|
||||||
|
github.com/streamingfast/logging v0.0.0-20250918142248-ac5a1e292845 h1:VMA0pZ3MI8BErRA3kh8dKJThP5d0Xh5vZVk5yFIgH/A=
|
||||||
|
github.com/streamingfast/logging v0.0.0-20250918142248-ac5a1e292845/go.mod h1:BtDq81Tyc7H8up5aXNi/I95nPmG3C0PLEqGWY/iWQ2E=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
|
||||||
|
github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
|
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||||
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||||
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||||
|
go.mongodb.org/mongo-driver v1.12.2 h1:gbWY1bJkkmUB9jjZzcdhOL8O85N9H+Vvsf2yFN0RDws=
|
||||||
|
go.mongodb.org/mongo-driver v1.12.2/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||||
|
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||||
|
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||||
|
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||||
|
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||||
|
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||||
|
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||||
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||||
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||||
|
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
|
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||||
|
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||||
|
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||||
|
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
|
go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA=
|
||||||
|
go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=
|
||||||
|
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||||
|
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||||
|
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
|
||||||
|
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
|
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
|
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
|
||||||
|
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||||
|
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
|
golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 h1:6/3JGEh1C88g7m+qzzTbl3A0FtsLguXieqofVLU/JAo=
|
||||||
|
golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||||
|
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
|
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
|
||||||
|
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||||
|
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba h1:UKgtfRM7Yh93Sya0Fo8ZzhDP4qBckrrxEr2oF5UIVb8=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||||
|
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
||||||
|
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||||
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||||
|
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
73
meta.go
Normal file
73
meta.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package pump_parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gagliardetto/solana-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pumpProgram = solana.MustPublicKeyFromBase58("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P")
|
||||||
|
var pumpMigrationAccount = solana.MustPublicKeyFromBase58("39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg")
|
||||||
|
var mayhemFeeAccounts = []solana.PublicKey{
|
||||||
|
solana.MustPublicKeyFromBase58("GesfTA3X2arioaHp8bbKdjG9vJtskViWACZoYvxp4twS"),
|
||||||
|
solana.MustPublicKeyFromBase58("4budycTjhs9fD6xw62VBducVTNgMgJJ5BgtKq7mAZwn6"),
|
||||||
|
solana.MustPublicKeyFromBase58("4UQeTP1T39KZ9Sfxzo3WR5skgsaP6NZa87BAkuazLEKH"),
|
||||||
|
solana.MustPublicKeyFromBase58("8SBKzEQU4nLSzcwF4a74F2iaUDQyTfjGndn6qUWBnrpR"),
|
||||||
|
solana.MustPublicKeyFromBase58("8sNeir4QsLsJdYpc9RZacohhK1Y5FLU3nC5LXgYB4aa6"),
|
||||||
|
solana.MustPublicKeyFromBase58("Fh9HmeLNUMVCvejxCtCL2DbYaRyBFVJ5xrWkLnMH6fdk"),
|
||||||
|
solana.MustPublicKeyFromBase58("463MEnMeGyJekNZFQSTUABBEbLnvMTALbT6ZmsxAbAdq"),
|
||||||
|
solana.MustPublicKeyFromBase58("6AUH3WEHucYZyC61hqpqYUWVto5qA5hjHuNQ32GNnNxA"),
|
||||||
|
}
|
||||||
|
|
||||||
|
var pumpBuyDiscriminator = calculateDiscriminator("global:buy")
|
||||||
|
var pumpBuyV2Discriminator = calculateDiscriminator("global:buy_exact_sol_in")
|
||||||
|
var pumpSellDiscriminator = calculateDiscriminator("global:sell")
|
||||||
|
var pumpCreateDiscriminator = calculateDiscriminator("global:create")
|
||||||
|
var pumpCreateV2Discriminator = calculateDiscriminator("global:create_v2")
|
||||||
|
var pumpAdminSetCreatorDiscriminator = calculateDiscriminator("global:admin_set_creator")
|
||||||
|
var pumpMigrateDiscriminator = calculateDiscriminator("global:migrate")
|
||||||
|
|
||||||
|
var pumpEventDiscriminator = [8]byte{228, 69, 165, 46, 81, 203, 154, 29}
|
||||||
|
var pumpTradeEventDiscriminator = [16]byte{228, 69, 165, 46, 81, 203, 154, 29, 189, 219, 127, 211, 78, 230, 97, 238}
|
||||||
|
var pumpCreateEventDiscriminator = [8]byte{27, 114, 169, 77, 222, 235, 99, 118}
|
||||||
|
var pumpCompleteEventDiscriminator = [8]byte{95, 114, 97, 156, 212, 46, 152, 8}
|
||||||
|
var pumpMigrateEventDiscriminator = calculateDiscriminator("event:CompletePumpAmmMigrationEvent")
|
||||||
|
var pumpBuyEventDiscriminator = [8]byte{189, 219, 127, 211, 78, 230, 97, 238}
|
||||||
|
|
||||||
|
var (
|
||||||
|
pumpAmmProgram = solana.MustPublicKeyFromBase58("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA")
|
||||||
|
wSolMint = solana.MustPublicKeyFromBase58("So11111111111111111111111111111111111111112")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
pumpAmmBuyDiscriminator = calculateDiscriminator("global:buy")
|
||||||
|
pumpAmmSellDiscriminator = calculateDiscriminator("global:sell")
|
||||||
|
pumpAmmCreateDiscriminator = calculateDiscriminator("global:create_pool")
|
||||||
|
pumpAmmWithdrawDiscriminator = calculateDiscriminator("global:withdraw")
|
||||||
|
pumpAmmDepositDiscriminator = calculateDiscriminator("global:deposit")
|
||||||
|
|
||||||
|
// UnParsedDiscriminator is the discriminator for unparsed data.
|
||||||
|
|
||||||
|
pumpAmmCreateConfigDiscriminator = calculateDiscriminator("global:create_config")
|
||||||
|
pumpAmmExtendAccountDiscriminator = calculateDiscriminator("global:extend_account")
|
||||||
|
pumpAmmUpdateFeeConfigDiscriminator = calculateDiscriminator("global:update_fee_config")
|
||||||
|
pumpAmmDisableDiscriminator = calculateDiscriminator("global:disable")
|
||||||
|
|
||||||
|
pumpAmmEventDiscriminator = [8]byte{228, 69, 165, 46, 81, 203, 154, 29}
|
||||||
|
pumpAmmBuyEventDiscriminator = calculateDiscriminator("event:BuyEvent")
|
||||||
|
pumpAmmSellEventDiscriminator = calculateDiscriminator("event:SellEvent")
|
||||||
|
pumpAmmCreateEventDiscriminator = calculateDiscriminator("event:CreatePoolEvent")
|
||||||
|
pumpAmmWithdrawEventDiscriminator = calculateDiscriminator("event:WithdrawEvent")
|
||||||
|
pumpAmmDepositEventDiscriminator = calculateDiscriminator("event:DepositEvent")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Program PumpAmm program ID
|
||||||
|
|
||||||
|
var budgGetProgram = solana.MustPublicKeyFromBase58("ComputeBudget111111111111111111111111111111")
|
||||||
|
var setComputeUnitLimitDiscriminator = uint8(2)
|
||||||
|
var setComputeUnitPriceDiscriminator = uint8(3)
|
||||||
|
|
||||||
|
var transferDiscriminator = uint32(2)
|
||||||
|
var createAccountWithSeedDiscriminator = uint32(3)
|
||||||
|
|
||||||
|
var systemProgram = solana.MustPublicKeyFromBase58("11111111111111111111111111111111")
|
||||||
|
|
||||||
|
var raydiumLaunchLabProgramID = solana.MustPublicKeyFromBase58("LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj")
|
||||||
97
parser.go
Normal file
97
parser.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package pump_parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/gagliardetto/solana-go"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
var swapPrograms = map[solana.PublicKey]swapParser{
|
||||||
|
pumpAmmProgram: pumpAmmParser,
|
||||||
|
pumpProgram: pumpParser,
|
||||||
|
}
|
||||||
|
|
||||||
|
var actionPrograms = map[solana.PublicKey]actionParser{
|
||||||
|
systemProgram: systemParser,
|
||||||
|
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)),
|
||||||
|
}
|
||||||
|
var innersMap = make(map[int]InnerInstructions)
|
||||||
|
for _, inner := range rawTx.Meta.InnerInstructions {
|
||||||
|
innersMap[inner.Index] = inner
|
||||||
|
}
|
||||||
|
for i, instr := range 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)})
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, InstructionIgnoredError) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, 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)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, InstructionIgnoredError) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ii := i
|
||||||
|
// unknown program, parser inner instructions
|
||||||
|
innerLength := len(innersMap[i].Instructions)
|
||||||
|
for j := 1; j <= innerLength; {
|
||||||
|
innerInstr := innersMap[i].Instructions[j-1]
|
||||||
|
innerProgramAccount := accountList[innerInstr.ProgramIDIndex]
|
||||||
|
|
||||||
|
if p, exists := swapPrograms[innerProgramAccount]; exists {
|
||||||
|
swaps, offset, err := p(rawTx, 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
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, InstructionIgnoredError) {
|
||||||
|
j = int(offset[1])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
j = int(offset[1])
|
||||||
|
ii = int(offset[0])
|
||||||
|
} else {
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
if j > innerLength || ii > i {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx, nil
|
||||||
|
}
|
||||||
439
pump.go
Normal file
439
pump.go
Normal file
@@ -0,0 +1,439 @@
|
|||||||
|
package pump_parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
agbinary "github.com/gagliardetto/binary"
|
||||||
|
"github.com/gagliardetto/solana-go"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func increaseOffset(offset [2]uint) [2]uint {
|
||||||
|
if offset[1] == 0 {
|
||||||
|
return [2]uint{offset[0] + 1, offset[1]}
|
||||||
|
}
|
||||||
|
return [2]uint{offset[0], offset[1] + 1}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
|
||||||
|
if !result.accountList[instruction.ProgramIDIndex].Equals(pumpProgram) {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pump program instruction not found, offset, %d, %d", offset[0], offset[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
decode := instruction.Data
|
||||||
|
if len(decode) < 8 {
|
||||||
|
offset[1] += 1
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pump program instruction data too short, offset, %d, %d", offset[0], offset[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
discriminator := *(*[8]byte)(decode[:8])
|
||||||
|
|
||||||
|
switch discriminator {
|
||||||
|
case pumpBuyV2Discriminator, pumpBuyDiscriminator, pumpSellDiscriminator:
|
||||||
|
return BuyOrSellParser(result, instruction, innerInstructions, offset)
|
||||||
|
case pumpCreateDiscriminator, pumpCreateV2Discriminator:
|
||||||
|
return CreateParser(result, instruction, innerInstructions, offset)
|
||||||
|
case pumpMigrateDiscriminator:
|
||||||
|
return MigrateParser(result, instruction, innerInstructions, offset)
|
||||||
|
default:
|
||||||
|
return nil, increaseOffset(offset), InstructionIgnoredError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PumpCreateData struct {
|
||||||
|
Discriminator uint64
|
||||||
|
Name string
|
||||||
|
Symbol string
|
||||||
|
Uri string
|
||||||
|
Creator solana.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
type PumpCreateV2Data struct {
|
||||||
|
Discriminator uint64
|
||||||
|
Name string
|
||||||
|
Symbol string
|
||||||
|
Uri string
|
||||||
|
|
||||||
|
Creator solana.PublicKey
|
||||||
|
IsMayhem bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type PumpCreateEvent struct {
|
||||||
|
Name string
|
||||||
|
Symbol string
|
||||||
|
Uri string
|
||||||
|
|
||||||
|
Mint solana.PublicKey
|
||||||
|
BondingCurve solana.PublicKey
|
||||||
|
User solana.PublicKey
|
||||||
|
Creator solana.PublicKey
|
||||||
|
|
||||||
|
Timestamp int64
|
||||||
|
VirtualTokenReserves uint64
|
||||||
|
VirtualSolReserves uint64
|
||||||
|
RealTokenReserves uint64
|
||||||
|
TokenTotalSupply uint64
|
||||||
|
TokenProgram solana.PublicKey
|
||||||
|
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) {
|
||||||
|
var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
||||||
|
|
||||||
|
var programIndex = instr.ProgramIDIndex
|
||||||
|
var err error
|
||||||
|
|
||||||
|
var createEvent PumpCreateEvent
|
||||||
|
var prefixLen = offset[1]
|
||||||
|
inners, err := getInnerInstructions(innerInstructions, prefixLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pump create get inner instructions error: %v, offset, %d, %d", err, offset[0], offset[1])
|
||||||
|
}
|
||||||
|
for innerIndex, innerInstr := range inners {
|
||||||
|
if innerInstr.ProgramIDIndex == programIndex && bytes.Equal(innerInstr.Data[:8], pumpEventDiscriminator[:]) && bytes.Equal(innerInstr.Data[8:16], pumpCreateEventDiscriminator[:]) {
|
||||||
|
err = agbinary.NewBorshDecoder(innerInstr.Data[16:]).Decode(&createEvent)
|
||||||
|
if offset[1] == 0 {
|
||||||
|
offset[0] += 1
|
||||||
|
} else {
|
||||||
|
offset[1] += uint(innerIndex) + 1 + prefixLen
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, offset, fmt.Errorf("pump create event decode error: %v, offset, %d, %d", err, offset[0], offset[1])
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if createEvent == (PumpCreateEvent{}) {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pump create event not found, offset, %d, %d", offset[0], offset[1])
|
||||||
|
}
|
||||||
|
userIndex := 0
|
||||||
|
if bytes.HasPrefix(instr.Data, pumpCreateV2Discriminator[:]) {
|
||||||
|
userIndex = instr.Accounts[5]
|
||||||
|
} else if bytes.HasPrefix(instr.Data, pumpCreateDiscriminator[:]) {
|
||||||
|
userIndex = instr.Accounts[7]
|
||||||
|
}
|
||||||
|
userBase := getAccountBalanceAfterTx(result, userIndex)
|
||||||
|
userQuote, _ := GetSolAfterTx(result, userIndex)
|
||||||
|
|
||||||
|
return []Swap{
|
||||||
|
{
|
||||||
|
Program: SolProgramPump,
|
||||||
|
Event: "create",
|
||||||
|
Pool: createEvent.BondingCurve,
|
||||||
|
BaseMint: createEvent.Mint,
|
||||||
|
QuoteMint: solana.PublicKey{},
|
||||||
|
BaseTokenProgram: createEvent.TokenProgram,
|
||||||
|
QuoteTokenProgram: solana.PublicKey{},
|
||||||
|
Creator: createEvent.Creator,
|
||||||
|
BaseMintDecimals: 6,
|
||||||
|
QuoteMintDecimals: 9,
|
||||||
|
User: createEvent.User,
|
||||||
|
BaseAmount: decimal.Zero,
|
||||||
|
QuoteAmount: decimal.Zero,
|
||||||
|
BaseReserve: decimal.NewFromUint64(createEvent.RealTokenReserves),
|
||||||
|
QuoteReserve: decimal.Zero,
|
||||||
|
Mayhem: createEvent.IsMayhemMode,
|
||||||
|
UserBaseBalance: userBase,
|
||||||
|
UserQuoteBalance: decimal.NewFromUint64(userQuote),
|
||||||
|
EntryContract: entryContract,
|
||||||
|
},
|
||||||
|
}, offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type PumpTradeEvent struct {
|
||||||
|
Mint solana.PublicKey
|
||||||
|
SolAmount uint64
|
||||||
|
TokenAmount uint64
|
||||||
|
IsBuy bool
|
||||||
|
User solana.PublicKey
|
||||||
|
Timestamp int64
|
||||||
|
VirtualSolReserves uint64
|
||||||
|
VirtualTokenReserves uint64
|
||||||
|
|
||||||
|
RealSolReserves uint64
|
||||||
|
RealTokenReserves uint64
|
||||||
|
|
||||||
|
FeeRecipient solana.PublicKey
|
||||||
|
FeeBasisPoints uint64
|
||||||
|
Fee uint64
|
||||||
|
|
||||||
|
Creator solana.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
type CompleteEvent struct {
|
||||||
|
User solana.PublicKey
|
||||||
|
Mint solana.PublicKey
|
||||||
|
BondingCurve solana.PublicKey
|
||||||
|
Timestamp int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuyOrSellParser(result *RawTx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||||
|
var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
||||||
|
var err error
|
||||||
|
var programIndex = instruction.ProgramIDIndex
|
||||||
|
|
||||||
|
var (
|
||||||
|
tradeEvent PumpTradeEvent
|
||||||
|
completeEvent CompleteEvent
|
||||||
|
completed bool
|
||||||
|
newoffset [2]uint
|
||||||
|
)
|
||||||
|
|
||||||
|
var prefixLen = offset[1]
|
||||||
|
inners, err := getInnerInstructions(innerInstructions, prefixLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pump create get inner instructions error: %v,offset, %d, %d", err, offset[0], offset[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
for innerIndex, innerInstr := range inners {
|
||||||
|
if innerInstr.ProgramIDIndex == programIndex && bytes.Equal(innerInstr.Data[:8], pumpEventDiscriminator[:]) {
|
||||||
|
if bytes.Equal(innerInstr.Data[8:16], pumpTradeEventDiscriminator[8:16]) {
|
||||||
|
err = agbinary.NewBorshDecoder(innerInstr.Data[16:]).Decode(&tradeEvent)
|
||||||
|
if offset[1] == 0 {
|
||||||
|
newoffset = [2]uint{offset[0] + 1, offset[1]}
|
||||||
|
} else {
|
||||||
|
newoffset = [2]uint{offset[0], prefixLen + uint(innerIndex) + 1}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, newoffset, fmt.Errorf("pump buy event decode error: %v, offset, %d, %d", err, offset[0], offset[1])
|
||||||
|
}
|
||||||
|
if !tradeEvent.IsBuy {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else if bytes.Equal(innerInstr.Data[8:16], pumpCompleteEventDiscriminator[:]) {
|
||||||
|
err = agbinary.NewBorshDecoder(innerInstr.Data[16:]).Decode(&completeEvent)
|
||||||
|
if offset[1] == 0 {
|
||||||
|
newoffset = [2]uint{offset[0] + 1, offset[1]}
|
||||||
|
} else {
|
||||||
|
newoffset = [2]uint{offset[0], prefixLen + uint(innerIndex) + 1}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, newoffset, fmt.Errorf("pump completeEvent event decode error: %v, offset, %d, %d", err, offset[0], offset[1])
|
||||||
|
}
|
||||||
|
completed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tradeEvent == (PumpTradeEvent{}) {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pmp buy/sell event not found, offset, %d, %d", offset[0], offset[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = [2]uint{newoffset[0], newoffset[1]}
|
||||||
|
ataUserIdx := instruction.Accounts[5]
|
||||||
|
userIndex := instruction.Accounts[6]
|
||||||
|
|
||||||
|
userBase := getAccountBalanceAfterTx(result, ataUserIdx)
|
||||||
|
userQuote, _ := GetSolAfterTx(result, userIndex)
|
||||||
|
|
||||||
|
event := ""
|
||||||
|
baseTokenProgram := solana.TokenProgramID
|
||||||
|
if tradeEvent.IsBuy {
|
||||||
|
event = "buy"
|
||||||
|
baseTokenProgram = result.accountList[instruction.Accounts[8]]
|
||||||
|
} else {
|
||||||
|
event = "sell"
|
||||||
|
baseTokenProgram = result.accountList[instruction.Accounts[9]]
|
||||||
|
}
|
||||||
|
swaps := []Swap{
|
||||||
|
{
|
||||||
|
Program: SolProgramPump,
|
||||||
|
Event: event,
|
||||||
|
Pool: result.accountList[instruction.Accounts[3]],
|
||||||
|
BaseMint: tradeEvent.Mint,
|
||||||
|
QuoteMint: solana.PublicKey{},
|
||||||
|
BaseTokenProgram: baseTokenProgram,
|
||||||
|
QuoteTokenProgram: solana.PublicKey{},
|
||||||
|
Creator: tradeEvent.Creator,
|
||||||
|
BaseMintDecimals: 6,
|
||||||
|
QuoteMintDecimals: 9,
|
||||||
|
User: tradeEvent.User,
|
||||||
|
BaseAmount: decimal.NewFromUint64(tradeEvent.TokenAmount),
|
||||||
|
QuoteAmount: decimal.NewFromUint64(tradeEvent.SolAmount),
|
||||||
|
BaseReserve: decimal.NewFromUint64(tradeEvent.RealTokenReserves),
|
||||||
|
QuoteReserve: decimal.NewFromUint64(tradeEvent.RealSolReserves),
|
||||||
|
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[1]]),
|
||||||
|
UserBaseBalance: userBase,
|
||||||
|
UserQuoteBalance: decimal.NewFromUint64(userQuote),
|
||||||
|
EntryContract: entryContract,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if completed {
|
||||||
|
swaps = append(swaps, Swap{
|
||||||
|
Program: SolProgramPump,
|
||||||
|
Event: "complete",
|
||||||
|
Pool: result.accountList[instruction.Accounts[3]],
|
||||||
|
BaseMint: tradeEvent.Mint,
|
||||||
|
QuoteMint: solana.PublicKey{},
|
||||||
|
BaseTokenProgram: result.accountList[instruction.Accounts[8]],
|
||||||
|
QuoteTokenProgram: solana.PublicKey{},
|
||||||
|
Creator: tradeEvent.Creator,
|
||||||
|
BaseMintDecimals: 6,
|
||||||
|
QuoteMintDecimals: 9,
|
||||||
|
User: tradeEvent.User,
|
||||||
|
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[1]]),
|
||||||
|
UserBaseBalance: userBase,
|
||||||
|
UserQuoteBalance: decimal.NewFromUint64(userQuote),
|
||||||
|
EntryContract: entryContract,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return swaps, offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type MigrateEvent struct {
|
||||||
|
User solana.PublicKey
|
||||||
|
Mint solana.PublicKey
|
||||||
|
//Creator solana.PublicKey
|
||||||
|
|
||||||
|
MintAmount uint64
|
||||||
|
SolAmount uint64
|
||||||
|
PoolMigrationFee uint64
|
||||||
|
//CreatorFee uint64
|
||||||
|
BondingCurve solana.PublicKey
|
||||||
|
TimeStamp int64
|
||||||
|
Pool solana.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func MigrateParser(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 err error
|
||||||
|
programIndex := instr.ProgramIDIndex
|
||||||
|
ammprogramIdx := 0
|
||||||
|
for i, b := range result.accountList {
|
||||||
|
if b.Equals(pumpAmmProgram) {
|
||||||
|
ammprogramIdx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ammprogramIdx == 0 {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pump migrate, amm program id not found in account list, offset, %d, %d", offset[0], offset[1])
|
||||||
|
}
|
||||||
|
var prefixLen = offset[1]
|
||||||
|
inners, err := getInnerInstructions(innerInstructions, prefixLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pump migrate get inner instructions offset, %d, %d", offset[0], offset[1])
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
migrateEvent MigrateEvent
|
||||||
|
createEvent ammCreatePoolEvent
|
||||||
|
newoffset [2]uint
|
||||||
|
)
|
||||||
|
for innerIndex, innerInstr := range inners {
|
||||||
|
if innerInstr.ProgramIDIndex == ammprogramIdx && bytes.Equal(innerInstr.Data[:8], pumpAmmEventDiscriminator[:]) {
|
||||||
|
if bytes.Equal(innerInstr.Data[8:16], pumpAmmCreateEventDiscriminator[:]) {
|
||||||
|
err = agbinary.NewBorshDecoder(innerInstr.Data[16:]).Decode(&createEvent)
|
||||||
|
if offset[1] == 0 {
|
||||||
|
newoffset = [2]uint{offset[0] + 1, offset[1]}
|
||||||
|
} else {
|
||||||
|
newoffset = [2]uint{offset[0], prefixLen + uint(innerIndex) + 1}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, newoffset, fmt.Errorf("pump amm createEvent decode error: %v, offset, %d, %d", err, offset[0], offset[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
} else if innerInstr.ProgramIDIndex == programIndex && bytes.Equal(innerInstr.Data[:8], pumpEventDiscriminator[:]) {
|
||||||
|
if bytes.Equal(innerInstr.Data[8:16], pumpMigrateEventDiscriminator[:]) {
|
||||||
|
err = agbinary.NewBorshDecoder(innerInstr.Data[16:]).Decode(&migrateEvent)
|
||||||
|
if offset[1] == 0 {
|
||||||
|
newoffset = [2]uint{offset[0] + 1, offset[1]}
|
||||||
|
} else {
|
||||||
|
newoffset = [2]uint{offset[0], prefixLen + uint(innerIndex) + 1}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, newoffset, fmt.Errorf("pump buy event decode error: %v, offset, %d, %d", err, offset[0], offset[1])
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if migrateEvent == (MigrateEvent{}) || createEvent == (ammCreatePoolEvent{}) {
|
||||||
|
offset = [2]uint{newoffset[0], newoffset[1]}
|
||||||
|
return nil, increaseOffset(offset), InstructionIgnoredError
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = [2]uint{newoffset[0], newoffset[1]}
|
||||||
|
// verify migrate by checking create pool and migrate event
|
||||||
|
userIndex := instr.Accounts[5]
|
||||||
|
ataBondingCurveAccountIndex := instr.Accounts[4]
|
||||||
|
bc, err := getTokenBalanceAfterTx(result, ataBondingCurveAccountIndex)
|
||||||
|
if err != nil || bc == nil {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pump migrate get bonding curve balance error: %v, offset, %d, %d", err, offset[0], offset[1])
|
||||||
|
}
|
||||||
|
baseTokenProgram := bc.ProgramIDAccount
|
||||||
|
var userBase decimal.Decimal
|
||||||
|
if result.accountList[userIndex].Equals(pumpMigrationAccount) {
|
||||||
|
userBase = decimal.Zero
|
||||||
|
} else {
|
||||||
|
userBase = GetTokenBalanceAfterTx(result, userIndex, baseTokenProgram, migrateEvent.Mint)
|
||||||
|
}
|
||||||
|
userQuote, _ := GetSolAfterTx(result, userIndex)
|
||||||
|
|
||||||
|
swaps := []Swap{
|
||||||
|
{
|
||||||
|
Program: SolProgramPump,
|
||||||
|
Event: "migrate",
|
||||||
|
Pool: migrateEvent.BondingCurve,
|
||||||
|
BaseMint: migrateEvent.Mint,
|
||||||
|
QuoteMint: solana.PublicKey{},
|
||||||
|
BaseTokenProgram: baseTokenProgram,
|
||||||
|
QuoteTokenProgram: solana.PublicKey{},
|
||||||
|
Creator: createEvent.Creator,
|
||||||
|
BaseMintDecimals: 6,
|
||||||
|
QuoteMintDecimals: 9,
|
||||||
|
User: migrateEvent.User,
|
||||||
|
//BaseAmount: decimal.Decimal{},
|
||||||
|
//QuoteAmount: decimal.Decimal{},
|
||||||
|
BaseReserve: decimal.NewFromUint64(migrateEvent.MintAmount),
|
||||||
|
QuoteReserve: decimal.NewFromUint64(migrateEvent.SolAmount),
|
||||||
|
Mayhem: createEvent.IsMayhemMode,
|
||||||
|
UserBaseBalance: userBase,
|
||||||
|
UserQuoteBalance: decimal.NewFromUint64(userQuote),
|
||||||
|
EntryContract: entryContract,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
swaps = append(swaps, Swap{
|
||||||
|
Program: SolProgramPumpAMM,
|
||||||
|
Event: "create",
|
||||||
|
Pool: migrateEvent.Pool,
|
||||||
|
BaseMint: migrateEvent.Mint,
|
||||||
|
QuoteMint: wSolMint,
|
||||||
|
BaseTokenProgram: baseTokenProgram,
|
||||||
|
QuoteTokenProgram: solana.TokenProgramID,
|
||||||
|
Creator: createEvent.Creator,
|
||||||
|
BaseMintDecimals: 6,
|
||||||
|
QuoteMintDecimals: 9,
|
||||||
|
User: migrateEvent.User,
|
||||||
|
BaseAmount: decimal.NewFromUint64(migrateEvent.MintAmount),
|
||||||
|
QuoteAmount: decimal.NewFromUint64(migrateEvent.SolAmount),
|
||||||
|
BaseReserve: decimal.NewFromUint64(migrateEvent.MintAmount),
|
||||||
|
QuoteReserve: decimal.NewFromUint64(migrateEvent.SolAmount),
|
||||||
|
Mayhem: createEvent.IsMayhemMode,
|
||||||
|
UserBaseBalance: userBase,
|
||||||
|
UserQuoteBalance: decimal.NewFromUint64(userQuote),
|
||||||
|
EntryContract: entryContract,
|
||||||
|
})
|
||||||
|
|
||||||
|
return swaps, offset, nil
|
||||||
|
|
||||||
|
}
|
||||||
513
pumpamm.go
Normal file
513
pumpamm.go
Normal file
@@ -0,0 +1,513 @@
|
|||||||
|
package pump_parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
agbinary "github.com/gagliardetto/binary"
|
||||||
|
"github.com/gagliardetto/solana-go"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ammBuyEvent struct {
|
||||||
|
TimeStamp int64
|
||||||
|
BaseAmountOut uint64
|
||||||
|
MaxQuoteAmountIn uint64
|
||||||
|
UserBaseTokenReserve uint64
|
||||||
|
UserQuoteTokenReserve uint64
|
||||||
|
PoolBaseTokenReserve uint64
|
||||||
|
PoolQuoteTokenReserve uint64
|
||||||
|
QuoteAmountIn uint64
|
||||||
|
LpFeeBasisPoints uint64
|
||||||
|
LpFee uint64
|
||||||
|
ProtocolFee uint64
|
||||||
|
QuoteAmountInWithLpFee uint64
|
||||||
|
UserQuoteAmountIn uint64
|
||||||
|
|
||||||
|
Pool solana.PublicKey
|
||||||
|
User solana.PublicKey
|
||||||
|
UserBaseTokenAccount solana.PublicKey
|
||||||
|
UserQuoteTokenAccount solana.PublicKey
|
||||||
|
ProtocolFeeRecipient solana.PublicKey
|
||||||
|
ProtocolFeeRecipientTokenAccount solana.PublicKey
|
||||||
|
CoinCreator solana.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
type ammCreatePoolEvent struct {
|
||||||
|
TimeStamp int64
|
||||||
|
Index uint16
|
||||||
|
Creator solana.PublicKey
|
||||||
|
BaseMint solana.PublicKey
|
||||||
|
QuoteMint solana.PublicKey
|
||||||
|
BaseMintDecimals uint8
|
||||||
|
QuoteMintDecimals uint8
|
||||||
|
BaseAmountIn uint64
|
||||||
|
QuoteAmountIn uint64
|
||||||
|
PoolBaseAmount uint64
|
||||||
|
PoolQuoteAmount uint64
|
||||||
|
MinimumLiquidity uint64
|
||||||
|
InitialLiquidity uint64
|
||||||
|
LpTokenAmountOut uint64
|
||||||
|
PoolBump uint8
|
||||||
|
|
||||||
|
Pool solana.PublicKey
|
||||||
|
LpMint solana.PublicKey
|
||||||
|
UserBaseTokenAccount solana.PublicKey
|
||||||
|
UserQuoteTokenAccount solana.PublicKey
|
||||||
|
CoinCreator solana.PublicKey
|
||||||
|
IsMayhemMode bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ammDepositEvent struct {
|
||||||
|
TimeStamp int64
|
||||||
|
LpTokenAmountOut uint64
|
||||||
|
MaxBaseAmountIn uint64
|
||||||
|
MaxQuoteAmountIn uint64
|
||||||
|
UserBaseTokenReserves uint64
|
||||||
|
UserQuoteTokenReserves uint64
|
||||||
|
PoolBaseTokenReserves uint64
|
||||||
|
PoolQuoteTokenReserves uint64
|
||||||
|
BaseAmountIn uint64
|
||||||
|
QuoteAmountIn uint64
|
||||||
|
LpMintSupply uint64
|
||||||
|
|
||||||
|
Pool solana.PublicKey
|
||||||
|
User solana.PublicKey
|
||||||
|
UserBaseTokenAccount solana.PublicKey
|
||||||
|
UserQuoteTokenAccount solana.PublicKey
|
||||||
|
UserPoolTokenAccount solana.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
type ammSellEvent struct {
|
||||||
|
Timestamp int64
|
||||||
|
BaseAmountIn uint64
|
||||||
|
MinQuoteAmountOut uint64
|
||||||
|
UserBaseTokenReserves uint64
|
||||||
|
UserQuoteTokenReserves uint64
|
||||||
|
PoolBaseTokenReserves uint64
|
||||||
|
PoolQuoteTokenReserves uint64
|
||||||
|
QuoteAmountOut uint64
|
||||||
|
LpFeeBasisPoints uint64
|
||||||
|
LpFee uint64
|
||||||
|
ProtocolFeeBasisPoints uint64
|
||||||
|
ProtocolFee uint64
|
||||||
|
QuoteAmountOutWithoutLpFee uint64
|
||||||
|
UserQuoteAmountOut uint64
|
||||||
|
|
||||||
|
Pool solana.PublicKey
|
||||||
|
User solana.PublicKey
|
||||||
|
UserBaseTokenAccount solana.PublicKey
|
||||||
|
UserQuoteTokenAccount solana.PublicKey
|
||||||
|
ProtocolFeeRecipient solana.PublicKey
|
||||||
|
ProtocolFeeRecipientTokenAccount solana.PublicKey
|
||||||
|
CoinCreator solana.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
type ammWithdrawEvent struct {
|
||||||
|
Timestamp int64
|
||||||
|
LpTokenAmountIn uint64
|
||||||
|
MinBaseAmountOut uint64
|
||||||
|
MinQuoteAmountOut uint64
|
||||||
|
UserBaseTokenReserves uint64
|
||||||
|
UserQuoteTokenReserves uint64
|
||||||
|
PoolBaseTokenReserves uint64
|
||||||
|
PoolQuoteTokenReserves uint64
|
||||||
|
BaseAmountOut uint64
|
||||||
|
QuoteAmountOut uint64
|
||||||
|
LpMintSupply uint64
|
||||||
|
|
||||||
|
Pool solana.PublicKey
|
||||||
|
User solana.PublicKey
|
||||||
|
UserBaseTokenAccount solana.PublicKey
|
||||||
|
UserQuoteTokenAccount solana.PublicKey
|
||||||
|
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) {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pump amm program instruction not found, offset: %d, %d", offset[0], offset[1])
|
||||||
|
}
|
||||||
|
decode := instruction.Data
|
||||||
|
if len(decode) < 8 {
|
||||||
|
offset[1] += 1
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pumpamm program instruction data too short, offset: %d, %d", offset[0], offset[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
discriminator := *(*[8]byte)(decode[:8])
|
||||||
|
switch discriminator {
|
||||||
|
case pumpAmmCreateDiscriminator:
|
||||||
|
return ammCreatePoolParser(result, instruction, innerInstructions, offset)
|
||||||
|
case pumpAmmBuyDiscriminator:
|
||||||
|
return ammBuyParser(result, instruction, innerInstructions, offset)
|
||||||
|
case pumpAmmSellDiscriminator:
|
||||||
|
return ammSellParser(result, instruction, innerInstructions, offset)
|
||||||
|
case pumpAmmDepositDiscriminator:
|
||||||
|
return depositParse(result, instruction, innerInstructions, offset)
|
||||||
|
case pumpAmmWithdrawDiscriminator:
|
||||||
|
return withdrawParse(result, instruction, innerInstructions, offset)
|
||||||
|
default:
|
||||||
|
return nil, increaseOffset(offset), InstructionIgnoredError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ammCreatePoolParser(result *RawTx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||||
|
var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
||||||
|
var err error
|
||||||
|
var prefixLen = offset[1]
|
||||||
|
inners, err := getInnerInstructions(innerInstructions, prefixLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pumpamm create get inner instructions error: %v, offset: %d, %d", err, offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
var createEvent ammCreatePoolEvent
|
||||||
|
for innerIndex, innerInstr := range inners {
|
||||||
|
if innerInstr.ProgramIDIndex == instruction.ProgramIDIndex &&
|
||||||
|
bytes.Equal(innerInstr.Data[:8], pumpAmmEventDiscriminator[:]) &&
|
||||||
|
bytes.Equal(innerInstr.Data[8:16], pumpAmmCreateEventDiscriminator[:]) {
|
||||||
|
err = agbinary.NewBorshDecoder(innerInstr.Data[16:]).Decode(&createEvent)
|
||||||
|
if offset[1] == 0 {
|
||||||
|
offset[0] += 1
|
||||||
|
} else {
|
||||||
|
offset[1] += uint(innerIndex) + 1 + prefixLen
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, offset, fmt.Errorf("pump amm create pool event decode error: %v, offset: %d, %d", err, offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if createEvent == (ammCreatePoolEvent{}) {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pump amm create pool event not found, offset: %d, %d", offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
baseTokenProgram := result.accountList[instruction.Accounts[13]]
|
||||||
|
quoteTokenProgram := result.accountList[instruction.Accounts[14]]
|
||||||
|
return []Swap{
|
||||||
|
{
|
||||||
|
Program: SolProgramPumpAMM,
|
||||||
|
Event: "create",
|
||||||
|
Pool: createEvent.Pool,
|
||||||
|
BaseMint: createEvent.BaseMint,
|
||||||
|
QuoteMint: createEvent.QuoteMint,
|
||||||
|
BaseTokenProgram: baseTokenProgram,
|
||||||
|
QuoteTokenProgram: quoteTokenProgram,
|
||||||
|
Creator: createEvent.CoinCreator,
|
||||||
|
BaseMintDecimals: createEvent.BaseMintDecimals,
|
||||||
|
QuoteMintDecimals: createEvent.QuoteMintDecimals,
|
||||||
|
User: createEvent.Creator,
|
||||||
|
BaseAmount: decimal.NewFromUint64(createEvent.BaseAmountIn),
|
||||||
|
QuoteAmount: decimal.NewFromUint64(createEvent.QuoteAmountIn),
|
||||||
|
BaseReserve: decimal.NewFromUint64(createEvent.PoolBaseAmount),
|
||||||
|
QuoteReserve: decimal.NewFromUint64(createEvent.PoolQuoteAmount),
|
||||||
|
UserBaseBalance: decimal.Decimal{},
|
||||||
|
UserQuoteBalance: decimal.Decimal{},
|
||||||
|
EntryContract: entryContract,
|
||||||
|
Mayhem: createEvent.IsMayhemMode,
|
||||||
|
},
|
||||||
|
}, offset, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ammBuyParser(result *RawTx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||||
|
var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
||||||
|
var err error
|
||||||
|
var prefixLen = offset[1]
|
||||||
|
inners, err := getInnerInstructions(innerInstructions, prefixLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pumpamm create get inner instructions error: %v, offset: %d, %d", err, offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
var event ammBuyEvent
|
||||||
|
for innerIndex, innerInstr := range inners {
|
||||||
|
if innerInstr.ProgramIDIndex == instruction.ProgramIDIndex &&
|
||||||
|
bytes.Equal(innerInstr.Data[:8], pumpAmmEventDiscriminator[:]) &&
|
||||||
|
bytes.Equal(innerInstr.Data[8:16], pumpAmmBuyEventDiscriminator[:]) {
|
||||||
|
err = agbinary.NewBorshDecoder(innerInstr.Data[16:]).Decode(&event)
|
||||||
|
if offset[1] == 0 {
|
||||||
|
offset[0] += 1
|
||||||
|
} else {
|
||||||
|
offset[1] += uint(innerIndex) + 1 + prefixLen
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, offset, fmt.Errorf("pump amm buy pool event decode error: %v, offset: %d, %d", err, offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if event == (ammBuyEvent{}) {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pump amm buy event not found, offset: %d, %d", offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
baseMint := result.accountList[instruction.Accounts[3]]
|
||||||
|
quoteMint := result.accountList[instruction.Accounts[4]]
|
||||||
|
baseTokenProgram := result.accountList[instruction.Accounts[11]]
|
||||||
|
quoteTokenProgram := result.accountList[instruction.Accounts[12]]
|
||||||
|
|
||||||
|
poolBaseAccountIdx := instruction.Accounts[7]
|
||||||
|
poolQuoteAccountIdx := instruction.Accounts[8]
|
||||||
|
var (
|
||||||
|
baseMintDecimals uint8
|
||||||
|
quoteMintDecimals uint8
|
||||||
|
)
|
||||||
|
for _, meta := range result.Meta.PostTokenBalances {
|
||||||
|
if meta.AccountIndex == poolBaseAccountIdx {
|
||||||
|
baseMintDecimals = uint8(meta.UITokenAmount.Decimals)
|
||||||
|
} else if meta.AccountIndex == poolQuoteAccountIdx {
|
||||||
|
quoteMintDecimals = uint8(meta.UITokenAmount.Decimals)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []Swap{
|
||||||
|
{
|
||||||
|
Program: SolProgramPumpAMM,
|
||||||
|
Event: "buy",
|
||||||
|
Pool: event.Pool,
|
||||||
|
BaseMint: baseMint,
|
||||||
|
QuoteMint: quoteMint,
|
||||||
|
BaseTokenProgram: baseTokenProgram,
|
||||||
|
QuoteTokenProgram: quoteTokenProgram,
|
||||||
|
Creator: event.CoinCreator,
|
||||||
|
BaseMintDecimals: baseMintDecimals,
|
||||||
|
QuoteMintDecimals: quoteMintDecimals,
|
||||||
|
User: event.User,
|
||||||
|
BaseAmount: decimal.NewFromUint64(event.BaseAmountOut),
|
||||||
|
QuoteAmount: decimal.NewFromUint64(event.UserQuoteAmountIn),
|
||||||
|
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserve - event.BaseAmountOut),
|
||||||
|
QuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserve + event.QuoteAmountIn),
|
||||||
|
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
|
||||||
|
UserBaseBalance: decimal.NewFromUint64(event.UserBaseTokenReserve + event.BaseAmountOut),
|
||||||
|
UserQuoteBalance: decimal.NewFromUint64(event.UserQuoteTokenReserve - event.UserQuoteAmountIn),
|
||||||
|
EntryContract: entryContract,
|
||||||
|
},
|
||||||
|
}, offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ammSellParser(result *RawTx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||||
|
var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
||||||
|
var err error
|
||||||
|
var prefixLen = offset[1]
|
||||||
|
inners, err := getInnerInstructions(innerInstructions, prefixLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pumpamm sell get inner instructions error: %v, offset: %d, %d", err, offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
var event ammSellEvent
|
||||||
|
for innerIndex, innerInstr := range inners {
|
||||||
|
if innerInstr.ProgramIDIndex == instruction.ProgramIDIndex &&
|
||||||
|
bytes.Equal(innerInstr.Data[:8], pumpAmmEventDiscriminator[:]) &&
|
||||||
|
bytes.Equal(innerInstr.Data[8:16], pumpAmmSellEventDiscriminator[:]) {
|
||||||
|
err = agbinary.NewBorshDecoder(innerInstr.Data[16:]).Decode(&event)
|
||||||
|
if offset[1] == 0 {
|
||||||
|
offset[0] += 1
|
||||||
|
} else {
|
||||||
|
offset[1] += uint(innerIndex) + 1 + prefixLen
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, offset, fmt.Errorf("pump amm sell pool event decode error: %v, offset: %d, %d", err, offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if event == (ammSellEvent{}) {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pump amm sell event not found, offset: %d, %d", offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
baseMint := result.accountList[instruction.Accounts[3]]
|
||||||
|
quoteMint := result.accountList[instruction.Accounts[4]]
|
||||||
|
baseTokenProgram := result.accountList[instruction.Accounts[11]]
|
||||||
|
quoteTokenProgram := result.accountList[instruction.Accounts[12]]
|
||||||
|
|
||||||
|
poolBaseAccountIdx := instruction.Accounts[7]
|
||||||
|
poolQuoteAccountIdx := instruction.Accounts[8]
|
||||||
|
var (
|
||||||
|
baseMintDecimals uint8
|
||||||
|
quoteMintDecimals uint8
|
||||||
|
)
|
||||||
|
for _, meta := range result.Meta.PostTokenBalances {
|
||||||
|
if meta.AccountIndex == poolBaseAccountIdx {
|
||||||
|
baseMintDecimals = uint8(meta.UITokenAmount.Decimals)
|
||||||
|
} else if meta.AccountIndex == poolQuoteAccountIdx {
|
||||||
|
quoteMintDecimals = uint8(meta.UITokenAmount.Decimals)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []Swap{
|
||||||
|
{
|
||||||
|
Program: SolProgramPumpAMM,
|
||||||
|
Event: "sell",
|
||||||
|
Pool: event.Pool,
|
||||||
|
BaseMint: baseMint,
|
||||||
|
QuoteMint: quoteMint,
|
||||||
|
BaseTokenProgram: baseTokenProgram,
|
||||||
|
QuoteTokenProgram: quoteTokenProgram,
|
||||||
|
Creator: event.CoinCreator,
|
||||||
|
BaseMintDecimals: baseMintDecimals,
|
||||||
|
QuoteMintDecimals: quoteMintDecimals,
|
||||||
|
User: event.User,
|
||||||
|
BaseAmount: decimal.NewFromUint64(event.BaseAmountIn),
|
||||||
|
QuoteAmount: decimal.NewFromUint64(event.UserQuoteAmountOut),
|
||||||
|
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserves + event.BaseAmountIn),
|
||||||
|
QuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserves - event.QuoteAmountOut),
|
||||||
|
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
|
||||||
|
UserBaseBalance: decimal.NewFromUint64(event.UserBaseTokenReserves - event.BaseAmountIn),
|
||||||
|
UserQuoteBalance: decimal.NewFromUint64(event.UserQuoteTokenReserves + event.UserQuoteAmountOut),
|
||||||
|
EntryContract: entryContract,
|
||||||
|
},
|
||||||
|
}, offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func depositParse(result *RawTx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||||
|
var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
||||||
|
var err error
|
||||||
|
var prefixLen = offset[1]
|
||||||
|
inners, err := getInnerInstructions(innerInstructions, prefixLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pumpamm deposit get inner instructions error: %v, offset: %d, %d", err, offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
var event ammDepositEvent
|
||||||
|
|
||||||
|
for innerIndex, innerInstr := range inners {
|
||||||
|
if innerInstr.ProgramIDIndex == instruction.ProgramIDIndex &&
|
||||||
|
bytes.Equal(innerInstr.Data[:8], pumpAmmEventDiscriminator[:]) &&
|
||||||
|
bytes.Equal(innerInstr.Data[8:16], pumpAmmDepositEventDiscriminator[:]) {
|
||||||
|
err = agbinary.NewBorshDecoder(innerInstr.Data[16:]).Decode(&event)
|
||||||
|
if offset[1] == 0 {
|
||||||
|
offset[0] += 1
|
||||||
|
} else {
|
||||||
|
offset[1] += uint(innerIndex) + 1 + prefixLen
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, offset, fmt.Errorf("pump amm deposit pool event decode error: %v, offset: %d, %d", err, offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if event == (ammDepositEvent{}) {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pump amm deposit event not found, offset: %d, %d", offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
poolBaseAccountIdx = instruction.Accounts[9]
|
||||||
|
poolQuoteAccountIdx = instruction.Accounts[10]
|
||||||
|
baseMintDecimals uint8
|
||||||
|
quoteMintDecimals uint8
|
||||||
|
baseMintProgram solana.PublicKey
|
||||||
|
quoteMintProgram solana.PublicKey
|
||||||
|
)
|
||||||
|
for _, meta := range result.Meta.PostTokenBalances {
|
||||||
|
if meta.AccountIndex == poolBaseAccountIdx {
|
||||||
|
baseMintDecimals = uint8(meta.UITokenAmount.Decimals)
|
||||||
|
baseMintProgram = meta.ProgramIDAccount
|
||||||
|
if baseMintProgram.IsZero() && meta.ProgramID != "" {
|
||||||
|
baseMintProgram = solana.MustPublicKeyFromBase58(meta.ProgramID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if meta.AccountIndex == poolQuoteAccountIdx {
|
||||||
|
quoteMintDecimals = uint8(meta.UITokenAmount.Decimals)
|
||||||
|
quoteMintProgram = meta.ProgramIDAccount
|
||||||
|
if quoteMintProgram.IsZero() && meta.ProgramID != "" {
|
||||||
|
quoteMintProgram = solana.MustPublicKeyFromBase58(meta.ProgramID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return []Swap{
|
||||||
|
{
|
||||||
|
Program: SolProgramPumpAMM,
|
||||||
|
Event: "deposit",
|
||||||
|
Pool: event.Pool,
|
||||||
|
BaseMint: result.accountList[instruction.Accounts[3]],
|
||||||
|
QuoteMint: result.accountList[instruction.Accounts[4]],
|
||||||
|
BaseTokenProgram: baseMintProgram,
|
||||||
|
QuoteTokenProgram: quoteMintProgram,
|
||||||
|
//Creator: solana.PublicKey{},
|
||||||
|
BaseMintDecimals: baseMintDecimals,
|
||||||
|
QuoteMintDecimals: quoteMintDecimals,
|
||||||
|
User: event.User,
|
||||||
|
BaseAmount: decimal.NewFromUint64(event.BaseAmountIn),
|
||||||
|
QuoteAmount: decimal.NewFromUint64(event.QuoteAmountIn),
|
||||||
|
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserves + event.BaseAmountIn),
|
||||||
|
QuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserves + event.QuoteAmountIn),
|
||||||
|
//Mayhem: false,
|
||||||
|
UserBaseBalance: decimal.NewFromUint64(event.UserBaseTokenReserves - event.BaseAmountIn),
|
||||||
|
UserQuoteBalance: decimal.NewFromUint64(event.UserQuoteTokenReserves - event.QuoteAmountIn),
|
||||||
|
EntryContract: entryContract,
|
||||||
|
},
|
||||||
|
}, offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func withdrawParse(result *RawTx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||||
|
var entryContract = result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
||||||
|
var err error
|
||||||
|
var prefixLen = offset[1]
|
||||||
|
inners, err := getInnerInstructions(innerInstructions, prefixLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pumpamm withdraw get inner instructions error: %v, offset: %d, %d", err, offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
var event ammWithdrawEvent
|
||||||
|
|
||||||
|
for innerIndex, innerInstr := range inners {
|
||||||
|
if innerInstr.ProgramIDIndex == instruction.ProgramIDIndex &&
|
||||||
|
bytes.Equal(innerInstr.Data[:8], pumpAmmEventDiscriminator[:]) &&
|
||||||
|
bytes.Equal(innerInstr.Data[8:16], pumpAmmWithdrawEventDiscriminator[:]) {
|
||||||
|
err = agbinary.NewBorshDecoder(innerInstr.Data[16:]).Decode(&event)
|
||||||
|
if offset[1] == 0 {
|
||||||
|
offset[0] += 1
|
||||||
|
} else {
|
||||||
|
offset[1] += uint(innerIndex) + 1 + prefixLen
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, offset, fmt.Errorf("pump amm withdraw pool event decode error: %v, offset: %d, %d", err, offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if event == (ammWithdrawEvent{}) {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("pump amm withdraw event not found, offset: %d, %d", offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
poolBaseAccountIdx = instruction.Accounts[9]
|
||||||
|
poolQuoteAccountIdx = instruction.Accounts[10]
|
||||||
|
baseMintDecimals uint8
|
||||||
|
quoteMintDecimals uint8
|
||||||
|
baseMintProgram solana.PublicKey
|
||||||
|
quoteMintProgram solana.PublicKey
|
||||||
|
)
|
||||||
|
for _, meta := range result.Meta.PostTokenBalances {
|
||||||
|
if meta.AccountIndex == poolBaseAccountIdx {
|
||||||
|
baseMintDecimals = uint8(meta.UITokenAmount.Decimals)
|
||||||
|
baseMintProgram = meta.ProgramIDAccount
|
||||||
|
if baseMintProgram.IsZero() && meta.ProgramID != "" {
|
||||||
|
baseMintProgram = solana.MustPublicKeyFromBase58(meta.ProgramID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if meta.AccountIndex == poolQuoteAccountIdx {
|
||||||
|
quoteMintDecimals = uint8(meta.UITokenAmount.Decimals)
|
||||||
|
quoteMintProgram = meta.ProgramIDAccount
|
||||||
|
if quoteMintProgram.IsZero() && meta.ProgramID != "" {
|
||||||
|
quoteMintProgram = solana.MustPublicKeyFromBase58(meta.ProgramID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return []Swap{
|
||||||
|
{
|
||||||
|
Program: SolProgramPumpAMM,
|
||||||
|
Event: "withdraw",
|
||||||
|
Pool: event.Pool,
|
||||||
|
BaseMint: result.accountList[instruction.Accounts[3]],
|
||||||
|
QuoteMint: result.accountList[instruction.Accounts[4]],
|
||||||
|
BaseTokenProgram: baseMintProgram,
|
||||||
|
QuoteTokenProgram: quoteMintProgram,
|
||||||
|
//Creator: solana.PublicKey{},
|
||||||
|
BaseMintDecimals: baseMintDecimals,
|
||||||
|
QuoteMintDecimals: quoteMintDecimals,
|
||||||
|
User: event.User,
|
||||||
|
BaseAmount: decimal.NewFromUint64(event.BaseAmountOut),
|
||||||
|
QuoteAmount: decimal.NewFromUint64(event.QuoteAmountOut),
|
||||||
|
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserves - event.BaseAmountOut),
|
||||||
|
QuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserves - event.QuoteAmountOut),
|
||||||
|
//Mayhem: false,
|
||||||
|
UserBaseBalance: decimal.NewFromUint64(event.UserBaseTokenReserves + event.BaseAmountOut),
|
||||||
|
UserQuoteBalance: decimal.NewFromUint64(event.UserQuoteTokenReserves + event.QuoteAmountOut),
|
||||||
|
EntryContract: entryContract,
|
||||||
|
},
|
||||||
|
}, offset, nil
|
||||||
|
}
|
||||||
41
pumpamm_test.go
Normal file
41
pumpamm_test.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package pump_parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
agbinary "github.com/gagliardetto/binary"
|
||||||
|
"github.com/gagliardetto/solana-go"
|
||||||
|
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Pool struct {
|
||||||
|
Bump uint8 // 1
|
||||||
|
Index uint16 // 3
|
||||||
|
Creator solana.PublicKey // 35
|
||||||
|
BaseMint solana.PublicKey // 67
|
||||||
|
QuoteMint solana.PublicKey // 99
|
||||||
|
LpMint solana.PublicKey // 131
|
||||||
|
PoolBaseTokenAccount solana.PublicKey // 163
|
||||||
|
PoolQuoteTokenAccount solana.PublicKey // 195
|
||||||
|
LpSupply uint64 // 203
|
||||||
|
CoinCreator solana.PublicKey // 235
|
||||||
|
IsMayhemMode bool // 236
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodePoolData(t *testing.T) {
|
||||||
|
var base64Data = "8ZptBBGxbbz9AAB/+s6++xNrbunrNkEpE9IY0qfortUqEOB5s3kKGSvL+9H0YLudD5fPy6QWG5FDfdl3FQRE3H804Ivg5J2OnSHvBpuIV/6rgYT7aH9jRhjANdrEOdwa6ztVmKDwAAAAAAGcWzgf1bP0MRLCJXxkgXyazv6CfdxoJGAh01hfNVqrkGoU6aOndSrbraRyP929gjNuQ4qg/nj2RjtLlBazO47we2q7UVSLFd69kGDUes9pm4n4dftp/lIZS068RLXhIR1niGtZ0AMAAFC4Q0TJ2zOhAXgWLRoYc4eSeu9nlTcupLKiXLyIDytOAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
|
||||||
|
data, _ := base64.StdEncoding.DecodeString(base64Data)
|
||||||
|
var pool Pool
|
||||||
|
fmt.Println("data length:", len(data))
|
||||||
|
err := agbinary.NewBorshDecoder(data[8:]).Decode(&pool)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to decode pool data: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Println(pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAmmBuyEvent(t *testing.T) {
|
||||||
|
fmt.Println(pumpAmmBuyEventDiscriminator)
|
||||||
|
}
|
||||||
443
rawtx.go
Normal file
443
rawtx.go
Normal file
@@ -0,0 +1,443 @@
|
|||||||
|
package pump_parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
bin "github.com/gagliardetto/binary"
|
||||||
|
"github.com/gagliardetto/solana-go"
|
||||||
|
"github.com/gagliardetto/solana-go/rpc"
|
||||||
|
"github.com/jackc/pgtype"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (tx *RawTx) getAccountList() []solana.PublicKey {
|
||||||
|
if tx.accountList != nil {
|
||||||
|
return tx.accountList
|
||||||
|
}
|
||||||
|
length := len(tx.Transaction.Message.AccountKeys) +
|
||||||
|
len(tx.Meta.LoadedAddresses.Writable) +
|
||||||
|
len(tx.Meta.LoadedAddresses.Readonly)
|
||||||
|
tx.accountList = make([]solana.PublicKey, length)
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
for _, v := range tx.Transaction.Message.AccountKeys {
|
||||||
|
tx.accountList[i] = v
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
for _, v := range tx.Meta.LoadedAddresses.Writable {
|
||||||
|
tx.accountList[i] = v
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
for _, v := range tx.Meta.LoadedAddresses.Readonly {
|
||||||
|
tx.accountList[i] = v
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return tx.accountList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *RawTx) GetSigner() solana.PublicKey {
|
||||||
|
accountList := tx.getAccountList()
|
||||||
|
if len(accountList) > 0 {
|
||||||
|
return accountList[0]
|
||||||
|
}
|
||||||
|
return solana.PublicKey{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RPCResponse struct {
|
||||||
|
JsonRPC string `json:"jsonrpc"`
|
||||||
|
Result RawTx `json:"result"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RawTx struct {
|
||||||
|
accountList []solana.PublicKey
|
||||||
|
|
||||||
|
BlockTime int64 `json:"blockTime"`
|
||||||
|
IndexWithinBlock int64 `json:"indexWithinBlock"`
|
||||||
|
Meta Meta `json:"meta"`
|
||||||
|
Slot uint64 `json:"slot"`
|
||||||
|
Transaction Transaction `json:"transaction"`
|
||||||
|
Version interface{} `json:"version"`
|
||||||
|
//Platform string `json:"platform,omitempty"`
|
||||||
|
//PlatformFee decimal.Decimal `json:"-"`
|
||||||
|
//CUPrice decimal.Decimal `json:"CUPrice,omitempty"`
|
||||||
|
//MevAgent string `json:"mevAgent,omitempty"`
|
||||||
|
//MevAgentFee decimal.Decimal `json:"mevAgentFee,omitempty"`
|
||||||
|
//EntryContract []string `json:"entryContract,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *RawTx) TxHash() string {
|
||||||
|
if len(tx.Transaction.Signatures) > 0 {
|
||||||
|
return tx.Transaction.Signatures[0].String()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *RawTx) GetSignerAfterBalance() decimal.Decimal {
|
||||||
|
if len(tx.Meta.PostBalances) > 0 {
|
||||||
|
return decimal.New(int64(tx.Meta.PostBalances[0]), -9)
|
||||||
|
}
|
||||||
|
return decimal.Zero
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *RawTx) GetSignerBeforeBalance() decimal.Decimal {
|
||||||
|
if len(tx.Meta.PreBalances) > 0 {
|
||||||
|
return decimal.New(int64(tx.Meta.PreBalances[0]), -9)
|
||||||
|
}
|
||||||
|
return decimal.Zero
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *RawTx) GetBlockTime() *pgtype.Timestamptz {
|
||||||
|
t := pgtype.Timestamptz{}
|
||||||
|
t.Set(time.Unix(tx.BlockTime, 0))
|
||||||
|
return &t
|
||||||
|
}
|
||||||
|
|
||||||
|
type Instruction struct {
|
||||||
|
Accounts []int `json:"accounts"`
|
||||||
|
Data solana.Base58 `json:"data"`
|
||||||
|
ProgramIDIndex int `json:"programIdIndex"`
|
||||||
|
StackHeight *int `json:"stackHeight"`
|
||||||
|
}
|
||||||
|
type InnerInstructions struct {
|
||||||
|
Index int `json:"index"`
|
||||||
|
Instructions []Instruction `json:"instructions"`
|
||||||
|
}
|
||||||
|
type LoadedAddresses struct {
|
||||||
|
Readonly solana.PublicKeySlice `json:"readonly"`
|
||||||
|
Writable solana.PublicKeySlice `json:"writable"`
|
||||||
|
}
|
||||||
|
type UITokenAmount struct {
|
||||||
|
Amount string `json:"amount"`
|
||||||
|
Decimals uint64 `json:"decimals"`
|
||||||
|
UIAmount float64 `json:"uiAmount"`
|
||||||
|
UIAmountString string `json:"uiAmountString"`
|
||||||
|
}
|
||||||
|
type TokenBalance struct {
|
||||||
|
AccountIndex int `json:"accountIndex"`
|
||||||
|
|
||||||
|
MintAccount solana.PublicKey `json:"mint_account"`
|
||||||
|
OwnerAccount *solana.PublicKey `json:"owner_account"`
|
||||||
|
ProgramIDAccount solana.PublicKey `json:"programId_account"`
|
||||||
|
|
||||||
|
Mint string `json:"mint"`
|
||||||
|
Owner string `json:"owner"`
|
||||||
|
ProgramID string `json:"programId"`
|
||||||
|
UITokenAmount UITokenAmount `json:"uiTokenAmount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tb *TokenBalance) ParseAccount() {
|
||||||
|
if tb.Mint == "" {
|
||||||
|
tb.Mint = tb.MintAccount.String()
|
||||||
|
}
|
||||||
|
if tb.Owner == "" && tb.OwnerAccount != nil {
|
||||||
|
tb.Owner = tb.OwnerAccount.String()
|
||||||
|
}
|
||||||
|
if tb.ProgramID == "" {
|
||||||
|
tb.ProgramID = tb.ProgramIDAccount.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Meta struct {
|
||||||
|
Err interface{} `json:"err"`
|
||||||
|
Fee uint64 `json:"fee"`
|
||||||
|
InnerInstructions []InnerInstructions `json:"innerInstructions"`
|
||||||
|
LoadedAddresses LoadedAddresses `json:"loadedAddresses"`
|
||||||
|
LogMessages []string `json:"logMessages"`
|
||||||
|
PostBalances []uint64 `json:"postBalances"`
|
||||||
|
PostTokenBalances []TokenBalance `json:"postTokenBalances"`
|
||||||
|
PreBalances []uint64 `json:"preBalances"`
|
||||||
|
PreTokenBalances []TokenBalance `json:"preTokenBalances"`
|
||||||
|
Rewards []interface{} `json:"rewards"`
|
||||||
|
}
|
||||||
|
type Header struct {
|
||||||
|
NumReadonlySignedAccounts int `json:"numReadonlySignedAccounts"`
|
||||||
|
NumReadonlyUnsignedAccounts int `json:"numReadonlyUnsignedAccounts"`
|
||||||
|
NumRequiredSignatures int `json:"numRequiredSignatures"`
|
||||||
|
}
|
||||||
|
type Message struct {
|
||||||
|
AccountKeys solana.PublicKeySlice `json:"accountKeys"`
|
||||||
|
AddressTableLookups solana.MessageAddressTableLookupSlice `json:"addressTableLookups"`
|
||||||
|
Header Header `json:"header"`
|
||||||
|
Instructions []Instruction `json:"instructions"`
|
||||||
|
RecentBlockHash string `json:"recentBlockhash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Transaction struct {
|
||||||
|
Message Message `json:"message"`
|
||||||
|
Signatures []solana.Signature `json:"signatures"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *Transaction) UnmarshalJSON(data []byte) error {
|
||||||
|
if len(data) == 0 || (len(data) == 4 && string(data) == "null") {
|
||||||
|
// TODO: is this an error?
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
firstChar := data[0]
|
||||||
|
|
||||||
|
switch firstChar {
|
||||||
|
// Check if first character is `[`, standing for a JSON array.
|
||||||
|
case '[':
|
||||||
|
// It's base64 (or similar)
|
||||||
|
{
|
||||||
|
var asDecodedBinary solana.Data
|
||||||
|
err := asDecodedBinary.UnmarshalJSON(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
asParsedTransaction := new(solana.Transaction)
|
||||||
|
err = asParsedTransaction.UnmarshalWithDecoder(bin.NewBinDecoder(asDecodedBinary.Content))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tx.Message = Message{
|
||||||
|
AccountKeys: asParsedTransaction.Message.AccountKeys,
|
||||||
|
AddressTableLookups: asParsedTransaction.Message.AddressTableLookups,
|
||||||
|
Header: Header{
|
||||||
|
NumReadonlySignedAccounts: int(asParsedTransaction.Message.Header.NumReadonlySignedAccounts),
|
||||||
|
NumReadonlyUnsignedAccounts: int(asParsedTransaction.Message.Header.NumReadonlyUnsignedAccounts),
|
||||||
|
NumRequiredSignatures: int(asParsedTransaction.Message.Header.NumRequiredSignatures),
|
||||||
|
},
|
||||||
|
Instructions: InstructionsFromRpc(asParsedTransaction.Message.Instructions),
|
||||||
|
RecentBlockHash: asParsedTransaction.Message.RecentBlockhash.String(),
|
||||||
|
}
|
||||||
|
tx.Signatures = asParsedTransaction.Signatures
|
||||||
|
}
|
||||||
|
case '{':
|
||||||
|
// It's JSON, most likely.
|
||||||
|
{
|
||||||
|
var asParsedTransaction solana.Transaction
|
||||||
|
err := json.Unmarshal(data, &asParsedTransaction)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tx.Message = Message{
|
||||||
|
AccountKeys: asParsedTransaction.Message.AccountKeys,
|
||||||
|
AddressTableLookups: asParsedTransaction.Message.AddressTableLookups,
|
||||||
|
Header: Header{
|
||||||
|
NumReadonlySignedAccounts: int(asParsedTransaction.Message.Header.NumReadonlySignedAccounts),
|
||||||
|
NumReadonlyUnsignedAccounts: int(asParsedTransaction.Message.Header.NumReadonlyUnsignedAccounts),
|
||||||
|
NumRequiredSignatures: int(asParsedTransaction.Message.Header.NumRequiredSignatures),
|
||||||
|
},
|
||||||
|
Instructions: InstructionsFromRpc(asParsedTransaction.Message.Instructions),
|
||||||
|
RecentBlockHash: asParsedTransaction.Message.RecentBlockhash.String(),
|
||||||
|
}
|
||||||
|
tx.Signatures = asParsedTransaction.Signatures
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Unknown kind: %v", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParsedTx struct {
|
||||||
|
AccountData []struct {
|
||||||
|
Account string `json:"account"`
|
||||||
|
NativeBalanceChange float64 `json:"nativeBalanceChange"`
|
||||||
|
TokenBalanceChanges []interface{} `json:"tokenBalanceChanges"`
|
||||||
|
} `json:"accountData"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Fee int `json:"fee"`
|
||||||
|
FeePayer string `json:"feePayer"`
|
||||||
|
Instructions []struct {
|
||||||
|
Accounts []string `json:"accounts"`
|
||||||
|
Data string `json:"data"`
|
||||||
|
InnerInstructions []struct {
|
||||||
|
Accounts []string `json:"accounts"`
|
||||||
|
Data string `json:"data"`
|
||||||
|
ProgramID string `json:"programId"`
|
||||||
|
} `json:"innerInstructions"`
|
||||||
|
ProgramID string `json:"programId"`
|
||||||
|
} `json:"instructions"`
|
||||||
|
NativeTransfers []struct {
|
||||||
|
Amount float64 `json:"amount"`
|
||||||
|
FromUserAccount string `json:"fromUserAccount"`
|
||||||
|
ToUserAccount string `json:"toUserAccount"`
|
||||||
|
} `json:"nativeTransfers"`
|
||||||
|
Signature string `json:"signature"`
|
||||||
|
Slot int `json:"slot"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
Timestamp int `json:"timestamp"`
|
||||||
|
TokenTransfers []struct {
|
||||||
|
FromTokenAccount string `json:"fromTokenAccount"`
|
||||||
|
FromUserAccount string `json:"fromUserAccount"`
|
||||||
|
Mint string `json:"mint"`
|
||||||
|
ToTokenAccount string `json:"toTokenAccount"`
|
||||||
|
ToUserAccount string `json:"toUserAccount"`
|
||||||
|
TokenAmount float64 `json:"tokenAmount"`
|
||||||
|
TokenStandard string `json:"tokenStandard"`
|
||||||
|
} `json:"tokenTransfers"`
|
||||||
|
TransactionError interface{} `json:"transactionError"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func InstructionsFromRpc(instructions []solana.CompiledInstruction) []Instruction {
|
||||||
|
var instrs []Instruction = make([]Instruction, len(instructions))
|
||||||
|
for i, instruction := range instructions {
|
||||||
|
instrs[i] = Instruction{
|
||||||
|
Accounts: intSliceFromUint16Slice(instruction.Accounts),
|
||||||
|
Data: instruction.Data,
|
||||||
|
ProgramIDIndex: int(instruction.ProgramIDIndex),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func InnerInstructionsFromRpc(instructions []rpc.InnerInstruction) []InnerInstructions {
|
||||||
|
var innerInstructions []InnerInstructions = make([]InnerInstructions, len(instructions))
|
||||||
|
for i, instruction := range instructions {
|
||||||
|
//instruction.Instructions
|
||||||
|
instrs := make([]Instruction, len(instruction.Instructions))
|
||||||
|
for j, instr := range instruction.Instructions {
|
||||||
|
instrs[j] = Instruction{
|
||||||
|
Accounts: intSliceFromUint16Slice(instr.Accounts),
|
||||||
|
Data: instr.Data,
|
||||||
|
ProgramIDIndex: int(instr.ProgramIDIndex),
|
||||||
|
//StackHeight: instr.StackHeight,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
innerInstructions[i] = InnerInstructions{
|
||||||
|
Index: int(instruction.Index),
|
||||||
|
Instructions: instrs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return innerInstructions
|
||||||
|
}
|
||||||
|
|
||||||
|
func intSliceFromUint16Slice(in []uint16) []int {
|
||||||
|
out := make([]int, len(in))
|
||||||
|
for i, v := range in {
|
||||||
|
out[i] = int(v)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTokenBalanceAfterTx(result *RawTx, accountIndex int) (*TokenBalance, error) {
|
||||||
|
var preBalance *TokenBalance
|
||||||
|
for _, pre := range result.Meta.PreTokenBalances {
|
||||||
|
if pre.AccountIndex == accountIndex {
|
||||||
|
preBalance = &pre
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var postBalance *TokenBalance
|
||||||
|
|
||||||
|
for _, post := range result.Meta.PostTokenBalances {
|
||||||
|
if post.AccountIndex == accountIndex {
|
||||||
|
post.ParseAccount()
|
||||||
|
postBalance = &post
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if preBalance == nil && postBalance == nil {
|
||||||
|
return nil, fmt.Errorf("account not found")
|
||||||
|
}
|
||||||
|
if postBalance == nil {
|
||||||
|
preBalance.ParseAccount()
|
||||||
|
return &TokenBalance{
|
||||||
|
AccountIndex: preBalance.AccountIndex,
|
||||||
|
MintAccount: preBalance.MintAccount,
|
||||||
|
OwnerAccount: preBalance.OwnerAccount,
|
||||||
|
ProgramIDAccount: preBalance.ProgramIDAccount,
|
||||||
|
Mint: preBalance.Mint,
|
||||||
|
Owner: preBalance.Owner,
|
||||||
|
ProgramID: preBalance.ProgramID,
|
||||||
|
UITokenAmount: UITokenAmount{
|
||||||
|
Amount: "0",
|
||||||
|
Decimals: preBalance.UITokenAmount.Decimals,
|
||||||
|
UIAmount: 0,
|
||||||
|
UIAmountString: "0",
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return postBalance, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAccountBalanceAfterTx(result *RawTx, accountIndex int) decimal.Decimal {
|
||||||
|
x, err := getTokenBalanceAfterTx(result, accountIndex)
|
||||||
|
if err != nil {
|
||||||
|
return decimal.Zero
|
||||||
|
}
|
||||||
|
amount, err := decimal.NewFromString(x.UITokenAmount.Amount)
|
||||||
|
if err != nil {
|
||||||
|
return decimal.Zero
|
||||||
|
}
|
||||||
|
return amount
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTokenBalanceAfterTx(result *RawTx, accountIndex int, tokenProgram, mint solana.PublicKey) decimal.Decimal {
|
||||||
|
ataAccount, _, _ := solana.FindProgramAddress([][]byte{
|
||||||
|
result.accountList[accountIndex][:],
|
||||||
|
tokenProgram[:],
|
||||||
|
mint[:],
|
||||||
|
},
|
||||||
|
solana.SPLAssociatedTokenAccountProgramID)
|
||||||
|
|
||||||
|
ataIndex := 0
|
||||||
|
for i, account := range result.accountList {
|
||||||
|
if account.Equals(ataAccount) {
|
||||||
|
ataIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ataIndex == 0 {
|
||||||
|
return decimal.Zero
|
||||||
|
}
|
||||||
|
x, err := getTokenBalanceAfterTx(result, ataIndex)
|
||||||
|
if err != nil {
|
||||||
|
return decimal.Zero
|
||||||
|
}
|
||||||
|
amount, err := decimal.NewFromString(x.UITokenAmount.Amount)
|
||||||
|
if err != nil {
|
||||||
|
return decimal.Zero
|
||||||
|
}
|
||||||
|
return amount
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSolAfterTx(result *RawTx, accountIndex int) (uint64, error) {
|
||||||
|
for i, post := range result.Meta.PostBalances {
|
||||||
|
if i == accountIndex {
|
||||||
|
return post, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("account not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func solSplAccount(owner, mint solana.PublicKey) (solana.PublicKey, error) {
|
||||||
|
ataAddress, _, err := solana.FindAssociatedTokenAddress(owner, mint)
|
||||||
|
if err != nil {
|
||||||
|
return solana.PublicKey{}, err
|
||||||
|
}
|
||||||
|
return ataAddress, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func solSpl2022Account(owner, mint solana.PublicKey) (solana.PublicKey, error) {
|
||||||
|
address, _, err := solana.FindProgramAddress([][]byte{
|
||||||
|
owner[:],
|
||||||
|
solana.Token2022ProgramID[:],
|
||||||
|
mint[:],
|
||||||
|
},
|
||||||
|
solana.SPLAssociatedTokenAccountProgramID,
|
||||||
|
)
|
||||||
|
return address, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func isAccountOwner(account, owner, mint solana.PublicKey) (bool, error) {
|
||||||
|
ata, err := solSplAccount(owner, mint)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if account == ata {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
ata, err = solSpl2022Account(owner, mint)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return account == ata, nil
|
||||||
|
}
|
||||||
83
system.go
Normal file
83
system.go
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
package pump_parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func systemParser(result *RawTx, instr Instruction, innerInstructions InnerInstructions, offset [2]uint, tx *Tx) ([2]uint, error) {
|
||||||
|
var instruction Instruction
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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])
|
||||||
|
}
|
||||||
|
|
||||||
|
decode := instruction.Data
|
||||||
|
discriminator := binary.LittleEndian.Uint32(decode[0:4])
|
||||||
|
|
||||||
|
switch discriminator {
|
||||||
|
case transferDiscriminator:
|
||||||
|
return TransferParser(result, instruction, offset, tx, decode[4:])
|
||||||
|
default:
|
||||||
|
return increaseOffset(offset), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TransferParser(result *RawTx, instruction Instruction, offset [2]uint, tx *Tx, decodeData []byte) ([2]uint, error) {
|
||||||
|
if len(decodeData) < 8 {
|
||||||
|
return increaseOffset(offset), nil
|
||||||
|
}
|
||||||
|
var lamports uint64 = binary.LittleEndian.Uint64(decodeData)
|
||||||
|
|
||||||
|
//from := result.accountList[result.Transaction.Message.Instructions[offset[0]].Accounts[0]]
|
||||||
|
to := result.accountList[instruction.Accounts[1]]
|
||||||
|
|
||||||
|
// load platform by to address
|
||||||
|
platform, ok := platformFeeAddresses[to]
|
||||||
|
if ok {
|
||||||
|
if tx.Platform != nil {
|
||||||
|
tx.Platform[platform] = platformInfo{
|
||||||
|
Platform: platform,
|
||||||
|
PlatformFee: decimal.NewFromInt(int64(lamports)).Div(decimal.NewFromInt(1e9)), // solana decimals
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tx.Platform = make(map[string]platformInfo)
|
||||||
|
tx.Platform[platform] = platformInfo{
|
||||||
|
Platform: platform,
|
||||||
|
PlatformFee: decimal.NewFromInt(int64(lamports)).Div(decimal.NewFromInt(1e9)), // solana decimals
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if offset[1] == 0 {
|
||||||
|
// load mev agent by to address
|
||||||
|
mevAgent, ok := mevAgentFeeAddresses[to]
|
||||||
|
if !ok {
|
||||||
|
// mark it as unknown
|
||||||
|
mevAgent = MevAgentUnknown
|
||||||
|
}
|
||||||
|
if tx.MevAgent == nil {
|
||||||
|
tx.MevAgent = make(map[string]mevInfo)
|
||||||
|
}
|
||||||
|
tx.MevAgent[mevAgent] = mevInfo{
|
||||||
|
MevAgent: mevAgent,
|
||||||
|
MevAgentFee: decimal.NewFromInt(int64(lamports)).Div(decimal.NewFromInt(1e9)), // solana decimals
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return increaseOffset(offset), nil
|
||||||
|
}
|
||||||
167
tx.go
Normal file
167
tx.go
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
package pump_parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gagliardetto/solana-go"
|
||||||
|
"github.com/mr-tron/base58"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Swap struct {
|
||||||
|
Program string
|
||||||
|
Event string
|
||||||
|
|
||||||
|
Pool solana.PublicKey
|
||||||
|
BaseMint solana.PublicKey
|
||||||
|
QuoteMint solana.PublicKey
|
||||||
|
|
||||||
|
BaseTokenProgram solana.PublicKey
|
||||||
|
QuoteTokenProgram solana.PublicKey
|
||||||
|
|
||||||
|
Creator solana.PublicKey
|
||||||
|
|
||||||
|
BaseMintDecimals uint8
|
||||||
|
QuoteMintDecimals uint8
|
||||||
|
|
||||||
|
User solana.PublicKey
|
||||||
|
BaseAmount decimal.Decimal
|
||||||
|
QuoteAmount decimal.Decimal
|
||||||
|
|
||||||
|
BaseReserve decimal.Decimal
|
||||||
|
QuoteReserve decimal.Decimal
|
||||||
|
Mayhem bool
|
||||||
|
|
||||||
|
UserBaseBalance decimal.Decimal
|
||||||
|
UserQuoteBalance decimal.Decimal
|
||||||
|
EntryContract solana.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
type platformInfo struct {
|
||||||
|
Platform string
|
||||||
|
PlatformFee decimal.Decimal
|
||||||
|
}
|
||||||
|
|
||||||
|
type mevInfo struct {
|
||||||
|
MevAgent string
|
||||||
|
MevAgentFee decimal.Decimal
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tx struct {
|
||||||
|
Signer solana.PublicKey
|
||||||
|
Err interface{} `json:"err,omitempty"`
|
||||||
|
Swaps []Swap `json:"swaps,omitempty"`
|
||||||
|
Block uint64 `json:"block"`
|
||||||
|
BlockIndex uint64 `json:"index"`
|
||||||
|
TxHash *[64]byte `json:"-"`
|
||||||
|
BlockAt int64 `json:"block_at"`
|
||||||
|
|
||||||
|
cachedTxHash string
|
||||||
|
|
||||||
|
Platform map[string]platformInfo `json:"platform"`
|
||||||
|
MevAgent map[string]mevInfo ` json:"tx_mev_agent"`
|
||||||
|
CUPrice decimal.Decimal ` json:"tx_cu_price"`
|
||||||
|
|
||||||
|
BeforeSolBalance decimal.Decimal `json:"-"`
|
||||||
|
AfterSOLBalance decimal.Decimal `json:"after_sol_balance"`
|
||||||
|
|
||||||
|
Token []TokenMeta `gorm:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TokenMeta struct {
|
||||||
|
Address solana.PublicKey `json:"address"`
|
||||||
|
TokenProgram solana.PublicKey `json:"token_program"`
|
||||||
|
|
||||||
|
Name string
|
||||||
|
Symbol string
|
||||||
|
Decimal uint8
|
||||||
|
Url string
|
||||||
|
|
||||||
|
TotalSupply *decimal.Decimal
|
||||||
|
SignerBalance *decimal.Decimal
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *Tx) GetTxHash() string {
|
||||||
|
if tx.cachedTxHash != "" {
|
||||||
|
return tx.cachedTxHash
|
||||||
|
}
|
||||||
|
if tx.TxHash == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
tx.cachedTxHash = base58.Encode(tx.TxHash[:])
|
||||||
|
return tx.cachedTxHash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *Tx) CheckPlatform(swap Swap, rawTx *RawTx) (string, decimal.Decimal) {
|
||||||
|
// hasSolProgramRaydiumLaunchLabBonk
|
||||||
|
var platform string
|
||||||
|
var platformFee decimal.Decimal
|
||||||
|
if len(tx.Platform) == 0 {
|
||||||
|
return PlatformNone, decimal.Zero
|
||||||
|
}
|
||||||
|
|
||||||
|
for p, info := range tx.Platform {
|
||||||
|
platform = p
|
||||||
|
platformFee = info.PlatformFee
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if swap.Event == "buy" && swap.Program == SolProgramRaydiumLaunchLabBonk {
|
||||||
|
for _, p := range tx.Platform {
|
||||||
|
switch p.Platform {
|
||||||
|
case PlatformAxiom:
|
||||||
|
if !checkBonkAxiomBuy(rawTx) {
|
||||||
|
platform = PlatformFake
|
||||||
|
}
|
||||||
|
case PlatformGMGN:
|
||||||
|
if !checkBonkGmgnBuy(rawTx) {
|
||||||
|
platform = PlatformFake
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if platform != "" &&
|
||||||
|
platform != PlatformFake {
|
||||||
|
if (swap.QuoteMint.Equals(wSolMint) || swap.QuoteMint.IsZero()) &&
|
||||||
|
platformFee.LessThan(swap.QuoteAmount.Div(decimal.New(1, int32(swap.QuoteMintDecimals))).Div(decimal.NewFromInt(10000)).Mul(decimal.NewFromInt(9))) {
|
||||||
|
fmt.Printf("\n amount: %s, platform: %s, fee: %s \n", swap.QuoteAmount.Div(decimal.New(1, int32(swap.QuoteMintDecimals))), platform, platformFee.String())
|
||||||
|
platform = PlatformFake
|
||||||
|
} else if swap.BaseMint.Equals(wSolMint) &&
|
||||||
|
platformFee.LessThan(swap.QuoteAmount.Div(decimal.New(1, int32(swap.QuoteMintDecimals))).Div(decimal.NewFromInt(10000)).Mul(decimal.NewFromInt(9))) {
|
||||||
|
platform = PlatformFake
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if platform == "" {
|
||||||
|
platform = PlatformNone
|
||||||
|
}
|
||||||
|
return platform, platformFee
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *Tx) CheckMevAgent() (string, decimal.Decimal) {
|
||||||
|
var mevAgent = MevAgentUnknown
|
||||||
|
var mevAgentFee = decimal.Zero
|
||||||
|
|
||||||
|
for m, info := range tx.MevAgent {
|
||||||
|
if len(tx.MevAgent) > 1 && info.MevAgent == MevAgentUnknown {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mevAgent = m
|
||||||
|
mevAgentFee = info.MevAgentFee
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if len(tx.MevAgent) == 0 && mevAgent == MevAgentUnknown {
|
||||||
|
// set the mev agent to none if the platform does not exist
|
||||||
|
return "", decimal.Zero
|
||||||
|
}
|
||||||
|
return mevAgent, mevAgentFee
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Swap) CheckEntryContract() string {
|
||||||
|
name, ok := entryContractAddresses[s.EntryContract]
|
||||||
|
if ok {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
return EntryContractUnknown
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user