335 lines
9.0 KiB
Rust
335 lines
9.0 KiB
Rust
// ACC-1155: 多类型资产协议(Multi-Token Protocol)
|
||
//
|
||
// NAC原生的多类型资产标准,专为RWA资产设计
|
||
// 不是ERC-1155的实现,是完全独立的NAC原生协议
|
||
|
||
use serde::{Deserialize, Serialize};
|
||
use std::collections::HashMap;
|
||
|
||
/// ACC-1155错误类型
|
||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub enum ACC1155Error {
|
||
/// 余额不足
|
||
InsufficientBalance,
|
||
/// 无效的接收地址
|
||
InvalidRecipient,
|
||
/// 无效的金额
|
||
InvalidAmount,
|
||
/// 资产类型不存在
|
||
TokenTypeNotFound,
|
||
/// 未授权操作
|
||
Unauthorized,
|
||
}
|
||
|
||
/// ACC-1155资产状态
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ACC1155State {
|
||
/// 账户余额映射 (account -> token_id -> balance)
|
||
pub balances: HashMap<String, HashMap<String, u128>>,
|
||
/// 操作员授权映射 (owner -> operator -> bool)
|
||
pub operator_approvals: HashMap<String, HashMap<String, bool>>,
|
||
/// 资产URI映射 (token_id -> uri)
|
||
pub token_uris: HashMap<String, String>,
|
||
}
|
||
|
||
/// ACC-1155接口
|
||
pub trait ACC1155 {
|
||
/// 获取账户指定资产类型的余额
|
||
fn balance_of(&self, account: &str, token_id: &str) -> u128;
|
||
|
||
/// 批量获取余额
|
||
fn balance_of_batch(&self, accounts: &[String], token_ids: &[String]) -> Vec<u128>;
|
||
|
||
/// 转移资产
|
||
fn safe_transfer_from(
|
||
&mut self,
|
||
from: &str,
|
||
to: &str,
|
||
token_id: &str,
|
||
amount: u128,
|
||
) -> Result<(), ACC1155Error>;
|
||
|
||
/// 批量转移资产
|
||
fn safe_batch_transfer_from(
|
||
&mut self,
|
||
from: &str,
|
||
to: &str,
|
||
token_ids: &[String],
|
||
amounts: &[u128],
|
||
) -> Result<(), ACC1155Error>;
|
||
|
||
/// 设置操作员授权
|
||
fn set_approval_for_all(
|
||
&mut self,
|
||
owner: &str,
|
||
operator: &str,
|
||
approved: bool,
|
||
) -> Result<(), ACC1155Error>;
|
||
|
||
/// 检查操作员授权
|
||
fn is_approved_for_all(&self, owner: &str, operator: &str) -> bool;
|
||
|
||
/// 铸造资产
|
||
fn mint(&mut self, to: &str, token_id: &str, amount: u128) -> Result<(), ACC1155Error>;
|
||
|
||
/// 批量铸造资产
|
||
fn mint_batch(
|
||
&mut self,
|
||
to: &str,
|
||
token_ids: &[String],
|
||
amounts: &[u128],
|
||
) -> Result<(), ACC1155Error>;
|
||
|
||
/// 销毁资产
|
||
fn burn(&mut self, from: &str, token_id: &str, amount: u128) -> Result<(), ACC1155Error>;
|
||
|
||
/// 获取资产URI
|
||
fn uri(&self, token_id: &str) -> Option<String>;
|
||
}
|
||
|
||
/// ACC-1155标准实现
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ACC1155Token {
|
||
state: ACC1155State,
|
||
}
|
||
|
||
impl ACC1155Token {
|
||
/// 创建新的ACC-1155资产集合
|
||
pub fn new() -> Self {
|
||
Self {
|
||
state: ACC1155State {
|
||
balances: HashMap::new(),
|
||
operator_approvals: HashMap::new(),
|
||
token_uris: HashMap::new(),
|
||
},
|
||
}
|
||
}
|
||
|
||
/// 获取状态的可变引用
|
||
pub fn state_mut(&mut self) -> &mut ACC1155State {
|
||
&mut self.state
|
||
}
|
||
|
||
/// 获取状态的不可变引用
|
||
pub fn state(&self) -> &ACC1155State {
|
||
&self.state
|
||
}
|
||
}
|
||
|
||
impl Default for ACC1155Token {
|
||
fn default() -> Self {
|
||
Self::new()
|
||
}
|
||
}
|
||
|
||
impl ACC1155 for ACC1155Token {
|
||
fn balance_of(&self, account: &str, token_id: &str) -> u128 {
|
||
self.state
|
||
.balances
|
||
.get(account)
|
||
.and_then(|tokens| tokens.get(token_id))
|
||
.copied()
|
||
.unwrap_or(0)
|
||
}
|
||
|
||
fn balance_of_batch(&self, accounts: &[String], token_ids: &[String]) -> Vec<u128> {
|
||
accounts
|
||
.iter()
|
||
.zip(token_ids.iter())
|
||
.map(|(account, token_id)| self.balance_of(account, token_id))
|
||
.collect()
|
||
}
|
||
|
||
fn safe_transfer_from(
|
||
&mut self,
|
||
from: &str,
|
||
to: &str,
|
||
token_id: &str,
|
||
amount: u128,
|
||
) -> Result<(), ACC1155Error> {
|
||
if amount == 0 {
|
||
return Err(ACC1155Error::InvalidAmount);
|
||
}
|
||
|
||
let from_balance = self.balance_of(from, token_id);
|
||
if from_balance < amount {
|
||
return Err(ACC1155Error::InsufficientBalance);
|
||
}
|
||
|
||
// 更新发送方余额
|
||
self.state
|
||
.balances
|
||
.entry(from.to_string())
|
||
.or_insert_with(HashMap::new)
|
||
.insert(token_id.to_string(), from_balance - amount);
|
||
|
||
// 更新接收方余额
|
||
let to_balance = self.balance_of(to, token_id);
|
||
self.state
|
||
.balances
|
||
.entry(to.to_string())
|
||
.or_insert_with(HashMap::new)
|
||
.insert(token_id.to_string(), to_balance + amount);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn safe_batch_transfer_from(
|
||
&mut self,
|
||
from: &str,
|
||
to: &str,
|
||
token_ids: &[String],
|
||
amounts: &[u128],
|
||
) -> Result<(), ACC1155Error> {
|
||
if token_ids.len() != amounts.len() {
|
||
return Err(ACC1155Error::InvalidAmount);
|
||
}
|
||
|
||
for (token_id, &amount) in token_ids.iter().zip(amounts.iter()) {
|
||
self.safe_transfer_from(from, to, token_id, amount)?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn set_approval_for_all(
|
||
&mut self,
|
||
owner: &str,
|
||
operator: &str,
|
||
approved: bool,
|
||
) -> Result<(), ACC1155Error> {
|
||
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, amount: u128) -> Result<(), ACC1155Error> {
|
||
let to_balance = self.balance_of(to, token_id);
|
||
self.state
|
||
.balances
|
||
.entry(to.to_string())
|
||
.or_insert_with(HashMap::new)
|
||
.insert(token_id.to_string(), to_balance + amount);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn mint_batch(
|
||
&mut self,
|
||
to: &str,
|
||
token_ids: &[String],
|
||
amounts: &[u128],
|
||
) -> Result<(), ACC1155Error> {
|
||
if token_ids.len() != amounts.len() {
|
||
return Err(ACC1155Error::InvalidAmount);
|
||
}
|
||
|
||
for (token_id, &amount) in token_ids.iter().zip(amounts.iter()) {
|
||
self.mint(to, token_id, amount)?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn burn(&mut self, from: &str, token_id: &str, amount: u128) -> Result<(), ACC1155Error> {
|
||
let from_balance = self.balance_of(from, token_id);
|
||
if from_balance < amount {
|
||
return Err(ACC1155Error::InsufficientBalance);
|
||
}
|
||
|
||
self.state
|
||
.balances
|
||
.entry(from.to_string())
|
||
.or_insert_with(HashMap::new)
|
||
.insert(token_id.to_string(), from_balance - amount);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn uri(&self, token_id: &str) -> Option<String> {
|
||
self.state.token_uris.get(token_id).cloned()
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_mint_and_balance() {
|
||
let mut token = ACC1155Token::new();
|
||
|
||
assert!(token.mint("alice", "1", 1000).is_ok());
|
||
assert_eq!(token.balance_of("alice", "1"), 1000);
|
||
}
|
||
|
||
#[test]
|
||
fn test_transfer() {
|
||
let mut token = ACC1155Token::new();
|
||
|
||
token.mint("alice", "1", 1000).expect("mainnet: handle error");
|
||
assert!(token.safe_transfer_from("alice", "bob", "1", 500).is_ok());
|
||
|
||
assert_eq!(token.balance_of("alice", "1"), 500);
|
||
assert_eq!(token.balance_of("bob", "1"), 500);
|
||
}
|
||
|
||
#[test]
|
||
fn test_batch_transfer() {
|
||
let mut token = ACC1155Token::new();
|
||
|
||
token.mint("alice", "1", 1000).expect("mainnet: handle error");
|
||
token.mint("alice", "2", 2000).expect("mainnet: handle error");
|
||
|
||
let token_ids = vec!["1".to_string(), "2".to_string()];
|
||
let amounts = vec![500, 1000];
|
||
|
||
assert!(token
|
||
.safe_batch_transfer_from("alice", "bob", &token_ids, &amounts)
|
||
.is_ok());
|
||
|
||
assert_eq!(token.balance_of("alice", "1"), 500);
|
||
assert_eq!(token.balance_of("alice", "2"), 1000);
|
||
assert_eq!(token.balance_of("bob", "1"), 500);
|
||
assert_eq!(token.balance_of("bob", "2"), 1000);
|
||
}
|
||
|
||
#[test]
|
||
fn test_burn() {
|
||
let mut token = ACC1155Token::new();
|
||
|
||
token.mint("alice", "1", 1000).expect("mainnet: handle error");
|
||
assert!(token.burn("alice", "1", 500).is_ok());
|
||
|
||
assert_eq!(token.balance_of("alice", "1"), 500);
|
||
}
|
||
|
||
#[test]
|
||
fn test_approval() {
|
||
let mut token = ACC1155Token::new();
|
||
|
||
assert!(token
|
||
.set_approval_for_all("alice", "bob", true)
|
||
.is_ok());
|
||
assert!(token.is_approved_for_all("alice", "bob"));
|
||
|
||
assert!(token
|
||
.set_approval_for_all("alice", "bob", false)
|
||
.is_ok());
|
||
assert!(!token.is_approved_for_all("alice", "bob"));
|
||
}
|
||
}
|