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 = "PDynKmdiRG61ZZQoTSmdoTvra2Q8MG91YRdce7vo7EhpoYwr5WjJLbEuJbocPunk2zb4ZCZjXSGNU5bXwJRL7Gu" 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, } }