Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09de6ba649 | ||
|
|
7a82990770 | ||
|
|
e82bcb3c07 | ||
|
|
a74f769064 | ||
|
|
1e276e8bd2 | ||
|
|
eb2bde98ac | ||
| 66f0d247f5 | |||
| 879b7fefad | |||
| 149dfae378 | |||
| 8c4b43747c | |||
|
|
e9ba16766f |
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
|
||||||
|
}
|
||||||
11
consts.go
11
consts.go
@@ -41,6 +41,12 @@ var platformFeeAddresses = map[solana.PublicKey]string{
|
|||||||
solana.MustPublicKeyFromBase58("9yMwSPk9mrXSN7yDHUuZurAh1sjbJsfpUqjZ7SvVtdco"): PlatformTrojan,
|
solana.MustPublicKeyFromBase58("9yMwSPk9mrXSN7yDHUuZurAh1sjbJsfpUqjZ7SvVtdco"): PlatformTrojan,
|
||||||
solana.MustPublicKeyFromBase58("92Med3qeK7duC5iiYsHX38H2f2twJfRsSx93oNrza2VH"): PlatformTrojan,
|
solana.MustPublicKeyFromBase58("92Med3qeK7duC5iiYsHX38H2f2twJfRsSx93oNrza2VH"): PlatformTrojan,
|
||||||
solana.MustPublicKeyFromBase58("65gDv7pZQCZELsNpNYSFEBtNFpWZAbxmRFB6BGMqFkHH"): 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("4mih95RmBqfHYvEfqq6uGGLp1Fr3gVS3VNSEa3JVRfQK"): PlatformRaybot,
|
||||||
solana.MustPublicKeyFromBase58("3udvfL24waJcLhskRAsStNMoNUvtyXdxrWQz4hgi953N"): PlatformMoonshot,
|
solana.MustPublicKeyFromBase58("3udvfL24waJcLhskRAsStNMoNUvtyXdxrWQz4hgi953N"): PlatformMoonshot,
|
||||||
solana.MustPublicKeyFromBase58("3kxSQybWEeQZsMuNWMRJH4TxrhwoDwfv41TNMLRzFP5A"): PlatformMEVX,
|
solana.MustPublicKeyFromBase58("3kxSQybWEeQZsMuNWMRJH4TxrhwoDwfv41TNMLRzFP5A"): PlatformMEVX,
|
||||||
@@ -61,6 +67,7 @@ var platformFeeAddresses = map[solana.PublicKey]string{
|
|||||||
solana.MustPublicKeyFromBase58("HkJYryz2BNeMQfuuSWDYktWt5fZLV26eK6nqu7EJycoG"): PlatformAxiom,
|
solana.MustPublicKeyFromBase58("HkJYryz2BNeMQfuuSWDYktWt5fZLV26eK6nqu7EJycoG"): PlatformAxiom,
|
||||||
solana.MustPublicKeyFromBase58("BfFX9rUm8qTZiZjmeq9BktWVTNuG3YWMc5AvkrCKJike"): PlatformAxiom,
|
solana.MustPublicKeyFromBase58("BfFX9rUm8qTZiZjmeq9BktWVTNuG3YWMc5AvkrCKJike"): PlatformAxiom,
|
||||||
solana.MustPublicKeyFromBase58("2ApLdwLrGayEmxgpLX9BTR47Q2QprfMg5SpjrLeaK8s7"): PlatformAxiom,
|
solana.MustPublicKeyFromBase58("2ApLdwLrGayEmxgpLX9BTR47Q2QprfMg5SpjrLeaK8s7"): PlatformAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("AVahywMVNRYzdgWrufSWrtdGXAeNEvfpJFxhVFK516mT"): PlatformDexScreener,
|
||||||
}
|
}
|
||||||
|
|
||||||
var mevAgentFeeAddresses = map[solana.PublicKey]string{
|
var mevAgentFeeAddresses = map[solana.PublicKey]string{
|
||||||
@@ -190,6 +197,7 @@ var mevAgentFeeAddresses = map[solana.PublicKey]string{
|
|||||||
solana.MustPublicKeyFromBase58("ASde6y8pBCU1aityWHRpqT7pEAcEonjCgFUMeh5egRes"): MevAgentAstralane,
|
solana.MustPublicKeyFromBase58("ASde6y8pBCU1aityWHRpqT7pEAcEonjCgFUMeh5egRes"): MevAgentAstralane,
|
||||||
solana.MustPublicKeyFromBase58("ASUv6G8Cj6zt71UAqD1aVtDC3CRn6FFddqF17ZiegrES"): MevAgentAstralane,
|
solana.MustPublicKeyFromBase58("ASUv6G8Cj6zt71UAqD1aVtDC3CRn6FFddqF17ZiegrES"): MevAgentAstralane,
|
||||||
solana.MustPublicKeyFromBase58("ASY4mvCtrACKFK8Jiuvqcu8fad9gGTzvfm5zp4megRes"): MevAgentAstralane,
|
solana.MustPublicKeyFromBase58("ASY4mvCtrACKFK8Jiuvqcu8fad9gGTzvfm5zp4megRes"): MevAgentAstralane,
|
||||||
|
solana.MustPublicKeyFromBase58("astraubkDw81n4LuutzSQ8uzHCv4BhPVhfvTcYv8SKC"): MevAgentAstralane,
|
||||||
solana.MustPublicKeyFromBase58("astraEJ2fEj8Xmy6KLG7B3VfbKfsHXhHrNdCQx7iGJK"): MevAgentAstralane,
|
solana.MustPublicKeyFromBase58("astraEJ2fEj8Xmy6KLG7B3VfbKfsHXhHrNdCQx7iGJK"): MevAgentAstralane,
|
||||||
solana.MustPublicKeyFromBase58("B1ooMsWjc4SUVVuLyCu1ig2RdomQnHKgMzBMfmSo3DK"): MevAgentAstralane,
|
solana.MustPublicKeyFromBase58("B1ooMsWjc4SUVVuLyCu1ig2RdomQnHKgMzBMfmSo3DK"): MevAgentAstralane,
|
||||||
solana.MustPublicKeyFromBase58("B1ooMZfUJmAvppzc5cr7eYG8Cenig4FbQGBytr4DGCh"): MevAgentAstralane,
|
solana.MustPublicKeyFromBase58("B1ooMZfUJmAvppzc5cr7eYG8Cenig4FbQGBytr4DGCh"): MevAgentAstralane,
|
||||||
@@ -360,6 +368,7 @@ var entryContractAddresses = map[solana.PublicKey]string{
|
|||||||
solana.MustPublicKeyFromBase58("B3111yJCeHBcA1bizdJjUFPALfhAfSRnAbJzGUtnt56A"): EntryContractBinanceWallet,
|
solana.MustPublicKeyFromBase58("B3111yJCeHBcA1bizdJjUFPALfhAfSRnAbJzGUtnt56A"): EntryContractBinanceWallet,
|
||||||
solana.MustPublicKeyFromBase58("FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9"): EntryContractAxiom,
|
solana.MustPublicKeyFromBase58("FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9"): EntryContractAxiom,
|
||||||
solana.MustPublicKeyFromBase58("F5tfvbLog9VdGUPqBDTT8rgXvTTcq7e5UiGnupL1zvBq"): EntryContractAxiom,
|
solana.MustPublicKeyFromBase58("F5tfvbLog9VdGUPqBDTT8rgXvTTcq7e5UiGnupL1zvBq"): EntryContractAxiom,
|
||||||
|
solana.MustPublicKeyFromBase58("Gz9VPiSLQYbvKyb3jZPjNfyA6n4T4qVFUuAukgL964nL"): EntryContractAxiom,
|
||||||
solana.MustPublicKeyFromBase58("B3jytJa6Tzpn4Ly7GNnDm3dMGqUin5aMRm5aPsJGU5G7"): EntryContractTradewiz,
|
solana.MustPublicKeyFromBase58("B3jytJa6Tzpn4Ly7GNnDm3dMGqUin5aMRm5aPsJGU5G7"): EntryContractTradewiz,
|
||||||
solana.MustPublicKeyFromBase58("DBotWvSso9oD1ZB3aHx2LiD2ZoFpF8PbKjaT4uHKLLVs"): EntryContractDbot,
|
solana.MustPublicKeyFromBase58("DBotWvSso9oD1ZB3aHx2LiD2ZoFpF8PbKjaT4uHKLLVs"): EntryContractDbot,
|
||||||
solana.MustPublicKeyFromBase58("term9YPb9mzAsABaqN71A4xdbxHmpBNZavpBiQKZzN3"): EntryContractPadre,
|
solana.MustPublicKeyFromBase58("term9YPb9mzAsABaqN71A4xdbxHmpBNZavpBiQKZzN3"): EntryContractPadre,
|
||||||
@@ -367,3 +376,5 @@ var entryContractAddresses = map[solana.PublicKey]string{
|
|||||||
|
|
||||||
var okxDexRoutersV2 = solana.MustPublicKeyFromBase58("proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u")
|
var okxDexRoutersV2 = solana.MustPublicKeyFromBase58("proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u")
|
||||||
var okxAggregatorV2 = solana.MustPublicKeyFromBase58("6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma")
|
var okxAggregatorV2 = solana.MustPublicKeyFromBase58("6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma")
|
||||||
|
|
||||||
|
var axiomOuterContract = solana.MustPublicKeyFromBase58("FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9")
|
||||||
|
|||||||
1
enum.go
1
enum.go
@@ -75,6 +75,7 @@ const (
|
|||||||
PlatformMaestro = "maestro"
|
PlatformMaestro = "maestro"
|
||||||
PlatformBonkBot = "bonkbot"
|
PlatformBonkBot = "bonkbot"
|
||||||
PlatformPadre = "padre"
|
PlatformPadre = "padre"
|
||||||
|
PlatformDexScreener = "dexscreener"
|
||||||
|
|
||||||
// used to flag transactions impersonating platform users
|
// used to flag transactions impersonating platform users
|
||||||
PlatformFake = "fake"
|
PlatformFake = "fake"
|
||||||
|
|||||||
14
meta.go
14
meta.go
@@ -81,11 +81,17 @@ var (
|
|||||||
meteoraDlmmAddLiquidity2Discriminator = calculateDiscriminator("global:add_liquidity2")
|
meteoraDlmmAddLiquidity2Discriminator = calculateDiscriminator("global:add_liquidity2")
|
||||||
meteoraDlmmAddLiquidityByStrategyDiscriminator = calculateDiscriminator("global:add_liquidity_by_strategy")
|
meteoraDlmmAddLiquidityByStrategyDiscriminator = calculateDiscriminator("global:add_liquidity_by_strategy")
|
||||||
meteoraDlmmAddLiquidityByStrategy2Discriminator = calculateDiscriminator("global:add_liquidity_by_strategy2")
|
meteoraDlmmAddLiquidityByStrategy2Discriminator = calculateDiscriminator("global:add_liquidity_by_strategy2")
|
||||||
|
meteoraDlmmClaimFeeDiscriminator = calculateDiscriminator("global:claim_fee")
|
||||||
|
meteoraDlmmClaimFee2Discriminator = calculateDiscriminator("global:claim_fee2")
|
||||||
|
meteoraDlmmRebalanceLiquidityDiscriminator = calculateDiscriminator("global:rebalance_liquidity")
|
||||||
meteoraDlmmRemoveLiquidityDiscriminator = calculateDiscriminator("global:remove_liquidity")
|
meteoraDlmmRemoveLiquidityDiscriminator = calculateDiscriminator("global:remove_liquidity")
|
||||||
meteoraDlmmRemoveLiquidity2Discriminator = calculateDiscriminator("global:remove_liquidity2")
|
meteoraDlmmRemoveLiquidity2Discriminator = calculateDiscriminator("global:remove_liquidity2")
|
||||||
meteoraDlmmRemoveLiquidityByRangeDiscriminator = calculateDiscriminator("global:remove_liquidity_by_range")
|
meteoraDlmmRemoveLiquidityByRangeDiscriminator = calculateDiscriminator("global:remove_liquidity_by_range")
|
||||||
meteoraDlmmRemoveLiquidityByRange2Discriminator = calculateDiscriminator("global:remove_liquidity_by_range2")
|
meteoraDlmmRemoveLiquidityByRange2Discriminator = calculateDiscriminator("global:remove_liquidity_by_range2")
|
||||||
meteoraDlmmAddLiquidityEventDiscriminator = calculateDiscriminator("event:AddLiquidity")
|
meteoraDlmmAddLiquidityEventDiscriminator = calculateDiscriminator("event:AddLiquidity")
|
||||||
|
meteoraDlmmClaimFeeEventDiscriminator = calculateDiscriminator("event:ClaimFee")
|
||||||
|
meteoraDlmmClaimFee2EventDiscriminator = calculateDiscriminator("event:ClaimFee2")
|
||||||
|
meteoraDlmmRebalancingEventDiscriminator = calculateDiscriminator("event:Rebalancing")
|
||||||
meteoraDlmmRemoveLiquidityEventDiscriminator = calculateDiscriminator("event:RemoveLiquidity")
|
meteoraDlmmRemoveLiquidityEventDiscriminator = calculateDiscriminator("event:RemoveLiquidity")
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -200,8 +206,10 @@ var (
|
|||||||
const (
|
const (
|
||||||
raydiumV4InitializePoolDiscriminator = uint8(1)
|
raydiumV4InitializePoolDiscriminator = uint8(1)
|
||||||
|
|
||||||
raydiumV4SwapBaseInDiscriminator = uint8(9)
|
raydiumV4SwapBaseInDiscriminator = uint8(9)
|
||||||
raydiumV4SwapBaseOutDiscriminator = uint8(11)
|
raydiumV4SwapBaseOutDiscriminator = uint8(11)
|
||||||
|
raydiumV4SwapBaseInV2Discriminator = uint8(16)
|
||||||
|
raydiumV4SwapBaseOutV2Discriminator = uint8(17)
|
||||||
|
|
||||||
raydiumV4AddLiquidityDiscriminator = uint8(3)
|
raydiumV4AddLiquidityDiscriminator = uint8(3)
|
||||||
raydiumV4RemoveLiquidityDiscriminator = uint8(4)
|
raydiumV4RemoveLiquidityDiscriminator = uint8(4)
|
||||||
@@ -236,4 +244,6 @@ var createAccountWithSeedDiscriminator = uint32(3)
|
|||||||
var systemProgram = solana.MustPublicKeyFromBase58("11111111111111111111111111111111")
|
var systemProgram = solana.MustPublicKeyFromBase58("11111111111111111111111111111111")
|
||||||
var momoProgram = solana.MustPublicKeyFromBase58("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr")
|
var momoProgram = solana.MustPublicKeyFromBase58("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr")
|
||||||
|
|
||||||
|
var chainLinkProgram = solana.MustPublicKeyFromBase58("cjg3oHmg9uuPsP8D6g29NWvhySJkdYdAo9D25PRbKXJ")
|
||||||
|
|
||||||
var eventDiscriminator = [8]byte{228, 69, 165, 46, 81, 203, 154, 29}
|
var eventDiscriminator = [8]byte{228, 69, 165, 46, 81, 203, 154, 29}
|
||||||
|
|||||||
528
metaoradlmm.go
528
metaoradlmm.go
@@ -55,6 +55,51 @@ type dlmmRemoveLiquidityEvent struct {
|
|||||||
ActiveBinId int32
|
ActiveBinId int32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type dlmmClaimFeeInnerEvent struct {
|
||||||
|
LbPair solana.PublicKey
|
||||||
|
Position solana.PublicKey
|
||||||
|
Owner solana.PublicKey
|
||||||
|
FeeX uint64
|
||||||
|
FeeY uint64
|
||||||
|
ActiveBinId int32
|
||||||
|
HasActiveBin bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type dlmmClaimFeeEvent struct {
|
||||||
|
LbPair solana.PublicKey
|
||||||
|
Position solana.PublicKey
|
||||||
|
Owner solana.PublicKey
|
||||||
|
FeeX uint64
|
||||||
|
FeeY uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type dlmmClaimFee2Event struct {
|
||||||
|
LbPair solana.PublicKey
|
||||||
|
Position solana.PublicKey
|
||||||
|
Owner solana.PublicKey
|
||||||
|
FeeX uint64
|
||||||
|
FeeY uint64
|
||||||
|
ActiveBinId int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type dlmmRebalancingEvent struct {
|
||||||
|
LbPair solana.PublicKey
|
||||||
|
Position solana.PublicKey
|
||||||
|
Owner solana.PublicKey
|
||||||
|
ActiveBinId int32
|
||||||
|
XWithdrawnAmount uint64
|
||||||
|
XAddedAmount uint64
|
||||||
|
YWithdrawnAmount uint64
|
||||||
|
YAddedAmount uint64
|
||||||
|
XFeeAmount uint64
|
||||||
|
YFeeAmount uint64
|
||||||
|
OldMinBinId int32
|
||||||
|
OldMaxBinId int32
|
||||||
|
NewMinBinId int32
|
||||||
|
NewMaxBinId int32
|
||||||
|
Rewards [2]uint64
|
||||||
|
}
|
||||||
|
|
||||||
type dlmmBinLiquidityDistribution struct {
|
type dlmmBinLiquidityDistribution struct {
|
||||||
BinId int32
|
BinId int32
|
||||||
DistributionX uint16
|
DistributionX uint16
|
||||||
@@ -207,6 +252,10 @@ func metaoradlmmParser(tx *Tx, instruction Instruction, innerInstructions InnerI
|
|||||||
case meteoraDlmmAddLiquidityDiscriminator, meteoraDlmmAddLiquidity2Discriminator,
|
case meteoraDlmmAddLiquidityDiscriminator, meteoraDlmmAddLiquidity2Discriminator,
|
||||||
meteoraDlmmAddLiquidityByStrategyDiscriminator, meteoraDlmmAddLiquidityByStrategy2Discriminator:
|
meteoraDlmmAddLiquidityByStrategyDiscriminator, meteoraDlmmAddLiquidityByStrategy2Discriminator:
|
||||||
return metaoradlmmAddLiquidityParser(tx, instruction, innerInstructions, offset)
|
return metaoradlmmAddLiquidityParser(tx, instruction, innerInstructions, offset)
|
||||||
|
case meteoraDlmmClaimFeeDiscriminator, meteoraDlmmClaimFee2Discriminator:
|
||||||
|
return metaoradlmmClaimFeeParser(tx, instruction, innerInstructions, offset)
|
||||||
|
case meteoraDlmmRebalanceLiquidityDiscriminator:
|
||||||
|
return metaoradlmmRebalanceLiquidityParser(tx, instruction, innerInstructions, offset)
|
||||||
case meteoraDlmmRemoveLiquidityDiscriminator, meteoraDlmmRemoveLiquidity2Discriminator,
|
case meteoraDlmmRemoveLiquidityDiscriminator, meteoraDlmmRemoveLiquidity2Discriminator,
|
||||||
meteoraDlmmRemoveLiquidityByRangeDiscriminator, meteoraDlmmRemoveLiquidityByRange2Discriminator:
|
meteoraDlmmRemoveLiquidityByRangeDiscriminator, meteoraDlmmRemoveLiquidityByRange2Discriminator:
|
||||||
return metaoradlmmRemoveLiquidityParser(tx, instruction, innerInstructions, offset)
|
return metaoradlmmRemoveLiquidityParser(tx, instruction, innerInstructions, offset)
|
||||||
@@ -634,7 +683,7 @@ func metaoradlmmAddLiquidityParser(tx *Tx, instruction Instruction, innerInstruc
|
|||||||
|
|
||||||
swap := Swap{
|
swap := Swap{
|
||||||
Program: SolProgramMeteoraDLMM,
|
Program: SolProgramMeteoraDLMM,
|
||||||
Event: "add_liquidity",
|
Event: "add",
|
||||||
Pool: pool,
|
Pool: pool,
|
||||||
BaseMint: baseMint,
|
BaseMint: baseMint,
|
||||||
QuoteMint: quoteMint,
|
QuoteMint: quoteMint,
|
||||||
@@ -802,7 +851,7 @@ func metaoradlmmRemoveLiquidityParser(tx *Tx, instruction Instruction, innerInst
|
|||||||
|
|
||||||
swap := Swap{
|
swap := Swap{
|
||||||
Program: SolProgramMeteoraDLMM,
|
Program: SolProgramMeteoraDLMM,
|
||||||
Event: "remove_liquidity",
|
Event: "remove",
|
||||||
Pool: pool,
|
Pool: pool,
|
||||||
BaseMint: baseMint,
|
BaseMint: baseMint,
|
||||||
QuoteMint: quoteMint,
|
QuoteMint: quoteMint,
|
||||||
@@ -826,6 +875,274 @@ func metaoradlmmRemoveLiquidityParser(tx *Tx, instruction Instruction, innerInst
|
|||||||
return []Swap{swap}, offset, nil
|
return []Swap{swap}, offset, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func metaoradlmmClaimFeeParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||||
|
result := tx.rawTx
|
||||||
|
|
||||||
|
entryContract := result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
accounts, err := resolveDlmmClaimFeeAccounts(result, instruction.Data, instruction.Accounts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("meteora dlmm claim fee accounts parse error: %v, offset, %d, %d", err, offset[0], offset[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
claimEvent, nextOffset, err := dlmmClaimFeeEventFromInnerInstructions(innerInstructions, instruction, offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nextOffset, err
|
||||||
|
}
|
||||||
|
offset = nextOffset
|
||||||
|
if claimEvent.FeeX == 0 && claimEvent.FeeY == 0 {
|
||||||
|
return nil, offset, InstructionIgnoredError
|
||||||
|
}
|
||||||
|
|
||||||
|
pool := result.accountList[accounts.poolIdx]
|
||||||
|
tokenXMint := result.accountList[accounts.tokenXMintIdx]
|
||||||
|
tokenYMint := result.accountList[accounts.tokenYMintIdx]
|
||||||
|
tokenXProgram := result.accountList[accounts.tokenXProgramIdx]
|
||||||
|
tokenYProgram := result.accountList[accounts.tokenYProgramIdx]
|
||||||
|
|
||||||
|
baseMint, quoteMint, baseIsX := dlmmSelectBaseQuote(tokenXMint, tokenYMint)
|
||||||
|
baseTokenProgram := tokenXProgram
|
||||||
|
quoteTokenProgram := tokenYProgram
|
||||||
|
baseReserveIdx := accounts.reserveXIdx
|
||||||
|
quoteReserveIdx := accounts.reserveYIdx
|
||||||
|
userBaseIdx := accounts.userTokenXIdx
|
||||||
|
userQuoteIdx := accounts.userTokenYIdx
|
||||||
|
baseAmount := decimal.NewFromUint64(claimEvent.FeeX)
|
||||||
|
quoteAmount := decimal.NewFromUint64(claimEvent.FeeY)
|
||||||
|
if !baseIsX {
|
||||||
|
baseTokenProgram = tokenYProgram
|
||||||
|
quoteTokenProgram = tokenXProgram
|
||||||
|
baseReserveIdx = accounts.reserveYIdx
|
||||||
|
quoteReserveIdx = accounts.reserveXIdx
|
||||||
|
userBaseIdx = accounts.userTokenYIdx
|
||||||
|
userQuoteIdx = accounts.userTokenXIdx
|
||||||
|
baseAmount = decimal.NewFromUint64(claimEvent.FeeY)
|
||||||
|
quoteAmount = decimal.NewFromUint64(claimEvent.FeeX)
|
||||||
|
}
|
||||||
|
|
||||||
|
eventUser := result.accountList[accounts.userIdx]
|
||||||
|
if !claimEvent.Owner.IsZero() {
|
||||||
|
eventUser = claimEvent.Owner
|
||||||
|
}
|
||||||
|
|
||||||
|
baseDecimals, ok := dlmmTokenDecimals(result, baseReserveIdx)
|
||||||
|
if !ok {
|
||||||
|
baseDecimals, _ = dlmmTokenDecimals(result, userBaseIdx)
|
||||||
|
}
|
||||||
|
quoteDecimals, ok := dlmmTokenDecimals(result, quoteReserveIdx)
|
||||||
|
if !ok {
|
||||||
|
quoteDecimals, _ = dlmmTokenDecimals(result, userQuoteIdx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := tx.Token[baseMint]; !exists && !baseMint.Equals(wSolMint) {
|
||||||
|
tx.Token[baseMint] = TokenMeta{
|
||||||
|
Mint: baseMint,
|
||||||
|
Decimals: baseDecimals,
|
||||||
|
TokenProgram: baseTokenProgram,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, exists := tx.Token[quoteMint]; !exists && !quoteMint.Equals(wSolMint) {
|
||||||
|
tx.Token[quoteMint] = TokenMeta{
|
||||||
|
Mint: quoteMint,
|
||||||
|
Decimals: quoteDecimals,
|
||||||
|
TokenProgram: quoteTokenProgram,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
baseReserve := getAccountBalanceAfterTx(result, baseReserveIdx)
|
||||||
|
quoteReserve := getAccountBalanceAfterTx(result, quoteReserveIdx)
|
||||||
|
userBase := getAccountBalanceAfterTx(result, userBaseIdx)
|
||||||
|
userQuote := getAccountBalanceAfterTx(result, userQuoteIdx)
|
||||||
|
if quoteMint.Equals(wSolMint) {
|
||||||
|
if solAmount, err := GetSolAfterTx(result, accounts.userIdx); err == nil {
|
||||||
|
userQuote = userQuote.Add(decimal.NewFromUint64(solAmount))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
swap := Swap{
|
||||||
|
Program: SolProgramMeteoraDLMM,
|
||||||
|
Event: "claim_fee",
|
||||||
|
Pool: pool,
|
||||||
|
BaseMint: baseMint,
|
||||||
|
QuoteMint: quoteMint,
|
||||||
|
BaseTokenProgram: baseTokenProgram,
|
||||||
|
QuoteTokenProgram: quoteTokenProgram,
|
||||||
|
BaseMintDecimals: baseDecimals,
|
||||||
|
QuoteMintDecimals: quoteDecimals,
|
||||||
|
User: eventUser,
|
||||||
|
BaseAmount: baseAmount,
|
||||||
|
QuoteAmount: quoteAmount,
|
||||||
|
BaseReserve: baseReserve,
|
||||||
|
QuoteReserve: quoteReserve,
|
||||||
|
UserBaseBalance: userBase,
|
||||||
|
UserQuoteBalance: userQuote,
|
||||||
|
EntryContract: entryContract,
|
||||||
|
}
|
||||||
|
if claimEvent.HasActiveBin {
|
||||||
|
swap.StartBinId = claimEvent.ActiveBinId
|
||||||
|
swap.EndBinId = claimEvent.ActiveBinId
|
||||||
|
}
|
||||||
|
|
||||||
|
return []Swap{swap}, offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func metaoradlmmRebalanceLiquidityParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||||
|
result := tx.rawTx
|
||||||
|
|
||||||
|
entryContract := result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
accounts, err := resolveDlmmRebalanceAccounts(result, instruction.Accounts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("meteora dlmm rebalance liquidity accounts parse error: %v, offset, %d, %d", err, offset[0], offset[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
event, nextOffset, err := dlmmRebalancingEventFromInnerInstructions(innerInstructions, instruction, offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nextOffset, err
|
||||||
|
}
|
||||||
|
offset = nextOffset
|
||||||
|
|
||||||
|
pool := result.accountList[accounts.poolIdx]
|
||||||
|
tokenXMint := result.accountList[accounts.tokenXMintIdx]
|
||||||
|
tokenYMint := result.accountList[accounts.tokenYMintIdx]
|
||||||
|
tokenXProgram := result.accountList[accounts.tokenXProgramIdx]
|
||||||
|
tokenYProgram := result.accountList[accounts.tokenYProgramIdx]
|
||||||
|
|
||||||
|
baseMint, quoteMint, baseIsX := dlmmSelectBaseQuote(tokenXMint, tokenYMint)
|
||||||
|
baseTokenProgram := tokenXProgram
|
||||||
|
quoteTokenProgram := tokenYProgram
|
||||||
|
baseReserveIdx := accounts.reserveXIdx
|
||||||
|
quoteReserveIdx := accounts.reserveYIdx
|
||||||
|
userBaseIdx := accounts.userTokenXIdx
|
||||||
|
userQuoteIdx := accounts.userTokenYIdx
|
||||||
|
withdrawBase := event.XWithdrawnAmount
|
||||||
|
withdrawQuote := event.YWithdrawnAmount
|
||||||
|
addBase := event.XAddedAmount
|
||||||
|
addQuote := event.YAddedAmount
|
||||||
|
if !baseIsX {
|
||||||
|
baseTokenProgram = tokenYProgram
|
||||||
|
quoteTokenProgram = tokenXProgram
|
||||||
|
baseReserveIdx = accounts.reserveYIdx
|
||||||
|
quoteReserveIdx = accounts.reserveXIdx
|
||||||
|
userBaseIdx = accounts.userTokenYIdx
|
||||||
|
userQuoteIdx = accounts.userTokenXIdx
|
||||||
|
withdrawBase = event.YWithdrawnAmount
|
||||||
|
withdrawQuote = event.XWithdrawnAmount
|
||||||
|
addBase = event.YAddedAmount
|
||||||
|
addQuote = event.XAddedAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
eventUser := result.accountList[accounts.userIdx]
|
||||||
|
if !event.Owner.IsZero() {
|
||||||
|
eventUser = event.Owner
|
||||||
|
}
|
||||||
|
|
||||||
|
baseDecimals, ok := dlmmTokenDecimals(result, baseReserveIdx)
|
||||||
|
if !ok {
|
||||||
|
baseDecimals, _ = dlmmTokenDecimals(result, userBaseIdx)
|
||||||
|
}
|
||||||
|
quoteDecimals, ok := dlmmTokenDecimals(result, quoteReserveIdx)
|
||||||
|
if !ok {
|
||||||
|
quoteDecimals, _ = dlmmTokenDecimals(result, userQuoteIdx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := tx.Token[baseMint]; !exists && !baseMint.Equals(wSolMint) {
|
||||||
|
tx.Token[baseMint] = TokenMeta{
|
||||||
|
Mint: baseMint,
|
||||||
|
Decimals: baseDecimals,
|
||||||
|
TokenProgram: baseTokenProgram,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, exists := tx.Token[quoteMint]; !exists && !quoteMint.Equals(wSolMint) {
|
||||||
|
tx.Token[quoteMint] = TokenMeta{
|
||||||
|
Mint: quoteMint,
|
||||||
|
Decimals: quoteDecimals,
|
||||||
|
TokenProgram: quoteTokenProgram,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
baseReserve := getAccountBalanceAfterTx(result, baseReserveIdx)
|
||||||
|
quoteReserve := getAccountBalanceAfterTx(result, quoteReserveIdx)
|
||||||
|
userBase := getAccountBalanceAfterTx(result, userBaseIdx)
|
||||||
|
userQuote := getAccountBalanceAfterTx(result, userQuoteIdx)
|
||||||
|
if quoteMint.Equals(wSolMint) {
|
||||||
|
if solAmount, err := GetSolAfterTx(result, accounts.userIdx); err == nil {
|
||||||
|
userQuote = userQuote.Add(decimal.NewFromUint64(solAmount))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var swaps []Swap
|
||||||
|
if withdrawBase > 0 || withdrawQuote > 0 {
|
||||||
|
swaps = append(swaps, Swap{
|
||||||
|
Program: SolProgramMeteoraDLMM,
|
||||||
|
Event: "remove",
|
||||||
|
Pool: pool,
|
||||||
|
BaseMint: baseMint,
|
||||||
|
QuoteMint: quoteMint,
|
||||||
|
BaseTokenProgram: baseTokenProgram,
|
||||||
|
QuoteTokenProgram: quoteTokenProgram,
|
||||||
|
BaseMintDecimals: baseDecimals,
|
||||||
|
QuoteMintDecimals: quoteDecimals,
|
||||||
|
User: eventUser,
|
||||||
|
BaseAmount: decimal.NewFromUint64(withdrawBase),
|
||||||
|
QuoteAmount: decimal.NewFromUint64(withdrawQuote),
|
||||||
|
BaseReserve: baseReserve,
|
||||||
|
QuoteReserve: quoteReserve,
|
||||||
|
UserBaseBalance: userBase,
|
||||||
|
UserQuoteBalance: userQuote,
|
||||||
|
EntryContract: entryContract,
|
||||||
|
StartBinId: event.OldMinBinId,
|
||||||
|
EndBinId: event.OldMaxBinId,
|
||||||
|
BinChanges: dlmmBinChangesFromRange(event.OldMinBinId, event.OldMaxBinId, 0),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if addBase > 0 || addQuote > 0 {
|
||||||
|
swaps = append(swaps, Swap{
|
||||||
|
Program: SolProgramMeteoraDLMM,
|
||||||
|
Event: "add",
|
||||||
|
Pool: pool,
|
||||||
|
BaseMint: baseMint,
|
||||||
|
QuoteMint: quoteMint,
|
||||||
|
BaseTokenProgram: baseTokenProgram,
|
||||||
|
QuoteTokenProgram: quoteTokenProgram,
|
||||||
|
BaseMintDecimals: baseDecimals,
|
||||||
|
QuoteMintDecimals: quoteDecimals,
|
||||||
|
User: eventUser,
|
||||||
|
BaseAmount: decimal.NewFromUint64(addBase),
|
||||||
|
QuoteAmount: decimal.NewFromUint64(addQuote),
|
||||||
|
BaseReserve: baseReserve,
|
||||||
|
QuoteReserve: quoteReserve,
|
||||||
|
UserBaseBalance: userBase,
|
||||||
|
UserQuoteBalance: userQuote,
|
||||||
|
EntryContract: entryContract,
|
||||||
|
StartBinId: event.NewMinBinId,
|
||||||
|
EndBinId: event.NewMaxBinId,
|
||||||
|
BinChanges: dlmmBinChangesFromRange(event.NewMinBinId, event.NewMaxBinId, 0),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if len(swaps) == 0 {
|
||||||
|
return nil, offset, InstructionIgnoredError
|
||||||
|
}
|
||||||
|
|
||||||
|
return swaps, offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
func dlmmSelectBaseQuote(tokenX, tokenY solana.PublicKey) (baseMint solana.PublicKey, quoteMint solana.PublicKey, baseIsX bool) {
|
func dlmmSelectBaseQuote(tokenX, tokenY solana.PublicKey) (baseMint solana.PublicKey, quoteMint solana.PublicKey, baseIsX bool) {
|
||||||
priority := []solana.PublicKey{wSolMint, usdcMint, usd1Mint}
|
priority := []solana.PublicKey{wSolMint, usdcMint, usd1Mint}
|
||||||
for _, mint := range priority {
|
for _, mint := range priority {
|
||||||
@@ -887,6 +1204,68 @@ func dlmmRemoveLiquidityEventFromInnerInstructions(innerInstructions InnerInstru
|
|||||||
return dlmmRemoveLiquidityEvent{}, increaseOffset(offset), fmt.Errorf("meteora dlmm remove liquidity event not found, offset, %d, %d", offset[0], prefixLen)
|
return dlmmRemoveLiquidityEvent{}, increaseOffset(offset), fmt.Errorf("meteora dlmm remove liquidity event not found, offset, %d, %d", offset[0], prefixLen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dlmmClaimFeeEventFromInnerInstructions(innerInstructions InnerInstructions, instruction Instruction, offset [2]uint) (dlmmClaimFeeInnerEvent, [2]uint, error) {
|
||||||
|
var prefixLen = offset[1]
|
||||||
|
inners, err := getInnerInstructions(innerInstructions, prefixLen)
|
||||||
|
if err != nil {
|
||||||
|
return dlmmClaimFeeInnerEvent{}, increaseOffset(offset), fmt.Errorf("meteora dlmm claim fee get inner instructions error: %v, offset, %d, %d", err, offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
found bool
|
||||||
|
event dlmmClaimFeeInnerEvent
|
||||||
|
matchedIdx int
|
||||||
|
)
|
||||||
|
for innerIndex, innerInstr := range inners {
|
||||||
|
if innerInstr.ProgramIDIndex != instruction.ProgramIDIndex {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
decoded, ok := dlmmDecodeClaimFeeEvent(innerInstr.Data)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
found = true
|
||||||
|
event = decoded
|
||||||
|
matchedIdx = innerIndex
|
||||||
|
if decoded.HasActiveBin {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return dlmmClaimFeeInnerEvent{}, increaseOffset(offset), fmt.Errorf("meteora dlmm claim fee event not found, offset, %d, %d", offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
if offset[1] == 0 {
|
||||||
|
offset[0] += 1
|
||||||
|
} else {
|
||||||
|
offset[1] = uint(matchedIdx) + 1 + prefixLen
|
||||||
|
}
|
||||||
|
return event, offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dlmmRebalancingEventFromInnerInstructions(innerInstructions InnerInstructions, instruction Instruction, offset [2]uint) (dlmmRebalancingEvent, [2]uint, error) {
|
||||||
|
var prefixLen = offset[1]
|
||||||
|
inners, err := getInnerInstructions(innerInstructions, prefixLen)
|
||||||
|
if err != nil {
|
||||||
|
return dlmmRebalancingEvent{}, increaseOffset(offset), fmt.Errorf("meteora dlmm rebalance liquidity get inner instructions error: %v, offset, %d, %d", err, offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
for innerIndex, innerInstr := range inners {
|
||||||
|
if innerInstr.ProgramIDIndex != instruction.ProgramIDIndex {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
event, ok := dlmmDecodeRebalancingEvent(innerInstr.Data)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if offset[1] == 0 {
|
||||||
|
offset[0] += 1
|
||||||
|
} else {
|
||||||
|
offset[1] = uint(innerIndex) + 1 + prefixLen
|
||||||
|
}
|
||||||
|
return event, offset, nil
|
||||||
|
}
|
||||||
|
return dlmmRebalancingEvent{}, increaseOffset(offset), fmt.Errorf("meteora dlmm rebalance liquidity event not found, offset, %d, %d", offset[0], prefixLen)
|
||||||
|
}
|
||||||
|
|
||||||
func dlmmDecodeAddLiquidityEvent(data []byte) (dlmmAddLiquidityEvent, bool) {
|
func dlmmDecodeAddLiquidityEvent(data []byte) (dlmmAddLiquidityEvent, bool) {
|
||||||
switch {
|
switch {
|
||||||
case len(data) >= 8 && bytes.Equal(data[:8], meteoraDlmmAddLiquidityEventDiscriminator[:]):
|
case len(data) >= 8 && bytes.Equal(data[:8], meteoraDlmmAddLiquidityEventDiscriminator[:]):
|
||||||
@@ -929,6 +1308,64 @@ func dlmmDecodeRemoveLiquidityEvent(data []byte) (dlmmRemoveLiquidityEvent, bool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dlmmDecodeClaimFeeEvent(data []byte) (dlmmClaimFeeInnerEvent, bool) {
|
||||||
|
switch {
|
||||||
|
case len(data) >= 16 &&
|
||||||
|
bytes.Equal(data[:8], eventDiscriminator[:]) &&
|
||||||
|
bytes.Equal(data[8:16], meteoraDlmmClaimFee2EventDiscriminator[:]):
|
||||||
|
var event dlmmClaimFee2Event
|
||||||
|
if err := agbinary.NewBorshDecoder(data[16:]).Decode(&event); err != nil {
|
||||||
|
return dlmmClaimFeeInnerEvent{}, false
|
||||||
|
}
|
||||||
|
return dlmmClaimFeeInnerEvent{
|
||||||
|
LbPair: event.LbPair,
|
||||||
|
Position: event.Position,
|
||||||
|
Owner: event.Owner,
|
||||||
|
FeeX: event.FeeX,
|
||||||
|
FeeY: event.FeeY,
|
||||||
|
ActiveBinId: event.ActiveBinId,
|
||||||
|
HasActiveBin: true,
|
||||||
|
}, true
|
||||||
|
case len(data) >= 16 &&
|
||||||
|
bytes.Equal(data[:8], eventDiscriminator[:]) &&
|
||||||
|
bytes.Equal(data[8:16], meteoraDlmmClaimFeeEventDiscriminator[:]):
|
||||||
|
var event dlmmClaimFeeEvent
|
||||||
|
if err := agbinary.NewBorshDecoder(data[16:]).Decode(&event); err != nil {
|
||||||
|
return dlmmClaimFeeInnerEvent{}, false
|
||||||
|
}
|
||||||
|
return dlmmClaimFeeInnerEvent{
|
||||||
|
LbPair: event.LbPair,
|
||||||
|
Position: event.Position,
|
||||||
|
Owner: event.Owner,
|
||||||
|
FeeX: event.FeeX,
|
||||||
|
FeeY: event.FeeY,
|
||||||
|
}, true
|
||||||
|
default:
|
||||||
|
return dlmmClaimFeeInnerEvent{}, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dlmmDecodeRebalancingEvent(data []byte) (dlmmRebalancingEvent, bool) {
|
||||||
|
switch {
|
||||||
|
case len(data) >= 8 && bytes.Equal(data[:8], meteoraDlmmRebalancingEventDiscriminator[:]):
|
||||||
|
var event dlmmRebalancingEvent
|
||||||
|
if err := agbinary.NewBorshDecoder(data[8:]).Decode(&event); err != nil {
|
||||||
|
return dlmmRebalancingEvent{}, false
|
||||||
|
}
|
||||||
|
return event, true
|
||||||
|
case len(data) >= 16 &&
|
||||||
|
bytes.Equal(data[:8], eventDiscriminator[:]) &&
|
||||||
|
bytes.Equal(data[8:16], meteoraDlmmRebalancingEventDiscriminator[:]):
|
||||||
|
var event dlmmRebalancingEvent
|
||||||
|
if err := agbinary.NewBorshDecoder(data[16:]).Decode(&event); err != nil {
|
||||||
|
return dlmmRebalancingEvent{}, false
|
||||||
|
}
|
||||||
|
return event, true
|
||||||
|
default:
|
||||||
|
return dlmmRebalancingEvent{}, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func resolveDlmmSwapAccounts(result *RawTx, accounts []int) (dlmmSwapAccounts, error) {
|
func resolveDlmmSwapAccounts(result *RawTx, accounts []int) (dlmmSwapAccounts, error) {
|
||||||
if len(accounts) < 13 {
|
if len(accounts) < 13 {
|
||||||
return dlmmSwapAccounts{}, fmt.Errorf("accounts too short, expected at least 13")
|
return dlmmSwapAccounts{}, fmt.Errorf("accounts too short, expected at least 13")
|
||||||
@@ -1046,6 +1483,93 @@ func resolveDlmmLiquidityAccounts(result *RawTx, accounts []int) (dlmmLiquidityA
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resolveDlmmClaimFeeAccounts(result *RawTx, data []byte, accounts []int) (dlmmLiquidityAccounts, error) {
|
||||||
|
if len(data) < 8 {
|
||||||
|
return dlmmLiquidityAccounts{}, fmt.Errorf("instruction data too short")
|
||||||
|
}
|
||||||
|
discriminator := *(*[8]byte)(data[:8])
|
||||||
|
accountList := result.accountList
|
||||||
|
|
||||||
|
switch discriminator {
|
||||||
|
case meteoraDlmmClaimFee2Discriminator:
|
||||||
|
if len(accounts) < 14 {
|
||||||
|
return dlmmLiquidityAccounts{}, fmt.Errorf("accounts too short, expected at least 14")
|
||||||
|
}
|
||||||
|
if !accountList[accounts[12]].Equals(meteoraDlmmEventAuthority) {
|
||||||
|
return dlmmLiquidityAccounts{}, fmt.Errorf("event authority mismatch")
|
||||||
|
}
|
||||||
|
if !accountList[accounts[13]].Equals(meteoraDlmmProgram) {
|
||||||
|
return dlmmLiquidityAccounts{}, fmt.Errorf("program id mismatch")
|
||||||
|
}
|
||||||
|
return dlmmLiquidityAccounts{
|
||||||
|
positionIdx: accounts[1],
|
||||||
|
poolIdx: accounts[0],
|
||||||
|
userTokenXIdx: accounts[5],
|
||||||
|
userTokenYIdx: accounts[6],
|
||||||
|
reserveXIdx: accounts[3],
|
||||||
|
reserveYIdx: accounts[4],
|
||||||
|
tokenXMintIdx: accounts[7],
|
||||||
|
tokenYMintIdx: accounts[8],
|
||||||
|
userIdx: accounts[2],
|
||||||
|
tokenXProgramIdx: accounts[9],
|
||||||
|
tokenYProgramIdx: accounts[10],
|
||||||
|
}, nil
|
||||||
|
case meteoraDlmmClaimFeeDiscriminator:
|
||||||
|
if len(accounts) < 14 {
|
||||||
|
return dlmmLiquidityAccounts{}, fmt.Errorf("accounts too short, expected at least 14")
|
||||||
|
}
|
||||||
|
if !accountList[accounts[12]].Equals(meteoraDlmmEventAuthority) {
|
||||||
|
return dlmmLiquidityAccounts{}, fmt.Errorf("event authority mismatch")
|
||||||
|
}
|
||||||
|
if !accountList[accounts[13]].Equals(meteoraDlmmProgram) {
|
||||||
|
return dlmmLiquidityAccounts{}, fmt.Errorf("program id mismatch")
|
||||||
|
}
|
||||||
|
return dlmmLiquidityAccounts{
|
||||||
|
positionIdx: accounts[1],
|
||||||
|
poolIdx: accounts[0],
|
||||||
|
userTokenXIdx: accounts[7],
|
||||||
|
userTokenYIdx: accounts[8],
|
||||||
|
reserveXIdx: accounts[5],
|
||||||
|
reserveYIdx: accounts[6],
|
||||||
|
tokenXMintIdx: accounts[9],
|
||||||
|
tokenYMintIdx: accounts[10],
|
||||||
|
userIdx: accounts[4],
|
||||||
|
tokenXProgramIdx: accounts[11],
|
||||||
|
tokenYProgramIdx: accounts[11],
|
||||||
|
}, nil
|
||||||
|
default:
|
||||||
|
return dlmmLiquidityAccounts{}, fmt.Errorf("unsupported claim fee discriminator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveDlmmRebalanceAccounts(result *RawTx, accounts []int) (dlmmLiquidityAccounts, error) {
|
||||||
|
if len(accounts) < 17 {
|
||||||
|
return dlmmLiquidityAccounts{}, fmt.Errorf("accounts too short, expected at least 17")
|
||||||
|
}
|
||||||
|
accountList := result.accountList
|
||||||
|
|
||||||
|
if !accountList[accounts[15]].Equals(meteoraDlmmEventAuthority) {
|
||||||
|
return dlmmLiquidityAccounts{}, fmt.Errorf("event authority mismatch")
|
||||||
|
}
|
||||||
|
if !accountList[accounts[16]].Equals(meteoraDlmmProgram) {
|
||||||
|
return dlmmLiquidityAccounts{}, fmt.Errorf("program id mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
return dlmmLiquidityAccounts{
|
||||||
|
positionIdx: accounts[0],
|
||||||
|
poolIdx: accounts[1],
|
||||||
|
userTokenXIdx: accounts[3],
|
||||||
|
userTokenYIdx: accounts[4],
|
||||||
|
reserveXIdx: accounts[5],
|
||||||
|
reserveYIdx: accounts[6],
|
||||||
|
tokenXMintIdx: accounts[7],
|
||||||
|
tokenYMintIdx: accounts[8],
|
||||||
|
userIdx: accounts[9],
|
||||||
|
tokenXProgramIdx: accounts[11],
|
||||||
|
tokenYProgramIdx: accounts[12],
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func dlmmTokenDecimals(result *RawTx, accountIndex int) (uint8, bool) {
|
func dlmmTokenDecimals(result *RawTx, accountIndex int) (uint8, bool) {
|
||||||
for _, meta := range result.Meta.PostTokenBalances {
|
for _, meta := range result.Meta.PostTokenBalances {
|
||||||
if meta.AccountIndex == accountIndex {
|
if meta.AccountIndex == accountIndex {
|
||||||
|
|||||||
11
parser.go
11
parser.go
@@ -60,8 +60,9 @@ func WithMeteoraDlmm() ParserOption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var actionPrograms = map[solana.PublicKey]actionParser{
|
var actionPrograms = map[solana.PublicKey]actionParser{
|
||||||
systemProgram: systemParser,
|
systemProgram: systemParser,
|
||||||
budgGetProgram: budgetParser,
|
budgGetProgram: budgetParser,
|
||||||
|
chainLinkProgram: chainLinkParser,
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseRawTx(rawTx *RawTx) (*Tx, error) {
|
func ParseRawTx(rawTx *RawTx) (*Tx, error) {
|
||||||
@@ -126,6 +127,9 @@ func (tx *Tx) Parser() error {
|
|||||||
//fmt.Printf("parser failed tx error: %s, block: %d tx: %s\n", err, tx.Block, tx.GetTxHash())
|
//fmt.Printf("parser failed tx error: %s, block: %d tx: %s\n", err, tx.Block, tx.GetTxHash())
|
||||||
}
|
}
|
||||||
if len(swaps) > 0 {
|
if len(swaps) > 0 {
|
||||||
|
for i := range swaps {
|
||||||
|
swaps[i].InstrIdx = tx.Err.Index
|
||||||
|
}
|
||||||
tx.Swaps = swaps
|
tx.Swaps = swaps
|
||||||
}
|
}
|
||||||
for i, instr := range tx.rawTx.Transaction.Message.Instructions {
|
for i, instr := range tx.rawTx.Transaction.Message.Instructions {
|
||||||
@@ -171,6 +175,7 @@ func (tx *Tx) Parser() error {
|
|||||||
swap.AfterSOLBalance = decimal.NewFromUint64(tx.rawTx.Meta.PostBalances[userIdx]).Div(decimal.NewFromInt(1e9))
|
swap.AfterSOLBalance = decimal.NewFromUint64(tx.rawTx.Meta.PostBalances[userIdx]).Div(decimal.NewFromInt(1e9))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
swap.InstrIdx = uint8(i)
|
||||||
tx.Swaps = append(tx.Swaps, swap)
|
tx.Swaps = append(tx.Swaps, swap)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,6 +223,8 @@ func (tx *Tx) Parser() error {
|
|||||||
swap.AfterSOLBalance = decimal.NewFromUint64(tx.rawTx.Meta.PostBalances[userIdx]).Div(decimal.NewFromInt(1e9))
|
swap.AfterSOLBalance = decimal.NewFromUint64(tx.rawTx.Meta.PostBalances[userIdx]).Div(decimal.NewFromInt(1e9))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
swap.InstrIdx = uint8(i)
|
||||||
|
swap.InnerIdx = uint8(j)
|
||||||
tx.Swaps = append(tx.Swaps, swap)
|
tx.Swaps = append(tx.Swaps, swap)
|
||||||
}
|
}
|
||||||
// tx.Swaps = append(tx.Swaps, swaps...)
|
// tx.Swaps = append(tx.Swaps, swaps...)
|
||||||
|
|||||||
12
pump.go
12
pump.go
@@ -344,11 +344,13 @@ func BuyOrSellParser(tx *Tx, instruction Instruction, innerInstructions InnerIns
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, increaseOffset(offset), fmt.Errorf("pump create get inner instructions error: %v,offset, %d, %d", err, offset[0], offset[1])
|
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 {
|
if !entryContract.Equals(axiomOuterContract) {
|
||||||
for _, innerInstr := range innerInstructions.Instructions {
|
if instruction.StackHeight != nil && *instruction.StackHeight > 2 {
|
||||||
if innerInstr.StackHeight != nil && *innerInstr.StackHeight == *instruction.StackHeight-1 {
|
for _, innerInstr := range innerInstructions.Instructions {
|
||||||
entryContract = result.accountList[innerInstr.ProgramIDIndex]
|
if innerInstr.StackHeight != nil && *innerInstr.StackHeight == *instruction.StackHeight-1 {
|
||||||
break
|
entryContract = result.accountList[innerInstr.ProgramIDIndex]
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
pumpamm.go
24
pumpamm.go
@@ -512,11 +512,13 @@ func ammBuyParser(tx *Tx, instruction Instruction, innerInstructions InnerInstru
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, increaseOffset(offset), fmt.Errorf("pumpamm create get inner instructions error: %v, offset: %d, %d", err, offset[0], prefixLen)
|
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 {
|
if !entryContract.Equals(axiomOuterContract) {
|
||||||
for _, innerInstr := range innerInstructions.Instructions {
|
if instruction.StackHeight != nil && *instruction.StackHeight > 2 {
|
||||||
if innerInstr.StackHeight != nil && *innerInstr.StackHeight == *instruction.StackHeight-1 {
|
for _, innerInstr := range innerInstructions.Instructions {
|
||||||
entryContract = result.accountList[innerInstr.ProgramIDIndex]
|
if innerInstr.StackHeight != nil && *innerInstr.StackHeight == *instruction.StackHeight-1 {
|
||||||
break
|
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)
|
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 {
|
if !entryContract.Equals(axiomOuterContract) {
|
||||||
for _, innerInstr := range innerInstructions.Instructions {
|
if instruction.StackHeight != nil && *instruction.StackHeight > 2 {
|
||||||
if innerInstr.StackHeight != nil && *innerInstr.StackHeight == *instruction.StackHeight-1 {
|
for _, innerInstr := range innerInstructions.Instructions {
|
||||||
entryContract = result.accountList[innerInstr.ProgramIDIndex]
|
if innerInstr.StackHeight != nil && *innerInstr.StackHeight == *instruction.StackHeight-1 {
|
||||||
break
|
entryContract = result.accountList[innerInstr.ProgramIDIndex]
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
98
raydiumv4.go
98
raydiumv4.go
@@ -29,6 +29,8 @@ func raydiumV4Parser(tx *Tx, instruction Instruction, innerInstructions InnerIns
|
|||||||
return raydiumv4WithdrawPNLParser(tx, instruction, innerInstructions, offset)
|
return raydiumv4WithdrawPNLParser(tx, instruction, innerInstructions, offset)
|
||||||
case raydiumV4SwapBaseInDiscriminator, raydiumV4SwapBaseOutDiscriminator:
|
case raydiumV4SwapBaseInDiscriminator, raydiumV4SwapBaseOutDiscriminator:
|
||||||
return raydiumv4SwapParser(tx, instruction, innerInstructions, offset)
|
return raydiumv4SwapParser(tx, instruction, innerInstructions, offset)
|
||||||
|
case raydiumV4SwapBaseInV2Discriminator, raydiumV4SwapBaseOutV2Discriminator:
|
||||||
|
return raydiumv4SwapV2Parser(tx, instruction, innerInstructions, offset)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, increaseOffset(offset), InstructionIgnoredError
|
return nil, increaseOffset(offset), InstructionIgnoredError
|
||||||
@@ -397,3 +399,99 @@ func raydiumv4SwapParser(tx *Tx, instruction Instruction, innerInstructions Inne
|
|||||||
},
|
},
|
||||||
}, offset, nil
|
}, offset, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func raydiumv4SwapV2Parser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||||
|
accountsLen := len(instruction.Accounts)
|
||||||
|
if accountsLen != 8 {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("invalid number of accounts for raydiumv4 swapv2 instruction, offset %d, %d", offset[0], offset[1])
|
||||||
|
}
|
||||||
|
var entryContract = tx.rawTx.accountList[tx.rawTx.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
||||||
|
|
||||||
|
ammAccount := tx.rawTx.accountList[instruction.Accounts[1]]
|
||||||
|
user := tx.rawTx.accountList[instruction.Accounts[7]]
|
||||||
|
userSourceTokenAccount := tx.rawTx.accountList[instruction.Accounts[5]]
|
||||||
|
userDestinationTokenAccount := tx.rawTx.accountList[instruction.Accounts[6]]
|
||||||
|
baseVaultIdx := instruction.Accounts[3]
|
||||||
|
quoteVaultIdx := instruction.Accounts[4]
|
||||||
|
|
||||||
|
baseTokenbalance, err := getTokenBalanceAfterTx(tx.rawTx, baseVaultIdx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("failed to get base vault balance after tx: %v", err)
|
||||||
|
}
|
||||||
|
quoteTokenbalance, err := getTokenBalanceAfterTx(tx.rawTx, quoteVaultIdx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("failed to get quote vault balance after tx: %v", err)
|
||||||
|
}
|
||||||
|
inners, err := getInnerInstructions(innerInstructions, offset[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, increaseOffset(offset), err
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextIndex int
|
||||||
|
var srcFound, destFound bool
|
||||||
|
var baseAmount, quoteAmount decimal.Decimal
|
||||||
|
var event string
|
||||||
|
for i, inner := range inners {
|
||||||
|
from, to, amount, err := parseTokenTransfer(tx.rawTx, inner)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if from.Equals(userSourceTokenAccount) && !srcFound {
|
||||||
|
if to.Equals(tx.rawTx.accountList[baseVaultIdx]) {
|
||||||
|
event = "sell"
|
||||||
|
baseAmount = decimal.NewFromUint64(amount)
|
||||||
|
srcFound = true
|
||||||
|
} else if to.Equals(tx.rawTx.accountList[quoteVaultIdx]) {
|
||||||
|
event = "buy"
|
||||||
|
quoteAmount = decimal.NewFromUint64(amount)
|
||||||
|
srcFound = true
|
||||||
|
}
|
||||||
|
} else if to.Equals(userDestinationTokenAccount) && !destFound {
|
||||||
|
if from.Equals(tx.rawTx.accountList[quoteVaultIdx]) {
|
||||||
|
event = "sell"
|
||||||
|
quoteAmount = decimal.NewFromUint64(amount)
|
||||||
|
destFound = true
|
||||||
|
} else if from.Equals(tx.rawTx.accountList[baseVaultIdx]) {
|
||||||
|
event = "buy"
|
||||||
|
baseAmount = decimal.NewFromUint64(amount)
|
||||||
|
destFound = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if srcFound && destFound {
|
||||||
|
nextIndex = i + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !srcFound || !destFound {
|
||||||
|
return nil, increaseOffset(offset), fmt.Errorf("raydiumv4 failed to find token transfer inner instruction for swapv2, offset %d, %d", offset[0], offset[1])
|
||||||
|
}
|
||||||
|
offset[1] += uint(nextIndex + 1)
|
||||||
|
|
||||||
|
userBase := getAccountBalanceAfterTx(tx.rawTx, instruction.Accounts[5])
|
||||||
|
userQuote := getAccountBalanceAfterTx(tx.rawTx, instruction.Accounts[6])
|
||||||
|
baseReserve, _ := decimal.NewFromString(baseTokenbalance.UITokenAmount.Amount)
|
||||||
|
quoteReserve, _ := decimal.NewFromString(quoteTokenbalance.UITokenAmount.Amount)
|
||||||
|
|
||||||
|
return []Swap{
|
||||||
|
{
|
||||||
|
Program: SolProgramRaydiumV4,
|
||||||
|
Event: event,
|
||||||
|
Pool: ammAccount,
|
||||||
|
BaseMint: baseTokenbalance.MintAccount,
|
||||||
|
QuoteMint: quoteTokenbalance.MintAccount,
|
||||||
|
BaseTokenProgram: baseTokenbalance.ProgramIDAccount,
|
||||||
|
QuoteTokenProgram: quoteTokenbalance.ProgramIDAccount,
|
||||||
|
BaseMintDecimals: uint8(baseTokenbalance.UITokenAmount.Decimals),
|
||||||
|
QuoteMintDecimals: uint8(quoteTokenbalance.UITokenAmount.Decimals),
|
||||||
|
User: user,
|
||||||
|
BaseAmount: baseAmount,
|
||||||
|
QuoteAmount: quoteAmount,
|
||||||
|
BaseReserve: baseReserve,
|
||||||
|
QuoteReserve: quoteReserve,
|
||||||
|
Mayhem: false,
|
||||||
|
UserBaseBalance: userBase,
|
||||||
|
UserQuoteBalance: userQuote,
|
||||||
|
EntryContract: entryContract,
|
||||||
|
},
|
||||||
|
}, offset, nil
|
||||||
|
}
|
||||||
|
|||||||
8
tx.go
8
tx.go
@@ -12,6 +12,9 @@ type Swap struct {
|
|||||||
|
|
||||||
TxIndex int
|
TxIndex int
|
||||||
|
|
||||||
|
InstrIdx uint8
|
||||||
|
InnerIdx uint8
|
||||||
|
|
||||||
Pool solana.PublicKey
|
Pool solana.PublicKey
|
||||||
BaseMint solana.PublicKey
|
BaseMint solana.PublicKey
|
||||||
QuoteMint solana.PublicKey
|
QuoteMint solana.PublicKey
|
||||||
@@ -74,6 +77,10 @@ type SolTransfer struct {
|
|||||||
To solana.PublicKey
|
To solana.PublicKey
|
||||||
Amount decimal.Decimal
|
Amount decimal.Decimal
|
||||||
}
|
}
|
||||||
|
type ChainLink struct {
|
||||||
|
Timestamp int64
|
||||||
|
Price decimal.Decimal
|
||||||
|
}
|
||||||
|
|
||||||
type Tx struct {
|
type Tx struct {
|
||||||
rawTx *RawTx
|
rawTx *RawTx
|
||||||
@@ -83,6 +90,7 @@ type Tx struct {
|
|||||||
Swaps []Swap `json:"swaps,omitempty"`
|
Swaps []Swap `json:"swaps,omitempty"`
|
||||||
SolTransfer []SolTransfer `json:"sol_transfer,omitempty"`
|
SolTransfer []SolTransfer `json:"sol_transfer,omitempty"`
|
||||||
Block uint64 `json:"block"`
|
Block uint64 `json:"block"`
|
||||||
|
ChainLink ChainLink `json:"chain_link"`
|
||||||
BlockIndex uint64 `json:"index"`
|
BlockIndex uint64 `json:"index"`
|
||||||
TxHash *[64]byte `json:"-"`
|
TxHash *[64]byte `json:"-"`
|
||||||
BlockAt int64 `json:"block_at"`
|
BlockAt int64 `json:"block_at"`
|
||||||
|
|||||||
Reference in New Issue
Block a user