Files
libsam/pkg/shreder/program_flas.go
2026-04-28 18:26:43 +08:00

351 lines
9.6 KiB
Go

package shreder
import (
"encoding/binary"
"fmt"
"github.com/gagliardetto/solana-go"
"github.com/shopspring/decimal"
)
var flasProgramID = solana.MustPublicKeyFromBase58("FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9")
var (
flasBuyTokensIXs = [][]byte{
{0x00, 0x01, 0x21},
{0x00, 0x01, 0x1b},
}
flasSellTokensIXs = [][]byte{
{0x01, 0x01, 0x1a},
}
flasAmmBuyTokensIXs = [][]byte{
{0x00, 0x02, 0x1f},
{0x00, 0x02, 0x02},
}
flasAmmSellTokensIXs = [][]byte{
{0x01, 0x02, 0x1f},
{0x01, 0x02, 0x02},
}
flasBonkBuyTokensIXs = [][]byte{
{0x00, 0x02, 0x07},
}
flasBonkSellTokensIXs = [][]byte{
{0x01, 0x02, 0x07},
}
)
type flasArgs struct {
Amount1 uint64
Amount2 uint64
Placeholder [3]uint8
}
func decodeFlasArgs(data []byte) (flasArgs, error) {
if len(data) < 20 {
return flasArgs{}, fmt.Errorf("data too short for args flas instruction, len: %d", len(data))
}
return flasArgs{
Amount1: binary.LittleEndian.Uint64(data[1:9]),
Amount2: binary.LittleEndian.Uint64(data[9:17]),
Placeholder: [3]uint8{data[17], data[18], data[19]},
}, nil
}
func matchFlasMethod(data []byte, methods [][]byte) bool {
for _, method := range methods {
if matchMethod(data, method) {
return true
}
}
return false
}
func parseFlasInstruction(tx VersionedTransaction, instructionIndex int) (TxSignalBatch, error) {
if instructionIndex >= len(tx.Instructions) {
return nil, fmt.Errorf("instruction index out of bounds")
}
instruction := tx.Instructions[instructionIndex]
if len(instruction.Data) == 0 {
return nil, fmt.Errorf("data is empty")
}
if len(instruction.Data) == 10 && instruction.Data[0] == 1 {
return nil, nil
}
if len(instruction.Data) < 20 {
return nil, fmt.Errorf("data too short for args flas instruction, len: %d", len(instruction.Data))
}
methodData := instruction.Data[17:20]
var (
err error
txSignal *TxSignal
)
if matchFlasMethod(methodData, flasBuyTokensIXs) {
txSignal, err = parseFlasBuy(tx, instructionIndex)
} else if matchFlasMethod(methodData, flasSellTokensIXs) {
txSignal, err = parseFlasSell(tx, instructionIndex)
} else if matchFlasMethod(methodData, flasAmmBuyTokensIXs) {
txSignal, err = parseFlasAmmBuy(tx, instructionIndex)
} else if matchFlasMethod(methodData, flasAmmSellTokensIXs) {
txSignal, err = parseFlasAmmSell(tx, instructionIndex)
} else if matchFlasMethod(methodData, flasBonkBuyTokensIXs) {
txSignal, err = parseFlasBonkBuy(tx, instructionIndex)
} else if matchFlasMethod(methodData, flasBonkSellTokensIXs) {
txSignal, err = parseFlasBonkSell(tx, instructionIndex)
}
if txSignal != nil {
return TxSignalBatch{txSignal}, err
}
return nil, err
}
func parseFlasAmmSell(tx VersionedTransaction, instructionIndex int) (*TxSignal, error) {
instruction := tx.Instructions[instructionIndex]
if len(instruction.Accounts) < 10 {
return nil, fmt.Errorf("accounts too short")
}
mint, err := tx.GetAccount(int(instruction.Accounts[9]))
if err != nil {
return nil, err
}
user, err := tx.GetAccount(int(instruction.Accounts[1]))
if err != nil {
return nil, err
}
args, err := decodeFlasArgs(instruction.Data)
if err != nil {
return nil, fmt.Errorf("failed to parse buy tokens args: %w", err)
}
token0Amount := formatTokenAmount(args.Amount1)
token0AmountUint64 := args.Amount1
if len(instruction.Accounts) == 52 {
token0Amount = decimal.Zero
token0AmountUint64 = 0
}
return &TxSignal{
TxHash: tx.Signatures[0].String(),
Label: "flas",
Maker: user.String(),
Token0Address: mint.String(),
Token1Address: wsolMint,
Token0Amount: token0Amount,
Token1Amount: formatSolAmount(args.Amount2),
Program: "PumpAMM",
Event: "sell",
IsToken2022: false,
IsMayhemMode: false,
ExactSOL: false,
Block: tx.Block,
Token0AmountUint64: token0AmountUint64,
Token1AmountUint64: args.Amount2,
}, nil
}
func parseFlasAmmBuy(tx VersionedTransaction, instructionIndex int) (*TxSignal, error) {
instruction := tx.Instructions[instructionIndex]
if len(instruction.Accounts) < 10 {
return nil, fmt.Errorf("accounts too short")
}
mint, err := tx.GetAccount(int(instruction.Accounts[9]))
if err != nil {
return nil, err
}
user, err := tx.GetAccount(int(instruction.Accounts[1]))
if err != nil {
return nil, err
}
args, err := decodeFlasArgs(instruction.Data)
if err != nil {
return nil, fmt.Errorf("failed to parse buy tokens args: %w", err)
}
return &TxSignal{
TxHash: tx.Signatures[0].String(),
Label: "flas",
Maker: user.String(),
Token0Address: mint.String(),
Token1Address: wsolMint,
Token0Amount: decimal.Zero,
Token1Amount: formatSolAmount(args.Amount1),
Program: "PumpAMM",
Event: "buy",
IsToken2022: false,
IsMayhemMode: false,
ExactSOL: true,
Block: tx.Block,
Token0AmountUint64: 0,
Token1AmountUint64: args.Amount1,
}, nil
}
func parseFlasSell(tx VersionedTransaction, instructionIndex int) (*TxSignal, error) {
instruction := tx.Instructions[instructionIndex]
if len(instruction.Accounts) < 11 {
return nil, fmt.Errorf("accounts too short")
}
mint, err := tx.GetAccount(int(instruction.Accounts[10]))
if err != nil {
return nil, err
}
user, err := tx.GetAccount(int(instruction.Accounts[1]))
if err != nil {
return nil, err
}
args, err := decodeFlasArgs(instruction.Data)
if err != nil {
return nil, fmt.Errorf("failed to parse buy tokens args: %w", err)
}
return &TxSignal{
TxHash: tx.Signatures[0].String(),
Label: "flas",
Maker: user.String(),
Token0Address: mint.String(),
Token1Address: wsolMint,
Token0Amount: formatTokenAmount(args.Amount1),
Token1Amount: formatSolAmount(args.Amount2),
Program: "Pump",
Event: "sell",
IsToken2022: false,
IsMayhemMode: false,
Block: tx.Block,
Token0AmountUint64: args.Amount1,
Token1AmountUint64: args.Amount2,
}, nil
}
func parseFlasBuy(tx VersionedTransaction, instructionIndex int) (*TxSignal, error) {
instruction := tx.Instructions[instructionIndex]
if len(instruction.Accounts) < 11 {
return nil, fmt.Errorf("accounts too short")
}
mint, err := tx.GetAccount(int(instruction.Accounts[10]))
if err != nil {
return nil, err
}
user, err := tx.GetAccount(int(instruction.Accounts[1]))
if err != nil {
return nil, err
}
args, err := decodeFlasArgs(instruction.Data)
if err != nil {
return nil, fmt.Errorf("failed to parse buy tokens args: %w", err)
}
return &TxSignal{
TxHash: tx.Signatures[0].String(),
Label: "flas",
Maker: user.String(),
Token0Address: mint.String(),
Token1Address: wsolMint,
Token0Amount: formatTokenAmount(args.Amount2),
Token1Amount: formatSolAmount(args.Amount1),
Program: "Pump",
Event: "buy",
IsToken2022: false,
IsMayhemMode: false,
ExactSOL: true,
Block: tx.Block,
Token0AmountUint64: args.Amount2,
Token1AmountUint64: args.Amount1,
}, nil
}
func parseFlasBonkBuy(tx VersionedTransaction, instructionIndex int) (*TxSignal, error) {
instruction := tx.Instructions[instructionIndex]
if len(instruction.Accounts) < 17 {
return nil, fmt.Errorf("accounts too short")
}
base, err := tx.GetAccount(int(instruction.Accounts[15]))
if err != nil {
return nil, err
}
quote, err := tx.GetAccount(int(instruction.Accounts[16]))
if err != nil {
return nil, err
}
user, err := tx.GetAccount(int(instruction.Accounts[1]))
if err != nil {
return nil, err
}
if !quote.Equals(wrappedSOL) {
// just ignore this
return nil, nil
}
args, err := decodeFlasArgs(instruction.Data)
if err != nil {
return nil, fmt.Errorf("failed to parse buy tokens args: %w", err)
}
return &TxSignal{
TxHash: tx.Signatures[0].String(),
Label: "flas",
Maker: user.String(),
Token0Address: base.String(),
Token1Address: wsolMint,
Token0Amount: formatTokenAmount(args.Amount2),
Token1Amount: formatSolAmount(args.Amount1),
Program: "RaydiumLaunchLab",
Event: "buy",
ExactSOL: true,
Block: tx.Block,
Token0AmountUint64: args.Amount2,
Token1AmountUint64: args.Amount1,
}, nil
}
func parseFlasBonkSell(tx VersionedTransaction, instructionIndex int) (*TxSignal, error) {
instruction := tx.Instructions[instructionIndex]
if len(instruction.Accounts) < 17 {
return nil, fmt.Errorf("accounts too short")
}
base, err := tx.GetAccount(int(instruction.Accounts[15]))
if err != nil {
return nil, err
}
quote, err := tx.GetAccount(int(instruction.Accounts[16]))
if err != nil {
return nil, err
}
user, err := tx.GetAccount(int(instruction.Accounts[1]))
if err != nil {
return nil, err
}
if !quote.Equals(wrappedSOL) {
// just ignore this
return nil, nil
}
args, err := decodeFlasArgs(instruction.Data)
if err != nil {
return nil, fmt.Errorf("failed to parse buy tokens args: %w", err)
}
return &TxSignal{
TxHash: tx.Signatures[0].String(),
Label: "flas",
Maker: user.String(),
Token0Address: base.String(),
Token1Address: wsolMint,
Token0Amount: formatTokenAmount(args.Amount1),
Token1Amount: formatSolAmount(args.Amount2),
Program: "RaydiumLaunchLab",
Event: "sell",
Block: tx.Block,
Token0AmountUint64: args.Amount1,
Token1AmountUint64: args.Amount2,
}, nil
}