1extern crate alloc;
2
3pub mod address;
4mod allowances;
5mod balances;
6pub mod constants;
7mod detail;
8pub mod entry_points;
9mod error;
10mod total_supply;
11
12use alloc::{
13 collections::BTreeMap,
14 string::{String, ToString},
15};
16
17use once_cell::unsync::OnceCell;
18
19use casper_contract::{
20 contract_api::{runtime, storage},
21 unwrap_or_revert::UnwrapOrRevert,
22};
23use casper_types::{contracts::NamedKeys, EntryPoints, Key, URef, U256};
24
25pub use address::Address;
26use constants::{
27 ALLOWANCES_KEY_NAME, BALANCES_KEY_NAME, DECIMALS_KEY_NAME, ERC20_TOKEN_CONTRACT_KEY_NAME,
28 NAME_KEY_NAME, SYMBOL_KEY_NAME, TOTAL_SUPPLY_KEY_NAME,
29};
30pub use error::Error;
31
32#[derive(Default)]
34pub struct ERC20 {
35 balances_uref: OnceCell<URef>,
36 allowances_uref: OnceCell<URef>,
37 total_supply_uref: OnceCell<URef>,
38}
39
40impl ERC20 {
41 fn new(balances_uref: URef, allowances_uref: URef, total_supply_uref: URef) -> Self {
42 Self {
43 balances_uref: balances_uref.into(),
44 allowances_uref: allowances_uref.into(),
45 total_supply_uref: total_supply_uref.into(),
46 }
47 }
48
49 fn total_supply_uref(&self) -> URef {
50 *self
51 .total_supply_uref
52 .get_or_init(total_supply::total_supply_uref)
53 }
54
55 fn read_total_supply(&self) -> U256 {
56 total_supply::read_total_supply_from(self.total_supply_uref())
57 }
58
59 pub fn write_total_supply(&self, total_supply: U256) {
60 total_supply::write_total_supply_to(self.total_supply_uref(), total_supply)
61 }
62
63 fn balances_uref(&self) -> URef {
64 *self.balances_uref.get_or_init(balances::get_balances_uref)
65 }
66
67 fn read_balance(&self, owner: Address) -> U256 {
68 balances::read_balance_from(self.balances_uref(), owner)
69 }
70
71 pub fn write_balance(&mut self, owner: Address, amount: U256) {
72 balances::write_balance_to(self.balances_uref(), owner, amount)
73 }
74
75 fn allowances_uref(&self) -> URef {
76 *self
77 .allowances_uref
78 .get_or_init(allowances::allowances_uref)
79 }
80
81 fn read_allowance(&self, owner: Address, spender: Address) -> U256 {
82 allowances::read_allowance_from(self.allowances_uref(), owner, spender)
83 }
84
85 pub fn write_allowance(&mut self, owner: Address, spender: Address, amount: U256) {
86 allowances::write_allowance_to(self.allowances_uref(), owner, spender, amount)
87 }
88
89 fn transfer_balance(
90 &mut self,
91 sender: Address,
92 recipient: Address,
93 amount: U256,
94 ) -> Result<(), Error> {
95 balances::transfer_balance(self.balances_uref(), sender, recipient, amount)
96 }
97
98 pub fn install(
102 name: String,
103 symbol: String,
104 decimals: u8,
105 initial_supply: U256,
106 ) -> Result<ERC20, Error> {
107 let default_entry_points = entry_points::default();
108 ERC20::install_custom(
109 name,
110 symbol,
111 decimals,
112 initial_supply,
113 ERC20_TOKEN_CONTRACT_KEY_NAME,
114 default_entry_points,
115 )
116 }
117
118 pub fn name(&self) -> String {
120 detail::read_from(NAME_KEY_NAME)
121 }
122
123 pub fn set_name(&self, value: String) {
125 detail::write_to(NAME_KEY_NAME, value);
126 }
127
128 pub fn symbol(&self) -> String {
130 detail::read_from(SYMBOL_KEY_NAME)
131 }
132
133 pub fn set_symbol(&self, value: String) {
135 detail::write_to(SYMBOL_KEY_NAME, value);
136 }
137
138 pub fn decimals(&self) -> u8 {
140 detail::read_from(DECIMALS_KEY_NAME)
141 }
142
143 pub fn total_supply(&self) -> U256 {
145 self.read_total_supply()
146 }
147
148 pub fn balance_of(&self, owner: Address) -> U256 {
150 self.read_balance(owner)
151 }
152
153 pub fn transfer(&mut self, recipient: Address, amount: U256) -> Result<(), Error> {
155 let sender = detail::get_immediate_caller_address()?;
156 self.transfer_balance(sender, recipient, amount)
157 }
158
159 pub fn transfer_from(
162 &mut self,
163 owner: Address,
164 recipient: Address,
165 amount: U256,
166 ) -> Result<(), Error> {
167 let spender = detail::get_immediate_caller_address()?;
168 if amount.is_zero() {
169 return Ok(());
170 }
171 let spender_allowance = self.read_allowance(owner, spender);
172 let new_spender_allowance = spender_allowance
173 .checked_sub(amount)
174 .ok_or(Error::InsufficientAllowance)?;
175 self.transfer_balance(owner, recipient, amount)?;
176 self.write_allowance(owner, spender, new_spender_allowance);
177 Ok(())
178 }
179
180 pub fn approve(&mut self, spender: Address, amount: U256) -> Result<(), Error> {
182 let owner = detail::get_immediate_caller_address()?;
183 self.write_allowance(owner, spender, amount);
184 Ok(())
185 }
186
187 pub fn allowance(&self, owner: Address, spender: Address) -> U256 {
189 self.read_allowance(owner, spender)
190 }
191
192 pub fn increase_allowance(&mut self, spender: Address, amount: U256) -> Result<(), Error> {
194 let owner = detail::get_immediate_caller_address()?;
195 let spender_allowance = self.read_allowance(owner, spender);
196 let new_allowance = spender_allowance
197 .checked_add(amount)
198 .ok_or(Error::Overflow)?;
199 if owner != spender {
200 self.approve(spender, new_allowance)?;
201 Ok(())
202 } else {
203 Err(Error::InvalidContext)
204 }
205 }
206
207 pub fn decrease_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: U256 = spender_allowance
212 .checked_sub(amount)
213 .ok_or(Error::Overflow)?;
214 if new_allowance >= 0.into() && new_allowance < spender_allowance && owner != spender {
215 self.approve(spender, new_allowance)?;
216 Ok(())
217 } else {
218 Err(Error::InvalidContext)
219 }
220 }
221
222 pub fn mint(&mut self, owner: Address, amount: U256) -> Result<(), Error> {
229 let new_balance = {
230 let balance = self.read_balance(owner);
231 balance.checked_add(amount).ok_or(Error::Overflow)?
232 };
233 let new_total_supply = {
234 let total_supply: U256 = self.read_total_supply();
235 total_supply.checked_add(amount).ok_or(Error::Overflow)?
236 };
237 self.write_balance(owner, new_balance);
238 self.write_total_supply(new_total_supply);
239 Ok(())
240 }
241
242 pub fn burn(&mut self, owner: Address, amount: U256) -> Result<(), Error> {
250 let new_balance = {
251 let balance = self.read_balance(owner);
252 balance
253 .checked_sub(amount)
254 .ok_or(Error::InsufficientBalance)?
255 };
256 let new_total_supply = {
257 let total_supply = self.read_total_supply();
258 total_supply.checked_sub(amount).ok_or(Error::Overflow)?
259 };
260 self.write_balance(owner, new_balance);
261 self.write_total_supply(new_total_supply);
262 Ok(())
263 }
264
265 pub fn named_keys(
272 &self,
273 name: String,
274 symbol: String,
275 decimals: u8,
276 initial_supply: U256,
277 ) -> Result<BTreeMap<String, Key>, Error> {
278 let balances_uref = storage::new_dictionary(BALANCES_KEY_NAME).unwrap_or_revert();
279 let allowances_uref = storage::new_dictionary(ALLOWANCES_KEY_NAME).unwrap_or_revert();
280 let total_supply_uref = storage::new_uref(initial_supply).into_read_write();
282
283 let mut named_keys = NamedKeys::new();
284
285 let name_key = {
286 let name_uref = storage::new_uref(name).into_read_write();
287 Key::from(name_uref)
288 };
289
290 let symbol_key = {
291 let symbol_uref = storage::new_uref(symbol).into_read_write();
292 Key::from(symbol_uref)
293 };
294
295 let decimals_key = {
296 let decimals_uref = storage::new_uref(decimals).into_read();
297 Key::from(decimals_uref)
298 };
299
300 let total_supply_key = Key::from(total_supply_uref);
301
302 let balances_dictionary_key = {
303 let caller = detail::get_caller_address()?;
305 balances::write_balance_to(balances_uref, caller, initial_supply);
306
307 runtime::remove_key(BALANCES_KEY_NAME);
308
309 Key::from(balances_uref)
310 };
311
312 let allowances_dictionary_key = {
313 runtime::remove_key(ALLOWANCES_KEY_NAME);
314
315 Key::from(allowances_uref)
316 };
317
318 named_keys.insert(NAME_KEY_NAME.to_string(), name_key);
319 named_keys.insert(SYMBOL_KEY_NAME.to_string(), symbol_key);
320 named_keys.insert(DECIMALS_KEY_NAME.to_string(), decimals_key);
321 named_keys.insert(BALANCES_KEY_NAME.to_string(), balances_dictionary_key);
322 named_keys.insert(ALLOWANCES_KEY_NAME.to_string(), allowances_dictionary_key);
323 named_keys.insert(TOTAL_SUPPLY_KEY_NAME.to_string(), total_supply_key);
324
325 Ok(named_keys)
326 }
327
328 #[doc(hidden)]
336 pub fn install_custom(
337 name: String,
338 symbol: String,
339 decimals: u8,
340 initial_supply: U256,
341 contract_key_name: &str,
342 entry_points: EntryPoints,
343 ) -> Result<ERC20, Error> {
344 let balances_uref = storage::new_dictionary(BALANCES_KEY_NAME).unwrap_or_revert();
345 let allowances_uref = storage::new_dictionary(ALLOWANCES_KEY_NAME).unwrap_or_revert();
346 let total_supply_uref = storage::new_uref(initial_supply).into_read_write();
348
349 let mut named_keys = NamedKeys::new();
350
351 let name_key = {
352 let name_uref = storage::new_uref(name).into_read();
353 Key::from(name_uref)
354 };
355
356 let symbol_key = {
357 let symbol_uref = storage::new_uref(symbol).into_read();
358 Key::from(symbol_uref)
359 };
360
361 let decimals_key = {
362 let decimals_uref = storage::new_uref(decimals).into_read();
363 Key::from(decimals_uref)
364 };
365
366 let total_supply_key = Key::from(total_supply_uref);
367
368 let balances_dictionary_key = {
369 let caller = detail::get_caller_address()?;
371 balances::write_balance_to(balances_uref, caller, initial_supply);
372
373 runtime::remove_key(BALANCES_KEY_NAME);
374
375 Key::from(balances_uref)
376 };
377
378 let allowances_dictionary_key = {
379 runtime::remove_key(ALLOWANCES_KEY_NAME);
380
381 Key::from(allowances_uref)
382 };
383
384 named_keys.insert(NAME_KEY_NAME.to_string(), name_key);
385 named_keys.insert(SYMBOL_KEY_NAME.to_string(), symbol_key);
386 named_keys.insert(DECIMALS_KEY_NAME.to_string(), decimals_key);
387 named_keys.insert(BALANCES_KEY_NAME.to_string(), balances_dictionary_key);
388 named_keys.insert(ALLOWANCES_KEY_NAME.to_string(), allowances_dictionary_key);
389 named_keys.insert(TOTAL_SUPPLY_KEY_NAME.to_string(), total_supply_key);
390
391 let (contract_hash, _version) =
392 storage::new_locked_contract(entry_points, Some(named_keys), None, None);
393
394 runtime::put_key(contract_key_name, Key::from(contract_hash));
396
397 Ok(ERC20::new(
398 balances_uref,
399 allowances_uref,
400 total_supply_uref,
401 ))
402 }
403}