Token-0x

First Flight #54
Beginner FriendlyDeFi
100 EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

High:- Allowance Storage Failure with Max Value

Root Cause

The fuzzer detected that when type(uint256).max(1.157e77) is used as the allowance amount, the subsequent read of that allowance returns 0.

Test: testFuzzTransferFrom

Failure: Initial allowance failed: 0 != 115792089237316195423570985008687907853269984665640564039457584007913129639935

This indicates a flaw in the _allowance mapping assembly logic when handling the all-ones hexadecimal representation (0xFF...FF). While the sstore in _approve likely executes, the read back in _allowance might be calculating an incorrect storage slot (dataSlot), causing sload to return 0.

Impact

Users cannot approve the maximum amount of tokens. If a protocol relies on a "sticky" approval of 2^{256}-1 (infinite approval), this function fails, rendering the token unusable for protocols requiring max allowance.

function _allowance(address owner, address spender) internal view returns (uint256 remaining) {
assembly {
if or(iszero(owner), iszero(spender)) {
revert(0, 0)
}
let ptr := mload(0x40)
let baseSlot := _allowances.slot
mstore(ptr, owner)
mstore(add(ptr, 0x20), baseSlot)
let initialHash := keccak256(ptr, 0x40)
mstore(ptr, spender)
mstore(add(ptr, 0x20), initialHash)
let allowanceSlot := keccak256(ptr, 0x40)
remaining := sload(allowanceSlot)
}
}

Proof of Concept

// --- Fuzz Test 3: TransferFrom/Allowance Integrity ---
// Goal: Test allowance approval and spending, checking all three key balances.
function testFuzzTransferFrom(address spender, address receiver, uint256 allowanceAmount, uint256 spendAmount) public {
// Constraints
vm.assume(spender != ZERO_ADDRESS && receiver != ZERO_ADDRESS);
vm.assume(spender != ALICE);
vm.assume(spendAmount <= allowanceAmount); // Ensure spend is <= allowance
vm.assume(spendAmount <= token.balanceOf(ALICE)); // Ensure spend is <= balance
// State Capture
uint256 initialAliceBalance = token.balanceOf(ALICE);
uint256 initialReceiverBalance = token.balanceOf(receiver);
// 1. Approve (using the internal helper via wrapper for simplicity)
emit Approval(ALICE, spender, allowanceAmount);
assertEq(token.allowance(ALICE, spender), allowanceAmount, "Initial allowance failed");
// 2. Execute transferFrom (caller is the spender)
vm.prank(spender);
assertTrue(token.transferFrom(ALICE, receiver, spendAmount), "TransferFrom must succeed");
// 3. Post-execution checks
uint256 remainingAllowance = allowanceAmount - spendAmount;
assertEq(token.allowance(ALICE, spender), remainingAllowance, "Allowance was not spent correctly");
assertEq(token.balanceOf(ALICE), initialAliceBalance - spendAmount, "Alice's balance incorrect after spend");
assertEq(token.balanceOf(receiver), initialReceiverBalance + spendAmount, "Receiver's balance incorrect after spend");
}
Updates

Lead Judging Commences

gaurangbrdv Lead Judge 18 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!