Files

192 lines
5.4 KiB
Go
Raw Permalink Normal View History

2026-01-22 14:32:45 +08:00
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"github.com/gagliardetto/solana-go"
addresslookuptable "github.com/gagliardetto/solana-go/programs/address-lookup-table"
2026-01-22 14:32:45 +08:00
"github.com/gagliardetto/solana-go/rpc"
2026-01-28 15:24:43 +08:00
"github.com/samlior/libsam/v2/pkg/shreder"
2026-01-22 14:32:45 +08:00
)
2026-01-22 17:10:13 +08:00
const (
2026-01-28 15:24:43 +08:00
rpcURL = "https://staked.helius-rpc.com?api-key=5adcf1f9-5719-43d1-bf3f-c2d4e1e5f94d"
2026-03-30 11:59:42 +08:00
txSignature = "4oSnHnDSscjmc6XX1rjXCEBavoLR9wkdZvGCAUn928iLWqrCwt2a6mgJpjP4NHqrCboUC82ugrjjEbNGNYAagkue"
2026-01-28 15:24:43 +08:00
labelFilter = ""
2026-01-22 17:10:13 +08:00
)
2026-01-22 14:32:45 +08:00
func main() {
2026-01-22 17:10:13 +08:00
if rpcURL == "" || rpcURL == "REPLACE_WITH_RPC_URL" {
log.Fatal("rpcURL is not set in cmd/dlmmparse/main.go")
}
if txSignature == "" || txSignature == "REPLACE_WITH_TX_SIGNATURE" {
log.Fatal("txSignature is not set in cmd/dlmmparse/main.go")
2026-01-22 14:32:45 +08:00
}
client := rpc.New(rpcURL)
2026-01-22 17:10:13 +08:00
sig, err := solana.SignatureFromBase58(txSignature)
2026-01-22 14:32:45 +08:00
if err != nil {
2026-01-22 17:10:13 +08:00
log.Fatalf("invalid txSignature: %v", err)
2026-01-22 14:32:45 +08:00
}
version := uint64(0)
tx, err := client.GetTransaction(
context.Background(),
sig,
&rpc.GetTransactionOpts{
Commitment: rpc.CommitmentFinalized,
MaxSupportedTransactionVersion: &version,
},
)
if err != nil {
log.Fatalf("getTransaction failed: %v", err)
}
if tx == nil || tx.Transaction == nil {
log.Fatal("transaction is empty")
}
rawTx, err := tx.Transaction.GetTransaction()
if err != nil {
log.Fatalf("decode transaction failed: %v", err)
}
if rawTx == nil {
log.Fatal("decoded transaction is nil")
}
if len(rawTx.Message.AddressTableLookups) > 0 {
tables := make(map[solana.PublicKey]solana.PublicKeySlice, len(rawTx.Message.AddressTableLookups))
for _, lookup := range rawTx.Message.AddressTableLookups {
state, err := addresslookuptable.GetAddressLookupTable(context.Background(), client, lookup.AccountKey)
if err != nil {
log.Fatalf("load address table %s failed: %v", lookup.AccountKey, err)
}
tables[lookup.AccountKey] = state.Addresses
}
if err := rawTx.Message.SetAddressTables(tables); err != nil {
log.Fatalf("set address tables failed: %v", err)
}
if err := rawTx.Message.ResolveLookups(); err != nil {
log.Fatalf("resolve address lookups failed: %v", err)
}
}
update := toSubscribeUpdate(tx.Slot, rawTx)
2026-01-28 15:24:43 +08:00
signals := parseSignals(context.Background(), update)
2026-01-22 14:32:45 +08:00
if len(signals) == 0 {
fmt.Println("no signals parsed")
return
}
printed := false
for _, signal := range signals {
2026-01-22 17:10:13 +08:00
if labelFilter != "" && signal.Label != labelFilter {
2026-01-22 14:32:45 +08:00
continue
}
printed = true
output, err := json.MarshalIndent(signal, "", " ")
if err != nil {
log.Fatalf("marshal signal failed: %v", err)
}
fmt.Println(string(output))
}
if printed {
return
}
2026-01-22 17:10:13 +08:00
if labelFilter != "" {
fmt.Printf("no %s signal parsed, dump all signals:\n", labelFilter)
} else {
fmt.Println("no matching signal parsed, dump all signals:")
}
2026-01-22 14:32:45 +08:00
for _, signal := range signals {
output, err := json.MarshalIndent(signal, "", " ")
if err != nil {
log.Fatalf("marshal signal failed: %v", err)
}
fmt.Println(string(output))
}
}
2026-01-28 15:24:43 +08:00
func parseSignals(ctx context.Context, update *shreder.SubscribeUpdateTransaction) []shreder.TxSignal {
signalsCh := make(chan shreder.TxSignal, 64)
done := make(chan struct{})
go func() {
shreder.ParseTransactionForSubscribe(ctx, update, nil, signalsCh, done)
}()
go func() {
<-done
close(signalsCh)
}()
signals := make([]shreder.TxSignal, 0)
for signal := range signalsCh {
signals = append(signals, signal)
}
return signals
}
2026-01-22 14:32:45 +08:00
func toSubscribeUpdate(slot uint64, tx *solana.Transaction) *shreder.SubscribeUpdateTransaction {
signatures := make([][]byte, len(tx.Signatures))
for i, sig := range tx.Signatures {
signatures[i] = sig[:]
}
accountKeys := make([][]byte, len(tx.Message.AccountKeys))
for i, key := range tx.Message.AccountKeys {
accountKeys[i] = key[:]
}
instructions := make([]*shreder.CompiledInstruction, len(tx.Message.Instructions))
for i, instr := range tx.Message.Instructions {
accounts := make([]byte, len(instr.Accounts))
for j, acc := range instr.Accounts {
accounts[j] = byte(acc)
}
instructions[i] = &shreder.CompiledInstruction{
ProgramIdIndex: uint32(instr.ProgramIDIndex),
Accounts: accounts,
Data: instr.Data[:],
}
}
addressTableLookups := make([]*shreder.MessageAddressTableLookup, len(tx.Message.AddressTableLookups))
for i, lookup := range tx.Message.AddressTableLookups {
writable := make([]byte, len(lookup.WritableIndexes))
for j, idx := range lookup.WritableIndexes {
writable[j] = byte(idx)
}
readonly := make([]byte, len(lookup.ReadonlyIndexes))
for j, idx := range lookup.ReadonlyIndexes {
readonly[j] = byte(idx)
}
addressTableLookups[i] = &shreder.MessageAddressTableLookup{
AccountKey: lookup.AccountKey[:],
WritableIndexes: writable,
ReadonlyIndexes: readonly,
}
}
return &shreder.SubscribeUpdateTransaction{
Transaction: &shreder.Transaction{
Signatures: signatures,
Message: &shreder.Message{
Header: &shreder.MessageHeader{
NumRequiredSignatures: uint32(tx.Message.Header.NumRequiredSignatures),
NumReadonlySignedAccounts: uint32(tx.Message.Header.NumReadonlySignedAccounts),
NumReadonlyUnsignedAccounts: uint32(tx.Message.Header.NumReadonlyUnsignedAccounts),
},
AccountKeys: accountKeys,
RecentBlockhash: nil,
Instructions: instructions,
Versioned: false,
AddressTableLookups: addressTableLookups,
},
},
Slot: slot,
}
}