Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3324a71117 | ||
|
|
7557414fff |
@@ -93,8 +93,8 @@ func main() {
|
|||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
case tx := <-txCh:
|
case tx := <-txCh:
|
||||||
if tx.Label == "photon" || tx.Label == "jupiterv6" || tx.Label == "okxdexroutev2" {
|
if tx.Label == "dbot" || tx.Label == "okxdexroutev2" {
|
||||||
fmt.Println("===============", tx.TxHash, tx.Label, tx.Event, tx.Token0Address, "token:", tx.Token0Amount, "parse time:", tx.ParseEnd.Sub(tx.ParseStart))
|
fmt.Println("===============", tx.TxHash, tx.Label,tx.Program ,tx.Event, tx.Token0Address, tx.Token1Address,"token0amount:", tx.Token0Amount, "token1amount:",tx.Token1Amount ,"parse time:", tx.ParseEnd.Sub(tx.ParseStart))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
rpcURL = "https://staked.helius-rpc.com?api-key=5adcf1f9-5719-43d1-bf3f-c2d4e1e5f94d"
|
rpcURL = "https://staked.helius-rpc.com?api-key=5adcf1f9-5719-43d1-bf3f-c2d4e1e5f94d"
|
||||||
txSignature = "3hFamox2W1oWMwbRkfF5r9YiPULsdRsnR2TQsFDVtFCXf6cJ8ijGNgHGFmEbxEbVEryLg21sbt4qoGLwrPfvJ2UC"
|
txSignature = "4tcpUCVUZtUFDKGwZVBexkQpYiPhFgW4wfnrTEQgPrkQxpfsDEVMWN2UBfYxpy1EsDzuatuP1wgsUowTqeh2fnie"
|
||||||
labelFilter = ""
|
labelFilter = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
134
pkg/shreder/program_dbot.go
Normal file
134
pkg/shreder/program_dbot.go
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
package shreder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gagliardetto/solana-go"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
var dbotProgramID = solana.MustPublicKeyFromBase58("DBotWvSso9oD1ZB3aHx2LiD2ZoFpF8PbKjaT4uHKLLVs")
|
||||||
|
|
||||||
|
var (
|
||||||
|
dbotPumpFunBuyIX = []byte{0x4e, 0x13, 0x6d, 0x72, 0x3d, 0x72, 0xbe, 0x9d}
|
||||||
|
dbotPumpAmmBuyIX = []byte{0x99, 0x76, 0xb6, 0x1e, 0xe4, 0x03, 0xdc, 0xf4}
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseDbotInstruction(tx VersionedTransaction, instructionIndex int) (TxSignalBatch, error) {
|
||||||
|
if instructionIndex >= len(tx.Instructions) {
|
||||||
|
return nil, fmt.Errorf("instruction index out of bounds")
|
||||||
|
}
|
||||||
|
ix := tx.Instructions[instructionIndex]
|
||||||
|
if len(ix.Data) < 8 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
isPumpFun := false
|
||||||
|
isPumpAmm := false
|
||||||
|
|
||||||
|
if len(ix.Accounts) > 11 {
|
||||||
|
key, err := tx.GetAccount(int(ix.Accounts[11]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if key.Equals(pumpProgramID) {
|
||||||
|
isPumpFun = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ix.Accounts) > 16 {
|
||||||
|
key, err := tx.GetAccount(int(ix.Accounts[16]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if key.Equals(pumpAmmProgramID) {
|
||||||
|
isPumpAmm = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disc := ix.Data[:8]
|
||||||
|
|
||||||
|
if isPumpFun {
|
||||||
|
if !bytes.Equal(disc, dbotPumpFunBuyIX) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if len(ix.Data) < 16 {
|
||||||
|
return nil, fmt.Errorf("data too short for dbot pumpfun buy args, len=%d", len(ix.Data))
|
||||||
|
}
|
||||||
|
if len(ix.Accounts) < 3 {
|
||||||
|
return nil, fmt.Errorf("accounts too short")
|
||||||
|
}
|
||||||
|
|
||||||
|
mint, err := tx.GetAccount(int(ix.Accounts[2]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
solAmount := binary.LittleEndian.Uint64(ix.Data[8:16])
|
||||||
|
|
||||||
|
return TxSignalBatch{&TxSignal{
|
||||||
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "dbot",
|
||||||
|
Maker: tx.StaticAccountKeys[0].String(),
|
||||||
|
Token0Address: mint.String(),
|
||||||
|
Token1Address: wsolMint,
|
||||||
|
Token0Amount: decimal.Zero,
|
||||||
|
Token1Amount: formatSolAmount(solAmount),
|
||||||
|
Program: "Pump",
|
||||||
|
Event: "buy",
|
||||||
|
ExactSOL: true,
|
||||||
|
IsToken2022: false,
|
||||||
|
IsMayhemMode: false,
|
||||||
|
Block: tx.Block,
|
||||||
|
Token0AmountUint64: 0,
|
||||||
|
Token1AmountUint64: solAmount,
|
||||||
|
}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if isPumpAmm {
|
||||||
|
if !bytes.Equal(disc, dbotPumpAmmBuyIX) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if len(ix.Data) < 16 {
|
||||||
|
return nil, fmt.Errorf("data too short for dbot pumpamm buy args, len=%d", len(ix.Data))
|
||||||
|
}
|
||||||
|
if len(ix.Accounts) < 5 {
|
||||||
|
return nil, fmt.Errorf("accounts too short")
|
||||||
|
}
|
||||||
|
|
||||||
|
base, err := tx.GetAccount(int(ix.Accounts[3]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
quote, err := tx.GetAccount(int(ix.Accounts[4]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !quote.Equals(solana.WrappedSol) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
solAmount := binary.LittleEndian.Uint64(ix.Data[8:16])
|
||||||
|
|
||||||
|
return TxSignalBatch{&TxSignal{
|
||||||
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "dbot",
|
||||||
|
Maker: tx.StaticAccountKeys[0].String(),
|
||||||
|
Token0Address: base.String(),
|
||||||
|
Token1Address: wsolMint,
|
||||||
|
Token0Amount: decimal.Zero,
|
||||||
|
Token1Amount: formatSolAmount(solAmount),
|
||||||
|
Program: "PumpAMM",
|
||||||
|
Event: "buy",
|
||||||
|
ExactSOL: true,
|
||||||
|
IsToken2022: false,
|
||||||
|
IsMayhemMode: false,
|
||||||
|
Block: tx.Block,
|
||||||
|
Token0AmountUint64: 0,
|
||||||
|
Token1AmountUint64: solAmount,
|
||||||
|
}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
@@ -291,77 +291,205 @@ func parseOkxDexRouteV2Instruction(tx VersionedTransaction, instructionIndex int
|
|||||||
return nil, fmt.Errorf("invalid account count: %d", len(ix.Accounts))
|
return nil, fmt.Errorf("invalid account count: %d", len(ix.Accounts))
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
inputAmount uint64
|
pumpAmmSellAmount uint64
|
||||||
routeCount int
|
pumpAmmBuyAmount uint64
|
||||||
|
pumpFunSellAmount uint64
|
||||||
|
pumpFunBuyAmount uint64
|
||||||
|
pumpAmmSellCount int
|
||||||
|
pumpAmmBuyCount int
|
||||||
|
pumpFunSellCount int
|
||||||
|
pumpFunBuyCount int
|
||||||
)
|
)
|
||||||
for _, route := range args.Routes {
|
for _, route := range args.Routes {
|
||||||
if route.Index == 1 && (route.Dex == OKCV2_PumpfunammSell ||
|
if route.Index != 1 {
|
||||||
route.Dex == OKCV2_PumpfunSell2) {
|
continue
|
||||||
routeCount++
|
}
|
||||||
inputAmount = args.AmountIn * uint64(route.Weight) / 10000
|
switch route.Dex {
|
||||||
|
case OKCV2_PumpfunammSell:
|
||||||
|
pumpAmmSellCount++
|
||||||
|
pumpAmmSellAmount = args.AmountIn * uint64(route.Weight) / 10000
|
||||||
|
case OKCV2_PumpfunammBuy:
|
||||||
|
pumpAmmBuyCount++
|
||||||
|
pumpAmmBuyAmount = args.AmountIn * uint64(route.Weight) / 10000
|
||||||
|
case OKCV2_PumpfunSell2:
|
||||||
|
pumpFunSellCount++
|
||||||
|
pumpFunSellAmount = args.AmountIn * uint64(route.Weight) / 10000
|
||||||
|
case OKCV2_PumpfunBuy2:
|
||||||
|
pumpFunBuyCount++
|
||||||
|
pumpFunBuyAmount = args.AmountIn * uint64(route.Weight) / 10000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if routeCount > 1 {
|
if pumpAmmSellCount > 1 {
|
||||||
logger.Warn("pumpSwapSell at inputIdx=0: multiple instances found", "tx", tx.Signatures[0].String(), "routeCount", routeCount)
|
logger.Warn("pumpAmmSwapSell at inputIdx=0: multiple instances found", "tx", tx.Signatures[0].String(), "routeCount", pumpAmmSellCount)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if inputAmount == 0 {
|
if pumpAmmBuyCount > 1 {
|
||||||
|
logger.Warn("pumpAmmSwapBuy at inputIdx=0: multiple instances found", "tx", tx.Signatures[0].String(), "routeCount", pumpAmmBuyCount)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if pumpFunSellCount > 1 {
|
||||||
|
logger.Warn("pumpFunSwapSell at inputIdx=0: multiple instances found", "tx", tx.Signatures[0].String(), "routeCount", pumpFunSellCount)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if pumpFunBuyCount > 1 {
|
||||||
|
logger.Warn("pumpFunSwapBuy at inputIdx=0: multiple instances found", "tx", tx.Signatures[0].String(), "routeCount", pumpFunBuyCount)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if pumpAmmSellAmount == 0 && pumpAmmBuyAmount == 0 && pumpFunSellAmount == 0 && pumpFunBuyAmount == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
srcMint, err := tx.GetAccount(int(ix.Accounts[3]))
|
out := make(TxSignalBatch, 0, 2)
|
||||||
|
|
||||||
var (
|
if pumpFunBuyAmount > 0 || pumpFunSellAmount > 0 {
|
||||||
srcIdx uint8
|
if pumpFunBuyAmount > 0 {
|
||||||
)
|
if len(ix.Accounts) < 5 {
|
||||||
if len(ix.Accounts) <= 15 {
|
return nil, fmt.Errorf("invalid account count: %d", len(ix.Accounts))
|
||||||
return nil, nil
|
}
|
||||||
|
baseMint, err := tx.GetAccount(int(ix.Accounts[4]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out = append(out, &TxSignal{
|
||||||
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Maker: tx.StaticAccountKeys[0].String(),
|
||||||
|
Token0Address: baseMint.String(),
|
||||||
|
Token1Address: wsolMint,
|
||||||
|
Token0Amount: decimal.Zero,
|
||||||
|
Token1Amount: formatSolAmount(pumpFunBuyAmount),
|
||||||
|
Event: "buy",
|
||||||
|
Program: "Pump",
|
||||||
|
IsProcessed: false,
|
||||||
|
IsToken2022: false,
|
||||||
|
IsMayhemMode: false,
|
||||||
|
ExactSOL: true,
|
||||||
|
Token0AmountUint64: 0,
|
||||||
|
Token1AmountUint64: pumpFunBuyAmount,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if pumpFunSellAmount > 0 {
|
||||||
|
if len(ix.Accounts) < 4 {
|
||||||
|
return nil, fmt.Errorf("invalid account count: %d", len(ix.Accounts))
|
||||||
|
}
|
||||||
|
baseMint, err := tx.GetAccount(int(ix.Accounts[3]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out = append(out, &TxSignal{
|
||||||
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Maker: tx.StaticAccountKeys[0].String(),
|
||||||
|
Token0Address: baseMint.String(),
|
||||||
|
Token1Address: wsolMint,
|
||||||
|
Token0Amount: formatTokenAmount(pumpFunSellAmount),
|
||||||
|
Token1Amount: decimal.Zero,
|
||||||
|
Event: "sell",
|
||||||
|
Program: "Pump",
|
||||||
|
IsProcessed: false,
|
||||||
|
IsToken2022: false,
|
||||||
|
IsMayhemMode: false,
|
||||||
|
ExactSOL: false,
|
||||||
|
Token0AmountUint64: pumpFunSellAmount,
|
||||||
|
Token1AmountUint64: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
accounts := ix.Accounts[14:]
|
|
||||||
for i, acctIdx := range accounts {
|
if pumpAmmBuyAmount > 0 || pumpAmmSellAmount > 0 {
|
||||||
key, err := tx.GetAccount(int(acctIdx))
|
if len(ix.Accounts) <= 15 {
|
||||||
|
if len(out) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
accounts := ix.Accounts[14:]
|
||||||
|
var pumpAmmIdx uint8
|
||||||
|
for i, acctIdx := range accounts {
|
||||||
|
key, err := tx.GetAccount(int(acctIdx))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if key.Equals(pumpAmmProgramID) {
|
||||||
|
pumpAmmIdx = uint8(i + 6)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pumpAmmIdx == 0 || int(pumpAmmIdx+1) >= len(accounts) {
|
||||||
|
if len(out) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
baseMint, err := tx.GetAccount(int(accounts[pumpAmmIdx]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if key.Equals(pumpAmmProgramID) {
|
quoteMint, err := tx.GetAccount(int(accounts[pumpAmmIdx+1]))
|
||||||
srcIdx = uint8(i + 6)
|
if err != nil {
|
||||||
break
|
return nil, err
|
||||||
|
}
|
||||||
|
if !quoteMint.Equals(solana.WrappedSol) {
|
||||||
|
if len(out) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
if pumpAmmBuyAmount > 0 {
|
||||||
|
if len(ix.Accounts) < 5 {
|
||||||
|
return nil, fmt.Errorf("invalid account count: %d", len(ix.Accounts))
|
||||||
|
}
|
||||||
|
srcMint, err := tx.GetAccount(int(ix.Accounts[4]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if baseMint.Equals(srcMint) {
|
||||||
|
out = append(out, &TxSignal{
|
||||||
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Maker: tx.StaticAccountKeys[0].String(),
|
||||||
|
Token0Address: baseMint.String(),
|
||||||
|
Token1Address: wsolMint,
|
||||||
|
Token0Amount: decimal.Zero,
|
||||||
|
Token1Amount: formatSolAmount(pumpAmmBuyAmount),
|
||||||
|
Event: "buy",
|
||||||
|
Program: "PumpAMM",
|
||||||
|
IsProcessed: false,
|
||||||
|
IsToken2022: false,
|
||||||
|
IsMayhemMode: false,
|
||||||
|
ExactSOL: true,
|
||||||
|
Token0AmountUint64: 0,
|
||||||
|
Token1AmountUint64: pumpAmmBuyAmount,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if pumpAmmSellAmount > 0 {
|
||||||
|
if len(ix.Accounts) < 4 {
|
||||||
|
return nil, fmt.Errorf("invalid account count: %d", len(ix.Accounts))
|
||||||
|
}
|
||||||
|
srcMint, err := tx.GetAccount(int(ix.Accounts[3]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if baseMint.Equals(srcMint) {
|
||||||
|
out = append(out, &TxSignal{
|
||||||
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Maker: tx.StaticAccountKeys[0].String(),
|
||||||
|
Token0Address: baseMint.String(),
|
||||||
|
Token1Address: wsolMint,
|
||||||
|
Token0Amount: formatTokenAmount(pumpAmmSellAmount),
|
||||||
|
Token1Amount: decimal.Zero,
|
||||||
|
Event: "sell",
|
||||||
|
Program: "PumpAMM",
|
||||||
|
IsProcessed: false,
|
||||||
|
IsToken2022: false,
|
||||||
|
IsMayhemMode: false,
|
||||||
|
ExactSOL: false,
|
||||||
|
Token0AmountUint64: pumpAmmSellAmount,
|
||||||
|
Token1AmountUint64: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if srcIdx == 0 || int(srcIdx+1) >= len(accounts) {
|
|
||||||
|
if len(out) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
return out, nil
|
||||||
baseMint, err := tx.GetAccount(int(accounts[srcIdx]))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !baseMint.Equals(srcMint) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
quoteMint, err := tx.GetAccount(int(accounts[srcIdx+1]))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !quoteMint.Equals(solana.WrappedSol) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return TxSignalBatch{&TxSignal{
|
|
||||||
TxHash: tx.Signatures[0].String(),
|
|
||||||
Maker: tx.StaticAccountKeys[0].String(),
|
|
||||||
Token0Address: baseMint.String(),
|
|
||||||
Token1Address: wsolMint,
|
|
||||||
Token0Amount: formatTokenAmount(inputAmount),
|
|
||||||
Token1Amount: decimal.Zero,
|
|
||||||
Event: "sell",
|
|
||||||
Program: "PumpAMM",
|
|
||||||
IsProcessed: false,
|
|
||||||
IsToken2022: false,
|
|
||||||
IsMayhemMode: false,
|
|
||||||
ExactSOL: false,
|
|
||||||
Token0AmountUint64: inputAmount,
|
|
||||||
Token1AmountUint64: 0,
|
|
||||||
}}, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ var (
|
|||||||
bonkProgramID: {parseBonkInstruction, "bonk"},
|
bonkProgramID: {parseBonkInstruction, "bonk"},
|
||||||
bloomRouterProgramID: {parseBloomRouterInstruction, "bloomrouter"},
|
bloomRouterProgramID: {parseBloomRouterInstruction, "bloomrouter"},
|
||||||
dlmmProgramID: {parseDlmmInstruction, "dlmm"},
|
dlmmProgramID: {parseDlmmInstruction, "dlmm"},
|
||||||
|
dbotProgramID: {parseDbotInstruction, "dbot"},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user