Solana开发进阶:链上事件到链下解析全攻略在之前我们已经写了三篇文章《探索SolanaSDK实战:Web3开发的双路径与轻量模块化》、《Solana开发实战:Rust客户端调用链上程序全流程》和《Solana开发进阶:在Devnet上实现链上程序部署、调用与更新》完美实现开
在之前我们已经写了三篇文章《探索 Solana SDK 实战:Web3 开发的双路径与轻量模块化》、《Solana 开发实战:Rust 客户端调用链上程序全流程》和《Solana 开发进阶:在 Devnet 上实现链上程序部署、调用与更新》完美实现开发、测试、部署、客户端调用、更新全流程。Solana 的高性能区块链为 Web3 开发打开了新视野,而链上事件是实现智能合约与链下交互的关键枢纽。本文将通过 Rust 实战案例,带你深入掌握链上事件定义、触发到链下解析的全流程,解锁 Solana 开发进阶技能,助你构建更高效的 Web3 应用!
本文延续 Solana 开发系列的实战风格,聚焦智能合约事件的开发与解析全流程。我们通过 Rust 实现一个 Solana 程序,定义并触发 GreetingEvent 事件,利用 JSON-RPC 接口从区块数据中提取日志并反序列化事件内容,完成链上到链下的无缝衔接。文章涵盖 Borsh 序列化、日志提取、RPC 配置等核心技术,配以详细代码解析和运行示例,为希望掌握 Solana 事件机制的开发者提供进阶指南。
solana-sandbox/sol-program on main [!?] is 📦 0.1.0 via 🦀 1.87.0 on 🐳 v28.2.2 (orbstack) took 4.0s
➜ tree . -L 6 -I "coverage_report|lib|.vscode|out|test-ledger|target|node_modules"
.
├── Cargo.lock
├── Cargo.toml
├── examples
│ ├── client.rs
│ └── event.rs
├── keys
│ └── SSoyAkBN9E3CjbWpr2SdgLa6Ejbqqdvasuxd8j1YsmN.json
└── src
├── lib.rs
└── lib2.rs
4 directories, 7 files
#![allow(unexpected_cfgs)]
use borsh_derive::{BorshDeserialize, BorshSerialize};
use solana_account_info::AccountInfo;
use solana_msg::msg;
use solana_program_entrypoint::entrypoint;
use solana_program_error::{ProgramError, ProgramResult};
use solana_pubkey::Pubkey;
// 定义事件结构体
#[derive(BorshDeserialize, BorshSerialize, Debug)]
pub struct GreetingEvent {
pub message: String, // Greeting message contained in the event
}
// 自定义事件触发函数
fn emit_event(event: &GreetingEvent) -> ProgramResult {
let event_data = borsh::to_vec(event).map_err(|_| ProgramError::Custom(1))?; // Serialize to byte array
msg!("EVENT:GREETING:{:?}", event_data); // Output event log
Ok(())
}
entrypoint!(process_instruction);
pub fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
_instruction_data: &[u8],
) -> ProgramResult {
msg!("Hello, Solana!");
let event = GreetingEvent {
message: "Hello from Solana program!".to_string(),
};
emit_event(&event)?;
msg!("Program executed successfully with greeting event!");
Ok(())
}
#[cfg(test)]
mod test {
use solana_program_test::*;
use solana_sdk::{
instruction::Instruction, pubkey::Pubkey, signature::Signer, transaction::Transaction,
};
#[tokio::test]
async fn test_sol_program() {
// let program_id = Pubkey::from_str("GGBjDqYdicSE6Qmtu6SAsueX1biM5LjbJ8R8vZvFfofA").unwrap();
let program_id = Pubkey::new_unique();
let mut program_test = ProgramTest::default();
program_test.add_program("sol_program", program_id, None);
let mut context = program_test.start_with_context().await;
let (banks_client, payer, recent_blockhash) = (
&mut context.banks_client,
&context.payer,
context.last_blockhash,
);
// Create instruction
let instruction = Instruction {
program_id,
accounts: vec![],
data: vec![],
};
// Create transaction with instruction
let mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey()));
// Sign transaction
transaction.sign(&[&payer], recent_blockhash);
let transaction_result = banks_client
.process_transaction_with_metadata(transaction)
.await
.expect("Failed to process transaction");
assert!(transaction_result.result.is_ok());
let logs = transaction_result.metadata.unwrap().log_messages;
assert!(logs.iter().any(|log| log.contains("Hello, Solana!")));
assert!(logs.iter().any(|log| log.contains("EVENT:GREETING:")));
}
}
use anyhow::Result;
use anyhow::anyhow;
use borsh::{BorshDeserialize, BorshSerialize};
use serde_json::Value;
use std::error::Error;
// 定义与程序相同的 GreetingEvent 结构体
#[derive(BorshDeserialize, BorshSerialize, Debug)]
pub struct GreetingEvent {
pub message: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let client = reqwest::Client::builder().build()?;
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("Content-Type", "application/json".parse()?);
let data = r#"
{
"jsonrpc": "2.0",
"id": 1,
"method": "getBlock",...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!