419 lines
11 KiB
Rust
419 lines
11 KiB
Rust
// 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<String>,
|
||
/// GNACS分类编码(NAC原生)
|
||
pub gnacs_code: Option<String>,
|
||
/// 是否可铸造
|
||
pub mintable: bool,
|
||
/// 是否可销毁
|
||
pub burnable: bool,
|
||
}
|
||
|
||
/// ACC-20资产状态
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ACC20State {
|
||
/// 账户余额映射
|
||
pub balances: HashMap<String, u128>,
|
||
/// 授权额度映射 (owner -> spender -> amount)
|
||
pub allowances: HashMap<String, HashMap<String, u128>>,
|
||
/// 冻结账户列表
|
||
pub frozen_accounts: Vec<String>,
|
||
/// 元数据
|
||
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());
|
||
}
|
||
}
|