Compare commits
6 Commits
v2.0.9
...
f6242f0193
| Author | SHA1 | Date | |
|---|---|---|---|
| f6242f0193 | |||
| b06a1fa377 | |||
| 75c35f56f1 | |||
| 79859bc079 | |||
|
|
bd2dbe3c91 | ||
|
|
22d2df3782 |
@@ -72,6 +72,11 @@ const (
|
||||
ActOpenPredictionsOrder
|
||||
ActScorchSwap
|
||||
ActIncludeAccount
|
||||
|
||||
ActDFLOWStabbleWeightedSwap
|
||||
ActVertigoSwap
|
||||
ActSetMinimumLegOutputs
|
||||
ActSetMinimumLegPrices
|
||||
)
|
||||
|
||||
// DynamicRouteV1CandidateAction tags
|
||||
@@ -104,7 +109,7 @@ type dflowSwapParams struct {
|
||||
// bytes to skip for Action variants; only PumpFun* actions are decoded.
|
||||
func skipDflowAction(dec *bin.Decoder, tag uint8) (*pumpFunAction, error) {
|
||||
switch tag {
|
||||
case ActWhirlpoolsSwap, ActClearpoolsSwap, ActWhirlpoolsSwapV2:
|
||||
case ActWhirlpoolsSwap, ActClearpoolsSwap, ActWhirlpoolsSwapV2, ActDFLOWStabbleWeightedSwap, ActVertigoSwap:
|
||||
// amount u64 + bool + orchestrator_flags u8
|
||||
return nil, dec.SkipBytes(8 + 1 + 1)
|
||||
case ActRaydiumAmmSwap, ActLifinityV2Swap, ActObricV2Swap,
|
||||
@@ -181,6 +186,19 @@ func skipDflowAction(dec *bin.Decoder, tag uint8) (*pumpFunAction, error) {
|
||||
return nil, dec.SkipBytes(8 + 16 + 1)
|
||||
case ActIncludeAccount:
|
||||
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:
|
||||
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 {
|
||||
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]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -220,6 +224,7 @@ func parseDlmmInstruction(tx VersionedTransaction, instructionIndex int) (TxSign
|
||||
ExactSOL: exactSol,
|
||||
ActiveBin: args.ActiveBin,
|
||||
MaxPriceImpactBps: args.MaxPriceImpactBps,
|
||||
LbPairAddress: lbPair.String(),
|
||||
Block: tx.Block,
|
||||
Token0AmountUint64: token0AmountUint64,
|
||||
Token1AmountUint64: token1AmountUint64,
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
|
||||
var flasProgramID = solana.MustPublicKeyFromBase58("FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9")
|
||||
var (
|
||||
flasBuyTokensIX = []byte{0x00, 0x1, 0x4}
|
||||
flasSellTokensIX = []byte{0x01, 0x1, 0x3}
|
||||
flasBuyTokensIX = []byte{0x00, 0x1, 0x1b}
|
||||
flasSellTokensIX = []byte{0x01, 0x1, 0x1a}
|
||||
flasAmmBuyTokensIX = []byte{0x00, 0x2, 0x2}
|
||||
flasAmmSellTokensIX = []byte{0x01, 0x2, 0x2}
|
||||
)
|
||||
|
||||
@@ -157,6 +157,9 @@ const (
|
||||
PerenaStar
|
||||
JupiterRfqV2
|
||||
GoonFiV2
|
||||
Scorch
|
||||
VaultLiquidUnstake
|
||||
XOrca
|
||||
)
|
||||
|
||||
var swapKindNames = [122]string{"Saber", "SaberAddDecimalsDeposit", "SaberAddDecimalsWithdraw", "TokenSwap", "Sencha", "Step", "Cropper",
|
||||
@@ -316,7 +319,7 @@ func decodeSwap(dec *bin.Decoder) (Swap, error) {
|
||||
BoopdotfunWrappedBuy, BoopdotfunWrappedSell, MeteoraDynamicBondingCurveSwapWithRemainingAccounts,
|
||||
PumpWrappedBuyV2, PumpWrappedSellV2, PumpSwapBuyV2, PumpSwapSellV2, Aquifer, PumpWrappedBuyV3, PumpWrappedSellV3,
|
||||
PumpSwapBuyV3, PumpSwapSellV3, JupiterLendDeposit, JupiterLendRedeem, RaydiumV2,
|
||||
MeteoraDammV2WithRemainingAccounts, Obsidian, PumpWrappedBuyV4, PumpWrappedSellV4, CarrotIssue, CarrotRedeem:
|
||||
MeteoraDammV2WithRemainingAccounts, Obsidian, PumpWrappedBuyV4, PumpWrappedSellV4, CarrotIssue, CarrotRedeem, XOrca:
|
||||
return out, nil
|
||||
|
||||
// -------- bool payload --------
|
||||
@@ -406,9 +409,23 @@ func decodeSwap(dec *bin.Decoder) (Swap, error) {
|
||||
return Swap{}, err
|
||||
}
|
||||
return out, nil
|
||||
case Scorch:
|
||||
// side
|
||||
if err := skipTwoU64(); err != nil {
|
||||
return Swap{}, err
|
||||
}
|
||||
return out, nil
|
||||
case VaultLiquidUnstake:
|
||||
// [{"name":"lst_amounts","type":{"array":["u64",5]}},{"name":"seed","type":"u64"}]
|
||||
for i := 0; i <= 5; i++ {
|
||||
if err := skipU64(); err != nil {
|
||||
return Swap{}, err
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
default:
|
||||
// Unknown/new variant: assume no payload (keeps decoder aligned for RoutePlanStepV2 if it really is no-payload).
|
||||
return out, nil
|
||||
return out, fmt.Errorf("unknown Swap variant: %d", tag)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1102,9 +1119,11 @@ func pumpRoutePlanStatsV2(in uint64, out uint64, plan []RoutePlanStepV2, include
|
||||
|
||||
func parseJupiterPumpAmmRoute(tx VersionedTransaction, instruction Instructions, in uint64, out uint64, plan []RoutePlanStep) (*TxSignal, bool, error) {
|
||||
var (
|
||||
isBuy bool
|
||||
isSell bool
|
||||
count int
|
||||
isBuy bool
|
||||
isSell bool
|
||||
count int
|
||||
sellPercent uint8
|
||||
buyPercent uint8
|
||||
)
|
||||
for _, step := range plan {
|
||||
if !isInputIdx0(step.InputIdx) {
|
||||
@@ -1113,9 +1132,11 @@ func parseJupiterPumpAmmRoute(tx VersionedTransaction, instruction Instructions,
|
||||
if isPumpSwapSellKind(step.Swap.Kind) {
|
||||
isSell = true
|
||||
count++
|
||||
sellPercent = step.Percent
|
||||
} else if isPumpSwapBuyKind(step.Swap.Kind) {
|
||||
isBuy = true
|
||||
count++
|
||||
buyPercent = step.Percent
|
||||
}
|
||||
}
|
||||
if count == 0 {
|
||||
@@ -1137,6 +1158,9 @@ func parseJupiterPumpAmmRoute(tx VersionedTransaction, instruction Instructions,
|
||||
if in > 0 {
|
||||
token0Amount = formatTokenAmount(in)
|
||||
}
|
||||
if sellPercent > 0 && sellPercent < 100 {
|
||||
token0Amount = token0Amount.Mul(decimal.NewFromInt(int64(sellPercent))).Div(decimal.NewFromInt(100))
|
||||
}
|
||||
return &TxSignal{
|
||||
TxHash: tx.Signatures[0].String(),
|
||||
Maker: tx.StaticAccountKeys[0].String(),
|
||||
@@ -1172,6 +1196,10 @@ func parseJupiterPumpAmmRoute(tx VersionedTransaction, instruction Instructions,
|
||||
if in > 0 {
|
||||
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{
|
||||
TxHash: tx.Signatures[0].String(),
|
||||
Maker: tx.StaticAccountKeys[0].String(),
|
||||
|
||||
@@ -65,7 +65,11 @@ func TestDecodeRouteArg(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "Jupiter V6 RouteArg Test 1",
|
||||
hexData: "e517cb977ae3ad2a0200000028640001510000000000000000640102c09ee605000000005e1bc48efa000000d00700",
|
||||
hexData: "e517cb977ae3ad2a03000000646400017ab0b6c3d206f46577050000000c0000526401025f00640203bb628e2902000000338c430100000000320000",
|
||||
},
|
||||
{
|
||||
name: "Jupiter V6 RouteArg Test 2",
|
||||
hexData: "e517cb977ae3ad2a04000000642300024b00000000410002761acfb15ea9fdcd0501200204769358e96343759bf8014402046196591e1e020000f5bf6fe101000000d00700",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -55,6 +55,8 @@ type TxSignal struct {
|
||||
ActiveBin int32 `json:"active_bin"`
|
||||
// MaxPriceImpactBps is the price impact guard for swap_with_price_impact(2).
|
||||
MaxPriceImpactBps uint16 `json:"max_price_impact_bps"`
|
||||
//
|
||||
LbPairAddress string `json:"lb_pair_address"`
|
||||
|
||||
// parsed values
|
||||
Token0AmountUint64 uint64 `json:"-"`
|
||||
|
||||
Reference in New Issue
Block a user