Skip to content

Commit 2fe3752

Browse files
committed
⚡️ Optimize ERC20 slot computation with bitwise operations
1 parent cbcfe00 commit 2fe3752

File tree

1 file changed

+34
-37
lines changed

1 file changed

+34
-37
lines changed

src/tokens/ERC20.sol

Lines changed: 34 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,7 @@ abstract contract ERC20 {
155155
function balanceOf(address owner) public view virtual returns (uint256 result) {
156156
/// @solidity memory-safe-assembly
157157
assembly {
158-
mstore(0x0c, _BALANCE_SLOT_SEED)
159-
mstore(0x00, owner)
160-
result := sload(keccak256(0x0c, 0x20))
158+
result := sload(or(shl(224, _BALANCE_SLOT_SEED), shr(96, shl(96, owner))))
161159
}
162160
}
163161

@@ -219,9 +217,8 @@ abstract contract ERC20 {
219217
/// @solidity memory-safe-assembly
220218
assembly {
221219
// Compute the balance slot and load its value.
222-
mstore(0x0c, _BALANCE_SLOT_SEED)
223-
mstore(0x00, caller())
224-
let fromBalanceSlot := keccak256(0x0c, 0x20)
220+
let slot_seed := shl(224, _BALANCE_SLOT_SEED)
221+
let fromBalanceSlot := or(slot_seed, caller())
225222
let fromBalance := sload(fromBalanceSlot)
226223
// Revert if insufficient balance.
227224
if gt(amount, fromBalance) {
@@ -231,15 +228,15 @@ abstract contract ERC20 {
231228
// Subtract and store the updated balance.
232229
sstore(fromBalanceSlot, sub(fromBalance, amount))
233230
// Compute the balance slot of `to`.
234-
mstore(0x00, to)
235-
let toBalanceSlot := keccak256(0x0c, 0x20)
231+
let to_ := shr(96, shl(96, to))
232+
let toBalanceSlot := or(slot_seed, to_)
236233
// Add and store the updated balance of `to`.
237234
// Will not overflow because the sum of all user balances
238235
// cannot exceed the maximum uint256 value.
239236
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
240237
// Emit the {Transfer} event.
241238
mstore(0x20, amount)
242-
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c)))
239+
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), to_)
243240
}
244241
_afterTokenTransfer(msg.sender, to, amount);
245242
return true;
@@ -260,11 +257,13 @@ abstract contract ERC20 {
260257
if (_givePermit2InfiniteAllowance()) {
261258
/// @solidity memory-safe-assembly
262259
assembly {
263-
let from_ := shl(96, from)
260+
let from_ := shr(96, shl(96, from))
261+
let to_ := shr(96, shl(96, to))
264262
if iszero(eq(caller(), _PERMIT2)) {
265263
// Compute the allowance slot and load its value.
266264
mstore(0x20, caller())
267-
mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
265+
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
266+
mstore(0x00, from_)
268267
let allowanceSlot := keccak256(0x0c, 0x34)
269268
let allowance_ := sload(allowanceSlot)
270269
// If the allowance is not the maximum uint256 value.
@@ -279,8 +278,8 @@ abstract contract ERC20 {
279278
}
280279
}
281280
// Compute the balance slot and load its value.
282-
mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
283-
let fromBalanceSlot := keccak256(0x0c, 0x20)
281+
let slot_seed := shl(224, _BALANCE_SLOT_SEED)
282+
let fromBalanceSlot := or(slot_seed, from_)
284283
let fromBalance := sload(fromBalanceSlot)
285284
// Revert if insufficient balance.
286285
if gt(amount, fromBalance) {
@@ -290,23 +289,23 @@ abstract contract ERC20 {
290289
// Subtract and store the updated balance.
291290
sstore(fromBalanceSlot, sub(fromBalance, amount))
292291
// Compute the balance slot of `to`.
293-
mstore(0x00, to)
294-
let toBalanceSlot := keccak256(0x0c, 0x20)
292+
let toBalanceSlot := or(slot_seed, to_)
295293
// Add and store the updated balance of `to`.
296294
// Will not overflow because the sum of all user balances
297295
// cannot exceed the maximum uint256 value.
298296
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
299297
// Emit the {Transfer} event.
300298
mstore(0x20, amount)
301-
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
299+
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, from_, to_)
302300
}
303301
} else {
304302
/// @solidity memory-safe-assembly
305303
assembly {
306-
let from_ := shl(96, from)
307304
// Compute the allowance slot and load its value.
305+
let from_ := shr(96, shl(96, from))
308306
mstore(0x20, caller())
309-
mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
307+
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
308+
mstore(0x00, from_)
310309
let allowanceSlot := keccak256(0x0c, 0x34)
311310
let allowance_ := sload(allowanceSlot)
312311
// If the allowance is not the maximum uint256 value.
@@ -320,8 +319,8 @@ abstract contract ERC20 {
320319
sstore(allowanceSlot, sub(allowance_, amount))
321320
}
322321
// Compute the balance slot and load its value.
323-
mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
324-
let fromBalanceSlot := keccak256(0x0c, 0x20)
322+
let slot_seed := shl(224, _BALANCE_SLOT_SEED)
323+
let fromBalanceSlot := or(slot_seed, from_)
325324
let fromBalance := sload(fromBalanceSlot)
326325
// Revert if insufficient balance.
327326
if gt(amount, fromBalance) {
@@ -331,15 +330,15 @@ abstract contract ERC20 {
331330
// Subtract and store the updated balance.
332331
sstore(fromBalanceSlot, sub(fromBalance, amount))
333332
// Compute the balance slot of `to`.
334-
mstore(0x00, to)
335-
let toBalanceSlot := keccak256(0x0c, 0x20)
333+
let to_ := shr(96, shl(96, to))
334+
let toBalanceSlot := or(slot_seed, to_)
336335
// Add and store the updated balance of `to`.
337336
// Will not overflow because the sum of all user balances
338337
// cannot exceed the maximum uint256 value.
339338
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
340339
// Emit the {Transfer} event.
341340
mstore(0x20, amount)
342-
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
341+
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, from_, to_)
343342
}
344343
}
345344
_afterTokenTransfer(from, to, amount);
@@ -506,15 +505,14 @@ abstract contract ERC20 {
506505
}
507506
// Store the updated total supply.
508507
sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter)
508+
let to_ := shr(96, shl(96, to))
509509
// Compute the balance slot and load its value.
510-
mstore(0x0c, _BALANCE_SLOT_SEED)
511-
mstore(0x00, to)
512-
let toBalanceSlot := keccak256(0x0c, 0x20)
510+
let toBalanceSlot := or(shl(224, _BALANCE_SLOT_SEED), to_)
513511
// Add and store the updated balance.
514512
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
515513
// Emit the {Transfer} event.
516514
mstore(0x20, amount)
517-
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c)))
515+
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, to_)
518516
}
519517
_afterTokenTransfer(address(0), to, amount);
520518
}
@@ -530,10 +528,9 @@ abstract contract ERC20 {
530528
_beforeTokenTransfer(from, address(0), amount);
531529
/// @solidity memory-safe-assembly
532530
assembly {
531+
let from_ := shr(96, shl(96, from))
533532
// Compute the balance slot and load its value.
534-
mstore(0x0c, _BALANCE_SLOT_SEED)
535-
mstore(0x00, from)
536-
let fromBalanceSlot := keccak256(0x0c, 0x20)
533+
let fromBalanceSlot := or(shl(224, _BALANCE_SLOT_SEED), from_)
537534
let fromBalance := sload(fromBalanceSlot)
538535
// Revert if insufficient balance.
539536
if gt(amount, fromBalance) {
@@ -546,7 +543,7 @@ abstract contract ERC20 {
546543
sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount))
547544
// Emit the {Transfer} event.
548545
mstore(0x00, amount)
549-
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
546+
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, from_, 0)
550547
}
551548
_afterTokenTransfer(from, address(0), amount);
552549
}
@@ -560,10 +557,11 @@ abstract contract ERC20 {
560557
_beforeTokenTransfer(from, to, amount);
561558
/// @solidity memory-safe-assembly
562559
assembly {
563-
let from_ := shl(96, from)
560+
let from_ := shr(96, shl(96, from))
561+
let to_ := shr(96, shl(96, to))
562+
let slot_seed := shl(224, _BALANCE_SLOT_SEED)
564563
// Compute the balance slot and load its value.
565-
mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
566-
let fromBalanceSlot := keccak256(0x0c, 0x20)
564+
let fromBalanceSlot := or(slot_seed, from_)
567565
let fromBalance := sload(fromBalanceSlot)
568566
// Revert if insufficient balance.
569567
if gt(amount, fromBalance) {
@@ -573,15 +571,14 @@ abstract contract ERC20 {
573571
// Subtract and store the updated balance.
574572
sstore(fromBalanceSlot, sub(fromBalance, amount))
575573
// Compute the balance slot of `to`.
576-
mstore(0x00, to)
577-
let toBalanceSlot := keccak256(0x0c, 0x20)
574+
let toBalanceSlot := or(slot_seed, to_)
578575
// Add and store the updated balance of `to`.
579576
// Will not overflow because the sum of all user balances
580577
// cannot exceed the maximum uint256 value.
581578
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
582579
// Emit the {Transfer} event.
583580
mstore(0x20, amount)
584-
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
581+
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, from_, to_)
585582
}
586583
_afterTokenTransfer(from, to, amount);
587584
}

0 commit comments

Comments
 (0)