Skip to content

[openzeppelin-revm] ERC165Checker fails with OutOfGas #221

@sekisamu

Description

@sekisamu

Problem Description

ERC165Checker test cases fail on local network(revm), node log shows that all fail with OutOfGas error.

Environment

OpenZeppelin Contracts Repository:

Polkadot SDK (REVM Runtime):

How to Reproduce

  1. Start REVM node (torsten/gas-fixes branch)
  2. Run P256 tests:
    cd openzeppelin-contracts-revm
    git checkout test-local-revm
    npm installl
    npx hardhat test --network local --grep 'ERC165Checker'

Test Results

30 passing (4s)
  15 failing

  1) ERC165Checker
       Return bomb resistance:

      AssertionError: expected 20904970957 to be below 120000.
      + expected - actual

      -20904970957
      +120000

      at Context.<anonymous> (test/utils/introspection/ERC165Checker.test.js:264:28)
      at processTicksAndRejections (node:internal/process/task_queues:105:5)

  2) ERC165Checker
       ERC165 malicious return data
         does not support mock interface via supportsERC165InterfaceUnchecked:

      AssertionError: expected false to be true
      + expected - actual

      -false
      +true



  3) ERC165Checker
       ERC165 revert on invalid interface
         support mock interface via supportsERC165InterfaceUnchecked:

      AssertionError: expected false to be true
      + expected - actual

      -false
      +true



  4) ERC165Checker
       ERC165 supported
         supports ERC165:

      AssertionError: expected false to be true
      + expected - actual

      -false
      +true



  5) ERC165Checker
       ERC165 and single interface supported
         supports ERC165:

      AssertionError: expected false to be true
      + expected - actual

      -false
      +true



  6) ERC165Checker
       ERC165 and single interface supported
         supports mock interface via supportsInterface:

      AssertionError: expected false to be true
      + expected - actual

      -false
      +true



  7) ERC165Checker
       ERC165 and single interface supported
         supports mock interface via supportsAllInterfaces:

      AssertionError: expected false to be true
      + expected - actual

      -false
      +true



  8) ERC165Checker
       ERC165 and single interface supported
         supports mock interface via getSupportedInterfaces:
     AssertionError: expected Result(1) [ false ] to deeply equal [ true ]
      at /Users/suvi/Documents/paritytech/papermoon/code/openzeppelin-contracts-revm/node_modules/chai-as-promised/lib/chai-as-promised.js:302:22
      at processTicksAndRejections (node:internal/process/task_queues:105:5)
      at Context.<anonymous> (test/utils/introspection/ERC165Checker.test.js:170:7)

  9) ERC165Checker
       ERC165 and single interface supported
         supports mock interface via supportsERC165InterfaceUnchecked:

      AssertionError: expected false to be true
      + expected - actual

      -false
      +true



  10) ERC165Checker
       ERC165 and many interfaces supported
         supports ERC165:

      AssertionError: expected false to be true
      + expected - actual

      -false
      +true



  11) ERC165Checker
       ERC165 and many interfaces supported
         supports each interfaceId via supportsInterface:

      AssertionError: expected false to be true
      + expected - actual

      -false
      +true



  12) ERC165Checker
       ERC165 and many interfaces supported
         supports all interfaceIds via supportsAllInterfaces:

      AssertionError: expected false to be true
      + expected - actual

      -false
      +true



  13) ERC165Checker
       ERC165 and many interfaces supported
         supports all interfaceIds via getSupportedInterfaces:
     AssertionError: expected Result(3) [ false, false, false ] to deeply equal [ true, true, true ]
      at /Users/suvi/Documents/paritytech/papermoon/code/openzeppelin-contracts-revm/node_modules/chai-as-promised/lib/chai-as-promised.js:302:22
      at processTicksAndRejections (node:internal/process/task_queues:105:5)
      at Context.<anonymous> (test/utils/introspection/ERC165Checker.test.js:210:7)

  14) ERC165Checker
       ERC165 and many interfaces supported
         supports not all of the interfaces queried via getSupportedInterfaces:
     AssertionError: expected Result(4) [ false, false, false, false ] to deeply equal [ true, true, true, false ]
      at /Users/suvi/Documents/paritytech/papermoon/code/openzeppelin-contracts-revm/node_modules/chai-as-promised/lib/chai-as-promised.js:302:22
      at processTicksAndRejections (node:internal/process/task_queues:105:5)
      at Context.<anonymous> (test/utils/introspection/ERC165Checker.test.js:226:7)

  15) ERC165Checker
       ERC165 and many interfaces supported
         supports each interfaceId via supportsERC165InterfaceUnchecked:

      AssertionError: expected false to be true
      + expected - actual

      -false
      +true

Root Cause Analysis

Interface Detection Failures

All tests expecting true for supported interfaces incorrectly return false:

  1. ❌ supportsERC165() - returns false for ERC165-compliant contracts
  2. ❌ supportsInterface() - returns false for supported interfaces
  3. ❌ supportsAllInterfaces() - returns false for all supported interfaces
  4. ❌ getSupportedInterfaces() - returns [false, false, ...] instead of [true, true, ...]
  5. ❌ supportsERC165InterfaceUnchecked() - returns false for valid interfaces

Contract Code Analysis

The issue originates in ERC165Checker.sol at line 135:

function _trySupportsInterface(
    address account,
    bytes4 interfaceId
) private view returns (bool success, bool supported) {
    bytes4 selector = IERC165.supportsInterface.selector;
    assembly ("memory-safe") {
        mstore(0x00, selector)
        mstore(0x04, interfaceId)
        success := staticcall(
            30000,           // ← HARDCODED 30k gas limit
            account,
            0x00,
            0x24,
            0x00,
            0x20
        )
        supported := and(
            gt(returndatasize(), 0x1F),
            iszero(iszero(mload(0x00)))
        )
    }
}

From the code we could see it hardcoded a 3000 gas limit for supportInterface check operation, which is insufficient in revm.

Impact Analysis

Affected Methods

All ERC165Checker public methods rely on _trySupportsInterface:

supportsERC165(address account)
✓ supportsInterface(address account, bytes4 interfaceId)
✓ supportsAllInterfaces(address account, bytes4[] interfaceIds)
✓ getSupportedInterfaces(address account, bytes4[] interfaceIds)
✓ supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId)

OpenZeppelin Contracts Affected

Direct Usage:

  • AccessControl - role interface detection
  • ERC721 - receiver interface detection
  • ERC1155 - receiver interface detection
  • ERC2981 - royalty standard detection
  • SafeERC20 - permit detection

Ecosystem Impact:

  • Token standards (ERC721, ERC1155) cannot verify receiver capabilities
  • Cross-contract interface detection breaks
  • Upgradeable contracts cannot validate interface compatibility
  • Account abstraction cannot detect wallet capabilities

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions