Update juptier pumpfun usdc usdt usd1 filter
This commit is contained in:
@@ -22,6 +22,10 @@ var (
|
||||
|
||||
jupiterSharedAccountsExactOutRouteV2 = []byte{53, 96, 229, 202, 216, 187, 250, 24}
|
||||
jupiterSharedAccountsRouteV2 = []byte{209, 152, 83, 147, 124, 254, 216, 233}
|
||||
|
||||
usdcMint = solana.MustPublicKeyFromBase58("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
|
||||
usd1Mint = solana.MustPublicKeyFromBase58("USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB")
|
||||
usdtMint = solana.MustPublicKeyFromBase58("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB")
|
||||
)
|
||||
|
||||
type Side uint8
|
||||
@@ -882,6 +886,32 @@ func isPumpWrappedSell(kind SwapKind) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func isStableMint(mint solana.PublicKey) bool {
|
||||
if mint.Equals(usdcMint) {
|
||||
return true
|
||||
}
|
||||
if mint.Equals(usd1Mint) {
|
||||
return true
|
||||
}
|
||||
if mint.Equals(usdtMint) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isToken1Mint(mint solana.PublicKey) bool {
|
||||
return mint.Equals(solana.WrappedSol) || mint.Equals(solana.SystemProgramID) || isStableMint(mint)
|
||||
}
|
||||
|
||||
func isJupiterV6Token1RequiredDisc(disc []byte) bool {
|
||||
return bytes.Equal(disc, jupiterRouteV2) ||
|
||||
bytes.Equal(disc, jupiterSharedAccountsRouteV2) ||
|
||||
bytes.Equal(disc, jupiterExactOutRouteV2) ||
|
||||
bytes.Equal(disc, jupiterSharedAccountsExactOutRouteV2) ||
|
||||
bytes.Equal(disc, jupiterSharedAccountsRoute) ||
|
||||
bytes.Equal(disc, jupiterSharedAccountsExactOutRoute)
|
||||
}
|
||||
|
||||
func pumpWrappedAtIdx0(in uint64, out uint64, plan []RoutePlanStep) (pumpWrappedMatch, int) {
|
||||
var (
|
||||
ret pumpWrappedMatch
|
||||
@@ -932,6 +962,42 @@ func pumpWrappedAtIdx0V2(in uint64, out uint64, plan []RoutePlanStepV2) (pumpWra
|
||||
return ret, count
|
||||
}
|
||||
|
||||
func pumpWrappedAny(plan []RoutePlanStep) (pumpWrappedMatch, int) {
|
||||
var (
|
||||
ret pumpWrappedMatch
|
||||
count int
|
||||
)
|
||||
for _, step := range plan {
|
||||
if !isPumpWrappedBuy(step.Swap.Kind) && !isPumpWrappedSell(step.Swap.Kind) {
|
||||
continue
|
||||
}
|
||||
count++
|
||||
if count > 1 {
|
||||
return pumpWrappedMatch{}, count
|
||||
}
|
||||
ret.IsBuy = isPumpWrappedBuy(step.Swap.Kind)
|
||||
}
|
||||
return ret, count
|
||||
}
|
||||
|
||||
func pumpWrappedAnyV2(plan []RoutePlanStepV2) (pumpWrappedMatch, int) {
|
||||
var (
|
||||
ret pumpWrappedMatch
|
||||
count int
|
||||
)
|
||||
for _, step := range plan {
|
||||
if !isPumpWrappedBuy(step.Swap.Kind) && !isPumpWrappedSell(step.Swap.Kind) {
|
||||
continue
|
||||
}
|
||||
count++
|
||||
if count > 1 {
|
||||
return pumpWrappedMatch{}, count
|
||||
}
|
||||
ret.IsBuy = isPumpWrappedBuy(step.Swap.Kind)
|
||||
}
|
||||
return ret, count
|
||||
}
|
||||
|
||||
func findPumpFunMint(staticKeys []solana.PublicKey, accounts []uint8) (solana.PublicKey, bool, error) {
|
||||
for i, acctIdx := range accounts {
|
||||
key, err := getStaticKey(staticKeys, int(acctIdx))
|
||||
@@ -953,6 +1019,43 @@ func findPumpFunMint(staticKeys []solana.PublicKey, accounts []uint8) (solana.Pu
|
||||
return solana.PublicKey{}, false, nil
|
||||
}
|
||||
|
||||
func jupiterV6SourceDestMints(msg versionedMessage, instruction compiledInstruction, disc []byte) (solana.PublicKey, solana.PublicKey, bool, error) {
|
||||
switch {
|
||||
case bytes.Equal(disc, jupiterRouteV2),
|
||||
bytes.Equal(disc, jupiterSharedAccountsRouteV2),
|
||||
bytes.Equal(disc, jupiterExactOutRouteV2),
|
||||
bytes.Equal(disc, jupiterSharedAccountsExactOutRouteV2):
|
||||
if len(instruction.Accounts) < 5 {
|
||||
return solana.PublicKey{}, solana.PublicKey{}, false, fmt.Errorf("not enough accounts for jupiter v6 v2 instruction")
|
||||
}
|
||||
src, err := getStaticKey(msg.StaticAccountKeys, int(instruction.Accounts[3]))
|
||||
if err != nil {
|
||||
return solana.PublicKey{}, solana.PublicKey{}, false, err
|
||||
}
|
||||
dst, err := getStaticKey(msg.StaticAccountKeys, int(instruction.Accounts[4]))
|
||||
if err != nil {
|
||||
return solana.PublicKey{}, solana.PublicKey{}, false, err
|
||||
}
|
||||
return src, dst, true, nil
|
||||
case bytes.Equal(disc, jupiterSharedAccountsRoute),
|
||||
bytes.Equal(disc, jupiterSharedAccountsExactOutRoute):
|
||||
if len(instruction.Accounts) < 9 {
|
||||
return solana.PublicKey{}, solana.PublicKey{}, false, fmt.Errorf("not enough accounts for jupiter v6 shared accounts instruction")
|
||||
}
|
||||
src, err := getStaticKey(msg.StaticAccountKeys, int(instruction.Accounts[7]))
|
||||
if err != nil {
|
||||
return solana.PublicKey{}, solana.PublicKey{}, false, err
|
||||
}
|
||||
dst, err := getStaticKey(msg.StaticAccountKeys, int(instruction.Accounts[8]))
|
||||
if err != nil {
|
||||
return solana.PublicKey{}, solana.PublicKey{}, false, err
|
||||
}
|
||||
return src, dst, true, nil
|
||||
default:
|
||||
return solana.PublicKey{}, solana.PublicKey{}, false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// only decodes inputIdx = 0 container pumpSwap instructions for now
|
||||
func parseJupiterV6Instruction(tx *versionedTransaction, instructionIndex int) (*TxSignal, error) {
|
||||
msg := tx.Message
|
||||
@@ -973,9 +1076,13 @@ func parseJupiterV6Instruction(tx *versionedTransaction, instructionIndex int) (
|
||||
var (
|
||||
sourceMint solana.PublicKey
|
||||
inputAmount uint64
|
||||
routeIn uint64
|
||||
routeOut uint64
|
||||
planCount int
|
||||
wrapped pumpWrappedMatch
|
||||
wrappedCnt int
|
||||
wrappedAny pumpWrappedMatch
|
||||
wrappedAnyC int
|
||||
exactOut bool
|
||||
err error
|
||||
)
|
||||
@@ -990,6 +1097,9 @@ func parseJupiterV6Instruction(tx *versionedTransaction, instructionIndex int) (
|
||||
}
|
||||
inputAmount, planCount = pumpSwapSellAtIdx0V2(args.In, args.Plan)
|
||||
wrapped, wrappedCnt = pumpWrappedAtIdx0V2(args.In, args.Out, args.Plan)
|
||||
wrappedAny, wrappedAnyC = pumpWrappedAnyV2(args.Plan)
|
||||
routeIn = args.In
|
||||
routeOut = args.Out
|
||||
case bytes.Equal(disc, jupiterSharedAccountsRouteV2):
|
||||
args, err := decodeJupiterV6SharedAccountsRouteV2Arg(instruction.Data[8:])
|
||||
if err != nil {
|
||||
@@ -997,6 +1107,9 @@ func parseJupiterV6Instruction(tx *versionedTransaction, instructionIndex int) (
|
||||
}
|
||||
inputAmount, planCount = pumpSwapSellAtIdx0V2(args.In, args.RoutePlan)
|
||||
wrapped, wrappedCnt = pumpWrappedAtIdx0V2(args.In, args.QuotedOut, args.RoutePlan)
|
||||
wrappedAny, wrappedAnyC = pumpWrappedAnyV2(args.RoutePlan)
|
||||
routeIn = args.In
|
||||
routeOut = args.QuotedOut
|
||||
case bytes.Equal(disc, jupiterExactOutRouteV2):
|
||||
args, err := decodeJupiterV6ExactOutRouteV2Arg(instruction.Data[8:])
|
||||
if err != nil {
|
||||
@@ -1004,6 +1117,9 @@ func parseJupiterV6Instruction(tx *versionedTransaction, instructionIndex int) (
|
||||
}
|
||||
exactOut = true
|
||||
wrapped, wrappedCnt = pumpWrappedAtIdx0V2(args.QuotedIn, args.Out, args.RoutePlan)
|
||||
wrappedAny, wrappedAnyC = pumpWrappedAnyV2(args.RoutePlan)
|
||||
routeIn = args.QuotedIn
|
||||
routeOut = args.Out
|
||||
case bytes.Equal(disc, jupiterSharedAccountsExactOutRouteV2):
|
||||
args, err := decodeJupiterV6SharedAccountsExactOutRouteV2Arg(instruction.Data[8:])
|
||||
if err != nil {
|
||||
@@ -1011,6 +1127,9 @@ func parseJupiterV6Instruction(tx *versionedTransaction, instructionIndex int) (
|
||||
}
|
||||
exactOut = true
|
||||
wrapped, wrappedCnt = pumpWrappedAtIdx0V2(args.QuotedIn, args.Out, args.RoutePlan)
|
||||
wrappedAny, wrappedAnyC = pumpWrappedAnyV2(args.RoutePlan)
|
||||
routeIn = args.QuotedIn
|
||||
routeOut = args.Out
|
||||
case bytes.Equal(disc, jupiterRoute):
|
||||
args, err := decodeJupiterV6RouteArg(instruction.Data[8:])
|
||||
if err != nil {
|
||||
@@ -1019,6 +1138,9 @@ func parseJupiterV6Instruction(tx *versionedTransaction, instructionIndex int) (
|
||||
_ = args
|
||||
inputAmount, planCount = pumpSwapSellAtIdx0(args.In, args.Plan)
|
||||
wrapped, wrappedCnt = pumpWrappedAtIdx0(args.In, args.QuotedOut, args.Plan)
|
||||
wrappedAny, wrappedAnyC = pumpWrappedAny(args.Plan)
|
||||
routeIn = args.In
|
||||
routeOut = args.QuotedOut
|
||||
case bytes.Equal(disc, jupiterSharedAccountsExactOutRoute):
|
||||
args, err := decodeJupiterV6SharedAccountsExactOutRouteArg(instruction.Data[8:])
|
||||
if err != nil {
|
||||
@@ -1026,6 +1148,9 @@ func parseJupiterV6Instruction(tx *versionedTransaction, instructionIndex int) (
|
||||
}
|
||||
exactOut = true
|
||||
wrapped, wrappedCnt = pumpWrappedAtIdx0(args.QuotedIn, args.Out, args.Plan)
|
||||
wrappedAny, wrappedAnyC = pumpWrappedAny(args.Plan)
|
||||
routeIn = args.QuotedIn
|
||||
routeOut = args.Out
|
||||
case bytes.Equal(disc, jupiterSharedAccountsRoute):
|
||||
args, err := decodeJupiterV6SharedAccountsRouteArg(instruction.Data[8:])
|
||||
if err != nil {
|
||||
@@ -1034,9 +1159,74 @@ func parseJupiterV6Instruction(tx *versionedTransaction, instructionIndex int) (
|
||||
_ = args
|
||||
inputAmount, planCount = pumpSwapSellAtIdx0(args.In, args.Plan)
|
||||
wrapped, wrappedCnt = pumpWrappedAtIdx0(args.In, args.QuotedOut, args.Plan)
|
||||
wrappedAny, wrappedAnyC = pumpWrappedAny(args.Plan)
|
||||
routeIn = args.In
|
||||
routeOut = args.QuotedOut
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
if bytes.Equal(disc, jupiterRoute) {
|
||||
if len(instruction.Accounts) < 13 {
|
||||
return nil, nil
|
||||
}
|
||||
destMint, err := getStaticKey(tx.Message.StaticAccountKeys, int(instruction.Accounts[5]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isToken1Mint(destMint) {
|
||||
pumpKey, err := getStaticKey(tx.Message.StaticAccountKeys, int(instruction.Accounts[9]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !pumpKey.Equals(pumpProgramID) {
|
||||
return nil, nil
|
||||
}
|
||||
token0Mint, err := getStaticKey(tx.Message.StaticAccountKeys, int(instruction.Accounts[12]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
token0Amount := decimal.Zero
|
||||
if routeIn > 0 {
|
||||
token0Amount = formatTokenAmount(routeIn)
|
||||
}
|
||||
return &TxSignal{
|
||||
TxHash: tx.Signatures[0].String(),
|
||||
Maker: tx.Message.StaticAccountKeys[0].String(),
|
||||
Token0Address: token0Mint.String(),
|
||||
Token1Address: destMint.String(),
|
||||
Token0Amount: token0Amount,
|
||||
Token1Amount: decimal.Zero,
|
||||
Program: "Pump",
|
||||
Event: "sell",
|
||||
IsToken2022: false,
|
||||
IsMayhemMode: false,
|
||||
ExactSOL: false,
|
||||
Block: tx.Block,
|
||||
Token0AmountUint64: routeIn,
|
||||
Token1AmountUint64: 0,
|
||||
}, nil
|
||||
}
|
||||
token0Amount := decimal.Zero
|
||||
if routeOut > 0 {
|
||||
token0Amount = formatTokenAmount(routeOut)
|
||||
}
|
||||
return &TxSignal{
|
||||
TxHash: tx.Signatures[0].String(),
|
||||
Maker: tx.Message.StaticAccountKeys[0].String(),
|
||||
Token0Address: destMint.String(),
|
||||
Token1Address: wsolMint,
|
||||
Token0Amount: token0Amount,
|
||||
Token1Amount: decimal.Zero,
|
||||
Program: "Pump",
|
||||
Event: "buy",
|
||||
IsToken2022: false,
|
||||
IsMayhemMode: false,
|
||||
ExactSOL: false,
|
||||
Block: tx.Block,
|
||||
Token0AmountUint64: routeOut,
|
||||
Token1AmountUint64: 0,
|
||||
}, nil
|
||||
}
|
||||
if wrappedCnt > 1 {
|
||||
logger.Warn("pumpWrapped at inputIdx=0: multiple instances found", "tx", tx.Signatures[0].String(), "planCount", wrappedCnt)
|
||||
}
|
||||
@@ -1048,6 +1238,31 @@ func parseJupiterV6Instruction(tx *versionedTransaction, instructionIndex int) (
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
token1Mint := solana.WrappedSol
|
||||
token1IsStable := false
|
||||
srcMint, dstMint, ok, err := jupiterV6SourceDestMints(tx.Message, instruction, disc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isJupiterV6Token1RequiredDisc(disc) {
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
if !isToken1Mint(srcMint) && !isToken1Mint(dstMint) {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
if srcMint.Equals(solana.WrappedSol) || dstMint.Equals(solana.WrappedSol) {
|
||||
token1Mint = solana.WrappedSol
|
||||
} else if isStableMint(srcMint) {
|
||||
token1Mint = srcMint
|
||||
token1IsStable = true
|
||||
} else if isStableMint(dstMint) {
|
||||
token1Mint = dstMint
|
||||
token1IsStable = true
|
||||
}
|
||||
}
|
||||
event := "sell"
|
||||
exactSol := false
|
||||
var (
|
||||
@@ -1070,13 +1285,106 @@ func parseJupiterV6Instruction(tx *versionedTransaction, instructionIndex int) (
|
||||
}
|
||||
token1Amount := decimal.Zero
|
||||
if token1AmountUint64 > 0 {
|
||||
if token1IsStable {
|
||||
token1Amount = formatTokenAmount(token1AmountUint64)
|
||||
} else {
|
||||
token1Amount = formatSolAmount(token1AmountUint64)
|
||||
}
|
||||
}
|
||||
token1Address := wsolMint
|
||||
if token1IsStable {
|
||||
token1Address = token1Mint.String()
|
||||
}
|
||||
return &TxSignal{
|
||||
TxHash: tx.Signatures[0].String(),
|
||||
Maker: tx.Message.StaticAccountKeys[0].String(),
|
||||
Token0Address: mint.String(),
|
||||
Token1Address: wsolMint,
|
||||
Token1Address: token1Address,
|
||||
Token0Amount: token0Amount,
|
||||
Token1Amount: token1Amount,
|
||||
Program: "Pump",
|
||||
Event: event,
|
||||
IsToken2022: false,
|
||||
IsMayhemMode: false,
|
||||
ExactSOL: exactSol,
|
||||
Block: tx.Block,
|
||||
Token0AmountUint64: token0AmountUint64,
|
||||
Token1AmountUint64: token1AmountUint64,
|
||||
}, nil
|
||||
}
|
||||
if wrappedAnyC > 1 {
|
||||
logger.Warn("pumpWrapped at inputIdx!=0: multiple instances found", "tx", tx.Signatures[0].String(), "planCount", wrappedAnyC)
|
||||
}
|
||||
if wrappedAnyC == 1 && routeIn > 0 && routeOut > 0 {
|
||||
mint, ok, err := findPumpFunMint(tx.Message.StaticAccountKeys, instruction.Accounts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
token1Mint := solana.WrappedSol
|
||||
token1IsStable := false
|
||||
srcMint, dstMint, ok, err := jupiterV6SourceDestMints(tx.Message, instruction, disc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isJupiterV6Token1RequiredDisc(disc) {
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
if !isToken1Mint(srcMint) && !isToken1Mint(dstMint) {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
if srcMint.Equals(solana.WrappedSol) || dstMint.Equals(solana.WrappedSol) {
|
||||
token1Mint = solana.WrappedSol
|
||||
} else if isStableMint(srcMint) {
|
||||
token1Mint = srcMint
|
||||
token1IsStable = true
|
||||
} else if isStableMint(dstMint) {
|
||||
token1Mint = dstMint
|
||||
token1IsStable = true
|
||||
}
|
||||
}
|
||||
event := "sell"
|
||||
exactSol := false
|
||||
var (
|
||||
token0AmountUint64 uint64
|
||||
token1AmountUint64 uint64
|
||||
)
|
||||
if wrappedAny.IsBuy {
|
||||
event = "buy"
|
||||
exactSol = !exactOut
|
||||
token0AmountUint64 = routeOut
|
||||
token1AmountUint64 = routeIn
|
||||
} else {
|
||||
exactSol = exactOut && routeOut > 0
|
||||
token0AmountUint64 = routeIn
|
||||
token1AmountUint64 = routeOut
|
||||
}
|
||||
token0Amount := decimal.Zero
|
||||
if token0AmountUint64 > 0 {
|
||||
token0Amount = formatTokenAmount(token0AmountUint64)
|
||||
}
|
||||
token1Amount := decimal.Zero
|
||||
if token1AmountUint64 > 0 {
|
||||
if token1IsStable {
|
||||
token1Amount = formatTokenAmount(token1AmountUint64)
|
||||
} else {
|
||||
token1Amount = formatSolAmount(token1AmountUint64)
|
||||
}
|
||||
}
|
||||
token1Address := wsolMint
|
||||
if token1IsStable {
|
||||
token1Address = token1Mint.String()
|
||||
}
|
||||
return &TxSignal{
|
||||
TxHash: tx.Signatures[0].String(),
|
||||
Maker: tx.Message.StaticAccountKeys[0].String(),
|
||||
Token0Address: mint.String(),
|
||||
Token1Address: token1Address,
|
||||
Token0Amount: token0Amount,
|
||||
Token1Amount: token1Amount,
|
||||
Program: "Pump",
|
||||
|
||||
Reference in New Issue
Block a user