1#![cfg_attr(not(feature = "std"), no_std)]
2
3use ink_lang as ink;
4
5#[ink::contract]
6mod erc20 {
7 use ink_storage::{
8 traits::SpreadAllocate,
9 Mapping,
10 };
11
12 #[ink(storage)]
14 #[derive(SpreadAllocate)]
15 pub struct Erc20 {
16 total_supply: Balance,
18 balances: Mapping<AccountId, Balance>,
20 allowances: Mapping<(AccountId, AccountId), Balance>,
23 }
24
25 #[ink(event)]
27 pub struct Transfer {
28 #[ink(topic)]
29 from: Option<AccountId>,
30 #[ink(topic)]
31 to: Option<AccountId>,
32 value: Balance,
33 }
34
35 #[ink(event)]
38 pub struct Approval {
39 #[ink(topic)]
40 owner: AccountId,
41 #[ink(topic)]
42 spender: AccountId,
43 value: Balance,
44 }
45
46 #[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)]
48 #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
49 pub enum Error {
50 InsufficientBalance,
52 InsufficientAllowance,
54 }
55
56 pub type Result<T> = core::result::Result<T, Error>;
58
59 impl Erc20 {
60 #[ink(constructor)]
62 pub fn new(initial_supply: Balance) -> Self {
63 ink_lang::utils::initialize_contract(|contract| {
66 Self::new_init(contract, initial_supply)
67 })
68 }
69
70 fn new_init(&mut self, initial_supply: Balance) {
72 let caller = Self::env().caller();
73 self.balances.insert(&caller, &initial_supply);
74 self.total_supply = initial_supply;
75 Self::env().emit_event(Transfer {
76 from: None,
77 to: Some(caller),
78 value: initial_supply,
79 });
80 }
81
82 #[ink(message)]
84 pub fn total_supply(&self) -> Balance {
85 self.total_supply
86 }
87
88 #[ink(message)]
92 pub fn balance_of(&self, owner: AccountId) -> Balance {
93 self.balance_of_impl(&owner)
94 }
95
96 #[inline]
105 fn balance_of_impl(&self, owner: &AccountId) -> Balance {
106 self.balances.get(owner).unwrap_or_default()
107 }
108
109 #[ink(message)]
113 pub fn allowance(&self, owner: AccountId, spender: AccountId) -> Balance {
114 self.allowance_impl(&owner, &spender)
115 }
116
117 #[inline]
126 fn allowance_impl(&self, owner: &AccountId, spender: &AccountId) -> Balance {
127 self.allowances.get((owner, spender)).unwrap_or_default()
128 }
129
130 #[ink(message)]
139 pub fn transfer(&mut self, to: AccountId, value: Balance) -> Result<()> {
140 let from = self.env().caller();
141 self.transfer_from_to(&from, &to, value)
142 }
143
144 #[ink(message)]
151 pub fn approve(&mut self, spender: AccountId, value: Balance) -> Result<()> {
152 let owner = self.env().caller();
153 self.allowances.insert((&owner, &spender), &value);
154 self.env().emit_event(Approval {
155 owner,
156 spender,
157 value,
158 });
159 Ok(())
160 }
161
162 #[ink(message)]
177 pub fn transfer_from(
178 &mut self,
179 from: AccountId,
180 to: AccountId,
181 value: Balance,
182 ) -> Result<()> {
183 let caller = self.env().caller();
184 let allowance = self.allowance_impl(&from, &caller);
185 if allowance < value {
186 return Err(Error::InsufficientAllowance)
187 }
188 self.transfer_from_to(&from, &to, value)?;
189 self.allowances
190 .insert((&from, &caller), &(allowance - value));
191 Ok(())
192 }
193
194 fn transfer_from_to(
203 &mut self,
204 from: &AccountId,
205 to: &AccountId,
206 value: Balance,
207 ) -> Result<()> {
208 let from_balance = self.balance_of_impl(from);
209 if from_balance < value {
210 return Err(Error::InsufficientBalance)
211 }
212
213 self.balances.insert(from, &(from_balance - value));
214 let to_balance = self.balance_of_impl(to);
215 self.balances.insert(to, &(to_balance + value));
216 self.env().emit_event(Transfer {
217 from: Some(*from),
218 to: Some(*to),
219 value,
220 });
221 Ok(())
222 }
223 }
224
225 #[cfg(test)]
226 mod tests {
227 use super::*;
228
229 use ink_env::Clear;
230 use ink_lang as ink;
231
232 type Event = <Erc20 as ::ink_lang::reflect::ContractEventBase>::Type;
233
234 fn assert_transfer_event(
235 event: &ink_env::test::EmittedEvent,
236 expected_from: Option<AccountId>,
237 expected_to: Option<AccountId>,
238 expected_value: Balance,
239 ) {
240 let decoded_event = <Event as scale::Decode>::decode(&mut &event.data[..])
241 .expect("encountered invalid contract event data buffer");
242 if let Event::Transfer(Transfer { from, to, value }) = decoded_event {
243 assert_eq!(from, expected_from, "encountered invalid Transfer.from");
244 assert_eq!(to, expected_to, "encountered invalid Transfer.to");
245 assert_eq!(value, expected_value, "encountered invalid Trasfer.value");
246 } else {
247 panic!("encountered unexpected event kind: expected a Transfer event")
248 }
249 let expected_topics = vec![
250 encoded_into_hash(&PrefixedValue {
251 value: b"Erc20::Transfer",
252 prefix: b"",
253 }),
254 encoded_into_hash(&PrefixedValue {
255 prefix: b"Erc20::Transfer::from",
256 value: &expected_from,
257 }),
258 encoded_into_hash(&PrefixedValue {
259 prefix: b"Erc20::Transfer::to",
260 value: &expected_to,
261 }),
262 encoded_into_hash(&PrefixedValue {
263 prefix: b"Erc20::Transfer::value",
264 value: &expected_value,
265 }),
266 ];
267
268 let topics = event.topics.clone();
269 for (n, (actual_topic, expected_topic)) in
270 topics.iter().zip(expected_topics).enumerate()
271 {
272 let mut topic_hash = Hash::clear();
273 let len = actual_topic.len();
274 topic_hash.as_mut()[0..len].copy_from_slice(&actual_topic[0..len]);
275
276 assert_eq!(
277 topic_hash, expected_topic,
278 "encountered invalid topic at {}",
279 n
280 );
281 }
282 }
283
284 #[ink::test]
286 fn new_works() {
287 let _erc20 = Erc20::new(100);
289
290 let emitted_events = ink_env::test::recorded_events().collect::<Vec<_>>();
292 assert_eq!(1, emitted_events.len());
293
294 assert_transfer_event(
295 &emitted_events[0],
296 None,
297 Some(AccountId::from([0x01; 32])),
298 100,
299 );
300 }
301
302 #[ink::test]
304 fn total_supply_works() {
305 let erc20 = Erc20::new(100);
307 let emitted_events = ink_env::test::recorded_events().collect::<Vec<_>>();
309 assert_transfer_event(
310 &emitted_events[0],
311 None,
312 Some(AccountId::from([0x01; 32])),
313 100,
314 );
315 assert_eq!(erc20.total_supply(), 100);
317 }
318
319 #[ink::test]
321 fn balance_of_works() {
322 let erc20 = Erc20::new(100);
324 let emitted_events = ink_env::test::recorded_events().collect::<Vec<_>>();
326 assert_transfer_event(
327 &emitted_events[0],
328 None,
329 Some(AccountId::from([0x01; 32])),
330 100,
331 );
332 let accounts =
333 ink_env::test::default_accounts::<ink_env::DefaultEnvironment>();
334 assert_eq!(erc20.balance_of(accounts.alice), 100);
336 assert_eq!(erc20.balance_of(accounts.bob), 0);
338 }
339
340 #[ink::test]
341 fn transfer_works() {
342 let mut erc20 = Erc20::new(100);
344 let accounts =
346 ink_env::test::default_accounts::<ink_env::DefaultEnvironment>();
347
348 assert_eq!(erc20.balance_of(accounts.bob), 0);
349 assert_eq!(erc20.transfer(accounts.bob, 10), Ok(()));
351 assert_eq!(erc20.balance_of(accounts.bob), 10);
353
354 let emitted_events = ink_env::test::recorded_events().collect::<Vec<_>>();
355 assert_eq!(emitted_events.len(), 2);
356 assert_transfer_event(
358 &emitted_events[0],
359 None,
360 Some(AccountId::from([0x01; 32])),
361 100,
362 );
363 assert_transfer_event(
365 &emitted_events[1],
366 Some(AccountId::from([0x01; 32])),
367 Some(AccountId::from([0x02; 32])),
368 10,
369 );
370 }
371
372 #[ink::test]
373 fn invalid_transfer_should_fail() {
374 let mut erc20 = Erc20::new(100);
376 let accounts =
377 ink_env::test::default_accounts::<ink_env::DefaultEnvironment>();
378
379 assert_eq!(erc20.balance_of(accounts.bob), 0);
380
381 let contract = ink_env::account_id::<ink_env::DefaultEnvironment>();
383 ink_env::test::set_callee::<ink_env::DefaultEnvironment>(contract);
384 ink_env::test::set_caller::<ink_env::DefaultEnvironment>(accounts.bob);
385
386 assert_eq!(
388 erc20.transfer(accounts.eve, 10),
389 Err(Error::InsufficientBalance)
390 );
391 assert_eq!(erc20.balance_of(accounts.alice), 100);
393 assert_eq!(erc20.balance_of(accounts.bob), 0);
394 assert_eq!(erc20.balance_of(accounts.eve), 0);
395
396 let emitted_events = ink_env::test::recorded_events().collect::<Vec<_>>();
398 assert_eq!(emitted_events.len(), 1);
399 assert_transfer_event(
400 &emitted_events[0],
401 None,
402 Some(AccountId::from([0x01; 32])),
403 100,
404 );
405 }
406
407 #[ink::test]
408 fn transfer_from_works() {
409 let mut erc20 = Erc20::new(100);
411 let accounts =
413 ink_env::test::default_accounts::<ink_env::DefaultEnvironment>();
414
415 assert_eq!(
417 erc20.transfer_from(accounts.alice, accounts.eve, 10),
418 Err(Error::InsufficientAllowance)
419 );
420 assert_eq!(erc20.approve(accounts.bob, 10), Ok(()));
422
423 assert_eq!(ink_env::test::recorded_events().count(), 2);
425
426 let contract = ink_env::account_id::<ink_env::DefaultEnvironment>();
428 ink_env::test::set_callee::<ink_env::DefaultEnvironment>(contract);
429 ink_env::test::set_caller::<ink_env::DefaultEnvironment>(accounts.bob);
430
431 assert_eq!(
433 erc20.transfer_from(accounts.alice, accounts.eve, 10),
434 Ok(())
435 );
436 assert_eq!(erc20.balance_of(accounts.eve), 10);
438
439 let emitted_events = ink_env::test::recorded_events().collect::<Vec<_>>();
441 assert_eq!(emitted_events.len(), 3);
442 assert_transfer_event(
443 &emitted_events[0],
444 None,
445 Some(AccountId::from([0x01; 32])),
446 100,
447 );
448 assert_transfer_event(
450 &emitted_events[2],
451 Some(AccountId::from([0x01; 32])),
452 Some(AccountId::from([0x05; 32])),
453 10,
454 );
455 }
456
457 #[ink::test]
458 fn allowance_must_not_change_on_failed_transfer() {
459 let mut erc20 = Erc20::new(100);
460 let accounts =
461 ink_env::test::default_accounts::<ink_env::DefaultEnvironment>();
462
463 let alice_balance = erc20.balance_of(accounts.alice);
465 let initial_allowance = alice_balance + 2;
466 assert_eq!(erc20.approve(accounts.bob, initial_allowance), Ok(()));
467
468 let callee = ink_env::account_id::<ink_env::DefaultEnvironment>();
470 ink_env::test::set_callee::<ink_env::DefaultEnvironment>(callee);
471 ink_env::test::set_caller::<ink_env::DefaultEnvironment>(accounts.bob);
472
473 let emitted_events_before = ink_env::test::recorded_events().count();
475 assert_eq!(
476 erc20.transfer_from(accounts.alice, accounts.eve, alice_balance + 1),
477 Err(Error::InsufficientBalance)
478 );
479 assert_eq!(
481 erc20.allowance(accounts.alice, accounts.bob),
482 initial_allowance
483 );
484 assert_eq!(
486 emitted_events_before,
487 ink_env::test::recorded_events().count()
488 )
489 }
490
491 struct PrefixedValue<'a, 'b, T> {
493 pub prefix: &'a [u8],
494 pub value: &'b T,
495 }
496
497 impl<X> scale::Encode for PrefixedValue<'_, '_, X>
498 where
499 X: scale::Encode,
500 {
501 #[inline]
502 fn size_hint(&self) -> usize {
503 self.prefix.size_hint() + self.value.size_hint()
504 }
505
506 #[inline]
507 fn encode_to<T: scale::Output + ?Sized>(&self, dest: &mut T) {
508 self.prefix.encode_to(dest);
509 self.value.encode_to(dest);
510 }
511 }
512
513 fn encoded_into_hash<T>(entity: &T) -> Hash
514 where
515 T: scale::Encode,
516 {
517 use ink_env::{
518 hash::{
519 Blake2x256,
520 CryptoHash,
521 HashOutput,
522 },
523 Clear,
524 };
525 let mut result = Hash::clear();
526 let len_result = result.as_ref().len();
527 let encoded = entity.encode();
528 let len_encoded = encoded.len();
529 if len_encoded <= len_result {
530 result.as_mut()[..len_encoded].copy_from_slice(&encoded);
531 return result
532 }
533 let mut hash_output =
534 <<Blake2x256 as HashOutput>::Type as Default>::default();
535 <Blake2x256 as CryptoHash>::hash(&encoded, &mut hash_output);
536 let copy_len = core::cmp::min(hash_output.len(), len_result);
537 result.as_mut()[0..copy_len].copy_from_slice(&hash_output[0..copy_len]);
538 result
539 }
540 }
541}