Files
libsam/pkg/shreder/program_flas.go

319 lines
9.1 KiB
Go
Raw Normal View History

2026-01-28 14:11:34 +08:00
package shreder
import (
"fmt"
"github.com/gagliardetto/solana-go"
"github.com/near/borsh-go"
"github.com/shopspring/decimal"
)
var flasProgramID = solana.MustPublicKeyFromBase58("FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9")
var (
2026-03-23 16:01:41 +08:00
flasBuyTokensIX = []byte{0x00, 0x1, 0x1b}
flasSellTokensIX = []byte{0x01, 0x1, 0x1a}
flasAmmBuyTokensIX = []byte{0x00, 0x2, 0x2}
flasAmmSellTokensIX = []byte{0x01, 0x2, 0x2}
flasBonkBuyTokensIX = []byte{0x00, 0x2, 0x7}
flasBonkSellTokensIX = []byte{0x01, 0x2, 0x7}
2026-01-28 14:11:34 +08:00
)
type flasArgs struct {
Amount1 uint64
Amount2 uint64
Placeholder [3]uint8
}
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]
//if !matchMethod(methodData, flasBuyTokensIX) {
// return nil, nil
//}
var (
err error
txSignal *TxSignal
)
if matchMethod(methodData, flasBuyTokensIX) {
txSignal, err = parseFlasBuy(tx, instructionIndex)
} else if matchMethod(methodData, flasSellTokensIX) {
txSignal, err = parseFlasSell(tx, instructionIndex)
} else if matchMethod(methodData, flasAmmBuyTokensIX) {
txSignal, err = parseFlasAmmBuy(tx, instructionIndex)
} else if matchMethod(methodData, flasAmmSellTokensIX) {
txSignal, err = parseFlasAmmSell(tx, instructionIndex)
2026-03-23 16:01:41 +08:00
} else if matchMethod(methodData, flasBonkBuyTokensIX) {
txSignal, err = parseFlasBonkBuy(tx, instructionIndex)
} else if matchMethod(methodData, flasBonkSellTokensIX) {
txSignal, err = parseFlasBonkSell(tx, instructionIndex)
2026-01-28 14:11:34 +08:00
}
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
}
var args flasArgs
if err := borsh.Deserialize(&args, instruction.Data[1:]); 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: "PumpAMM",
Event: "sell",
IsToken2022: false,
IsMayhemMode: false,
ExactSOL: false,
Block: tx.Block,
Token0AmountUint64: args.Amount1,
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
}
var args flasArgs
if err := borsh.Deserialize(&args, instruction.Data[1:]); 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]
2026-02-21 19:03:00 +08:00
if len(instruction.Accounts) < 11 {
2026-01-28 14:11:34 +08:00
return nil, fmt.Errorf("accounts too short")
}
2026-02-21 19:03:00 +08:00
mint, err := tx.GetAccount(int(instruction.Accounts[10]))
2026-01-28 14:11:34 +08:00
if err != nil {
return nil, err
}
user, err := tx.GetAccount(int(instruction.Accounts[1]))
if err != nil {
return nil, err
}
var args flasArgs
if err := borsh.Deserialize(&args, instruction.Data[1:]); 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]
2026-02-21 19:03:00 +08:00
if len(instruction.Accounts) < 11 {
2026-01-28 14:11:34 +08:00
return nil, fmt.Errorf("accounts too short")
}
2026-02-21 19:03:00 +08:00
mint, err := tx.GetAccount(int(instruction.Accounts[10]))
2026-01-28 14:11:34 +08:00
if err != nil {
return nil, err
}
2026-02-21 19:03:00 +08:00
user, err := tx.GetAccount(int(instruction.Accounts[1]))
2026-01-28 14:11:34 +08:00
if err != nil {
return nil, err
}
if len(instruction.Data) > 20 {
instruction.Data = instruction.Data[:20]
}
var args flasArgs
if err := borsh.Deserialize(&args, instruction.Data[1:]); 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
}
2026-03-23 16:01:41 +08:00
func parseFlasBonkBuy(tx VersionedTransaction, instructionIndex int) (*TxSignal, error) {
instruction := tx.Instructions[instructionIndex]
if len(instruction.Accounts) < 16 {
return nil, fmt.Errorf("accounts too short")
}
2026-03-23 16:08:55 +08:00
base, err := tx.GetAccount(int(instruction.Accounts[15]))
if err != nil {
return nil, err
}
quote, err := tx.GetAccount(int(instruction.Accounts[16]))
2026-03-23 16:01:41 +08:00
if err != nil {
return nil, err
}
user, err := tx.GetAccount(int(instruction.Accounts[1]))
if err != nil {
return nil, err
}
2026-03-23 16:08:55 +08:00
if !quote.Equals(wrappedSOL) {
// just ignore this
return nil, nil
}
2026-03-23 16:01:41 +08:00
if len(instruction.Data) > 20 {
instruction.Data = instruction.Data[:20]
}
var args flasArgs
if err := borsh.Deserialize(&args, instruction.Data[1:]); 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(),
2026-03-23 16:08:55 +08:00
Token0Address: base.String(),
2026-03-23 16:01:41 +08:00
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) < 16 {
return nil, fmt.Errorf("accounts too short")
}
2026-03-23 16:08:55 +08:00
base, err := tx.GetAccount(int(instruction.Accounts[15]))
if err != nil {
return nil, err
}
quote, err := tx.GetAccount(int(instruction.Accounts[16]))
2026-03-23 16:01:41 +08:00
if err != nil {
return nil, err
}
user, err := tx.GetAccount(int(instruction.Accounts[1]))
if err != nil {
return nil, err
}
2026-03-23 16:08:55 +08:00
if !quote.Equals(wrappedSOL) {
// just ignore this
return nil, nil
}
2026-03-23 16:01:41 +08:00
var args flasArgs
if err := borsh.Deserialize(&args, instruction.Data[1:]); 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(),
2026-03-23 16:08:55 +08:00
Token0Address: base.String(),
2026-03-23 16:01:41 +08:00
Token1Address: wsolMint,
Token0Amount: formatTokenAmount(args.Amount1),
Token1Amount: formatSolAmount(args.Amount2),
Program: "RaydiumLaunchLab",
Event: "sell",
Block: tx.Block,
Token0AmountUint64: args.Amount1,
Token1AmountUint64: args.Amount2,
}, nil
}