Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
972ddc7960 | ||
| bcd442195c | |||
| 0633707142 | |||
| 8e49f01054 | |||
|
|
62cc64a90a | ||
|
|
629ffe2ea7 | ||
|
|
56dac04a2a | ||
|
|
852ad4b382 | ||
|
|
3fdd4c4490 | ||
|
|
40012b531c |
@@ -27,10 +27,11 @@ func budgetParser(tx *Tx, instr Instruction, _ InnerInstructions, offset [2]uint
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func computeUnitLimitParser(offset [2]uint, _ *Tx, decodedData []byte) ([2]uint, error) {
|
func computeUnitLimitParser(offset [2]uint, tx *Tx, decodedData []byte) ([2]uint, error) {
|
||||||
if len(decodedData) < 8 {
|
if len(decodedData) < 4 {
|
||||||
return increaseOffset(offset), nil
|
return increaseOffset(offset), nil
|
||||||
}
|
}
|
||||||
|
tx.CuLimit = binary.LittleEndian.Uint32(decodedData[:4])
|
||||||
return increaseOffset(offset), nil
|
return increaseOffset(offset), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ var platformFeeAddresses = map[solana.PublicKey]string{
|
|||||||
solana.MustPublicKeyFromBase58("F4hJ3Ee3c5UuaorKAMfELBjYCjiiLH75haZTKqTywRP3"): PlatformBullX,
|
solana.MustPublicKeyFromBase58("F4hJ3Ee3c5UuaorKAMfELBjYCjiiLH75haZTKqTywRP3"): PlatformBullX,
|
||||||
solana.MustPublicKeyFromBase58("47hEzz83VFR23rLTEeVm9A7eFzjJwjvdupPPmX3cePqF"): PlatformBanana,
|
solana.MustPublicKeyFromBase58("47hEzz83VFR23rLTEeVm9A7eFzjJwjvdupPPmX3cePqF"): PlatformBanana,
|
||||||
solana.MustPublicKeyFromBase58("9yMwSPk9mrXSN7yDHUuZurAh1sjbJsfpUqjZ7SvVtdco"): PlatformTrojan,
|
solana.MustPublicKeyFromBase58("9yMwSPk9mrXSN7yDHUuZurAh1sjbJsfpUqjZ7SvVtdco"): PlatformTrojan,
|
||||||
|
solana.MustPublicKeyFromBase58("92Med3qeK7duC5iiYsHX38H2f2twJfRsSx93oNrza2VH"): PlatformTrojan,
|
||||||
|
solana.MustPublicKeyFromBase58("65gDv7pZQCZELsNpNYSFEBtNFpWZAbxmRFB6BGMqFkHH"): 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,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ func main() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ptx := msg.Tx
|
ptx := msg.Tx
|
||||||
|
fmt.Println("consume", ptx.ComputeUnitsConsumed, "limit", ptx.CuLimit, "hash", ptx.GetTxHash())
|
||||||
//data, _ := json.Marshal(tx)
|
//data, _ := json.Marshal(tx)
|
||||||
//fmt.Println(string(data))
|
//fmt.Println(string(data))
|
||||||
//continue
|
//continue
|
||||||
@@ -60,16 +61,13 @@ func main() {
|
|||||||
if tx.Program != parser.SolProgramPumpAMM {
|
if tx.Program != parser.SolProgramPumpAMM {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if tx.EntryContract == "" || tx.EntryContract == parser.SolProgramPumpAMM || tx.EntryContract == parser.EntryContractOKXDexRouterV2 || tx.EntryContract == "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
//if tx.Token1Amount.GreaterThanOrEqual(decimal.NewFromFloat(0.1)) || tx.Event != "buy" {
|
//if tx.Token1Amount.GreaterThanOrEqual(decimal.NewFromFloat(0.1)) || tx.Event != "buy" {
|
||||||
// continue
|
// continue
|
||||||
//}
|
//}
|
||||||
// printed = true
|
// printed = true
|
||||||
fmt.Printf("t: %s, block: %d, hash: %s, maker: %s, program: %s, event: %s, token0: %s, entryContract: %s, token balance: %s, EntryContract: %s\n",
|
fmt.Printf("t: %s, block: %d, is cash:%v hash: %s, maker: %s, program: %s, event: %s, token0: %s, entryContract: %s, token balance: %s, EntryContract: %s\n",
|
||||||
time.Now().Format(time.RFC3339Nano),
|
time.Now().Format(time.RFC3339Nano),
|
||||||
tx.Block, tx.GetTxHash(), tx.Maker, tx.Program, tx.Event, tx.Token0Amount, tx.EntryContract, tx.AfterSignerToken0Balance, tx.EntryContract)
|
tx.Block, tx.Cashback, tx.GetTxHash(), tx.Maker, tx.Program, tx.Event, tx.Token0Amount, tx.EntryContract, tx.AfterSignerToken0Balance, tx.EntryContract)
|
||||||
//break
|
//break
|
||||||
}
|
}
|
||||||
//if !printed {
|
//if !printed {
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ type Tx struct {
|
|||||||
EntryContract string `gorm:"column:tx_entry_contract;type:entry_contract;default:'none'" json:"tx_entry_contract"`
|
EntryContract string `gorm:"column:tx_entry_contract;type:entry_contract;default:'none'" json:"tx_entry_contract"`
|
||||||
|
|
||||||
Mayhem bool
|
Mayhem bool
|
||||||
|
Cashback bool `json:"is_cashback_coin"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *Tx) GetTxHash() string {
|
func (tx *Tx) GetTxHash() string {
|
||||||
@@ -121,6 +122,7 @@ func FromTx(tx *parser.Tx) []*Tx {
|
|||||||
|
|
||||||
EntryContract: s.CheckEntryContract(),
|
EntryContract: s.CheckEntryContract(),
|
||||||
Mayhem: s.Mayhem,
|
Mayhem: s.Mayhem,
|
||||||
|
Cashback: s.Cashback,
|
||||||
}
|
}
|
||||||
} else if s.Program == "PumpAMM" {
|
} else if s.Program == "PumpAMM" {
|
||||||
if s.BaseMint.Equals(solana.WrappedSol) {
|
if s.BaseMint.Equals(solana.WrappedSol) {
|
||||||
@@ -175,6 +177,7 @@ func FromTx(tx *parser.Tx) []*Tx {
|
|||||||
|
|
||||||
EntryContract: s.CheckEntryContract(),
|
EntryContract: s.CheckEntryContract(),
|
||||||
Mayhem: s.Mayhem,
|
Mayhem: s.Mayhem,
|
||||||
|
Cashback: s.Cashback,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newTx = &Tx{
|
newTx = &Tx{
|
||||||
@@ -219,6 +222,7 @@ func FromTx(tx *parser.Tx) []*Tx {
|
|||||||
|
|
||||||
EntryContract: s.CheckEntryContract(),
|
EntryContract: s.CheckEntryContract(),
|
||||||
Mayhem: s.Mayhem,
|
Mayhem: s.Mayhem,
|
||||||
|
Cashback: s.Cashback,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,9 +62,7 @@ func NewClientWithPumpSwap(endpoint string, ch chan SubscriptionMessage) *Client
|
|||||||
"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA", //Pump AMM
|
"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA", //Pump AMM
|
||||||
"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", //Pump
|
"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", //Pump
|
||||||
}
|
}
|
||||||
subscription.Transactions["transactions_sub"].AccountRequired = []string{
|
|
||||||
"ARu4n5mFdZogZAravu7CcizaojWnS6oqka37gdLT5SZn",
|
|
||||||
}
|
|
||||||
subscription.BlocksMeta = make(map[string]*pb.SubscribeRequestFilterBlocksMeta)
|
subscription.BlocksMeta = make(map[string]*pb.SubscribeRequestFilterBlocksMeta)
|
||||||
subscription.BlocksMeta["block_meta"] = &pb.SubscribeRequestFilterBlocksMeta{}
|
subscription.BlocksMeta["block_meta"] = &pb.SubscribeRequestFilterBlocksMeta{}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ var ()
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
var slot uint64 = 399060766
|
var slot uint64 = 399477968
|
||||||
var data = NewBlockData(decimal.NewFromFloat(100.0))
|
var data = NewBlockData(decimal.NewFromFloat(100.0))
|
||||||
client := rpc.New("https://staked.helius-rpc.com?api-key=5adcf1f9-5719-43d1-bf3f-c2d4e1e5f94d")
|
client := rpc.New("https://staked.helius-rpc.com?api-key=5adcf1f9-5719-43d1-bf3f-c2d4e1e5f94d")
|
||||||
var rewards = false
|
var rewards = false
|
||||||
|
|||||||
817
internal/test3/test.go
Normal file
817
internal/test3/test.go
Normal file
@@ -0,0 +1,817 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"log/slog"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gagliardetto/solana-go"
|
||||||
|
"github.com/gagliardetto/solana-go/rpc"
|
||||||
|
"github.com/jackc/pgtype"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
solana_parser "github.com/thloyi/pump-parser"
|
||||||
|
"gorm.io/driver/postgres"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ()
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
var data = NewBlockData(decimal.NewFromFloat(100.0))
|
||||||
|
client := rpc.New("https://staked.helius-rpc.com?api-key=5adcf1f9-5719-43d1-bf3f-c2d4e1e5f94d")
|
||||||
|
var version uint64 = 0
|
||||||
|
txSig, _ := solana.SignatureFromBase58("2LCw5yZy6sGTWKpJNxpFxR11M66cXPsrGmJXnQmWW9QVv6SDWRmu1aevc6yE9NeUz78mFb4T8TEx9w5781NHnz2T")
|
||||||
|
tx, err := client.GetTransaction(context.Background(), txSig, &rpc.GetTransactionOpts{
|
||||||
|
Commitment: rpc.CommitmentFinalized,
|
||||||
|
Encoding: solana.EncodingBase64,
|
||||||
|
MaxSupportedTransactionVersion: &version,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("get block error:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
solana_parser.EnableAllParsers()
|
||||||
|
|
||||||
|
var blockTime uint64
|
||||||
|
|
||||||
|
rawTx, err := solana_parser.FromRpcTransactionWithMeta(rpc.TransactionWithMeta{
|
||||||
|
Slot: 0,
|
||||||
|
BlockTime: nil,
|
||||||
|
Transaction: rpc.DataBytesOrJSONFromBytes(tx.Transaction.GetBinary()),
|
||||||
|
Meta: tx.Meta,
|
||||||
|
Version: tx.Version,
|
||||||
|
}, &blockTime, 0, int64(0))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("from rpc tx error:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result, err := solana_parser.ParseRawTx(rawTx)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("parse tx error:", rawTx.TxHash(), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
swapsLen := len(result.Swaps)
|
||||||
|
for i := 0; i < swapsLen; i++ {
|
||||||
|
action := result.Swaps[i]
|
||||||
|
var actions []solana_parser.Swap = make([]solana_parser.Swap, 0, 2)
|
||||||
|
actions = append(actions, action)
|
||||||
|
if i+1 < swapsLen {
|
||||||
|
nextAction := result.Swaps[i+1]
|
||||||
|
if action.Event == "buy" && nextAction.Event == "complete" &&
|
||||||
|
action.Program == solana_parser.SolProgramPump &&
|
||||||
|
nextAction.Program == solana_parser.SolProgramPump &&
|
||||||
|
action.BaseMint == nextAction.BaseMint {
|
||||||
|
actions = append(actions, nextAction)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if action.Event == "migrate" && nextAction.Event == "create" &&
|
||||||
|
action.Program == solana_parser.SolProgramPump &&
|
||||||
|
nextAction.Program == solana_parser.SolProgramPumpAMM &&
|
||||||
|
action.BaseMint == nextAction.BaseMint {
|
||||||
|
actions = append(actions, nextAction)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err = HandleAction(context.Background(), result, actions, data); err != nil {
|
||||||
|
//h.logger.Errorf("handle action error: %s - %v", result.RawTx.Transaction.Signatures[0].String(), err)
|
||||||
|
fmt.Println("parse action error:", "tx", result.GetTxHash(), "i", i, "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("tx count: ", len(data.Txs))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
meteoraDammV2Program = solana.MustPublicKeyFromBase58("cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG")
|
||||||
|
raydiumCPmmProgramID = solana.MustPublicKeyFromBase58("CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C")
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleAction(ctx context.Context, tx *solana_parser.Tx, swaps []solana_parser.Swap, data *BlockData) error {
|
||||||
|
swapLen := len(swaps)
|
||||||
|
if len(swaps) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if swaps[0].BaseMint != solana_parser.WSOL && swaps[0].QuoteMint != solana_parser.WSOL && !swaps[0].QuoteMint.IsZero() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(swaps) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
event := swaps[0].Event
|
||||||
|
swap := swaps[0]
|
||||||
|
action := SwapGetter{swap}
|
||||||
|
switch event {
|
||||||
|
case "buy", "sell":
|
||||||
|
|
||||||
|
data.AppendTx(action.GetTx(tx, uint64(swap.TxIndex), data.Price))
|
||||||
|
if swap.Program == solana_parser.SolProgramPump {
|
||||||
|
if swapLen == 2 && swaps[1].Event == "complete" {
|
||||||
|
t := pgtype.Timestamptz{}
|
||||||
|
t.Set(time.Unix(tx.BlockAt, 0))
|
||||||
|
data.AppendAction(Action{
|
||||||
|
Maker: swaps[1].User.String(),
|
||||||
|
Token: swaps[1].BaseMint.String(),
|
||||||
|
Pair: swaps[1].Pool.String(),
|
||||||
|
Action: "pump-migrate",
|
||||||
|
Block: tx.Block,
|
||||||
|
BlockAt: t,
|
||||||
|
TxHash: tx.GetTxHash(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data.SetPair(action, tx.Block, "")
|
||||||
|
|
||||||
|
case "create":
|
||||||
|
pair, err := action.GetPair(tx.Block, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data.AppendTx(action.GetTx(tx, uint64(swap.TxIndex), data.Price))
|
||||||
|
data.Pairs[pair.Address] = *pair
|
||||||
|
case "add_liquidity", "remove_liquidity", "deposit", "withdraw", "add", "remove":
|
||||||
|
liquidityTx, err := action.GetLiquidityTx(tx, uint64(swap.TxIndex))
|
||||||
|
if liquidityTx == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data.AppendTx(*liquidityTx)
|
||||||
|
return data.SetPair(action, tx.Block, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if event != "migrate" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if swap.Program == solana_parser.SolProgramPump {
|
||||||
|
t := pgtype.Timestamptz{}
|
||||||
|
t.Set(time.Unix(tx.BlockAt, 0))
|
||||||
|
if swapLen == 2 && swaps[1].Event == "create" && swaps[1].Program == solana_parser.SolProgramPumpAMM && swaps[1].BaseMint == swap.BaseMint {
|
||||||
|
tokenMint := swap.BaseMint.String()
|
||||||
|
data.AppendAction(Action{
|
||||||
|
Maker: swap.User.String(),
|
||||||
|
Token: tokenMint,
|
||||||
|
Pair: swaps[1].Pool.String(),
|
||||||
|
Action: "on-pumpswap",
|
||||||
|
Block: tx.Block,
|
||||||
|
BlockAt: t,
|
||||||
|
TxHash: tx.GetTxHash(),
|
||||||
|
})
|
||||||
|
data.NewRaydium = append(data.NewRaydium, tokenMint)
|
||||||
|
}
|
||||||
|
} else if swap.Program == solana_parser.SolProgramRaydiumLaunchLab || swap.Program == solana_parser.SolProgramRaydiumLaunchLabBonk {
|
||||||
|
t := pgtype.Timestamptz{}
|
||||||
|
t.Set(time.Unix(tx.BlockAt, 0))
|
||||||
|
var actionType string
|
||||||
|
if action.MigrateTopProgram == raydiumCPmmProgramID {
|
||||||
|
actionType = "on-raydium-cpmm"
|
||||||
|
} else {
|
||||||
|
actionType = "on-raydium-amm"
|
||||||
|
}
|
||||||
|
data.AppendAction(Action{
|
||||||
|
Maker: action.User.String(),
|
||||||
|
Token: action.BaseMint.String(),
|
||||||
|
Pair: action.MigrateToPool.String(),
|
||||||
|
Action: actionType,
|
||||||
|
Block: tx.Block,
|
||||||
|
BlockAt: t,
|
||||||
|
TxHash: tx.GetTxHash(),
|
||||||
|
})
|
||||||
|
} else if swap.Program == solana_parser.SolProgramMeteoraBondingCurve {
|
||||||
|
t := pgtype.Timestamptz{}
|
||||||
|
t.Set(time.Unix(tx.BlockAt, 0))
|
||||||
|
var actionType string
|
||||||
|
if swap.MigrateTopProgram == meteoraDammV2Program {
|
||||||
|
actionType = "on-meteora-amm-v2"
|
||||||
|
} else {
|
||||||
|
actionType = "on-meteora-amm-v1"
|
||||||
|
}
|
||||||
|
data.AppendAction(Action{
|
||||||
|
Maker: action.User.String(),
|
||||||
|
Token: action.BaseMint.String(),
|
||||||
|
Pair: action.MigrateToPool.String(),
|
||||||
|
Action: actionType,
|
||||||
|
Block: uint64(tx.Block),
|
||||||
|
BlockAt: t,
|
||||||
|
TxHash: tx.GetTxHash(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Pair struct {
|
||||||
|
Id string `gorm:"column:id;primaryKey;default:uuid_generate_v4()"`
|
||||||
|
Address string
|
||||||
|
Name string
|
||||||
|
Token0 string
|
||||||
|
Token1 string
|
||||||
|
LpToken string
|
||||||
|
ChainId int64
|
||||||
|
Reserve0 decimal.Decimal
|
||||||
|
Reserve1 decimal.Decimal
|
||||||
|
Block uint64
|
||||||
|
BlockAt *pgtype.Timestamptz `gorm:"column:block_at;default:NULL" json:"block_at,omitempty"`
|
||||||
|
CreatedAt *pgtype.Timestamptz `gorm:"autoCreateTime" json:"created_at,omitempty"`
|
||||||
|
SortId uint64
|
||||||
|
Program string
|
||||||
|
|
||||||
|
IsCreate bool `gorm:"-"`
|
||||||
|
//TokenObj *Token `gorm:"-" json:"token_obj,omitempty"`
|
||||||
|
UpdateSlot uint64 `gorm:"-"`
|
||||||
|
InDB bool `gorm:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tx struct {
|
||||||
|
Id pgtype.UUID `gorm:"column:id;primaryKey;default:uuid_generate_v4()" json:"-"`
|
||||||
|
PairAddress string `json:"pair_address"`
|
||||||
|
Maker string `json:"maker"`
|
||||||
|
Token0Address string `json:"token0_address"`
|
||||||
|
Token1Address string `json:"token1_address"`
|
||||||
|
Token0Amount decimal.Decimal `json:"token0Amount" gorm:"column:token0_amount;type:numeric"`
|
||||||
|
Token1Amount decimal.Decimal `json:"token1Amount" gorm:"column:token1_amount;type:numeric"`
|
||||||
|
PriceUsd decimal.Decimal `json:"price_usd" gorm:"column:price_usd;type:numeric"`
|
||||||
|
AmountUsd decimal.Decimal `json:"amount_usd" gorm:"column:amount_usd;type:numeric"`
|
||||||
|
Block uint64 `json:"block"`
|
||||||
|
BlockIndex uint64 `json:"index"`
|
||||||
|
Event string `json:"event"`
|
||||||
|
TxHash string `json:"tx_hash"`
|
||||||
|
TxIndex uint64 `json:"topic_index"`
|
||||||
|
Program string `json:"program"`
|
||||||
|
BlockAt pgtype.Timestamptz `gorm:"column:block_at;default:NULL" json:"block_at"`
|
||||||
|
CreatedAt *pgtype.Timestamptz `gorm:"autoCreateTime" json:"-"`
|
||||||
|
TotalSupply string `gorm:"total_supply"`
|
||||||
|
AfterReserve0 string `gorm:"after_reserve0"`
|
||||||
|
AfterReserve1 string `gorm:"after_reserve1"`
|
||||||
|
PositionChange int64 `gorm:"position_change"`
|
||||||
|
Platform string `gorm:"column:tx_platform;type:platform;default:'none'" json:"tx_platform"`
|
||||||
|
PlatformFee decimal.Decimal `gorm:"-" json:"-"` // TODO: save to db
|
||||||
|
CUPrice decimal.Decimal `gorm:"column:tx_cu_price;type:numeric" json:"tx_cu_price"`
|
||||||
|
MevAgent string `gorm:"column:tx_mev_agent;type:mev_agent;default:'none'" json:"tx_mev_agent"`
|
||||||
|
MevAgentFee decimal.Decimal `gorm:"column:tx_mev_agent_fee;type:numeric" json:"tx_mev_agent_fee"`
|
||||||
|
AfterSOLBalance decimal.Decimal `gorm:"column:after_sol_balance;type:numeric" json:"after_sol_balance"`
|
||||||
|
EntryContract string `gorm:"column:tx_entry_contract;type:entry_contract;default:'none'" json:"tx_entry_contract"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action struct {
|
||||||
|
Id pgtype.UUID `gorm:"column:id;primaryKey;default:uuid_generate_v4()" json:"-"`
|
||||||
|
Maker string `json:"maker"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
Pair string `json:"pair"`
|
||||||
|
Action string `json:"action"`
|
||||||
|
Block uint64 `json:"block"`
|
||||||
|
BlockAt pgtype.Timestamptz `json:"block_at"`
|
||||||
|
TxHash string `json:"tx_hash"`
|
||||||
|
CreatedAt *pgtype.Timestamptz `gorm:"autoCreateTime" json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlockData struct {
|
||||||
|
Pairs map[string]Pair
|
||||||
|
Txs []Tx
|
||||||
|
Actions []Action
|
||||||
|
Price decimal.Decimal
|
||||||
|
NewRaydium []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBlockData(price decimal.Decimal) *BlockData {
|
||||||
|
return &BlockData{
|
||||||
|
Pairs: make(map[string]Pair),
|
||||||
|
Txs: make([]Tx, 0),
|
||||||
|
Actions: make([]Action, 0),
|
||||||
|
Price: price,
|
||||||
|
NewRaydium: make([]string, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bd *BlockData) AppendTx(tx Tx) {
|
||||||
|
bd.Txs = append(bd.Txs, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bd *BlockData) AppendAction(action Action) {
|
||||||
|
bd.Actions = append(bd.Actions, action)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bd *BlockData) SetPair(action SwapGetter, block uint64, _ string) error {
|
||||||
|
pair, err := action.GetPair(block, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bd.Pairs[pair.Address] = *pair
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SwapGetter struct {
|
||||||
|
solana_parser.Swap
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
PositionChangeNone = int64(iota)
|
||||||
|
PositionChangeNewBuy
|
||||||
|
PositionChangeBuyMore
|
||||||
|
PositionChangeSellPart
|
||||||
|
PositionChangeSellAll
|
||||||
|
)
|
||||||
|
|
||||||
|
func (spg SwapGetter) GetLiquidityTx(tx *solana_parser.Tx, index uint64) (*Tx, error) {
|
||||||
|
if spg.BaseMint != solana.WrappedSol && spg.QuoteMint != solana.WrappedSol {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
token0 string
|
||||||
|
amount0 decimal.Decimal
|
||||||
|
amount1 decimal.Decimal
|
||||||
|
pool0 decimal.Decimal
|
||||||
|
pool1 decimal.Decimal
|
||||||
|
|
||||||
|
event string
|
||||||
|
)
|
||||||
|
|
||||||
|
if spg.BaseMint == solana.WrappedSol {
|
||||||
|
amount0 = spg.QuoteAmount.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
|
||||||
|
amount1 = spg.BaseAmount.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
|
||||||
|
token0 = spg.QuoteMint.String()
|
||||||
|
pool0 = spg.QuoteReserve.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
|
||||||
|
pool1 = spg.BaseReserve.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
|
||||||
|
} else {
|
||||||
|
amount0 = spg.BaseAmount.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
|
||||||
|
amount1 = spg.QuoteAmount.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
|
||||||
|
token0 = spg.BaseMint.String()
|
||||||
|
pool0 = spg.BaseReserve.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
|
||||||
|
pool1 = spg.QuoteReserve.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
|
||||||
|
}
|
||||||
|
if spg.Event == "deposit" || spg.Event == "add" || spg.Event == "add_liquidity" || spg.Event == "add_liquidity_one_side" {
|
||||||
|
event = "add"
|
||||||
|
} else if spg.Event == "withdraw" || spg.Event == "remove" || spg.Event == "remove_liquidity" || spg.Event == "remove_liquidity_one_side" {
|
||||||
|
event = "remove"
|
||||||
|
}
|
||||||
|
if event == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mevName, mevFee := tx.CheckMevAgent()
|
||||||
|
platformName, platformFee := tx.CheckPlatform(spg.Swap)
|
||||||
|
|
||||||
|
pairString := ""
|
||||||
|
if spg.Program == solana_parser.SolProgramPump {
|
||||||
|
pairString = spg.BaseMint.String()
|
||||||
|
} else {
|
||||||
|
pairString = spg.Pool.String()
|
||||||
|
}
|
||||||
|
t := pgtype.Timestamptz{}
|
||||||
|
_ = t.Set(time.Unix(tx.BlockAt, 0))
|
||||||
|
return &Tx{
|
||||||
|
PairAddress: pairString,
|
||||||
|
Maker: spg.User.String(),
|
||||||
|
Token0Address: token0,
|
||||||
|
Token1Address: "So11111111111111111111111111111111111111112",
|
||||||
|
Token0Amount: amount0,
|
||||||
|
Token1Amount: amount1,
|
||||||
|
Block: tx.Block,
|
||||||
|
BlockIndex: tx.BlockIndex,
|
||||||
|
Event: event,
|
||||||
|
TxHash: tx.GetTxHash(),
|
||||||
|
TxIndex: index,
|
||||||
|
BlockAt: t,
|
||||||
|
Program: spg.Program,
|
||||||
|
AfterReserve0: pool0.String(),
|
||||||
|
AfterReserve1: pool1.String(),
|
||||||
|
Platform: platformName,
|
||||||
|
PlatformFee: platformFee,
|
||||||
|
CUPrice: tx.CUPrice,
|
||||||
|
MevAgent: mevName,
|
||||||
|
MevAgentFee: mevFee,
|
||||||
|
AfterSOLBalance: spg.AfterSOLBalance,
|
||||||
|
EntryContract: spg.CheckEntryContract(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spg SwapGetter) GetTx(tx *solana_parser.Tx, index uint64, price decimal.Decimal) Tx {
|
||||||
|
var (
|
||||||
|
token0 string
|
||||||
|
amount0 decimal.Decimal
|
||||||
|
amount1 decimal.Decimal
|
||||||
|
pool0 decimal.Decimal
|
||||||
|
pool1 decimal.Decimal
|
||||||
|
|
||||||
|
event string
|
||||||
|
)
|
||||||
|
|
||||||
|
if spg.BaseMint == solana.WrappedSol {
|
||||||
|
amount0 = spg.QuoteAmount.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
|
||||||
|
amount1 = spg.BaseAmount.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
|
||||||
|
token0 = spg.QuoteMint.String()
|
||||||
|
pool0 = spg.QuoteReserve.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
|
||||||
|
pool1 = spg.BaseReserve.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
|
||||||
|
if spg.Event == "buy" {
|
||||||
|
event = "sell"
|
||||||
|
} else if spg.Event == "sell" {
|
||||||
|
event = "buy"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
amount0 = spg.BaseAmount.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
|
||||||
|
amount1 = spg.QuoteAmount.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
|
||||||
|
token0 = spg.BaseMint.String()
|
||||||
|
pool0 = spg.BaseReserve.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
|
||||||
|
pool1 = spg.QuoteReserve.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
|
||||||
|
event = spg.Event
|
||||||
|
}
|
||||||
|
|
||||||
|
priceUsd := decimal.Zero
|
||||||
|
if amount0.GreaterThan(priceUsd) {
|
||||||
|
priceUsd = amount1.Div(amount0).Mul(price)
|
||||||
|
}
|
||||||
|
pc := PositionChangeNone
|
||||||
|
if event == "buy" {
|
||||||
|
pc = PositionChangeNewBuy
|
||||||
|
if spg.BaseMint == solana.WrappedSol {
|
||||||
|
if spg.UserQuoteBalance.GreaterThan(spg.QuoteAmount) {
|
||||||
|
pc = PositionChangeBuyMore
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if spg.UserBaseBalance.GreaterThan(spg.BaseAmount) {
|
||||||
|
pc = PositionChangeBuyMore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if event == "sell" {
|
||||||
|
pc = PositionChangeSellPart
|
||||||
|
if spg.BaseMint == solana.WrappedSol {
|
||||||
|
if spg.UserQuoteBalance.Div(decimal.New(1, int32(spg.QuoteMintDecimals))).LessThan(decimal.NewFromFloat(0.01)) {
|
||||||
|
pc = PositionChangeSellAll
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if spg.UserBaseBalance.Div(decimal.New(1, int32(spg.BaseMintDecimals))).LessThan(decimal.NewFromFloat(0.01)) {
|
||||||
|
pc = PositionChangeSellAll
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mevName, mevFee := tx.CheckMevAgent()
|
||||||
|
platformName, platformFee := tx.CheckPlatform(spg.Swap)
|
||||||
|
|
||||||
|
if mevName == "" {
|
||||||
|
mevName = "none"
|
||||||
|
}
|
||||||
|
if mevName == "unknown" {
|
||||||
|
mevName = "none"
|
||||||
|
mevFee = decimal.Zero
|
||||||
|
}
|
||||||
|
pairString := ""
|
||||||
|
if spg.Program == solana_parser.SolProgramPump {
|
||||||
|
pairString = spg.BaseMint.String()
|
||||||
|
} else {
|
||||||
|
pairString = spg.Pool.String()
|
||||||
|
}
|
||||||
|
t := pgtype.Timestamptz{}
|
||||||
|
_ = t.Set(time.Unix(tx.BlockAt, 0))
|
||||||
|
|
||||||
|
return Tx{
|
||||||
|
PairAddress: pairString,
|
||||||
|
Maker: spg.User.String(),
|
||||||
|
Token0Address: token0,
|
||||||
|
Token1Address: "So11111111111111111111111111111111111111112",
|
||||||
|
Token0Amount: amount0,
|
||||||
|
Token1Amount: amount1,
|
||||||
|
PriceUsd: priceUsd,
|
||||||
|
AmountUsd: amount1.Mul(price),
|
||||||
|
Block: tx.Block,
|
||||||
|
BlockIndex: tx.BlockIndex,
|
||||||
|
Event: event,
|
||||||
|
TxHash: tx.GetTxHash(),
|
||||||
|
TxIndex: index,
|
||||||
|
BlockAt: t,
|
||||||
|
Program: spg.Program,
|
||||||
|
AfterReserve0: pool0.String(),
|
||||||
|
AfterReserve1: pool1.String(),
|
||||||
|
PositionChange: pc,
|
||||||
|
Platform: platformName,
|
||||||
|
PlatformFee: platformFee,
|
||||||
|
CUPrice: tx.CUPrice,
|
||||||
|
MevAgent: mevName,
|
||||||
|
MevAgentFee: mevFee,
|
||||||
|
AfterSOLBalance: spg.AfterSOLBalance,
|
||||||
|
EntryContract: spg.CheckEntryContract(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spg SwapGetter) GetPair(slot uint64, _ string) (*Pair, error) {
|
||||||
|
//pump amm
|
||||||
|
if spg.Program == solana_parser.SolProgramPump {
|
||||||
|
tokenMint := spg.BaseMint.String()
|
||||||
|
return &Pair{
|
||||||
|
Address: tokenMint,
|
||||||
|
Token0: tokenMint,
|
||||||
|
Token1: "So11111111111111111111111111111111111111112",
|
||||||
|
ChainId: 900,
|
||||||
|
Reserve0: spg.BaseReserve.Div(decimal.New(1, int32(spg.BaseMintDecimals))),
|
||||||
|
Reserve1: spg.QuoteReserve.Div(decimal.New(1, int32(spg.QuoteMintDecimals))),
|
||||||
|
IsCreate: spg.Event == "create",
|
||||||
|
Program: spg.Program,
|
||||||
|
UpdateSlot: slot,
|
||||||
|
}, nil
|
||||||
|
} else {
|
||||||
|
var (
|
||||||
|
token0 string
|
||||||
|
amount0 decimal.Decimal
|
||||||
|
amount1 decimal.Decimal
|
||||||
|
)
|
||||||
|
if spg.BaseMint.IsZero() || spg.QuoteMint.IsZero() {
|
||||||
|
return nil, errors.New("base mint or quote mint is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if spg.BaseMint == solana.WrappedSol {
|
||||||
|
amount0 = spg.QuoteReserve.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
|
||||||
|
amount1 = spg.BaseReserve.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
|
||||||
|
//decimal0 = spg.QuoteMintDecimals
|
||||||
|
token0 = spg.QuoteMint.String()
|
||||||
|
} else {
|
||||||
|
amount0 = spg.BaseReserve.Div(decimal.New(1, int32(spg.BaseMintDecimals)))
|
||||||
|
amount1 = spg.QuoteReserve.Div(decimal.New(1, int32(spg.QuoteMintDecimals)))
|
||||||
|
//decimal0 = a.BaseDecimals
|
||||||
|
token0 = spg.BaseMint.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Pair{
|
||||||
|
Address: spg.Pool.String(),
|
||||||
|
LpToken: spg.LpMint.String(),
|
||||||
|
Token0: token0,
|
||||||
|
Token1: "So11111111111111111111111111111111111111112",
|
||||||
|
ChainId: 900,
|
||||||
|
Reserve0: amount0,
|
||||||
|
Reserve1: amount1,
|
||||||
|
IsCreate: false,
|
||||||
|
Program: spg.Program,
|
||||||
|
UpdateSlot: slot,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBlockTxsFromDb(db *gorm.DB, block uint64) ([]Tx, error) {
|
||||||
|
var txs []Tx
|
||||||
|
result := db.Table("tx").Where("block = ?", block).Find(&txs)
|
||||||
|
return txs, result.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBlockActionsFromDb(db *gorm.DB, block uint64) ([]Action, error) {
|
||||||
|
var txs []Action
|
||||||
|
result := db.Table("action").Where("block = ?", block).Find(&txs)
|
||||||
|
return txs, result.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
type dbLog struct {
|
||||||
|
logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *dbLog) Printf(format string, args ...interface{}) {
|
||||||
|
l.logger.Info(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDbLog() *dbLog {
|
||||||
|
return &dbLog{logger: slog.Default()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGorm(dsn string) *gorm.DB {
|
||||||
|
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
|
||||||
|
Logger: logger.New(newDbLog(), logger.Config{
|
||||||
|
Colorful: false,
|
||||||
|
LogLevel: logger.Warn,
|
||||||
|
SlowThreshold: time.Second * 10,
|
||||||
|
IgnoreRecordNotFoundError: true,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareTxs(dbTxs []Tx, dataTxs []Tx) (diff int, missing int) {
|
||||||
|
dataByHash := make(map[string][]Tx, len(dataTxs))
|
||||||
|
for _, tx := range dataTxs {
|
||||||
|
dataByHash[fmt.Sprintf("%s-%d", tx.TxHash, tx.TxIndex)] = append(dataByHash[fmt.Sprintf("%s-%d", tx.TxHash, tx.TxIndex)], tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dbTx := range dbTxs {
|
||||||
|
candidates := dataByHash[fmt.Sprintf("%s-%d", dbTx.TxHash, dbTx.TxIndex)]
|
||||||
|
if len(candidates) == 0 {
|
||||||
|
missing++
|
||||||
|
log.Printf("missing tx: %s", txCompareString(dbTx))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
matched := false
|
||||||
|
for _, dataTx := range candidates {
|
||||||
|
if txEqualWithoutHash(dbTx, dataTx) {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
diff++
|
||||||
|
log.Printf("tx diff hash=%s, program=%s, event:%s: %s, ", dbTx.TxHash, dbTx.Program, dbTx.Event, txCompareDiffString(dbTx, candidates[0]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Printf("compare txs done: db=%d parsed=%d missing=%d diff=%d", len(dbTxs), len(dataTxs), missing, diff)
|
||||||
|
return diff, missing
|
||||||
|
}
|
||||||
|
|
||||||
|
func withinOnePercentDecimal(a decimal.Decimal, b decimal.Decimal) bool {
|
||||||
|
if a.IsZero() {
|
||||||
|
return b.IsZero()
|
||||||
|
}
|
||||||
|
diff := a.Sub(b).Abs()
|
||||||
|
threshold := a.Abs().Mul(decimal.NewFromFloat(0.03))
|
||||||
|
return diff.LessThanOrEqual(threshold)
|
||||||
|
}
|
||||||
|
|
||||||
|
func withinOnePercentStringDecimal(a string, b string) bool {
|
||||||
|
ad, errA := decimal.NewFromString(a)
|
||||||
|
bd, errB := decimal.NewFromString(b)
|
||||||
|
if errA != nil || errB != nil {
|
||||||
|
return a == b
|
||||||
|
}
|
||||||
|
return withinOnePercentDecimal(ad, bd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func txEqualWithoutHash(a Tx, b Tx) bool {
|
||||||
|
//mevMatch := a.MevAgent == b.MevAgent || (a.MevAgent == "none" && b.MevAgent == "unknown") || (a.MevAgent == "unknown" && b.MevAgent == "none")
|
||||||
|
//mevNone := a.MevAgent == "none" || a.MevAgent == "unknown"
|
||||||
|
|
||||||
|
return a.PairAddress == b.PairAddress &&
|
||||||
|
a.Token1Address == b.Token1Address &&
|
||||||
|
(a.Token0Address == "" || a.Token0Address == b.Token0Address) &&
|
||||||
|
//a.Maker == b.Maker &&
|
||||||
|
(a.Token0Address == "" || withinOnePercentDecimal(a.Token0Amount, b.Token0Amount)) &&
|
||||||
|
withinOnePercentDecimal(a.Token1Amount, b.Token1Amount) &&
|
||||||
|
a.Block == b.Block &&
|
||||||
|
a.BlockIndex == b.BlockIndex &&
|
||||||
|
a.Event == b.Event &&
|
||||||
|
a.TxIndex == b.TxIndex &&
|
||||||
|
a.Program == b.Program &&
|
||||||
|
(a.Program == solana_parser.SolProgramPumpAMM || a.Program == solana_parser.SolProgramPump || a.Program == solana_parser.SolProgramMeteoraPools || (a.Event == "create") || withinOnePercentStringDecimal(a.AfterReserve0, b.AfterReserve0)) &&
|
||||||
|
(a.Program == solana_parser.SolProgramPumpAMM || a.Program == solana_parser.SolProgramPump || a.Program == solana_parser.SolProgramMeteoraPools || (a.Event == "create") || withinOnePercentStringDecimal(a.AfterReserve1, b.AfterReserve1)) &&
|
||||||
|
// a.PositionChange == b.PositionChange &&
|
||||||
|
(a.Platform == b.Platform || (a.Platform == "photon" && b.Platform == "fake") || (a.Platform == "trojan" && b.Platform == "fake")) &&
|
||||||
|
a.CUPrice.String() == b.CUPrice.String() // &&
|
||||||
|
//mevMatch &&
|
||||||
|
//(mevNone || a.MevAgentFee.String() == b.MevAgentFee.String()) &&
|
||||||
|
//(a.Program == solana_parser.SolProgramRaydiumV4 || a.AfterSOLBalance.String() == b.AfterSOLBalance.String())
|
||||||
|
//&&
|
||||||
|
// a.EntryContract == b.EntryContract
|
||||||
|
}
|
||||||
|
|
||||||
|
func txCompareDiffString(a Tx, b Tx) string {
|
||||||
|
var diffs []string
|
||||||
|
if a.PairAddress != b.PairAddress {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("PairAddress db=%s data=%s", a.PairAddress, b.PairAddress))
|
||||||
|
}
|
||||||
|
//if a.Maker != b.Maker {
|
||||||
|
// diffs = append(diffs, fmt.Sprintf("Maker db=%s, data=%s", a.Maker, b.Maker))
|
||||||
|
//}
|
||||||
|
if a.Token1Address != b.Token1Address {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("Token1Address db=%s data=%s", a.Token1Address, b.Token1Address))
|
||||||
|
}
|
||||||
|
if a.Token0Address != b.Token0Address {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("Token0Address db=%s data=%s", a.Token0Address, b.Token0Address))
|
||||||
|
}
|
||||||
|
if !withinOnePercentDecimal(a.Token0Amount, b.Token0Amount) {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("Token0Amount db=%s data=%s", a.Token0Amount.String(), b.Token0Amount.String()))
|
||||||
|
}
|
||||||
|
if !withinOnePercentDecimal(a.Token1Amount, b.Token1Amount) {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("Token1Amount db=%s data=%s", a.Token1Amount.String(), b.Token1Amount.String()))
|
||||||
|
}
|
||||||
|
if a.Block != b.Block {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("Block db=%d data=%d", a.Block, b.Block))
|
||||||
|
}
|
||||||
|
if a.BlockIndex != b.BlockIndex {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("BlockIndex db=%d data=%d", a.BlockIndex, b.BlockIndex))
|
||||||
|
}
|
||||||
|
if a.Event != b.Event {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("Event db=%s data=%s", a.Event, b.Event))
|
||||||
|
}
|
||||||
|
if a.TxIndex != b.TxIndex {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("TxIndex db=%d data=%d", a.TxIndex, b.TxIndex))
|
||||||
|
}
|
||||||
|
if a.Program != b.Program {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("Program db=%s data=%s", a.Program, b.Program))
|
||||||
|
}
|
||||||
|
if !withinOnePercentStringDecimal(a.AfterReserve0, b.AfterReserve0) {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("AfterReserve0 db=%s data=%s", a.AfterReserve0, b.AfterReserve0))
|
||||||
|
}
|
||||||
|
if !withinOnePercentStringDecimal(a.AfterReserve1, b.AfterReserve1) {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("AfterReserve1 db=%s data=%s", a.AfterReserve1, b.AfterReserve1))
|
||||||
|
}
|
||||||
|
//if a.PositionChange != b.PositionChange {
|
||||||
|
// diffs = append(diffs, fmt.Sprintf("PositionChange db=%d data=%d", a.PositionChange, b.PositionChange))
|
||||||
|
//}
|
||||||
|
if a.Platform != b.Platform {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("Platform db=%s data=%s", a.Platform, b.Platform))
|
||||||
|
}
|
||||||
|
if a.CUPrice.String() != b.CUPrice.String() {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("CUPrice db=%s data=%s", a.CUPrice.String(), b.CUPrice.String()))
|
||||||
|
}
|
||||||
|
//if a.MevAgent != b.MevAgent {
|
||||||
|
// diffs = append(diffs, fmt.Sprintf("MevAgent db=%s data=%s", a.MevAgent, b.MevAgent))
|
||||||
|
//}
|
||||||
|
//if a.MevAgentFee.String() != b.MevAgentFee.String() {
|
||||||
|
// diffs = append(diffs, fmt.Sprintf("MevAgentFee db=%s data=%s", a.MevAgentFee.String(), b.MevAgentFee.String()))
|
||||||
|
//}
|
||||||
|
//if a.AfterSOLBalance.String() != b.AfterSOLBalance.String() {
|
||||||
|
// diffs = append(diffs, fmt.Sprintf("AfterSOLBalance db=%s data=%s", a.AfterSOLBalance.String(), b.AfterSOLBalance.String()))
|
||||||
|
//}
|
||||||
|
//if a.EntryContract != b.EntryContract {
|
||||||
|
// diffs = append(diffs, fmt.Sprintf("EntryContract db=%s data=%s", a.EntryContract, b.EntryContract))
|
||||||
|
//}
|
||||||
|
return strings.Join(diffs, "; ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareActions(dbActions []Action, dataActions []Action) (diff, missing int) {
|
||||||
|
dataByHash := make(map[string][]Action, len(dataActions))
|
||||||
|
for _, action := range dataActions {
|
||||||
|
dataByHash[action.TxHash] = append(dataByHash[action.TxHash], action)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dbAction := range dbActions {
|
||||||
|
candidates := dataByHash[dbAction.TxHash]
|
||||||
|
if len(candidates) == 0 {
|
||||||
|
missing++
|
||||||
|
log.Printf("missing action: %s", actionCompareString(dbAction))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
matched := false
|
||||||
|
for _, dataAction := range candidates {
|
||||||
|
if actionEqualWithoutHash(dbAction, dataAction) {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
diff++
|
||||||
|
log.Printf("action diff hash=%s: %s", dbAction.TxHash, actionCompareDiffString(dbAction, candidates[0]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Printf("compare actions done: db=%d parsed=%d missing=%d diff=%d", len(dbActions), len(dataActions), missing, diff)
|
||||||
|
return diff, missing
|
||||||
|
}
|
||||||
|
|
||||||
|
func actionEqualWithoutHash(a Action, b Action) bool {
|
||||||
|
return a.Maker == b.Maker &&
|
||||||
|
a.Token == b.Token &&
|
||||||
|
a.Pair == b.Pair &&
|
||||||
|
a.Action == b.Action &&
|
||||||
|
a.Block == b.Block
|
||||||
|
}
|
||||||
|
|
||||||
|
func actionCompareDiffString(a Action, b Action) string {
|
||||||
|
var diffs []string
|
||||||
|
if a.Maker != b.Maker {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("Maker db=%s data=%s", a.Maker, b.Maker))
|
||||||
|
}
|
||||||
|
if a.Token != b.Token {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("Token db=%s data=%s", a.Token, b.Token))
|
||||||
|
}
|
||||||
|
if a.Pair != b.Pair {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("Pair db=%s data=%s", a.Pair, b.Pair))
|
||||||
|
}
|
||||||
|
if a.Action != b.Action {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("Action db=%s data=%s", a.Action, b.Action))
|
||||||
|
}
|
||||||
|
if a.Block != b.Block {
|
||||||
|
diffs = append(diffs, fmt.Sprintf("Block db=%d data=%d", a.Block, b.Block))
|
||||||
|
}
|
||||||
|
return strings.Join(diffs, "; ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func actionCompareString(action Action) string {
|
||||||
|
return fmt.Sprintf("Maker=%s Token=%s Pair=%s Action=%s Block=%d TxHash=%s", action.Maker, action.Token, action.Pair, action.Action, action.Block, action.TxHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func txCompareString(tx Tx) string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"tx.Program=%s TxHash=%s PairAddress=%s Token1Address=%s Token0Amount=%s Token1Amount=%s Block=%d BlockIndex=%d Event=%s TxIndex=%d AfterReserve0=%s AfterReserve1=%s PositionChange=%d Platform=%s CUPrice=%s MevAgent=%s MevAgentFee=%s AfterSOLBalance=%s EntryContract=%s",
|
||||||
|
tx.Program,
|
||||||
|
tx.TxHash,
|
||||||
|
tx.PairAddress,
|
||||||
|
tx.Token1Address,
|
||||||
|
tx.Token0Amount.String(),
|
||||||
|
tx.Token1Amount.String(),
|
||||||
|
tx.Block,
|
||||||
|
tx.BlockIndex,
|
||||||
|
tx.Event,
|
||||||
|
tx.TxIndex,
|
||||||
|
tx.AfterReserve0,
|
||||||
|
tx.AfterReserve1,
|
||||||
|
tx.PositionChange,
|
||||||
|
tx.Platform,
|
||||||
|
tx.CUPrice.String(),
|
||||||
|
tx.MevAgent,
|
||||||
|
tx.MevAgentFee.String(),
|
||||||
|
tx.AfterSOLBalance.String(),
|
||||||
|
tx.EntryContract,
|
||||||
|
)
|
||||||
|
}
|
||||||
1
meta.go
1
meta.go
@@ -234,5 +234,6 @@ var transferDiscriminator = uint32(2)
|
|||||||
var createAccountWithSeedDiscriminator = uint32(3)
|
var createAccountWithSeedDiscriminator = uint32(3)
|
||||||
|
|
||||||
var systemProgram = solana.MustPublicKeyFromBase58("11111111111111111111111111111111")
|
var systemProgram = solana.MustPublicKeyFromBase58("11111111111111111111111111111111")
|
||||||
|
var momoProgram = solana.MustPublicKeyFromBase58("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr")
|
||||||
|
|
||||||
var eventDiscriminator = [8]byte{228, 69, 165, 46, 81, 203, 154, 29}
|
var eventDiscriminator = [8]byte{228, 69, 165, 46, 81, 203, 154, 29}
|
||||||
|
|||||||
@@ -371,6 +371,7 @@ func metaoraPoolAddLiquidity(tx *Tx, instruction Instruction, innerInstructions
|
|||||||
if len(instruction.Accounts) < 14 {
|
if len(instruction.Accounts) < 14 {
|
||||||
return nil, increaseOffset(offset), fmt.Errorf("invalid instruction accounts length")
|
return nil, increaseOffset(offset), fmt.Errorf("invalid instruction accounts length")
|
||||||
}
|
}
|
||||||
|
var entryContract = tx.rawTx.accountList[tx.rawTx.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
||||||
|
|
||||||
pool := tx.rawTx.accountList[instruction.Accounts[0]]
|
pool := tx.rawTx.accountList[instruction.Accounts[0]]
|
||||||
lpMint := tx.rawTx.accountList[instruction.Accounts[1]]
|
lpMint := tx.rawTx.accountList[instruction.Accounts[1]]
|
||||||
@@ -494,8 +495,6 @@ func metaoraPoolAddLiquidity(tx *Tx, instruction Instruction, innerInstructions
|
|||||||
return nil, increaseOffset(offset), fmt.Errorf("failed to find deposit instructions")
|
return nil, increaseOffset(offset), fmt.Errorf("failed to find deposit instructions")
|
||||||
}
|
}
|
||||||
|
|
||||||
var entryContract = tx.rawTx.accountList[tx.rawTx.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
|
||||||
|
|
||||||
var event = "add_liquidity_one_side"
|
var event = "add_liquidity_one_side"
|
||||||
if baseFound && quoteFound {
|
if baseFound && quoteFound {
|
||||||
// both sides
|
// both sides
|
||||||
@@ -530,6 +529,7 @@ func metaoraPoolRemoveLiquidity(tx *Tx, instruction Instruction, innerInstructio
|
|||||||
if len(instruction.Accounts) < 14 {
|
if len(instruction.Accounts) < 14 {
|
||||||
return nil, increaseOffset(offset), fmt.Errorf("invalid instruction accounts length")
|
return nil, increaseOffset(offset), fmt.Errorf("invalid instruction accounts length")
|
||||||
}
|
}
|
||||||
|
var entryContract = tx.rawTx.accountList[tx.rawTx.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
||||||
|
|
||||||
pool := tx.rawTx.accountList[instruction.Accounts[0]]
|
pool := tx.rawTx.accountList[instruction.Accounts[0]]
|
||||||
lpMint := tx.rawTx.accountList[instruction.Accounts[1]]
|
lpMint := tx.rawTx.accountList[instruction.Accounts[1]]
|
||||||
@@ -700,8 +700,6 @@ func metaoraPoolRemoveLiquidity(tx *Tx, instruction Instruction, innerInstructio
|
|||||||
return nil, increaseOffset(offset), fmt.Errorf("failed to find withdraw instructions, baseFound: %v, quoteFound: %v", baseFound, quoteFound)
|
return nil, increaseOffset(offset), fmt.Errorf("failed to find withdraw instructions, baseFound: %v, quoteFound: %v", baseFound, quoteFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
var entryContract = tx.rawTx.accountList[tx.rawTx.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
|
||||||
|
|
||||||
var event = "remove_liquidity_one_side"
|
var event = "remove_liquidity_one_side"
|
||||||
if baseFound && quoteFound {
|
if baseFound && quoteFound {
|
||||||
event = "remove_liquidity"
|
event = "remove_liquidity"
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ func meteoraDammV2InitializePoolParser(tx *Tx, instruction Instruction, innerIns
|
|||||||
var prefixLen = offset[1]
|
var prefixLen = offset[1]
|
||||||
inners, err := getInnerInstructions(innerInstructions, prefixLen)
|
inners, err := getInnerInstructions(innerInstructions, prefixLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, increaseOffset(offset), fmt.Errorf("meta Bonding Curve initial get inner instructions error: %v, offset, %d, %d", err, offset[0], offset[1])
|
return nil, increaseOffset(offset), fmt.Errorf("meta damm initial get inner instructions error: %v, offset, %d, %d", err, offset[0], offset[1])
|
||||||
}
|
}
|
||||||
var loadedEvent bool
|
var loadedEvent bool
|
||||||
var initializePoolEvent MetaoraDammInitializePoolEvent
|
var initializePoolEvent MetaoraDammInitializePoolEvent
|
||||||
@@ -119,6 +119,13 @@ func meteoraDammV2InitializePoolParser(tx *Tx, instruction Instruction, innerIns
|
|||||||
}
|
}
|
||||||
baseVaultAccountIndex := instruction.Accounts[10]
|
baseVaultAccountIndex := instruction.Accounts[10]
|
||||||
quoteVaultAccountIndex := instruction.Accounts[11]
|
quoteVaultAccountIndex := instruction.Accounts[11]
|
||||||
|
if bytes.Equal(instruction.Data[:8], meteoraDammV2InitializePoolWithDynamicConfig[:]) {
|
||||||
|
baseVaultAccountIndex = instruction.Accounts[11]
|
||||||
|
quoteVaultAccountIndex = instruction.Accounts[12]
|
||||||
|
} else if bytes.Equal(instruction.Data[:8], meteoraDammV2InitializeCustomizablePoolDiscriminator[:]) {
|
||||||
|
baseVaultAccountIndex = instruction.Accounts[9]
|
||||||
|
quoteVaultAccountIndex = instruction.Accounts[10]
|
||||||
|
}
|
||||||
|
|
||||||
baseVaultTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, baseVaultAccountIndex)
|
baseVaultTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, baseVaultAccountIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -848,11 +848,11 @@ func orcaWhirPoolSwapV2Parser(tx *Tx, instruction Instruction, innerInstructions
|
|||||||
var baseFound, quoteFound bool
|
var baseFound, quoteFound bool
|
||||||
var skipOffset = 0
|
var skipOffset = 0
|
||||||
for i, inner := range inners {
|
for i, inner := range inners {
|
||||||
from, to, amount, err := parseTokenTransfer(tx.rawTx, inner)
|
if !tx.rawTx.accountList[inner.ProgramIDIndex].Equals(solana.Token2022ProgramID) && !tx.rawTx.accountList[inner.ProgramIDIndex].Equals(solana.TokenProgramID) {
|
||||||
if err != nil {
|
|
||||||
if i <= 1 { //maybe momo??
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
from, to, amount, err := parseTokenTransfer(tx.rawTx, inner)
|
||||||
|
if err != nil {
|
||||||
return nil, increaseOffset(offset), fmt.Errorf("orca whirpool swapv2 parse token transfer error: %v, offset, %d, %d", err, offset[0], offset[1])
|
return nil, increaseOffset(offset), fmt.Errorf("orca whirpool swapv2 parse token transfer error: %v, offset, %d, %d", err, offset[0], offset[1])
|
||||||
}
|
}
|
||||||
if !baseFound && (from.Equals(vault0Account) || to.Equals(vault0Account)) {
|
if !baseFound && (from.Equals(vault0Account) || to.Equals(vault0Account)) {
|
||||||
@@ -1010,11 +1010,11 @@ func orcaWhirPoolTwoHopSwapParser(tx *Tx, instruction Instruction, innerInstruct
|
|||||||
{
|
{
|
||||||
baseTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, pool2VaultBase)
|
baseTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, pool2VaultBase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, increaseOffset(offset), fmt.Errorf("failed to get pool1 token0 vault balance after tx: %v", err)
|
return nil, increaseOffset(offset), fmt.Errorf("failed to get pool2 token0 vault balance after tx: %v", err)
|
||||||
}
|
}
|
||||||
quoteTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, pool2VaultQuote)
|
quoteTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, pool2VaultQuote)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, increaseOffset(offset), fmt.Errorf("failed to get pool1 token1 vault balance after tx: %v", err)
|
return nil, increaseOffset(offset), fmt.Errorf("failed to get pool2 token1 vault balance after tx: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
userBase := getAccountBalanceAfterTx(tx.rawTx, pool2UserBase)
|
userBase := getAccountBalanceAfterTx(tx.rawTx, pool2UserBase)
|
||||||
@@ -1105,8 +1105,8 @@ func orcaWhirPoolTwoHopSwapV2Parser(tx *Tx, instruction Instruction, innerInstru
|
|||||||
//pool2UserBase := instruction.Accounts[8]
|
//pool2UserBase := instruction.Accounts[8]
|
||||||
pool2VaultBase := instruction.Accounts[11]
|
pool2VaultBase := instruction.Accounts[11]
|
||||||
|
|
||||||
pool2UserQuote := instruction.Accounts[12]
|
pool2VaultQuote := instruction.Accounts[12]
|
||||||
pool2VaultQuote := instruction.Accounts[13]
|
pool2UserQuote := instruction.Accounts[13]
|
||||||
|
|
||||||
swaps := make([]Swap, 2)
|
swaps := make([]Swap, 2)
|
||||||
{
|
{
|
||||||
@@ -1186,11 +1186,11 @@ func orcaWhirPoolTwoHopSwapV2Parser(tx *Tx, instruction Instruction, innerInstru
|
|||||||
{
|
{
|
||||||
baseTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, pool2VaultBase)
|
baseTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, pool2VaultBase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, increaseOffset(offset), fmt.Errorf("failed to get pool1 token0 vault balance after tx: %v", err)
|
return nil, increaseOffset(offset), fmt.Errorf("failed to get pool2 token0 vault balance after tx: %v", err)
|
||||||
}
|
}
|
||||||
quoteTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, pool2VaultQuote)
|
quoteTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, pool2VaultQuote)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, increaseOffset(offset), fmt.Errorf("failed to get pool1 token1 vault balance after tx: %v", err)
|
return nil, increaseOffset(offset), fmt.Errorf("failed to get pool2 token1 vault balance after tx: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
userBase := decimal.Zero
|
userBase := decimal.Zero
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ func (tx *Tx) Parser() error {
|
|||||||
tx.BlockAt = tx.rawTx.BlockTime
|
tx.BlockAt = tx.rawTx.BlockTime
|
||||||
tx.CuFee = decimal.NewFromUint64(tx.rawTx.Meta.Fee)
|
tx.CuFee = decimal.NewFromUint64(tx.rawTx.Meta.Fee)
|
||||||
|
|
||||||
|
tx.ComputeUnitsConsumed = tx.rawTx.Meta.ComputeUnitsConsumed
|
||||||
tx.BeforeSolBalance = decimal.NewFromUint64(tx.rawTx.Meta.PreBalances[0]).Div(decimal.NewFromInt(1e9))
|
tx.BeforeSolBalance = decimal.NewFromUint64(tx.rawTx.Meta.PreBalances[0]).Div(decimal.NewFromInt(1e9))
|
||||||
tx.AfterSOLBalance = decimal.NewFromUint64(tx.rawTx.Meta.PostBalances[0]).Div(decimal.NewFromInt(1e9))
|
tx.AfterSOLBalance = decimal.NewFromUint64(tx.rawTx.Meta.PostBalances[0]).Div(decimal.NewFromInt(1e9))
|
||||||
|
|
||||||
@@ -176,8 +177,13 @@ func (tx *Tx) Parser() error {
|
|||||||
tx.Swaps = append(tx.Swaps, swap)
|
tx.Swaps = append(tx.Swaps, swap)
|
||||||
}
|
}
|
||||||
// tx.Swaps = append(tx.Swaps, swaps...)
|
// tx.Swaps = append(tx.Swaps, swaps...)
|
||||||
|
if ii == int(offset[0]) && j == int(offset[1]) {
|
||||||
|
j = j + 1
|
||||||
|
} else {
|
||||||
j = int(offset[1])
|
j = int(offset[1])
|
||||||
ii = int(offset[0])
|
ii = int(offset[0])
|
||||||
|
}
|
||||||
|
|
||||||
} else if p, exists := actionPrograms[innerProgramAccount]; exists {
|
} else if p, exists := actionPrograms[innerProgramAccount]; exists {
|
||||||
offset, err := p(tx, innerInstr, innersMap[i], [2]uint{uint(i), uint(j)})
|
offset, err := p(tx, innerInstr, innersMap[i], [2]uint{uint(i), uint(j)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
4
pump.go
4
pump.go
@@ -79,6 +79,7 @@ type PumpCreateEvent struct {
|
|||||||
TokenTotalSupply uint64
|
TokenTotalSupply uint64
|
||||||
TokenProgram solana.PublicKey
|
TokenProgram solana.PublicKey
|
||||||
IsMayhemMode bool
|
IsMayhemMode bool
|
||||||
|
IsCashbackEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateParser(tx *Tx, instr Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
func CreateParser(tx *Tx, instr Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||||
@@ -148,6 +149,7 @@ func CreateParser(tx *Tx, instr Instruction, innerInstructions InnerInstructions
|
|||||||
BaseReserve: decimal.NewFromUint64(createEvent.RealTokenReserves),
|
BaseReserve: decimal.NewFromUint64(createEvent.RealTokenReserves),
|
||||||
QuoteReserve: decimal.Zero,
|
QuoteReserve: decimal.Zero,
|
||||||
Mayhem: createEvent.IsMayhemMode,
|
Mayhem: createEvent.IsMayhemMode,
|
||||||
|
Cashback: createEvent.IsCashbackEnabled,
|
||||||
UserBaseBalance: userBase,
|
UserBaseBalance: userBase,
|
||||||
UserQuoteBalance: decimal.NewFromUint64(userQuote),
|
UserQuoteBalance: decimal.NewFromUint64(userQuote),
|
||||||
EntryContract: entryContract,
|
EntryContract: entryContract,
|
||||||
@@ -348,6 +350,8 @@ func BuyOrSellParser(tx *Tx, instruction Instruction, innerInstructions InnerIns
|
|||||||
BaseMintDecimals: 6,
|
BaseMintDecimals: 6,
|
||||||
QuoteMintDecimals: 9,
|
QuoteMintDecimals: 9,
|
||||||
User: user,
|
User: user,
|
||||||
|
BaseReserve: decimal.NewFromUint64(tradeEvent.RealTokenReserves),
|
||||||
|
QuoteReserve: decimal.NewFromUint64(tradeEvent.RealSolReserves),
|
||||||
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[1]]),
|
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[1]]),
|
||||||
UserBaseBalance: userBase,
|
UserBaseBalance: userBase,
|
||||||
UserQuoteBalance: decimal.NewFromUint64(userQuote),
|
UserQuoteBalance: decimal.NewFromUint64(userQuote),
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ type ammBuyEvent struct {
|
|||||||
LastUpdateTimestamp int64
|
LastUpdateTimestamp int64
|
||||||
MinBaseAmountOut uint64
|
MinBaseAmountOut uint64
|
||||||
IxName string
|
IxName string
|
||||||
|
CashbackFeeBasisPoints uint64
|
||||||
|
Cashback uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type ammCreatePoolEvent struct {
|
type ammCreatePoolEvent struct {
|
||||||
@@ -113,6 +115,8 @@ type ammSellEvent struct {
|
|||||||
CoinCreator solana.PublicKey
|
CoinCreator solana.PublicKey
|
||||||
CoinCreatorFeeBasisPoints uint64
|
CoinCreatorFeeBasisPoints uint64
|
||||||
CoinCreatorFee uint64
|
CoinCreatorFee uint64
|
||||||
|
CashbackFeeBasisPoints uint64
|
||||||
|
Cashback uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type ammWithdrawEvent struct {
|
type ammWithdrawEvent struct {
|
||||||
@@ -329,6 +333,7 @@ func ammBuyParser(tx *Tx, instruction Instruction, innerInstructions InnerInstru
|
|||||||
userBalance, _ := GetSolAfterTx(result, userIndex)
|
userBalance, _ := GetSolAfterTx(result, userIndex)
|
||||||
userQuote = userQuote.Add(decimal.NewFromUint64(userBalance))
|
userQuote = userQuote.Add(decimal.NewFromUint64(userBalance))
|
||||||
}
|
}
|
||||||
|
isCashbackCoin := event.CashbackFeeBasisPoints > 0 || event.Cashback > 0
|
||||||
return []Swap{
|
return []Swap{
|
||||||
{
|
{
|
||||||
Program: SolProgramPumpAMM,
|
Program: SolProgramPumpAMM,
|
||||||
@@ -347,6 +352,7 @@ func ammBuyParser(tx *Tx, instruction Instruction, innerInstructions InnerInstru
|
|||||||
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserve - event.BaseAmountOut),
|
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserve - event.BaseAmountOut),
|
||||||
QuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserve + event.QuoteAmountIn),
|
QuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserve + event.QuoteAmountIn),
|
||||||
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
|
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
|
||||||
|
Cashback: isCashbackCoin,
|
||||||
UserBaseBalance: userBase,
|
UserBaseBalance: userBase,
|
||||||
UserQuoteBalance: userQuote,
|
UserQuoteBalance: userQuote,
|
||||||
EntryContract: entryContract,
|
EntryContract: entryContract,
|
||||||
@@ -448,6 +454,7 @@ func ammSellParser(tx *Tx, instruction Instruction, innerInstructions InnerInstr
|
|||||||
userBalance, _ := GetSolAfterTx(result, userIndex)
|
userBalance, _ := GetSolAfterTx(result, userIndex)
|
||||||
userQuote = userQuote.Add(decimal.NewFromUint64(userBalance))
|
userQuote = userQuote.Add(decimal.NewFromUint64(userBalance))
|
||||||
}
|
}
|
||||||
|
isCashbackCoin := event.CashbackFeeBasisPoints > 0 || event.Cashback > 0
|
||||||
return []Swap{
|
return []Swap{
|
||||||
{
|
{
|
||||||
Program: SolProgramPumpAMM,
|
Program: SolProgramPumpAMM,
|
||||||
@@ -466,6 +473,7 @@ func ammSellParser(tx *Tx, instruction Instruction, innerInstructions InnerInstr
|
|||||||
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserves + event.BaseAmountIn),
|
BaseReserve: decimal.NewFromUint64(event.PoolBaseTokenReserves + event.BaseAmountIn),
|
||||||
QuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserves - event.QuoteAmountOut),
|
QuoteReserve: decimal.NewFromUint64(event.PoolQuoteTokenReserves - event.QuoteAmountOut),
|
||||||
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
|
Mayhem: isMayhemPump(result.accountList[instruction.Accounts[9]]),
|
||||||
|
Cashback: isCashbackCoin,
|
||||||
UserBaseBalance: userBase,
|
UserBaseBalance: userBase,
|
||||||
UserQuoteBalance: userQuote,
|
UserQuoteBalance: userQuote,
|
||||||
EntryContract: entryContract,
|
EntryContract: entryContract,
|
||||||
|
|||||||
8
rawtx.go
8
rawtx.go
@@ -156,6 +156,7 @@ type Meta struct {
|
|||||||
PreBalances []uint64 `json:"preBalances"`
|
PreBalances []uint64 `json:"preBalances"`
|
||||||
PreTokenBalances []TokenBalance `json:"preTokenBalances"`
|
PreTokenBalances []TokenBalance `json:"preTokenBalances"`
|
||||||
Rewards []interface{} `json:"rewards"`
|
Rewards []interface{} `json:"rewards"`
|
||||||
|
ComputeUnitsConsumed uint64 `json:"computeUnitsConsumed"`
|
||||||
}
|
}
|
||||||
type Header struct {
|
type Header struct {
|
||||||
NumReadonlySignedAccounts int `json:"numReadonlySignedAccounts"`
|
NumReadonlySignedAccounts int `json:"numReadonlySignedAccounts"`
|
||||||
@@ -325,7 +326,9 @@ func FromRpcTransactionWithMeta(tx rpc.TransactionWithMeta, blockTime *uint64, s
|
|||||||
}
|
}
|
||||||
sTx.Meta.Fee = meta.Fee
|
sTx.Meta.Fee = meta.Fee
|
||||||
//sTx.Meta.InnerInstructions = meta.InnerInstructions
|
//sTx.Meta.InnerInstructions = meta.InnerInstructions
|
||||||
|
if meta.ComputeUnitsConsumed != nil {
|
||||||
|
sTx.Meta.ComputeUnitsConsumed = *meta.ComputeUnitsConsumed
|
||||||
|
}
|
||||||
for _, innerInstr := range meta.InnerInstructions {
|
for _, innerInstr := range meta.InnerInstructions {
|
||||||
var instrs []Instruction
|
var instrs []Instruction
|
||||||
for _, instr := range innerInstr.Instructions {
|
for _, instr := range innerInstr.Instructions {
|
||||||
@@ -513,6 +516,7 @@ func intSliceFromUint16Slice(in []uint16) []int {
|
|||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAtaIdxByOwner(result *RawTx, owner solana.PublicKey, mint solana.PublicKey) (int, error) {
|
func getAtaIdxByOwner(result *RawTx, owner solana.PublicKey, mint solana.PublicKey) (int, error) {
|
||||||
var preBalance *TokenBalance
|
var preBalance *TokenBalance
|
||||||
for _, pre := range result.Meta.PreTokenBalances {
|
for _, pre := range result.Meta.PreTokenBalances {
|
||||||
@@ -805,7 +809,7 @@ func ConvertYellowstoneGrpcTransactionToSolanaTransaction(y *pb.SubscribeUpdateT
|
|||||||
}
|
}
|
||||||
sTx.Meta.Fee = meta.Fee
|
sTx.Meta.Fee = meta.Fee
|
||||||
//sTx.Meta.InnerInstructions = meta.InnerInstructions
|
//sTx.Meta.InnerInstructions = meta.InnerInstructions
|
||||||
|
sTx.Meta.ComputeUnitsConsumed = *meta.ComputeUnitsConsumed
|
||||||
for _, innerInstr := range meta.InnerInstructions {
|
for _, innerInstr := range meta.InnerInstructions {
|
||||||
var instrs []Instruction
|
var instrs []Instruction
|
||||||
for _, instr := range innerInstr.Instructions {
|
for _, instr := range innerInstr.Instructions {
|
||||||
|
|||||||
@@ -54,12 +54,12 @@ func raydiumCPmmCreatePoolParser(tx *Tx, instruction Instruction, innerInstructi
|
|||||||
vault1 = instruction.Accounts[12]
|
vault1 = instruction.Accounts[12]
|
||||||
}
|
}
|
||||||
|
|
||||||
baseTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, instruction.Accounts[vault0])
|
baseTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, vault0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, increaseOffset(offset), fmt.Errorf("failed to get token0 vault balance after tx: %v", err)
|
return nil, increaseOffset(offset), fmt.Errorf("failed to get token0 vault balance after tx: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
quoteTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, instruction.Accounts[vault1])
|
quoteTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, vault1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, increaseOffset(offset), fmt.Errorf("failed to get token1 vault balance after tx: %v", err)
|
return nil, increaseOffset(offset), fmt.Errorf("failed to get token1 vault balance after tx: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ type RaydiumLaunchLabCreateEvent struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func raydiumLaunchLabInitializeParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
func raydiumLaunchLabInitializeParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||||
if len(instruction.Accounts) < 16 {
|
if len(instruction.Accounts) < 15 {
|
||||||
return nil, increaseOffset(offset), fmt.Errorf("not enough accounts for initialize instruction")
|
return nil, increaseOffset(offset), fmt.Errorf("not enough accounts for initialize instruction")
|
||||||
}
|
}
|
||||||
user := tx.rawTx.accountList[instruction.Accounts[0]]
|
user := tx.rawTx.accountList[instruction.Accounts[0]]
|
||||||
|
|||||||
6
tx.go
6
tx.go
@@ -31,6 +31,7 @@ type Swap struct {
|
|||||||
BaseReserve decimal.Decimal
|
BaseReserve decimal.Decimal
|
||||||
QuoteReserve decimal.Decimal
|
QuoteReserve decimal.Decimal
|
||||||
Mayhem bool
|
Mayhem bool
|
||||||
|
Cashback bool
|
||||||
|
|
||||||
UserBaseBalance decimal.Decimal
|
UserBaseBalance decimal.Decimal
|
||||||
UserQuoteBalance decimal.Decimal
|
UserQuoteBalance decimal.Decimal
|
||||||
@@ -47,6 +48,8 @@ type Swap struct {
|
|||||||
StartBinId int32
|
StartBinId int32
|
||||||
EndBinId int32
|
EndBinId int32
|
||||||
BinChanges []DlmmBinLiquidityChange
|
BinChanges []DlmmBinLiquidityChange
|
||||||
|
|
||||||
|
ConsumeUnit uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type DlmmBinLiquidityChange struct {
|
type DlmmBinLiquidityChange struct {
|
||||||
@@ -98,6 +101,9 @@ type Tx struct {
|
|||||||
// update tokenInfo
|
// update tokenInfo
|
||||||
Token map[solana.PublicKey]TokenMeta `gorm:"-"`
|
Token map[solana.PublicKey]TokenMeta `gorm:"-"`
|
||||||
|
|
||||||
|
ComputeUnitsConsumed uint64 `json:"compute_units_consumed"`
|
||||||
|
CuLimit uint32 `json:"cu_limit"`
|
||||||
|
|
||||||
// todo pool info ??
|
// todo pool info ??
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user