1use std::collections::{HashMap, HashSet};
2use wasm_bindgen::prelude::*;
3use serde::{Serialize, Deserialize};
4use web_sys::console;
5
6#[wasm_bindgen]
8pub struct NFTManager {
9 owner: String,
10 nft_classes: HashMap<String, NFTClass>,
11 tokens: HashMap<String, Token>,
12 approvals: HashMap<String, HashSet<String>>, }
14
15#[derive(Serialize, Deserialize, Clone)]
17pub struct NFTClass {
18 metadata: String,
19 total_issuance: u32,
20 owner: String,
21}
22
23#[derive(Serialize, Deserialize, Clone)]
25pub struct Token {
26 metadata: String,
27 owner: String,
28 approved: Option<String>, }
30
31#[wasm_bindgen]
32impl NFTManager {
33 #[wasm_bindgen(constructor)]
35 pub fn new(owner: &str) -> NFTManager {
36 console::log_1(&format!("NFT Manager initialized with owner: {}", owner).into());
37 NFTManager {
38 owner: owner.to_string(),
39 nft_classes: HashMap::new(),
40 tokens: HashMap::new(),
41 approvals: HashMap::new(),
42 }
43 }
44
45 fn is_owner(&self, caller: &str) -> Result<bool, String> {
47 if caller == self.owner {
48 Ok(true)
49 } else {
50 Err("Caller is not the contract owner".into())
51 }
52 }
53
54 fn is_owner_of_class(&self, class_id: &str, caller: &str) -> Result<bool, String> {
56 if let Some(class) = self.nft_classes.get(class_id) {
57 if class.owner == caller {
58 Ok(true)
59 } else {
60 Err("Caller is not the class owner".into())
61 }
62 } else {
63 Err("Class not found".into())
64 }
65 }
66
67 pub fn create_class(&mut self, class_id: &str, metadata: &str, caller: &str) -> Result<(), String> {
69 if self.is_owner(caller)? {
70 let class = NFTClass {
71 metadata: metadata.to_string(),
72 total_issuance: 0,
73 owner: caller.to_string(),
74 };
75
76 self.nft_classes.insert(class_id.to_string(), class);
77 console::log_1(&format!("Class created with ID: {}", class_id).into());
78 Ok(())
79 } else {
80 Err("Caller is not authorized to create classes".into())
81 }
82 }
83
84 pub fn mint(&mut self, class_id: &str, token_id: &str, metadata: &str, caller: &str) -> Result<(), String> {
86 if self.is_owner_of_class(class_id, caller)? {
87 let token = Token {
88 metadata: metadata.to_string(),
89 owner: caller.to_string(),
90 approved: None,
91 };
92
93 if let Some(class) = self.nft_classes.get_mut(class_id) {
94 class.total_issuance += 1;
95 self.tokens.insert(token_id.to_string(), token);
96 console::log_1(&format!("Token minted with ID: {}", token_id).into());
97 Ok(())
98 } else {
99 Err("Class not found".into())
100 }
101 } else {
102 Err("Caller is not authorized to mint tokens".into())
103 }
104 }
105
106 pub fn transfer(&mut self, token_id: &str, new_owner: &str, caller: &str) -> Result<(), String> {
108 let owner = self.get_owner(token_id)?;
110
111 if let Some(token) = self.tokens.get_mut(token_id) {
113 if owner == caller {
115 token.owner = new_owner.to_string();
116 token.approved = None; console::log_1(&format!("Token {} transferred to {}", token_id, new_owner).into());
118 Ok(())
119 } else {
120 Err("Caller is not the token owner".into())
121 }
122 } else {
123 Err("Token not found.".into())
124 }
125 }
126
127
128
129 pub fn approve(&mut self, token_id: &str, approved: &str, caller: &str) -> Result<(), String> {
131 if let Some(token) = self.tokens.get_mut(token_id) {
132 if token.owner != caller {
133 console::log_1(&format!("Failed to approve token {}: Caller is not owner", token_id).into());
134 return Err("Only the owner can approve a transfer.".into());
135 }
136
137 token.approved = Some(approved.to_string());
138 self.approvals.entry(token_id.to_string()).or_default().insert(approved.to_string());
139 console::log_1(&format!("Approval set for token {}: {}", token_id, approved).into());
140 Ok(())
141 } else {
142 console::log_1(&format!("Token with ID: {} not found", token_id).into());
143 Err("Token not found.".into())
144 }
145 }
146
147 pub fn burn(&mut self, token_id: &str, caller: &str) -> Result<(), String> {
149 if let Some(token) = self.tokens.get(token_id) {
150 if token.owner != caller {
151 console::log_1(&format!("Failed to burn token {}: Caller is not owner", token_id).into());
152 return Err("Only the owner can burn the token.".into());
153 }
154 }
155
156 if let Some(_token) = self.tokens.remove(token_id) {
157 console::log_1(&format!("Token with ID: {} burned", token_id).into());
158 Ok(())
159 } else {
160 console::log_1(&format!("Token with ID: {} not found", token_id).into());
161 Err("Token not found.".into())
162 }
163 }
164
165 pub fn get_owner(&self, token_id: &str) -> Result<String, String> {
167 if let Some(token) = self.tokens.get(token_id) {
168 Ok(token.owner.clone())
169 } else {
170 console::log_1(&format!("Token with ID: {} not found", token_id).into());
171 Err("Token not found.".into())
172 }
173 }
174
175 pub fn is_approved(&self, token_id: &str, operator: &str) -> bool {
177 if let Some(approvals) = self.approvals.get(token_id) {
178 approvals.contains(operator)
179 } else {
180 false
181 }
182 }
183
184 pub fn transfer_ownership(&mut self, new_owner: &str, caller: &str) -> Result<(), String> {
186 if self.is_owner(caller)? {
187 self.owner = new_owner.to_string();
188 console::log_1(&format!("Ownership transferred to {}", new_owner).into());
189 Ok(())
190 } else {
191 Err("Caller is not authorized to transfer ownership".into())
192 }
193 }
194}