use serde::{Deserialize, Serialize}; use anyhow::{Result, Context}; use reqwest::Client; use std::sync::Arc; /// NAC区块链RPC客户端 #[derive(Clone)] pub struct NacClient { client: Arc, rpc_url: String, } impl NacClient { pub fn new(rpc_url: String) -> Self { Self { client: Arc::new(Client::new()), rpc_url, } } /// 获取账户余额 pub async fn get_balance(&self, address: &str) -> Result { let request = RpcRequest { jsonac_lens: "2.0".to_string(), method: "nac_getBalance".to_string(), params: vec![address.to_string()], id: 1, }; let response: RpcResponse = self .client .post(&self.rpc_url) .json(&request) .send() .await .context("Failed to send RPC request")? .json() .await .context("Failed to parse RPC response")?; response.result.ok_or_else(|| anyhow::anyhow!("No result in RPC response")) } /// 发送交易 pub async fn send_transaction(&self, tx: Transaction) -> Result { let request = RpcRequest { jsonac_lens: "2.0".to_string(), method: "nac_sendTransaction".to_string(), params: vec![serde_json::to_string(&tx)?], id: 1, }; let response: RpcResponse = self .client .post(&self.rpc_url) .json(&request) .send() .await .context("Failed to send transaction")? .json() .await .context("Failed to parse transaction response")?; response.result.ok_or_else(|| anyhow::anyhow!("No transaction hash in response")) } /// 获取交易历史 pub async fn get_transactions(&self, address: &str, limit: u32) -> Result> { let request = RpcRequest { jsonac_lens: "2.0".to_string(), method: "nac_getTransactions".to_string(), params: vec![address.to_string(), limit.to_string()], id: 1, }; let response: RpcResponse> = self .client .post(&self.rpc_url) .json(&request) .send() .await .context("Failed to get transactions")? .json() .await .context("Failed to parse transactions response")?; response.result.ok_or_else(|| anyhow::anyhow!("No transactions in response")) } /// 获取交易详情 pub async fn get_transaction(&self, tx_hash: &str) -> Result { let request = RpcRequest { jsonac_lens: "2.0".to_string(), method: "nac_getTransaction".to_string(), params: vec![tx_hash.to_string()], id: 1, }; let response: RpcResponse = self .client .post(&self.rpc_url) .json(&request) .send() .await .context("Failed to get transaction")? .json() .await .context("Failed to parse transaction response")?; response.result.ok_or_else(|| anyhow::anyhow!("Transaction not found")) } /// 获取区块高度 pub async fn get_block_height(&self) -> Result { let request = RpcRequest { jsonac_lens: "2.0".to_string(), method: "nac_blockNumber".to_string(), params: vec![], id: 1, }; let response: RpcResponse = self .client .post(&self.rpc_url) .json(&request) .send() .await .context("Failed to get block height")? .json() .await .context("Failed to parse block height response")?; let height_str = response.result.ok_or_else(|| anyhow::anyhow!("No block height in response"))?; height_str.parse::().context("Failed to parse block height") } } #[derive(Debug, Serialize, Deserialize)] struct RpcRequest { jsonac_lens: String, method: String, params: Vec, id: u64, } #[derive(Debug, Serialize, Deserialize)] struct RpcResponse { jsonac_lens: String, result: Option, error: Option, id: u64, } #[derive(Debug, Serialize, Deserialize)] struct RpcError { code: i32, message: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BalanceInfo { pub address: String, pub balance: String, pub assets: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AssetBalance { pub symbol: String, pub amount: String, pub decimals: u8, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Transaction { pub from: String, pub to: String, pub amount: String, pub asset: String, pub nonce: u64, pub signature: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TransactionInfo { pub hash: String, pub from: String, pub to: String, pub amount: String, pub asset: String, pub block_number: u64, pub timestamp: i64, pub status: String, pub fee: String, } #[cfg(test)] mod tests { use super::*; #[tokio::test] async fn test_client_creation() { let client = NacClient::new("http://localhost:8545".to_string()); // 验证客户端创建成功 assert!(Arc::strong_count(&client.client) >= 1); } }