Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 149dfae378 | |||
| 8c4b43747c | |||
|
|
e9ba16766f | ||
|
|
cd1d681621 |
120
chainlink.go
Normal file
120
chainlink.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package pump_parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/gagliardetto/solana-go"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
var (
|
||||
chainlinkSOLUSDFeedAccount = solana.MustPublicKeyFromBase58("CH31Xns5z3M1cTAbKW34jcxPPciazARpijcHj9rxtemt")
|
||||
chainlinkSubmitDiscriminator = calculateDiscriminator("global:submit")
|
||||
)
|
||||
|
||||
func chainLinkParser(tx *Tx, instruction Instruction, inners InnerInstructions, offset [2]uint) ([2]uint, error) {
|
||||
if !tx.rawTx.accountList[instruction.ProgramIDIndex].Equals(chainLinkProgram) {
|
||||
return increaseOffset(offset), fmt.Errorf("system program instruction not found, block: %d, tx: %s, outerIndex: %d, innerIndex: %d", tx.rawTx.Slot, tx.rawTx.TxHash(), offset[0], offset[1])
|
||||
}
|
||||
if tx.rawTx.Meta.Err != nil {
|
||||
return increaseOffset(offset), nil
|
||||
}
|
||||
|
||||
decode := instruction.Data
|
||||
discriminator := binary.LittleEndian.Uint32(decode[0:4])
|
||||
|
||||
switch discriminator {
|
||||
case transferDiscriminator:
|
||||
return chainLinkSubmitParser(instruction, inners, offset, tx, decode[4:])
|
||||
default:
|
||||
return increaseOffset(offset), nil
|
||||
}
|
||||
}
|
||||
|
||||
func chainLinkSubmitParser(instruction Instruction, inners InnerInstructions, offset [2]uint, tx *Tx, decodeData []byte) ([2]uint, error) {
|
||||
if len(instruction.Accounts) < 6 {
|
||||
return increaseOffset(offset), InstructionIgnoredError
|
||||
}
|
||||
|
||||
inner, err := getInnerInstructions(inners, offset[1])
|
||||
if err != nil {
|
||||
return increaseOffset(offset), err
|
||||
}
|
||||
|
||||
if len(inner) < 1 {
|
||||
return increaseOffset(offset), InstructionIgnoredError
|
||||
}
|
||||
storeInstruction := inner[0]
|
||||
if len(storeInstruction.Accounts) < 2 {
|
||||
return increaseOffset(offset), InstructionIgnoredError
|
||||
}
|
||||
if storeInstruction.Accounts[0] >= len(tx.rawTx.accountList) || tx.rawTx.accountList[storeInstruction.Accounts[0]] != chainlinkSOLUSDFeedAccount {
|
||||
return increaseOffset(offset), InstructionIgnoredError
|
||||
}
|
||||
if !bytes.Equal(storeInstruction.Data[0:8], chainlinkSubmitDiscriminator[:]) {
|
||||
return increaseOffset(offset), InstructionIgnoredError
|
||||
}
|
||||
data, err := parseChainLinkSubmitData(storeInstruction.Data)
|
||||
if err != nil {
|
||||
return increaseOffset(offset), InstructionIgnoredError
|
||||
}
|
||||
tx.ChainLink.Timestamp = int64(data.Timestamp)
|
||||
tx.ChainLink.Price = decimal.NewFromBigInt(data.Price(), -8)
|
||||
return increaseOffset(offset), nil
|
||||
}
|
||||
|
||||
type SubmitData struct {
|
||||
Discriminator [8]byte
|
||||
Timestamp uint64
|
||||
Answer [16]byte
|
||||
}
|
||||
|
||||
func parseChainLinkSubmitData(data []byte) (*SubmitData, error) {
|
||||
if len(data) != 32 {
|
||||
return nil, errors.New("invalid submit data length")
|
||||
}
|
||||
var submitData SubmitData
|
||||
copy(submitData.Discriminator[:], data[:8])
|
||||
submitData.Timestamp = binary.LittleEndian.Uint64(data[8:16])
|
||||
copy(submitData.Answer[:], data[16:32])
|
||||
return &submitData, nil
|
||||
}
|
||||
|
||||
func (s *SubmitData) Price() *big.Int {
|
||||
return int128LEBytesToBigInt(s.Answer)
|
||||
}
|
||||
|
||||
func int128LEBytesToBigInt(bytes [16]byte) *big.Int {
|
||||
// Create new big.Int
|
||||
bigInt := new(big.Int)
|
||||
|
||||
// Reverse bytes for little-endian to big-endian conversion
|
||||
reversed := make([]byte, 16)
|
||||
for i := 0; i < 16; i++ {
|
||||
reversed[15-i] = bytes[i]
|
||||
}
|
||||
|
||||
// Check if negative (first byte in little-endian is highest byte)
|
||||
isNegative := bytes[15]&0x80 != 0
|
||||
|
||||
if isNegative {
|
||||
// If negative, flip all bits
|
||||
for i := range reversed {
|
||||
reversed[i] = ^reversed[i]
|
||||
}
|
||||
// Convert to big.Int
|
||||
bigInt.SetBytes(reversed)
|
||||
// Add 1 and negate
|
||||
bigInt.Add(bigInt, big.NewInt(1))
|
||||
bigInt.Neg(bigInt)
|
||||
} else {
|
||||
// If positive, convert directly
|
||||
bigInt.SetBytes(reversed)
|
||||
}
|
||||
|
||||
return bigInt
|
||||
}
|
||||
@@ -41,6 +41,12 @@ var platformFeeAddresses = map[solana.PublicKey]string{
|
||||
solana.MustPublicKeyFromBase58("9yMwSPk9mrXSN7yDHUuZurAh1sjbJsfpUqjZ7SvVtdco"): PlatformTrojan,
|
||||
solana.MustPublicKeyFromBase58("92Med3qeK7duC5iiYsHX38H2f2twJfRsSx93oNrza2VH"): PlatformTrojan,
|
||||
solana.MustPublicKeyFromBase58("65gDv7pZQCZELsNpNYSFEBtNFpWZAbxmRFB6BGMqFkHH"): PlatformTrojan,
|
||||
solana.MustPublicKeyFromBase58("8jgg7moFJkHyTtAv9M6RBSPMp2oXeXhuiUMKW8YbYCWn"): PlatformTrojan,
|
||||
solana.MustPublicKeyFromBase58("BJgbYMZgqm79gNrmm31tV3L8GQorw91XFm4m7evyfPjr"): PlatformTrojan,
|
||||
solana.MustPublicKeyFromBase58("BWgb8wR1FEGiu1jCDSKuHKf752W27b4iN6SvoNCiK4qp"): PlatformTrojan,
|
||||
solana.MustPublicKeyFromBase58("GV4Bt6ehW5x5dqtaWAJBSnz8uum5Z2Rp9P2Tr5iVuQn5"): PlatformTrojan,
|
||||
solana.MustPublicKeyFromBase58("2jwHNxavSoMZMEDbT1eV9PcPt5dDcayCqM6MkgaPpmWQ"): PlatformTrojan,
|
||||
solana.MustPublicKeyFromBase58("66N1M2aaDSdJFZ1d7YoVN4EU45ju6XiscapLVHn5FLms"): PlatformTrojan,
|
||||
solana.MustPublicKeyFromBase58("4mih95RmBqfHYvEfqq6uGGLp1Fr3gVS3VNSEa3JVRfQK"): PlatformRaybot,
|
||||
solana.MustPublicKeyFromBase58("3udvfL24waJcLhskRAsStNMoNUvtyXdxrWQz4hgi953N"): PlatformMoonshot,
|
||||
solana.MustPublicKeyFromBase58("3kxSQybWEeQZsMuNWMRJH4TxrhwoDwfv41TNMLRzFP5A"): PlatformMEVX,
|
||||
@@ -360,6 +366,7 @@ var entryContractAddresses = map[solana.PublicKey]string{
|
||||
solana.MustPublicKeyFromBase58("B3111yJCeHBcA1bizdJjUFPALfhAfSRnAbJzGUtnt56A"): EntryContractBinanceWallet,
|
||||
solana.MustPublicKeyFromBase58("FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9"): EntryContractAxiom,
|
||||
solana.MustPublicKeyFromBase58("F5tfvbLog9VdGUPqBDTT8rgXvTTcq7e5UiGnupL1zvBq"): EntryContractAxiom,
|
||||
solana.MustPublicKeyFromBase58("Gz9VPiSLQYbvKyb3jZPjNfyA6n4T4qVFUuAukgL964nL"): EntryContractAxiom,
|
||||
solana.MustPublicKeyFromBase58("B3jytJa6Tzpn4Ly7GNnDm3dMGqUin5aMRm5aPsJGU5G7"): EntryContractTradewiz,
|
||||
solana.MustPublicKeyFromBase58("DBotWvSso9oD1ZB3aHx2LiD2ZoFpF8PbKjaT4uHKLLVs"): EntryContractDbot,
|
||||
solana.MustPublicKeyFromBase58("term9YPb9mzAsABaqN71A4xdbxHmpBNZavpBiQKZzN3"): EntryContractPadre,
|
||||
@@ -367,3 +374,5 @@ var entryContractAddresses = map[solana.PublicKey]string{
|
||||
|
||||
var okxDexRoutersV2 = solana.MustPublicKeyFromBase58("proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u")
|
||||
var okxAggregatorV2 = solana.MustPublicKeyFromBase58("6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma")
|
||||
|
||||
var axiomOuterContract = solana.MustPublicKeyFromBase58("FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9")
|
||||
|
||||
2
meta.go
2
meta.go
@@ -236,4 +236,6 @@ var createAccountWithSeedDiscriminator = uint32(3)
|
||||
var systemProgram = solana.MustPublicKeyFromBase58("11111111111111111111111111111111")
|
||||
var momoProgram = solana.MustPublicKeyFromBase58("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr")
|
||||
|
||||
var chainLinkProgram = solana.MustPublicKeyFromBase58("cjg3oHmg9uuPsP8D6g29NWvhySJkdYdAo9D25PRbKXJ")
|
||||
|
||||
var eventDiscriminator = [8]byte{228, 69, 165, 46, 81, 203, 154, 29}
|
||||
|
||||
@@ -60,8 +60,9 @@ func WithMeteoraDlmm() ParserOption {
|
||||
}
|
||||
|
||||
var actionPrograms = map[solana.PublicKey]actionParser{
|
||||
systemProgram: systemParser,
|
||||
budgGetProgram: budgetParser,
|
||||
systemProgram: systemParser,
|
||||
budgGetProgram: budgetParser,
|
||||
chainLinkProgram: chainLinkParser,
|
||||
}
|
||||
|
||||
func ParseRawTx(rawTx *RawTx) (*Tx, error) {
|
||||
|
||||
14
pump.go
14
pump.go
@@ -225,7 +225,7 @@ func failedTxBuyOrSellParser(tx *Tx, instruction Instruction, innerInstructions
|
||||
if tx.Err.Variant != InstructionError {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed tx pump failed but error variant is not instruction error, offset, %d, %d", offset[0], offset[1])
|
||||
}
|
||||
if tx.Err.Enum != Custom && tx.Err.Enum != ComputationalBudgetExceeded {
|
||||
if tx.Err.Enum != Custom && tx.Err.Enum != ComputationalBudgetExceeded && tx.Err.Enum != ProgramFailedToComplete {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed tx pump failed but error is not custom or computational budget exceeded, offset, %d, %d", offset[0], offset[1])
|
||||
}
|
||||
if tx.Err.Enum == Custom {
|
||||
@@ -344,11 +344,13 @@ func BuyOrSellParser(tx *Tx, instruction Instruction, innerInstructions InnerIns
|
||||
if err != nil {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("pump create get inner instructions error: %v,offset, %d, %d", err, offset[0], offset[1])
|
||||
}
|
||||
if instruction.StackHeight != nil && *instruction.StackHeight > 2 {
|
||||
for _, innerInstr := range innerInstructions.Instructions {
|
||||
if innerInstr.StackHeight != nil && *innerInstr.StackHeight == *instruction.StackHeight-1 {
|
||||
entryContract = result.accountList[innerInstr.ProgramIDIndex]
|
||||
break
|
||||
if !entryContract.Equals(axiomOuterContract) {
|
||||
if instruction.StackHeight != nil && *instruction.StackHeight > 2 {
|
||||
for _, innerInstr := range innerInstructions.Instructions {
|
||||
if innerInstr.StackHeight != nil && *innerInstr.StackHeight == *instruction.StackHeight-1 {
|
||||
entryContract = result.accountList[innerInstr.ProgramIDIndex]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
28
pumpamm.go
28
pumpamm.go
@@ -268,7 +268,7 @@ func failedTxAmmBuyParser(tx *Tx, instruction Instruction, innerInstructions Inn
|
||||
if tx.Err.Variant != InstructionError {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed tx pump amm sell failed but error variant is not instruction error, offset, %d, %d", offset[0], offset[1])
|
||||
}
|
||||
if tx.Err.Enum != Custom && tx.Err.Enum != ComputationalBudgetExceeded {
|
||||
if tx.Err.Enum != Custom && tx.Err.Enum != ComputationalBudgetExceeded && tx.Err.Enum != ProgramFailedToComplete {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed tx pump amm sell failed but error is not custom or computational budget exceeded, offset, %d, %d", offset[0], offset[1])
|
||||
}
|
||||
if tx.Err.Enum == Custom {
|
||||
@@ -392,7 +392,7 @@ func failedTxAmmSellParser(tx *Tx, instruction Instruction, innerInstructions In
|
||||
if tx.Err.Variant != InstructionError {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed tx pump amm sell failed but error variant is not instruction error, offset, %d, %d", offset[0], offset[1])
|
||||
}
|
||||
if tx.Err.Enum != Custom && tx.Err.Enum != ComputationalBudgetExceeded {
|
||||
if tx.Err.Enum != Custom && tx.Err.Enum != ComputationalBudgetExceeded && tx.Err.Enum != ProgramFailedToComplete {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed tx pump amm sell failed but error is not custom or computational budget exceeded, offset, %d, %d", offset[0], offset[1])
|
||||
}
|
||||
if tx.Err.Enum == Custom {
|
||||
@@ -512,11 +512,13 @@ func ammBuyParser(tx *Tx, instruction Instruction, innerInstructions InnerInstru
|
||||
if err != nil {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("pumpamm create get inner instructions error: %v, offset: %d, %d", err, offset[0], prefixLen)
|
||||
}
|
||||
if instruction.StackHeight != nil && *instruction.StackHeight > 2 {
|
||||
for _, innerInstr := range innerInstructions.Instructions {
|
||||
if innerInstr.StackHeight != nil && *innerInstr.StackHeight == *instruction.StackHeight-1 {
|
||||
entryContract = result.accountList[innerInstr.ProgramIDIndex]
|
||||
break
|
||||
if !entryContract.Equals(axiomOuterContract) {
|
||||
if instruction.StackHeight != nil && *instruction.StackHeight > 2 {
|
||||
for _, innerInstr := range innerInstructions.Instructions {
|
||||
if innerInstr.StackHeight != nil && *innerInstr.StackHeight == *instruction.StackHeight-1 {
|
||||
entryContract = result.accountList[innerInstr.ProgramIDIndex]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -633,11 +635,13 @@ func ammSellParser(tx *Tx, instruction Instruction, innerInstructions InnerInstr
|
||||
return nil, increaseOffset(offset), fmt.Errorf("pumpamm sell get inner instructions error: %v, offset: %d, %d", err, offset[0], prefixLen)
|
||||
}
|
||||
|
||||
if instruction.StackHeight != nil && *instruction.StackHeight > 2 {
|
||||
for _, innerInstr := range innerInstructions.Instructions {
|
||||
if innerInstr.StackHeight != nil && *innerInstr.StackHeight == *instruction.StackHeight-1 {
|
||||
entryContract = result.accountList[innerInstr.ProgramIDIndex]
|
||||
break
|
||||
if !entryContract.Equals(axiomOuterContract) {
|
||||
if instruction.StackHeight != nil && *instruction.StackHeight > 2 {
|
||||
for _, innerInstr := range innerInstructions.Instructions {
|
||||
if innerInstr.StackHeight != nil && *innerInstr.StackHeight == *instruction.StackHeight-1 {
|
||||
entryContract = result.accountList[innerInstr.ProgramIDIndex]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2
rawtx.go
2
rawtx.go
@@ -354,7 +354,7 @@ func FromRpcTransactionWithMeta(tx rpc.TransactionWithMeta, blockTime *uint64, s
|
||||
}
|
||||
errDetail, ok := oErr[1].(string)
|
||||
if ok {
|
||||
if errDetail == "ComputationalBudgetExceeded" {
|
||||
if errDetail == "ComputationalBudgetExceeded" || errDetail == "ProgramFailedToComplete" {
|
||||
sTx.Meta.Err.Enum = ComputationalBudgetExceeded
|
||||
} else {
|
||||
sTx.Meta.Err.UnKnown = errDetail
|
||||
|
||||
5
tx.go
5
tx.go
@@ -74,6 +74,10 @@ type SolTransfer struct {
|
||||
To solana.PublicKey
|
||||
Amount decimal.Decimal
|
||||
}
|
||||
type ChainLink struct {
|
||||
Timestamp int64
|
||||
Price decimal.Decimal
|
||||
}
|
||||
|
||||
type Tx struct {
|
||||
rawTx *RawTx
|
||||
@@ -83,6 +87,7 @@ type Tx struct {
|
||||
Swaps []Swap `json:"swaps,omitempty"`
|
||||
SolTransfer []SolTransfer `json:"sol_transfer,omitempty"`
|
||||
Block uint64 `json:"block"`
|
||||
ChainLink ChainLink `json:"chain_link"`
|
||||
BlockIndex uint64 `json:"index"`
|
||||
TxHash *[64]byte `json:"-"`
|
||||
BlockAt int64 `json:"block_at"`
|
||||
|
||||
Reference in New Issue
Block a user