335 lines
10 KiB
Rust
335 lines
10 KiB
Rust
//! 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");
|
||
}
|
||
}
|