165 lines
4.5 KiB
Rust
165 lines
4.5 KiB
Rust
//! ACC协议模块
|
||
|
||
///! # ACC-20: 可替代代币标准
|
||
///!
|
||
///! UID: nac.acc.ACC20.v1
|
||
///!
|
||
///! ACC-20是NAC的可替代代币标准,完全替代以太坊的ERC-20。
|
||
|
||
#[allow(unused_imports)]
|
||
use crate::primitives::{Address, Hash, Timestamp};
|
||
use serde::{Deserialize, Serialize};
|
||
use std::collections::HashMap;
|
||
|
||
/// ACC-20代币
|
||
///
|
||
/// UID: nac.acc.ACC20.v1
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
/// ACC20Token
|
||
pub struct ACC20Token {
|
||
/// 代币名称
|
||
pub name: String,
|
||
|
||
/// 代币符号
|
||
pub symbol: String,
|
||
|
||
/// 小数位数
|
||
pub decimals: u8,
|
||
|
||
/// 总供应量
|
||
pub total_supply: u128,
|
||
|
||
/// 合约地址
|
||
pub contract_address: Address,
|
||
|
||
/// 余额映射
|
||
pub balances: HashMap<Address, u128>,
|
||
|
||
/// 授权映射 (owner -> spender -> amount)
|
||
pub allowances: HashMap<Address, HashMap<Address, u128>>,
|
||
|
||
/// 创建时间
|
||
pub created_at: Timestamp,
|
||
}
|
||
|
||
impl ACC20Token {
|
||
/// 创建新的ACC-20代币
|
||
pub fn new(name: String, symbol: String, decimals: u8, total_supply: u128) -> Self {
|
||
Self {
|
||
name,
|
||
symbol,
|
||
decimals,
|
||
total_supply,
|
||
contract_address: Address::zero(),
|
||
balances: HashMap::new(),
|
||
allowances: HashMap::new(),
|
||
created_at: Timestamp::now(),
|
||
}
|
||
}
|
||
|
||
/// 获取余额
|
||
pub fn balance_of(&self, owner: &Address) -> u128 {
|
||
*self.balances.get(owner).unwrap_or(&0)
|
||
}
|
||
|
||
/// 转账
|
||
pub fn transfer(&mut self, from: &Address, to: &Address, amount: u128) -> Result<(), String> {
|
||
let from_balance = self.balance_of(from);
|
||
if from_balance < amount {
|
||
return Err("Insufficient balance".to_string());
|
||
}
|
||
|
||
self.balances.insert(*from, from_balance - amount);
|
||
let to_balance = self.balance_of(to);
|
||
self.balances.insert(*to, to_balance + amount);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 授权
|
||
pub fn approve(&mut self, owner: &Address, spender: &Address, amount: u128) {
|
||
self.allowances
|
||
.entry(*owner)
|
||
.or_insert_with(HashMap::new)
|
||
.insert(*spender, amount);
|
||
}
|
||
|
||
/// 获取授权额度
|
||
pub fn allowance(&self, owner: &Address, spender: &Address) -> u128 {
|
||
self.allowances
|
||
.get(owner)
|
||
.and_then(|spenders| spenders.get(spender))
|
||
.copied()
|
||
.unwrap_or(0)
|
||
}
|
||
|
||
/// 从授权额度转账
|
||
pub fn transfer_from(
|
||
&mut self,
|
||
spender: &Address,
|
||
from: &Address,
|
||
to: &Address,
|
||
amount: u128,
|
||
) -> Result<(), String> {
|
||
let allowed = self.allowance(from, spender);
|
||
if allowed < amount {
|
||
return Err("Insufficient allowance".to_string());
|
||
}
|
||
|
||
self.transfer(from, to, amount)?;
|
||
self.approve(from, spender, allowed - amount);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 铸造代币
|
||
pub fn mint(&mut self, to: &Address, amount: u128) -> Result<(), String> {
|
||
let balance = self.balance_of(to);
|
||
self.balances.insert(*to, balance + amount);
|
||
self.total_supply += amount;
|
||
Ok(())
|
||
}
|
||
|
||
/// 销毁代币
|
||
pub fn burn(&mut self, from: &Address, amount: u128) -> Result<(), String> {
|
||
let balance = self.balance_of(from);
|
||
if balance < amount {
|
||
return Err("Insufficient balance to burn".to_string());
|
||
}
|
||
|
||
self.balances.insert(*from, balance - amount);
|
||
self.total_supply -= amount;
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
|
||
|
||
#[test]
|
||
fn test_acc20_creation() {
|
||
let token = ACC20Token::new("Test Token".to_string(), "TEST".to_string(), 18, 1_000_000);
|
||
assert_eq!(token.name, "Test Token");
|
||
assert_eq!(token.symbol, "TEST");
|
||
assert_eq!(token.decimals, 18);
|
||
assert_eq!(token.total_supply, 1_000_000);
|
||
}
|
||
|
||
#[test]
|
||
fn test_acc20_transfer() {
|
||
let mut token = ACC20Token::new("Test".to_string(), "TST".to_string(), 18, 1_000_000);
|
||
let addr1 = Address::new([1u8; 32]);
|
||
let addr2 = Address::new([2u8; 32]);
|
||
|
||
token.mint(&addr1, 1000).expect("mainnet: handle error");
|
||
assert_eq!(token.balance_of(&addr1), 1000);
|
||
|
||
token.transfer(&addr1, &addr2, 500).expect("mainnet: handle error");
|
||
assert_eq!(token.balance_of(&addr1), 500);
|
||
assert_eq!(token.balance_of(&addr2), 500);
|
||
}
|
||
}
|