//! ACC-20C: 兼容层协议接口 //! //! 提供与NAC区块链上ACC-20C兼容层交互的客户端接口 // NacLensClient 已更名为 NAC Lens 客户端 use crate::error::{NACError, Result}; use crate::adapters::NacLensClient; use nac_udm::primitives::{Address, Hash, Timestamp}; use nac_udm::l1_protocol::gnacs::GNACSCode; use nac_udm::l1_protocol::acc20c::{ WrappedAsset, WrapperConfig, WrapperStatus, WrappedAssetStatus, ComplianceSnapshot, EthAddress, u256, }; use serde_json::json; /// ACC-20C兼容层接口 /// /// ACC-20C是NAC与以太坊生态之间的战略性桥梁,提供: /// - 双向包装:NAC ACC-20 ↔ 以太坊 ERC-721 /// - 合规保留:包装后仍保留KYC/AML检查 /// - 状态同步:跨链事件监听和状态同步 /// - 元数据生成:符合OpenSea标准的ERC-721元数据 pub struct ACC20C { client: NacLensClient, } impl ACC20C { /// 创建新的ACC-20C接口实例 pub fn new(client: NacLensClient) -> Self { Self { client } } /// 包装ACC-20为ERC-721 /// /// 将NAC链上的ACC-20资产包装成以太坊链上的ERC-721 NFT /// /// # 参数 /// * `wrapper_address` - 包装器合约地址 /// * `owner` - NAC链上的所有者地址 /// * `eth_recipient` - 以太坊链上的接收者地址 /// * `amount` - 包装数量 /// /// # 返回 /// ERC-721 Token ID /// /// # 流程 /// 1. 验证包装器状态 /// 2. 验证合规性 /// 3. 锁定ACC-20资产 /// 4. 生成ERC-721 NFT /// 5. 记录包装信息 pub async fn wrap( &self, wrapper_address: &Address, owner: &Address, eth_recipient: &EthAddress, amount: u128, ) -> Result { let params = json!({ "wrapper_address": wrapper_address, "owner": owner, "eth_recipient": format!("0x{}", hex::encode(eth_recipient.0)), "amount": amount.to_string(), }); let response = self.client.call("acc20c_wrap", params).await?; let token_id_data = &response["result"]["token_id"]; let token_id = u256 { low: token_id_data["low"] .as_str() .ok_or(NACError::InvalidResponse("Missing token_id.low".to_string()))? .parse() .map_err(|e| NACError::InvalidResponse(format!("Invalid token_id.low: {}", e)))?, high: token_id_data["high"] .as_str() .unwrap_or("0") .parse() .unwrap_or(0), }; Ok(token_id) } /// 解包装ERC-721为ACC-20 /// /// 将以太坊链上的ERC-721 NFT解包装回NAC链上的ACC-20资产 /// /// # 参数 /// * `wrapper_address` - 包装器合约地址 /// * `token_id` - ERC-721 Token ID /// * `eth_owner` - 以太坊链上的所有者地址 /// * `nac_recipient` - NAC链上的接收者地址 /// /// # 返回 /// 解包装的ACC-20数量 /// /// # 流程 /// 1. 验证TokenId存在 /// 2. 验证所有权 /// 3. 销毁ERC-721 NFT /// 4. 解锁ACC-20资产 /// 5. 转回接收者 pub async fn unwrap( &self, wrapper_address: &Address, token_id: u256, eth_owner: &EthAddress, nac_recipient: &Address, ) -> Result { let params = json!({ "wrapper_address": wrapper_address, "token_id": { "low": token_id.low.to_string(), "high": token_id.high.to_string(), }, "eth_owner": format!("0x{}", hex::encode(eth_owner.0)), "nac_recipient": nac_recipient, }); let response = self.client.call("acc20c_unwrap", params).await?; let amount = response["result"]["amount"] .as_str() .ok_or(NACError::InvalidResponse("Missing amount".to_string()))? .parse() .map_err(|e| NACError::InvalidResponse(format!("Invalid amount: {}", e)))?; Ok(amount) } /// 查询包装资产信息 /// /// # 参数 /// * `wrapper_address` - 包装器合约地址 /// * `token_id` - ERC-721 Token ID /// /// # 返回 /// 包装资产详细信息 pub async fn get_wrapped_asset( &self, wrapper_address: &Address, token_id: u256, ) -> Result { let params = json!({ "wrapper_address": wrapper_address, "token_id": { "low": token_id.low.to_string(), "high": token_id.high.to_string(), }, }); let response = self.client.call("acc20c_getWrappedAsset", params).await?; let asset_data = &response["result"]; // 解析以太坊地址 let eth_owner_hex = asset_data["current_owner"] .as_str() .ok_or(NACError::InvalidResponse("Missing current_owner".to_string()))? .trim_start_matches("0x"); let eth_owner_bytes = hex::decode(eth_owner_hex) .map_err(|e| NACError::InvalidAddress(format!("Invalid eth address: {}", e)))?; let mut eth_owner_array = [0u8; 20]; eth_owner_array.copy_from_slice(ð_owner_bytes[..20]); let wrapped_asset = WrappedAsset { token_id, original_holdings: asset_data["original_holdings"] .as_str() .ok_or(NACError::InvalidResponse("Missing original_holdings".to_string()))? .parse() .map_err(|e| NACError::InvalidResponse(format!("Invalid holdings: {}", e)))?, original_owner: Address::from_hex( asset_data["original_owner"] .as_str() .ok_or(NACError::InvalidResponse("Missing original_owner".to_string()))? ).map_err(|e| NACError::InvalidAddress(format!("Invalid original_owner: {}", e)))?, current_owner: EthAddress(eth_owner_array), gnacs_code: serde_json::from_value(asset_data["gnacs_code"].clone()) .map_err(|e| NACError::InvalidResponse(format!("Invalid gnacs_code: {}", e)))?, wrapped_at: Timestamp::from_secs( asset_data["wrapped_at"] .as_u64() .ok_or(NACError::InvalidResponse("Missing wrapped_at".to_string()))? ), metadata_uri: asset_data["metadata_uri"] .as_str() .ok_or(NACError::InvalidResponse("Missing metadata_uri".to_string()))? .to_string(), compliance_snapshot: self.parse_compliance_snapshot(&asset_data["compliance_snapshot"])?, status: serde_json::from_value(asset_data["status"].clone()) .map_err(|e| NACError::InvalidResponse(format!("Invalid status: {}", e)))?, }; Ok(wrapped_asset) } /// 查询锁定的持有量 /// /// # 参数 /// * `wrapper_address` - 包装器合约地址 /// * `owner` - 所有者地址 /// /// # 返回 /// 锁定的ACC-20数量 pub async fn get_locked_holdings( &self, wrapper_address: &Address, owner: &Address, ) -> Result { let params = json!({ "wrapper_address": wrapper_address, "owner": owner, }); let response = self.client.call("acc20c_getLockedHoldings", params).await?; let holdings = response["result"] .as_str() .ok_or(NACError::InvalidResponse("Missing holdings".to_string()))? .parse() .map_err(|e| NACError::InvalidResponse(format!("Invalid holdings: {}", e)))?; Ok(holdings) } /// 冻结包装资产 /// /// # 参数 /// * `wrapper_address` - 包装器合约地址 /// * `token_id` - ERC-721 Token ID pub async fn freeze_wrapped_asset( &self, wrapper_address: &Address, token_id: u256, ) -> Result<()> { let params = json!({ "wrapper_address": wrapper_address, "token_id": { "low": token_id.low.to_string(), "high": token_id.high.to_string(), }, }); self.client.call("acc20c_freezeWrappedAsset", params).await?; Ok(()) } /// 解冻包装资产 /// /// # 参数 /// * `wrapper_address` - 包装器合约地址 /// * `token_id` - ERC-721 Token ID pub async fn unfreeze_wrapped_asset( &self, wrapper_address: &Address, token_id: u256, ) -> Result<()> { let params = json!({ "wrapper_address": wrapper_address, "token_id": { "low": token_id.low.to_string(), "high": token_id.high.to_string(), }, }); self.client.call("acc20c_unfreezeWrappedAsset", params).await?; Ok(()) } /// 查询包装器配置 /// /// # 参数 /// * `wrapper_address` - 包装器合约地址 /// /// # 返回 /// 包装器配置信息 pub async fn get_wrapper_config( &self, wrapper_address: &Address, ) -> Result { let params = json!({ "wrapper_address": wrapper_address, }); let response = self.client.call("acc20c_getWrapperConfig", params).await?; let config_data = &response["result"]; let config = WrapperConfig { acc20_per_nft: config_data["acc20_per_nft"] .as_str() .ok_or(NACError::InvalidResponse("Missing acc20_per_nft".to_string()))? .parse() .map_err(|e| NACError::InvalidResponse(format!("Invalid acc20_per_nft: {}", e)))?, allow_partial_wrap: config_data["allow_partial_wrap"] .as_bool() .ok_or(NACError::InvalidResponse("Missing allow_partial_wrap".to_string()))?, wrap_fee_bps: config_data["wrap_fee_bps"] .as_u64() .ok_or(NACError::InvalidResponse("Missing wrap_fee_bps".to_string()))? as u16, unwrap_fee_bps: config_data["unwrap_fee_bps"] .as_u64() .ok_or(NACError::InvalidResponse("Missing unwrap_fee_bps".to_string()))? as u16, fee_recipient: Address::from_hex( config_data["fee_recipient"] .as_str() .ok_or(NACError::InvalidResponse("Missing fee_recipient".to_string()))? ).map_err(|e| NACError::InvalidAddress(format!("Invalid fee_recipient: {}", e)))?, preserve_compliance: config_data["preserve_compliance"] .as_bool() .ok_or(NACError::InvalidResponse("Missing preserve_compliance".to_string()))?, min_wrap_amount: config_data["min_wrap_amount"] .as_str() .ok_or(NACError::InvalidResponse("Missing min_wrap_amount".to_string()))? .parse() .map_err(|e| NACError::InvalidResponse(format!("Invalid min_wrap_amount: {}", e)))?, max_wrap_amount: config_data["max_wrap_amount"] .as_str() .ok_or(NACError::InvalidResponse("Missing max_wrap_amount".to_string()))? .parse() .map_err(|e| NACError::InvalidResponse(format!("Invalid max_wrap_amount: {}", e)))?, }; Ok(config) } /// 查询包装器状态 /// /// # 参数 /// * `wrapper_address` - 包装器合约地址 /// /// # 返回 /// 包装器状态 pub async fn get_wrapper_status( &self, wrapper_address: &Address, ) -> Result { let params = json!({ "wrapper_address": wrapper_address, }); let response = self.client.call("acc20c_getWrapperStatus", params).await?; let status = serde_json::from_value(response["result"].clone()) .map_err(|e| NACError::InvalidResponse(format!("Invalid status: {}", e)))?; Ok(status) } /// 获取元数据URI /// /// # 参数 /// * `wrapper_address` - 包装器合约地址 /// * `token_id` - ERC-721 Token ID /// /// # 返回 /// 元数据URI(符合ERC-721标准) pub async fn get_metadata_uri( &self, wrapper_address: &Address, token_id: u256, ) -> Result { let params = json!({ "wrapper_address": wrapper_address, "token_id": { "low": token_id.low.to_string(), "high": token_id.high.to_string(), }, }); let response = self.client.call("acc20c_getMetadataURI", params).await?; let uri = response["result"] .as_str() .ok_or(NACError::InvalidResponse("Missing metadata URI".to_string()))? .to_string(); Ok(uri) } /// 计算包装手续费 /// /// # 参数 /// * `wrapper_address` - 包装器合约地址 /// * `amount` - 包装数量 /// /// # 返回 /// 手续费金额 pub async fn calculate_wrap_fee( &self, wrapper_address: &Address, amount: u128, ) -> Result { let params = json!({ "wrapper_address": wrapper_address, "amount": amount.to_string(), }); let response = self.client.call("acc20c_calculateWrapFee", params).await?; let fee = response["result"] .as_str() .ok_or(NACError::InvalidResponse("Missing fee".to_string()))? .parse() .map_err(|e| NACError::InvalidResponse(format!("Invalid fee: {}", e)))?; Ok(fee) } /// 计算解包装手续费 /// /// # 参数 /// * `wrapper_address` - 包装器合约地址 /// * `amount` - 解包装数量 /// /// # 返回 /// 手续费金额 pub async fn calculate_unwrap_fee( &self, wrapper_address: &Address, amount: u128, ) -> Result { let params = json!({ "wrapper_address": wrapper_address, "amount": amount.to_string(), }); let response = self.client.call("acc20c_calculateUnwrapFee", params).await?; let fee = response["result"] .as_str() .ok_or(NACError::InvalidResponse("Missing fee".to_string()))? .parse() .map_err(|e| NACError::InvalidResponse(format!("Invalid fee: {}", e)))?; Ok(fee) } // === 私有辅助函数 === /// 解析合规快照 fn parse_compliance_snapshot( &self, snapshot_data: &serde_json::Value, ) -> Result { let snapshot = ComplianceSnapshot { kyc_level: snapshot_data["kyc_level"] .as_u64() .ok_or(NACError::InvalidResponse("Missing kyc_level".to_string()))? as u8, aml_status: snapshot_data["aml_status"] .as_u64() .ok_or(NACError::InvalidResponse("Missing aml_status".to_string()))? as u8, compliance_level: snapshot_data["compliance_level"] .as_u64() .ok_or(NACError::InvalidResponse("Missing compliance_level".to_string()))? as u8, jurisdiction: snapshot_data["jurisdiction"] .as_u64() .ok_or(NACError::InvalidResponse("Missing jurisdiction".to_string()))? as u8, snapshot_at: Timestamp::from_secs( snapshot_data["snapshot_at"] .as_u64() .ok_or(NACError::InvalidResponse("Missing snapshot_at".to_string()))? ), snapshot_hash: Hash::from_hex( snapshot_data["snapshot_hash"] .as_str() .ok_or(NACError::InvalidResponse("Missing snapshot_hash".to_string()))? ).map_err(|e| NACError::InvalidHash(format!("Invalid snapshot_hash: {}", e)))?, }; Ok(snapshot) } } #[cfg(test)] mod tests { use super::*; #[tokio::test] async fn test_acc20c_interface() { // 这里只是接口测试,实际需要连接到NAC节点 let client = NacLensClient::new("https://rpc.newassetchain.io"); let acc20c = ACC20C::new(client); // 测试将在实际连接到NAC节点后进行 assert!(true); } #[test] fn test_u256() { let token_id = u256::from_u128(12345); assert_eq!(token_id.low, 12345); assert_eq!(token_id.high, 0); } #[test] fn test_eth_address() { let eth_addr = EthAddress([0u8; 20]); assert_eq!(eth_addr.0.len(), 20); } }