Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fa6944a37 | ||
|
|
5d06d18aa8 | ||
| 9877794d1c | |||
| f6242f0193 | |||
| b06a1fa377 | |||
| 75c35f56f1 | |||
| 79859bc079 | |||
|
|
bd2dbe3c91 |
@@ -72,6 +72,11 @@ const (
|
|||||||
ActOpenPredictionsOrder
|
ActOpenPredictionsOrder
|
||||||
ActScorchSwap
|
ActScorchSwap
|
||||||
ActIncludeAccount
|
ActIncludeAccount
|
||||||
|
|
||||||
|
ActDFLOWStabbleWeightedSwap
|
||||||
|
ActVertigoSwap
|
||||||
|
ActSetMinimumLegOutputs
|
||||||
|
ActSetMinimumLegPrices
|
||||||
)
|
)
|
||||||
|
|
||||||
// DynamicRouteV1CandidateAction tags
|
// DynamicRouteV1CandidateAction tags
|
||||||
@@ -104,7 +109,7 @@ type dflowSwapParams struct {
|
|||||||
// bytes to skip for Action variants; only PumpFun* actions are decoded.
|
// bytes to skip for Action variants; only PumpFun* actions are decoded.
|
||||||
func skipDflowAction(dec *bin.Decoder, tag uint8) (*pumpFunAction, error) {
|
func skipDflowAction(dec *bin.Decoder, tag uint8) (*pumpFunAction, error) {
|
||||||
switch tag {
|
switch tag {
|
||||||
case ActWhirlpoolsSwap, ActClearpoolsSwap, ActWhirlpoolsSwapV2:
|
case ActWhirlpoolsSwap, ActClearpoolsSwap, ActWhirlpoolsSwapV2, ActDFLOWStabbleWeightedSwap, ActVertigoSwap:
|
||||||
// amount u64 + bool + orchestrator_flags u8
|
// amount u64 + bool + orchestrator_flags u8
|
||||||
return nil, dec.SkipBytes(8 + 1 + 1)
|
return nil, dec.SkipBytes(8 + 1 + 1)
|
||||||
case ActRaydiumAmmSwap, ActLifinityV2Swap, ActObricV2Swap,
|
case ActRaydiumAmmSwap, ActLifinityV2Swap, ActObricV2Swap,
|
||||||
@@ -181,6 +186,19 @@ func skipDflowAction(dec *bin.Decoder, tag uint8) (*pumpFunAction, error) {
|
|||||||
return nil, dec.SkipBytes(8 + 16 + 1)
|
return nil, dec.SkipBytes(8 + 16 + 1)
|
||||||
case ActIncludeAccount:
|
case ActIncludeAccount:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
case ActSetMinimumLegOutputs:
|
||||||
|
ln, err := dec.ReadUint32(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, dec.SkipBytes(uint(8 * ln))
|
||||||
|
case ActSetMinimumLegPrices:
|
||||||
|
// Vec<(u64, u8)>; read length and skip the pairs
|
||||||
|
ln, err := dec.ReadUint32(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, dec.SkipBytes(uint(uint64(ln) * (8 + 1)))
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported action tag %d", tag)
|
return nil, fmt.Errorf("unsupported action tag %d", tag)
|
||||||
}
|
}
|
||||||
|
|||||||
61
pkg/shreder/program_dflow_test.go
Normal file
61
pkg/shreder/program_dflow_test.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package shreder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDFlowDecodedSwapParams(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
hexData string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "DFlow swap Test 0",
|
||||||
|
hexData: "f8c69e91e17587c806000000256cb411cd4dcea8c073833936254cc3a7a6f1bc3e1106af1fceaed1bf6d75184d8149476a66d1f0d4c23c177e81d73b8b11297c7f7d8a8d6e339939647915d8096cfcdd170000000093000000300300000000000000000000000000000000000000a84325c4000000002d0decfc36e0bc09000001197bcde00df80000000180130edffead0800000081711ebdc4000000002c013200",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
instrData, err := hex.DecodeString(tt.hexData)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to decode hex string: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//t.Logf("raw bytes: %x", instrData[8:])
|
||||||
|
args, err := decodeSwapParams(instrData[8:])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to decode dflow arguments: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Logf("decoded args: %+v", args)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDFlowV2DecodedSwapParams(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
hexData string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "DFlow swap 2 Test 0",
|
||||||
|
hexData: "414b3f4ceb5b5b880300000025427ee16eed91684faaad4a3f161acd31d92bbc3d1ba0e2ebdb4678448fd5a7aeade9c8b38e8755e811f3373a0056cd5647e4cc3510135f98e97cb03c046ade049d08de17000000007800000023f50a0000000000002e1bfe04000000000001c3e13700000000003601000005",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
instrData, err := hex.DecodeString(tt.hexData)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to decode hex string: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
args, err := decodeSwap2Params(instrData[8:])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to decode dflow arguments: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Logf("decoded args: %+v", args)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -122,6 +122,10 @@ func parseDlmmInstruction(tx VersionedTransaction, instructionIndex int) (TxSign
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
lbPair, err := tx.GetAccount(int(instruction.Accounts[0]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
userTokenOut, err := tx.GetAccount(int(instruction.Accounts[5]))
|
userTokenOut, err := tx.GetAccount(int(instruction.Accounts[5]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -220,6 +224,7 @@ func parseDlmmInstruction(tx VersionedTransaction, instructionIndex int) (TxSign
|
|||||||
ExactSOL: exactSol,
|
ExactSOL: exactSol,
|
||||||
ActiveBin: args.ActiveBin,
|
ActiveBin: args.ActiveBin,
|
||||||
MaxPriceImpactBps: args.MaxPriceImpactBps,
|
MaxPriceImpactBps: args.MaxPriceImpactBps,
|
||||||
|
LbPairAddress: lbPair.String(),
|
||||||
Block: tx.Block,
|
Block: tx.Block,
|
||||||
Token0AmountUint64: token0AmountUint64,
|
Token0AmountUint64: token0AmountUint64,
|
||||||
Token1AmountUint64: token1AmountUint64,
|
Token1AmountUint64: token1AmountUint64,
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import (
|
|||||||
|
|
||||||
var flasProgramID = solana.MustPublicKeyFromBase58("FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9")
|
var flasProgramID = solana.MustPublicKeyFromBase58("FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9")
|
||||||
var (
|
var (
|
||||||
flasBuyTokensIX = []byte{0x00, 0x1, 0x4}
|
flasBuyTokensIX = []byte{0x00, 0x1, 0x1b}
|
||||||
flasSellTokensIX = []byte{0x01, 0x1, 0x3}
|
flasSellTokensIX = []byte{0x01, 0x1, 0x1a}
|
||||||
flasAmmBuyTokensIX = []byte{0x00, 0x2, 0x2}
|
flasAmmBuyTokensIX = []byte{0x00, 0x2, 0x2}
|
||||||
flasAmmSellTokensIX = []byte{0x01, 0x2, 0x2}
|
flasAmmSellTokensIX = []byte{0x01, 0x2, 0x2}
|
||||||
)
|
)
|
||||||
@@ -140,11 +140,11 @@ func parseFlasAmmBuy(tx VersionedTransaction, instructionIndex int) (*TxSignal,
|
|||||||
|
|
||||||
func parseFlasSell(tx VersionedTransaction, instructionIndex int) (*TxSignal, error) {
|
func parseFlasSell(tx VersionedTransaction, instructionIndex int) (*TxSignal, error) {
|
||||||
instruction := tx.Instructions[instructionIndex]
|
instruction := tx.Instructions[instructionIndex]
|
||||||
if len(instruction.Accounts) < 9 {
|
if len(instruction.Accounts) < 11 {
|
||||||
return nil, fmt.Errorf("accounts too short")
|
return nil, fmt.Errorf("accounts too short")
|
||||||
}
|
}
|
||||||
|
|
||||||
mint, err := tx.GetAccount(int(instruction.Accounts[8]))
|
mint, err := tx.GetAccount(int(instruction.Accounts[10]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -178,15 +178,15 @@ func parseFlasSell(tx VersionedTransaction, instructionIndex int) (*TxSignal, er
|
|||||||
|
|
||||||
func parseFlasBuy(tx VersionedTransaction, instructionIndex int) (*TxSignal, error) {
|
func parseFlasBuy(tx VersionedTransaction, instructionIndex int) (*TxSignal, error) {
|
||||||
instruction := tx.Instructions[instructionIndex]
|
instruction := tx.Instructions[instructionIndex]
|
||||||
if len(instruction.Accounts) < 9 {
|
if len(instruction.Accounts) < 11 {
|
||||||
return nil, fmt.Errorf("accounts too short")
|
return nil, fmt.Errorf("accounts too short")
|
||||||
}
|
}
|
||||||
|
|
||||||
mint, err := tx.GetAccount(int(instruction.Accounts[8]))
|
mint, err := tx.GetAccount(int(instruction.Accounts[10]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
user, err := tx.GetAccount(int(instruction.Accounts[0]))
|
user, err := tx.GetAccount(int(instruction.Accounts[1]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1122,6 +1122,8 @@ func parseJupiterPumpAmmRoute(tx VersionedTransaction, instruction Instructions,
|
|||||||
isBuy bool
|
isBuy bool
|
||||||
isSell bool
|
isSell bool
|
||||||
count int
|
count int
|
||||||
|
sellPercent uint8
|
||||||
|
buyPercent uint8
|
||||||
)
|
)
|
||||||
for _, step := range plan {
|
for _, step := range plan {
|
||||||
if !isInputIdx0(step.InputIdx) {
|
if !isInputIdx0(step.InputIdx) {
|
||||||
@@ -1130,9 +1132,11 @@ func parseJupiterPumpAmmRoute(tx VersionedTransaction, instruction Instructions,
|
|||||||
if isPumpSwapSellKind(step.Swap.Kind) {
|
if isPumpSwapSellKind(step.Swap.Kind) {
|
||||||
isSell = true
|
isSell = true
|
||||||
count++
|
count++
|
||||||
|
sellPercent = step.Percent
|
||||||
} else if isPumpSwapBuyKind(step.Swap.Kind) {
|
} else if isPumpSwapBuyKind(step.Swap.Kind) {
|
||||||
isBuy = true
|
isBuy = true
|
||||||
count++
|
count++
|
||||||
|
buyPercent = step.Percent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
@@ -1154,6 +1158,9 @@ func parseJupiterPumpAmmRoute(tx VersionedTransaction, instruction Instructions,
|
|||||||
if in > 0 {
|
if in > 0 {
|
||||||
token0Amount = formatTokenAmount(in)
|
token0Amount = formatTokenAmount(in)
|
||||||
}
|
}
|
||||||
|
if sellPercent > 0 && sellPercent < 100 {
|
||||||
|
token0Amount = token0Amount.Mul(decimal.NewFromInt(int64(sellPercent))).Div(decimal.NewFromInt(100))
|
||||||
|
}
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
Maker: tx.StaticAccountKeys[0].String(),
|
Maker: tx.StaticAccountKeys[0].String(),
|
||||||
@@ -1189,6 +1196,10 @@ func parseJupiterPumpAmmRoute(tx VersionedTransaction, instruction Instructions,
|
|||||||
if in > 0 {
|
if in > 0 {
|
||||||
token1Amount = formatSolAmount(in)
|
token1Amount = formatSolAmount(in)
|
||||||
}
|
}
|
||||||
|
if buyPercent > 0 && buyPercent < 100 {
|
||||||
|
token1Amount = token1Amount.Mul(decimal.NewFromInt(int64(buyPercent))).Div(decimal.NewFromInt(100))
|
||||||
|
token0Amount = token0Amount.Mul(decimal.NewFromInt(int64(buyPercent))).Div(decimal.NewFromInt(100))
|
||||||
|
}
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
Maker: tx.StaticAccountKeys[0].String(),
|
Maker: tx.StaticAccountKeys[0].String(),
|
||||||
|
|||||||
@@ -67,6 +67,10 @@ func TestDecodeRouteArg(t *testing.T) {
|
|||||||
name: "Jupiter V6 RouteArg Test 1",
|
name: "Jupiter V6 RouteArg Test 1",
|
||||||
hexData: "e517cb977ae3ad2a03000000646400017ab0b6c3d206f46577050000000c0000526401025f00640203bb628e2902000000338c430100000000320000",
|
hexData: "e517cb977ae3ad2a03000000646400017ab0b6c3d206f46577050000000c0000526401025f00640203bb628e2902000000338c430100000000320000",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Jupiter V6 RouteArg Test 2",
|
||||||
|
hexData: "e517cb977ae3ad2a04000000642300024b00000000410002761acfb15ea9fdcd0501200204769358e96343759bf8014402046196591e1e020000f5bf6fe101000000d00700",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ type TxSignal struct {
|
|||||||
IsToken2022 bool `json:"is_token2022"`
|
IsToken2022 bool `json:"is_token2022"`
|
||||||
IsMayhemMode bool `json:"is_mayhem_mode"`
|
IsMayhemMode bool `json:"is_mayhem_mode"`
|
||||||
CUPrice decimal.Decimal `json:"cu_price"`
|
CUPrice decimal.Decimal `json:"cu_price"`
|
||||||
|
CULimit uint32 `json:"cu_limit"`
|
||||||
SWQoSAgent string `json:"swqos_agent"`
|
SWQoSAgent string `json:"swqos_agent"`
|
||||||
SWQoSTips decimal.Decimal `json:"swqos_tips"`
|
SWQoSTips decimal.Decimal `json:"swqos_tips"`
|
||||||
|
|
||||||
@@ -55,6 +56,8 @@ type TxSignal struct {
|
|||||||
ActiveBin int32 `json:"active_bin"`
|
ActiveBin int32 `json:"active_bin"`
|
||||||
// MaxPriceImpactBps is the price impact guard for swap_with_price_impact(2).
|
// MaxPriceImpactBps is the price impact guard for swap_with_price_impact(2).
|
||||||
MaxPriceImpactBps uint16 `json:"max_price_impact_bps"`
|
MaxPriceImpactBps uint16 `json:"max_price_impact_bps"`
|
||||||
|
//
|
||||||
|
LbPairAddress string `json:"lb_pair_address"`
|
||||||
|
|
||||||
// parsed values
|
// parsed values
|
||||||
Token0AmountUint64 uint64 `json:"-"`
|
Token0AmountUint64 uint64 `json:"-"`
|
||||||
|
|||||||
@@ -203,16 +203,19 @@ func ParseTransactionWithHandler(ctx context.Context, versioned VersionedTransac
|
|||||||
cuPrice := decimal.Zero
|
cuPrice := decimal.Zero
|
||||||
swqosAgent := ""
|
swqosAgent := ""
|
||||||
swqosTips := decimal.Zero
|
swqosTips := decimal.Zero
|
||||||
|
cuLimit := uint32(0)
|
||||||
for _, instruction := range versioned.Instructions {
|
for _, instruction := range versioned.Instructions {
|
||||||
program, err := versioned.GetAccount(int(instruction.ProgramIDIndex))
|
program, err := versioned.GetAccount(int(instruction.ProgramIDIndex))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if program.Equals(ComputeBudgetProgram) &&
|
if program.Equals(ComputeBudgetProgram) {
|
||||||
len(instruction.Data) == 9 &&
|
if len(instruction.Data) == 9 && instruction.Data[0] == 0x03 {
|
||||||
instruction.Data[0] == 0x03 {
|
|
||||||
cuPriceUint64 := binary.LittleEndian.Uint64(instruction.Data[1:9])
|
cuPriceUint64 := binary.LittleEndian.Uint64(instruction.Data[1:9])
|
||||||
cuPrice = formatCUPrice(cuPriceUint64)
|
cuPrice = formatCUPrice(cuPriceUint64)
|
||||||
|
} else if len(instruction.Data) == 5 && instruction.Data[0] == 0x02 {
|
||||||
|
cuLimit = binary.LittleEndian.Uint32(instruction.Data[1:4])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if program.Equals(solana.SystemProgramID) &&
|
if program.Equals(solana.SystemProgramID) &&
|
||||||
len(instruction.Data) == 12 &&
|
len(instruction.Data) == 12 &&
|
||||||
@@ -260,6 +263,7 @@ func ParseTransactionWithHandler(ctx context.Context, versioned VersionedTransac
|
|||||||
one.Label = handler.Label
|
one.Label = handler.Label
|
||||||
one.Block = versioned.Block
|
one.Block = versioned.Block
|
||||||
one.CUPrice = cuPrice
|
one.CUPrice = cuPrice
|
||||||
|
one.CULimit = cuLimit
|
||||||
one.SWQoSAgent = swqosAgent
|
one.SWQoSAgent = swqosAgent
|
||||||
one.SWQoSTips = swqosTips
|
one.SWQoSTips = swqosTips
|
||||||
select {
|
select {
|
||||||
|
|||||||
Reference in New Issue
Block a user