use std::collections::HashMap;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct ERC20 {
name: String,
symbol: String,
total_supply: u64, max_supply: u64, balances: HashMap<String, u64>, allowances: HashMap<String, HashMap<String, u64>>, }
#[wasm_bindgen]
impl ERC20 {
#[wasm_bindgen(constructor)]
pub fn new(name: &str, symbol: &str, initial_supply: u64, max_supply: u64) -> ERC20 {
let mut balances = HashMap::new();
balances.insert("owner".to_string(), initial_supply);
ERC20 {
name: name.to_string(),
symbol: symbol.to_string(),
total_supply: initial_supply,
max_supply,
balances,
allowances: HashMap::new(),
}
}
pub fn name(&self) -> String {
self.name.clone()
}
pub fn symbol(&self) -> String {
self.symbol.clone()
}
pub fn total_supply(&self) -> u64 {
self.total_supply
}
pub fn max_supply(&self) -> u64 {
self.max_supply
}
pub fn balance_of(&self, owner: &str) -> u64 {
*self.balances.get(owner).unwrap_or(&0)
}
pub fn transfer(&mut self, from: &str, to: &str, amount: u64) -> bool {
if from == to || amount == 0 {
return false; }
let sender_balance = self.balances.get(from).unwrap_or(&0);
if *sender_balance < amount {
return false; }
self.balances.insert(from.to_string(), sender_balance - amount);
let recipient_balance = self.balances.get(to).unwrap_or(&0);
self.balances.insert(to.to_string(), recipient_balance + amount);
self.log_event("Transfer", &format!("From: {}, To: {}, Amount: {}", from, to, amount));
true
}
pub fn approve(&mut self, owner: &str, spender: &str, amount: u64) -> bool {
if owner == spender {
return false; }
let owner_allowances = self.allowances.entry(owner.to_string()).or_insert(HashMap::new());
owner_allowances.insert(spender.to_string(), amount);
self.log_event("Approval", &format!("Owner: {}, Spender: {}, Amount: {}", owner, spender, amount));
true
}
pub fn allowance(&self, owner: &str, spender: &str) -> u64 {
self.allowances
.get(owner)
.and_then(|allowances| allowances.get(spender))
.cloned()
.unwrap_or(0)
}
pub fn transfer_from(&mut self, owner: &str, spender: &str, to: &str, amount: u64) -> bool {
let allowance = self.allowance(owner, spender);
if allowance < amount {
return false; }
if self.transfer(owner, to, amount) {
self.approve(owner, spender, allowance - amount); return true;
}
false
}
pub fn burn(&mut self, owner: &str, amount: u64) -> bool {
let owner_balance = self.balances.get(owner).unwrap_or(&0);
if *owner_balance < amount {
return false; }
self.balances.insert(owner.to_string(), owner_balance - amount);
self.total_supply -= amount;
self.log_event("Burn", &format!("Owner: {}, Amount: {}", owner, amount));
true
}
pub fn mint(&mut self, recipient: &str, amount: u64) -> bool {
if self.total_supply + amount > self.max_supply {
return false; }
let recipient_balance = self.balances.get(recipient).unwrap_or(&0);
self.balances.insert(recipient.to_string(), recipient_balance + amount);
self.total_supply += amount;
self.log_event("Mint", &format!("Recipient: {}, Amount: {}", recipient, amount));
true
}
fn log_event(&self, event_name: &str, details: &str) {
web_sys::console::log_2(&event_name.into(), &details.into());
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_initial_supply() {
let token = ERC20::new("Spawn Token", "SPN", 1000, 2000);
assert_eq!(token.balance_of("owner"), 1000);
}
#[test]
fn test_transfer() {
let mut token = ERC20::new("Spawn Token", "SPN", 1000, 2000);
let success = token.transfer("owner", "user1", 200);
assert!(success);
assert_eq!(token.balance_of("user1"), 200);
assert_eq!(token.balance_of("owner"), 800);
}
#[test]
fn test_approve_and_transfer_from() {
let mut token = ERC20::new("Spawn Token", "SPN", 1000, 2000);
token.approve("owner", "user1", 100);
let success = token.transfer_from("owner", "user1", "user2", 50);
assert!(success);
assert_eq!(token.balance_of("user2"), 50);
assert_eq!(token.allowance("owner", "user1"), 50);
}
#[test]
fn test_burn() {
let mut token = ERC20::new("Spawn Token", "SPN", 1000, 2000);
let success = token.burn("owner", 100);
assert!(success);
assert_eq!(token.total_supply(), 900);
assert_eq!(token.balance_of("owner"), 900);
}
#[test]
fn test_mint() {
let mut token = ERC20::new("Spawn Token", "SPN", 1000, 2000);
let success = token.mint("user1", 200);
assert!(success);
assert_eq!(token.total_supply(), 1200);
assert_eq!(token.balance_of("user1"), 200);
}
#[test]
fn test_max_supply_limit() {
let mut token = ERC20::new("Spawn Token", "SPN", 1000, 1500);
let success = token.mint("user1", 600);
assert!(!success); assert_eq!(token.total_supply(), 1000);
}
}