464 lines
13 KiB
Rust
464 lines
13 KiB
Rust
// ACC-721: 唯一资产协议(Non-Fungible Token Protocol)
|
||
//
|
||
// NAC原生的唯一资产标准,专为RWA资产设计
|
||
// 不是ERC-721的实现,是完全独立的NAC原生协议
|
||
|
||
use serde::{Deserialize, Serialize};
|
||
use std::collections::HashMap;
|
||
|
||
/// ACC-721错误类型
|
||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub enum ACC721Error {
|
||
/// 资产不存在
|
||
TokenNotFound,
|
||
/// 资产已存在
|
||
TokenAlreadyExists,
|
||
/// 无效的所有者
|
||
InvalidOwner,
|
||
/// 无效的接收地址
|
||
InvalidRecipient,
|
||
/// 未授权操作
|
||
Unauthorized,
|
||
/// 资产已冻结
|
||
AssetFrozen,
|
||
/// 合规检查失败
|
||
ComplianceFailed(String),
|
||
}
|
||
|
||
/// ACC-721资产元数据
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ACC721Metadata {
|
||
/// 资产集合名称
|
||
pub name: String,
|
||
/// 资产集合符号
|
||
pub symbol: String,
|
||
/// 基础URI
|
||
pub base_uri: String,
|
||
}
|
||
|
||
/// ACC-721资产信息
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct TokenInfo {
|
||
/// 资产ID
|
||
pub token_id: String,
|
||
/// 所有者地址
|
||
pub owner: String,
|
||
/// 资产URI
|
||
pub token_uri: String,
|
||
/// 资产DNA(NAC原生)
|
||
pub asset_dna: Option<String>,
|
||
/// GNACS分类编码(NAC原生)
|
||
pub gnacs_code: Option<String>,
|
||
/// 是否冻结
|
||
pub frozen: bool,
|
||
}
|
||
|
||
/// ACC-721资产状态
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ACC721State {
|
||
/// 资产信息映射 (token_id -> TokenInfo)
|
||
pub tokens: HashMap<String, TokenInfo>,
|
||
/// 所有者资产列表 (owner -> [token_ids])
|
||
pub owner_tokens: HashMap<String, Vec<String>>,
|
||
/// 资产授权映射 (token_id -> approved_address)
|
||
pub token_approvals: HashMap<String, String>,
|
||
/// 操作员授权映射 (owner -> operator -> bool)
|
||
pub operator_approvals: HashMap<String, HashMap<String, bool>>,
|
||
/// 元数据
|
||
pub metadata: ACC721Metadata,
|
||
}
|
||
|
||
/// ACC-721接口
|
||
pub trait ACC721 {
|
||
/// 获取资产集合名称
|
||
fn name(&self) -> String;
|
||
|
||
/// 获取资产集合符号
|
||
fn symbol(&self) -> String;
|
||
|
||
/// 获取资产URI
|
||
fn token_uri(&self, token_id: &str) -> Result<String, ACC721Error>;
|
||
|
||
/// 获取资产所有者
|
||
fn owner_of(&self, token_id: &str) -> Result<String, ACC721Error>;
|
||
|
||
/// 获取账户拥有的资产数量
|
||
fn balance_of(&self, owner: &str) -> u64;
|
||
|
||
/// 转移资产
|
||
fn transfer_from(&mut self, from: &str, to: &str, token_id: &str) -> Result<(), ACC721Error>;
|
||
|
||
/// 安全转移资产
|
||
fn safe_transfer_from(
|
||
&mut self,
|
||
from: &str,
|
||
to: &str,
|
||
token_id: &str,
|
||
) -> Result<(), ACC721Error>;
|
||
|
||
/// 授权资产
|
||
fn approve(&mut self, to: &str, token_id: &str) -> Result<(), ACC721Error>;
|
||
|
||
/// 获取资产授权地址
|
||
fn get_approved(&self, token_id: &str) -> Result<Option<String>, ACC721Error>;
|
||
|
||
/// 设置操作员授权
|
||
fn set_approval_for_all(&mut self, owner: &str, operator: &str, approved: bool) -> Result<(), ACC721Error>;
|
||
|
||
/// 检查操作员授权
|
||
fn is_approved_for_all(&self, owner: &str, operator: &str) -> bool;
|
||
|
||
/// 铸造资产
|
||
fn mint(&mut self, to: &str, token_id: &str, token_uri: &str) -> Result<(), ACC721Error>;
|
||
|
||
/// 销毁资产
|
||
fn burn(&mut self, token_id: &str) -> Result<(), ACC721Error>;
|
||
|
||
/// 冻结资产
|
||
fn freeze_token(&mut self, token_id: &str) -> Result<(), ACC721Error>;
|
||
|
||
/// 解冻资产
|
||
fn unfreeze_token(&mut self, token_id: &str) -> Result<(), ACC721Error>;
|
||
|
||
/// 检查资产是否冻结
|
||
fn is_frozen(&self, token_id: &str) -> bool;
|
||
}
|
||
|
||
/// ACC-721标准实现
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ACC721Token {
|
||
state: ACC721State,
|
||
}
|
||
|
||
impl ACC721Token {
|
||
/// 创建新的ACC-721资产集合
|
||
pub fn new(metadata: ACC721Metadata) -> Self {
|
||
Self {
|
||
state: ACC721State {
|
||
tokens: HashMap::new(),
|
||
owner_tokens: HashMap::new(),
|
||
token_approvals: HashMap::new(),
|
||
operator_approvals: HashMap::new(),
|
||
metadata,
|
||
},
|
||
}
|
||
}
|
||
|
||
/// 获取状态的可变引用
|
||
pub fn state_mut(&mut self) -> &mut ACC721State {
|
||
&mut self.state
|
||
}
|
||
|
||
/// 获取状态的不可变引用
|
||
pub fn state(&self) -> &ACC721State {
|
||
&self.state
|
||
}
|
||
}
|
||
|
||
impl ACC721 for ACC721Token {
|
||
fn name(&self) -> String {
|
||
self.state.metadata.name.clone()
|
||
}
|
||
|
||
fn symbol(&self) -> String {
|
||
self.state.metadata.symbol.clone()
|
||
}
|
||
|
||
fn token_uri(&self, token_id: &str) -> Result<String, ACC721Error> {
|
||
self.state
|
||
.tokens
|
||
.get(token_id)
|
||
.map(|info| info.token_uri.clone())
|
||
.ok_or(ACC721Error::TokenNotFound)
|
||
}
|
||
|
||
fn owner_of(&self, token_id: &str) -> Result<String, ACC721Error> {
|
||
self.state
|
||
.tokens
|
||
.get(token_id)
|
||
.map(|info| info.owner.clone())
|
||
.ok_or(ACC721Error::TokenNotFound)
|
||
}
|
||
|
||
fn balance_of(&self, owner: &str) -> u64 {
|
||
self.state
|
||
.owner_tokens
|
||
.get(owner)
|
||
.map(|tokens| tokens.len() as u64)
|
||
.unwrap_or(0)
|
||
}
|
||
|
||
fn transfer_from(&mut self, from: &str, to: &str, token_id: &str) -> Result<(), ACC721Error> {
|
||
// 检查资产是否存在
|
||
let token_info = self.state
|
||
.tokens
|
||
.get(token_id)
|
||
.ok_or(ACC721Error::TokenNotFound)?;
|
||
|
||
// 检查资产是否冻结
|
||
if token_info.frozen {
|
||
return Err(ACC721Error::AssetFrozen);
|
||
}
|
||
|
||
// 检查所有者
|
||
if token_info.owner != from {
|
||
return Err(ACC721Error::InvalidOwner);
|
||
}
|
||
|
||
// 更新所有者
|
||
self.state
|
||
.tokens
|
||
.get_mut(token_id)
|
||
.unwrap()
|
||
.owner = to.to_string();
|
||
|
||
// 更新所有者资产列表
|
||
if let Some(from_tokens) = self.state.owner_tokens.get_mut(from) {
|
||
from_tokens.retain(|id| id != token_id);
|
||
}
|
||
self.state
|
||
.owner_tokens
|
||
.entry(to.to_string())
|
||
.or_insert_with(Vec::new)
|
||
.push(token_id.to_string());
|
||
|
||
// 清除授权
|
||
self.state.token_approvals.remove(token_id);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn safe_transfer_from(
|
||
&mut self,
|
||
from: &str,
|
||
to: &str,
|
||
token_id: &str,
|
||
) -> Result<(), ACC721Error> {
|
||
// 简化实现:与transfer_from相同
|
||
// 实际应用中应检查接收地址是否支持ACC-721
|
||
self.transfer_from(from, to, token_id)
|
||
}
|
||
|
||
fn approve(&mut self, to: &str, token_id: &str) -> Result<(), ACC721Error> {
|
||
// 检查资产是否存在
|
||
if !self.state.tokens.contains_key(token_id) {
|
||
return Err(ACC721Error::TokenNotFound);
|
||
}
|
||
|
||
// 设置授权
|
||
self.state.token_approvals.insert(token_id.to_string(), to.to_string());
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn get_approved(&self, token_id: &str) -> Result<Option<String>, ACC721Error> {
|
||
// 检查资产是否存在
|
||
if !self.state.tokens.contains_key(token_id) {
|
||
return Err(ACC721Error::TokenNotFound);
|
||
}
|
||
|
||
Ok(self.state.token_approvals.get(token_id).cloned())
|
||
}
|
||
|
||
fn set_approval_for_all(&mut self, owner: &str, operator: &str, approved: bool) -> Result<(), ACC721Error> {
|
||
self.state
|
||
.operator_approvals
|
||
.entry(owner.to_string())
|
||
.or_insert_with(HashMap::new)
|
||
.insert(operator.to_string(), approved);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn is_approved_for_all(&self, owner: &str, operator: &str) -> bool {
|
||
self.state
|
||
.operator_approvals
|
||
.get(owner)
|
||
.and_then(|approvals| approvals.get(operator))
|
||
.copied()
|
||
.unwrap_or(false)
|
||
}
|
||
|
||
fn mint(&mut self, to: &str, token_id: &str, token_uri: &str) -> Result<(), ACC721Error> {
|
||
// 检查资产是否已存在
|
||
if self.state.tokens.contains_key(token_id) {
|
||
return Err(ACC721Error::TokenAlreadyExists);
|
||
}
|
||
|
||
// 创建资产
|
||
let token_info = TokenInfo {
|
||
token_id: token_id.to_string(),
|
||
owner: to.to_string(),
|
||
token_uri: token_uri.to_string(),
|
||
asset_dna: None,
|
||
gnacs_code: None,
|
||
frozen: false,
|
||
};
|
||
|
||
self.state.tokens.insert(token_id.to_string(), token_info);
|
||
|
||
// 更新所有者资产列表
|
||
self.state
|
||
.owner_tokens
|
||
.entry(to.to_string())
|
||
.or_insert_with(Vec::new)
|
||
.push(token_id.to_string());
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn burn(&mut self, token_id: &str) -> Result<(), ACC721Error> {
|
||
// 检查资产是否存在
|
||
let token_info = self.state
|
||
.tokens
|
||
.get(token_id)
|
||
.ok_or(ACC721Error::TokenNotFound)?;
|
||
|
||
let owner = token_info.owner.clone();
|
||
|
||
// 删除资产
|
||
self.state.tokens.remove(token_id);
|
||
|
||
// 更新所有者资产列表
|
||
if let Some(owner_tokens) = self.state.owner_tokens.get_mut(&owner) {
|
||
owner_tokens.retain(|id| id != token_id);
|
||
}
|
||
|
||
// 清除授权
|
||
self.state.token_approvals.remove(token_id);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn freeze_token(&mut self, token_id: &str) -> Result<(), ACC721Error> {
|
||
let token_info = self.state
|
||
.tokens
|
||
.get_mut(token_id)
|
||
.ok_or(ACC721Error::TokenNotFound)?;
|
||
|
||
token_info.frozen = true;
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn unfreeze_token(&mut self, token_id: &str) -> Result<(), ACC721Error> {
|
||
let token_info = self.state
|
||
.tokens
|
||
.get_mut(token_id)
|
||
.ok_or(ACC721Error::TokenNotFound)?;
|
||
|
||
token_info.frozen = false;
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn is_frozen(&self, token_id: &str) -> bool {
|
||
self.state
|
||
.tokens
|
||
.get(token_id)
|
||
.map(|info| info.frozen)
|
||
.unwrap_or(false)
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
fn create_test_collection() -> ACC721Token {
|
||
let metadata = ACC721Metadata {
|
||
name: "Test Collection".to_string(),
|
||
symbol: "TEST".to_string(),
|
||
base_uri: "https://example.com/".to_string(),
|
||
};
|
||
ACC721Token::new(metadata)
|
||
}
|
||
|
||
#[test]
|
||
fn test_metadata() {
|
||
let collection = create_test_collection();
|
||
assert_eq!(collection.name(), "Test Collection");
|
||
assert_eq!(collection.symbol(), "TEST");
|
||
}
|
||
|
||
#[test]
|
||
fn test_mint_and_owner_of() {
|
||
let mut collection = create_test_collection();
|
||
|
||
// 铸造资产
|
||
assert!(collection.mint("alice", "1", "https://example.com/1").is_ok());
|
||
assert_eq!(collection.owner_of("1").unwrap(), "alice");
|
||
assert_eq!(collection.balance_of("alice"), 1);
|
||
|
||
// 重复铸造
|
||
assert_eq!(
|
||
collection.mint("alice", "1", "https://example.com/1"),
|
||
Err(ACC721Error::TokenAlreadyExists)
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn test_transfer() {
|
||
let mut collection = create_test_collection();
|
||
|
||
// 铸造资产
|
||
collection.mint("alice", "1", "https://example.com/1").unwrap();
|
||
|
||
// 转移资产
|
||
assert!(collection.transfer_from("alice", "bob", "1").is_ok());
|
||
assert_eq!(collection.owner_of("1").unwrap(), "bob");
|
||
assert_eq!(collection.balance_of("alice"), 0);
|
||
assert_eq!(collection.balance_of("bob"), 1);
|
||
}
|
||
|
||
#[test]
|
||
fn test_approve() {
|
||
let mut collection = create_test_collection();
|
||
|
||
// 铸造资产
|
||
collection.mint("alice", "1", "https://example.com/1").unwrap();
|
||
|
||
// 授权
|
||
assert!(collection.approve("bob", "1").is_ok());
|
||
assert_eq!(collection.get_approved("1").unwrap(), Some("bob".to_string()));
|
||
}
|
||
|
||
#[test]
|
||
fn test_burn() {
|
||
let mut collection = create_test_collection();
|
||
|
||
// 铸造资产
|
||
collection.mint("alice", "1", "https://example.com/1").unwrap();
|
||
|
||
// 销毁资产
|
||
assert!(collection.burn("1").is_ok());
|
||
assert_eq!(collection.owner_of("1"), Err(ACC721Error::TokenNotFound));
|
||
assert_eq!(collection.balance_of("alice"), 0);
|
||
}
|
||
|
||
#[test]
|
||
fn test_freeze() {
|
||
let mut collection = create_test_collection();
|
||
|
||
// 铸造资产
|
||
collection.mint("alice", "1", "https://example.com/1").unwrap();
|
||
|
||
// 冻结资产
|
||
assert!(collection.freeze_token("1").is_ok());
|
||
assert!(collection.is_frozen("1"));
|
||
|
||
// 冻结资产无法转移
|
||
assert_eq!(
|
||
collection.transfer_from("alice", "bob", "1"),
|
||
Err(ACC721Error::AssetFrozen)
|
||
);
|
||
|
||
// 解冻资产
|
||
assert!(collection.unfreeze_token("1").is_ok());
|
||
assert!(!collection.is_frozen("1"));
|
||
|
||
// 解冻后可以转移
|
||
assert!(collection.transfer_from("alice", "bob", "1").is_ok());
|
||
}
|
||
}
|