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}