//! ACC-1155: 多代币证书协议接口 //! //! 提供与NAC区块链上ACC-1155证书交互的客户端接口 // NRPC3Client 已更名为 NAC Lens 客户端 use crate::error::{NACError, Result}; use crate::adapters::NRPC3Client; use nac_udm::primitives::{Address, Hash, Timestamp}; use nac_udm::l1_protocol::gnacs::GNACSCode; use nac_udm::l1_protocol::acc::acc1155::{ TokenId, TokenType, TokenTypeDNA, TokenTypeMetadata, BatchTransfer, BatchMint, BatchBurn, TokenCustodyInfo, TokenInsuranceInfo, TokenTypeValuation, }; use nac_udm::l2_governance::sovereignty::SovereigntyRight; use serde_json::json; /// ACC-1155多代币证书接口 pub struct ACC1155 { client: NRPC3Client, } impl ACC1155 { /// 创建新的ACC-1155接口实例 pub fn new(client: NRPC3Client) -> Self { Self { client } } /// 获取代币余额 /// /// # 参数 /// * `certificate_address` - 证书地址 /// * `holder` - 持有者地址 /// * `token_id` - 代币类型ID /// /// # 返回 /// 代币余额 pub async fn balance_of( &self, certificate_address: &Address, holder: &Address, token_id: TokenId, ) -> Result { let params = json!({ "certificate_address": certificate_address, "holder": holder, "token_id": token_id, }); let response = self.client.call("acc1155_balanceOf", params).await?; let balance = response["result"] .as_str() .ok_or(NACError::InvalidResponse("Missing balance".to_string()))? .parse() .map_err(|e| NACError::InvalidResponse(format!("Invalid balance: {}", e)))?; Ok(balance) } /// 批量获取代币余额 /// /// # 参数 /// * `certificate_address` - 证书地址 /// * `holders` - 持有者地址列表 /// * `token_ids` - 代币类型ID列表 /// /// # 返回 /// 代币余额列表 pub async fn balance_of_batch( &self, certificate_address: &Address, holders: Vec
, token_ids: Vec, ) -> Result> { let params = json!({ "certificate_address": certificate_address, "holders": holders, "token_ids": token_ids, }); let response = self.client.call("acc1155_balanceOfBatch", params).await?; let balances: Vec = response["result"] .as_array() .ok_or(NACError::InvalidResponse("Missing balances array".to_string()))? .iter() .map(|v| { v.as_str() .ok_or(NACError::InvalidResponse("Invalid balance".to_string()))? .parse() .map_err(|e| NACError::InvalidResponse(format!("Invalid balance: {}", e))) }) .collect::>>()?; Ok(balances) } /// 创建新的代币类型 /// /// # 参数 /// * `certificate_address` - 证书地址 /// * `token_id` - 代币类型ID /// * `token_type` - 代币类型(可替代性) /// * `gnacs_code` - GNACS编码 /// * `sovereignty_type` - 主权类型 /// * `metadata` - 代币元数据 /// * `max_supply` - 最大供应量(None表示无限制) /// /// # 返回 /// 代币类型DNA pub async fn create_token_type( &self, certificate_address: &Address, token_id: TokenId, token_type: TokenType, gnacs_code: GNACSCode, sovereignty_type: SovereigntyRight, metadata: TokenTypeMetadata, max_supply: Option, ) -> Result { let params = json!({ "certificate_address": certificate_address, "token_id": token_id, "token_type": token_type, "gnacs_code": gnacs_code, "sovereignty_type": sovereignty_type, "metadata": { "name": metadata.name, "symbol": metadata.symbol, "uri": metadata.uri, }, "max_supply": max_supply, }); let response = self.client.call("acc1155_createTokenType", params).await?; // 解析TokenTypeDNA let dna_data = &response["result"]; let token_dna = TokenTypeDNA { dna_hash: Hash::from_hex( dna_data["dna_hash"] .as_str() .ok_or(NACError::InvalidResponse("Missing dna_hash".to_string()))? ).map_err(|e| NACError::InvalidHash(format!("Invalid dna_hash: {}", e)))?, token_id, gnacs_code, token_type, sovereignty_type, metadata_hash: Hash::from_hex( dna_data["metadata_hash"] .as_str() .ok_or(NACError::InvalidResponse("Missing metadata_hash".to_string()))? ).map_err(|e| NACError::InvalidHash(format!("Invalid metadata_hash: {}", e)))?, generated_at: Timestamp::from_secs( dna_data["generated_at"] .as_u64() .ok_or(NACError::InvalidResponse("Missing generated_at".to_string()))? ), }; Ok(token_dna) } /// 批量铸造代币 /// /// # 参数 /// * `certificate_address` - 证书地址 /// * `to` - 接收者地址 /// * `token_ids` - 代币类型ID列表 /// * `amounts` - 数量列表 /// /// # 返回 /// 批量铸造记录 pub async fn mint_batch( &self, certificate_address: &Address, to: &Address, token_ids: Vec, amounts: Vec, ) -> Result { let params = json!({ "certificate_address": certificate_address, "to": to, "token_ids": token_ids, "amounts": amounts.iter().map(|a| a.to_string()).collect::>(), }); let response = self.client.call("acc1155_mintBatch", params).await?; let mint_data = &response["result"]; let batch_mint = BatchMint { to: *to, token_ids, amounts, minted_at: Timestamp::from_secs( mint_data["minted_at"] .as_u64() .ok_or(NACError::InvalidResponse("Missing minted_at".to_string()))? ), constitutional_receipt: Hash::from_hex( mint_data["constitutional_receipt"] .as_str() .ok_or(NACError::InvalidResponse("Missing constitutional_receipt".to_string()))? ).map_err(|e| NACError::InvalidHash(format!("Invalid receipt hash: {}", e)))?, }; Ok(batch_mint) } /// 批量转移代币 /// /// # 参数 /// * `certificate_address` - 证书地址 /// * `from` - 发送者地址 /// * `to` - 接收者地址 /// * `token_ids` - 代币类型ID列表 /// * `amounts` - 数量列表 /// /// # 返回 /// 批量转移记录 pub async fn safe_transfer_batch( &self, certificate_address: &Address, from: &Address, to: &Address, token_ids: Vec, amounts: Vec, ) -> Result { let params = json!({ "certificate_address": certificate_address, "from": from, "to": to, "token_ids": token_ids, "amounts": amounts.iter().map(|a| a.to_string()).collect::>(), }); let response = self.client.call("acc1155_safeTransferBatch", params).await?; let transfer_data = &response["result"]; let batch_transfer = BatchTransfer { from: *from, to: *to, token_ids, amounts, transferred_at: Timestamp::from_secs( transfer_data["transferred_at"] .as_u64() .ok_or(NACError::InvalidResponse("Missing transferred_at".to_string()))? ), constitutional_receipt: Hash::from_hex( transfer_data["constitutional_receipt"] .as_str() .ok_or(NACError::InvalidResponse("Missing constitutional_receipt".to_string()))? ).map_err(|e| NACError::InvalidHash(format!("Invalid receipt hash: {}", e)))?, }; Ok(batch_transfer) } /// 批量销毁代币 /// /// # 参数 /// * `certificate_address` - 证书地址 /// * `from` - 持有者地址 /// * `token_ids` - 代币类型ID列表 /// * `amounts` - 数量列表 /// /// # 返回 /// 批量销毁记录 pub async fn burn_batch( &self, certificate_address: &Address, from: &Address, token_ids: Vec, amounts: Vec, ) -> Result { let params = json!({ "certificate_address": certificate_address, "from": from, "token_ids": token_ids, "amounts": amounts.iter().map(|a| a.to_string()).collect::>(), }); let response = self.client.call("acc1155_burnBatch", params).await?; let burn_data = &response["result"]; let batch_burn = BatchBurn { from: *from, token_ids, amounts, burned_at: Timestamp::from_secs( burn_data["burned_at"] .as_u64() .ok_or(NACError::InvalidResponse("Missing burned_at".to_string()))? ), constitutional_receipt: Hash::from_hex( burn_data["constitutional_receipt"] .as_str() .ok_or(NACError::InvalidResponse("Missing constitutional_receipt".to_string()))? ).map_err(|e| NACError::InvalidHash(format!("Invalid receipt hash: {}", e)))?, }; Ok(batch_burn) } /// 设置授权 /// /// # 参数 /// * `certificate_address` - 证书地址 /// * `owner` - 所有者地址 /// * `operator` - 操作者地址 /// * `approved` - 是否授权 pub async fn set_approval_for_all( &self, certificate_address: &Address, owner: &Address, operator: &Address, approved: bool, ) -> Result<()> { let params = json!({ "certificate_address": certificate_address, "owner": owner, "operator": operator, "approved": approved, }); self.client.call("acc1155_setApprovalForAll", params).await?; Ok(()) } /// 检查授权状态 /// /// # 参数 /// * `certificate_address` - 证书地址 /// * `owner` - 所有者地址 /// * `operator` - 操作者地址 /// /// # 返回 /// 是否已授权 pub async fn is_approved_for_all( &self, certificate_address: &Address, owner: &Address, operator: &Address, ) -> Result { let params = json!({ "certificate_address": certificate_address, "owner": owner, "operator": operator, }); let response = self.client.call("acc1155_isApprovedForAll", params).await?; let approved = response["result"] .as_bool() .ok_or(NACError::InvalidResponse("Missing approval status".to_string()))?; Ok(approved) } /// 获取代币类型元数据 /// /// # 参数 /// * `certificate_address` - 证书地址 /// * `token_id` - 代币类型ID /// /// # 返回 /// 代币类型元数据 pub async fn get_token_metadata( &self, certificate_address: &Address, token_id: TokenId, ) -> Result { let params = json!({ "certificate_address": certificate_address, "token_id": token_id, }); let response = self.client.call("acc1155_getTokenMetadata", params).await?; let meta_data = &response["result"]; let metadata = TokenTypeMetadata { token_id, name: meta_data["name"] .as_str() .ok_or(NACError::InvalidResponse("Missing name".to_string()))? .to_string(), symbol: meta_data["symbol"] .as_str() .ok_or(NACError::InvalidResponse("Missing symbol".to_string()))? .to_string(), token_type: serde_json::from_value(meta_data["token_type"].clone()) .map_err(|e| NACError::InvalidResponse(format!("Invalid token_type: {}", e)))?, uri: meta_data["uri"] .as_str() .ok_or(NACError::InvalidResponse("Missing uri".to_string()))? .to_string(), max_supply: meta_data["max_supply"] .as_str() .and_then(|s| s.parse().ok()), current_supply: meta_data["current_supply"] .as_str() .ok_or(NACError::InvalidResponse("Missing current_supply".to_string()))? .parse() .map_err(|e| NACError::InvalidResponse(format!("Invalid current_supply: {}", e)))?, created_at: Timestamp::from_secs( meta_data["created_at"] .as_u64() .ok_or(NACError::InvalidResponse("Missing created_at".to_string()))? ), }; Ok(metadata) } /// 获取代币类型DNA /// /// # 参数 /// * `certificate_address` - 证书地址 /// * `token_id` - 代币类型ID /// /// # 返回 /// 代币类型DNA pub async fn get_token_dna( &self, certificate_address: &Address, token_id: TokenId, ) -> Result { let params = json!({ "certificate_address": certificate_address, "token_id": token_id, }); let response = self.client.call("acc1155_getTokenDNA", params).await?; let dna_data = &response["result"]; let token_dna = TokenTypeDNA { dna_hash: Hash::from_hex( dna_data["dna_hash"] .as_str() .ok_or(NACError::InvalidResponse("Missing dna_hash".to_string()))? ).map_err(|e| NACError::InvalidHash(format!("Invalid dna_hash: {}", e)))?, token_id, gnacs_code: serde_json::from_value(dna_data["gnacs_code"].clone()) .map_err(|e| NACError::InvalidResponse(format!("Invalid gnacs_code: {}", e)))?, token_type: serde_json::from_value(dna_data["token_type"].clone()) .map_err(|e| NACError::InvalidResponse(format!("Invalid token_type: {}", e)))?, sovereignty_type: serde_json::from_value(dna_data["sovereignty_type"].clone()) .map_err(|e| NACError::InvalidResponse(format!("Invalid sovereignty_type: {}", e)))?, metadata_hash: Hash::from_hex( dna_data["metadata_hash"] .as_str() .ok_or(NACError::InvalidResponse("Missing metadata_hash".to_string()))? ).map_err(|e| NACError::InvalidHash(format!("Invalid metadata_hash: {}", e)))?, generated_at: Timestamp::from_secs( dna_data["generated_at"] .as_u64() .ok_or(NACError::InvalidResponse("Missing generated_at".to_string()))? ), }; Ok(token_dna) } /// 获取托管信息 /// /// # 参数 /// * `certificate_address` - 证书地址 /// * `token_id` - 代币类型ID /// /// # 返回 /// 托管信息 pub async fn get_custody_info( &self, certificate_address: &Address, token_id: TokenId, ) -> Result { let params = json!({ "certificate_address": certificate_address, "token_id": token_id, }); let response = self.client.call("acc1155_getCustodyInfo", params).await?; let custody_data = &response["result"]; let custody_info = TokenCustodyInfo { token_id, custodian: Address::from_hex( custody_data["custodian"] .as_str() .ok_or(NACError::InvalidResponse("Missing custodian".to_string()))? ).map_err(|e| NACError::InvalidAddress(format!("Invalid custodian: {}", e)))?, custody_start: Timestamp::from_secs( custody_data["custody_start"] .as_u64() .ok_or(NACError::InvalidResponse("Missing custody_start".to_string()))? ), is_active: custody_data["is_active"] .as_bool() .ok_or(NACError::InvalidResponse("Missing is_active".to_string()))?, custody_proof: Hash::from_hex( custody_data["custody_proof"] .as_str() .ok_or(NACError::InvalidResponse("Missing custody_proof".to_string()))? ).map_err(|e| NACError::InvalidHash(format!("Invalid custody_proof: {}", e)))?, }; Ok(custody_info) } /// 获取保险信息 /// /// # 参数 /// * `certificate_address` - 证书地址 /// * `token_id` - 代币类型ID /// /// # 返回 /// 保险信息 pub async fn get_insurance_info( &self, certificate_address: &Address, token_id: TokenId, ) -> Result { let params = json!({ "certificate_address": certificate_address, "token_id": token_id, }); let response = self.client.call("acc1155_getInsuranceInfo", params).await?; let insurance_data = &response["result"]; let insurance_info = TokenInsuranceInfo { token_id, insurer: Address::from_hex( insurance_data["insurer"] .as_str() .ok_or(NACError::InvalidResponse("Missing insurer".to_string()))? ).map_err(|e| NACError::InvalidAddress(format!("Invalid insurer: {}", e)))?, coverage_per_unit_xtzh: insurance_data["coverage_per_unit_xtzh"] .as_str() .ok_or(NACError::InvalidResponse("Missing coverage_per_unit_xtzh".to_string()))? .parse() .map_err(|e| NACError::InvalidResponse(format!("Invalid coverage: {}", e)))?, insurance_start: Timestamp::from_secs( insurance_data["insurance_start"] .as_u64() .ok_or(NACError::InvalidResponse("Missing insurance_start".to_string()))? ), insurance_expiry: Timestamp::from_secs( insurance_data["insurance_expiry"] .as_u64() .ok_or(NACError::InvalidResponse("Missing insurance_expiry".to_string()))? ), policy_number: insurance_data["policy_number"] .as_str() .ok_or(NACError::InvalidResponse("Missing policy_number".to_string()))? .to_string(), }; Ok(insurance_info) } /// 更新代币类型估值 /// /// # 参数 /// * `certificate_address` - 证书地址 /// * `token_id` - 代币类型ID /// * `valuation` - 新的估值 pub async fn update_token_valuation( &self, certificate_address: &Address, token_id: TokenId, valuation: TokenTypeValuation, ) -> Result<()> { let params = json!({ "certificate_address": certificate_address, "token_id": token_id, "valuation": { "value_per_unit_xtzh": valuation.value_per_unit_xtzh.to_string(), "valuation_provider": valuation.valuation_provider, "valued_at": valuation.valued_at.as_secs(), "validity_period": valuation.validity_period, }, }); self.client.call("acc1155_updateTokenValuation", params).await?; Ok(()) } /// 获取代币类型估值 /// /// # 参数 /// * `certificate_address` - 证书地址 /// * `token_id` - 代币类型ID /// /// # 返回 /// 代币类型估值 pub async fn get_token_valuation( &self, certificate_address: &Address, token_id: TokenId, ) -> Result { let params = json!({ "certificate_address": certificate_address, "token_id": token_id, }); let response = self.client.call("acc1155_getTokenValuation", params).await?; let val_data = &response["result"]; let valuation = TokenTypeValuation { token_id, value_per_unit_xtzh: val_data["value_per_unit_xtzh"] .as_str() .ok_or(NACError::InvalidResponse("Missing value_per_unit_xtzh".to_string()))? .parse() .map_err(|e| NACError::InvalidResponse(format!("Invalid value: {}", e)))?, valuation_provider: Address::from_hex( val_data["valuation_provider"] .as_str() .ok_or(NACError::InvalidResponse("Missing valuation_provider".to_string()))? ).map_err(|e| NACError::InvalidAddress(format!("Invalid valuation_provider: {}", e)))?, valued_at: Timestamp::from_secs( val_data["valued_at"] .as_u64() .ok_or(NACError::InvalidResponse("Missing valued_at".to_string()))? ), validity_period: val_data["validity_period"] .as_u64() .ok_or(NACError::InvalidResponse("Missing validity_period".to_string()))?, }; Ok(valuation) } } #[cfg(test)] mod tests { use super::*; #[tokio::test] async fn test_acc1155_interface() { // 这里只是接口测试,实际需要连接到NAC节点 let client = NRPC3Client::new("https://rpc.newassetchain.io"); let acc1155 = ACC1155::new(client); // 测试将在实际连接到NAC节点后进行 assert!(true); } }