NAC_Blockchain/nac-bridge-ethereum/src/ethereum_bridge.rs

281 lines
8.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 以太坊桥插件实现(独立版本)
use ethers::prelude::*;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
/// 桥插件错误
#[derive(Debug, thiserror::Error)]
pub enum BridgeError {
#[error("Network error: {0}")]
Network(String),
#[error("Invalid proof: {0}")]
InvalidProof(String),
#[error("Plugin error: {0}")]
PluginError(String),
}
/// Token信息
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TokenInfo {
pub contract_address: Option<String>,
pub symbol: String,
pub decimals: u8,
}
/// 以太坊桥插件
pub struct EthereumBridgePlugin {
/// 链ID1=主网5=Goerli11155111=Sepolia
chain_id: u64,
/// RPC提供商
provider: Arc<Provider<Http>>,
/// 桥合约地址
bridge_contract_address: String,
/// 链名称
chain_name: String,
/// Logo URL
_logo_url: String,
}
impl EthereumBridgePlugin {
/// 创建新的以太坊桥插件
///
/// # 参数
/// - `rpc_url`: 以太坊RPC节点URL
/// - `chain_id`: 链ID1=主网)
/// - `bridge_contract_address`: 桥合约地址
pub async fn new(
rpc_url: &str,
chain_id: u64,
bridge_contract_address: String,
) -> Result<Self, BridgeError> {
let provider = Provider::<Http>::try_from(rpc_url)
.map_err(|e| BridgeError::Network(format!("Failed to connect to RPC: {}", e)))?;
let provider = Arc::new(provider);
let chain_name = match chain_id {
1 => "Ethereum Mainnet".to_string(),
5 => "Goerli Testnet".to_string(),
11155111 => "Sepolia Testnet".to_string(),
_ => format!("Ethereum Chain {}", chain_id),
};
let _logo_url = "https://ethereum.org/static/eth-diamond-black.png".to_string();
Ok(Self {
chain_id,
provider,
bridge_contract_address,
chain_name,
_logo_url,
})
}
/// 获取链ID
pub fn chain_id(&self) -> u64 {
self.chain_id
}
/// 获取链名称
pub fn chain_name(&self) -> &str {
&self.chain_name
}
/// 获取ETH余额
pub async fn get_eth_balance(&self, address: &str) -> Result<u128, BridgeError> {
let address: Address = address
.parse()
.map_err(|e| BridgeError::PluginError(format!("Invalid address: {}", e)))?;
let balance = self.provider
.get_balance(address, None)
.await
.map_err(|e| BridgeError::Network(format!("Failed to get balance: {}", e)))?;
Ok(balance.as_u128())
}
/// 获取ERC-20余额
pub async fn get_erc20_balance(
&self,
user_address: &str,
token_address: &str,
) -> Result<u128, BridgeError> {
let user_addr: Address = user_address
.parse()
.map_err(|e| BridgeError::PluginError(format!("Invalid user address: {}", e)))?;
let token_addr: Address = token_address
.parse()
.map_err(|e| BridgeError::PluginError(format!("Invalid token address: {}", e)))?;
// ERC-20 balanceOf函数选择器: 0x70a08231
let mut data = vec![0x70, 0xa0, 0x82, 0x31]; // balanceOf(address)
data.extend_from_slice(&[0u8; 12]); // 填充12字节
data.extend_from_slice(user_addr.as_bytes());
let tx = Eip1559TransactionRequest::new()
.to(token_addr)
.data(data);
let result = self.provider
.call(&tx.into(), None)
.await
.map_err(|e| BridgeError::Network(format!("Failed to call balanceOf: {}", e)))?;
// 解析返回值32字节uint256
if result.len() != 32 {
return Err(BridgeError::PluginError(
"Invalid balanceOf response".to_string()
));
}
let balance = U256::from_big_endian(&result);
Ok(balance.as_u128())
}
/// 获取余额自动判断ETH或ERC-20
pub async fn get_balance(
&self,
address: &str,
token: &TokenInfo,
) -> Result<u128, BridgeError> {
if let Some(contract_address) = &token.contract_address {
self.get_erc20_balance(address, contract_address).await
} else {
self.get_eth_balance(address).await
}
}
/// 构造锁定ETH交易数据
///
/// # 参数
/// - `amount`: ETH数量wei
/// - `nac_target_address`: NAC目标地址32字节
///
/// # 返回
/// 交易数据(可用于签名和广播)
pub fn build_lock_eth_tx_data(
&self,
_amount: u128,
nac_target_address: &[u8; 32],
) -> Vec<u8> {
// lockETH(bytes32 nacTargetAddress)
let mut data = vec![0x12, 0x34, 0x56, 0x78]; // 函数选择器(占位符)
data.extend_from_slice(nac_target_address);
data
}
/// 构造锁定ERC-20交易数据
///
/// # 参数
/// - `token_address`: ERC-20合约地址
/// - `amount`: Token数量
/// - `nac_target_address`: NAC目标地址32字节
///
/// # 返回
/// 交易数据
pub fn build_lock_erc20_tx_data(
&self,
token_address: &str,
amount: u128,
nac_target_address: &[u8; 32],
) -> Result<Vec<u8>, BridgeError> {
let token_addr: Address = token_address
.parse()
.map_err(|e| BridgeError::PluginError(format!("Invalid token address: {}", e)))?;
// lockERC20(address token, uint256 amount, bytes32 nacTargetAddress)
let mut data = vec![0xab, 0xcd, 0xef, 0x12]; // 函数选择器(占位符)
// token地址
data.extend_from_slice(&[0u8; 12]);
data.extend_from_slice(token_addr.as_bytes());
// amount
let mut amount_bytes = [0u8; 32];
U256::from(amount).to_big_endian(&mut amount_bytes);
data.extend_from_slice(&amount_bytes);
// NAC目标地址
data.extend_from_slice(nac_target_address);
Ok(data)
}
/// 获取交易收据
pub async fn get_transaction_receipt(
&self,
tx_hash: &str,
) -> Result<TransactionReceipt, BridgeError> {
let tx_hash_bytes = hex::decode(tx_hash.trim_start_matches("0x"))
.map_err(|e| BridgeError::PluginError(format!("Invalid tx hash: {}", e)))?;
let tx_hash_h256 = H256::from_slice(&tx_hash_bytes);
let receipt = self.provider
.get_transaction_receipt(tx_hash_h256)
.await
.map_err(|e| BridgeError::Network(format!("Failed to get receipt: {}", e)))?
.ok_or_else(|| BridgeError::InvalidProof("Transaction not found".to_string()))?;
Ok(receipt)
}
/// 获取区块信息
pub async fn get_block(&self, block_number: u64) -> Result<Block<H256>, BridgeError> {
let block = self.provider
.get_block(block_number)
.await
.map_err(|e| BridgeError::Network(format!("Failed to get block: {}", e)))?
.ok_or_else(|| BridgeError::Network("Block not found".to_string()))?;
Ok(block)
}
/// 获取桥合约地址
pub fn bridge_contract_address(&self) -> &str {
&self.bridge_contract_address
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
#[ignore] // 需要实际的RPC节点
async fn test_ethereum_bridge_creation() {
let bridge = EthereumBridgePlugin::new(
"https://eth-mainnet.g.alchemy.com/v2/demo",
1,
"0x1234567890123456789012345678901234567890".to_string(),
).await;
assert!(bridge.is_ok());
let bridge = bridge.expect("mainnet: handle error");
assert_eq!(bridge.chain_id(), 1);
assert_eq!(bridge.chain_name(), "Ethereum Mainnet");
}
#[test]
fn test_build_lock_eth_tx_data() {
let bridge = EthereumBridgePlugin {
chain_id: 1,
provider: Arc::new(Provider::<Http>::try_from("http://localhost:8545").expect("mainnet: handle error")),
bridge_contract_address: "0x1234...".to_string(),
chain_name: "Test".to_string(),
_logo_url: "".to_string(),
};
let nac_address = [1u8; 32];
let data = bridge.build_lock_eth_tx_data(1000000, &nac_address);
// 函数选择器(4字节) + NAC地址(32字节) = 36字节
assert_eq!(data.len(), 36);
}
}