// File: script/foundry/utils/HooksFactory.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol";
import { Hook } from "contracts/lib/hooks/Hook.sol";

contract HooksFactory {
    function deploy(bytes memory code_, uint256 hookTypeFlag_, uint256 seed_) external returns (address hookAddr) {
        uint256 randomNumber = uint256(keccak256(abi.encodePacked(seed_)));
        for (uint256 i = 0; i < 1500; i++) {
            bytes32 salt = bytes32(randomNumber + i);
            bytes32 bytecodeHash = keccak256(code_);
            address expectedAddress = Create2.computeAddress(salt, bytecodeHash);
            uint160 prefix = hookTypeFlag_ == Hook.SYNC_FLAG ? 0x02 : 0x01;
            if (_doesAddressStartWith(expectedAddress, prefix)) {
                hookAddr = Create2.deploy(0, salt, code_);
                return hookAddr;
            }
        }
    }

    function _doesAddressStartWith(address address_,uint160 prefix_) private pure returns (bool) {
        return uint160(address_) >> (160 - 2) == prefix_;
    }
}
// File: node_modules/@openzeppelin/contracts/utils/Create2.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Create2.sol)

pragma solidity ^0.8.0;

/**
 * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
 * `CREATE2` can be used to compute in advance the address where a smart
 * contract will be deployed, which allows for interesting new mechanisms known
 * as 'counterfactual interactions'.
 *
 * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
 * information.
 */
library Create2 {
    /**
     * @dev Deploys a contract using `CREATE2`. The address where the contract
     * will be deployed can be known in advance via {computeAddress}.
     *
     * The bytecode for a contract can be obtained from Solidity with
     * `type(contractName).creationCode`.
     *
     * Requirements:
     *
     * - `bytecode` must not be empty.
     * - `salt` must have not been used for `bytecode` already.
     * - the factory must have a balance of at least `amount`.
     * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
     */
    function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
        require(address(this).balance >= amount, "Create2: insufficient balance");
        require(bytecode.length != 0, "Create2: bytecode length is zero");
        /// @solidity memory-safe-assembly
        assembly {
            addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
        }
        require(addr != address(0), "Create2: Failed on deploy");
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
     * `bytecodeHash` or `salt` will result in a new destination address.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
        return computeAddress(salt, bytecodeHash, address(this));
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
     * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40) // Get free memory pointer

            // |                   | ↓ ptr ...  ↓ ptr + 0x0B (start) ...  ↓ ptr + 0x20 ...  ↓ ptr + 0x40 ...   |
            // |-------------------|---------------------------------------------------------------------------|
            // | bytecodeHash      |                                                        CCCCCCCCCCCCC...CC |
            // | salt              |                                      BBBBBBBBBBBBB...BB                   |
            // | deployer          | 000000...0000AAAAAAAAAAAAAAAAAAA...AA                                     |
            // | 0xFF              |            FF                                                             |
            // |-------------------|---------------------------------------------------------------------------|
            // | memory            | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
            // | keccak(start, 85) |            ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |

            mstore(add(ptr, 0x40), bytecodeHash)
            mstore(add(ptr, 0x20), salt)
            mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
            let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
            mstore8(start, 0xff)
            addr := keccak256(start, 85)
        }
    }
}

// File: contracts/lib/hooks/Hook.sol



// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.19;

import { IHook } from "contracts/interfaces/hooks/base/IHook.sol";

/// @title Hooks Library
/// @notice This library defines the ExecutionContext struct used when executing hooks.
/// @dev The ExecutionContext struct contains two fields: config and params, both of type bytes.
library Hook {
    uint256 internal constant SYNC_FLAG = 1 << 159;
    uint256 internal constant ASYNC_FLAG = 1 << 158;
    /// @notice Defines the execution context for a hook.
    /// @dev The ExecutionContext struct is used as a parameter when executing hooks.
    struct ExecutionContext {
        /// @notice The configuration data for the hook, encoded as bytes.
        /// @dev This data is used to configure the hook before execution.
        /// The configuration is stored in the Module.
        bytes config;
        /// @notice The parameters for the hook, encoded as bytes.
        /// @dev These parameters are passed from the external caller when executing modules.
        bytes params;
    }

    /// @notice Checks if the hook can support synchronous calls.
    /// @dev This function checks if the first bit of the hook address is set to 1,
    ///      indicating that the hook can support synchronous calls.
    /// @param self_ The hook to check.
    /// @return A boolean indicating if the hook can support synchronous calls.
    function canSupportSyncCall(IHook self_) internal pure returns (bool) {
        return uint256(uint160(address(self_))) & SYNC_FLAG != 0;
    }

    /// @notice Checks if the hook can support asynchronous calls.
    /// @dev This function checks if the second bit of the hook address is set to 1,
    ///      indicating that the hook can support asynchronous calls.
    /// @param self_ The hook to check.
    /// @return A boolean indicating if the hook can support asynchronous calls.
    function canSupportAsyncCall(IHook self_) internal pure returns (bool) {
        return uint256(uint160(address(self_))) & ASYNC_FLAG != 0;
    }
}

// File: contracts/interfaces/hooks/base/IHook.sol



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

/// @notice Enum for representing various states of an async hook.
enum HookResult {
    Pending, // Indicates the hook is ongoing execution.
    Completed // Indicates the hook has successfully completed.
}

/// @title Hook Interface.
/// @notice This interface defines methods for synchronous and asynchronous hooks.
/// @dev Hooks are used to execute custom logic in response to certain events or conditions.
interface IHook {
    /// @notice Executes a synchronous hook.
    /// @param hookContext_ The context of an executing hook. It is an encoded version of Hook.ExecutionContext
    /// @return result The result of the hook execution.
    /// @return hookData The data returned by the hook.
    function executeSync(bytes calldata hookContext_) external returns (HookResult result, bytes memory hookData);

    /// @notice Executes an asynchronous hook.
    /// @param hookContext_ The context of an executing hook. It is an encoded version of Hook.ExecutionContext
    /// @param callbackHandler_ The address of the callback handler.
    /// @return result The result of the hook execution.
    /// @return hookData The data returned by the hook.
    /// @return requestId The id of the request.
    function executeAsync(
        bytes calldata hookContext_,
        address callbackHandler_
    ) external returns (HookResult result, bytes memory hookData, bytes32 requestId);

    /// @notice Validates the configuration for the hook.
    /// @dev This should be overridden by hook implementations to provide custom validation logic.
    /// @param hookConfig_ The configuration data for the hook.
    function validateConfig(bytes calldata hookConfig_) external view;
}