1use crate::erc20::errors::Error;
3use crate::erc20::events::*;
4use odra::casper_types::U256;
5use odra::prelude::*;
6
7#[odra::module(events = [Approval, Transfer], errors = Error)]
9pub struct Erc20 {
10 decimals: Var<u8>,
11 symbol: Var<String>,
12 name: Var<String>,
13 total_supply: Var<U256>,
14 balances: Mapping<Address, U256>,
15 allowances: Mapping<(Address, Address), U256>
16}
17
18#[odra::module]
19impl Erc20 {
20 pub fn init(
22 &mut self,
23 symbol: String,
24 name: String,
25 decimals: u8,
26 initial_supply: Option<U256>
27 ) {
28 let caller = self.env().caller();
29 self.symbol.set(symbol);
30 self.name.set(name);
31 self.decimals.set(decimals);
32
33 if let Some(initial_supply) = initial_supply {
34 self.total_supply.set(initial_supply);
35 self.balances.set(&caller, initial_supply);
36
37 if !initial_supply.is_zero() {
38 self.env().emit_event(Transfer {
39 from: None,
40 to: Some(caller),
41 amount: initial_supply
42 });
43 }
44 }
45 }
46
47 pub fn transfer(&mut self, recipient: &Address, amount: &U256) {
49 let caller = self.env().caller();
50 self.raw_transfer(&caller, recipient, amount);
51 }
52
53 pub fn transfer_from(&mut self, owner: &Address, recipient: &Address, amount: &U256) {
55 let spender = self.env().caller();
56
57 self.spend_allowance(owner, &spender, amount);
58 self.raw_transfer(owner, recipient, amount);
59 }
60
61 pub fn approve(&mut self, spender: &Address, amount: &U256) {
63 let owner = self.env().caller();
64
65 self.allowances.set(&(owner, *spender), *amount);
66 self.env().emit_event(Approval {
67 owner,
68 spender: *spender,
69 value: *amount
70 });
71 }
72
73 pub fn name(&self) -> String {
75 self.name.get_or_revert_with(Error::NameNotSet)
76 }
77
78 pub fn symbol(&self) -> String {
80 self.symbol.get_or_revert_with(Error::SymbolNotSet)
81 }
82
83 pub fn decimals(&self) -> u8 {
85 self.decimals.get_or_revert_with(Error::DecimalsNotSet)
86 }
87
88 pub fn total_supply(&self) -> U256 {
90 self.total_supply.get_or_default()
91 }
92
93 pub fn balance_of(&self, address: &Address) -> U256 {
95 self.balances.get_or_default(address)
96 }
97
98 pub fn allowance(&self, owner: &Address, spender: &Address) -> U256 {
100 self.allowances.get_or_default(&(*owner, *spender))
101 }
102
103 pub fn mint(&mut self, address: &Address, amount: &U256) {
105 self.total_supply.add(*amount);
106 self.balances.add(address, *amount);
107
108 self.env().emit_event(Transfer {
109 from: None,
110 to: Some(*address),
111 amount: *amount
112 });
113 }
114
115 pub fn burn(&mut self, address: &Address, amount: &U256) {
117 if self.balance_of(address) < *amount {
118 self.env().revert(Error::InsufficientBalance);
119 }
120 self.total_supply.subtract(*amount);
121 self.balances.subtract(address, *amount);
122
123 self.env().emit_event(Transfer {
124 from: Some(*address),
125 to: None,
126 amount: *amount
127 });
128 }
129}
130
131impl Erc20 {
132 fn raw_transfer(&mut self, owner: &Address, recipient: &Address, amount: &U256) {
133 if *amount > self.balances.get_or_default(owner) {
134 self.env().revert(Error::InsufficientBalance)
135 }
136
137 self.balances.subtract(owner, *amount);
138 self.balances.add(recipient, *amount);
139
140 self.env().emit_event(Transfer {
141 from: Some(*owner),
142 to: Some(*recipient),
143 amount: *amount
144 });
145 }
146
147 fn spend_allowance(&mut self, owner: &Address, spender: &Address, amount: &U256) {
148 let allowance = self.allowances.get_or_default(&(*owner, *spender));
149 if allowance < *amount {
150 self.env().revert(Error::InsufficientAllowance)
151 }
152 self.allowances.subtract(&(*owner, *spender), *amount);
153
154 self.env().emit_event(Approval {
155 owner: *owner,
156 spender: *spender,
157 value: allowance - *amount
158 });
159 }
160}
161
162pub mod events {
164 use odra::casper_event_standard;
165 use odra::casper_types::U256;
166 use odra::prelude::*;
167
168 #[odra::event]
170 pub struct Transfer {
171 pub from: Option<Address>,
173 pub to: Option<Address>,
175 pub amount: U256
177 }
178
179 #[odra::event]
181 pub struct Approval {
182 pub owner: Address,
184 pub spender: Address,
186 pub value: U256
188 }
189}
190
191pub mod errors {
193 use odra::prelude::*;
194
195 #[odra::odra_error]
197 pub enum Error {
198 InsufficientBalance = 30_000,
200 InsufficientAllowance = 30_001,
202 NameNotSet = 30_002,
204 SymbolNotSet = 30_003,
206 DecimalsNotSet = 30_004
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::{
214 errors::Error,
215 events::{Approval, Transfer},
216 Erc20, Erc20HostRef, Erc20InitArgs
217 };
218 use odra::{
219 casper_types::U256,
220 host::{Deployer, HostEnv},
221 prelude::*
222 };
223
224 const NAME: &str = "Plascoin";
225 const SYMBOL: &str = "PLS";
226 const DECIMALS: u8 = 10;
227 const INITIAL_SUPPLY: u32 = 10_000;
228
229 fn setup() -> (HostEnv, Erc20HostRef) {
230 let env = odra_test::env();
231 (
232 env.clone(),
233 Erc20::deploy(
234 &env,
235 Erc20InitArgs {
236 symbol: SYMBOL.to_string(),
237 name: NAME.to_string(),
238 decimals: DECIMALS,
239 initial_supply: Some(INITIAL_SUPPLY.into())
240 }
241 )
242 )
243 }
244
245 #[test]
246 fn initialization() {
247 let (env, erc20) = setup();
249
250 assert_eq!(erc20.symbol(), SYMBOL.to_string());
252 assert_eq!(erc20.name(), NAME.to_string());
253 assert_eq!(erc20.decimals(), DECIMALS);
254
255 assert_eq!(erc20.total_supply(), INITIAL_SUPPLY.into());
257
258 assert!(env.emitted_event(
260 &erc20,
261 Transfer {
262 from: None,
263 to: Some(env.get_account(0)),
264 amount: INITIAL_SUPPLY.into()
265 }
266 ));
267 }
268
269 #[test]
270 fn transfer_works() {
271 let (env, mut erc20) = setup();
273
274 let sender = env.get_account(0);
276 let recipient = env.get_account(1);
277 let amount = 1_000.into();
278 erc20.transfer(&recipient, &amount);
279
280 assert_eq!(
282 erc20.balance_of(&sender),
283 U256::from(INITIAL_SUPPLY) - amount
284 );
285
286 assert_eq!(erc20.balance_of(&recipient), amount);
288
289 assert!(env.emitted_event(
291 &erc20,
292 Transfer {
293 from: Some(sender),
294 to: Some(recipient),
295 amount
296 }
297 ));
298 }
299
300 #[test]
301 fn transfer_error() {
302 let (env, mut erc20) = setup();
304
305 let recipient = env.get_account(1);
307 let amount = U256::from(INITIAL_SUPPLY) + U256::one();
308
309 assert!(erc20.try_transfer(&recipient, &amount).is_err());
311 }
312
313 #[test]
314 fn transfer_from_and_approval_work() {
315 let (env, mut erc20) = setup();
316
317 let (owner, recipient, spender) =
318 (env.get_account(0), env.get_account(1), env.get_account(2));
319 let approved_amount = 3_000.into();
320 let transfer_amount = 1_000.into();
321
322 assert_eq!(erc20.balance_of(&owner), U256::from(INITIAL_SUPPLY));
323
324 erc20.approve(&spender, &approved_amount);
326
327 assert_eq!(erc20.allowance(&owner, &spender), approved_amount);
329 assert!(env.emitted_event(
330 &erc20,
331 Approval {
332 owner,
333 spender,
334 value: approved_amount
335 }
336 ));
337
338 env.set_caller(spender);
340 erc20.transfer_from(&owner, &recipient, &transfer_amount);
341
342 assert_eq!(
344 erc20.balance_of(&owner),
345 U256::from(INITIAL_SUPPLY) - transfer_amount
346 );
347 assert_eq!(erc20.balance_of(&recipient), transfer_amount);
348 assert!(env.emitted_event(
349 &erc20,
350 Approval {
351 owner,
352 spender,
353 value: approved_amount - transfer_amount
354 }
355 ));
356 assert!(env.emitted_event(
357 &erc20,
358 Transfer {
359 from: Some(owner),
360 to: Some(recipient),
361 amount: transfer_amount
362 }
363 ));
364 }
365
366 #[test]
367 fn transfer_from_error() {
368 let (env, mut erc20) = setup();
370
371 let (owner, spender, recipient) =
373 (env.get_account(0), env.get_account(1), env.get_account(2));
374 let amount = 1_000.into();
375 env.set_caller(spender);
376
377 assert_eq!(
379 erc20.try_transfer_from(&owner, &recipient, &amount),
380 Err(Error::InsufficientAllowance.into())
381 );
382 }
383}