NAC_Blockchain/nac-udm/src/l1_protocol/acc20c/metadata_generator.rs

335 lines
10 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.

//! 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<String>,
/// 属性列表
pub attributes: Vec<MetadataAttribute>,
/// 背景颜色(十六进制,不带#
pub background_color: Option<String>,
/// 动画URL
pub animation_url: Option<String>,
/// YouTube URL
pub youtube_url: Option<String>,
}
/// 元数据属性
#[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<String>,
}
/// 元数据生成器
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<MetadataAttribute> {
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");
}
}