1use std::collections::{HashMap, HashSet};
2use wasm_bindgen::prelude::*;
3use web_sys::console;
4
5#[wasm_bindgen]
7pub struct ERC1155 {
8    access_control: AccessControl,
9    balances: HashMap<(String, u32), u64>,  approvals: HashMap<String, HashMap<String, bool>>, reentrancy_guard: ReentrancyGuard,
12}
13
14pub struct ReentrancyGuard {
16    is_locked: bool,
17}
18
19impl ReentrancyGuard {
20    pub fn new() -> Self {
22        Self { is_locked: false }
23    }
24
25    pub fn enter(&mut self) -> Result<(), String> {
27        if self.is_locked {
28            return Err("Reentrancy detected.".into());
29        }
30        self.is_locked = true;
31        Ok(())
32    }
33
34    pub fn exit(&mut self) {
36        self.is_locked = false;
37    }
38}
39
40pub struct AccessControl {
42    owner: String,
43    admins: HashSet<String>,
44}
45
46impl AccessControl {
47    pub fn new(owner: String) -> Self {
49        Self {
50            owner,
51            admins: HashSet::new(),
52        }
53    }
54
55    pub fn is_owner(&self, caller: &str) -> bool {
57        self.owner == caller
58    }
59
60    pub fn is_admin(&self, caller: &str) -> bool {
62        self.admins.contains(caller)
63    }
64
65    pub fn add_admin(&mut self, caller: &str, new_admin: &str) -> Result<(), String> {
67        if !self.is_owner(caller) {
68            return Err("Only the owner can add admins.".into());
69        }
70        self.admins.insert(new_admin.to_string());
71        Ok(())
72    }
73}
74
75#[wasm_bindgen]
76impl ERC1155 {
77    #[wasm_bindgen(constructor)]
81    pub fn new(owner: &str) -> ERC1155 {
82        console::log_1(&format!("ERC1155 initialized with owner: {}", owner).into());
83        ERC1155 {
84            access_control: AccessControl::new(owner.to_string()),
85            balances: HashMap::new(),
86            approvals: HashMap::new(),
87            reentrancy_guard: ReentrancyGuard::new(),
88        }
89    }
90
91    pub fn mint(&mut self, caller: &str, token_id: u32, amount: u64) -> Result<(), String> {
97        if !self.access_control.is_admin(caller) {
98            console::log_1(&format!("Mint failed: {} is not an admin", caller).into());
99            return Err("Caller is not authorized to mint tokens.".into());
100        }
101
102        self.reentrancy_guard.enter()?; let balance = self.balances.entry((caller.to_string(), token_id)).or_insert(0);
105        *balance += amount;
106
107        console::log_1(&format!("Minted {} tokens of ID {} to {}", amount, token_id, caller).into());
108        self.reentrancy_guard.exit(); Ok(())
111    }
112
113    pub fn transfer(&mut self, caller: &str, to: &str, token_id: u32, amount: u64) -> Result<(), String> {
120        if !self.is_approved(caller, token_id) && !self.access_control.is_owner(caller) {
122            console::log_1(&format!("Transfer failed: {} is not approved or the owner.", caller).into());
123            return Err("Caller is not authorized to transfer.".into());
124        }
125
126        let balance = self.balances.entry((caller.to_string(), token_id)).or_insert(0);
128        if *balance < amount {
129            return Err("Insufficient balance.".into());
130        }
131        *balance -= amount;
132        let recipient_balance = self.balances.entry((to.to_string(), token_id)).or_insert(0);
133        *recipient_balance += amount;
134
135        console::log_1(&format!("Transferred {} tokens of ID {} from {} to {}", amount, token_id, caller, to).into());
136        Ok(())
137    }
138
139    pub fn approve(&mut self, caller: &str, approved: &str, token_id: u32) -> Result<(), String> {
141        let approval_entry = self.approvals.entry(caller.to_string()).or_insert_with(HashMap::new);
142        approval_entry.insert(approved.to_string(), true);
143
144        console::log_1(&format!("Approval set for {} to transfer token ID {} by {}", approved, token_id, caller).into());
145        Ok(())
146    }
147
148    pub fn balance_of(&self, owner: &str, token_id: u32) -> u64 {
150        *self.balances.get(&(owner.to_string(), token_id)).unwrap_or(&0)
151    }
152
153    pub fn add_admin(&mut self, caller: &str, new_admin: &str) -> Result<(), String> {
155        self.access_control.add_admin(caller, new_admin)
156    }
157
158    pub fn transfer_ownership(&mut self, caller: &str, new_owner: &str) -> Result<(), String> {
160        if self.access_control.is_owner(caller) {
161            self.access_control = AccessControl::new(new_owner.to_string());
162            console::log_1(&format!("Ownership transferred to {}", new_owner).into());
163            Ok(())
164        } else {
165            Err("Caller is not authorized to transfer ownership.".into())
166        }
167    }
168
169    fn is_approved(&self, caller: &str, token_id: u32) -> bool {
171        if let Some(approval_map) = self.approvals.get(caller) {
172            return *approval_map.get(&token_id.to_string()).unwrap_or(&false);
173        }
174        false
175    }
176}