-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathlib.rs
168 lines (135 loc) · 4.95 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#![no_std]
#![no_main]
use core::default::Default;
use contract_derive::{contract, payable, storage, Event, Error};
use eth_riscv_runtime::types::*;
use alloy_core::primitives::{address, Address, U256, Bytes};
extern crate alloc;
use alloc::string::String;
#[storage]
pub struct ERC20 {
total_supply: Slot<U256>,
balances: Mapping<Address, Slot<U256>>,
allowances: Mapping<Address, Mapping<Address, Slot<U256>>>,
owner: Slot<Address>,
// name: String,
// symbol: String,
// decimals: u8,
}
#[derive(Error)]
pub enum ERC20Error {
OnlyOwner,
ZeroAmount,
InsufficientBalance(U256),
InsufficientAllowance(U256)
}
#[derive(Event)]
pub struct Transfer {
#[indexed]
pub from: Address,
#[indexed]
pub to: Address,
pub amount: U256,
}
#[derive(Event)]
pub struct OwnershipTransferred {
#[indexed]
pub from: Address,
#[indexed]
pub to: Address,
}
#[contract]
impl ERC20 {
// -- CONSTRUCTOR ---------------------------------------------------------
pub fn new(owner: Address) -> Self {
// Init the contract
let mut erc20 = ERC20::default();
// Store the owner
erc20.owner.write(owner);
// Return the initialized contract
erc20
}
// -- STATE MODIFYING FUNCTIONS -------------------------------------------
#[payable]
pub fn mint(&mut self, to: Address, amount: U256) -> Result<bool, ERC20Error> {
// Perform sanity checks
if msg_sender() != self.owner.read() { return Err(ERC20Error::OnlyOwner) };
if amount == U256::ZERO { return Err(ERC20Error::ZeroAmount) };
// Increase user balance
let to_balance = self.balances.read(to);
self.balances.write(to, to_balance + amount);
log::emit(Transfer::new(
address!("0000000000000000000000000000000000000000"),
to,
amount,
));
// Increase total supply
self.total_supply += amount;
// Return true to stick to (EVM) ERC20 convention
Ok(true)
}
// Despite user-define return type is `bool`, R55 will wrap it into an `Option<bool>`
// to ensure that callers have proper error-handling.
// Note that this is a zero-cost (runtime) abstraction that provides better compile-time guarantees.
pub fn approve(&mut self, spender: Address, amount: U256) -> bool {
let mut spender_allowances = self.allowances.read(msg_sender());
spender_allowances.write(spender, amount);
true
}
pub fn transfer(&mut self, to: Address, amount: U256) -> Result<bool, ERC20Error> {
// Perform sanity checks
if amount == U256::ZERO { return Err(ERC20Error::ZeroAmount) };
// Read user balances
let from = msg_sender();
let from_balance = self.balances.read(from);
let to_balance = self.balances.read(to);
// Ensure enough balance
if from_balance < amount { return Err(ERC20Error::InsufficientBalance(from_balance)) }
// Update state
self.balances.write(from, from_balance - amount);
self.balances.write(to, to_balance + amount);
// Emit event + return
log::emit(Transfer::new(from, to, amount));
Ok(true)
}
pub fn transfer_from(&mut self, sender: Address, recipient: Address, amount: U256) -> Result<bool, ERC20Error> {
// Perform sanity checks
if amount == U256::ZERO { return Err(ERC20Error::ZeroAmount) };
// Ensure enough allowance
let allowance = self.allowances.read(sender).read(msg_sender());
if allowance < amount { return Err(ERC20Error::InsufficientAllowance(allowance)) };
// Ensure enough balance
let sender_balance = self.balances.read(sender);
if allowance < amount { return Err(ERC20Error::InsufficientBalance(sender_balance)) };
// Update state
self.allowances
.read(sender)
.write(msg_sender(), allowance - amount);
self.balances.write(sender, sender_balance - amount);
self.balances.write(recipient, self.balances.read(recipient) + amount);
Ok(true)
}
pub fn transfer_ownership(&mut self, new_owner: Address) -> Result<bool, ERC20Error> {
// Perform safety check
let from = msg_sender();
if from != self.owner.read() { return Err(ERC20Error::OnlyOwner) };
// Update state
self.owner.write(new_owner);
// Emit event + return
log::emit(OwnershipTransferred::new(from, new_owner));
Ok(true)
}
// -- READ-ONLY FUNCTIONS --------------------------------------------------
pub fn owner(&self) -> Address {
self.owner.read()
}
pub fn total_supply(&self) -> U256 {
self.total_supply.read()
}
pub fn balance_of(&self, owner: Address) -> U256 {
self.balances.read(owner)
}
pub fn allowance(&self, owner: Address, spender: Address) -> U256 {
self.allowances.read(owner).read(spender)
}
}