-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathERC20Yul.sol
379 lines (330 loc) · 12.4 KB
/
ERC20Yul.sol
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ERC20Yul{
// The full 32-byte Keccak-256 hash uniquely identifies the event type.
// the full hash ensures that the event can be uniquely identified
// entire keccak256 of the event is used in the abi
bytes32 internal constant _TRANSFER_HASH = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef ;
bytes32 internal constant _APPROVAL_HASH = 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925 ;
// 4 bytes selectors of ERROR strings -> padded to 32 bytes
// padded for easy storage access
// error messages
// keccac256(InsufficientBalance())
bytes32 internal constant _INSUFFICIENT_BALANCE = 0xf4d678b800000000000000000000000000000000000000000000000000000000 ;
bytes32 internal constant _INSUFFICIENT_ALLOWANCE_SELECTOR = 0x13be252b00000000000000000000000000000000000000000000000000000000;
bytes32 internal constant _RECIPIENT_ZERO_SELECTOR = 0x4c131ee600000000000000000000000000000000000000000000000000000000;
bytes32 internal constant _INVALID_SIG_SELECTOR = 0x8baa579f00000000000000000000000000000000000000000000000000000000;
bytes32 internal constant _EXPIRED_SELECTOR = 0x203d82d800000000000000000000000000000000000000000000000000000000;
bytes32 internal constant _STRING_TOO_LONG_SELECTOR = 0xb11b2ad800000000000000000000000000000000000000000000000000000000;
bytes32 internal constant _OVERFLOW_SELECTOR = 0x35278d1200000000000000000000000000000000000000000000000000000000;
// _MAX amount value
// maximum value that can be stored in uni256 in decimal
bytes32 internal constant _MAX = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
// EIP 712
bytes32 internal constant _EIP712_DOMAIN_PREFIX_HASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
bytes32 internal constant _VERSION_1_HASH = 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;
// EIP 2612
bytes32 internal constant _PERMIT_HASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
// name and symbol
// prevents dynamic allocation as strings
bytes32 internal immutable _name;
bytes32 internal immutable _symbol;
// length of name and string
// for conversion in future
uint256 internal immutable _nameLen;
uint256 internal immutable _symbolLen;
// EIP 2612
uint256 internal immutable _initialChainId;
bytes32 internal immutable _initialDomainSeparator;
// balances mappings
mapping(address => uint256) internal _balances;
mapping(address => mapping( address => uint256 )) internal _allowances;
// maintains the total supply of tokens
// updates when getting burned or minted
uint256 internal _supply;
mapping(address => uint256) internal _nonces;
constructor(string memory name_, string memory symbol_){
bytes memory nameX = bytes(name_);
bytes memory symbolX = bytes(symbol_);
uint256 nameLen = nameX.length;
uint256 symbolLen = symbolX.length;
// checks if name and symbol are less than 32 bytes
assembly{
if or(lt(0x20,nameLen), lt(0x20,symbolX)){
mstore(0x00,_STRING_TOO_LONG_SELECTOR)
revert(0x00, 0x04)
}
}
// compute domain separator
bytes32 initialDomainSeparator = _computeDomainSeparator(
keccak256(nameX)
);
// setting the storage variables
_name = bytes32(nameX);
_symbol = bytes32(symbolX);
_initialChainId = block.chainid;
_initialDomainSeparator = initialDomainSeparator;
_nameLen = nameLen;
_symbolLen = symbolLen;
}
// transfer function from address to address
function transfer(address _to, uint _value) public virtual returns(bool success){
assembly{
// checks if the receipinet address is nonzero
if iszero(_to){
mstore(0x00,_RECIPIENT_ZERO_SELECTOR)
revert(0x00, 0x04)
}
// load the caller() and store at 0x00 memory location
mstore(0x00, caller())
mstore(0x20,0x00) // stores 0x00 at 0x20 location
let srcSender := keccak256(0x00,0x40)
let balSender := sload(srcSender)
// checking for sufficient balance
if lt(balSender,_value){
mstore(0x00,_INSUFFICIENT_BALANCE)
revert(0x00,0x04)
}
// update senders balance
sstore(srcSender, sub(balSender,_value))
// update receipt balance
mstore(0x00,_to)
let srcReceipt := keccak256(0x00,0x40)
let balReceipt := sload(srcReceipt)
// receipt balance update
sstore(srcReceipt, add(balReceipt,_value))
// emit event logs
mstore(0x00,_value)
log3(0x00,0x20,_TRANSFER_HASH,caller(),_to)
// returns true
success := 0x01
}
}
function transferFrom(address _from , address _to , uint256 _value) public virtual returns(bool success){
assembly{
// check for addresses not zero
if or( iszero(_from) , iszero(_to) ) {
mstore(0x00,_RECIPIENT_ZERO_SELECTOR )
revert(0x00,0x04)
}
mstore(0x00,_from)
mstore(0x20,0x01)
mstore(0x20,keccak256(0x00,0x40) )
mstore(0x00,caller())
let srcAllowance := keccak256(0x00,0x40)
let balAllowance := sload(srcAllowance)
// check for sufficient funds
if lt(balAllowance,_MAX){
if lt(balAllowance,_value){
mstore(0x00,_INSUFFICIENT_BALANCE)
revert(0x00,0x04)
}
// update allowance
sstore(srcAllowance, sub(balAllowance,_value))
}
// update senders balance
mstore(0x00,_from)
mstore(0x20,0x00)
let srcSender := keccak256(0x00,0x40)
let balSender := sload(srcSender)
if lt(balSender,_value){
mstore(0x00,_INSUFFICIENT_BALANCE)
revert(0x00,0x04)
}
sstore(srcSender, sub(balSender,_value))
// update receiver balance
mstore(0x00,_to)
mstore(0x20,0x00)
let srcRecipt := keccak256(0x00,0x40)
let balReceipt := sload(srcRecipt)
sstore(srcRecipt, add(balReceipt,_value))
mstore(0x00,_value)
log3(0x00,0x20,_TRANSFER_HASH,_from , _to )
success := 0x00
}
}
function approve(address _to, uint256 _value) public virtual returns(bool success){
assembly{
// check address zero
if iszero(_to){
mstore(0x00,_RECIPIENT_ZERO_SELECTOR)
revert(0x00,0x04)
}
// set approval
// stores caller address at 0x00
mstore(0x00, caller())
// stores 0x01 sat 0x20
mstore(0x20, 0x01)
// stores the value that approval of _from
mstore(0x20, keccak256(0x00,0x40))
// stores the address of _to at 0x00
mstore(0x00,_to)
sstore(keccak256(0x00,0x40),_value)
// emit approval event
mstore(0x00,_value)
log3(0x00,0x20,_APPROVAL_HASH,caller(),_to)
// return true
success := 0x01
}
}
function allowance(address _from ,address _to) public virtual returns(uint256 _value){
assembly{
// check if address is zero
if iszero(_to){
mstore(0x00,_RECIPIENT_ZERO_SELECTOR)
revert(0x00,0x04)
}
// retreive the allowance
mstore(0x00,_from)
mstore(0x20,0x01)
mstore(0x20,keccak256(0x00,0x40))
mstore(0x00,_to)
let allowanceSlot := keccak256(0x00,0x40)
// returns the value
_value := sload(allowanceSlot)
}
}
function baalanceOf(address _to) public virtual returns (uint256 _value){
assembly{
// check if balance is zero
if iszero(_to){
mstore(0x00,_RECIPIENT_ZERO_SELECTOR)
revert(0x00,0x04)
}
mstore(0x00,_to)
mstore(0x20,0x00)
let srcReceipt := keccak256(0x00,0x40)
// load the amount and retrun
_value := sload(srcReceipt)
}
}
function nonces(address _to) public virtual returns(uint256 _nonceValue){
assembly{
// check if address is zero
if iszero(_to){
mstore(0x00,_RECIPIENT_ZERO_SELECTOR)
revert(0x00,0x04)
}
// get storage slot of the nonce from address
mstore(0x00,_to)
mstore(0x20,0x03)
// load and return the nonce value
_nonceValue := sload(keccak256(0x00,0x40))
}
}
function totalSupply() public virtual returns(uint256 _totalSupply){
assembly{
// load value from 0x03 storage slot
_totalSupply := sload(0x02)
}
}
function name() public virtual returns(string memory value){
bytes32 nameTemp = _name;
uint256 nameLenTemp = _symbolLen ;
assembly{
// load the memory location available for storing in memory
value := mload(0x40)
// store the length of string at the starting of free memory available
mstore(value,nameLenTemp)
// store the value of the string at memory location just after the stored length
mstore(add(0x20,value),nameTemp)
// update the memory pointer
mstore(0x40,add(0x40,value))
}
}
function symbol() public virtual returns(uint256 value){
bytes32 symbolTemp = _symbol ;
uint256 symbolLenTemp = _symbolLen;
assembly{
// loads the free memory location where data can be stored
value:= mload(0x40)
// stroring the length of symbol at starting of the free memory location
mstore(value,symbolLenTemp)
// storing the value of symbol in memory slot just after the stored length of symbol
mstore(add(value,0x20),symbolTemp)
// updating the value of free memory pointer
mstore(0x40,add(0x40,value))
}
}
function decimal() public virtual returns(uint256 ){
return 18;
}
function _mint(address _to, uint256 _amount) public virtual returns( bool success){
assembly{
//check if address is non zero
if iszero(_to){
mstore(0x00,_RECIPIENT_ZERO_SELECTOR)
revert(0x00,0x04)
}
let supply := sload(_supply.slot)
let newSupply := add(supply, _amount)
// check for overflow
if lt(newSupply, supply){
mstore(0x00,_OVERFLOW_SELECTOR)
revert(0x00,0x04)
}
// update the stoarge value with new value
sstore(_supply.slot,newSupply)
mstore(0x00,_to)
mstore(0x20,_balances.slot)
// slot where the balance is saved
let srcTo := keccak256(0x00,0x40)
let srcBal := sload(srcTo)
let newBalance := add(srcBal,_amount)
// check for overflow
if lt(newBalance,srcBal){
mstore(0x00,_OVERFLOW_SELECTOR)
revert(0x00,0x04)
}
sstore(srcTo,newBalance)
// Emit a Transfer event from the zero address to indicate tokens were minted.
mstore(0x00, _amount)
log3(0x00, 0x20, _TRANSFER_HASH, _to, _amount)
success := 0x01
}
}
function _burn(address _src, uint256 _amount) internal virtual {
assembly {
// Check the balance of the source address to ensure it has enough tokens to burn.
mstore(0x00, _src)
mstore(0x20, _balances.slot)
let srcSlot := keccak256(0x00, 0x40)
let srcBalance := sload(srcSlot)
if lt(srcBalance, _amount) {
mstore(0x00, _INSUFFICIENT_BALANCE)
revert(0x00, 0x04)
}
// Deduct the amount from the source address's balance.
sstore(srcSlot, sub(srcBalance, _amount))
// Reduce the total supply by the amount burned.
let supply := sload(_supply.slot)
sstore(_supply.slot, sub(supply, _amount))
// Emit a Transfer event with the destination address as the zero address to indicate burning.
mstore(0x00, _amount)
log3(0x00, 0x20, _TRANSFER_HASH, _src, _amount)
}
}
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return
block.chainid == _initialChainId
? _initialDomainSeparator
: _computeDomainSeparator(keccak256(abi.encode(_name)));
}
function _computeDomainSeparator(bytes32 nameHash)
public
view virtual
returns (bytes32 domainSeparator)
{
assembly {
let memptr := mload(0x40) // Load the free memory pointer.
mstore(memptr, _EIP712_DOMAIN_PREFIX_HASH) // EIP-712 domain prefix hash.
mstore(add(memptr, 0x20), nameHash) // Token name hash.
mstore(add(memptr, 0x40), _VERSION_1_HASH) // Version hash ("1").
mstore(add(memptr, 0x60), chainid()) // Current chain ID.
mstore(add(memptr, 0x80), address()) // Contract address.
// Compute the EIP-712 domain separator.
domainSeparator := keccak256(memptr, 0xA0)
}
}
}