Token-0x

First Flight #54
Beginner FriendlyDeFi
100 EXP
View results
Submission Details
Severity: low
Valid

Allowance Error Ambiguity in _allowance() Function

Root + Impact

Description

  • Normal error handling in ERC20 implementations should provide specific error selectors to distinguish between different failure modes. The _allowance() function in Token-0x's base implementation uses a generic revert(0, 0) for both invalid owner and invalid spender addresses, making it impossible to determine which parameter caused the validation failure.


  • Other functions like _approve() correctly use specific error selectors from IERC20Errors , but _allowance() fails to follow this pattern .

function _allowance(address owner, address spender) internal view returns (uint256 remaining) {
assembly {
@> if or(iszero(owner), iszero(spender)) {
@> revert(0, 0) // Generic revert - cannot distinguish which parameter failed
}
// ... rest of function
}
}

Risk

Likelihood:

  • Every call to allowance() with zero addresses triggers this ambiguous error

  • DeFi integrations that need to handle different error types cannot distinguish failures

  • Any frontend or contract interacting with the token will experience this ambiguity

Impact:

  • Poor debugging experience - developers cannot identify which parameter is invalid

  • Frontend error handling becomes generic and unhelpful for users

  • Smart contract integrations cannot implement different logic for different error types

Proof of Concept

The test demonstrates that both invalid owner and invalid spender addresses produce identical generic reverts. This makes it impossible for callers to determine whether the owner or spender parameter caused the failure, forcing them to treat all validation failures identically.

function test_AllowanceErrorAmbiguity() public {
VulnerableToken baseToken = new VulnerableToken();
// Test zero address revert - should be specific but isn't
try baseToken.allowance(address(0), makeAddr("spender")) {
fail("Should revert");
} catch (bytes memory reason) {
// Generic revert with no error selector
assertEq(reason.length, 0, "Generic revert instead of specific error");
}
// Test zero spender revert - should be specific but isn't
try baseToken.allowance(makeAddr("owner"), address(0)) {
fail("Should revert");
} catch (bytes memory reason) {
// Same generic revert - can't distinguish which parameter failed
assertEq(reason.length, 0, "Cannot distinguish error type");
}
}

Recommended Mitigation

Replace the generic revert with specific error selectors that match the IERC20Errors interface. This allows callers to distinguish between invalid owner and invalid spender errors, enabling proper error handling and debugging.

function _allowance(address owner, address spender) internal view returns (uint256 remaining) {
assembly {
- if or(iszero(owner), iszero(spender)) {
- revert(0, 0)
- }
+ if iszero(owner) {
+ mstore(0x00, shl(224, 0xe602df05)) // ERC20InvalidApprover
+ mstore(add(0x00, 4), owner)
+ revert(0x00, 0x24)
+ }
+ if iszero(spender) {
+ mstore(0x00, shl(224, 0x94280d62)) // ERC20InvalidSpender
+ mstore(add(0x00, 4), spender)
+ revert(0x00, 0x24)
+ }
// ... rest of function
}
}
Updates

Lead Judging Commences

gaurangbrdv Lead Judge
24 days ago
gaurangbrdv Lead Judge 19 days ago
Submission Judgement Published
Validated
Assigned finding tags:

accounting

accounting related issue in token-0x

Support

FAQs

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

Give us feedback!