// ACC-20: 可替代资产协议(Fungible Token Protocol) // // NAC原生的可替代资产标准,专为RWA资产设计 // 不是ERC-20的实现,是完全独立的NAC原生协议 use serde::{Deserialize, Serialize}; use std::collections::HashMap; /// ACC-20错误类型 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum ACC20Error { /// 余额不足 InsufficientBalance, /// 授权额度不足 InsufficientAllowance, /// 无效的接收地址 InvalidRecipient, /// 无效的金额 InvalidAmount, /// 资产已冻结 AssetFrozen, /// 未授权操作 Unauthorized, /// 合规检查失败 ComplianceFailed(String), } /// ACC-20资产元数据 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ACC20Metadata { /// 资产名称 pub name: String, /// 资产符号 pub symbol: String, /// 小数位数 pub decimals: u8, /// 总供应量 pub total_supply: u128, /// 资产DNA(NAC原生) pub asset_dna: Option, /// GNACS分类编码(NAC原生) pub gnacs_code: Option, /// 是否可铸造 pub mintable: bool, /// 是否可销毁 pub burnable: bool, } /// ACC-20资产状态 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ACC20State { /// 账户余额映射 pub balances: HashMap, /// 授权额度映射 (owner -> spender -> amount) pub allowances: HashMap>, /// 冻结账户列表 pub frozen_accounts: Vec, /// 元数据 pub metadata: ACC20Metadata, } /// ACC-20接口 pub trait ACC20 { /// 获取资产名称 fn name(&self) -> String; /// 获取资产符号 fn symbol(&self) -> String; /// 获取小数位数 fn decimals(&self) -> u8; /// 获取总供应量 fn total_supply(&self) -> u128; /// 获取账户余额 fn balance_of(&self, account: &str) -> u128; /// 转账 fn transfer(&mut self, from: &str, to: &str, amount: u128) -> Result<(), ACC20Error>; /// 授权 fn approve(&mut self, owner: &str, spender: &str, amount: u128) -> Result<(), ACC20Error>; /// 获取授权额度 fn allowance(&self, owner: &str, spender: &str) -> u128; /// 从授权额度中转账 fn transfer_from( &mut self, spender: &str, from: &str, to: &str, amount: u128, ) -> Result<(), ACC20Error>; /// 铸造(仅限授权地址) fn mint(&mut self, to: &str, amount: u128) -> Result<(), ACC20Error>; /// 销毁 fn burn(&mut self, from: &str, amount: u128) -> Result<(), ACC20Error>; /// 冻结账户 fn freeze_account(&mut self, account: &str) -> Result<(), ACC20Error>; /// 解冻账户 fn unfreeze_account(&mut self, account: &str) -> Result<(), ACC20Error>; /// 检查账户是否冻结 fn is_frozen(&self, account: &str) -> bool; } /// ACC-20标准实现 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ACC20Token { state: ACC20State, } impl ACC20Token { /// 创建新的ACC-20资产 pub fn new(metadata: ACC20Metadata) -> Self { Self { state: ACC20State { balances: HashMap::new(), allowances: HashMap::new(), frozen_accounts: Vec::new(), metadata, }, } } /// 获取状态的可变引用 pub fn state_mut(&mut self) -> &mut ACC20State { &mut self.state } /// 获取状态的不可变引用 pub fn state(&self) -> &ACC20State { &self.state } } impl ACC20 for ACC20Token { fn name(&self) -> String { self.state.metadata.name.clone() } fn symbol(&self) -> String { self.state.metadata.symbol.clone() } fn decimals(&self) -> u8 { self.state.metadata.decimals } fn total_supply(&self) -> u128 { self.state.metadata.total_supply } fn balance_of(&self, account: &str) -> u128 { *self.state.balances.get(account).unwrap_or(&0) } fn transfer(&mut self, from: &str, to: &str, amount: u128) -> Result<(), ACC20Error> { // 检查账户是否冻结 if self.is_frozen(from) || self.is_frozen(to) { return Err(ACC20Error::AssetFrozen); } // 检查金额有效性 if amount == 0 { return Err(ACC20Error::InvalidAmount); } // 检查余额 let from_balance = self.balance_of(from); if from_balance < amount { return Err(ACC20Error::InsufficientBalance); } // 执行转账 self.state.balances.insert(from.to_string(), from_balance - amount); let to_balance = self.balance_of(to); self.state.balances.insert(to.to_string(), to_balance + amount); Ok(()) } fn approve(&mut self, owner: &str, spender: &str, amount: u128) -> Result<(), ACC20Error> { // 检查账户是否冻结 if self.is_frozen(owner) || self.is_frozen(spender) { return Err(ACC20Error::AssetFrozen); } // 设置授权额度 self.state .allowances .entry(owner.to_string()) .or_insert_with(HashMap::new) .insert(spender.to_string(), amount); Ok(()) } fn allowance(&self, owner: &str, spender: &str) -> u128 { self.state .allowances .get(owner) .and_then(|allowances| allowances.get(spender)) .copied() .unwrap_or(0) } fn transfer_from( &mut self, spender: &str, from: &str, to: &str, amount: u128, ) -> Result<(), ACC20Error> { // 检查账户是否冻结 if self.is_frozen(from) || self.is_frozen(to) || self.is_frozen(spender) { return Err(ACC20Error::AssetFrozen); } // 检查授权额度 let current_allowance = self.allowance(from, spender); if current_allowance < amount { return Err(ACC20Error::InsufficientAllowance); } // 执行转账 self.transfer(from, to, amount)?; // 减少授权额度 self.state .allowances .get_mut(from) .unwrap() .insert(spender.to_string(), current_allowance - amount); Ok(()) } fn mint(&mut self, to: &str, amount: u128) -> Result<(), ACC20Error> { // 检查是否可铸造 if !self.state.metadata.mintable { return Err(ACC20Error::Unauthorized); } // 检查账户是否冻结 if self.is_frozen(to) { return Err(ACC20Error::AssetFrozen); } // 增加余额 let to_balance = self.balance_of(to); self.state.balances.insert(to.to_string(), to_balance + amount); // 增加总供应量 self.state.metadata.total_supply += amount; Ok(()) } fn burn(&mut self, from: &str, amount: u128) -> Result<(), ACC20Error> { // 检查是否可销毁 if !self.state.metadata.burnable { return Err(ACC20Error::Unauthorized); } // 检查账户是否冻结 if self.is_frozen(from) { return Err(ACC20Error::AssetFrozen); } // 检查余额 let from_balance = self.balance_of(from); if from_balance < amount { return Err(ACC20Error::InsufficientBalance); } // 减少余额 self.state.balances.insert(from.to_string(), from_balance - amount); // 减少总供应量 self.state.metadata.total_supply -= amount; Ok(()) } fn freeze_account(&mut self, account: &str) -> Result<(), ACC20Error> { if !self.is_frozen(account) { self.state.frozen_accounts.push(account.to_string()); } Ok(()) } fn unfreeze_account(&mut self, account: &str) -> Result<(), ACC20Error> { self.state.frozen_accounts.retain(|a| a != account); Ok(()) } fn is_frozen(&self, account: &str) -> bool { self.state.frozen_accounts.contains(&account.to_string()) } } #[cfg(test)] mod tests { use super::*; fn create_test_token() -> ACC20Token { let metadata = ACC20Metadata { name: "Test Token".to_string(), symbol: "TEST".to_string(), decimals: 18, total_supply: 1000000, asset_dna: None, gnacs_code: None, mintable: true, burnable: true, }; ACC20Token::new(metadata) } #[test] fn test_metadata() { let token = create_test_token(); assert_eq!(token.name(), "Test Token"); assert_eq!(token.symbol(), "TEST"); assert_eq!(token.decimals(), 18); assert_eq!(token.total_supply(), 1000000); } #[test] fn test_transfer() { let mut token = create_test_token(); // 初始化账户余额 token.state_mut().balances.insert("alice".to_string(), 1000); // 转账 assert!(token.transfer("alice", "bob", 500).is_ok()); assert_eq!(token.balance_of("alice"), 500); assert_eq!(token.balance_of("bob"), 500); // 余额不足 assert_eq!( token.transfer("alice", "bob", 1000), Err(ACC20Error::InsufficientBalance) ); } #[test] fn test_approve_and_transfer_from() { let mut token = create_test_token(); // 初始化账户余额 token.state_mut().balances.insert("alice".to_string(), 1000); // 授权 assert!(token.approve("alice", "bob", 500).is_ok()); assert_eq!(token.allowance("alice", "bob"), 500); // 从授权额度中转账 assert!(token.transfer_from("bob", "alice", "charlie", 300).is_ok()); assert_eq!(token.balance_of("alice"), 700); assert_eq!(token.balance_of("charlie"), 300); assert_eq!(token.allowance("alice", "bob"), 200); // 授权额度不足 assert_eq!( token.transfer_from("bob", "alice", "charlie", 300), Err(ACC20Error::InsufficientAllowance) ); } #[test] fn test_mint_and_burn() { let mut token = create_test_token(); // 铸造 assert!(token.mint("alice", 1000).is_ok()); assert_eq!(token.balance_of("alice"), 1000); assert_eq!(token.total_supply(), 1001000); // 销毁 assert!(token.burn("alice", 500).is_ok()); assert_eq!(token.balance_of("alice"), 500); assert_eq!(token.total_supply(), 1000500); } #[test] fn test_freeze_and_unfreeze() { let mut token = create_test_token(); // 初始化账户余额 token.state_mut().balances.insert("alice".to_string(), 1000); // 冻结账户 assert!(token.freeze_account("alice").is_ok()); assert!(token.is_frozen("alice")); // 冻结账户无法转账 assert_eq!( token.transfer("alice", "bob", 500), Err(ACC20Error::AssetFrozen) ); // 解冻账户 assert!(token.unfreeze_account("alice").is_ok()); assert!(!token.is_frozen("alice")); // 解冻后可以转账 assert!(token.transfer("alice", "bob", 500).is_ok()); } }