192 lines
5.4 KiB
Go
192 lines
5.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/gagliardetto/solana-go"
|
|
addresslookuptable "github.com/gagliardetto/solana-go/programs/address-lookup-table"
|
|
"github.com/gagliardetto/solana-go/rpc"
|
|
|
|
"github.com/samlior/libsam/v2/pkg/shreder"
|
|
)
|
|
|
|
const (
|
|
rpcURL = "https://staked.helius-rpc.com?api-key=5adcf1f9-5719-43d1-bf3f-c2d4e1e5f94d"
|
|
txSignature = "4oSnHnDSscjmc6XX1rjXCEBavoLR9wkdZvGCAUn928iLWqrCwt2a6mgJpjP4NHqrCboUC82ugrjjEbNGNYAagkue"
|
|
labelFilter = ""
|
|
)
|
|
|
|
func main() {
|
|
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")
|
|
}
|
|
|
|
client := rpc.New(rpcURL)
|
|
sig, err := solana.SignatureFromBase58(txSignature)
|
|
if err != nil {
|
|
log.Fatalf("invalid txSignature: %v", err)
|
|
}
|
|
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)
|
|
signals := parseSignals(context.Background(), update)
|
|
if len(signals) == 0 {
|
|
fmt.Println("no signals parsed")
|
|
return
|
|
}
|
|
|
|
printed := false
|
|
for _, signal := range signals {
|
|
if labelFilter != "" && signal.Label != labelFilter {
|
|
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
|
|
}
|
|
|
|
if labelFilter != "" {
|
|
fmt.Printf("no %s signal parsed, dump all signals:\n", labelFilter)
|
|
} else {
|
|
fmt.Println("no matching signal parsed, dump all signals:")
|
|
}
|
|
for _, signal := range signals {
|
|
output, err := json.MarshalIndent(signal, "", " ")
|
|
if err != nil {
|
|
log.Fatalf("marshal signal failed: %v", err)
|
|
}
|
|
fmt.Println(string(output))
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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,
|
|
}
|
|
}
|