330 lines
9.7 KiB
Rust
330 lines
9.7 KiB
Rust
// NAC Lens方法包装层
|
||
// 提供高级API,封装nac-sdk的NacLensClient
|
||
|
||
use nac_sdk::client::NacLensClient;
|
||
use nac_sdk::error::Result as NACResult;
|
||
use serde_json::{json, Value};
|
||
|
||
/// NAC Lens包装客户端
|
||
///
|
||
/// 提供钱包常用的RPC方法包装
|
||
pub struct NacLensWrapper {
|
||
client: NacLensClient,
|
||
}
|
||
|
||
impl NacLensWrapper {
|
||
/// 创建新的NAC Lens包装客户端
|
||
pub fn new(endpoint: impl Into<String>) -> Self {
|
||
Self {
|
||
client: NacLensClient::new(endpoint),
|
||
}
|
||
}
|
||
|
||
/// 查询余额
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// * `address` - 账户地址(32字节)
|
||
/// * `asset_id` - 资产ID(可选,默认为XTZH)
|
||
///
|
||
/// # Returns
|
||
///
|
||
/// 余额(u128)
|
||
pub async fn get_balance(&self, address: &[u8; 32], asset_id: Option<&str>) -> NACResult<u128> {
|
||
let address_hex = hex::encode(address);
|
||
let asset = asset_id.unwrap_or("xtzh");
|
||
|
||
let params = json!({
|
||
"address": address_hex,
|
||
"asset_id": asset,
|
||
});
|
||
|
||
let result = self.client.call("nac_getBalance", params).await?;
|
||
|
||
// 解析余额
|
||
let balance_str = result.as_str()
|
||
.ok_or_else(|| nac_sdk::error::NACError::InvalidResponse("Invalid balance format".to_string()))?;
|
||
|
||
let balance = u128::from_str_radix(balance_str.trim_start_matches("0x"), 16)
|
||
.map_err(|e| nac_sdk::error::NACError::InvalidResponse(format!("Failed to parse balance: {}", e)))?;
|
||
|
||
Ok(balance)
|
||
}
|
||
|
||
/// 查询Nonce
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// * `address` - 账户地址(32字节)
|
||
///
|
||
/// # Returns
|
||
///
|
||
/// Nonce值(u64)
|
||
pub async fn get_nonce(&self, address: &[u8; 32]) -> NACResult<u64> {
|
||
let address_hex = hex::encode(address);
|
||
|
||
let params = json!({
|
||
"address": address_hex,
|
||
});
|
||
|
||
let result = self.client.call("nac_getTransactionCount", params).await?;
|
||
|
||
let nonce_str = result.as_str()
|
||
.ok_or_else(|| nac_sdk::error::NACError::InvalidResponse("Invalid nonce format".to_string()))?;
|
||
|
||
let nonce = u64::from_str_radix(nonce_str.trim_start_matches("0x"), 16)
|
||
.map_err(|e| nac_sdk::error::NACError::InvalidResponse(format!("Failed to parse nonce: {}", e)))?;
|
||
|
||
Ok(nonce)
|
||
}
|
||
|
||
/// 发送原始交易
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// * `raw_tx` - 原始交易数据(十六进制字符串)
|
||
///
|
||
/// # Returns
|
||
///
|
||
/// 交易哈希
|
||
pub async fn send_raw_transaction(&self, raw_tx: &str) -> NACResult<String> {
|
||
let params = json!({
|
||
"data": raw_tx,
|
||
});
|
||
|
||
let result = self.client.call("nac_sendRawTransaction", params).await?;
|
||
|
||
let tx_hash = result.as_str()
|
||
.ok_or_else(|| nac_sdk::error::NACError::InvalidResponse("Invalid tx hash format".to_string()))?
|
||
.to_string();
|
||
|
||
Ok(tx_hash)
|
||
}
|
||
|
||
/// 查询交易
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// * `tx_hash` - 交易哈希
|
||
///
|
||
/// # Returns
|
||
///
|
||
/// 交易详情(JSON)
|
||
pub async fn get_transaction(&self, tx_hash: &str) -> NACResult<Value> {
|
||
let params = json!({
|
||
"hash": tx_hash,
|
||
});
|
||
|
||
self.client.call("nac_getTransactionByHash", params).await
|
||
}
|
||
|
||
/// 查询交易收据
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// * `tx_hash` - 交易哈希
|
||
///
|
||
/// # Returns
|
||
///
|
||
/// 交易收据(JSON)
|
||
pub async fn get_transaction_receipt(&self, tx_hash: &str) -> NACResult<Value> {
|
||
let params = json!({
|
||
"hash": tx_hash,
|
||
});
|
||
|
||
self.client.call("nac_getTransactionReceipt", params).await
|
||
}
|
||
|
||
/// 查询当前区块号
|
||
///
|
||
/// # Returns
|
||
///
|
||
/// 区块号(u64)
|
||
pub async fn get_block_number(&self) -> NACResult<u64> {
|
||
let params = json!({});
|
||
|
||
let result = self.client.call("nac_blockNumber", params).await?;
|
||
|
||
let block_str = result.as_str()
|
||
.ok_or_else(|| nac_sdk::error::NACError::InvalidResponse("Invalid block number format".to_string()))?;
|
||
|
||
let block_number = u64::from_str_radix(block_str.trim_start_matches("0x"), 16)
|
||
.map_err(|e| nac_sdk::error::NACError::InvalidResponse(format!("Failed to parse block number: {}", e)))?;
|
||
|
||
Ok(block_number)
|
||
}
|
||
|
||
/// 查询区块
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// * `block_number` - 区块号
|
||
/// * `full_transactions` - 是否包含完整交易
|
||
///
|
||
/// # Returns
|
||
///
|
||
/// 区块详情(JSON)
|
||
pub async fn get_block(&self, block_number: u64, full_transactions: bool) -> NACResult<Value> {
|
||
let block_hex = format!("0x{:x}", block_number);
|
||
|
||
let params = json!({
|
||
"block": block_hex,
|
||
"full": full_transactions,
|
||
});
|
||
|
||
self.client.call("nac_getBlockByNumber", params).await
|
||
}
|
||
|
||
/// 估算Gas
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// * `from` - 发送方地址
|
||
/// * `to` - 接收方地址(可选)
|
||
/// * `value` - 转账金额
|
||
/// * `data` - 交易数据(可选)
|
||
///
|
||
/// # Returns
|
||
///
|
||
/// Gas估算值(u64)
|
||
pub async fn estimate_gas(
|
||
&self,
|
||
from: &[u8; 32],
|
||
to: Option<&[u8; 32]>,
|
||
value: u128,
|
||
data: Option<&str>,
|
||
) -> NACResult<u64> {
|
||
let from_hex = hex::encode(from);
|
||
let to_hex = to.map(|addr| hex::encode(addr));
|
||
let value_hex = format!("0x{:x}", value);
|
||
|
||
let mut params_obj = json!({
|
||
"from": from_hex,
|
||
"value": value_hex,
|
||
});
|
||
|
||
if let Some(to_addr) = to_hex {
|
||
params_obj["to"] = json!(to_addr);
|
||
}
|
||
|
||
if let Some(tx_data) = data {
|
||
params_obj["data"] = json!(tx_data);
|
||
}
|
||
|
||
let params = json!([params_obj]);
|
||
|
||
let result = self.client.call("nac_estimateGas", params).await?;
|
||
|
||
let gas_str = result.as_str()
|
||
.ok_or_else(|| nac_sdk::error::NACError::InvalidResponse("Invalid gas format".to_string()))?;
|
||
|
||
let gas = u64::from_str_radix(gas_str.trim_start_matches("0x"), 16)
|
||
.map_err(|e| nac_sdk::error::NACError::InvalidResponse(format!("Failed to parse gas: {}", e)))?;
|
||
|
||
Ok(gas)
|
||
}
|
||
|
||
/// 查询Gas价格
|
||
///
|
||
/// # Returns
|
||
///
|
||
/// Gas价格(u64)
|
||
pub async fn get_gas_price(&self) -> NACResult<u64> {
|
||
let params = json!({});
|
||
|
||
let result = self.client.call("nac_gasPrice", params).await?;
|
||
|
||
let price_str = result.as_str()
|
||
.ok_or_else(|| nac_sdk::error::NACError::InvalidResponse("Invalid gas price format".to_string()))?;
|
||
|
||
let price = u64::from_str_radix(price_str.trim_start_matches("0x"), 16)
|
||
.map_err(|e| nac_sdk::error::NACError::InvalidResponse(format!("Failed to parse gas price: {}", e)))?;
|
||
|
||
Ok(price)
|
||
}
|
||
|
||
/// 查询链ID
|
||
///
|
||
/// # Returns
|
||
///
|
||
/// 链ID(u64)
|
||
pub async fn get_chain_id(&self) -> NACResult<u64> {
|
||
let params = json!({});
|
||
|
||
let result = self.client.call("nac_chainId", params).await?;
|
||
|
||
let chain_id_str = result.as_str()
|
||
.ok_or_else(|| nac_sdk::error::NACError::InvalidResponse("Invalid chain ID format".to_string()))?;
|
||
|
||
let chain_id = u64::from_str_radix(chain_id_str.trim_start_matches("0x"), 16)
|
||
.map_err(|e| nac_sdk::error::NACError::InvalidResponse(format!("Failed to parse chain ID: {}", e)))?;
|
||
|
||
Ok(chain_id)
|
||
}
|
||
|
||
/// 等待交易确认
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// * `tx_hash` - 交易哈希
|
||
/// * `confirmations` - 需要的确认数
|
||
/// * `timeout_secs` - 超时时间(秒)
|
||
///
|
||
/// # Returns
|
||
///
|
||
/// 交易收据(JSON)
|
||
pub async fn wait_for_confirmation(
|
||
&self,
|
||
tx_hash: &str,
|
||
confirmations: u64,
|
||
timeout_secs: u64,
|
||
) -> NACResult<Value> {
|
||
use tokio::time::{sleep, Duration};
|
||
|
||
let start = std::time::Instant::now();
|
||
let timeout = Duration::from_secs(timeout_secs);
|
||
|
||
loop {
|
||
if start.elapsed() > timeout {
|
||
return Err(nac_sdk::error::NACError::Timeout(format!(
|
||
"Transaction {} not confirmed after {} seconds",
|
||
tx_hash, timeout_secs
|
||
)));
|
||
}
|
||
|
||
// 查询交易收据
|
||
match self.get_transaction_receipt(tx_hash).await {
|
||
Ok(receipt) => {
|
||
// 检查确认数
|
||
if let Some(block_number) = receipt.get("blockNumber").and_then(|v| v.as_str()) {
|
||
let tx_block = u64::from_str_radix(block_number.trim_start_matches("0x"), 16)
|
||
.unwrap_or(0);
|
||
|
||
let current_block = self.get_block_number().await?;
|
||
|
||
if current_block >= tx_block + confirmations {
|
||
return Ok(receipt);
|
||
}
|
||
}
|
||
}
|
||
Err(_) => {
|
||
// 交易还未被打包,继续等待
|
||
}
|
||
}
|
||
|
||
sleep(Duration::from_secs(2)).await;
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_nac_lens_wrapper_creation() {
|
||
let wrapper = NacLensWrapper::new("https://rpc.newassetchain.io");
|
||
assert!(true); // 创建成功
|
||
}
|
||
}
|