Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c732bb2b46 | ||
| 99ff9968bd | |||
|
|
8c98ec7875 | ||
|
|
e6922e4561 | ||
| 4afa412231 |
62
cmd/debug_jupv6/main.go
Normal file
62
cmd/debug_jupv6/main.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
hexData := "bb64facc31c4af14be34e6edcc0000006f03a4df67000000b903320000000300000064342100024b00000000dc0500026310270203"
|
||||||
|
b, err := hex.DecodeString(hexData)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
payload := b[8:]
|
||||||
|
|
||||||
|
off := 0
|
||||||
|
read := func(n int) []byte {
|
||||||
|
if off+n > len(payload) {
|
||||||
|
fmt.Printf("OOB read: off=%d n=%d len=%d\n", off, n, len(payload))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
out := payload[off : off+n]
|
||||||
|
off += n
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
u8 := func() uint8 { return read(1)[0] }
|
||||||
|
leU16 := func() uint16 {
|
||||||
|
b := read(2)
|
||||||
|
return uint16(b[0]) | uint16(b[1])<<8
|
||||||
|
}
|
||||||
|
leU32 := func() uint32 {
|
||||||
|
b := read(4)
|
||||||
|
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||||
|
}
|
||||||
|
leU64 := func() uint64 {
|
||||||
|
b := read(8)
|
||||||
|
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
|
||||||
|
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("payload len=%d\n", len(payload))
|
||||||
|
amountIn := leU64()
|
||||||
|
quotedOut := leU64()
|
||||||
|
slippage := leU16()
|
||||||
|
platform := leU16()
|
||||||
|
posSlip := leU16()
|
||||||
|
fmt.Printf("in=%d out=%d slip=%d plat=%d pos=%d\n", amountIn, quotedOut, slippage, platform, posSlip)
|
||||||
|
|
||||||
|
planLen := leU32()
|
||||||
|
fmt.Printf("planLen=%d\n", planLen)
|
||||||
|
for i := uint32(0); i < planLen; i++ {
|
||||||
|
swapTag := u8()
|
||||||
|
fmt.Printf("step[%d] swapTag=%d (0x%02x) off=%d\n", i, swapTag, swapTag, off)
|
||||||
|
// payload depends on swapTag; we don't know, so just print next few bytes and stop
|
||||||
|
bps := leU16()
|
||||||
|
inIdx := u8()
|
||||||
|
outIdx := u8()
|
||||||
|
fmt.Printf(" bps=%d inIdx=%d outIdx=%d off=%d\n", bps, inIdx, outIdx, off)
|
||||||
|
}
|
||||||
|
fmt.Printf("done off=%d\n", off)
|
||||||
|
}
|
||||||
@@ -2,13 +2,15 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/gagliardetto/solana-go/rpc"
|
||||||
|
|
||||||
"github.com/samlior/libsam/pkg/shreder"
|
"github.com/samlior/libsam/pkg/shreder"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,13 +19,25 @@ func main() {
|
|||||||
if url == "" {
|
if url == "" {
|
||||||
panic("URL is not set")
|
panic("URL is not set")
|
||||||
}
|
}
|
||||||
|
rpcUrl := os.Getenv("RPC_URL")
|
||||||
|
if rpcUrl == "" {
|
||||||
|
panic("RPC_URL is not set")
|
||||||
|
}
|
||||||
|
rpcClient := rpc.New(rpcUrl)
|
||||||
|
shreder.SetLogLevel(slog.LevelDebug)
|
||||||
shrederClient, cleanup, err := shreder.NewShrederClient(
|
shrederClient, cleanup, err := shreder.NewShrederClient(
|
||||||
url,
|
url,
|
||||||
|
rpcClient,
|
||||||
map[string]*shreder.SubscribeRequestFilterTransactions{
|
map[string]*shreder.SubscribeRequestFilterTransactions{
|
||||||
"pumpfunamm": {
|
"pumpfunamm": {
|
||||||
AccountRequired: []string{
|
//AccountRequired: []string{
|
||||||
|
// "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA",
|
||||||
|
//},
|
||||||
|
AccountInclude: []string{
|
||||||
"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA",
|
"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA",
|
||||||
|
"GS4CU59F31iL7aR2Q8zVS8DRrcRnXX1yjQ66TqNVQnaR", //Event Authority
|
||||||
|
"5PHirr8joyTMp9JMm6nW7hNDVyEYdkzDqazxPD7RaTjx", // Fee Config
|
||||||
|
"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ", // pump fee program
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"photon": {
|
"photon": {
|
||||||
@@ -31,6 +45,11 @@ func main() {
|
|||||||
"BSfD6SHZigAfDWSjzD5Q41jw8LmKwtmjskPH9XW1mrRW",
|
"BSfD6SHZigAfDWSjzD5Q41jw8LmKwtmjskPH9XW1mrRW",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"jupiterV6": {
|
||||||
|
AccountRequired: []string{
|
||||||
|
"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4",
|
||||||
|
},
|
||||||
|
},
|
||||||
// TODO: axiom, gmgn, etc.
|
// TODO: axiom, gmgn, etc.
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -63,8 +82,13 @@ func main() {
|
|||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
case txBatch := <-txCh:
|
case txBatch := <-txCh:
|
||||||
jsonData, _ := json.MarshalIndent(txBatch, "", " ")
|
//jsonData, _ := json.MarshalIndent(txBatch, "", " ")
|
||||||
fmt.Println(string(jsonData))
|
for _, tx := range txBatch {
|
||||||
|
if tx.Label == "jupiterV6" {
|
||||||
|
fmt.Println("===============", tx.TxHash, tx.Token0Address, tx.Token0Amount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//fmt.Println(txBatch[0].TxHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
go.mod
5
go.mod
@@ -5,7 +5,9 @@ go 1.25.1
|
|||||||
require (
|
require (
|
||||||
github.com/BlockRazorinc/solana-trader-client-go v0.0.0-20250722092120-44561cb37455
|
github.com/BlockRazorinc/solana-trader-client-go v0.0.0-20250722092120-44561cb37455
|
||||||
github.com/gagliardetto/solana-go v1.12.0
|
github.com/gagliardetto/solana-go v1.12.0
|
||||||
|
github.com/mr-tron/base58 v1.2.0
|
||||||
github.com/near/borsh-go v0.3.2-0.20220516180422-1ff87d108454
|
github.com/near/borsh-go v0.3.2-0.20220516180422-1ff87d108454
|
||||||
|
github.com/panjf2000/ants/v2 v2.11.4
|
||||||
github.com/shopspring/decimal v1.4.0
|
github.com/shopspring/decimal v1.4.0
|
||||||
google.golang.org/grpc v1.75.0
|
google.golang.org/grpc v1.75.0
|
||||||
google.golang.org/protobuf v1.36.10
|
google.golang.org/protobuf v1.36.10
|
||||||
@@ -20,6 +22,7 @@ require (
|
|||||||
github.com/gagliardetto/binary v0.8.0 // indirect
|
github.com/gagliardetto/binary v0.8.0 // indirect
|
||||||
github.com/gagliardetto/treeout v0.1.4 // indirect
|
github.com/gagliardetto/treeout v0.1.4 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.13.6 // indirect
|
github.com/klauspost/compress v1.13.6 // indirect
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
|
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
|
||||||
@@ -29,7 +32,6 @@ require (
|
|||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect
|
github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect
|
||||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
|
||||||
github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect
|
github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect
|
||||||
go.mongodb.org/mongo-driver v1.12.2 // indirect
|
go.mongodb.org/mongo-driver v1.12.2 // indirect
|
||||||
go.uber.org/atomic v1.7.0 // indirect
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
@@ -38,6 +40,7 @@ require (
|
|||||||
go.uber.org/zap v1.21.0 // indirect
|
go.uber.org/zap v1.21.0 // indirect
|
||||||
golang.org/x/crypto v0.44.0 // indirect
|
golang.org/x/crypto v0.44.0 // indirect
|
||||||
golang.org/x/net v0.47.0 // indirect
|
golang.org/x/net v0.47.0 // indirect
|
||||||
|
golang.org/x/sync v0.18.0 // indirect
|
||||||
golang.org/x/sys v0.38.0 // indirect
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
golang.org/x/term v0.37.0 // indirect
|
golang.org/x/term v0.37.0 // indirect
|
||||||
golang.org/x/text v0.31.0 // indirect
|
golang.org/x/text v0.31.0 // indirect
|
||||||
|
|||||||
9
go.sum
9
go.sum
@@ -36,6 +36,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX
|
|||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
@@ -68,6 +70,8 @@ github.com/near/borsh-go v0.3.2-0.20220516180422-1ff87d108454 h1:lFN7TVecCMbCHVN
|
|||||||
github.com/near/borsh-go v0.3.2-0.20220516180422-1ff87d108454/go.mod h1:NeMochZp7jN/pYFuxLkrZtmLqbADmnp/y1+/dL+AsyQ=
|
github.com/near/borsh-go v0.3.2-0.20220516180422-1ff87d108454/go.mod h1:NeMochZp7jN/pYFuxLkrZtmLqbADmnp/y1+/dL+AsyQ=
|
||||||
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
|
github.com/panjf2000/ants/v2 v2.11.4 h1:UJQbtN1jIcI5CYNocTj0fuAUYvsLjPoYi0YuhqV/Y48=
|
||||||
|
github.com/panjf2000/ants/v2 v2.11.4/go.mod h1:8u92CYMUc6gyvTIw8Ru7Mt7+/ESnJahz5EVtqfrilek=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@@ -81,8 +85,9 @@ github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:Vl
|
|||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
|
github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
|
||||||
github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
|
github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
@@ -140,6 +145,8 @@ golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
|||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||||
|
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
|||||||
124
pkg/consts/swqos_fee_addresses.go
Normal file
124
pkg/consts/swqos_fee_addresses.go
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package consts
|
||||||
|
|
||||||
|
import "github.com/samlior/libsam/pkg/enum"
|
||||||
|
|
||||||
|
var SWQoSFeeAddresses = map[string]string{
|
||||||
|
"96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5": enum.SWQoSAgentJito,
|
||||||
|
"HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe": enum.SWQoSAgentJito,
|
||||||
|
"Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY": enum.SWQoSAgentJito,
|
||||||
|
"ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49": enum.SWQoSAgentJito,
|
||||||
|
"DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh": enum.SWQoSAgentJito,
|
||||||
|
"ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt": enum.SWQoSAgentJito,
|
||||||
|
"DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL": enum.SWQoSAgentJito,
|
||||||
|
"3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT": enum.SWQoSAgentJito,
|
||||||
|
"6fQaVhYZA4w3MBSXjJ81Vf6W1EDYeUPXpgVQ6UQyU1Av": enum.SWQoSAgent0slot,
|
||||||
|
"4HiwLEP2Bzqj3hM2ENxJuzhcPCdsafwiet3oGkMkuQY4": enum.SWQoSAgent0slot,
|
||||||
|
"7toBU3inhmrARGngC7z6SjyP85HgGMmCTEwGNRAcYnEK": enum.SWQoSAgent0slot,
|
||||||
|
"8mR3wB1nh4D6J9RUCugxUpc6ya8w38LPxZ3ZjcBhgzws": enum.SWQoSAgent0slot,
|
||||||
|
"6SiVU5WEwqfFapRuYCndomztEwDjvS5xgtEof3PLEGm9": enum.SWQoSAgent0slot,
|
||||||
|
"TpdxgNJBWZRL8UXF5mrEsyWxDWx9HQexA9P1eTWQ42p": enum.SWQoSAgent0slot,
|
||||||
|
"D8f3WkQu6dCF33cZxuAsrKHrGsqGP2yvAHf8mX6RXnwf": enum.SWQoSAgent0slot,
|
||||||
|
"GQPFicsy3P3NXxB5piJohoxACqTvWE9fKpLgdsMduoHE": enum.SWQoSAgent0slot,
|
||||||
|
"Ey2JEr8hDkgN8qKJGrLf2yFjRhW7rab99HVxwi5rcvJE": enum.SWQoSAgent0slot,
|
||||||
|
"4iUgjMT8q2hNZnLuhpqZ1QtiV8deFPy2ajvvjEpKKgsS": enum.SWQoSAgent0slot,
|
||||||
|
"3Rz8uD83QsU8wKvZbgWAPvCNDU6Fy8TSZTMcPm3RB6zt": enum.SWQoSAgent0slot,
|
||||||
|
"DiTmWENJsHQdawVUUKnUXkconcpW4Jv52TnMWhkncF6t": enum.SWQoSAgent0slot,
|
||||||
|
"HRyRhQ86t3H4aAtgvHVpUJmw64BDrb61gRiKcdKUXs5c": enum.SWQoSAgent0slot,
|
||||||
|
"7y4whZmw388w1ggjToDLSBLv47drw5SUXcLk6jtmwixd": enum.SWQoSAgent0slot,
|
||||||
|
"J9BMEWFbCBEjtQ1fG5Lo9kouX1HfrKQxeUxetwXrifBw": enum.SWQoSAgent0slot,
|
||||||
|
"8U1JPQh3mVQ4F5jwRdFTBzvNRQaYFQppHQYoH38DJGSQ": enum.SWQoSAgent0slot,
|
||||||
|
"Eb2KpSC8uMt9GmzyAEm5Eb1AAAgTjRaXWFjKyFXHZxF3": enum.SWQoSAgent0slot,
|
||||||
|
"FCjUJZ1qozm1e8romw216qyfQMaaWKxWsuySnumVCCNe": enum.SWQoSAgent0slot,
|
||||||
|
"ENxTEjSQ1YabmUpXAdCgevnHQ9MHdLv8tzFiuiYJqa13": enum.SWQoSAgent0slot,
|
||||||
|
"6rYLG55Q9RpsPGvqdPNJs4z5WTxJVatMB8zV3WJhs5EK": enum.SWQoSAgent0slot,
|
||||||
|
"Cix2bHfqPcKcM233mzxbLk14kSggUUiz2A87fJtGivXr": enum.SWQoSAgent0slot,
|
||||||
|
"HWEoBxYs7ssKuudEjzjmpfJVX7Dvi7wescFsVx2L5yoY": enum.SWQoSAgentBlocxRoute,
|
||||||
|
"HZTmLyC683y74TW3HtGbNX5orxjm2sPuZBEYwwSgAM8v": enum.SWQoSAgentBlocxRoute,
|
||||||
|
"FogxVNs6Mm2w9rnGL1vkARSwJxvLE8mujTv3LK8RnUhF": enum.SWQoSAgentBlocxRoute,
|
||||||
|
"3UQUKjhMKaY2S6bjcQD6yHB7utcZt5bfarRCmctpRtUd": enum.SWQoSAgentBlocxRoute,
|
||||||
|
"TEMPaMeCRFAS9EKF53Jd6KpHxgL47uWLcpFArU1Fanq": enum.SWQoSAgentNozomi,
|
||||||
|
"noz3jAjPiHuBPqiSPkkugaJDkJscPuRhYnSpbi8UvC4": enum.SWQoSAgentNozomi,
|
||||||
|
"noz3str9KXfpKknefHji8L1mPgimezaiUyCHYMDv1GE": enum.SWQoSAgentNozomi,
|
||||||
|
"noz6uoYCDijhu1V7cutCpwxNiSovEwLdRHPwmgCGDNo": enum.SWQoSAgentNozomi,
|
||||||
|
"noz9EPNcT7WH6Sou3sr3GGjHQYVkN3DNirpbvDkv9YJ": enum.SWQoSAgentNozomi,
|
||||||
|
"nozc5yT15LazbLTFVZzoNZCwjh3yUtW86LoUyqsBu4L": enum.SWQoSAgentNozomi,
|
||||||
|
"nozFrhfnNGoyqwVuwPAW4aaGqempx4PU6g6D9CJMv7Z": enum.SWQoSAgentNozomi,
|
||||||
|
"nozievPk7HyK1Rqy1MPJwVQ7qQg2QoJGyP71oeDwbsu": enum.SWQoSAgentNozomi,
|
||||||
|
"noznbgwYnBLDHu8wcQVCEw6kDrXkPdKkydGJGNXGvL7": enum.SWQoSAgentNozomi,
|
||||||
|
"nozNVWs5N8mgzuD3qigrCG2UoKxZttxzZ85pvAQVrbP": enum.SWQoSAgentNozomi,
|
||||||
|
"nozpEGbwx4BcGp6pvEdAh1JoC2CQGZdU6HbNP1v2p6P": enum.SWQoSAgentNozomi,
|
||||||
|
"nozrhjhkCr3zXT3BiT4WCodYCUFeQvcdUkM7MqhKqge": enum.SWQoSAgentNozomi,
|
||||||
|
"nozrwQtWhEdrA6W8dkbt9gnUaMs52PdAv5byipnadq3": enum.SWQoSAgentNozomi,
|
||||||
|
"nozUacTVWub3cL4mJmGCYjKZTnE9RbdY5AP46iQgbPJ": enum.SWQoSAgentNozomi,
|
||||||
|
"nozWCyTPppJjRuw2fpzDhhWbW355fzosWSzrrMYB1Qk": enum.SWQoSAgentNozomi,
|
||||||
|
"nozWNju6dY353eMkMqURqwQEoM3SFgEKC6psLCSfUne": enum.SWQoSAgentNozomi,
|
||||||
|
"nozxNBgWohjR75vdspfxR5H9ceC7XXH99xpxhVGt3Bb": enum.SWQoSAgentNozomi,
|
||||||
|
"NextbLoCkVtMGcV47JzewQdvBpLqT9TxQFozQkN98pE": enum.SWQoSAgentNextBlock,
|
||||||
|
"NexTbLoCkWykbLuB1NkjXgFWkX9oAtcoagQegygXXA2": enum.SWQoSAgentNextBlock,
|
||||||
|
"NeXTBLoCKs9F1y5PJS9CKrFNNLU1keHW71rfh7KgA1X": enum.SWQoSAgentNextBlock,
|
||||||
|
"NexTBLockJYZ7QD7p2byrUa6df8ndV2WSd8GkbWqfbb": enum.SWQoSAgentNextBlock,
|
||||||
|
"neXtBLock1LeC67jYd1QdAa32kbVeubsfPNTJC1V5At": enum.SWQoSAgentNextBlock,
|
||||||
|
"nEXTBLockYgngeRmRrjDV31mGSekVPqZoMGhQEZtPVG": enum.SWQoSAgentNextBlock,
|
||||||
|
"NEXTbLoCkB51HpLBLojQfpyVAMorm3zzKg7w9NFdqid": enum.SWQoSAgentNextBlock,
|
||||||
|
"nextBLoCkPMgmG8ZgJtABeScP35qLa2AMCNKntAP7Xc": enum.SWQoSAgentNextBlock,
|
||||||
|
"4ACfpUFoaSD9bfPdeu6DBt89gB6ENTeHBXCAi87NhDEE": enum.SWQoSAgentHelius,
|
||||||
|
"D2L6yPZ2FmmmTKPgzaMKdhu6EWZcTpLy1Vhx8uvZe7NZ": enum.SWQoSAgentHelius,
|
||||||
|
"9bnz4RShgq1hAnLnZbP8kbgBg1kEmcJBYQq3gQbmnSta": enum.SWQoSAgentHelius,
|
||||||
|
"5VY91ws6B2hMmBFRsXkoAAdsPHBJwRfBht4DXox3xkwn": enum.SWQoSAgentHelius,
|
||||||
|
"2nyhqdwKcJZR2vcqCyrYsaPVdAnFoJjiksCXJ7hfEYgD": enum.SWQoSAgentHelius,
|
||||||
|
"2q5pghRs6arqVjRvT5gfgWfWcHWmw1ZuCzphgd5KfWGJ": enum.SWQoSAgentHelius,
|
||||||
|
"wyvPkWjVZz1M8fHQnMMCDTQDbkManefNNhweYk5WkcF": enum.SWQoSAgentHelius,
|
||||||
|
"3KCKozbAaF75qEU33jtzozcJ29yJuaLJTy2jFdzUY8bT": enum.SWQoSAgentHelius,
|
||||||
|
"4vieeGHPYPG2MmyPRcYjdiDmmhN3ww7hsFNap8pVN3Ey": enum.SWQoSAgentHelius,
|
||||||
|
"4TQLFNWK8AovT1gFvda5jfw2oJeRMKEmw7aH6MGBJ3or": enum.SWQoSAgentHelius,
|
||||||
|
"node1PqAa3BWWzUnTHVbw8NJHC874zn9ngAkXjgWEej": enum.SWQoSAgentNode1,
|
||||||
|
"node1UzzTxAAeBTpfZkQPJXBAqixsbdth11ba1NXLBG": enum.SWQoSAgentNode1,
|
||||||
|
"node1Qm1bV4fwYnCurP8otJ9s5yrkPq7SPZ5uhj3Tsv": enum.SWQoSAgentNode1,
|
||||||
|
"node1PUber6SFmSQgvf2ECmXsHP5o3boRSGhvJyPMX1": enum.SWQoSAgentNode1,
|
||||||
|
"node1AyMbeqiVN6eoQzEAwCA6Pk826hrdqdAHR7cdJ3": enum.SWQoSAgentNode1,
|
||||||
|
"node1YtWCoTwwVYTFLfS19zquRQzYX332hs1HEuRBjC": enum.SWQoSAgentNode1,
|
||||||
|
"node1EoLojAvoUmyDytcvgdXs6GPtY3zpQXPCRVncEA": enum.SWQoSAgentNode1,
|
||||||
|
"node1CVxtFas2Pw5Vcf86Pq89Hqx4jveo1ntY7ARFMK": enum.SWQoSAgentNode1,
|
||||||
|
"node1E3hguapYA18HCpEEkRHQmLNiyv9pdfE9s2zo5X": enum.SWQoSAgentNode1,
|
||||||
|
"node1zrVjcY2XB3Au8qYj5MxjbNfGu3baHaqZMkPM7Z": enum.SWQoSAgentNode1,
|
||||||
|
"node1FdMPnJBN7QTuhzNw3VS823nxFuDTizrrbcEqzp": enum.SWQoSAgentNode1,
|
||||||
|
"node1VwH169UqyJHr5MYCH3EBuwrdvn5KHXAkhEEfav": enum.SWQoSAgentNode1,
|
||||||
|
"node1L7Xat2tSkRNNi6TSuUScMYfj64ovhr2aceJm9g": enum.SWQoSAgentNode1,
|
||||||
|
"FLasHstqx11M8W56zrSEqkCyhMCCpr6ze6Mjdvqope5s": enum.SWQoSAgentFlashBlock,
|
||||||
|
"FLasHXTqrbNvpWFB6grN47HGZfK6pze9HLNTgbukfPSk": enum.SWQoSAgentFlashBlock,
|
||||||
|
"FLashhsorBmM9dLpuq6qATawcpqk1Y2aqaZfkd48iT3W": enum.SWQoSAgentFlashBlock,
|
||||||
|
"FLASHRzANfcAKDuQ3RXv9hbkBy4WVEKDzoAgxJ56DiE4": enum.SWQoSAgentFlashBlock,
|
||||||
|
"FLAsHZTRcf3Dy1APaz6j74ebdMC6Xx4g6i9YxjyrDybR": enum.SWQoSAgentFlashBlock,
|
||||||
|
"FLAshyAyBcKb39KPxSzXcepiS8iDYUhDGwJcJDPX4g2B": enum.SWQoSAgentFlashBlock,
|
||||||
|
"FLaSHJNm5dWYzEgnHJWWJP5ccu128Mu61NJLxUf7mUXU": enum.SWQoSAgentFlashBlock,
|
||||||
|
"FLaSHR4Vv7sttd6TyDF4yR1bJyAxRwWKbohDytEMu3wL": enum.SWQoSAgentFlashBlock,
|
||||||
|
"FLaShB3iXXTWE1vu9wQsChUKq3HFtpMAhb8kAh1pf1wi": enum.SWQoSAgentFlashBlock,
|
||||||
|
"FLAShWTjcweNT4NSotpjpxAkwxUr2we3eXQGhpTVzRwy": enum.SWQoSAgentFlashBlock,
|
||||||
|
"FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9": enum.SWQoSAgentBlockRazor,
|
||||||
|
"6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG": enum.SWQoSAgentBlockRazor,
|
||||||
|
"A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22": enum.SWQoSAgentBlockRazor,
|
||||||
|
"Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm": enum.SWQoSAgentBlockRazor,
|
||||||
|
"68Pwb4jS7eZATjDfhmTXgRJjCiZmw1L7Huy4HNpnxJ3o": enum.SWQoSAgentBlockRazor,
|
||||||
|
"4ABhJh5rZPjv63RBJBuyWzBK3g9gWMUQdTZP2kiW31V9": enum.SWQoSAgentBlockRazor,
|
||||||
|
"B2M4NG5eyZp5SBQrSdtemzk5TqVuaWGQnowGaCBt8GyM": enum.SWQoSAgentBlockRazor,
|
||||||
|
"5jA59cXMKQqZAVdtopv8q3yyw9SYfiE3vUCbt7p8MfVf": enum.SWQoSAgentBlockRazor,
|
||||||
|
"5YktoWygr1Bp9wiS1xtMtUki1PeYuuzuCF98tqwYxf61": enum.SWQoSAgentBlockRazor,
|
||||||
|
"295Avbam4qGShBYK7E9H5Ldew4B3WyJGmgmXfiWdeeyV": enum.SWQoSAgentBlockRazor,
|
||||||
|
"EDi4rSy2LZgKJX74mbLTFk4mxoTgT6F7HxxzG2HBAFyK": enum.SWQoSAgentBlockRazor,
|
||||||
|
"BnGKHAC386n4Qmv9xtpBVbRaUTKixjBe3oagkPFKtoy6": enum.SWQoSAgentBlockRazor,
|
||||||
|
"Dd7K2Fp7AtoN8xCghKDRmyqr5U169t48Tw5fEd3wT9mq": enum.SWQoSAgentBlockRazor,
|
||||||
|
"AP6qExwrbRgBAVaehg4b5xHENX815sMabtBzUzVB4v8S": enum.SWQoSAgentBlockRazor,
|
||||||
|
"astrazznxsGUhWShqgNtAdfrzP2G83DzcWVJDxwV9bF": enum.SWQoSAgentAstralane,
|
||||||
|
"astra4uejePWneqNaJKuFFA8oonqCE1sqF6b45kDMZm": enum.SWQoSAgentAstralane,
|
||||||
|
"astra9xWY93QyfG6yM8zwsKsRodscjQ2uU2HKNL5prk": enum.SWQoSAgentAstralane,
|
||||||
|
"astraRVUuTHjpwEVvNBeQEgwYx9w9CFyfxjYoobCZhL": enum.SWQoSAgentAstralane,
|
||||||
|
"astraEJ2fEj8Xmy6KLG7B3VfbKfsHXhHrNdCQx7iGJK": enum.SWQoSAgentAstralane,
|
||||||
|
"astraubkDw81n4LuutzSQ8uzHCv4BhPVhfvTcYv8SKC": enum.SWQoSAgentAstralane,
|
||||||
|
"astraZW5GLFefxNPAatceHhYjfA1ciq9gvfEg2S47xk": enum.SWQoSAgentAstralane,
|
||||||
|
"astrawVNP4xDBKT7rAdxrLYiTSTdqtUr63fSMduivXK": enum.SWQoSAgentAstralane,
|
||||||
|
"ste11JV3MLMM7x7EJUM2sXcJC1H7F4jBLnP9a9PG8PH": enum.SWQoSAgentStellium,
|
||||||
|
"ste11MWPjXCRfQryCshzi86SGhuXjF4Lv6xMXD2AoSt": enum.SWQoSAgentStellium,
|
||||||
|
"ste11p5x8tJ53H1NbNQsRBg1YNRd4GcVpxtDw8PBpmb": enum.SWQoSAgentStellium,
|
||||||
|
"ste11p7e2KLYou5bwtt35H7BM6uMdo4pvioGjJXKFcN": enum.SWQoSAgentStellium,
|
||||||
|
"ste11TMV68LMi1BguM4RQujtbNCZvf1sjsASpqgAvSX": enum.SWQoSAgentStellium,
|
||||||
|
}
|
||||||
101
pkg/shreder/addresstables.go
Normal file
101
pkg/shreder/addresstables.go
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package shreder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/gagliardetto/solana-go"
|
||||||
|
"github.com/gagliardetto/solana-go/rpc"
|
||||||
|
lru "github.com/hashicorp/golang-lru/v2"
|
||||||
|
"github.com/panjf2000/ants/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AddressTables struct {
|
||||||
|
rpcClient *rpc.Client
|
||||||
|
mux sync.RWMutex
|
||||||
|
tables *lru.Cache[solana.PublicKey, []solana.PublicKey]
|
||||||
|
loading map[solana.PublicKey]struct{}
|
||||||
|
|
||||||
|
pool *ants.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAddressTables(rpcClient *rpc.Client) *AddressTables {
|
||||||
|
pool, _ := ants.NewPool(5, ants.WithPreAlloc(true), ants.WithNonblocking(true))
|
||||||
|
cache, _ := lru.New[solana.PublicKey, []solana.PublicKey](10000)
|
||||||
|
return &AddressTables{
|
||||||
|
rpcClient: rpcClient,
|
||||||
|
tables: cache,
|
||||||
|
loading: make(map[solana.PublicKey]struct{}),
|
||||||
|
pool: pool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (at *AddressTables) loadAddressTable(tablePubkey solana.PublicKey) ([]solana.PublicKey, error) {
|
||||||
|
// decode acc
|
||||||
|
acc, err := at.rpcClient.GetAccountInfoWithOpts(context.Background(), tablePubkey, &rpc.GetAccountInfoOpts{
|
||||||
|
Encoding: solana.EncodingBase64,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data := acc.GetBinary()
|
||||||
|
if len(data) <= 56 {
|
||||||
|
return nil, fmt.Errorf("account data too short")
|
||||||
|
}
|
||||||
|
offset := 56
|
||||||
|
var addresses solana.PublicKeySlice = make([]solana.PublicKey, 0, (len(data)-offset)/32)
|
||||||
|
for offset+32 <= len(data) {
|
||||||
|
addresses = append(addresses, solana.PublicKeyFromBytes(data[offset:offset+32]))
|
||||||
|
offset += 32
|
||||||
|
}
|
||||||
|
// addresses = append(addresses, solana.PublicKeyFromBytes(data[start:start+32]))
|
||||||
|
return addresses, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (at *AddressTables) GetAddressTable(tablePubkey solana.PublicKey, idx []uint8) []solana.PublicKey {
|
||||||
|
at.mux.RLock()
|
||||||
|
addresses, ok := at.tables.Get(tablePubkey)
|
||||||
|
if !ok {
|
||||||
|
at.mux.RUnlock()
|
||||||
|
_ = at.pool.Submit(func() {
|
||||||
|
at.mux.RLock()
|
||||||
|
_, loading := at.loading[tablePubkey]
|
||||||
|
if loading {
|
||||||
|
at.mux.RUnlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
at.mux.RUnlock()
|
||||||
|
at.mux.Lock()
|
||||||
|
at.loading[tablePubkey] = struct{}{}
|
||||||
|
at.mux.Unlock()
|
||||||
|
|
||||||
|
table, err := at.loadAddressTable(tablePubkey)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("loadAddressTable failed", "err", err, "table", tablePubkey)
|
||||||
|
at.mux.Lock()
|
||||||
|
delete(at.loading, tablePubkey)
|
||||||
|
at.mux.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
at.mux.Lock()
|
||||||
|
at.tables.Add(tablePubkey, table)
|
||||||
|
total := at.tables.Len()
|
||||||
|
delete(at.loading, tablePubkey)
|
||||||
|
at.mux.Unlock()
|
||||||
|
logger.Info("loadAddressTable", "table", tablePubkey.String(), "table count:", total)
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
at.mux.RUnlock()
|
||||||
|
|
||||||
|
var result solana.PublicKeySlice = make([]solana.PublicKey, 0, len(idx))
|
||||||
|
for _, i := range idx {
|
||||||
|
if int(i) >= len(addresses) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, addresses[i])
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -2,35 +2,39 @@ package shreder
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log/slog"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gagliardetto/solana-go/rpc"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
log *slog.Logger
|
|
||||||
|
|
||||||
conn *grpc.ClientConn
|
conn *grpc.ClientConn
|
||||||
client ShrederServiceClient
|
client ShrederServiceClient
|
||||||
|
tableLoader *AddressTables
|
||||||
subscription map[string]*SubscribeRequestFilterTransactions
|
subscription map[string]*SubscribeRequestFilterTransactions
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewShrederClient(
|
func NewShrederClient(
|
||||||
url string,
|
url string,
|
||||||
|
rpcClient *rpc.Client,
|
||||||
subscription map[string]*SubscribeRequestFilterTransactions,
|
subscription map[string]*SubscribeRequestFilterTransactions,
|
||||||
) (*Client, func(), error) {
|
) (*Client, func(), error) {
|
||||||
|
if rpcClient == nil {
|
||||||
|
return nil, func() {}, fmt.Errorf("rpc client is nil")
|
||||||
|
}
|
||||||
|
|
||||||
conn, err := grpc.NewClient(url, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
conn, err := grpc.NewClient(url, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, func() {}, err
|
return nil, func() {}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger := slog.Default()
|
|
||||||
s := &Client{
|
s := &Client{
|
||||||
log: logger,
|
|
||||||
conn: conn,
|
conn: conn,
|
||||||
client: NewShrederServiceClient(conn),
|
client: NewShrederServiceClient(conn),
|
||||||
subscription: subscription,
|
subscription: subscription,
|
||||||
|
tableLoader: NewAddressTables(rpcClient),
|
||||||
}
|
}
|
||||||
|
|
||||||
return s, func() {
|
return s, func() {
|
||||||
@@ -39,14 +43,14 @@ func NewShrederClient(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Wait() {
|
func (c *Client) Wait() {
|
||||||
c.log.Debug("waiting for shreder client to stop")
|
logger.Debug("waiting for shreder client to stop")
|
||||||
|
|
||||||
err := c.conn.Close()
|
err := c.conn.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.log.Error("failed to close connection: ", "err", err)
|
logger.Error("failed to close connection: ", "err", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.log.Debug("shreder client stopped")
|
logger.Debug("shreder client stopped")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) ReadSync(ctx context.Context, txCh chan<- TxSignalBatch) error {
|
func (c *Client) ReadSync(ctx context.Context, txCh chan<- TxSignalBatch) error {
|
||||||
@@ -68,7 +72,7 @@ func (c *Client) ReadSync(ctx context.Context, txCh chan<- TxSignalBatch) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
txBatch := ParseTransaction(response.Transaction)
|
txBatch := ParseTransaction(response.Transaction, c.tableLoader)
|
||||||
if len(txBatch) == 0 {
|
if len(txBatch) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
2570
pkg/shreder/juptierv6-idl.json
Normal file
2570
pkg/shreder/juptierv6-idl.json
Normal file
File diff suppressed because it is too large
Load Diff
983
pkg/shreder/juptierv6.go
Normal file
983
pkg/shreder/juptierv6.go
Normal file
@@ -0,0 +1,983 @@
|
|||||||
|
package shreder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
bin "github.com/gagliardetto/binary"
|
||||||
|
"github.com/gagliardetto/solana-go"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
jupiterRouteV2 = []byte{187, 100, 250, 204, 49, 196, 175, 20}
|
||||||
|
jupiterExactOutRouteV2 = []byte{157, 138, 184, 82, 21, 244, 243, 36}
|
||||||
|
|
||||||
|
jupiterRoute = []byte{229, 23, 203, 151, 122, 227, 173, 42}
|
||||||
|
jupiterRouteWithTokenLedger = []byte{150, 86, 71, 116, 167, 93, 14, 104}
|
||||||
|
jupiterSharedAccountsExactOutRoute = []byte{176, 209, 105, 168, 154, 125, 69, 62}
|
||||||
|
jupiterSharedAccountsRoute = []byte{193, 32, 155, 51, 65, 214, 156, 129}
|
||||||
|
jupiterSharedAccountsRouteWithTokenLedger = []byte{230, 121, 143, 80, 119, 159, 106, 170}
|
||||||
|
|
||||||
|
jupiterSharedAccountsExactOutRouteV2 = []byte{53, 96, 229, 202, 216, 187, 250, 24}
|
||||||
|
jupiterSharedAccountsRouteV2 = []byte{209, 152, 83, 147, 124, 254, 216, 233}
|
||||||
|
)
|
||||||
|
|
||||||
|
type Side uint8
|
||||||
|
|
||||||
|
type SwapKind uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
Saber SwapKind = iota
|
||||||
|
SaberAddDecimalsDeposit
|
||||||
|
SaberAddDecimalsWithdraw
|
||||||
|
TokenSwap
|
||||||
|
Sencha
|
||||||
|
Step
|
||||||
|
Cropper
|
||||||
|
Raydium
|
||||||
|
Crema
|
||||||
|
Lifinity
|
||||||
|
Mercurial
|
||||||
|
Cykura
|
||||||
|
Serum
|
||||||
|
MarinadeDeposit
|
||||||
|
MarinadeUnstake
|
||||||
|
Aldrin
|
||||||
|
AldrinV2
|
||||||
|
Whirlpool
|
||||||
|
Invariant
|
||||||
|
Meteora
|
||||||
|
GooseFX
|
||||||
|
DeltaFi
|
||||||
|
Balansol
|
||||||
|
MarcoPolo
|
||||||
|
Dradex
|
||||||
|
LifinityV2
|
||||||
|
RaydiumClmm
|
||||||
|
Openbook
|
||||||
|
Phoenix
|
||||||
|
Symmetry
|
||||||
|
TokenSwapV2
|
||||||
|
HeliumTreasuryManagementRedeemV0
|
||||||
|
StakeDexStakeWrappedSol
|
||||||
|
StakeDexSwapViaStake
|
||||||
|
GooseFXV2
|
||||||
|
Perps
|
||||||
|
PerpsAddLiquidity
|
||||||
|
PerpsRemoveLiquidity
|
||||||
|
MeteoraDlmm
|
||||||
|
OpenBookV2
|
||||||
|
RaydiumClmmV2
|
||||||
|
StakeDexPrefundWithdrawStakeAndDepositStake
|
||||||
|
Clone
|
||||||
|
SanctumS
|
||||||
|
SanctumSAddLiquidity
|
||||||
|
SanctumSRemoveLiquidity
|
||||||
|
RaydiumCP
|
||||||
|
WhirlpoolSwapV2
|
||||||
|
OneIntro
|
||||||
|
PumpWrappedBuy
|
||||||
|
PumpWrappedSell
|
||||||
|
PerpsV2
|
||||||
|
PerpsV2AddLiquidity
|
||||||
|
PerpsV2RemoveLiquidity
|
||||||
|
MoonshotWrappedBuy
|
||||||
|
MoonshotWrappedSell
|
||||||
|
StabbleStableSwap
|
||||||
|
StabbleWeightedSwap
|
||||||
|
Obric
|
||||||
|
FoxBuyFromEstimatedCost
|
||||||
|
FoxClaimPartial
|
||||||
|
SolFi
|
||||||
|
SolayerDelegateNoInit
|
||||||
|
SolayerUndelegateNoInit
|
||||||
|
TokenMill
|
||||||
|
DaosFunBuy
|
||||||
|
DaosFunSell
|
||||||
|
ZeroFi
|
||||||
|
StakeDexWithdrawWrappedSol
|
||||||
|
VirtualsBuy
|
||||||
|
VirtualsSell
|
||||||
|
Perena
|
||||||
|
PumpSwapBuy
|
||||||
|
PumpSwapSell
|
||||||
|
Gamma
|
||||||
|
MeteoraDlmmSwapV2
|
||||||
|
Woofi
|
||||||
|
MeteoraDammV2
|
||||||
|
MeteoraDynamicBondingCurveSwap
|
||||||
|
StabbleStableSwapV2
|
||||||
|
StabbleWeightedSwapV2
|
||||||
|
RaydiumLaunchlabBuy
|
||||||
|
RaydiumLaunchlabSell
|
||||||
|
BoopdotfunWrappedBuy
|
||||||
|
BoopdotfunWrappedSell
|
||||||
|
Plasma
|
||||||
|
GoonFi
|
||||||
|
HumidiFi
|
||||||
|
MeteoraDynamicBondingCurveSwapWithRemainingAccounts
|
||||||
|
TesseraV
|
||||||
|
PumpWrappedBuyV2
|
||||||
|
PumpWrappedSellV2
|
||||||
|
PumpSwapBuyV2
|
||||||
|
PumpSwapSellV2
|
||||||
|
Heaven
|
||||||
|
SolFiV2
|
||||||
|
Aquifer
|
||||||
|
PumpWrappedBuyV3
|
||||||
|
PumpWrappedSellV3
|
||||||
|
PumpSwapBuyV3
|
||||||
|
PumpSwapSellV3
|
||||||
|
JupiterLendDeposit
|
||||||
|
JupiterLendRedeem
|
||||||
|
DefiTuna
|
||||||
|
AlphaQ
|
||||||
|
RaydiumV2
|
||||||
|
SarosDlmm
|
||||||
|
Futarchy
|
||||||
|
MeteoraDammV2WithRemainingAccounts
|
||||||
|
Obsidian
|
||||||
|
WhaleStreet
|
||||||
|
DynamicV1
|
||||||
|
PumpWrappedBuyV4
|
||||||
|
PumpWrappedSellV4
|
||||||
|
CarrotIssue
|
||||||
|
CarrotRedeem
|
||||||
|
Manifest
|
||||||
|
BisonFi
|
||||||
|
HumidiFiV2
|
||||||
|
PerenaStar
|
||||||
|
JupiterRfqV2
|
||||||
|
GoonFiV2
|
||||||
|
)
|
||||||
|
|
||||||
|
var swapKindNames = [122]string{"Saber", "SaberAddDecimalsDeposit", "SaberAddDecimalsWithdraw", "TokenSwap", "Sencha", "Step", "Cropper",
|
||||||
|
"Raydium", "Crema", "Lifinity", "Mercurial", "Cykura", "Serum", "MarinadeDeposit", "MarinadeUnstake", "Aldrin", "AldrinV2", "Whirlpool",
|
||||||
|
"Invariant", "Meteora", "GooseFX", "DeltaFi", "Balansol", "MarcoPolo", "Dradex", "LifinityV2", "RaydiumClmm", "Openbook", "Phoenix",
|
||||||
|
"Symmetry", "TokenSwapV2", "HeliumTreasuryManagementRedeemV0", "StakeDexStakeWrappedSol", "StakeDexSwapViaStake", "GooseFXV2", "Perps",
|
||||||
|
"PerpsAddLiquidity", "PerpsRemoveLiquidity", "MeteoraDlmm", "OpenBookV2", "RaydiumClmmV2", "StakeDexPrefundWithdrawStakeAndDepositStake",
|
||||||
|
"Clone", "SanctumS", "SanctumSAddLiquidity", "SanctumSRemoveLiquidity", "RaydiumCP", "WhirlpoolSwapV2", "OneIntro", "PumpWrappedBuy",
|
||||||
|
"PumpWrappedSell", "PerpsV2", "PerpsV2AddLiquidity", "PerpsV2RemoveLiquidity", "MoonshotWrappedBuy", "MoonshotWrappedSell", "StabbleStableSwap",
|
||||||
|
"StabbleWeightedSwap", "Obric", "FoxBuyFromEstimatedCost", "FoxClaimPartial", "SolFi", "SolayerDelegateNoInit", "SolayerUndelegateNoInit", "TokenMill",
|
||||||
|
"DaosFunBuy", "DaosFunSell", "ZeroFi", "StakeDexWithdrawWrappedSol", "VirtualsBuy", "VirtualsSell", "Perena", "PumpSwapBuy", "PumpSwapSell", "Gamma",
|
||||||
|
"MeteoraDlmmSwapV2", "Woofi", "MeteoraDammV2", "MeteoraDynamicBondingCurveSwap", "StabbleStableSwapV2", "StabbleWeightedSwapV2", "RaydiumLaunchlabBuy",
|
||||||
|
"RaydiumLaunchlabSell", "BoopdotfunWrappedBuy", "BoopdotfunWrappedSell", "Plasma", "GoonFi", "HumidiFi",
|
||||||
|
"MeteoraDynamicBondingCurveSwapWithRemainingAccounts", "TesseraV", "PumpWrappedBuyV2", "PumpWrappedSellV2", "PumpSwapBuyV2", "PumpSwapSellV2",
|
||||||
|
"Heaven", "SolFiV2", "Aquifer", "PumpWrappedBuyV3", "PumpWrappedSellV3", "PumpSwapBuyV3", "PumpSwapSellV3", "JupiterLendDeposit", "JupiterLendRedeem",
|
||||||
|
"DefiTuna", "AlphaQ", "RaydiumV2", "SarosDlmm", "Futarchy", "MeteoraDammV2WithRemainingAccounts", "Obsidian", "WhaleStreet", "DynamicV1",
|
||||||
|
"PumpWrappedBuyV4", "PumpWrappedSellV4", "CarrotIssue", "CarrotRedeem", "Manifest", "BisonFi", "HumidiFiV2", "PerenaStar", "JupiterRfqV2", "GoonFiV2"}
|
||||||
|
|
||||||
|
func (s SwapKind) String() string {
|
||||||
|
idx := int(s)
|
||||||
|
if idx < 0 || idx >= len(swapKindNames) {
|
||||||
|
return fmt.Sprintf("SwapKind(%d)", uint8(s))
|
||||||
|
}
|
||||||
|
return swapKindNames[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
type Swap struct {
|
||||||
|
Kind SwapKind
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoutePlanStepV2 struct {
|
||||||
|
Swap Swap
|
||||||
|
Bps uint16
|
||||||
|
|
||||||
|
InputIdx uint8
|
||||||
|
OutputIdx uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoutePlanStep struct {
|
||||||
|
Swap Swap
|
||||||
|
Percent uint8
|
||||||
|
|
||||||
|
InputIdx uint8
|
||||||
|
OutputIdx uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type JupiterV6RouteV2Arg struct {
|
||||||
|
In uint64
|
||||||
|
Out uint64
|
||||||
|
|
||||||
|
SlippageBps uint16
|
||||||
|
PlatformFeeBps uint16
|
||||||
|
PositiveSlippageBps uint16
|
||||||
|
|
||||||
|
Plan []RoutePlanStepV2
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipRemainingAccountsInfo(dec *bin.Decoder) error {
|
||||||
|
// RemainingAccountsInfo { slices: Vec<RemainingAccountsSlice> }
|
||||||
|
// RemainingAccountsSlice { accounts_type: u8, length: u8 }
|
||||||
|
ln, err := dec.ReadUint32(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// each slice is 2 bytes
|
||||||
|
return dec.SkipBytes(uint(ln) * 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipOptionRemainingAccountsInfo(dec *bin.Decoder) error {
|
||||||
|
pos := dec.Position()
|
||||||
|
tag, err := dec.ReadUint8()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch tag {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case 1:
|
||||||
|
return skipRemainingAccountsInfo(dec)
|
||||||
|
default:
|
||||||
|
// Version drift: sometimes a swap variant we think has Option<RemainingAccountsInfo> is actually no-payload.
|
||||||
|
_ = dec.SetPosition(pos)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipCandidateSwap(dec *bin.Decoder) error {
|
||||||
|
// CandidateSwap enum (this IDL variant):
|
||||||
|
// 0 HumidiFi { u64, bool }
|
||||||
|
// 1 TesseraV { Side(u8) }
|
||||||
|
// NOTE: other IDL versions may include more variants (e.g. HumidiFiV2).
|
||||||
|
tag, err := dec.ReadUint8()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch tag {
|
||||||
|
case 0:
|
||||||
|
// HumidiFi u64 + bool
|
||||||
|
if err := dec.SkipBytes(8); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return dec.SkipBytes(1)
|
||||||
|
case 1:
|
||||||
|
// TesseraV (Side: u8)
|
||||||
|
return dec.SkipBytes(1)
|
||||||
|
case 2:
|
||||||
|
// Seen in other IDLs: HumidiFiV2 { u64, bool }
|
||||||
|
if err := dec.SkipBytes(8); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return dec.SkipBytes(1)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown CandidateSwap variant: %d", tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipDynamicV1(dec *bin.Decoder) error {
|
||||||
|
// DynamicV1 { candidate_swaps: Vec<CandidateSwap> }
|
||||||
|
ln, err := dec.ReadUint32(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := uint32(0); i < ln; i++ {
|
||||||
|
if err := skipCandidateSwap(dec); err != nil {
|
||||||
|
return fmt.Errorf("CandidateSwap[%d]: %w", i, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeSwap(dec *bin.Decoder) (Swap, error) {
|
||||||
|
tag, err := dec.ReadUint8()
|
||||||
|
if err != nil {
|
||||||
|
return Swap{}, fmt.Errorf("read Swap variant: %w", err)
|
||||||
|
}
|
||||||
|
k := SwapKind(tag)
|
||||||
|
out := Swap{Kind: k}
|
||||||
|
|
||||||
|
skipU8 := func() error { return dec.SkipBytes(1) }
|
||||||
|
skipBool := func() error { return dec.SkipBytes(1) }
|
||||||
|
skipU32 := func() error { return dec.SkipBytes(4) }
|
||||||
|
skipU64 := func() error { return dec.SkipBytes(8) }
|
||||||
|
skipTwoU64 := func() error { return dec.SkipBytes(16) }
|
||||||
|
skipRemaining := func() error { return skipRemainingAccountsInfo(dec) }
|
||||||
|
skipOptRemaining := func() error { return skipOptionRemainingAccountsInfo(dec) }
|
||||||
|
|
||||||
|
switch k {
|
||||||
|
// -------- payload-less variants --------
|
||||||
|
case Saber, SaberAddDecimalsDeposit, SaberAddDecimalsWithdraw, TokenSwap, Sencha, Step, Cropper, Raydium, Lifinity,
|
||||||
|
Mercurial, Cykura, MarinadeDeposit, MarinadeUnstake, Meteora, GooseFX, Balansol, LifinityV2, RaydiumClmm,
|
||||||
|
TokenSwapV2, HeliumTreasuryManagementRedeemV0, StakeDexStakeWrappedSol, GooseFXV2, Perps, PerpsAddLiquidity,
|
||||||
|
PerpsRemoveLiquidity, MeteoraDlmm, RaydiumClmmV2, RaydiumCP, OneIntro, PumpWrappedBuy, PumpWrappedSell, PerpsV2,
|
||||||
|
PerpsV2AddLiquidity, PerpsV2RemoveLiquidity, MoonshotWrappedBuy, MoonshotWrappedSell, StabbleStableSwap,
|
||||||
|
StabbleWeightedSwap, FoxBuyFromEstimatedCost, SolayerDelegateNoInit, SolayerUndelegateNoInit, DaosFunBuy,
|
||||||
|
DaosFunSell, ZeroFi, StakeDexWithdrawWrappedSol, VirtualsBuy, VirtualsSell, PumpSwapBuy, PumpSwapSell,
|
||||||
|
Gamma, Woofi, MeteoraDammV2, MeteoraDynamicBondingCurveSwap, StabbleStableSwapV2, StabbleWeightedSwapV2,
|
||||||
|
BoopdotfunWrappedBuy, BoopdotfunWrappedSell, MeteoraDynamicBondingCurveSwapWithRemainingAccounts,
|
||||||
|
PumpWrappedBuyV2, PumpWrappedSellV2, PumpSwapBuyV2, PumpSwapSellV2, Aquifer, PumpWrappedBuyV3, PumpWrappedSellV3,
|
||||||
|
PumpSwapBuyV3, PumpSwapSellV3, JupiterLendDeposit, JupiterLendRedeem, RaydiumV2,
|
||||||
|
MeteoraDammV2WithRemainingAccounts, Obsidian, PumpWrappedBuyV4, PumpWrappedSellV4, CarrotIssue, CarrotRedeem:
|
||||||
|
return out, nil
|
||||||
|
|
||||||
|
// -------- bool payload --------
|
||||||
|
case Crema, Whirlpool, Invariant, DeltaFi, MarcoPolo, Obric, FoxClaimPartial, SolFi, Heaven, SolFiV2, AlphaQ,
|
||||||
|
SarosDlmm, BisonFi, PerenaStar, GoonFiV2:
|
||||||
|
return out, skipBool()
|
||||||
|
// -------- u32 --------
|
||||||
|
case StakeDexSwapViaStake, StakeDexPrefundWithdrawStakeAndDepositStake:
|
||||||
|
return out, skipU32()
|
||||||
|
// -------- u64 --------
|
||||||
|
case RaydiumLaunchlabBuy, RaydiumLaunchlabSell:
|
||||||
|
return out, skipU64()
|
||||||
|
// -------- Side(u8) payload --------
|
||||||
|
case Serum, Aldrin, AldrinV2, Dradex, Openbook, Phoenix, OpenBookV2, TokenMill, Plasma, TesseraV, Futarchy, WhaleStreet, Manifest:
|
||||||
|
return out, skipU8()
|
||||||
|
// -------- MeteoraDlmmSwapV2: RemainingAccountsInfo --------
|
||||||
|
case MeteoraDlmmSwapV2:
|
||||||
|
return out, skipRemaining()
|
||||||
|
// -------- DynamicV1: Vec<CandidateSwap> --------
|
||||||
|
case DynamicV1:
|
||||||
|
return out, skipDynamicV1(dec)
|
||||||
|
// -------- u64 + u64 --------
|
||||||
|
case Symmetry:
|
||||||
|
return out, skipTwoU64()
|
||||||
|
// -------- Clone: u8 + bool + bool --------
|
||||||
|
case Clone:
|
||||||
|
if err := skipU8(); err != nil {
|
||||||
|
return Swap{}, err
|
||||||
|
}
|
||||||
|
if err := skipBool(); err != nil {
|
||||||
|
return Swap{}, err
|
||||||
|
}
|
||||||
|
return out, skipBool()
|
||||||
|
// -------- SanctumS: u8 + u8 + u32 + u32 --------
|
||||||
|
case SanctumS:
|
||||||
|
if err := skipU8(); err != nil {
|
||||||
|
return Swap{}, err
|
||||||
|
}
|
||||||
|
if err := skipU8(); err != nil {
|
||||||
|
return Swap{}, err
|
||||||
|
}
|
||||||
|
if err := skipU32(); err != nil {
|
||||||
|
return Swap{}, err
|
||||||
|
}
|
||||||
|
return out, skipU32()
|
||||||
|
// -------- SanctumS(Add/Remove)Liquidity: u8 + u32 --------
|
||||||
|
case SanctumSAddLiquidity, SanctumSRemoveLiquidity:
|
||||||
|
if err := skipU8(); err != nil {
|
||||||
|
return Swap{}, err
|
||||||
|
}
|
||||||
|
return out, skipU32()
|
||||||
|
// -------- WhirlpoolSwapV2 / DefiTuna: bool + Option<RemainingAccountsInfo> --------
|
||||||
|
case WhirlpoolSwapV2, DefiTuna:
|
||||||
|
if err := skipBool(); err != nil {
|
||||||
|
return Swap{}, err
|
||||||
|
}
|
||||||
|
return out, skipOptRemaining()
|
||||||
|
// -------- Perena: u8 + u8 --------
|
||||||
|
case Perena:
|
||||||
|
if err := skipU8(); err != nil {
|
||||||
|
return Swap{}, err
|
||||||
|
}
|
||||||
|
return out, skipU8()
|
||||||
|
case GoonFi:
|
||||||
|
if err := skipBool(); err != nil {
|
||||||
|
return Swap{}, err
|
||||||
|
}
|
||||||
|
return out, skipU8()
|
||||||
|
// -------- HumidiFi/HumidiFiV2: u64 + bool --------
|
||||||
|
case HumidiFi, HumidiFiV2:
|
||||||
|
if err := skipU64(); err != nil {
|
||||||
|
return Swap{}, err
|
||||||
|
}
|
||||||
|
return out, skipBool()
|
||||||
|
|
||||||
|
// -------- JupiterRfqV2: Side(u8) + bytes --------
|
||||||
|
case JupiterRfqV2:
|
||||||
|
// side
|
||||||
|
if err := skipU8(); err != nil {
|
||||||
|
return Swap{}, err
|
||||||
|
}
|
||||||
|
ln, err := dec.ReadUint32(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return Swap{}, err
|
||||||
|
}
|
||||||
|
if err := dec.SkipBytes(uint(ln)); 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeRoutePlanStepV2(dec *bin.Decoder) (RoutePlanStepV2, error) {
|
||||||
|
sw, err := decodeSwap(dec)
|
||||||
|
if err != nil {
|
||||||
|
return RoutePlanStepV2{}, err
|
||||||
|
}
|
||||||
|
bps, err := dec.ReadUint16(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return RoutePlanStepV2{}, err
|
||||||
|
}
|
||||||
|
inIdx, err := dec.ReadUint8()
|
||||||
|
if err != nil {
|
||||||
|
return RoutePlanStepV2{}, err
|
||||||
|
}
|
||||||
|
outIdx, err := dec.ReadUint8()
|
||||||
|
if err != nil {
|
||||||
|
return RoutePlanStepV2{}, err
|
||||||
|
}
|
||||||
|
return RoutePlanStepV2{Swap: sw, Bps: bps, InputIdx: inIdx, OutputIdx: outIdx}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeRoutePlanStep(dec *bin.Decoder) (RoutePlanStep, error) {
|
||||||
|
sw, err := decodeSwap(dec)
|
||||||
|
if err != nil {
|
||||||
|
return RoutePlanStep{}, err
|
||||||
|
}
|
||||||
|
percent, err := dec.ReadUint8()
|
||||||
|
if err != nil {
|
||||||
|
return RoutePlanStep{}, err
|
||||||
|
}
|
||||||
|
inIdx, err := dec.ReadUint8()
|
||||||
|
if err != nil {
|
||||||
|
return RoutePlanStep{}, err
|
||||||
|
}
|
||||||
|
outIdx, err := dec.ReadUint8()
|
||||||
|
if err != nil {
|
||||||
|
return RoutePlanStep{}, err
|
||||||
|
}
|
||||||
|
return RoutePlanStep{Swap: sw, Percent: percent, InputIdx: inIdx, OutputIdx: outIdx}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeJupiterV6RouteV2Arg(data []byte) (*JupiterV6RouteV2Arg, error) {
|
||||||
|
dec := bin.NewBorshDecoder(data)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
out := &JupiterV6RouteV2Arg{}
|
||||||
|
if out.In, err = dec.ReadUint64(binary.LittleEndian); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if out.Out, err = dec.ReadUint64(binary.LittleEndian); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if out.SlippageBps, err = dec.ReadUint16(binary.LittleEndian); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if out.PlatformFeeBps, err = dec.ReadUint16(binary.LittleEndian); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if out.PositiveSlippageBps, err = dec.ReadUint16(binary.LittleEndian); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// vec<RoutePlanStepV2>: u32 length + elements
|
||||||
|
ln, err := dec.ReadUint32(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out.Plan = make([]RoutePlanStepV2, 0, ln)
|
||||||
|
for i := uint32(0); i < ln; i++ {
|
||||||
|
step, err := decodeRoutePlanStepV2(dec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decode Plan[%d]: %w", i, err)
|
||||||
|
}
|
||||||
|
out.Plan = append(out.Plan, step)
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type JupiterV6RouteArg struct {
|
||||||
|
Plan []RoutePlanStep
|
||||||
|
|
||||||
|
In uint64
|
||||||
|
QuotedOut uint64
|
||||||
|
SlippageBps uint16
|
||||||
|
PlatformFeeBps uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type JupiterV6RouteWithTokenLedgerArg struct {
|
||||||
|
Plan []RoutePlanStep
|
||||||
|
|
||||||
|
QuotedOut uint64
|
||||||
|
SlippageBps uint16
|
||||||
|
PlatformFeeBps uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type JupiterV6SharedAccountsExactOutRouteArg struct {
|
||||||
|
ID uint8
|
||||||
|
|
||||||
|
Plan []RoutePlanStep
|
||||||
|
|
||||||
|
Out uint64
|
||||||
|
QuotedIn uint64
|
||||||
|
SlippageBps uint16
|
||||||
|
PlatformFeeBps uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type JupiterV6SharedAccountsRouteArg struct {
|
||||||
|
ID uint8
|
||||||
|
|
||||||
|
Plan []RoutePlanStep
|
||||||
|
|
||||||
|
In uint64
|
||||||
|
QuotedOut uint64
|
||||||
|
SlippageBps uint16
|
||||||
|
PlatformFeeBps uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type JupiterV6SharedAccountsRouteWithTokenLedgerArg struct {
|
||||||
|
ID uint8
|
||||||
|
|
||||||
|
Plan []RoutePlanStep
|
||||||
|
|
||||||
|
QuotedOut uint64
|
||||||
|
SlippageBps uint16
|
||||||
|
PlatformFeeBps uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type JupiterV6ExactOutRouteV2Arg struct {
|
||||||
|
Out uint64
|
||||||
|
QuotedIn uint64
|
||||||
|
Slippage uint16
|
||||||
|
PlatFee uint16
|
||||||
|
PosSlip uint16
|
||||||
|
RoutePlan []RoutePlanStepV2
|
||||||
|
}
|
||||||
|
|
||||||
|
type JupiterV6SharedAccountsExactOutRouteV2Arg struct {
|
||||||
|
ID uint8
|
||||||
|
|
||||||
|
Out uint64
|
||||||
|
QuotedIn uint64
|
||||||
|
Slippage uint16
|
||||||
|
PlatFee uint16
|
||||||
|
PosSlip uint16
|
||||||
|
RoutePlan []RoutePlanStepV2
|
||||||
|
}
|
||||||
|
|
||||||
|
type JupiterV6SharedAccountsRouteV2Arg struct {
|
||||||
|
ID uint8
|
||||||
|
|
||||||
|
In uint64
|
||||||
|
QuotedOut uint64
|
||||||
|
Slippage uint16
|
||||||
|
PlatFee uint16
|
||||||
|
PosSlip uint16
|
||||||
|
RoutePlan []RoutePlanStepV2
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeVecRoutePlanStep(dec *bin.Decoder) ([]RoutePlanStep, error) {
|
||||||
|
ln, err := dec.ReadUint32(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out := make([]RoutePlanStep, 0, ln)
|
||||||
|
for i := uint32(0); i < ln; i++ {
|
||||||
|
step, err := decodeRoutePlanStep(dec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decode RoutePlanStep[%d]: %w", i, err)
|
||||||
|
}
|
||||||
|
out = append(out, step)
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeVecRoutePlanStepV2(dec *bin.Decoder) ([]RoutePlanStepV2, error) {
|
||||||
|
ln, err := dec.ReadUint32(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out := make([]RoutePlanStepV2, 0, ln)
|
||||||
|
for i := uint32(0); i < ln; i++ {
|
||||||
|
step, err := decodeRoutePlanStepV2(dec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decode RoutePlanStepV2[%d]: %w", i, err)
|
||||||
|
}
|
||||||
|
out = append(out, step)
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeJupiterV6RouteArg(data []byte) (*JupiterV6RouteArg, error) {
|
||||||
|
dec := bin.NewBorshDecoder(data)
|
||||||
|
plan, err := decodeVecRoutePlanStep(dec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
in, err := dec.ReadUint64(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
quotedOut, err := dec.ReadUint64(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
slippage, err := dec.ReadUint16(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pf, err := dec.ReadUint8()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &JupiterV6RouteArg{Plan: plan, In: in, QuotedOut: quotedOut, SlippageBps: slippage, PlatformFeeBps: pf}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeJupiterV6RouteWithTokenLedgerArg(data []byte) (*JupiterV6RouteWithTokenLedgerArg, error) {
|
||||||
|
dec := bin.NewBorshDecoder(data)
|
||||||
|
plan, err := decodeVecRoutePlanStep(dec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
quotedOut, err := dec.ReadUint64(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
slippage, err := dec.ReadUint16(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pf, err := dec.ReadUint8()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &JupiterV6RouteWithTokenLedgerArg{Plan: plan, QuotedOut: quotedOut, SlippageBps: slippage, PlatformFeeBps: pf}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeJupiterV6SharedAccountsExactOutRouteArg(data []byte) (*JupiterV6SharedAccountsExactOutRouteArg, error) {
|
||||||
|
dec := bin.NewBorshDecoder(data)
|
||||||
|
id, err := dec.ReadUint8()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
plan, err := decodeVecRoutePlanStep(dec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
outAmt, err := dec.ReadUint64(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
quotedIn, err := dec.ReadUint64(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
slippage, err := dec.ReadUint16(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pf, err := dec.ReadUint8()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &JupiterV6SharedAccountsExactOutRouteArg{ID: id, Plan: plan, Out: outAmt, QuotedIn: quotedIn, SlippageBps: slippage, PlatformFeeBps: pf}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeJupiterV6SharedAccountsRouteArg(data []byte) (*JupiterV6SharedAccountsRouteArg, error) {
|
||||||
|
dec := bin.NewBorshDecoder(data)
|
||||||
|
id, err := dec.ReadUint8()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
plan, err := decodeVecRoutePlanStep(dec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
inAmt, err := dec.ReadUint64(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
quotedOut, err := dec.ReadUint64(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
slippage, err := dec.ReadUint16(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pf, err := dec.ReadUint8()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &JupiterV6SharedAccountsRouteArg{ID: id, Plan: plan, In: inAmt, QuotedOut: quotedOut, SlippageBps: slippage, PlatformFeeBps: pf}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeJupiterV6SharedAccountsRouteWithTokenLedgerArg(data []byte) (*JupiterV6SharedAccountsRouteWithTokenLedgerArg, error) {
|
||||||
|
dec := bin.NewBorshDecoder(data)
|
||||||
|
id, err := dec.ReadUint8()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
plan, err := decodeVecRoutePlanStep(dec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
quotedOut, err := dec.ReadUint64(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
slippage, err := dec.ReadUint16(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pf, err := dec.ReadUint8()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &JupiterV6SharedAccountsRouteWithTokenLedgerArg{ID: id, Plan: plan, QuotedOut: quotedOut, SlippageBps: slippage, PlatformFeeBps: pf}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeJupiterV6ExactOutRouteV2Arg(data []byte) (*JupiterV6ExactOutRouteV2Arg, error) {
|
||||||
|
dec := bin.NewBorshDecoder(data)
|
||||||
|
outAmt, err := dec.ReadUint64(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
quotedIn, err := dec.ReadUint64(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
slippage, err := dec.ReadUint16(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pf, err := dec.ReadUint16(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pos, err := dec.ReadUint16(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
plan, err := decodeVecRoutePlanStepV2(dec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &JupiterV6ExactOutRouteV2Arg{Out: outAmt, QuotedIn: quotedIn, Slippage: slippage, PlatFee: pf, PosSlip: pos, RoutePlan: plan}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeJupiterV6SharedAccountsExactOutRouteV2Arg(data []byte) (*JupiterV6SharedAccountsExactOutRouteV2Arg, error) {
|
||||||
|
dec := bin.NewBorshDecoder(data)
|
||||||
|
id, err := dec.ReadUint8()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
outAmt, err := dec.ReadUint64(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
quotedIn, err := dec.ReadUint64(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
slippage, err := dec.ReadUint16(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pf, err := dec.ReadUint16(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pos, err := dec.ReadUint16(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
plan, err := decodeVecRoutePlanStepV2(dec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &JupiterV6SharedAccountsExactOutRouteV2Arg{ID: id, Out: outAmt, QuotedIn: quotedIn, Slippage: slippage, PlatFee: pf, PosSlip: pos, RoutePlan: plan}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeJupiterV6SharedAccountsRouteV2Arg(data []byte) (*JupiterV6SharedAccountsRouteV2Arg, error) {
|
||||||
|
dec := bin.NewBorshDecoder(data)
|
||||||
|
id, err := dec.ReadUint8()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
inAmt, err := dec.ReadUint64(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
quotedOut, err := dec.ReadUint64(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
slippage, err := dec.ReadUint16(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pf, err := dec.ReadUint16(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pos, err := dec.ReadUint16(binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
plan, err := decodeVecRoutePlanStepV2(dec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &JupiterV6SharedAccountsRouteV2Arg{ID: id, In: inAmt, QuotedOut: quotedOut, Slippage: slippage, PlatFee: pf, PosSlip: pos, RoutePlan: plan}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pumpSwapSellAtIdx0(amount uint64, plan []RoutePlanStep) uint64 {
|
||||||
|
var ret uint64
|
||||||
|
for _, step := range plan {
|
||||||
|
if step.InputIdx == 0 &&
|
||||||
|
(step.Swap.Kind == PumpSwapSell || step.Swap.Kind == PumpSwapSellV2 || step.Swap.Kind == PumpSwapSellV3) {
|
||||||
|
ret += amount * uint64(step.Percent) / 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func pumpSwapSellAtIdx0V2(amount uint64, plan []RoutePlanStepV2) uint64 {
|
||||||
|
var ret uint64
|
||||||
|
for _, step := range plan {
|
||||||
|
if step.InputIdx == 0 &&
|
||||||
|
(step.Swap.Kind == PumpSwapSell || step.Swap.Kind == PumpSwapSellV2 || step.Swap.Kind == PumpSwapSellV3) {
|
||||||
|
ret += amount * uint64(step.Bps) / 10000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// only decodes inputIdx = 0 container pumpSwap instructions for now
|
||||||
|
func parseJupiterV6Instruction(tx *versionedTransaction, instructionIndex int) (*TxSignal, error) {
|
||||||
|
msg := tx.Message
|
||||||
|
if instructionIndex >= len(msg.Instructions) {
|
||||||
|
return nil, fmt.Errorf("instruction index out of bounds")
|
||||||
|
}
|
||||||
|
|
||||||
|
instruction := msg.Instructions[instructionIndex]
|
||||||
|
if len(instruction.Data) == 0 {
|
||||||
|
return nil, fmt.Errorf("data is empty")
|
||||||
|
}
|
||||||
|
if len(instruction.Data) < 8 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
disc := instruction.Data[:8]
|
||||||
|
|
||||||
|
var (
|
||||||
|
sourceMint solana.PublicKey
|
||||||
|
inputAmount uint64
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
// route_v2 / exact_out_route_v2 / shared_accounts_*_v2 use accounts[3]/[4] as src/dst mints (per IDL)
|
||||||
|
// route/shared_accounts_* (v1) use different account layouts; we only decode args here.
|
||||||
|
switch {
|
||||||
|
case bytes.Equal(disc, jupiterRouteV2):
|
||||||
|
args, err := decodeJupiterV6RouteV2Arg(instruction.Data[8:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
inputAmount = pumpSwapSellAtIdx0V2(args.In, args.Plan)
|
||||||
|
|
||||||
|
case bytes.Equal(disc, jupiterSharedAccountsRouteV2):
|
||||||
|
args, err := decodeJupiterV6SharedAccountsRouteV2Arg(instruction.Data[8:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
inputAmount = pumpSwapSellAtIdx0V2(args.In, args.RoutePlan)
|
||||||
|
|
||||||
|
case bytes.Equal(disc, jupiterRoute):
|
||||||
|
args, err := decodeJupiterV6RouteArg(instruction.Data[8:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_ = args
|
||||||
|
inputAmount = pumpSwapSellAtIdx0(args.In, args.Plan)
|
||||||
|
|
||||||
|
case bytes.Equal(disc, jupiterSharedAccountsRoute):
|
||||||
|
args, err := decodeJupiterV6SharedAccountsRouteArg(instruction.Data[8:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_ = args
|
||||||
|
inputAmount = pumpSwapSellAtIdx0(args.In, args.Plan)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if inputAmount == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// existing mint extraction logic only valid for route_v2/ exact_out_route_v2. Keep it but guard.
|
||||||
|
if bytes.Equal(disc, jupiterRouteV2) || bytes.Equal(disc, jupiterSharedAccountsRouteV2) {
|
||||||
|
if len(instruction.Accounts) < 6 {
|
||||||
|
return nil, fmt.Errorf("not enough accounts for jupiter v6 v2 instruction")
|
||||||
|
}
|
||||||
|
sourceMint, err = getStaticKey(tx.Message.StaticAccountKeys, int(instruction.Accounts[3]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else if bytes.Equal(disc, jupiterSharedAccountsRoute) {
|
||||||
|
if len(instruction.Accounts) < 12 {
|
||||||
|
return nil, fmt.Errorf("not enough accounts for jupiter v6 jupiterSharedAccountsRoute instruction")
|
||||||
|
}
|
||||||
|
sourceMint, err = getStaticKey(tx.Message.StaticAccountKeys, int(instruction.Accounts[7]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(instruction.Accounts) < 10 {
|
||||||
|
return nil, fmt.Errorf("not enough accounts for jupiter v6 jupiterRoute instruction")
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
srcIdx uint8
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, acctIdx := range instruction.Accounts {
|
||||||
|
if i <= 9 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key, err := getStaticKey(tx.Message.StaticAccountKeys, int(acctIdx))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if key.Equals(pumpAmmProgramID) {
|
||||||
|
srcIdx = uint8(i + 4)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if srcIdx == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
sourceMint, err = getStaticKey(tx.Message.StaticAccountKeys, int(instruction.Accounts[srcIdx]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
distMint, err := getStaticKey(tx.Message.StaticAccountKeys, int(instruction.Accounts[srcIdx+1]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !distMint.Equals(solana.WrappedSol) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signal := &TxSignal{
|
||||||
|
Label: "jupiterV6",
|
||||||
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Maker: tx.Message.StaticAccountKeys[0].String(),
|
||||||
|
Token0Address: sourceMint.String(),
|
||||||
|
Token1Address: wsolMint,
|
||||||
|
Token0Amount: formatTokenAmount(inputAmount),
|
||||||
|
Token1Amount: decimal.Zero,
|
||||||
|
Program: "PumpAMM",
|
||||||
|
Event: "buy",
|
||||||
|
IsToken2022: false,
|
||||||
|
IsMayhemMode: false,
|
||||||
|
ExactSOL: false,
|
||||||
|
Block: tx.Block,
|
||||||
|
Token0AmountUint64: inputAmount,
|
||||||
|
Token1AmountUint64: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
return signal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep lints happy if solana-go isn't referenced elsewhere in this file for build tags
|
||||||
|
var _ = solana.PublicKey{}
|
||||||
88
pkg/shreder/juptierv6_test.go
Normal file
88
pkg/shreder/juptierv6_test.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
package shreder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDecodeRouteV2Arg(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
hexData string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Jupiter V6 RouteV2Arg Test 0",
|
||||||
|
hexData: "bb64facc31c4af14809fd500000000002222e8db1800000064000a000000020000005601fe102700016310270102",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Jupiter V6 RouteV2Arg Test 1",
|
||||||
|
hexData: "bb64facc31c4af144ff91634b90000004e6c4d05000000002c013200000003000000520000000000000000102700014f102701024310270203",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Jupiter V6 RouteV2Arg Test 2",
|
||||||
|
hexData: "bb64facc31c4af14ba2eafa02c1d0000777a9b2200000000f4010a0000000100000052000000000000000010270001",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Jupiter V6 RouteV2Arg Test 3",
|
||||||
|
hexData: "bb64facc31c4af144a3521186b07000030508d0e00000000c201320000000300000052000000000000000010270001740110270102590010270203",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Jupiter V6 RouteV2Arg Test 4",
|
||||||
|
hexData: "bb64facc31c4af14092d05050000000013701f198c0100008102380100000300000059011027000168001027010251000000000000000010270203",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Jupiter V6 RouteV2Arg Test 5",
|
||||||
|
hexData: "bb64facc31c4af1480969800000000006f44ad39bd0000001202320000000200000068001027000151000000000000000010270102",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
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 := decodeJupiterV6RouteV2Arg(instrData[8:])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to decode jupiter arguments: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Logf("decoded args: %+v", args)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeRouteArg(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
hexData string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Jupiter V6 RouteArg Test 0",
|
||||||
|
hexData: "e517cb977ae3ad2a030000004f6400014f64010251000000000000000064020340420f00000000005c1c81900e000000640000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Jupiter V6 RouteArg Test 1",
|
||||||
|
hexData: "e517cb977ae3ad2a0200000028640001510000000000000000640102c09ee605000000005e1bc48efa000000d00700",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
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 := decodeJupiterV6RouteArg(instrData[8:])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to decode jupiter arguments: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Logf("decoded args: %+v", args)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
1
pkg/shreder/okxonchainlab.go
Normal file
1
pkg/shreder/okxonchainlab.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package shreder
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package shreder
|
package shreder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
"github.com/shopspring/decimal"
|
||||||
@@ -11,8 +13,23 @@ const (
|
|||||||
SolDecimals = 9
|
SolDecimals = 9
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
logger *slog.Logger
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})
|
||||||
|
logger = slog.New(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetLogLevel(level slog.Level) {
|
||||||
|
handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: level})
|
||||||
|
logger = slog.New(handler)
|
||||||
|
}
|
||||||
|
|
||||||
type TxSignal struct {
|
type TxSignal struct {
|
||||||
Source string `json:"source"`
|
Source string `json:"source"`
|
||||||
|
Label string `json:"label"`
|
||||||
TxHash string `json:"tx_hash"`
|
TxHash string `json:"tx_hash"`
|
||||||
Maker string `json:"maker"`
|
Maker string `json:"maker"`
|
||||||
Token0Address string `json:"token0_address"`
|
Token0Address string `json:"token0_address"`
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/gagliardetto/solana-go"
|
"github.com/gagliardetto/solana-go"
|
||||||
@@ -43,8 +42,23 @@ var (
|
|||||||
flasProgramID = solana.MustPublicKeyFromBase58("FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9")
|
flasProgramID = solana.MustPublicKeyFromBase58("FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9")
|
||||||
|
|
||||||
terminalProgramID = solana.MustPublicKeyFromBase58("term9YPb9mzAsABaqN71A4xdbxHmpBNZavpBiQKZzN3")
|
terminalProgramID = solana.MustPublicKeyFromBase58("term9YPb9mzAsABaqN71A4xdbxHmpBNZavpBiQKZzN3")
|
||||||
|
|
||||||
|
jupiterV6ProgramID = solana.MustPublicKeyFromBase58("JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type AccountNotFoundError struct {
|
||||||
|
Index int
|
||||||
|
Len int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAccountNotFoundError(i, l int) error {
|
||||||
|
return &AccountNotFoundError{i, l}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e AccountNotFoundError) Error() string {
|
||||||
|
return fmt.Sprintf("account index %d out of range, len=%d", e.Index, e.Len)
|
||||||
|
}
|
||||||
|
|
||||||
// instruction discriminators
|
// instruction discriminators
|
||||||
var (
|
var (
|
||||||
pumpCreateCoinIX = []byte{24, 30, 200, 40, 5, 28, 7, 119}
|
pumpCreateCoinIX = []byte{24, 30, 200, 40, 5, 28, 7, 119}
|
||||||
@@ -81,11 +95,6 @@ var (
|
|||||||
terminalAmmSellTokensIX = []byte{0x40, 0x64, 0x97, 0xb9, 0x16, 0xfa, 0xec, 0xb1}
|
terminalAmmSellTokensIX = []byte{0x40, 0x64, 0x97, 0xb9, 0x16, 0xfa, 0xec, 0xb1}
|
||||||
)
|
)
|
||||||
|
|
||||||
// table lookups
|
|
||||||
const (
|
|
||||||
photonTableLookup = "3r6paeFSLpeUVmWtShb5uZtXYpcBE3729kUxkUS7xKi1"
|
|
||||||
)
|
|
||||||
|
|
||||||
type compiledInstruction struct {
|
type compiledInstruction struct {
|
||||||
ProgramIDIndex uint8
|
ProgramIDIndex uint8
|
||||||
Accounts []uint8
|
Accounts []uint8
|
||||||
@@ -193,7 +202,7 @@ type fjszBuyArgs struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ParseTransaction mirrors the Rust parse_transaction entry point.
|
// ParseTransaction mirrors the Rust parse_transaction entry point.
|
||||||
func ParseTransaction(update *SubscribeUpdateTransaction) []*TxSignal {
|
func ParseTransaction(update *SubscribeUpdateTransaction, loader *AddressTables) []*TxSignal {
|
||||||
versioned, err := toVersionedTransaction(update)
|
versioned, err := toVersionedTransaction(update)
|
||||||
if err != nil || versioned == nil || len(versioned.Signatures) == 0 {
|
if err != nil || versioned == nil || len(versioned.Signatures) == 0 {
|
||||||
return nil
|
return nil
|
||||||
@@ -203,6 +212,34 @@ func ParseTransaction(update *SubscribeUpdateTransaction) []*TxSignal {
|
|||||||
staticKeys := versioned.Message.StaticAccountKeys
|
staticKeys := versioned.Message.StaticAccountKeys
|
||||||
instructions := versioned.Message.Instructions
|
instructions := versioned.Message.Instructions
|
||||||
|
|
||||||
|
if loader != nil && len(versioned.Message.AddressTableLookups) > 0 {
|
||||||
|
lookupTableOk := true
|
||||||
|
for _, lookup := range versioned.Message.AddressTableLookups {
|
||||||
|
if len(lookup.WritableIndexes) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
accounts := loader.GetAddressTable(lookup.AccountKey, lookup.WritableIndexes)
|
||||||
|
if len(accounts) != len(lookup.WritableIndexes) {
|
||||||
|
lookupTableOk = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
staticKeys = append(staticKeys, accounts...)
|
||||||
|
|
||||||
|
}
|
||||||
|
if lookupTableOk {
|
||||||
|
for _, lookup := range versioned.Message.AddressTableLookups {
|
||||||
|
if len(lookup.ReadonlyIndexes) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
accounts := loader.GetAddressTable(lookup.AccountKey, lookup.ReadonlyIndexes)
|
||||||
|
if len(accounts) != len(lookup.ReadonlyIndexes) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
staticKeys = append(staticKeys, accounts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var parsed []*TxSignal
|
var parsed []*TxSignal
|
||||||
|
|
||||||
for i := range instructions {
|
for i := range instructions {
|
||||||
@@ -243,6 +280,9 @@ func ParseTransaction(update *SubscribeUpdateTransaction) []*TxSignal {
|
|||||||
case terminalProgramID:
|
case terminalProgramID:
|
||||||
txRes, err := parseTermInstruction(versioned, i)
|
txRes, err := parseTermInstruction(versioned, i)
|
||||||
parsed = appendParsed(parsed, txRes, err, txHash, "terminal")
|
parsed = appendParsed(parsed, txRes, err, txHash, "terminal")
|
||||||
|
//case jupiterV6ProgramID:
|
||||||
|
// txRes, err := parseJupiterV6Instruction(versioned, i)
|
||||||
|
// parsed = appendParsed(parsed, txRes, err, txHash, "jupiterv6")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +291,10 @@ func ParseTransaction(update *SubscribeUpdateTransaction) []*TxSignal {
|
|||||||
|
|
||||||
func appendParsed(list []*TxSignal, parsed *TxSignal, err error, txHash [64]byte, label string) []*TxSignal {
|
func appendParsed(list []*TxSignal, parsed *TxSignal, err error, txHash [64]byte, label string) []*TxSignal {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("txparser: failed to parse", "label", label, "instruction", err, "tx_hash", base58.Encode(txHash[:]))
|
//if errors.Is(err, &AccountNotFoundError{}) {
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
logger.Debug("txparser: failed to parse", "label", label, "instruction", err, "tx_hash", base58.Encode(txHash[:]))
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
if parsed != nil {
|
if parsed != nil {
|
||||||
@@ -322,7 +365,7 @@ func formatSolAmount(lamports uint64) decimal.Decimal {
|
|||||||
|
|
||||||
func getStaticKey(static []solana.PublicKey, index int) (solana.PublicKey, error) {
|
func getStaticKey(static []solana.PublicKey, index int) (solana.PublicKey, error) {
|
||||||
if index < 0 || index >= len(static) {
|
if index < 0 || index >= len(static) {
|
||||||
return solana.PublicKey{}, fmt.Errorf("account index %d out of bounds", index)
|
return solana.PublicKey{}, NewAccountNotFoundError(index, len(static))
|
||||||
}
|
}
|
||||||
return static[index], nil
|
return static[index], nil
|
||||||
}
|
}
|
||||||
@@ -368,6 +411,7 @@ func parsePumpCreate(tx *versionedTransaction, instruction *compiledInstruction)
|
|||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "pump",
|
||||||
Maker: creator.String(),
|
Maker: creator.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -388,7 +432,7 @@ func parsePumpCreateV2(tx *versionedTransaction, instruction *compiledInstructio
|
|||||||
return nil, fmt.Errorf("accounts too short")
|
return nil, fmt.Errorf("accounts too short")
|
||||||
}
|
}
|
||||||
if len(instruction.Data) < 8 {
|
if len(instruction.Data) < 8 {
|
||||||
return nil, fmt.Errorf("data too short for create v2 args")
|
return nil, fmt.Errorf("data too short for pump create v2 args, len=%d", len(instruction.Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
staticKeys := tx.Message.StaticAccountKeys
|
staticKeys := tx.Message.StaticAccountKeys
|
||||||
@@ -408,6 +452,7 @@ func parsePumpCreateV2(tx *versionedTransaction, instruction *compiledInstructio
|
|||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "pump",
|
||||||
Maker: args.Creator.String(),
|
Maker: args.Creator.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -425,7 +470,7 @@ func parsePumpCreateV2(tx *versionedTransaction, instruction *compiledInstructio
|
|||||||
|
|
||||||
func decodePumpBuyArgs(data []byte) (uint64, uint64, error) {
|
func decodePumpBuyArgs(data []byte) (uint64, uint64, error) {
|
||||||
if len(data) < 9 {
|
if len(data) < 9 {
|
||||||
return 0, 0, fmt.Errorf("data too short for buy args")
|
return 0, 0, fmt.Errorf("data too short for pump buy buy args, len=%d", len(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
var args pumpBuyArgs
|
var args pumpBuyArgs
|
||||||
@@ -471,6 +516,7 @@ func parsePumpBuy(tx *versionedTransaction, instruction *compiledInstruction) (*
|
|||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "pump",
|
||||||
Maker: buyer.String(),
|
Maker: buyer.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -489,7 +535,7 @@ func parsePumpBuy(tx *versionedTransaction, instruction *compiledInstruction) (*
|
|||||||
|
|
||||||
func decodePumpSellArgs(data []byte) (uint64, uint64, error) {
|
func decodePumpSellArgs(data []byte) (uint64, uint64, error) {
|
||||||
if len(data) < 9 {
|
if len(data) < 9 {
|
||||||
return 0, 0, fmt.Errorf("data too short for sell args")
|
return 0, 0, fmt.Errorf("data too short for pump sell sell args, len=%d", len(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
var args pumpExtendedSellArgs
|
var args pumpExtendedSellArgs
|
||||||
@@ -528,6 +574,7 @@ func parsePumpSell(tx *versionedTransaction, instruction *compiledInstruction) (
|
|||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "pump",
|
||||||
Maker: seller.String(),
|
Maker: seller.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -579,13 +626,14 @@ func parseAzczAmmBuy(tx *versionedTransaction, instructionIndex int) (*TxSignal,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(instruction.Data) < 17 {
|
if len(instruction.Data) < 17 {
|
||||||
return nil, fmt.Errorf("data too short for buy args")
|
return nil, fmt.Errorf("data too short for azcz amm buy args, len=%d", len(instruction.Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
solAmount := binary.LittleEndian.Uint64(instruction.Data[1:9])
|
solAmount := binary.LittleEndian.Uint64(instruction.Data[1:9])
|
||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "azcz",
|
||||||
Maker: user.String(),
|
Maker: user.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -619,7 +667,7 @@ func parseAzczBuy(tx *versionedTransaction, instructionIndex int) (*TxSignal, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(instruction.Data) < 2 {
|
if len(instruction.Data) < 2 {
|
||||||
return nil, fmt.Errorf("data too short for buy args")
|
return nil, fmt.Errorf("data too short for azcz buy args len=%d", len(instruction.Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
var args azczBuyArgs
|
var args azczBuyArgs
|
||||||
@@ -629,6 +677,7 @@ func parseAzczBuy(tx *versionedTransaction, instructionIndex int) (*TxSignal, er
|
|||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "azcz",
|
||||||
Maker: user.String(),
|
Maker: user.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -673,7 +722,7 @@ func parseF5tfInstruction(tx *versionedTransaction, instructionIndex int) (*TxSi
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(instruction.Data) < 2 {
|
if len(instruction.Data) < 2 {
|
||||||
return nil, fmt.Errorf("data too short for buy args")
|
return nil, fmt.Errorf("data too short for f5tf buy args len=%d", len(instruction.Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
var args f5tfBuyArgs
|
var args f5tfBuyArgs
|
||||||
@@ -683,6 +732,7 @@ func parseF5tfInstruction(tx *versionedTransaction, instructionIndex int) (*TxSi
|
|||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "f5tf",
|
||||||
Maker: user.String(),
|
Maker: user.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -708,8 +758,11 @@ func parseFlasInstruction(tx *versionedTransaction, instructionIndex int) (*TxSi
|
|||||||
if len(instruction.Data) == 0 {
|
if len(instruction.Data) == 0 {
|
||||||
return nil, fmt.Errorf("data is empty")
|
return nil, fmt.Errorf("data is empty")
|
||||||
}
|
}
|
||||||
|
if len(instruction.Data) == 10 && instruction.Data[0] == 1 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
if len(instruction.Data) < 20 {
|
if len(instruction.Data) < 20 {
|
||||||
return nil, fmt.Errorf("data too short for args")
|
return nil, fmt.Errorf("data too short for args flas instruction, len: %d", len(instruction.Data))
|
||||||
}
|
}
|
||||||
methodData := instruction.Data[17:20]
|
methodData := instruction.Data[17:20]
|
||||||
if !matchMethod(methodData, flasBuyTokensIX) {
|
if !matchMethod(methodData, flasBuyTokensIX) {
|
||||||
@@ -750,6 +803,7 @@ func parseFlasAmmSell(tx *versionedTransaction, instructionIndex int) (*TxSignal
|
|||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "flas",
|
||||||
Maker: user.String(),
|
Maker: user.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -789,6 +843,7 @@ func parseFlasAmmBuy(tx *versionedTransaction, instructionIndex int) (*TxSignal,
|
|||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "flas",
|
||||||
Maker: user.String(),
|
Maker: user.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -828,6 +883,7 @@ func parseFlasSell(tx *versionedTransaction, instructionIndex int) (*TxSignal, e
|
|||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "flas",
|
||||||
Maker: user.String(),
|
Maker: user.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -866,6 +922,7 @@ func parseFlasBuy(tx *versionedTransaction, instructionIndex int) (*TxSignal, er
|
|||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "flas",
|
||||||
Maker: user.String(),
|
Maker: user.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -911,7 +968,7 @@ func parsePhotonBuy(tx *versionedTransaction, instruction *compiledInstruction)
|
|||||||
return nil, fmt.Errorf("accounts too short")
|
return nil, fmt.Errorf("accounts too short")
|
||||||
}
|
}
|
||||||
if len(instruction.Data) < 16 {
|
if len(instruction.Data) < 16 {
|
||||||
return nil, fmt.Errorf("data too short for buy args")
|
return nil, fmt.Errorf("data too short for photon buy args, len=%d", len(instruction.Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
staticKeys := tx.Message.StaticAccountKeys
|
staticKeys := tx.Message.StaticAccountKeys
|
||||||
@@ -932,6 +989,7 @@ func parsePhotonBuy(tx *versionedTransaction, instruction *compiledInstruction)
|
|||||||
solAmount := args.SolAmount * (100000000 - 1234568) / 100000000
|
solAmount := args.SolAmount * (100000000 - 1234568) / 100000000
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "photon",
|
||||||
Maker: user.String(),
|
Maker: user.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -952,7 +1010,7 @@ func parsePhotonSwap(tx *versionedTransaction, instruction *compiledInstruction)
|
|||||||
return nil, fmt.Errorf("accounts too short")
|
return nil, fmt.Errorf("accounts too short")
|
||||||
}
|
}
|
||||||
if len(instruction.Data) < 16 {
|
if len(instruction.Data) < 16 {
|
||||||
return nil, fmt.Errorf("data too short for swap args")
|
return nil, fmt.Errorf("data too short for swap args for photon. len=%d", len(instruction.Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
staticKeys := tx.Message.StaticAccountKeys
|
staticKeys := tx.Message.StaticAccountKeys
|
||||||
@@ -961,12 +1019,11 @@ func parsePhotonSwap(tx *versionedTransaction, instruction *compiledInstruction)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
quoteIndex := int(instruction.Accounts[4])
|
quote, err := getStaticKey(staticKeys, int(instruction.Accounts[4]))
|
||||||
quote, err := resolveQuoteAccount(tx, quoteIndex, []string{photonTableLookup}, 0)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if quote != wsolMint {
|
if !quote.Equals(solana.WrappedSol) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -988,6 +1045,7 @@ func parsePhotonSwap(tx *versionedTransaction, instruction *compiledInstruction)
|
|||||||
solAmount := args.FromAmount * (100000000 - 1234568) / 100000000
|
solAmount := args.FromAmount * (100000000 - 1234568) / 100000000
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "photon",
|
||||||
Maker: buyer.String(),
|
Maker: buyer.String(),
|
||||||
Token0Address: base.String(),
|
Token0Address: base.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -1069,6 +1127,7 @@ func parseTermAmmSell(tx *versionedTransaction, instruction *compiledInstruction
|
|||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "term",
|
||||||
Maker: user.String(),
|
Maker: user.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -1104,6 +1163,7 @@ func parseTermBuy(tx *versionedTransaction, instruction *compiledInstruction) (*
|
|||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "term",
|
||||||
Maker: user.String(),
|
Maker: user.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -1138,6 +1198,7 @@ func parseTermSell(tx *versionedTransaction, instruction *compiledInstruction) (
|
|||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "term",
|
||||||
Maker: user.String(),
|
Maker: user.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -1155,7 +1216,7 @@ func parseTermSell(tx *versionedTransaction, instruction *compiledInstruction) (
|
|||||||
|
|
||||||
func decodePumpAmmBuyArgs(data []byte) (uint64, uint64, error) {
|
func decodePumpAmmBuyArgs(data []byte) (uint64, uint64, error) {
|
||||||
if len(data) < 9 {
|
if len(data) < 9 {
|
||||||
return 0, 0, fmt.Errorf("data too short for buy args")
|
return 0, 0, fmt.Errorf("data too short for pump amm buy args, len=%d", len(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
var args pumpAmmBuyArgs
|
var args pumpAmmBuyArgs
|
||||||
@@ -1194,12 +1255,11 @@ func parsePumpAmmBuy(tx *versionedTransaction, instruction *compiledInstruction)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
quoteIndex := int(instruction.Accounts[4])
|
quote, err := getStaticKey(staticKeys, int(instruction.Accounts[4]))
|
||||||
quote, err := resolveQuoteAccount(tx, quoteIndex, nil, 0)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if quote != wsolMint {
|
if !quote.Equals(solana.WrappedSol) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1210,6 +1270,7 @@ func parsePumpAmmBuy(tx *versionedTransaction, instruction *compiledInstruction)
|
|||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "pumpamm",
|
||||||
Maker: buyer.String(),
|
Maker: buyer.String(),
|
||||||
Token0Address: base.String(),
|
Token0Address: base.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -1241,12 +1302,11 @@ func parsePumpAmmSell(tx *versionedTransaction, instruction *compiledInstruction
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
quoteIndex := int(instruction.Accounts[4])
|
quote, err := getStaticKey(staticKeys, int(instruction.Accounts[4]))
|
||||||
quote, err := resolveQuoteAccount(tx, quoteIndex, nil, 0)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if quote != wsolMint {
|
if !quote.Equals(solana.WrappedSol) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1257,6 +1317,7 @@ func parsePumpAmmSell(tx *versionedTransaction, instruction *compiledInstruction
|
|||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "pumpamm",
|
||||||
Maker: buyer.String(),
|
Maker: buyer.String(),
|
||||||
Token0Address: base.String(),
|
Token0Address: base.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -1290,7 +1351,7 @@ func parseBoboInstruction(tx *versionedTransaction, instructionIndex int) (*TxSi
|
|||||||
return nil, fmt.Errorf("accounts too short")
|
return nil, fmt.Errorf("accounts too short")
|
||||||
}
|
}
|
||||||
if len(instruction.Data) < 16 {
|
if len(instruction.Data) < 16 {
|
||||||
return nil, fmt.Errorf("data too short for buy args")
|
return nil, fmt.Errorf("data too short for bobo buy args, len=%d", len(instruction.Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
staticKeys := tx.Message.StaticAccountKeys
|
staticKeys := tx.Message.StaticAccountKeys
|
||||||
@@ -1310,6 +1371,7 @@ func parseBoboInstruction(tx *versionedTransaction, instructionIndex int) (*TxSi
|
|||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "bobo",
|
||||||
Maker: user.String(),
|
Maker: user.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -1353,7 +1415,7 @@ func parseQtkvSell(tx *versionedTransaction, instructionIndex int) (*TxSignal, e
|
|||||||
return nil, fmt.Errorf("accounts too short")
|
return nil, fmt.Errorf("accounts too short")
|
||||||
}
|
}
|
||||||
if len(instruction.Data) < 24 {
|
if len(instruction.Data) < 24 {
|
||||||
return nil, fmt.Errorf("data too short for sell args")
|
return nil, fmt.Errorf("data too short for qtkv sell args, len=%d", len(instruction.Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
staticKeys := tx.Message.StaticAccountKeys
|
staticKeys := tx.Message.StaticAccountKeys
|
||||||
@@ -1370,6 +1432,7 @@ func parseQtkvSell(tx *versionedTransaction, instructionIndex int) (*TxSignal, e
|
|||||||
tokenAmount := binary.LittleEndian.Uint64(instruction.Data[19:25])
|
tokenAmount := binary.LittleEndian.Uint64(instruction.Data[19:25])
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "qtkv",
|
||||||
Maker: user.String(),
|
Maker: user.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -1391,7 +1454,7 @@ func parseQtkvAmmSell(tx *versionedTransaction, instructionIndex int) (*TxSignal
|
|||||||
return nil, fmt.Errorf("accounts too short")
|
return nil, fmt.Errorf("accounts too short")
|
||||||
}
|
}
|
||||||
if len(instruction.Data) < 24 {
|
if len(instruction.Data) < 24 {
|
||||||
return nil, fmt.Errorf("data too short for sell args")
|
return nil, fmt.Errorf("data too short for qtkv amm sell args, len=%d", len(instruction.Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
staticKeys := tx.Message.StaticAccountKeys
|
staticKeys := tx.Message.StaticAccountKeys
|
||||||
@@ -1408,6 +1471,7 @@ func parseQtkvAmmSell(tx *versionedTransaction, instructionIndex int) (*TxSignal
|
|||||||
tokenAmount := binary.LittleEndian.Uint64(instruction.Data[19:25])
|
tokenAmount := binary.LittleEndian.Uint64(instruction.Data[19:25])
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "qtkv",
|
||||||
Maker: user.String(),
|
Maker: user.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -1446,6 +1510,7 @@ func parseQtkvBuy(tx *versionedTransaction, instructionIndex int) (*TxSignal, er
|
|||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "qtkv",
|
||||||
Maker: user.String(),
|
Maker: user.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -1479,7 +1544,7 @@ func parseFjszInstruction(tx *versionedTransaction, instructionIndex int) (*TxSi
|
|||||||
return nil, fmt.Errorf("accounts too short")
|
return nil, fmt.Errorf("accounts too short")
|
||||||
}
|
}
|
||||||
if len(instruction.Data) < 16 {
|
if len(instruction.Data) < 16 {
|
||||||
return nil, fmt.Errorf("data too short for buy args")
|
return nil, fmt.Errorf("data too short for fjzs buy args, len=%d", len(instruction.Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
staticKeys := tx.Message.StaticAccountKeys
|
staticKeys := tx.Message.StaticAccountKeys
|
||||||
@@ -1499,6 +1564,7 @@ func parseFjszInstruction(tx *versionedTransaction, instructionIndex int) (*TxSi
|
|||||||
|
|
||||||
return &TxSignal{
|
return &TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
|
Label: "fjsz",
|
||||||
Maker: user.String(),
|
Maker: user.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: mint.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: wsolMint,
|
||||||
@@ -1535,43 +1601,6 @@ func parseTerminalInstruction(tx *versionedTransaction, instructionIndex int) (*
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveQuoteAccount(tx *versionedTransaction, quoteIndex int, expectedTableKeys []string, targetIndex uint8) (string, error) {
|
|
||||||
staticKeys := tx.Message.StaticAccountKeys
|
|
||||||
if quoteIndex < len(staticKeys) {
|
|
||||||
quoteKey := staticKeys[quoteIndex].String()
|
|
||||||
return quoteKey, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// attempt to load from address table lookup
|
|
||||||
if len(expectedTableKeys) == 0 || len(tx.Message.AddressTableLookups) != 1 {
|
|
||||||
return "", fmt.Errorf("parse quote from table lookup failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
table := tx.Message.AddressTableLookups[0]
|
|
||||||
match := false
|
|
||||||
for _, key := range expectedTableKeys {
|
|
||||||
if table.AccountKey.String() == key {
|
|
||||||
match = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !match {
|
|
||||||
return "", fmt.Errorf("parse quote from table lookup failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
indexOfTarget := indexOf(table.ReadonlyIndexes, targetIndex)
|
|
||||||
if indexOfTarget < 0 {
|
|
||||||
return "", fmt.Errorf("parse quote from table lookup failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedIndex := len(staticKeys) + len(table.WritableIndexes) + indexOfTarget
|
|
||||||
if quoteIndex != expectedIndex {
|
|
||||||
return "", fmt.Errorf("parse quote from table lookup failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
return wsolMint, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func indexOf(haystack []uint8, needle uint8) int {
|
func indexOf(haystack []uint8, needle uint8) int {
|
||||||
for i, v := range haystack {
|
for i, v := range haystack {
|
||||||
if v == needle {
|
if v == needle {
|
||||||
|
|||||||
Reference in New Issue
Block a user