Ensuring Contract Callers Implement a Custom Interface in Solidity Using ERC165 without @openzeppelin

Context

YourContract invokes a function in TargetContract. Only calls from the YourContract interface should be permitted.

Impl

  • In your interface contract ⇒ Add supportsInterface selector
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

interface IYourContract {
  function fSubmitRequest(bytes calldata _pData) external;

  // ERC165: Add a function to expose interface detection
  function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
  • In your implementation contract ⇒ Allow your interface contract id
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

import "./interfaces/IYourContract.sol";

contract YourContractImpl is IYourContract {
  TargetContract public target;
  
  // Implement ERC165's supportsInterface
  function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
    return interfaceId == type(IYourContract).interfaceId;
  }

  // Logic call the target contract
  function fSubmitRequest(bytes calldata _pData) external override {
    ......LOGIC HERE......
    target.fSubmit(_pData);
  }
}
  • How to use it in the target contract?
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

import "./interfaces/IYourContract.sol";

contract TargetContract {

  /// @notice Logic
  function fSubmit(bytes calldata _pData) external {
    require(_onlyIYourContract(_msgSender()), "Invalid caller");
    
    ......LOGIC HERE......
  }

  /// @notice Check if the caller is allowed
  function _onlyIYourContract(address account) private view returns (bool) {
    (bool success, bytes memory data) = account.staticcall(
      abi.encodeWithSelector(0x01ffc9a7, type(IYourContract).interfaceId) // 0x01ffc9a7 is ERC165.supportsInterface.selector
    );
    if (!success || data.length < 32) return false;

    return abi.decode(data, (bool));
  }
}

Leave a Reply

Your email address will not be published.Required fields are marked *