{"file_path":"src/permissions/BitmaskVerifier.sol","creation_status":"success","source_code":"// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.25;\n\nimport \"../interfaces/permissions/ICustomVerifier.sol\";\nimport \"@openzeppelin/contracts/access/IAccessControl.sol\";\n\n/// @title BitmaskVerifier\n/// @notice Verifier contract implementing selective calldata hashing using bitmasking rules.\n/// @dev Enables flexible permissioning by allowing dynamic verification of transaction intent through partial data matching.\ncontract BitmaskVerifier is ICustomVerifier {\n    /// @notice Computes a hash of a call using selective masking over its fields.\n    /// @dev The hash is computed in the following sequence:\n    /// 1. Hashing `who` using `bitmask[0:32]`\n    /// 2. Hashing `where` using `bitmask[32:64]`\n    /// 3. Hashing `value` using `bitmask[64:96]`\n    /// 4. Iteratively hashing each byte of `data` masked with `bitmask[i + 96]`\n    ///\n    /// This allows whitelisting specific parts of calldata or sender/target/value.\n    ///\n    /// @param bitmask A byte array encoding which bytes should be included in the hash.\n    ///        - [0:32]    Mask for `who` (caller)\n    ///        - [32:64]   Mask for `where` (target contract)\n    ///        - [64:96]   Mask for `value` (ETH sent)\n    ///        - [96:]     Mask for each byte of `data` (calldata)\n    /// @param who Caller address.\n    /// @param where Target contract address.\n    /// @param value ETH value to be sent with the call.\n    /// @param data Full calldata of the call.\n    /// @return Hash of the masked components using `keccak256`.\n    function calculateHash(bytes calldata bitmask, address who, address where, uint256 value, bytes calldata data)\n        public\n        pure\n        returns (bytes32)\n    {\n        bytes32 hash_;\n        hash_ = keccak256(bytes.concat(hash_, bytes32(bitmask[0:32]) & bytes32(bytes20(who))));\n        hash_ = keccak256(bytes.concat(hash_, bytes32(bitmask[32:64]) & bytes32(bytes20(where))));\n        hash_ = keccak256(bytes.concat(hash_, bytes32(bitmask[64:96]) & bytes32(value)));\n        for (uint256 i = 0; i < data.length; i++) {\n            hash_ = keccak256(bytes.concat(hash_, (data[i] & bitmask[i + 96])));\n        }\n        return hash_;\n    }\n\n    /// @notice Verifies whether the provided call matches a pre-approved hash using a given bitmask.\n    /// @dev The expected hash and bitmask are ABI-encoded in `verificationData`.\n    /// - `verificationData = abi.encode(expectedHash, bitmask)`\n    /// - Function reverts to false if bitmask length does not match (data.length + 96).\n    ///\n    /// @param who Caller address of the original call.\n    /// @param where Target contract address.\n    /// @param value ETH value to be sent.\n    /// @param data Calldata for the function call.\n    /// @param verificationData ABI-encoded data containing:\n    ///        - bytes32 expectedHash\n    ///        - bytes bitmask (variable length)\n    /// @return True if the calculated masked hash matches the expected hash, false otherwise.\n    function verifyCall(address who, address where, uint256 value, bytes calldata data, bytes calldata verificationData)\n        public\n        pure\n        returns (bool)\n    {\n        bytes32 verificationHash_;\n        bytes calldata bitmask;\n        assembly {\n            verificationHash_ := calldataload(verificationData.offset)\n            let temp := add(verificationData.offset, calldataload(add(verificationData.offset, 0x20)))\n            bitmask.offset := add(temp, 0x20)\n            bitmask.length := calldataload(temp)\n        }\n        if (data.length + 0x60 != bitmask.length) {\n            return false;\n        }\n        return verificationHash_ == calculateHash(bitmask, who, where, value, data);\n    }\n}\n","deployed_bytecode":"0x608060405234801561000f575f80fd5b5060043610610034575f3560e01c806370e46bcb14610038578063d0b8c4a914610060575b5f80fd5b61004b6100463660046102da565b610081565b60405190151581526020015b60405180910390f35b61007361006e36600461036e565b6100cf565b604051908152602001610057565b5f8235602080850135850190810190358061009d8860606103e5565b146100ad575f93505050506100c4565b6100bc82828d8d8d8d8d6100cf565b831493505050505b979650505050505050565b5f80806bffffffffffffffffffffffff19606089901b166100f36020838c8e61040a565b6100fc91610431565b16604051602001610117929190918252602082015260400190565b60408051601f198184030181529181528151602092830120925082916bffffffffffffffffffffffff1960608a901b1691610153918c8e61040a565b61015c91610431565b16604051602001610177929190918252602082015260400190565b60405160208183030381529060405280519060200120905080855f1b8a8a6040906060926101a79392919061040a565b6101b091610431565b166040516020016101cb929190918252602082015260400190565b6040516020818303038152906040528051906020012090505f5b8381101561026d57818a8a6101fb8460606103e5565b81811061020a5761020a61044e565b9050013560f81c60f81b8686848181106102265761022661044e565b604080516020810196909652920135929092166001600160f81b031916908301525060410160408051601f19818403018152919052805160209091012091506001016101e5565b5098975050505050505050565b80356001600160a01b0381168114610290575f80fd5b919050565b5f8083601f8401126102a5575f80fd5b50813567ffffffffffffffff8111156102bc575f80fd5b6020830191508360208285010111156102d3575f80fd5b9250929050565b5f805f805f805f60a0888a0312156102f0575f80fd5b6102f98861027a565b96506103076020890161027a565b955060408801359450606088013567ffffffffffffffff8082111561032a575f80fd5b6103368b838c01610295565b909650945060808a013591508082111561034e575f80fd5b5061035b8a828b01610295565b989b979a50959850939692959293505050565b5f805f805f805f60a0888a031215610384575f80fd5b873567ffffffffffffffff8082111561039b575f80fd5b6103a78b838c01610295565b90995097508791506103bb60208b0161027a565b96506103c960408b0161027a565b955060608a0135945060808a013591508082111561034e575f80fd5b8082018082111561040457634e487b7160e01b5f52601160045260245ffd5b92915050565b5f8085851115610418575f80fd5b83861115610424575f80fd5b5050820193919092039150565b80356020831015610404575f19602084900360031b1b1692915050565b634e487b7160e01b5f52603260045260245ffdfea2646970667358221220be1d6d144158533863283032311004b2f5428f8b14ad463c3bdfa662cf3e1fdb64736f6c63430008190033","optimization_enabled":true,"verified_twin_address_hash":null,"is_verified":true,"compiler_settings":{"evmVersion":"cancun","libraries":{},"metadata":{"appendCBOR":true,"bytecodeHash":"ipfs","useLiteralContent":false},"optimizer":{"enabled":true,"runs":200},"outputSelection":{"*":{"":["*"],"*":["*"]}},"remappings":["forge-std/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/src/","@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/","@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/","erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/","halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/","openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/","openzeppelin-contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/"],"viaIR":false},"optimization_runs":200,"sourcify_repo_url":null,"decoded_constructor_args":null,"compiler_version":"v0.8.25+commit.b61c2a91","is_verified_via_verifier_alliance":false,"verified_at":"2026-05-12T19:36:15.668799Z","implementations":[],"proxy_type":null,"external_libraries":[],"creation_bytecode":"0x6080604052348015600e575f80fd5b506104988061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c806370e46bcb14610038578063d0b8c4a914610060575b5f80fd5b61004b6100463660046102da565b610081565b60405190151581526020015b60405180910390f35b61007361006e36600461036e565b6100cf565b604051908152602001610057565b5f8235602080850135850190810190358061009d8860606103e5565b146100ad575f93505050506100c4565b6100bc82828d8d8d8d8d6100cf565b831493505050505b979650505050505050565b5f80806bffffffffffffffffffffffff19606089901b166100f36020838c8e61040a565b6100fc91610431565b16604051602001610117929190918252602082015260400190565b60408051601f198184030181529181528151602092830120925082916bffffffffffffffffffffffff1960608a901b1691610153918c8e61040a565b61015c91610431565b16604051602001610177929190918252602082015260400190565b60405160208183030381529060405280519060200120905080855f1b8a8a6040906060926101a79392919061040a565b6101b091610431565b166040516020016101cb929190918252602082015260400190565b6040516020818303038152906040528051906020012090505f5b8381101561026d57818a8a6101fb8460606103e5565b81811061020a5761020a61044e565b9050013560f81c60f81b8686848181106102265761022661044e565b604080516020810196909652920135929092166001600160f81b031916908301525060410160408051601f19818403018152919052805160209091012091506001016101e5565b5098975050505050505050565b80356001600160a01b0381168114610290575f80fd5b919050565b5f8083601f8401126102a5575f80fd5b50813567ffffffffffffffff8111156102bc575f80fd5b6020830191508360208285010111156102d3575f80fd5b9250929050565b5f805f805f805f60a0888a0312156102f0575f80fd5b6102f98861027a565b96506103076020890161027a565b955060408801359450606088013567ffffffffffffffff8082111561032a575f80fd5b6103368b838c01610295565b909650945060808a013591508082111561034e575f80fd5b5061035b8a828b01610295565b989b979a50959850939692959293505050565b5f805f805f805f60a0888a031215610384575f80fd5b873567ffffffffffffffff8082111561039b575f80fd5b6103a78b838c01610295565b90995097508791506103bb60208b0161027a565b96506103c960408b0161027a565b955060608a0135945060808a013591508082111561034e575f80fd5b8082018082111561040457634e487b7160e01b5f52601160045260245ffd5b92915050565b5f8085851115610418575f80fd5b83861115610424575f80fd5b5050820193919092039150565b80356020831015610404575f19602084900360031b1b1692915050565b634e487b7160e01b5f52603260045260245ffdfea2646970667358221220be1d6d144158533863283032311004b2f5428f8b14ad463c3bdfa662cf3e1fdb64736f6c63430008190033","name":"BitmaskVerifier","is_blueprint":false,"license_type":"none","is_fully_verified":true,"is_verified_via_eth_bytecode_db":true,"language":"solidity","evm_version":"cancun","can_be_visualized_via_sol2uml":true,"is_verified_via_sourcify":false,"additional_sources":[{"file_path":"lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/access/IAccessControl.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.3.0) (access/IAccessControl.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev External interface of AccessControl declared to support ERC-165 detection.\n */\ninterface IAccessControl {\n    /**\n     * @dev The `account` is missing a role.\n     */\n    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);\n\n    /**\n     * @dev The caller of a function is not the expected one.\n     *\n     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.\n     */\n    error AccessControlBadConfirmation();\n\n    /**\n     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n     *\n     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n     * {RoleAdminChanged} not being emitted to signal this.\n     */\n    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n    /**\n     * @dev Emitted when `account` is granted `role`.\n     *\n     * `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).\n     * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.\n     */\n    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Emitted when `account` is revoked `role`.\n     *\n     * `sender` is the account that originated the contract call:\n     *   - if using `revokeRole`, it is the admin role bearer\n     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)\n     */\n    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) external view returns (bool);\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function grantRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function revokeRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function's\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been granted `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `callerConfirmation`.\n     */\n    function renounceRole(bytes32 role, address callerConfirmation) external;\n}\n"},{"file_path":"src/interfaces/permissions/ICustomVerifier.sol","source_code":"// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.25;\n\n/// @notice Interface for external/custom verification logic\n/// @dev Allows plug-in modules to define arbitrary logic for verifying function calls.\n///      Used with `VerificationType.CUSTOM_VERIFIER` in the main Verifier contract.\ninterface ICustomVerifier {\n    /// @notice Verifies whether the given call is permitted using custom logic\n    /// @param who               Address attempting the call\n    /// @param where             Target contract the call is directed to\n    /// @param value             ETH value sent with the call\n    /// @param callData          Full calldata of the intended call\n    /// @param verificationData  Extra data provided by the caller to support verification logic\n    /// @return isValid          True if the call is considered valid, false otherwise\n    function verifyCall(\n        address who,\n        address where,\n        uint256 value,\n        bytes calldata callData,\n        bytes calldata verificationData\n    ) external view returns (bool);\n}\n"}],"certified":false,"conflicting_implementations":null,"abi":[{"inputs":[{"internalType":"bytes","name":"bitmask","type":"bytes"},{"internalType":"address","name":"who","type":"address"},{"internalType":"address","name":"where","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"calculateHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"},{"internalType":"address","name":"where","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bytes","name":"verificationData","type":"bytes"}],"name":"verifyCall","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"}],"is_changed_bytecode":false,"is_partially_verified":false,"constructor_args":null}