//! ACC-20C兼容层模块 ///! # ACC-20C 元数据生成器 ///! ///! UID: nac.acc20c.MetadataGenerator.v1 ///! ///! 生成符合ERC-721标准的元数据,同时包含NAC特有的GNACS信息。 use crate::l1_protocol::acc::ACC20Enhanced; use crate::l1_protocol::gnacs::GNACSCode; use serde::{Deserialize, Serialize}; use serde_json::Value; /// ERC-721元数据(OpenSea标准) #[derive(Debug, Clone, Serialize, Deserialize)] /// ERC721Metadata pub struct ERC721Metadata { /// 名称 pub name: String, /// 描述 pub description: String, /// 图片URL pub image: String, /// 外部URL pub external_url: Option, /// 属性列表 pub attributes: Vec, /// 背景颜色(十六进制,不带#) pub background_color: Option, /// 动画URL pub animation_url: Option, /// YouTube URL pub youtube_url: Option, } /// 元数据属性 #[derive(Debug, Clone, Serialize, Deserialize)] /// MetadataAttribute pub struct MetadataAttribute { /// 特征类型 pub trait_type: String, /// 值 pub value: Value, /// 显示类型(可选) #[serde(skip_serializing_if = "Option::is_none")] /// display_type pub display_type: Option, } /// 元数据生成器 pub struct MetadataGenerator { /// 基础URL pub base_url: String, /// 图片基础URL pub image_base_url: String, } impl MetadataGenerator { /// 创建新的元数据生成器 pub fn new(base_url: String, image_base_url: String) -> Self { Self { base_url, image_base_url, } } /// 为包装的ACC-20资产生成ERC-721元数据 pub fn generate_metadata( &self, token_id: u128, acc20_asset: &ACC20Enhanced, wrapped_amount: u128, ) -> ERC721Metadata { let gnacs = &acc20_asset.gnacs_code; // 生成名称 let name = format!( "{} #{} ({})", acc20_asset.name, token_id, self.format_amount(wrapped_amount, acc20_asset.decimals) ); // 生成描述 let description = self.generate_description(acc20_asset, wrapped_amount); // 生成图片URL let image = format!( "{}/{}/{}.png", self.image_base_url, acc20_asset.contract_address, token_id ); // 生成外部URL let external_url = Some(format!( "{}/asset/{}/{}", self.base_url, acc20_asset.contract_address, token_id )); // 生成属性 let attributes = self.generate_attributes(acc20_asset, wrapped_amount, gnacs); /// ERC721Metadata ERC721Metadata { name, description, image, external_url, attributes, background_color: Some("1A1D24".to_string()), // NAC品牌色 animation_url: None, youtube_url: None, } } /// 生成描述 fn generate_description( &self, acc20_asset: &ACC20Enhanced, wrapped_amount: u128, ) -> String { format!( "This NFT represents {} {} ({}) on the NAC blockchain, wrapped for compatibility with Ethereum ecosystem.\n\n\ Asset Type: {}\n\ Sovereignty: {}\n\ Compliance Level: {:?}\n\ Jurisdiction: {:?}\n\n\ This is a bridge asset that maintains full NAC compliance while enabling liquidity in Ethereum DeFi.", self.format_amount(wrapped_amount, acc20_asset.decimals), acc20_asset.symbol, acc20_asset.name, format!("{:?}", acc20_asset.gnacs_attributes.asset_category), acc20_asset.sovereignty_type.name(), acc20_asset.compliance_level, acc20_asset.jurisdiction ) } /// 生成属性列表 fn generate_attributes( &self, acc20_asset: &ACC20Enhanced, wrapped_amount: u128, gnacs: &GNACSCode, ) -> Vec { let mut attributes = Vec::new(); // 1. 资产符号 attributes.push(MetadataAttribute { trait_type: "Asset Symbol".to_string(), value: Value::String(acc20_asset.symbol.clone()), display_type: None, }); // 2. 包装数量 attributes.push(MetadataAttribute { trait_type: "Wrapped Amount".to_string(), value: Value::String(self.format_amount(wrapped_amount, acc20_asset.decimals)), display_type: None, }); // 3. GNACS编码 attributes.push(MetadataAttribute { trait_type: "GNACS Code".to_string(), value: Value::String(gnacs.to_hex()), display_type: None, }); // 4. 资产类别 attributes.push(MetadataAttribute { trait_type: "Asset Category".to_string(), value: Value::String(format!("{:?}", acc20_asset.gnacs_attributes.asset_category)), display_type: None, }); // 5. 主权类型 attributes.push(MetadataAttribute { trait_type: "Sovereignty Type".to_string(), value: Value::String(acc20_asset.sovereignty_type.name().to_string()), display_type: None, }); // 6. 合规级别 attributes.push(MetadataAttribute { trait_type: "Compliance Level".to_string(), value: Value::String(format!("{:?}", acc20_asset.compliance_level)), display_type: None, }); // 7. 司法辖区 attributes.push(MetadataAttribute { trait_type: "Jurisdiction".to_string(), value: Value::String(format!("{:?}", acc20_asset.jurisdiction)), display_type: None, }); // 8. 风险等级 attributes.push(MetadataAttribute { trait_type: "Risk Level".to_string(), value: Value::String(format!("{:?}", acc20_asset.risk_level)), display_type: None, }); // 9. 合规状态 attributes.push(MetadataAttribute { trait_type: "Compliance Status".to_string(), value: Value::String(format!("{:?}", acc20_asset.compliance_status)), display_type: None, }); // 10. 是否可碎片化 attributes.push(MetadataAttribute { trait_type: "Fragmentable".to_string(), value: Value::Bool(acc20_asset.is_fragmentable), display_type: None, }); // 11. 是否启用跨链 attributes.push(MetadataAttribute { trait_type: "Cross-Chain Enabled".to_string(), value: Value::Bool(acc20_asset.cross_chain_enabled), display_type: None, }); // 12. 估值(如果有) if let Some(ref valuation) = acc20_asset.valuation_info { attributes.push(MetadataAttribute { trait_type: "Valuation (XTZH)".to_string(), value: Value::String(self.format_amount(valuation.current_valuation, 18)), display_type: Some("number".to_string()), }); } // 13. 创建时间 attributes.push(MetadataAttribute { trait_type: "Created At".to_string(), value: Value::Number(acc20_asset.created_at.as_secs().into()), display_type: Some("date".to_string()), }); attributes } /// 格式化数量 fn format_amount(&self, amount: u128, decimals: u8) -> String { let divisor = 10u128.pow(decimals as u32); let integer_part = amount / divisor; let fractional_part = amount % divisor; if fractional_part == 0 { format!("{}", integer_part) } else { let fractional_str = format!("{:0width$}", fractional_part, width = decimals as usize); let trimmed = fractional_str.trim_end_matches('0'); format!("{}.{}", integer_part, trimmed) } } /// 生成元数据JSON pub fn generate_metadata_json( &self, token_id: u128, acc20_asset: &ACC20Enhanced, wrapped_amount: u128, ) -> String { let metadata = self.generate_metadata(token_id, acc20_asset, wrapped_amount); serde_json::to_string_pretty(&metadata).unwrap() } } #[cfg(test)] mod tests { use super::*; use crate::primitives::Address; use crate::l1_protocol::gnacs::{AssetCategory, Jurisdiction, ComplianceLevel, RiskLevel}; use crate::l2_governance::sovereignty::SovereigntyRight; #[test] fn test_metadata_generation() { let gnacs = GNACSCode::new( AssetCategory::Equity, 1, Jurisdiction::US, ComplianceLevel::High, RiskLevel::Medium, ); let mut asset = ACC20Enhanced::new( "Shanghai Office Share".to_string(), "SH-OFFICE".to_string(), 18, gnacs, SovereigntyRight::A0, ).unwrap(); asset.contract_address = Address::zero(); let generator = MetadataGenerator::new( "https://nac.network".to_string(), "https://images.nac.network".to_string(), ); let metadata = generator.generate_metadata(1, &asset, 1_000_000_000_000_000_000); assert!(metadata.name.contains("Shanghai Office Share")); assert!(metadata.description.contains("NAC blockchain")); assert!(metadata.attributes.len() > 10); } #[test] fn test_format_amount() { let generator = MetadataGenerator::new( "https://nac.network".to_string(), "https://images.nac.network".to_string(), ); assert_eq!(generator.format_amount(1_000_000_000_000_000_000, 18), "1"); assert_eq!(generator.format_amount(1_500_000_000_000_000_000, 18), "1.5"); assert_eq!(generator.format_amount(123_456_789_000_000_000, 18), "0.123456789"); } }