Project

One World
NFTDeFi
15,000 USDC
View results
Submission Details
Severity: low
Invalid

Ambiguous Logic in `OWPIdentity::_update` Function Can Lead to Inefficient or Unnecessary Checks

Summary

In the OWPIdentity.sol contract, the internal function _update overrides the ERC1155 _update function. The overriding function includes a require check that uses a logical OR operator to impose restrictions. However, this check is ambiguous and could lead to unnecessary or meaningless logic, making the contract potentially inefficient.

Vulnerability Details

The OWPIdentity::_update function includes the following code:

// The following functions are overrides required by Solidity.
function _update(address from, address to, uint256[] memory ids, uint256[] memory amounts)
internal
virtual
override(ERC1155, ERC1155Supply)
{
@> // @info: Ambiguous check
require(from == address(0) || to == address(0), "OWPIdentity: NFT Not transferrable.");
super._update(from, to, ids, amounts);
}

The require statement checks whether either the from or to address is address(0). While this check might seem reasonable at first glance, it is problematic because it is based on an incorrect assumption. According to the ERC-1155 standard, the _update function is called in scenarios such as minting, transferring, and burning tokens. In none of these operations should both from and to be the zero address (address(0)), meaning the require condition is essentially redundant and not applicable in any valid scenario.

The issue arises due to the short-circuiting behavior in Boolean logic. In computer science, when using a logical OR (||) operator, if the first condition evaluates to true, the second condition is not evaluated at all. This is because the overall expression is already guaranteed to be true once one of the conditions is met. As a result, the second condition is effectively skipped, and control proceeds to the next line of code.

Proof of Concept (PoC) for Short-Circuiting Behavior

The following is a proof of concept demonstrating the short-circuiting behavior in logical operations. This is a Foundry test, so please make sure you are using Foundry to run it.

Steps to Run the Test

  1. Create the test file
    Add the following test to any test file of your choice.

function testShortCircuit() public pure {
bool isNotShortCircuit = false;
bool intoFalseOfNotShortCircuit = false;
// As we know from logic gates:
// 0 OR 0 = 0
// 0 OR 1 = 1
// 1 OR 0 = 1
// 1 OR 1 = 1
require(
address(1) == address(1) || address(2) == address(2)
? isNotShortCircuit = true
: intoFalseOfNotShortCircuit = true,
"something went wrong"
);
// Observe the effects of short-circuiting
console.log("isNotShortCircuit: ", isNotShortCircuit);
console.log("intoFalseOfNotShortCircuit: ", intoFalseOfNotShortCircuit);
}
  1. Run the Test
    Open your terminal and execute the following command to run the test:

forge test --mt testShortCircuit -vv
  1. Review the Logs
    After running the test, you should see output similar to the following in your terminal:

[⠊] Compiling...
[⠃] Compiling 1 files with Solc 0.8.22
[⠒] Solc 0.8.22 finished in 3.81s
Compiler run successful!
Ran 1 test for test/MembershipERC1155Test.t.sol:MembershipERC1155Test
[PASS] testShortCircuit() (gas: 4228)
Logs:
isNotShortCircuit: true
intoFalseOfNotShortCircuit: false
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.10ms (302.50µs CPU time)
Ran 1 test suite in 9.74ms (3.10ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Observations

As demonstrated in the log output, short-circuiting behavior is in action. When the first condition in the logical OR (address(1) == address(1)) evaluates to true, the second condition (address(2) == address(2)) is not evaluated. This is a clear example of short-circuiting, which prevents unnecessary computations and ensures that only the first condition's result is used to determine the outcome.

In this case:

  • The variable isNotShortCircuit is set to true, as expected from the first condition.

  • The variable intoFalseOfNotShortCircuit remains false, as the second condition is not evaluated.

This demonstrates the short-circuiting phenomenon in action, which is fundamental to understanding how logical operators work in Solidity and most programming languages.

Impact

  • Inefficient Code: The check require(from == address(0) || to == address(0), "OWPIdentity: NFT Not transferrable.") introduces unnecessary logic. It is impossible for both from and to to be address(0) in normal token operations (minting, transferring, burning). The inclusion of this check causes needless gas usage and adds complexity without providing any real security or validation.

  • Meaningless Event Emissions: If both from and to were somehow set to address(0) (which is not possible in typical use cases), the require statement would allow the function to proceed, leading to a meaningless event emission (either TransferSingle or TransferBatch) that could confuse developers and users, and clutter event logs.

  • Ambiguity: The logic used in this check is ambiguous and could create confusion for developers reviewing the code, as the condition contradicts the intended flow of ERC-1155 token operations.

Tools Used

Manual Code Review

Recommendations

The require condition should be revised to make it more explicit and logically sound. The intent appears to be to restrict token transfers or operations to only minting (when from == address(0)) or burning (when to == address(0)). Therefore, the require condition should be updated as follows:

function _update(address from, address to, uint256[] memory ids, uint256[] memory amounts)
internal
virtual
override(ERC1155, ERC1155Supply)
{
- require(from == address(0) || to == address(0), "OWPIdentity: NFT Not transferrable.");
+ require((from == address(0) && to != address(0)) || (to == address(0) && from != address(0)), "OWPIdentity: NFT Not transferrable.");
super._update(from, to, ids, amounts);
}

This revised condition ensures that:

  • Tokens can only be minted (when from == address(0) and to != address(0)).

  • Tokens can only be burned (when to == address(0) and from != address(0)).

  • Any other transfer scenario will be blocked, preventing unnecessary logic and improving code clarity.

By making this adjustment, the contract will be more efficient, readable, and aligned with the intended ERC-1155 behavior.

Updates

Lead Judging Commences

0xbrivan2 Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice
0xbrivan2 Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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

Give us feedback!