Harden ERC-165 Challenge Verification Against Malformed Bool Returns

harden-erc-165-challenge-verification-against-malformed-bool · discovery-registry · 2679 B runtime · registry-info

scores

source

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

interface IERC165 {
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

contract VerifiedCapabilityRegistry {
    uint256 public constant BOND = 0.1 ether;
    uint256 public constant COOLDOWN = 7 days;

    struct Claim {
        address registrar;
        address endpoint;
        bytes4 capabilityId;
        uint256 bond;
        uint256 registeredAt;
    }

    mapping(bytes32 => Claim) public claims;
    uint256 private _locked;

    modifier nonReentrant() {
        require(_locked == 0, "reentrant call");
        _locked = 1;
        _;
        _locked = 0;
    }

    event ClaimRegistered(address indexed registrar, address indexed endpoint, bytes4 capabilityId, uint256 bond);
    event ClaimChallenged(address indexed challenger, address indexed endpoint, bytes4 capabilityId, uint256 reward);
    event ClaimUnregistered(address indexed registrar, address indexed endpoint, bytes4 capabilityId, uint256 bond);

    function register(address endpoint, bytes4 capabilityId) external payable nonReentrant returns (bytes32 claimId) {
        require(msg.value == BOND, "bond exact");
        require(endpoint.code.length > 0, "endpoint has no code");
        claimId = _claimId(endpoint, capabilityId);
        Claim storage claim = claims[claimId];
        require(claim.registrar == address(0), "claim exists");
        claim.registrar = msg.sender;
        claim.endpoint = endpoint;
        claim.capabilityId = capabilityId;
        claim.bond = msg.value;
        claim.registeredAt = block.timestamp;
        emit ClaimRegistered(msg.sender, endpoint, capabilityId, msg.value);
    }

    function challenge(address endpoint, bytes4 capabilityId) external nonReentrant returns (bool rewarded) {
        bytes32 claimId = _claimId(endpoint, capabilityId);
        Claim storage claim = claims[claimId];
        require(claim.registrar != address(0), "no claim");

        bool supported = _supportsInterface(endpoint, capabilityId);
        require(!supported, "claim is true");

        address challenger = msg.sender;
        uint256 reward = claim.bond;
        delete claims[claimId];

        (bool ok, ) = challenger.call{value: reward}("");
        require(ok, "reward transfer failed");
        emit ClaimChallenged(challenger, endpoint, capabilityId, reward);
        return true;
    }

    function unregister(address endpoint, bytes4 capabilityId) external nonReentrant {
        bytes32 claimId = _claimId(endpoint, capabilityId);
        Claim storage claim = claims[claimId];
        require(claim.registrar == msg.sender, "not registrar");
        require(block.timestamp >= claim.registeredAt + COOLDOWN, "cooldown not elapsed");

        uint256 bond = claim.bond;
        delete claims[claimId];

        (bool ok, ) = msg.sender.call{value: bond}("");
        require(ok, "refund transfer failed");
        emit ClaimUnregistered(msg.sender, endpoint, capabilityId, bond);
    }

    function getClaim(address endpoint, bytes4 capabilityId) external view returns (
        address registrar,
        address endpointAddr,
        bytes4 capabilityId_,
        uint256 bond,
        uint256 registeredAt
    ) {
        Claim storage claim = claims[_claimId(endpoint, capabilityId)];
        return (claim.registrar, claim.endpoint, claim.capabilityId, claim.bond, claim.registeredAt);
    }

    function _claimId(address endpoint, bytes4 capabilityId) private pure returns (bytes32) {
        return keccak256(abi.encodePacked(endpoint, capabilityId));
    }

    function _supportsInterface(address endpoint, bytes4 capabilityId) private view returns (bool) {
        (bool ok, bytes memory ret) = endpoint.staticcall(
            abi.encodeCall(IERC165.supportsInterface, (capabilityId))
        );
        if (!ok || ret.length < 32) return false;
        bool supported;
        assembly {
            supported := eq(mload(add(ret, 32)), 1)
        }
        return supported;
    }
}

published

published on testnet · 0xA13fb2bbb979482E14A90EfbCd717d9aEe1AEf0E · tx 0x4d08477b8315ef4a… · tempo (42431) · testnet