Token-0x

First Flight #54
Beginner FriendlyDeFi
100 EXP
Submission Details
Impact: low
Likelihood: high

Unnecessary Zero Memory Stores in Assembly Functions

Author Revealed upon completion

Unnecessary Zero Memory Stores in Assembly Functions

Description

The contract contains several assembly blocks that unnecessarily store zero values (0x00) to memory locations before reverting or returning. These operations consume extra gas without providing any functional benefit. Memory slots are already initialized to zero by default in the EVM, making these explicit zero stores redundant.

The issue is particularly evident in error handling paths and return statements where memory is unnecessarily zeroed before reverting or returning data.

// This is one of the function where we store 0 data in memory which can be deleted
function _balanceOf(address owner) internal view returns (uint256) {
assembly {
if iszero(owner) {
revert(0, 0)
}
let baseSlot := _balances.slot
let ptr := mload(0x40)
mstore(ptr, owner)
mstore(add(ptr, 0x20), baseSlot)
let dataSlot := keccak256(ptr, 0x40)
let amount := sload(dataSlot)
mstore(ptr, amount)
mstore(add(ptr, 0x20), 0) //<@ we can remove this line. mstore with 0 data
return(ptr, 0x20)
}
}

Risk

Likelihood:HIGH

  • The code is always present in the contract

Impact:LOW

  • Gas inefficiency: Unnecessary gas costs for users

  • Code bloat: Additional operations that serve no purpose

  • Minor performance impact: Extra EVM operations slow down execution

Proof of Concept

Run the current test suite Token.t.sol with forge test in terminal. We can get each function gas list

  • [PASS] test_allowance() (gas: 91933)

    [PASS] test_approveRevert() (gas: 12744)

    [PASS] test_burn() (gas: 47459)

    [PASS] test_burnRevert() (gas: 9900)

    [PASS] test_metadata() (gas: 20271)

    [PASS] test_mint() (gas: 58836)

    [PASS] test_mintRevert() (gas: 9836)

    [PASS] test_spendallowanceRevert() (gas: 93551)

    [PASS] test_transfer() (gas: 93646)

    [PASS] test_transferFrom() (gas: 104014)

    [PASS] test_transferRevert() (gas: 62946)

    [PASS] test_transferRevert2() (gas: 65918)

If we remove all line with mstore(location, 0x00) and we run the same test suite

  • [PASS] test_allowance() (gas: 91919)

    [PASS] test_approveRevert() (gas: 12730)

    [PASS] test_burn() (gas: 47436)

    [PASS] test_burnRevert() (gas: 9887)

    [PASS] test_metadata() (gas: 20271)

    [PASS] test_mint() (gas: 58822)

    [PASS] test_mintRevert() (gas: 9823)

    [PASS] test_spendallowanceRevert() (gas: 93551)

    [PASS] test_transfer() (gas: 93604)

    [PASS] test_transferFrom() (gas: 103986)

    [PASS] test_transferRevert() (gas: 62919)

    [PASS] test_transferRevert2() (gas: 65904)

Recommended Mitigation

Remove all unnecessary codes inside contract

function _balanceOf(address owner) internal view returns (uint256) {
assembly {
if iszero(owner) {
revert(0, 0)
}
let baseSlot := _balances.slot
let ptr := mload(0x40)
mstore(ptr, owner)
mstore(add(ptr, 0x20), baseSlot)
let dataSlot := keccak256(ptr, 0x40)
let amount := sload(dataSlot)
mstore(ptr, amount)
- mstore(add(ptr, 0x20), 0)
return(ptr, 0x20)
}
}
function _approve(address owner, address spender, uint256 value) internal virtual returns (bool success) {
assembly ("memory-safe") {
if iszero(owner) {
//0xe602df05 ERC20InvalidApprover(address)
mstore(0x00, shl(224, 0xe602df05))
- mstore(add(0x00, 4), owner)
revert(0x00, 0x24)
}
if iszero(spender) {
//0x94280d62 ERC20InvalidSpender(address)
mstore(0x00, shl(224, 0x94280d62))
- mstore(add(0x00, 4), spender)
revert(0x00, 0x24)
}
... //the rest of code
function _transfer(address from, address to, uint256 value) internal returns (bool success) {
assembly ("memory-safe") {
if iszero(from) {
//ERC20InvalidSender(address) 0x96c6fd1e
mstore(0x00, shl(224, 0x96c6fd1e))
- mstore(add(0x00, 4), 0x00)
revert(0x00, 0x24)
}
if iszero(to) {
//0xec442f05 ERC20InvalidReceiver(address)
mstore(0x00, shl(224, 0xec442f05))
- mstore(add(0x00, 4), 0x00)
revert(0x00, 0x24)
}
... //the rest of code
function _mint(address account, uint256 value) internal {
assembly ("memory-safe") {
if iszero(account) {
//0xec442f05 ERC20InvalidReceiver(address)
mstore(0x00, shl(224, 0xec442f05))
- mstore(add(0x00, 4), 0x00)
revert(0x00, 0x24)
}
... //the rest of code
function _burn(address account, uint256 value) internal {
assembly ("memory-safe") {
if iszero(account) {
//ERC20InvalidSender(address) 0x96c6fd1e
mstore(0x00, shl(224, 0x96c6fd1e))
- mstore(add(0x00, 4), 0x00)
revert(0x00, 0x24)
}
... //the rest of code

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!