//! 以太坊桥插件实现(独立版本) 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, pub symbol: String, pub decimals: u8, } /// 以太坊桥插件 pub struct EthereumBridgePlugin { /// 链ID(1=主网,5=Goerli,11155111=Sepolia) chain_id: u64, /// RPC提供商 provider: Arc>, /// 桥合约地址 bridge_contract_address: String, /// 链名称 chain_name: String, /// Logo URL _logo_url: String, } impl EthereumBridgePlugin { /// 创建新的以太坊桥插件 /// /// # 参数 /// - `rpc_url`: 以太坊RPC节点URL /// - `chain_id`: 链ID(1=主网) /// - `bridge_contract_address`: 桥合约地址 pub async fn new( rpc_url: &str, chain_id: u64, bridge_contract_address: String, ) -> Result { let provider = Provider::::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 { 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 { 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 { 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 { // 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, 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 { 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, 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::::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); } }