読了時間:17分
概要
特定のSolanaプログラムのトランザクションを監視することは、開発、分析、あるいは運用上の目的において貴重な知見をもたらします。リアルタイム監視のためにプログラムデータにアクセスするのは困難な場合があり、エンコードされたプログラムデータを解析して意味を把握するのはさらに難しい場合があります。このガイドでは、Solana gRPC(Yellowstone互換のGeyser gRPC)と、データ解析に用いる軽量なインデックス作成フレームワークであるCarbonを使用して、Pump.fun向けのトランザクションモニターを構築します。
主な業務内容
このガイドを読み終える頃には、次のような機能を備えたRustアプリケーションが完成しているはずです:
- Solana gRPC および Carbon インデックス作成フレームワークを活用しています
- SolanaのgRPCエンドポイントに接続します
- Pump.funのフィルターとストリームを活用して、新しいトークンの発行やトークンのAMM移行をリアルタイムで監視する
- 対象となるイベントに関する主要な情報を処理し、記録する
必要なもの
- お使いのシステムにRustとCargoがインストールされています
- Solana gRPC アクセス権限付きのQuicknode アカウント(Scale プランおよび Business プランには含まれており、Build プランおよび Accelerate プランではSolana gRPC アドオンを通じて利用可能です)
- SolanaおよびRustプログラミングに関する基礎的な知識
- コマンドラインインターフェースの知識
| 依存関係 | バージョン |
|---|---|
| rustc | 1.85.0 |
| 貨物 | 1.85.0 |
| tokio | 1.43.0 |
| yellowstone-grpc-proto | 5.0.0 |
| ログ | 0.4.25 |
| env_logger | 0.11.5 |
| カーボンコア | 0.8.0 |
| carbon-yellowstone-grpc-データソース | 0.8.0 |
| カーボン・ポンプファン・デコーダー | 0.8.0 |
| async-trait | 0.1.86 |
| dotenv | 0.15.0 |
ツールの理解
Solana gRPCとは何ですか?
Solana gRPCは、Geyserプラグインシステムを基盤として構築された、Solana向けの高性能データストリーミングソリューションです。以下の機能を提供します:
- 口座の更新情報、取引履歴、およびスロット通知のリアルタイム配信
- 従来のWebSocket実装に比べてレイテンシが低い
- gRPCによる強力な型安全性
- サブスクリプション向けの効率的なフィルタリング機能
Pump.funとは何ですか?
Pump.funは、Solana上で人気のトークン作成プラットフォームであり、誰でもミームトークンを迅速かつ簡単に作成することができます。Pump.funの取引を監視することで、新しいトークンのリリース、市場の動向、ユーザーの活動に関する洞察を得ることができます。
炭素とは何ですか?
Carbonは、Solana向けの軽量なインデックス作成フレームワークであり、オンチェーンデータの取得と処理のプロセスを簡素化します。このフレームワークは、以下の主要コンポーネントからなるモジュール式のパイプラインアーキテクチャを提供します:
- パイプライン:データフローを管理する中核となるオーケストレーター
- データソース:データの更新を提供するコンポーネント(トランザクション、勘定科目)
- パイプ:デコーダおよびプロセッサを介して、プロセス固有の更新を処理する
- デコーダ:ブロックチェーンの生データを構造化された型に変換する
- プロセッサ:デコードされたデータに対してカスタムロジックを実装する
Carbonは、ブロックチェーンデータのストリーミング、デコード、処理といった複雑な詳細処理を代行するため、開発者はアプリケーションロジックの構築に集中することができます。本稿執筆時点で、Carbonでは以下のプログラムデコーダーを提供しています:
利用可能なプログラムデコーダ
| クレートの名前 | 説明 |
|---|---|
カーボン関連トークンアカウントデコーダー | 関連トークンアカウントデコーダ |
carbon-drift-v2-decoder | Drift V2 プログラムデコーダー |
カーボン・フラックスビーム・デコーダー | Fluxbeam プログラムデコーダー |
carbon-jupiter-dca-デコーダー | Jupiter DCAプログラムデコーダー |
カーボン・ジュピター・リミット・オーダー・デコーダー | ジュピター指値注文プログラム・デコーダー |
carbon-jupiter-limit-order-2-デコーダ | Jupiter Limit Order 2 プログラムデコーダー |
カーボン・ジュピター・パーペチュアルズ・デコーダー | ジュピター・パーペチュアルズ・プログラム・デコーダー |
carbon-jupiter-swap-decoder | ジュピター・スワップ・プログラム・デコーダー |
カーボン・カミノ・ファームズ・デコーダー | カミノ・ファームズ・プログラム・デコーダー |
カーボン・カミノ・レンディング・デコーダー | カミノ・レンド・デコーダー |
カーボン・カミノ・リミット・オーダー・デコーダー | カミノ指値注文プログラムデコーダー |
カーボン・カミノ・ヴォールト・デコーダー | カミノ・ヴォルト・デコーダー |
carbon-lifinity-amm-v2-デコーダー | Lifinity AMM V2 プログラムデコーダー |
carbon-marginfi-v2-decoder | Marginfi V2 プログラムデコーダー |
カーボン・マリネード・ファイナンス・デコーダー | マリネード・ファイナンス・プログラム解説 |
carbon-memo-program-decoder | SPLメモプログラムデコーダー |
carbon-meteora-dlmm-decoder | メテオラ DLMM プログラムデコーダ |
carbon-meteora-pools-decoder | 「メテオラ・プールズ」プログラム解説 |
カーボン・ムーンショット・デコーダー | ムーンショット・プログラム・デコーダー |
carbon-mpl-core-decoder | MPLコアプログラムデコーダ |
carbon-mpl-token-metadata-decoder | MPLトークン メタデータ プログラム デコーダー |
カーボン・ネーム・サービス・デコーダー | SPL ネームサービス・プログラム・デコーダ |
carbon-okx-dex-decoder | OKX DEX デコーダー |
carbon-openbook-v2-デコーダー | Openbook V2 プログラムデコーダー |
カーボン・オルカ・ワールプール・デコーダー | オルカ・ワールプール・プログラム・デコーダー |
carbon-phoenix-v1-decoder | Phoenix V1 プログラムデコーダー |
カーボン・ポンプファン・デコーダー | Pumpfun プログラムデコーダー |
カーボンポンプ交換用デコーダー | PumpSwap プログラム デコーダー |
carbon-raydium-amm-v4-decoder | Raydium AMM V4 プログラムデコーダー |
カーボン・レイディウム・CLMM・デコーダー | Raydium CLMM プログラムデコーダ |
カーボン・レイディウム・CPMM・デコーダー | Raydium CPMM プログラムデコーダ |
カーボン・レイディウム・ローンチパッド・デコーダー | Raydium ローンチパッド・プログラム解説 |
カーボン・レイディウム・流動性・ロック・デコーダー | Raydium 流動性ロックプログラム解説 |
carbon-sharky-decoder | SharkyFi デコーダー |
カーボン・ソレイヤー・プール・リステーキング・デコーダー | Solayer プール再ステーキング・プログラム解説 |
carbon-stabble-stable-swap-decoder | Stabble Stable Swap デコーダ |
カーボン・スタブル・加重・スワップ・デコーダ | スタブル加重スワップデコーダ |
カーボン・ステーク・プログラム・デコーダー | ステーク・プログラム・デコーダー |
カーボン・システム・プログラム・デコーダー | システムプログラムデコーダ |
carbon-token-2022-decoder | 「Token 2022」プログラム解説 |
カーボン・トークン・プログラム・デコーダー | トークン・プログラム・デコーダ |
カーボン・バーチャル・カーブ・デコーダー | メテオラ・バーチャル・カーブ・プログラム・デコーダー |
carbon-virtuals-decoder | バーチャルプログラム・デコーダー |
カーボン・ゼータ・デコーダー | ゼータ・プログラム・デコーダー |
以下を確認してください カーボンに関する資料 最新情報や追加のデコーダーについては。このガイドでは、以下のものを使用します。 カーボン・ポンプファン・デコーダー Pump.fun プログラムの命令をデコードするには――このガイドを最後まで読み終えれば、他のデコーダーも同様の方法で利用できるはずです。
プロジェクトの設定
まずは、Rustプロジェクトの構造を設定することから始めましょう。
1. 新しいプロジェクトを作成する
cargo new pump-fun-carbon
cd pump-fun-carbon
2. 依存関係の設定
のコンテンツを次のように置き換えてください Cargo.toml 以下のファイルで:
[パッケージ]
名前 = "pump-fun-carbon"
バージョン = "0.1.0"
エディション = "2024"
[依存関係]
# イエローストーン & Carbon 依存関係
carbon-コア = "0.8.0"
カーボン-pumpfun-デコーダー = "0.8.0"
carbon-イエローストーン-grpc-データソース = "0.8.0"
yellowstone-grpc-proto = "5.0.0"
# 非同期 およびユーティリティ
async-トレイト = "0.1.86"
tokio = { バージョン = "1.43.0", 機能 = ["full"] }
dotenv = "0.15.0"
env_logger = "0.11.5"
log = "0.4.25"
3. 環境設定の作成
[作成] .env プロジェクトのルートディレクトリに、SolanaのgRPCエンドポイントの詳細を保存するためのファイルを作成します:
GEYSER_URL=your-quicknode-grpc-endpoint
X_TOKEN=your-quicknode-x-token
プレースホルダーの値を、実際のQuicknode Solana gRPCエンドポイントおよび認証トークンに置き換えてください。エンドポイントの設定方法については、こちらのドキュメントをご覧ください。
実装
それでは、Pump.funのトランザクションモニターを作成していきましょう。プロジェクトを初期化した際、 src ディレクトリに main.rs ファイル。このファイルにコードを追加していきます。このファイルがない場合は、作成してください。
1. 定数とインポートの作成
まずは、必要なライブラリをインポートし、アプリケーション用の定数をいくつか定義しましょう:
use {
async_trait::async_trait,
carbon_core::{
deserialize::ArrangeAccounts,
error::CarbonResult,
instruction::{DecodedInstruction, InstructionMetadata, NestedInstructions},
metrics::MetricsCollection,
processor::Processor,
},
carbon_pumpfun_decoder::{
PROGRAM_ID as PUMP_FUN_PROGRAM_ID, PumpfunDecoder,
instructions::{PumpfunInstruction, create::Create, migrate::Migrate},
},
carbon_yellowstone_grpc_datasource::YellowstoneGrpcGeyserClient,
std::{
collections::{HashMap, HashSet},
env,
sync::Arc,
},
tokio::sync::RwLock,
yellowstone_grpc_proto::geyser::{
CommitmentLevel, SubscribeRequestFilterAccounts, SubscribeRequestFilterTransactions,
},
};
// Pump.fun authority addresses to monitor
const PUMP_FUN_MINT_AUTHORITY: &str = "TSLvdd1pWpHVjahSpsvCXUbgwsL3JAcvokwaKt1eokM";
const PUMP_FUN_MIGRATION_AUTHORITY: &str = "39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg";
このコードでは、Carbon および Pump.fun デコーダーから必要なモジュールをインポートしています。また、トランザクションのフィルタリングに使用する Pump.fun のミントおよびマイグレーション・オーソリティに関する定数を定義しています。これにより、処理が必要なデータ量を削減し、関連するトランザクションに焦点を当てることができます(これについては後ほど詳しく説明します)。
2. main関数を実装する
次に、次の機能を実装しましょう。 メイン アプリケーションのエントリポイントとなる関数です。以下のコードを main.rs ファイル(コードについては後で詳しく見ていきます):
#[tokio::main]
pub async fn main() -> CarbonResult<()> {
// 1 - Initialize logging
unsafe {
std::env::set_var("RUST_LOG", "info");
}
env_logger::init();
log::info!("Starting Pumpfun Transaction Processor Using Carbon");
// 2 - Check environment variables
dotenv::dotenv().ok();
let geyser_url = match env::var("GEYSER_URL") {
Ok(url) if !url.is_empty() => url,
_ => {
log::error!("GEYSER_URL environment variable not set or empty");
return Err(carbon_core::error::Error::Custom(
"GEYSER_URL not set".into(),
));
}
};
log::info!("Using GEYSER_URL: {}", geyser_url);
let x_token = env::var("X_TOKEN").ok();
log::info!(
"X_TOKEN is {}",
if x_token.is_some() { "set" } else { "not set" }
);
// 3 - Initialize account filters
let account_filters: HashMap<String, SubscribeRequestFilterAccounts> = HashMap::new();
// 4 - Initialize transaction filter
let transaction_filter = SubscribeRequestFilterTransactions {
vote: Some(false),
failed: Some(false),
account_include: vec![
PUMP_FUN_MINT_AUTHORITY.to_string(),
PUMP_FUN_MIGRATION_AUTHORITY.to_string(),
],
account_exclude: vec![],
account_required: vec![PUMP_FUN_PROGRAM_ID.to_string()],
signature: None,
};
let mut transaction_filters: HashMap<String, SubscribeRequestFilterTransactions> =
HashMap::new();
transaction_filters.insert(
"raydium_launchpad_transaction_filter".to_string(),
transaction_filter,
);
// 5 - Initialize Yellowstone Geyser gRPC Client
let yellowstone_grpc = YellowstoneGrpcGeyserClient::new(
env::var("GEYSER_URL").unwrap_or_default(),
env::var("X_TOKEN").ok(),
Some(CommitmentLevel::Processed),
account_filters,
transaction_filters,
Arc::new(RwLock::new(HashSet::new())),
);
// 6 - Build and run the Carbon pipeline
carbon_core::pipeline::Pipeline::builder()
.datasource(yellowstone_grpc)
.instruction(PumpfunDecoder, PumpfunInstructionProcessor)
//.account(PumpfunDecoder, PumpfunAccountProcessor)
.shutdown_strategy(carbon_core::pipeline::ShutdownStrategy::Immediate)
.build()?
.run()
.await?;
Ok(())
}
このコードが何をしているのか、詳しく見ていきましょう:
-
ロギングの初期化- ログレベルを「info」に設定し、ロガーを初期化して、コンソール出力に情報メッセージが表示されるようにします。
-
環境変数の確認 - 変数は
.envファイルを作成し、必要な項目がGEYSER_URLが設定されています。また、X_TOKEN認証に使用できます。 -
アカウントフィルターの初期化- アカウントフィルター用に空のHashMapを作成します。この実装では、特定のアカウントによるフィルタリングは行いませんが、将来的な拡張(例えば、トランザクションイベントではなく、特定のLPアカウントの変更を監視したい場合など)に備えて、そのための構造を整えています。
-
トランザクション・フィルターの初期化- Pump.fun のトランザクションを具体的に対象とするフィルターを作成します:
- 投票トランザクションと失敗したトランザクションを除外する
- ミント権限または移行権限のいずれかが関与するトランザクションを含めます。このフィルタを設定しないと、Pump.fun プログラムに関するすべてのトランザクション(買いと売りを含む)が受信されてしまいます。その結果、監視の目的とは無関係なノイズが大量に発生し、最終的にはクレジットの消費量が増えてしまいます。ミント権限は新しいトークンの作成にのみ使用され、移行権限はトークンの移行にのみ使用されることがわかっているため、監視対象となるトランザクションを、私たちが注目すべき指示を含むものに限定できるようになりました。
- Pump.fun プログラム ID が存在することを必須とする
- 除外すべき特定のアカウントや照合すべき署名はありません(ただし、このフィールドを使用して、悪意のある作成者やその他の望ましくないアカウントをフィルタリングすることを検討することもできます)
-
Solana gRPC クライアントの初期化 - 設定したフィルターを使用して、SolanaのgRPCエンドポイントに接続するクライアントを作成します。ここでは、
処理済み最新のデータを確実に取得できるよう、コミットメントレベルを設定しています。この例ではアカウントプロセッサを使用していないため、コメントアウトしていますが、アカウントの変更も監視したい場合は、再度追加することができます。 -
Carbonパイプラインの構築と実行- 最後に、次のような機能を持つCarbonパイプラインを構築します:
- Solana gRPC クライアントをデータソースとして使用します
- Pump.funデコーダーを当社のプロセッサに接続します
- 即時シャットダウン戦略を設定します
- パイプラインを構築して実行する
いいですね!あとは、 PumpfunInstructionProcessor デコードされたPump.fun命令を処理するものです。
3. 命令プロセッサを実装する
あなたの下の メイン 関数、それでは PumpfunInstructionProcessor デコードされたPump.fun命令を処理するものです:
pub struct PumpfunInstructionProcessor;
#[async_trait]
impl Processor for PumpfunInstructionProcessor {
type InputType = (
InstructionMetadata,
DecodedInstruction<PumpfunInstruction>,
NestedInstructions,
);
async fn process(
&mut self,
(metadata, instruction, _nested_instructions): Self::InputType,
_metrics: Arc<MetricsCollection>,
) -> CarbonResult<()> {
let signature = metadata.transaction_metadata.signature;
let accounts = instruction.accounts;
match instruction.data {
PumpfunInstruction::Sell(_params) => {
log::info!("❌ - EXPECT TO NEVER SEE SELL INSTRUCTION");
log::info!("TxId: {}", signature);
}
PumpfunInstruction::Create(params) => {
log::info!("💊 NEW PUMPFUN TOKEN CREATED");
log::info!(
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
);
log::info!("TxId: {}", signature);
log::info!("Token Details:");
log::info!(" • Name: {}", params.name);
log::info!(" • Symbol: {}", params.symbol);
match Create::arrange_accounts(&accounts) {
Some(arranged_accounts) => {
log::info!(" • Mint: {}", arranged_accounts.mint);
log::info!(
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
);
}
None => {
log::warn!(
"Failed to arrange accounts for Create instruction. Create instruction: signature: {signature}"
);
}
}
}
PumpfunInstruction::Migrate(_params) => {
log::info!("🔄 MIGRATE INSTRUCTION DETECTED");
log::info!(
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
);
log::info!("TxId: {}", signature);
match Migrate::arrange_accounts(&accounts) {
Some(arranged_accounts) => {
log::info!(" • Mint: {}", arranged_accounts.mint);
log::info!(
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
);
}
None => {
log::warn!(
"Failed to arrange accounts for Migrate instruction. Migrate instruction: signature: {signature}"
);
}
}
}
_ => {
// Ignore non-target instructions
}
}
Ok(())
}
}
この命令プロセッサを詳しく見ていきましょう:
- 構造体と特質の実装 - 私たちは、
PumpfunInstructionProcessorCarbon を実装する構造体プロセッサデコードされた命令の処理を担当するtraitです。Carbonのドキュメントによると、 「そのプロセッサこのトレイトは、1つの非同期メソッドを提供し、プロセス, これは特定のタイプのデータの処理を担当する(入力タイプ). このトレイトは、パイプライン内でデータを処理する必要がある型によって実装されるように設計されており、さまざまなデータ型に対してカスタマイズされた処理を可能にします。」:
#[async_trait]
pub trait Processor {
type InputType;
async fn process(
&mut self,
data: Self::InputType,
metrics: Arc<MetricsCollection>,
) -> CarbonResult<()>;
}
-
入力型の定義 - その
入力タイプは、3つの要素を含むタプルです:InstructionMetadata: トランザクションのコンテキスト情報が含まれていますDecodedInstruction<PumpfunInstruction>: 復号されたPump.funの命令ネストされた命令: CPI呼び出しなど、ネストされた内部命令
-
処理メソッド- ここで実際の処理ロジックが実行されます:
- トランザクションの署名とアカウント一覧を抽出します
- さまざまな命令タイプを照合し、それぞれを適切に処理します
-
命令ハンドラ:
-
売却指示 - これは、当社の製品がどのように機能するかを説明するためのデモンストレーション目的でのみ含まれています。
account_includeフィルタは正常に動作しています。当社のトランザクションフィルタでは、対象となるトランザクションにミントまたは移行権限アカウントが含まれていることを条件としていますが、売却指示にはこれらのアカウントは含まれていません。したがって、このコードパスが実行されることは決してありません。「EXPECT TO NEVER SEE SELL INSTRUCTION」というログメッセージも、この点を裏付けています。必要であれば、このコードを完全に削除することも可能です。あるいは、売却指示も監視したい場合は、フィルタを修正して売却指示を含めるようにすることもできます。 -
作成時の処理- 新しいPump.funトークンが作成されたとき:
- トランザクションをログに記録し、読みやすい形式に整えます
- params から、次のようなトークンの詳細情報を抽出します。
名前そして記号 - 当社では
arrange_accounts関連するアカウントへの体系的なアクセス権を取得し、それによってミント住所
-
移行に関する説明- トークンの移行が行われる際、Createと同様の情報をログに記録します
-
その他の指示- Pump.fun に関するその他の指示はすべて無視されます
-
-
口座の設定 - 「作成」および「移行」の手順のいずれにおいても、私たちは
arrange_accountsヘルパーメソッド。このCarbon関数:- アカウントの未加工のリストを受け取ります
- 特定の口座を名前で簡単に参照できる、構造化されたオブジェクトとしてそれらを返します
- 返品
なしアカウントが想定されたパターンと一致しない場合
このプロセッサの設計は、Carbonの大きな強みの一つ、すなわち生のバイトデータを扱うのではなく、デコードされた命令データを強型付けの方法で処理できる能力を実証しています。この PumpfunDecoder (これは、 カーボン・ポンプファン・デコーダー crate) は、生の命令データの逆シリアル化という複雑な処理を処理するため、当社のプロセッサはビジネスロジックに集中することができます。IDEのインテリセンス機能を使用して、 Pumpfunの取扱説明書 enum を確認し、デコード可能なすべての命令を確認してください。
アプリケーションの実行
Pump.funのトランザクションモニターを実行するには:
- 必ず
.envファイルには、有効なSolana gRPC認証情報が設定されています - アプリケーションをビルドします:
cargo build
- アプリケーションを実行します:
cargo run
次のような出力が表示されるはずです:
[INFO] Carbon を使用して Pumpfun トランザクションプロセッサを起動中
[INFO] GEYSER_URL: your-geyser-url を使用中
[INFO] X_TOKEN が設定されました
新しいPump.funトークンが作成されると、次のような出力が表示されます:
[INFO] 💊 新しいPUMPFUNトークンが作成されました
[INFO] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[INFO] TxId: 5QrTR9...(一部省略)...
[INFO] トークンの詳細:
[INFO] • 名称: Awesome Token
[INFO] • シンボル: AWSM
[INFO] • 発行: AWsM4rKh...(一部省略)...
[INFO] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
よくやった!
モニターの拡張
この基本的なモニターを拡張する方法として、以下のようなものがあります:
- データベース連携:分析のために取引データをデータベースに保存する
- フィルタを展開:さらにフィルタを追加して、他の命令タイプを対象に設定する
- 複数のプログラムの監視: 定義し、
挿入複数のプログラムを監視し、複数のデコーダーを活用するための追加のトランザクションフィルター - 自動取引:Pump.funに対応したMetis Swap APIを活用して、自動取引戦略を構築・実装する
まとめ
このガイドでは、CarbonとSolana gRPCを使用してPump.funのトランザクションモニターを構築しました。このアプローチにより、最小限のコードで高性能かつリアルタイムなモニタリングを実現できます。Carbonフレームワークは、Solanaのトランザクションやアカウントデータの解析に伴う複雑さの多くを抽象化してくれるため、アプリケーションロジックに集中することができます。
Solanaの開発プロジェクトに関してご質問やサポートが必要な場合は、QuicknodeのDiscordにご参加いただくか、Twitterをフォローしてください。
皆様からのフィードバックを心よりお待ちしております!❤️
ご意見や新しいトピックに関するご要望などがありましたら、ぜひお知らせください。皆様からのご連絡を心よりお待ちしております。
