NAC_Blockchain/nac-api-server/src/blockchain/client.rs

208 lines
5.5 KiB
Rust

use serde::{Deserialize, Serialize};
use anyhow::{Result, Context};
use reqwest::Client;
use std::sync::Arc;
/// NAC区块链RPC客户端
#[derive(Clone)]
pub struct NacClient {
client: Arc<Client>,
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<BalanceInfo> {
let request = RpcRequest {
jsonac_lens: "2.0".to_string(),
method: "nac_getBalance".to_string(),
params: vec![address.to_string()],
id: 1,
};
let response: RpcResponse<BalanceInfo> = 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<String> {
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<String> = 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<Vec<TransactionInfo>> {
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<Vec<TransactionInfo>> = 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<TransactionInfo> {
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<TransactionInfo> = 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<u64> {
let request = RpcRequest {
jsonac_lens: "2.0".to_string(),
method: "nac_blockNumber".to_string(),
params: vec![],
id: 1,
};
let response: RpcResponse<String> = 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::<u64>().context("Failed to parse block height")
}
}
#[derive(Debug, Serialize, Deserialize)]
struct RpcRequest {
jsonac_lens: String,
method: String,
params: Vec<String>,
id: u64,
}
#[derive(Debug, Serialize, Deserialize)]
struct RpcResponse<T> {
jsonac_lens: String,
result: Option<T>,
error: Option<RpcError>,
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<AssetBalance>,
}
#[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);
}
}