Created
October 24, 2024 16:23
-
-
Save melardev/73880368497fd05f16428dbb7cbbdc4e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "context" | |
| "fmt" | |
| bin "github.com/gagliardetto/binary" | |
| tokenMetadata "github.com/gagliardetto/metaplex-go/clients/token-metadata" | |
| "github.com/gagliardetto/solana-go" | |
| "github.com/gagliardetto/solana-go/rpc" | |
| "github.com/gagliardetto/solana-go/rpc/ws" | |
| "log" | |
| "strings" | |
| "testing" | |
| "time" | |
| ) | |
| var ( | |
| metadataProgramID = solana.MustPublicKeyFromBase58("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s") // METADATA_PROGRAM_ID | |
| pumpFunRaydiumMigrationProgramId = solana.MustPublicKeyFromBase58("39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg") | |
| pumpFunProgramId = solana.MustPublicKeyFromBase58("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P") | |
| ) | |
| func getMetadataPDA(mint solana.PublicKey) (solana.PublicKey, error) { | |
| seeds := [][]byte{ | |
| []byte("metadata"), | |
| metadataProgramID.Bytes(), | |
| mint.Bytes(), | |
| } | |
| pda, _, err := solana.FindProgramAddress(seeds, metadataProgramID) | |
| if err != nil { | |
| return solana.PublicKey{}, fmt.Errorf("failed to find program address: %w", err) | |
| } | |
| return pda, nil | |
| } | |
| func getTokenMetadata(tokenAddr solana.PublicKey) (tokenMetadata.Metadata, error) { | |
| ctx := context.Background() | |
| client := rpc.New(rpc.MainNetBeta_RPC) | |
| metadataPDA, err := getMetadataPDA(tokenAddr) | |
| if err != nil { | |
| return tokenMetadata.Metadata{}, err | |
| } | |
| accountInfo, err := client.GetAccountInfo(ctx, metadataPDA) | |
| if err != nil { | |
| return tokenMetadata.Metadata{}, fmt.Errorf("failed to get account info: %w", err) | |
| } | |
| if accountInfo == nil || accountInfo.Value == nil { | |
| return tokenMetadata.Metadata{}, fmt.Errorf("account not found") | |
| } | |
| dec := bin.NewBorshDecoder(accountInfo.Value.Data.GetBinary()) | |
| metadata := tokenMetadata.Metadata{} | |
| // err = dec.Decode(&metadata) | |
| // Or | |
| err = metadata.UnmarshalWithDecoder(dec) | |
| if err != nil { | |
| return tokenMetadata.Metadata{}, fmt.Errorf("failed to unmarshal metadata: %w", err) | |
| } | |
| return metadata, nil | |
| } | |
| func main() { | |
| rpcClient := rpc.New(rpc.MainNetBeta.RPC) | |
| wsClient, err := ws.Connect(context.Background(), rpc.MainNetBeta.WS) | |
| if err != nil { | |
| panic(err) | |
| } | |
| sub, err := wsClient.LogsSubscribeMentions(pumpFunRaydiumMigrationProgramId, rpc.CommitmentConfirmed) | |
| if err != nil { | |
| panic(err) | |
| } | |
| defer sub.Unsubscribe() | |
| var tokensMigrated []int | |
| var totalTokens int32 = 0 | |
| var tokenMigrationsCh = make(chan struct{}) | |
| // Counter | |
| go func() { | |
| ticker := time.NewTicker(time.Minute) | |
| defer ticker.Stop() | |
| for { | |
| select { | |
| case <-ticker.C: | |
| tokensMigrated = append(tokensMigrated, int(totalTokens)) | |
| // Rotate every 1000 entries | |
| if len(tokensMigrated) > 1000 { | |
| tokensMigrated = tokensMigrated[1:] | |
| } | |
| // Print total number of tokens created so far | |
| sum := 0 | |
| for _, count := range tokensMigrated { | |
| sum += count | |
| } | |
| fmt.Printf("Total tokens migrated, last minute: %d, total: %d\n", totalTokens, sum) | |
| // Reset token count for the next minute | |
| totalTokens = 0 | |
| case <-tokenMigrationsCh: | |
| totalTokens++ | |
| } | |
| } | |
| }() | |
| for { | |
| msg, err := sub.Recv() | |
| if err != nil { | |
| log.Printf("Error receiving log: %v\n", err) | |
| continue | |
| } | |
| for _, logEntry := range msg.Value.Logs { | |
| if !strings.Contains(logEntry, "Program log: Instruction: Withdraw") { | |
| continue | |
| } | |
| tokenMigrationsCh <- struct{}{} // Signal token creation | |
| fmt.Printf("Migration found at https://solscan.io/tx/%s\n", msg.Value.Signature) | |
| zero := uint64(0) | |
| transactionDetails, err := rpcClient.GetParsedTransaction( | |
| context.TODO(), | |
| msg.Value.Signature, | |
| &rpc.GetParsedTransactionOpts{ | |
| Commitment: rpc.CommitmentConfirmed, | |
| MaxSupportedTransactionVersion: &zero, | |
| }, | |
| ) | |
| if err != nil { | |
| log.Printf("GET_PARSED_TRANSACTIONS_ERROR: %v\n", err) | |
| continue | |
| } | |
| if transactionDetails != nil { | |
| tx := transactionDetails | |
| // accountKeys := tx.Transaction.Message.AccountKeys | |
| instructions := tx.Transaction.Message.Instructions | |
| for _, ix := range instructions { | |
| if ix.ProgramId.String() == pumpFunProgramId.String() { | |
| if len(ix.Accounts) >= 5 { | |
| tokenMintAddress := ix.Accounts[2].String() | |
| // bondingCurveAddress := ix.Accounts[4].String() | |
| metadata, err := getTokenMetadata(solana.MustPublicKeyFromBase58(tokenMintAddress)) | |
| if err != nil { | |
| fmt.Printf("Token migrated %s, but error getting token metadata: %v\n", | |
| tokenMintAddress, err) | |
| } else { | |
| name := strings.ReplaceAll(metadata.Data.Name, "\x00", "") | |
| symbol := strings.ReplaceAll(metadata.Data.Symbol, "\x00", "") | |
| fmt.Printf("Token %s %s (%s)\n", | |
| tokenMintAddress, name, symbol) | |
| } | |
| } else { | |
| log.Println("Unexpected instruction format") | |
| } | |
| } | |
| } | |
| } | |
| break | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment