1#![warn(missing_docs)]
14#![no_std]
15
16extern crate alloc;
17
18pub mod address;
19mod allowances;
20mod balances;
21pub mod constants;
22mod detail;
23pub mod entry_points;
24mod error;
25mod total_supply;
26
27use alloc::{
28 collections::BTreeMap,
29 string::{String, ToString},
30};
31
32use once_cell::unsync::OnceCell;
33
34use casper_contract::{
35 contract_api::{runtime, storage},
36 unwrap_or_revert::UnwrapOrRevert,
37};
38use casper_types::{contracts::NamedKeys, EntryPoints, Key, URef, U256};
39
40pub use address::Address;
41use constants::{
42 ALLOWANCES_KEY_NAME, BALANCES_KEY_NAME, DECIMALS_KEY_NAME, ERC20_TOKEN_CONTRACT_KEY_NAME,
43 NAME_KEY_NAME, SYMBOL_KEY_NAME, TOTAL_SUPPLY_KEY_NAME,
44};
45pub use error::Error;
46
47#[derive(Default)]
49pub struct ERC20 {
50 balances_uref: OnceCell<URef>,
51 allowances_uref: OnceCell<URef>,
52 total_supply_uref: OnceCell<URef>,
53}
54
55impl ERC20 {
56 fn new(balances_uref: URef, allowances_uref: URef, total_supply_uref: URef) -> Self {
57 Self {
58 balances_uref: balances_uref.into(),
59 allowances_uref: allowances_uref.into(),
60 total_supply_uref: total_supply_uref.into(),
61 }
62 }
63
64 fn total_supply_uref(&self) -> URef {
65 *self
66 .total_supply_uref
67 .get_or_init(total_supply::total_supply_uref)
68 }
69
70 fn read_total_supply(&self) -> U256 {
71 total_supply::read_total_supply_from(self.total_supply_uref())
72 }
73
74 fn write_total_supply(&self, total_supply: U256) {
75 total_supply::write_total_supply_to(self.total_supply_uref(), total_supply)
76 }
77
78 fn balances_uref(&self) -> URef {
79 *self.balances_uref.get_or_init(balances::get_balances_uref)
80 }
81
82 fn read_balance(&self, owner: Address) -> U256 {
83 balances::read_balance_from(self.balances_uref(), owner)
84 }
85
86 fn write_balance(&mut self, owner: Address, amount: U256) {
87 balances::write_balance_to(self.balances_uref(), owner, amount)
88 }
89
90 fn allowances_uref(&self) -> URef {
91 *self
92 .allowances_uref
93 .get_or_init(allowances::allowances_uref)
94 }
95
96 fn read_allowance(&self, owner: Address, spender: Address) -> U256 {
97 allowances::read_allowance_from(self.allowances_uref(), owner, spender)
98 }
99
100 fn write_allowance(&mut self, owner: Address, spender: Address, amount: U256) {
101 allowances::write_allowance_to(self.allowances_uref(), owner, spender, amount)
102 }
103
104 fn transfer_balance(
105 &mut self,
106 sender: Address,
107 recipient: Address,
108 amount: U256,
109 ) -> Result<(), Error> {
110 balances::transfer_balance(self.balances_uref(), sender, recipient, amount)
111 }
112
113 pub fn install(
117 name: String,
118 symbol: String,
119 decimals: u8,
120 initial_supply: U256,
121 ) -> Result<ERC20, Error> {
122 let default_entry_points = entry_points::default();
123 ERC20::install_custom(
124 name,
125 symbol,
126 decimals,
127 initial_supply,
128 ERC20_TOKEN_CONTRACT_KEY_NAME,
129 default_entry_points,
130 )
131 }
132
133 pub fn name(&self) -> String {
135 detail::read_from(NAME_KEY_NAME)
136 }
137
138 pub fn symbol(&self) -> String {
140 detail::read_from(SYMBOL_KEY_NAME)
141 }
142
143 pub fn decimals(&self) -> u8 {
145 detail::read_from(DECIMALS_KEY_NAME)
146 }
147
148 pub fn total_supply(&self) -> U256 {
150 self.read_total_supply()
151 }
152
153 pub fn balance_of(&self, owner: Address) -> U256 {
155 self.read_balance(owner)
156 }
157
158 pub fn transfer(&mut self, recipient: Address, amount: U256) -> Result<(), Error> {
160 let sender = detail::get_immediate_caller_address()?;
161 self.transfer_balance(sender, recipient, amount)
162 }
163
164 pub fn transfer_from(
167 &mut self,
168 owner: Address,
169 recipient: Address,
170 amount: U256,
171 ) -> Result<(), Error> {
172 let spender = detail::get_immediate_caller_address()?;
173 if amount.is_zero() {
174 return Ok(());
175 }
176 let spender_allowance = self.read_allowance(owner, spender);
177 let new_spender_allowance = spender_allowance
178 .checked_sub(amount)
179 .ok_or(Error::InsufficientAllowance)?;
180 self.transfer_balance(owner, recipient, amount)?;
181 self.write_allowance(owner, spender, new_spender_allowance);
182 Ok(())
183 }
184
185 pub fn _approve(
187 &mut self,
188 owner: Address,
189 spender: Address,
190 amount: U256,
191 ) -> Result<(), Error> {
192 self.write_allowance(owner, spender, amount);
193 Ok(())
194 }
195
196 pub fn approve(&mut self, spender: Address, amount: U256) -> Result<(), Error> {
198 let owner = detail::get_immediate_caller_address()?;
199 self._approve(owner, spender, amount)
200 }
201
202 pub fn allowance(&self, owner: Address, spender: Address) -> U256 {
204 self.read_allowance(owner, spender)
205 }
206
207 pub fn increase_allowance(&mut self, spender: Address, amount: U256) -> Result<(), Error> {
209 let owner = detail::get_immediate_caller_address()?;
210 let spender_allowance = self.read_allowance(owner, spender);
211 let new_allowance = spender_allowance
212 .checked_add(amount)
213 .ok_or(Error::Overflow)?;
214 if owner != spender {
215 self.approve(spender, new_allowance)?;
216 Ok(())
217 } else {
218 Err(Error::InvalidContext)
219 }
220 }
221
222 pub fn decrease_allowance(&mut self, spender: Address, amount: U256) -> Result<(), Error> {
224 let owner = detail::get_immediate_caller_address()?;
225 let spender_allowance = self.read_allowance(owner, spender);
226 let new_allowance: U256 = spender_allowance
227 .checked_sub(amount)
228 .ok_or(Error::Overflow)?;
229 if new_allowance >= 0.into() && new_allowance < spender_allowance && owner != spender {
230 self.approve(spender, new_allowance)?;
231 Ok(())
232 } else {
233 Err(Error::InvalidContext)
234 }
235 }
236
237 pub fn mint(&mut self, owner: Address, amount: U256) -> Result<(), Error> {
244 let new_balance = {
245 let balance = self.read_balance(owner);
246 balance.checked_add(amount).ok_or(Error::Overflow)?
247 };
248 let new_total_supply = {
249 let total_supply: U256 = self.read_total_supply();
250 total_supply.checked_add(amount).ok_or(Error::Overflow)?
251 };
252 self.write_balance(owner, new_balance);
253 self.write_total_supply(new_total_supply);
254 Ok(())
255 }
256
257 pub fn burn(&mut self, owner: Address, amount: U256) -> Result<(), Error> {
265 let new_balance = {
266 let balance = self.read_balance(owner);
267 balance
268 .checked_sub(amount)
269 .ok_or(Error::InsufficientBalance)?
270 };
271 let new_total_supply = {
272 let total_supply = self.read_total_supply();
273 total_supply.checked_sub(amount).ok_or(Error::Overflow)?
274 };
275 self.write_balance(owner, new_balance);
276 self.write_total_supply(new_total_supply);
277 Ok(())
278 }
279
280 pub fn named_keys(
287 &self,
288 name: String,
289 symbol: String,
290 decimals: u8,
291 initial_supply: U256,
292 ) -> Result<BTreeMap<String, Key>, Error> {
293 let balances_uref = storage::new_dictionary(BALANCES_KEY_NAME).unwrap_or_revert();
294 let allowances_uref = storage::new_dictionary(ALLOWANCES_KEY_NAME).unwrap_or_revert();
295 let total_supply_uref = storage::new_uref(initial_supply).into_read_write();
297
298 let mut named_keys = NamedKeys::new();
299
300 let name_key = {
301 let name_uref = storage::new_uref(name).into_read();
302 Key::from(name_uref)
303 };
304
305 let symbol_key = {
306 let symbol_uref = storage::new_uref(symbol).into_read();
307 Key::from(symbol_uref)
308 };
309
310 let decimals_key = {
311 let decimals_uref = storage::new_uref(decimals).into_read();
312 Key::from(decimals_uref)
313 };
314
315 let total_supply_key = Key::from(total_supply_uref);
316
317 let balances_dictionary_key = {
318 let caller = detail::get_caller_address()?;
320 balances::write_balance_to(balances_uref, caller, initial_supply);
321
322 runtime::remove_key(BALANCES_KEY_NAME);
323
324 Key::from(balances_uref)
325 };
326
327 let allowances_dictionary_key = {
328 runtime::remove_key(ALLOWANCES_KEY_NAME);
329
330 Key::from(allowances_uref)
331 };
332
333 named_keys.insert(NAME_KEY_NAME.to_string(), name_key);
334 named_keys.insert(SYMBOL_KEY_NAME.to_string(), symbol_key);
335 named_keys.insert(DECIMALS_KEY_NAME.to_string(), decimals_key);
336 named_keys.insert(BALANCES_KEY_NAME.to_string(), balances_dictionary_key);
337 named_keys.insert(ALLOWANCES_KEY_NAME.to_string(), allowances_dictionary_key);
338 named_keys.insert(TOTAL_SUPPLY_KEY_NAME.to_string(), total_supply_key);
339
340 Ok(named_keys)
341 }
342
343 #[doc(hidden)]
351 pub fn install_custom(
352 name: String,
353 symbol: String,
354 decimals: u8,
355 initial_supply: U256,
356 contract_key_name: &str,
357 entry_points: EntryPoints,
358 ) -> Result<ERC20, Error> {
359 let balances_uref = storage::new_dictionary(BALANCES_KEY_NAME).unwrap_or_revert();
360 let allowances_uref = storage::new_dictionary(ALLOWANCES_KEY_NAME).unwrap_or_revert();
361 let total_supply_uref = storage::new_uref(initial_supply).into_read_write();
363
364 let mut named_keys = NamedKeys::new();
365
366 let name_key = {
367 let name_uref = storage::new_uref(name).into_read();
368 Key::from(name_uref)
369 };
370
371 let symbol_key = {
372 let symbol_uref = storage::new_uref(symbol).into_read();
373 Key::from(symbol_uref)
374 };
375
376 let decimals_key = {
377 let decimals_uref = storage::new_uref(decimals).into_read();
378 Key::from(decimals_uref)
379 };
380
381 let total_supply_key = Key::from(total_supply_uref);
382
383 let balances_dictionary_key = {
384 let caller = detail::get_caller_address()?;
386 balances::write_balance_to(balances_uref, caller, initial_supply);
387
388 runtime::remove_key(BALANCES_KEY_NAME);
389
390 Key::from(balances_uref)
391 };
392
393 let allowances_dictionary_key = {
394 runtime::remove_key(ALLOWANCES_KEY_NAME);
395
396 Key::from(allowances_uref)
397 };
398
399 named_keys.insert(NAME_KEY_NAME.to_string(), name_key);
400 named_keys.insert(SYMBOL_KEY_NAME.to_string(), symbol_key);
401 named_keys.insert(DECIMALS_KEY_NAME.to_string(), decimals_key);
402 named_keys.insert(BALANCES_KEY_NAME.to_string(), balances_dictionary_key);
403 named_keys.insert(ALLOWANCES_KEY_NAME.to_string(), allowances_dictionary_key);
404 named_keys.insert(TOTAL_SUPPLY_KEY_NAME.to_string(), total_supply_key);
405
406 let (contract_hash, _version) =
407 storage::new_locked_contract(entry_points, Some(named_keys), None, None);
408
409 runtime::put_key(contract_key_name, Key::from(contract_hash));
411
412 Ok(ERC20::new(
413 balances_uref,
414 allowances_uref,
415 total_supply_uref,
416 ))
417 }
418}