ETH Price: $2,411.12 (+1.39%)
Gas: 7.97 Gwei

Contract

0x1F6E5215D3DE35Dbaa2Fe0Ff61B7f8843b53b678
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040153946932022-08-23 5:13:18774 days ago1661231598IN
 Create: HookERC721MultiVaultImplV1
0 ETH0.009286424.31813771

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
HookERC721MultiVaultImplV1

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 10 runs

Other Settings:
default evmVersion, MIT license
File 1 of 15 : HookERC721MultiVaultImplV1.sol
// SPDX-License-Identifier: MIT
//
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        ██████████████                                        ██████████████
//        ██████████████          ▄▄████████████████▄▄         ▐█████████████▌
//        ██████████████    ▄█████████████████████████████▄    ██████████████
//         ██████████▀   ▄█████████████████████████████████   ██████████████▌
//          ██████▀   ▄██████████████████████████████████▀  ▄███████████████
//           ███▀   ██████████████████████████████████▀   ▄████████████████
//            ▀▀  ████████████████████████████████▀▀   ▄█████████████████▌
//              █████████████████████▀▀▀▀▀▀▀      ▄▄███████████████████▀
//             ██████████████████▀    ▄▄▄█████████████████████████████▀
//            ████████████████▀   ▄█████████████████████████████████▀  ██▄
//          ▐███████████████▀  ▄██████████████████████████████████▀   █████▄
//          ██████████████▀  ▄█████████████████████████████████▀   ▄████████
//         ██████████████▀   ███████████████████████████████▀   ▄████████████
//        ▐█████████████▌     ▀▀▀▀████████████████████▀▀▀▀      █████████████▌
//        ██████████████                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████

pragma solidity ^0.8.10;

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

import "./interfaces/IHookERC721Vault.sol";
import "./interfaces/IERC721FlashLoanReceiver.sol";
import "./interfaces/IHookProtocol.sol";
import "./lib/Entitlements.sol";
import "./lib/Signatures.sol";
import "./mixin/EIP712.sol";

/// @title  HookMultiVault-implementation of a Vault for multiple assets within a NFT collection, with entitlements.
/// @author Jake Nyquist - [email protected]
/// @custom:coauthor Regynald [email protected]
/// @notice HookVault holds a multiple NFT asset in escrow on behalf of multiple beneficial owners. Other contracts
/// are able to register "entitlements" for a fixed period of time on the asset, which give them the ability to
/// change the vault's owner.
/// @dev This contract implements ERC721Receiver
/// This contract views the tokenId for the asset on the ERC721 contract as the corresponding assetId for that asset
/// when deposited into the vault
contract HookERC721MultiVaultImplV1 is
  IHookERC721Vault,
  EIP712,
  Initializable,
  ReentrancyGuard
{
  /// ----------------  STORAGE ---------------- ///

  /// @dev these are the NFT contract address and tokenId the vault is covering
  IERC721 internal _nftContract;

  struct Asset {
    address beneficialOwner;
    address operator;
    uint32 expiry;
  }

  /// @dev the current entitlement applied to each asset, which includes the beneficialOwner
  /// for the asset
  /// if the entitled operator field is non-null, it means an unreleased entitlement has been
  /// applied; however, that entitlement could still be expired (if block.timestamp > entitlement.expiry)
  mapping(uint32 => Asset) internal assets;

  // Mapping from asset ID to approved address
  mapping(uint32 => address) private _assetApprovals;

  IHookProtocol internal _hookProtocol;

  /// Upgradeable Implementations cannot have a constructor, so we call the initialize instead;
  constructor() {}

  ///-constructor
  function initialize(address nftContract, address hookAddress)
    public
    initializer
  {
    setAddressForEipDomain(hookAddress);
    _nftContract = IERC721(nftContract);
    _hookProtocol = IHookProtocol(hookAddress);
  }

  /// ---------------- PUBLIC FUNCTIONS ---------------- ///

  ///
  /// @dev See {IERC165-supportsInterface}.
  ///
  function supportsInterface(bytes4 interfaceId)
    public
    view
    virtual
    returns (bool)
  {
    return
      interfaceId == type(IHookERC721Vault).interfaceId ||
      interfaceId == type(IERC165).interfaceId;
  }

  /// @dev See {IHookERC721Vault-withdrawalAsset}.
  /// @dev withdrawals can only be performed to the beneficial owner if there are no entitlements
  function withdrawalAsset(uint32 assetId) public virtual nonReentrant {
    require(
      !hasActiveEntitlement(assetId),
      "withdrawalAsset-the asset cannot be withdrawn with an active entitlement"
    );
    require(
      assets[assetId].beneficialOwner == msg.sender,
      "withdrawalAsset-only the beneficial owner can withdrawal an asset"
    );

    _nftContract.safeTransferFrom(
      address(this),
      assets[assetId].beneficialOwner,
      _assetTokenId(assetId)
    );

    emit AssetWithdrawn(assetId, msg.sender, assets[assetId].beneficialOwner);
  }

  /// @dev See {IHookERC721Vault-imposeEntitlement}.
  /// @dev The entitlement must be signed by the current beneficial owner of the contract. Anyone can submit the
  /// entitlement
  function imposeEntitlement(
    address operator,
    uint32 expiry,
    uint32 assetId,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) public virtual {
    // check that the asset has a current beneficial owner
    // before creating a new entitlement
    require(
      assets[assetId].beneficialOwner != address(0),
      "imposeEntitlement-beneficial owner must be set to impose an entitlement"
    );

    // the beneficial owner of an asset is able to set any entitlement on their own asset
    // as long as it has not already been committed to someone else.
    _verifyAndRegisterEntitlement(operator, expiry, assetId, v, r, s);
  }

  /// @dev See {IHookERC721Vault-grantEntitlement}.
  /// @dev The entitlement must be sent by the current beneficial owner
  function grantEntitlement(Entitlements.Entitlement calldata entitlement)
    external
  {
    require(
      assets[entitlement.assetId].beneficialOwner == msg.sender ||
        _assetApprovals[entitlement.assetId] == msg.sender,
      "grantEntitlement-only the beneficial owner or approved operator can grant an entitlement"
    );

    // the beneficial owner of an asset is able to directly set any entitlement on their own asset
    // as long as it has not already been committed to someone else.

    _registerEntitlement(
      entitlement.assetId,
      entitlement.operator,
      entitlement.expiry,
      msg.sender
    );
  }

  /// @dev See {IERC721Receiver-onERC721Received}.
  ///
  /// Always returns `IERC721Receiver.onERC721Received.selector`.
  function onERC721Received(
    address operator, // this arg is the address of the operator
    address from,
    uint256 tokenId,
    bytes calldata data
  ) external virtual override returns (bytes4) {
    require(
      tokenId <= type(uint32).max,
      "onERC721Received-tokenId is out of range"
    );
    /// (1) When receiving a nft from the ERC-721 contract this vault covers, create a new entitlement entry
    /// with the sender as the beneficial owner to track the asset within the vault.
    ///
    /// (1a) If the transfer additionally specifies data (i.e. an abi-encoded entitlement), the entitlement will
    /// be imposed via that transfer, including a new beneficial owner.
    ///     NOTE: this is an opinionated approach, however, the authors believe that anyone with the ability to
    ///     transfer the asset into this contract could also trivially transfer the asset to another address
    ///     they control and then deposit, so allowing this method of setting the beneficial owner simply
    ///     saves gas and has no practical impact on the rights a hypothetical sender has regarding the asset.
    ///
    /// (2) If another nft is sent to the contract, we should verify that airdrops are allowed to this vault;
    /// if they are disabled, we should not return the selector, otherwise we can allow them.
    ///
    /// IMPORTANT: If an unrelated contract is currently holding the asset on behalf of an owner and then
    /// subsequently transfers the asset into the contract, it needs to manually call (setBeneficialOwner)
    /// after making this call to ensure that the true owner of the asset is known to the vault. Otherwise,
    /// the owner will lose the ability to reclaim their asset. Alternatively, they could pass an entitlement
    /// in pre-populated with the correct beneficial owner, which will give that owner the ability to reclaim
    /// the asset.
    if (msg.sender == address(_nftContract)) {
      // There is no need to check if we currently have this token or an entitlement set.
      // Even if the contract were able to get into this state, it should still accept the asset
      // which will allow it to enforce the entitlement.

      // If additional data is sent with the transfer, we attempt to parse an entitlement from it.
      // this allows the entitlement to be registered ahead of time.
      if (data.length > 0) {
        /// If the abi-encoded parameters are 3 words long, assume no approved operator was provided.
        if (data.length == 3 * 32) {
          // Decode the order, signature from `data`. If `data` does not encode such parameters, this
          // will throw.
          (
            address beneficialOwner,
            address entitledOperator,
            uint32 expirationTime
          ) = abi.decode(data, (address, address, uint32));

          // if someone has the asset, they should be able to set whichever beneficial owner they'd like.
          // equally, they could transfer the asset first to themselves and subsequently grant a specific
          // entitlement, which is equivalent to this.
          _registerEntitlement(
            uint32(tokenId),
            entitledOperator,
            expirationTime,
            beneficialOwner
          );
        } else {
          /// additionally decode the approved operator from the payload. The abi decoder ensures that the
          /// there are exactly 4 parameters
          (
            address beneficialOwner,
            address entitledOperator,
            uint32 expirationTime,
            address approvedOperator
          ) = abi.decode(data, (address, address, uint32, address));

          _registerEntitlement(
            uint32(tokenId),
            entitledOperator,
            expirationTime,
            beneficialOwner
          );

          /// if an approved operator is provided with this contract call, set the approval accepting it for the
          /// same reason.

          _approve(approvedOperator, uint32(tokenId));
        }
      } else {
        _setBeneficialOwner(uint32(tokenId), from);
      }
      emit AssetReceived(
        this.getBeneficialOwner(uint32(tokenId)),
        operator,
        msg.sender,
        uint32(tokenId)
      );
    } else {
      // If we're receiving an airdrop or other asset uncovered by escrow to this address, we should ensure
      // that this is allowed by our current settings.
      require(
        _hookProtocol.getCollectionConfig(
          address(_nftContract),
          keccak256("vault.multiAirdropsAllowed")
        ),
        "onERC721Received-non-escrow asset returned when airdrops are disabled"
      );
    }
    return this.onERC721Received.selector;
  }

  /// @dev See {IHookERC721Vault-flashLoan}.
  function flashLoan(
    uint32 assetId,
    address receiverAddress,
    bytes calldata params
  ) external override nonReentrant {
    IERC721FlashLoanReceiver receiver = IERC721FlashLoanReceiver(
      receiverAddress
    );
    require(receiverAddress != address(0), "flashLoan-zero address");
    require(
      _assetOwner(assetId) == address(this),
      "flashLoan-asset not in vault"
    );
    require(
      msg.sender == assets[assetId].beneficialOwner,
      "flashLoan-not called by the asset owner"
    );

    require(
      !_hookProtocol.getCollectionConfig(
        address(_nftContract),
        keccak256("vault.flashLoanDisabled")
      ),
      "flashLoan-flashLoan feature disabled for this contract"
    );

    // (1) store a hash of our current entitlement state as a snapshot to diff
    bytes32 startState = keccak256(abi.encode(assets[assetId]));

    // (2) send the flashloan contract the vaulted NFT
    _nftContract.safeTransferFrom(
      address(this),
      receiverAddress,
      _assetTokenId(assetId)
    );

    // (3) call the flashloan contract, giving it a chance to do whatever it wants
    // NOTE: The flashloan contract MUST approve this vault contract as an operator
    // for the nft, such that we're able to make sure it has arrived.
    require(
      receiver.executeOperation(
        address(_nftContract),
        _assetTokenId(assetId),
        msg.sender,
        address(this),
        params
      ),
      "flashLoan-the flash loan contract must return true"
    );

    // (4) return the nft back into the vault
    //        Use transferFrom instead of safeTransfer from because transferFrom
    //        would modify our state ( it calls erc721Receiver ). and because we know
    //        for sure that this contract can handle ERC-721s.
    _nftContract.transferFrom(
      receiverAddress,
      address(this),
      _assetTokenId(assetId)
    );

    // (5) sanity check to ensure the asset was actually returned to the vault.
    // this is a concern because its possible that the safeTransferFrom implemented by
    // some contract fails silently
    require(_assetOwner(assetId) == address(this));

    // (6) additional sanity check to ensure that the internal state of
    // the entitlement has not somehow been modified during the flash loan, for example
    // via some re-entrancy attack or by sending the asset back into the contract
    // prematurely
    require(
      startState == keccak256(abi.encode(assets[assetId])),
      "flashLoan-entitlement state cannot be modified"
    );

    // (7) emit an event to record the flashloan
    emit AssetFlashLoaned(
      assets[assetId].beneficialOwner,
      assetId,
      receiverAddress
    );
  }

  /// @dev See {IHookVault-entitlementExpiration}.
  function entitlementExpiration(uint32 assetId)
    external
    view
    returns (uint32)
  {
    if (!hasActiveEntitlement(assetId)) {
      return 0;
    } else {
      return assets[assetId].expiry;
    }
  }

  /// @dev See {IHookERC721Vault-getBeneficialOwner}.
  function getBeneficialOwner(uint32 assetId) external view returns (address) {
    return assets[assetId].beneficialOwner;
  }

  /// @dev See {IHookERC721Vault-getHoldsAsset}.
  function getHoldsAsset(uint32 assetId) external view returns (bool) {
    return _assetOwner(assetId) == address(this);
  }

  function assetAddress(uint32) external view returns (address) {
    return address(_nftContract);
  }

  /// @dev returns the underlying token ID for a given asset. In this case
  /// the tokenId == the assetId
  function assetTokenId(uint32 assetId) external view returns (uint256) {
    return _assetTokenId(assetId);
  }

  /// @dev See {IHookERC721Vault-setBeneficialOwner}.
  /// setBeneficialOwner can only be called by the entitlementContract if there is an activeEntitlement.
  function setBeneficialOwner(uint32 assetId, address newBeneficialOwner)
    public
    virtual
  {
    if (hasActiveEntitlement(assetId)) {
      require(
        msg.sender == assets[assetId].operator,
        "setBeneficialOwner-only the contract with the active entitlement can update the beneficial owner"
      );
    } else {
      require(
        msg.sender == assets[assetId].beneficialOwner,
        "setBeneficialOwner-only the current owner can update the beneficial owner"
      );
    }
    _setBeneficialOwner(assetId, newBeneficialOwner);
  }

  /// @dev See {IHookERC721Vault-clearEntitlement}.
  /// @dev This can only be called if an entitlement currently exists, otherwise it would be a no-op
  function clearEntitlement(uint32 assetId) public {
    require(
      hasActiveEntitlement(assetId),
      "clearEntitlement-an active entitlement must exist"
    );
    require(
      msg.sender == assets[assetId].operator,
      "clearEntitlement-only the entitled address can clear the entitlement"
    );
    _clearEntitlement(assetId);
  }

  /// @dev See {IHookERC721Vault-clearEntitlementAndDistribute}.
  /// @dev The entitlement must be exist, and must be called by the {operator}. The operator can specify a
  /// intended receiver, which should match the beneficialOwner. The function will throw if
  /// the receiver and owner do not match.
  /// @param assetId the id of the specific vaulted asset
  /// @param receiver the intended receiver of the asset
  function clearEntitlementAndDistribute(uint32 assetId, address receiver)
    external
    nonReentrant
  {
    require(
      assets[assetId].beneficialOwner == receiver,
      "clearEntitlementAndDistribute-Only the beneficial owner can receive the asset"
    );
    require(
      receiver != address(0),
      "clearEntitlementAndDistribute-assets cannot be sent to null address"
    );
    clearEntitlement(assetId);
    IERC721(_nftContract).safeTransferFrom(
      address(this),
      receiver,
      _assetTokenId(assetId)
    );
    emit AssetWithdrawn(assetId, receiver, assets[assetId].beneficialOwner);
  }

  /// @dev Validates that a specific signature is actually the entitlement
  /// EIP-712 signed by the beneficial owner specified in the entitlement.
  function validateEntitlementSignature(
    address operator,
    uint32 expiry,
    uint32 assetId,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) public view {
    bytes32 entitlementHash = _getEIP712Hash(
      Entitlements.getEntitlementStructHash(
        Entitlements.Entitlement({
          beneficialOwner: assets[assetId].beneficialOwner,
          expiry: expiry,
          operator: operator,
          assetId: assetId,
          vaultAddress: address(this)
        })
      )
    );
    address signer = ecrecover(entitlementHash, v, r, s);

    require(signer != address(0), "recovered address is null");
    require(
      signer == assets[assetId].beneficialOwner,
      "validateEntitlementSignature --- not signed by beneficialOwner"
    );
  }

  ///
  /// @dev See {IHookVault-approveOperator}.
  ///
  function approveOperator(address to, uint32 assetId) public virtual override {
    address beneficialOwner = assets[assetId].beneficialOwner;

    require(
      to != beneficialOwner,
      "approve-approval to current beneficialOwner"
    );

    require(
      msg.sender == beneficialOwner,
      "approve-approve caller is not current beneficial owner"
    );

    _approve(to, assetId);
  }

  /// @dev See {IHookVault-getApprovedOperator}.
  function getApprovedOperator(uint32 assetId)
    public
    view
    virtual
    override
    returns (address)
  {
    return _assetApprovals[assetId];
  }

  /// @dev Approve `to` to operate on `tokenId`
  ///
  /// Emits an {Approval} event.
  /// @param to the address to approve
  /// @param assetId the assetId on which the address will be approved
  function _approve(address to, uint32 assetId) internal virtual {
    _assetApprovals[assetId] = to;
    emit Approval(assets[assetId].beneficialOwner, to, assetId);
  }

  /// ---------------- INTERNAL/PRIVATE FUNCTIONS ---------------- ///

  /// @notice Verify that an entitlement is properly signed and apply it to the asset if able.
  /// @dev The entitlement must be signed by the beneficial owner of the asset in order for it to be considered valid
  /// @param operator the operator to entitle
  /// @param expiry the duration of the entitlement
  /// @param assetId the id of the asset within the vault
  /// @param v sig v
  /// @param r sig r
  /// @param s sig s
  function _verifyAndRegisterEntitlement(
    address operator,
    uint32 expiry,
    uint32 assetId,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) private {
    validateEntitlementSignature(operator, expiry, assetId, v, r, s);
    _registerEntitlement(
      assetId,
      operator,
      expiry,
      assets[assetId].beneficialOwner
    );
  }

  function _registerEntitlement(
    uint32 assetId,
    address operator,
    uint32 expiry,
    address beneficialOwner
  ) internal {
    require(
      !hasActiveEntitlement(assetId),
      "_registerEntitlement-existing entitlement must be cleared before registering a new one"
    );

    require(
      expiry > block.timestamp,
      "_registerEntitlement-entitlement must expire in the future"
    );
    assets[assetId] = Asset({
      operator: operator,
      expiry: expiry,
      beneficialOwner: beneficialOwner
    });
    emit EntitlementImposed(assetId, operator, expiry, beneficialOwner);
  }

  function _clearEntitlement(uint32 assetId) private {
    assets[assetId].expiry = 0;
    assets[assetId].operator = address(0);
    emit EntitlementCleared(assetId, assets[assetId].beneficialOwner);
  }

  function hasActiveEntitlement(uint32 assetId) public view returns (bool) {
    /// Although we do clear the expiry in _clearEntitlement, making the second half of the AND redundant,
    /// we choose to include it here because we rely on this field being null to clear an entitlement.
    return
      block.timestamp < assets[assetId].expiry &&
      assets[assetId].operator != address(0);
  }

  function getCurrentEntitlementOperator(uint32 assetId)
    external
    view
    returns (bool, address)
  {
    bool isActive = hasActiveEntitlement(assetId);
    address operator = assets[assetId].operator;

    return (isActive, operator);
  }

  /// @dev determine the owner of a specific asset according to is contract based
  /// on that assets assetId within this vault.
  ///
  /// this function can be overridden if the assetId -> tokenId mapping is modified.
  function _assetOwner(uint32 assetId) internal view returns (address) {
    return _nftContract.ownerOf(_assetTokenId(assetId));
  }

  /// @dev get the token id based on an asset's ID
  ///
  /// this function can be overridden if the assetId -> tokenId mapping is modified.
  function _assetTokenId(uint32 assetId)
    internal
    view
    virtual
    returns (uint256)
  {
    return assetId;
  }

  /// @dev sets the new beneficial owner for a particular asset within the vault
  function _setBeneficialOwner(uint32 assetId, address newBeneficialOwner)
    internal
  {
    require(
      newBeneficialOwner != address(0),
      "_setBeneficialOwner-new owner is the zero address"
    );
    assets[assetId].beneficialOwner = newBeneficialOwner;
    _approve(address(0), assetId);
    emit BeneficialOwnerSet(assetId, newBeneficialOwner, msg.sender);
  }
}

File 2 of 15 : IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

File 3 of 15 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/Address.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
     */
    modifier initializer() {
        bool isTopLevelCall = _setInitializedVersion(1);
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
     * initialization step. This is essential to configure modules that are added through upgrades and that require
     * initialization.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     */
    modifier reinitializer(uint8 version) {
        bool isTopLevelCall = _setInitializedVersion(version);
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(version);
        }
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     */
    function _disableInitializers() internal virtual {
        _setInitializedVersion(type(uint8).max);
    }

    function _setInitializedVersion(uint8 version) private returns (bool) {
        // If the contract is initializing we ignore whether _initialized is set in order to support multiple
        // inheritance patterns, but we only do this in the context of a constructor, and for the lowest level
        // of initializers, because in other contexts the contract may have been reentered.
        if (_initializing) {
            require(
                version == 1 && !Address.isContract(address(this)),
                "Initializable: contract is already initialized"
            );
            return false;
        } else {
            require(_initialized < version, "Initializable: contract is already initialized");
            _initialized = version;
            return true;
        }
    }
}

File 4 of 15 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 5 of 15 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

File 6 of 15 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 7 of 15 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 8 of 15 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 9 of 15 : IERC721FlashLoanReceiver.sol
// SPDX-License-Identifier: MIT
//
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        ██████████████                                        ██████████████
//        ██████████████          ▄▄████████████████▄▄         ▐█████████████▌
//        ██████████████    ▄█████████████████████████████▄    ██████████████
//         ██████████▀   ▄█████████████████████████████████   ██████████████▌
//          ██████▀   ▄██████████████████████████████████▀  ▄███████████████
//           ███▀   ██████████████████████████████████▀   ▄████████████████
//            ▀▀  ████████████████████████████████▀▀   ▄█████████████████▌
//              █████████████████████▀▀▀▀▀▀▀      ▄▄███████████████████▀
//             ██████████████████▀    ▄▄▄█████████████████████████████▀
//            ████████████████▀   ▄█████████████████████████████████▀  ██▄
//          ▐███████████████▀  ▄██████████████████████████████████▀   █████▄
//          ██████████████▀  ▄█████████████████████████████████▀   ▄████████
//         ██████████████▀   ███████████████████████████████▀   ▄████████████
//        ▐█████████████▌     ▀▀▀▀████████████████████▀▀▀▀      █████████████▌
//        ██████████████                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████

pragma solidity ^0.8.10;

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";

/// @title Flash Loan Operator Interface (ERC-721)
/// @author Jake [email protected]
/// @dev contracts that will utilize vaulted assets in flash loans should implement this interface in order to
/// receive the asset. Users may want to receive the asset within a single block to claim airdrops, participate
/// in governance, and other things with their assets.
///
/// The implementer may do whatever they like with the vaulted NFT within the executeOperation method,
/// so long as they approve the vault (passed as a param) to operate the underlying NFT. The Vault
/// will move the asset back into the vault after executionOperation returns, and also validate that
/// it is the owner of the asset.
///
/// The flashloan receiver is able to abort a flashloan by returning false from the executeOperation method.
interface IERC721FlashLoanReceiver is IERC721Receiver {
  /// @notice the method that contains the operations to be performed with the loaned asset
  /// @dev executeOperation is called immediately after the asset is transferred to this contract. After return,
  /// the asset is returned to the vault by the vault contract. The executeOperation implementation MUST
  /// approve the {vault} to operate the transferred NFT
  /// i.e. `IERC721(nftContract).setApprovalForAll(vault, true);`
  ///
  /// @param nftContract the address of the underlying erc-721 asset
  /// @param tokenId the address of the received erc-721 asset
  /// @param beneficialOwner the current beneficialOwner of the vault, who initialized the flashLoan
  /// @param vault the address of the vault performing the flashloan (in most cases, equal to msg.sender)
  /// @param params additional params passed by the caller into the flashloan
  function executeOperation(
    address nftContract,
    uint256 tokenId,
    address beneficialOwner,
    address vault,
    bytes calldata params
  ) external returns (bool);
}

File 10 of 15 : IHookERC721Vault.sol
// SPDX-License-Identifier: MIT
//
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        ██████████████                                        ██████████████
//        ██████████████          ▄▄████████████████▄▄         ▐█████████████▌
//        ██████████████    ▄█████████████████████████████▄    ██████████████
//         ██████████▀   ▄█████████████████████████████████   ██████████████▌
//          ██████▀   ▄██████████████████████████████████▀  ▄███████████████
//           ███▀   ██████████████████████████████████▀   ▄████████████████
//            ▀▀  ████████████████████████████████▀▀   ▄█████████████████▌
//              █████████████████████▀▀▀▀▀▀▀      ▄▄███████████████████▀
//             ██████████████████▀    ▄▄▄█████████████████████████████▀
//            ████████████████▀   ▄█████████████████████████████████▀  ██▄
//          ▐███████████████▀  ▄██████████████████████████████████▀   █████▄
//          ██████████████▀  ▄█████████████████████████████████▀   ▄████████
//         ██████████████▀   ███████████████████████████████▀   ▄████████████
//        ▐█████████████▌     ▀▀▀▀████████████████████▀▀▀▀      █████████████▌
//        ██████████████                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████

pragma solidity ^0.8.10;

import "./IHookVault.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";

/// @title Hook ERC-721 Vault interface
/// @author Jake [email protected]
/// @custom:coauthor Regynald [email protected]
///
/// @dev the IHookERC721 vault is an extension of the standard IHookVault
/// specifically designed to hold and receive ERC721 Tokens.
///
/// FLASH LOAN -
///     (1) beneficial owners are able to borrow the vaulted asset for a single function call
///     (2) to borrow the asset, they must implement and deploy a {IERC721FlashLoanReceiver}
///         contract, and then call the flashLoan method.
///     (3) At the end of the flashLoan, we ensure the asset is still owned by the vault.
interface IHookERC721Vault is IHookVault, IERC721Receiver {
  /// @notice emitted after an asset is flash loaned by its beneficial owner.
  /// @dev only one asset can be flash loaned at a time, and that asset is
  /// denoted by the tokenId emitted.
  event AssetFlashLoaned(address owner, uint256 tokenId, address flashLoanImpl);

  /// @notice the tokenID of the underlying ERC721 token;
  function assetTokenId(uint32 assetId) external view returns (uint256);

  /// @notice flashLoans the vaulted asset to another contract for use and return to the vault. Only the owner
  /// may perform the flashloan
  /// @dev the flashloan receiver can perform arbitrary logic, but must approve the vault as an operator
  /// before returning.
  /// @param receiverAddress the contract which implements the {IERC721FlashLoanReceiver} interface to utilize the
  /// asset while it is loaned out
  /// @param params calldata params to forward to the receiver
  function flashLoan(
    uint32 assetId,
    address receiverAddress,
    bytes calldata params
  ) external;
}

File 11 of 15 : IHookProtocol.sol
// SPDX-License-Identifier: MIT
//
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        ██████████████                                        ██████████████
//        ██████████████          ▄▄████████████████▄▄         ▐█████████████▌
//        ██████████████    ▄█████████████████████████████▄    ██████████████
//         ██████████▀   ▄█████████████████████████████████   ██████████████▌
//          ██████▀   ▄██████████████████████████████████▀  ▄███████████████
//           ███▀   ██████████████████████████████████▀   ▄████████████████
//            ▀▀  ████████████████████████████████▀▀   ▄█████████████████▌
//              █████████████████████▀▀▀▀▀▀▀      ▄▄███████████████████▀
//             ██████████████████▀    ▄▄▄█████████████████████████████▀
//            ████████████████▀   ▄█████████████████████████████████▀  ██▄
//          ▐███████████████▀  ▄██████████████████████████████████▀   █████▄
//          ██████████████▀  ▄█████████████████████████████████▀   ▄████████
//         ██████████████▀   ███████████████████████████████▀   ▄████████████
//        ▐█████████████▌     ▀▀▀▀████████████████████▀▀▀▀      █████████████▌
//        ██████████████                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████

pragma solidity ^0.8.10;

import "@openzeppelin/contracts/access/IAccessControl.sol";

/// @title HookProtocol configuration and access control repository
/// @author Jake [email protected]
/// @custom:coauthor Regynald [email protected]
///
/// @dev it is critically important that the particular protocol implementation
/// is correct as, if it is not, all assets contained within protocol contracts
/// can be easily compromised.
interface IHookProtocol is IAccessControl {
  /// @notice the address of the deployed CoveredCallFactory used by the protocol
  function coveredCallContract() external view returns (address);

  /// @notice the address of the deployed VaultFactory used by the protocol
  function vaultContract() external view returns (address);

  /// @notice callable function that reverts when the protocol is paused
  function throwWhenPaused() external;

  /// @notice the standard weth address on this chain
  /// @dev these are values for popular chains:
  /// mainnet: 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
  /// kovan: 0xd0a1e359811322d97991e03f863a0c30c2cf029c
  /// ropsten: 0xc778417e063141139fce010982780140aa0cd5ab
  /// rinkeby: 0xc778417e063141139fce010982780140aa0cd5ab
  /// @return the weth address
  function getWETHAddress() external view returns (address);

  /// @notice get a configuration flag with a specific key for a collection
  /// @param collectionAddress the collection for which to lookup a configuration flag
  /// @param conf the config identifier for the configuration flag
  /// @return the true or false value of the config
  function getCollectionConfig(address collectionAddress, bytes32 conf)
    external
    view
    returns (bool);
}

File 12 of 15 : IHookVault.sol
// SPDX-License-Identifier: MIT
//
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        ██████████████                                        ██████████████
//        ██████████████          ▄▄████████████████▄▄         ▐█████████████▌
//        ██████████████    ▄█████████████████████████████▄    ██████████████
//         ██████████▀   ▄█████████████████████████████████   ██████████████▌
//          ██████▀   ▄██████████████████████████████████▀  ▄███████████████
//           ███▀   ██████████████████████████████████▀   ▄████████████████
//            ▀▀  ████████████████████████████████▀▀   ▄█████████████████▌
//              █████████████████████▀▀▀▀▀▀▀      ▄▄███████████████████▀
//             ██████████████████▀    ▄▄▄█████████████████████████████▀
//            ████████████████▀   ▄█████████████████████████████████▀  ██▄
//          ▐███████████████▀  ▄██████████████████████████████████▀   █████▄
//          ██████████████▀  ▄█████████████████████████████████▀   ▄████████
//         ██████████████▀   ███████████████████████████████▀   ▄████████████
//        ▐█████████████▌     ▀▀▀▀████████████████████▀▀▀▀      █████████████▌
//        ██████████████                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████

pragma solidity ^0.8.10;

import "../lib/Entitlements.sol";
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/// @title Generic Hook Vault-a vault designed to contain a single asset to be used as escrow.
/// @author Jake [email protected]
/// @custom:coauthor Regynald [email protected]
///
/// @notice The Vault holds an asset on behalf of the owner. The owner is able to post this
/// asset as collateral to other protocols by signing a message, called an "entitlement", that gives
/// a specific account the ability to change the owner.
///
/// The vault can work with multiple assets via the assetId, where the asset or set of assets covered by
/// each segment is granted an individual id.
/// Every asset must be identified by an assetId to comply with this interface, even if the vault only contains
/// one asset.
///
/// ENTITLEMENTS -
///     (1) only one entitlement can be placed at a time.
///     (2) entitlements must expire, but can also be cleared by the entitled party
///     (3) if an entitlement expires, the current beneficial owner gains immediate sole control over the
///        asset
///     (4) the entitled entity can modify the beneficial owner of the asset, but cannot withdrawal.
///     (5) the beneficial owner cannot modify the beneficial owner while an entitlement is in place
///
interface IHookVault is IERC165 {
  /// @notice emitted when an entitlement is placed on an asset
  event EntitlementImposed(
    uint32 assetId,
    address entitledAccount,
    uint32 expiry,
    address beneficialOwner
  );

  /// @notice emitted when an entitlement is cleared from an asset
  event EntitlementCleared(uint256 assetId, address beneficialOwner);

  /// @notice emitted when the beneficial owner of an asset changes
  /// @dev it is not required that this event is emitted when an entitlement is
  /// imposed that also modifies the beneficial owner.
  event BeneficialOwnerSet(
    uint32 assetId,
    address beneficialOwner,
    address setBy
  );

  /// @notice emitted when an asset is added into the vault
  event AssetReceived(
    address owner,
    address sender,
    address contractAddress,
    uint32 assetId
  );

  /// @notice Emitted when `beneficialOwner` enables `approved` to manage the `assetId` asset.
  event Approval(
    address indexed beneficialOwner,
    address indexed approved,
    uint32 indexed assetId
  );

  /// @notice emitted when an asset is withdrawn from the vault
  event AssetWithdrawn(uint32 assetId, address to, address beneficialOwner);

  /// @notice Withdrawal an unencumbered asset from this vault
  /// @param assetId the asset to remove from the vault
  function withdrawalAsset(uint32 assetId) external;

  /// @notice setBeneficialOwner updates the current address that can claim the asset when it is free of entitlements.
  /// @param assetId the id of the subject asset to impose the entitlement
  /// @param newBeneficialOwner the account of the person who is able to withdrawal when there are no entitlements.
  function setBeneficialOwner(uint32 assetId, address newBeneficialOwner)
    external;

  /// @notice Add an entitlement claim to the asset held within the contract
  /// @param operator the operator to entitle
  /// @param expiry the duration of the entitlement
  /// @param assetId the id of the asset within the vault
  /// @param v sig v
  /// @param r sig r
  /// @param s sig s
  function imposeEntitlement(
    address operator,
    uint32 expiry,
    uint32 assetId,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

  /// @notice Allows the beneficial owner to grant an entitlement to an asset within the contract
  /// @dev this function call is signed by the sender per the EVM, so we know the entitlement is authentic
  /// @param entitlement The entitlement to impose onto the contract
  function grantEntitlement(Entitlements.Entitlement calldata entitlement)
    external;

  /// @notice Allows the entitled address to release their claim on the asset
  /// @param assetId the id of the asset to clear
  function clearEntitlement(uint32 assetId) external;

  /// @notice Removes the active entitlement from a vault and returns the asset to the beneficial owner
  /// @param receiver the intended receiver of the asset
  /// @param assetId the Id of the asset to clear
  function clearEntitlementAndDistribute(uint32 assetId, address receiver)
    external;

  /// @notice looks up the current beneficial owner of the asset
  /// @param assetId the referenced asset
  /// @return the address of the beneficial owner of the asset
  function getBeneficialOwner(uint32 assetId) external view returns (address);

  /// @notice checks if the asset is currently stored in the vault
  /// @param assetId the referenced asset
  /// @return true if the asset is currently within the vault, false otherwise
  function getHoldsAsset(uint32 assetId) external view returns (bool);

  /// @notice the contract address of the vaulted asset
  /// @param assetId the referenced asset
  /// @return the contract address of the vaulted asset
  function assetAddress(uint32 assetId) external view returns (address);

  /// @notice looks up the current operator of an entitlement on an asset
  /// @param assetId the id of the underlying asset
  function getCurrentEntitlementOperator(uint32 assetId)
    external
    view
    returns (bool, address);

  /// @notice Looks up the expiration timestamp of the current entitlement
  /// @dev returns the 0 if no entitlement is set
  /// @return the block timestamp after which the entitlement expires
  function entitlementExpiration(uint32 assetId) external view returns (uint32);

  /// @notice Gives permission to `to` to impose an entitlement upon `assetId`
  ///
  /// @dev Only a single account can be approved at a time, so approving the zero address clears previous approvals.
  ///   * Requirements:
  ///
  /// -  The caller must be the beneficial owner
  /// - `tokenId` must exist.
  ///
  /// Emits an {Approval} event.
  function approveOperator(address to, uint32 assetId) external;

  /// @dev Returns the account approved for `tokenId` token.
  ///
  /// Requirements:
  ///
  /// - `assetId` must exist.
  ///
  function getApprovedOperator(uint32 assetId) external view returns (address);
}

File 13 of 15 : Entitlements.sol
// SPDX-License-Identifier: MIT
//
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        ██████████████                                        ██████████████
//        ██████████████          ▄▄████████████████▄▄         ▐█████████████▌
//        ██████████████    ▄█████████████████████████████▄    ██████████████
//         ██████████▀   ▄█████████████████████████████████   ██████████████▌
//          ██████▀   ▄██████████████████████████████████▀  ▄███████████████
//           ███▀   ██████████████████████████████████▀   ▄████████████████
//            ▀▀  ████████████████████████████████▀▀   ▄█████████████████▌
//              █████████████████████▀▀▀▀▀▀▀      ▄▄███████████████████▀
//             ██████████████████▀    ▄▄▄█████████████████████████████▀
//            ████████████████▀   ▄█████████████████████████████████▀  ██▄
//          ▐███████████████▀  ▄██████████████████████████████████▀   █████▄
//          ██████████████▀  ▄█████████████████████████████████▀   ▄████████
//         ██████████████▀   ███████████████████████████████▀   ▄████████████
//        ▐█████████████▌     ▀▀▀▀████████████████████▀▀▀▀      █████████████▌
//        ██████████████                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████

pragma solidity ^0.8.10;

import "./Signatures.sol";

library Entitlements {
  uint256 private constant _ENTITLEMENT_TYPEHASH =
    uint256(
      keccak256(
        abi.encodePacked(
          "Entitlement(",
          "address beneficialOwner,",
          "address operator,",
          "address vaultAddress,",
          "uint32 assetId,",
          "uint32 expiry",
          ")"
        )
      )
    );

  /// ---- STRUCTS -----
  struct Entitlement {
    /// @notice the beneficial owner address this entitlement applies to. This address will also be the signer.
    address beneficialOwner;
    /// @notice the operating contract that can change ownership during the entitlement period.
    address operator;
    /// @notice the contract address for the vault that contains the underlying assets
    address vaultAddress;
    /// @notice the assetId of the asset or assets within the vault
    uint32 assetId;
    /// @notice the block timestamp after which the asset is free of the entitlement
    uint32 expiry;
  }

  function getEntitlementStructHash(Entitlement memory entitlement)
    internal
    pure
    returns (bytes32)
  {
    // TODO: Hash in place to save gas.
    return
      keccak256(
        abi.encode(
          _ENTITLEMENT_TYPEHASH,
          entitlement.beneficialOwner,
          entitlement.operator,
          entitlement.vaultAddress,
          entitlement.assetId,
          entitlement.expiry
        )
      );
  }
}

File 14 of 15 : Signatures.sol
// SPDX-License-Identifier: MIT
//
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        ██████████████                                        ██████████████
//        ██████████████          ▄▄████████████████▄▄         ▐█████████████▌
//        ██████████████    ▄█████████████████████████████▄    ██████████████
//         ██████████▀   ▄█████████████████████████████████   ██████████████▌
//          ██████▀   ▄██████████████████████████████████▀  ▄███████████████
//           ███▀   ██████████████████████████████████▀   ▄████████████████
//            ▀▀  ████████████████████████████████▀▀   ▄█████████████████▌
//              █████████████████████▀▀▀▀▀▀▀      ▄▄███████████████████▀
//             ██████████████████▀    ▄▄▄█████████████████████████████▀
//            ████████████████▀   ▄█████████████████████████████████▀  ██▄
//          ▐███████████████▀  ▄██████████████████████████████████▀   █████▄
//          ██████████████▀  ▄█████████████████████████████████▀   ▄████████
//         ██████████████▀   ███████████████████████████████▀   ▄████████████
//        ▐█████████████▌     ▀▀▀▀████████████████████▀▀▀▀      █████████████▌
//        ██████████████                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████

pragma solidity ^0.8.10;

/// @dev A library for validating signatures from ZeroEx
library Signatures {
  /// @dev Allowed signature types.
  enum SignatureType {
    EIP712
  }

  /// @dev Encoded EC signature.
  struct Signature {
    // How to validate the signature.
    SignatureType signatureType;
    // EC Signature data.
    uint8 v;
    // EC Signature data.
    bytes32 r;
    // EC Signature data.
    bytes32 s;
  }
}

File 15 of 15 : EIP712.sol
// SPDX-License-Identifier: MIT
//
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        █████████████▌                                        ▐█████████████
//        ██████████████                                        ██████████████
//        ██████████████          ▄▄████████████████▄▄         ▐█████████████▌
//        ██████████████    ▄█████████████████████████████▄    ██████████████
//         ██████████▀   ▄█████████████████████████████████   ██████████████▌
//          ██████▀   ▄██████████████████████████████████▀  ▄███████████████
//           ███▀   ██████████████████████████████████▀   ▄████████████████
//            ▀▀  ████████████████████████████████▀▀   ▄█████████████████▌
//              █████████████████████▀▀▀▀▀▀▀      ▄▄███████████████████▀
//             ██████████████████▀    ▄▄▄█████████████████████████████▀
//            ████████████████▀   ▄█████████████████████████████████▀  ██▄
//          ▐███████████████▀  ▄██████████████████████████████████▀   █████▄
//          ██████████████▀  ▄█████████████████████████████████▀   ▄████████
//         ██████████████▀   ███████████████████████████████▀   ▄████████████
//        ▐█████████████▌     ▀▀▀▀████████████████████▀▀▀▀      █████████████▌
//        ██████████████                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████
//        █████████████▌                                        ██████████████

pragma solidity ^0.8.10;

/// @dev EIP712 helpers for features.
abstract contract EIP712 {
  /// @dev The domain hash separator for the entire call option protocol
  bytes32 public EIP712_DOMAIN_SEPARATOR;

  function setAddressForEipDomain(address hookAddress) internal {
    // Compute `EIP712_DOMAIN_SEPARATOR`
    {
      uint256 chainId;
      assembly {
        chainId := chainid()
      }
      EIP712_DOMAIN_SEPARATOR = keccak256(
        abi.encode(
          keccak256(
            "EIP712Domain("
            "string name,"
            "string version,"
            "uint256 chainId,"
            "address verifyingContract"
            ")"
          ),
          keccak256("Hook"),
          keccak256("1.0.0"),
          chainId,
          hookAddress
        )
      );
    }
  }

  function _getEIP712Hash(bytes32 structHash)
    internal
    view
    returns (bytes32)
  {
    return
      keccak256(
        abi.encodePacked(hex"1901", EIP712_DOMAIN_SEPARATOR, structHash)
      );
  }
}

Settings
{
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 10
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beneficialOwner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"flashLoanImpl","type":"address"}],"name":"AssetFlashLoaned","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"contractAddress","type":"address"},{"indexed":false,"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"AssetReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"assetId","type":"uint32"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"beneficialOwner","type":"address"}],"name":"AssetWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"assetId","type":"uint32"},{"indexed":false,"internalType":"address","name":"beneficialOwner","type":"address"},{"indexed":false,"internalType":"address","name":"setBy","type":"address"}],"name":"BeneficialOwnerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assetId","type":"uint256"},{"indexed":false,"internalType":"address","name":"beneficialOwner","type":"address"}],"name":"EntitlementCleared","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"assetId","type":"uint32"},{"indexed":false,"internalType":"address","name":"entitledAccount","type":"address"},{"indexed":false,"internalType":"uint32","name":"expiry","type":"uint32"},{"indexed":false,"internalType":"address","name":"beneficialOwner","type":"address"}],"name":"EntitlementImposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"inputs":[],"name":"EIP712_DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"approveOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"assetAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"assetTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"clearEntitlement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"},{"internalType":"address","name":"receiver","type":"address"}],"name":"clearEntitlementAndDistribute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"entitlementExpiration","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"},{"internalType":"address","name":"receiverAddress","type":"address"},{"internalType":"bytes","name":"params","type":"bytes"}],"name":"flashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"getApprovedOperator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"getBeneficialOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"getCurrentEntitlementOperator","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"getHoldsAsset","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"beneficialOwner","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"vaultAddress","type":"address"},{"internalType":"uint32","name":"assetId","type":"uint32"},{"internalType":"uint32","name":"expiry","type":"uint32"}],"internalType":"struct Entitlements.Entitlement","name":"entitlement","type":"tuple"}],"name":"grantEntitlement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"hasActiveEntitlement","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"assetId","type":"uint32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"imposeEntitlement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"address","name":"hookAddress","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"},{"internalType":"address","name":"newBeneficialOwner","type":"address"}],"name":"setBeneficialOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint32","name":"expiry","type":"uint32"},{"internalType":"uint32","name":"assetId","type":"uint32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"validateEntitlementSignature","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"assetId","type":"uint32"}],"name":"withdrawalAsset","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b506001600255612585806100256000396000f3fe608060405234801561001057600080fd5b50600436106101075760003560e01c806301ffc9a71461010c57806303e7158b14610134578063150b7a02146101495780631f48f6ab1461017557806325a23982146101ab5780632ec14d3f146101be578063485cc955146101d15780634eeeaa32146101e457806360bbf4a4146101f7578063774a76271461020a578063942811b514610232578063af024ae614610245578063ba02481b14610274578063bf6346b714610287578063c72bed151461029a578063dab400f3146102cc578063dbd74309146102e3578063e7dc956b146102f6578063eaf8b32314610309578063ebc5eb071461031c578063ecb2c20b1461032f575b600080fd5b61011f61011a366004612014565b61035e565b60405190151581526020015b60405180910390f35b61014761014236600461206e565b610395565b005b61015c610157366004612126565b610451565b6040516001600160e01b0319909116815260200161012b565b610193610183366004612198565b506003546001600160a01b031690565b6040516001600160a01b03909116815260200161012b565b6101476101b93660046121b3565b610730565b6101476101cc3660046121b3565b61094f565b6101476101df3660046121ea565b610ad1565b6101476101f2366004612198565b610c1b565b610147610205366004612208565b610e47565b61021d610218366004612198565b610f89565b60405163ffffffff909116815260200161012b565b61011f610240366004612198565b610fcb565b610193610253366004612198565b63ffffffff166000908152600560205260409020546001600160a01b031690565b61011f610282366004612198565b611020565b610147610295366004612220565b61103c565b6102ad6102a8366004612198565b6115c2565b6040805192151583526001600160a01b0390911660208301520161012b565b6102d560005481565b60405190815260200161012b565b6101476102f136600461206e565b6115fe565b6102d5610304366004612198565b6117b0565b610147610317366004612198565b6117be565b61014761032a366004612282565b6118da565b61019361033d366004612198565b63ffffffff166000908152600460205260409020546001600160a01b031690565b60006001600160e01b0319821663162ff4f760e21b148061038f57506001600160e01b031982166301ffc9a760e01b145b92915050565b63ffffffff84166000908152600460205260409020546001600160a01b031661043b5760405162461bcd60e51b815260206004820152604760248201527f696d706f7365456e7469746c656d656e742d62656e6566696369616c206f776e60448201527f6572206d7573742062652073657420746f20696d706f736520616e20656e74696064820152661d1b195b595b9d60ca1b608482015260a4015b60405180910390fd5b6104498686868686866119e2565b505050505050565b600063ffffffff8411156104b85760405162461bcd60e51b815260206004820152602860248201527f6f6e45524337323152656365697665642d746f6b656e4964206973206f7574206044820152676f662072616e676560c01b6064820152608401610432565b6003546001600160a01b031633141561060757811561053c57606082141561050457600080806104ea858701876122b7565b9250925092506104fc87838386611a1a565b505050610546565b6000808080610515868801886122fe565b935093509350935061052988848487611a1a565b6105338189611bf7565b50505050610546565b6105468486611c64565b60405163ecb2c20b60e01b815263ffffffff851660048201527f7897fd58abdfc815ef9370ad67929a217fbaa4b692c4b1086930ae97e4a7fe2d90309063ecb2c20b90602401602060405180830381865afa1580156105a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105cd9190612358565b604080516001600160a01b0392831681529189166020830152339082015263ffffffff8616606082015260800160405180910390a161071e565b60065460035460405163194f672960e01b81526001600160a01b039283169263194f67299261065d929116907f0c6296a577719ee1835d23ea2ac4f93808edda4f265df0440caeecc95a06857590600401612375565b602060405180830381865afa15801561067a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061069e919061238e565b61071e5760405162461bcd60e51b815260206004820152604560248201527f6f6e45524337323152656365697665642d6e6f6e2d657363726f77206173736560448201527f742072657475726e6564207768656e2061697264726f7073206172652064697360648201526418589b195960da1b608482015260a401610432565b50630a85bd0160e11b95945050505050565b6002805414156107525760405162461bcd60e51b8152600401610432906123b0565b6002805563ffffffff82166000908152600460205260409020546001600160a01b038281169116146108025760405162461bcd60e51b815260206004820152604d60248201527f636c656172456e7469746c656d656e74416e64446973747269627574652d4f6e60448201527f6c79207468652062656e6566696369616c206f776e65722063616e207265636560648201526c1a5d99481d1a1948185cdcd95d609a1b608482015260a401610432565b6001600160a01b03811661088a5760405162461bcd60e51b815260206004820152604360248201527f636c656172456e7469746c656d656e74416e64446973747269627574652d617360448201527f736574732063616e6e6f742062652073656e7420746f206e756c6c206164647260648201526265737360e81b608482015260a401610432565b610893826117be565b6003546001600160a01b03166342842e0e308363ffffffff86166040518463ffffffff1660e01b81526004016108cb939291906123e7565b600060405180830381600087803b1580156108e557600080fd5b505af11580156108f9573d6000803e3d6000fd5b50505063ffffffff831660009081526004602052604090819020549051600080516020612530833981519152925061093e91859185916001600160a01b03169061240b565b60405180910390a150506001600255565b61095882610fcb565b15610a1e5763ffffffff82166000908152600460205260409020600101546001600160a01b03163314610a195760405162461bcd60e51b815260206004820152606060248201527f73657442656e6566696369616c4f776e65722d6f6e6c792074686520636f6e7460448201527f726163742077697468207468652061637469766520656e7469746c656d656e7460648201527f2063616e20757064617465207468652062656e6566696369616c206f776e6572608482015260a401610432565b610ac3565b63ffffffff82166000908152600460205260409020546001600160a01b03163314610ac35760405162461bcd60e51b815260206004820152604960248201527f73657442656e6566696369616c4f776e65722d6f6e6c7920746865206375727260448201527f656e74206f776e65722063616e20757064617465207468652062656e6566696360648201526834b0b61037bbb732b960b91b608482015260a401610432565b610acd8282611c64565b5050565b6000610add6001611d4b565b90508015610af5576001805461ff0019166101001790555b604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6020808301919091527fbefac456e8d3c7dcbe25358dd865ef756e23bdcd0f4f36a6e915a0f24b6849e9828401527f06c015bd22b4c69690933c1058878ebdfef31f9aaae40bbe86d8a09fe1b2972c60608301524660808301526001600160a01b03851660a0808401919091528351808403909101815260c09092019092528051910120600055600380546001600160a01b038086166001600160a01b03199283161790925560068054928516929091169190911790558015610c16576001805461ff00191681556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050565b600280541415610c3d5760405162461bcd60e51b8152600401610432906123b0565b60028055610c4a81610fcb565b15610cce5760405162461bcd60e51b815260206004820152604860248201527f7769746864726177616c41737365742d7468652061737365742063616e6e6f7460448201527f2062652077697468647261776e207769746820616e2061637469766520656e746064820152671a5d1b195b595b9d60c21b608482015260a401610432565b63ffffffff81166000908152600460205260409020546001600160a01b03163314610d6b5760405162461bcd60e51b815260206004820152604160248201527f7769746864726177616c41737365742d6f6e6c79207468652062656e6566696360448201527f69616c206f776e65722063616e207769746864726177616c20616e20617373656064820152601d60fa1b608482015260a401610432565b60035463ffffffff82166000908152600460205260409020546001600160a01b03918216916342842e0e91309116610da68563ffffffff1690565b6040518463ffffffff1660e01b8152600401610dc4939291906123e7565b600060405180830381600087803b158015610dde57600080fd5b505af1158015610df2573d6000803e3d6000fd5b50505063ffffffff8216600090815260046020526040908190205490516000805160206125308339815191529250610e3791849133916001600160a01b03169061240b565b60405180910390a1506001600255565b3360046000610e5c6080850160608601612198565b63ffffffff1681526020810191909152604001600020546001600160a01b03161480610eba57503360056000610e986080850160608601612198565b63ffffffff1681526020810191909152604001600020546001600160a01b0316145b610f4d5760405162461bcd60e51b815260206004820152605860248201527f6772616e74456e7469746c656d656e742d6f6e6c79207468652062656e65666960448201527f6369616c206f776e6572206f7220617070726f766564206f70657261746f722060648201527718d85b8819dc985b9d08185b88195b9d1a5d1b195b595b9d60421b608482015260a401610432565b610f86610f606080830160608401612198565b610f706040840160208501612432565b610f8060a0850160808601612198565b33611a1a565b50565b6000610f9482610fcb565b610fa057506000919050565b5063ffffffff908116600090815260046020526040902060010154600160a01b90041690565b919050565b63ffffffff8082166000908152600460205260408120600101549091600160a01b909104164210801561038f57505063ffffffff166000908152600460205260409020600101546001600160a01b0316151590565b60003061102c83611dd3565b6001600160a01b03161492915050565b60028054141561105e5760405162461bcd60e51b8152600401610432906123b0565b60028055826001600160a01b0381166110b25760405162461bcd60e51b8152602060048201526016602482015275666c6173684c6f616e2d7a65726f206164647265737360501b6044820152606401610432565b306110bc86611dd3565b6001600160a01b0316146111115760405162461bcd60e51b815260206004820152601c60248201527b199b185cda131bd85b8b585cdcd95d081b9bdd081a5b881d985d5b1d60221b6044820152606401610432565b63ffffffff85166000908152600460205260409020546001600160a01b0316331461118e5760405162461bcd60e51b815260206004820152602760248201527f666c6173684c6f616e2d6e6f742063616c6c65642062792074686520617373656044820152663a1037bbb732b960c91b6064820152608401610432565b60065460035460405163194f672960e01b81526001600160a01b039283169263194f6729926111e4929116907f7b2ae2881a80edd04dac29e0424a8afcb85d139d6204e45ea854215cb864990b90600401612375565b602060405180830381865afa158015611201573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611225919061238e565b156112915760405162461bcd60e51b815260206004820152603660248201527f666c6173684c6f616e2d666c6173684c6f616e20666561747572652064697361604482015275189b195908199bdc881d1a1a5cc818dbdb9d1c9858dd60521b6064820152608401610432565b63ffffffff8516600090815260046020908152604080832090516112b5920161244f565b60408051601f19818403018152908290528051602090910120600354632142170760e11b83529092506001600160a01b0316906342842e0e90611306903090899063ffffffff8c16906004016123e7565b600060405180830381600087803b15801561132057600080fd5b505af1158015611334573d6000803e3d6000fd5b505060035460405163817dc5e560e01b81526001600160a01b03868116945063817dc5e5935061137892169063ffffffff8b1690339030908b908b90600401612484565b6020604051808303816000875af1158015611397573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113bb919061238e565b6114225760405162461bcd60e51b815260206004820152603260248201527f666c6173684c6f616e2d74686520666c617368206c6f616e20636f6e7472616360448201527174206d7573742072657475726e207472756560701b6064820152608401610432565b6003546001600160a01b03166323b872dd863063ffffffff8a166040518463ffffffff1660e01b815260040161145a939291906123e7565b600060405180830381600087803b15801561147457600080fd5b505af1158015611488573d6000803e3d6000fd5b50505050306001600160a01b031661149f87611dd3565b6001600160a01b0316146114b257600080fd5b63ffffffff861660009081526004602090815260409182902091516114d892910161244f565b6040516020818303038152906040528051906020012081146115535760405162461bcd60e51b815260206004820152602e60248201527f666c6173684c6f616e2d656e7469746c656d656e742073746174652063616e6e60448201526d1bdd081899481b5bd91a599a595960921b6064820152608401610432565b63ffffffff86166000818152600460209081526040918290205482516001600160a01b0391821681529182019390935291871682820152517fb9a0fd57b76d147a2f98aed604908c2285d360c4349bf8c59e89365e39b2f9e29181900360600190a15050600160025550505050565b60008060006115d084610fcb565b63ffffffff90941660009081526004602052604090206001015493946001600160a01b039094169392505050565b6040805160a08101825263ffffffff808716600081815260046020908152858220546001600160a01b0390811686528c169085015230948401949094526060830152871660808201526116599061165490611e46565b611f7f565b6040805160008082526020820180845284905260ff88169282019290925260608101869052608081018590529192509060019060a0016020604051602081039080840390855afa1580156116b1573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166117105760405162461bcd60e51b81526020600482015260196024820152781c9958dbdd995c9959081859191c995cdcc81a5cc81b9d5b1b603a1b6044820152606401610432565b63ffffffff86166000908152600460205260409020546001600160a01b038281169116146117a65760405162461bcd60e51b815260206004820152603e60248201527f76616c6964617465456e7469746c656d656e745369676e6174757265202d2d2d60448201527f206e6f74207369676e65642062792062656e6566696369616c4f776e657200006064820152608401610432565b5050505050505050565b600063ffffffff821661038f565b6117c781610fcb565b61182d5760405162461bcd60e51b815260206004820152603160248201527f636c656172456e7469746c656d656e742d616e2061637469766520656e7469746044820152701b195b595b9d081b5d5cdd08195e1a5cdd607a1b6064820152608401610432565b63ffffffff81166000908152600460205260409020600101546001600160a01b031633146118d15760405162461bcd60e51b8152602060048201526044602482018190527f636c656172456e7469746c656d656e742d6f6e6c792074686520656e7469746c908201527f656420616464726573732063616e20636c6561722074686520656e7469746c656064820152631b595b9d60e21b608482015260a401610432565b610f8681611fa8565b63ffffffff81166000908152600460205260409020546001600160a01b039081169083168114156119615760405162461bcd60e51b815260206004820152602b60248201527f617070726f76652d617070726f76616c20746f2063757272656e742062656e6560448201526a3334b1b4b0b627bbb732b960a91b6064820152608401610432565b336001600160a01b038216146119d85760405162461bcd60e51b815260206004820152603660248201527f617070726f76652d617070726f76652063616c6c6572206973206e6f74206375604482015275393932b73a103132b732b334b1b4b0b61037bbb732b960511b6064820152608401610432565b610c168383611bf7565b6119f08686868686866115fe565b63ffffffff8416600090815260046020526040902054610449908590889088906001600160a01b03165b611a2384610fcb565b15611ab55760405162461bcd60e51b815260206004820152605660248201527f5f7265676973746572456e7469746c656d656e742d6578697374696e6720656e60448201527f7469746c656d656e74206d75737420626520636c6561726564206265666f7265606482015275207265676973746572696e672061206e6577206f6e6560501b608482015260a401610432565b428263ffffffff1611611b2d5760405162461bcd60e51b815260206004820152603a60248201527f5f7265676973746572456e7469746c656d656e742d656e7469746c656d656e74604482015279206d7573742065787069726520696e207468652066757475726560301b6064820152608401610432565b60408051606080820183526001600160a01b03848116808452878216602080860182815263ffffffff8a8116888a018181528e83166000818152600487528c90209a518b546001600160a01b031916908a16178b5593516001909a01805491519a9098166001600160c01b031990911617600160a01b9990921698909802179094558651938452830152938101929092528101919091527f595e586d44ea8c92723ac9fdd4897e8a5ee6d55e2bad6d386fe7ccbff5912b239060800160405180910390a150505050565b63ffffffff8116600081815260056020908152604080832080546001600160a01b0319166001600160a01b03888116918217909255600490935281842054915192939116917fe26b9b29b95cb706aaebdd86a3524de879b8590b0ba7e61b104f54196bab20319190a45050565b6001600160a01b038116611cd45760405162461bcd60e51b815260206004820152603160248201527f5f73657442656e6566696369616c4f776e65722d6e6577206f776e657220697360448201527020746865207a65726f206164647265737360781b6064820152608401610432565b63ffffffff8216600090815260046020526040812080546001600160a01b0319166001600160a01b038416179055611d0c9083611bf7565b7f183944454934706f9df1285ff0207d23600c244528512cdc01c94f5ec8f601b4828233604051611d3f9392919061240b565b60405180910390a15050565b600154600090610100900460ff1615611d94578160ff166001148015611d705750303b155b611d8c5760405162461bcd60e51b8152600401610432906124e1565b506000919050565b60015460ff808416911610611dbb5760405162461bcd60e51b8152600401610432906124e1565b506001805460ff191660ff9290921691909117815590565b6003546040516331a9108f60e11b815263ffffffff831660048201526000916001600160a01b031690636352211e90602401602060405180830381865afa158015611e22573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061038f9190612358565b6040516b08adce8d2e8d8cadacadce8560a31b6020820152771859191c995cdcc818995b99599a58da585b13dddb995c8b60421b602c820152701859191c995cdcc81bdc195c985d1bdc8b607a1b6044820152741859191c995cdcc81d985d5b1d1059191c995cdccb605a1b60558201526e1d5a5b9d0ccc88185cdcd95d12590b608a1b606a8201526c75696e7433322065787069727960981b6079820152602960f81b608682015260009060870160408051601f198184030181528282528051602091820120855186830151878501516060808a01516080808c0151978a01969096526001600160a01b039485169789019790975291831691870191909152169084015263ffffffff91821660a08401521660c082015260e0015b604051602081830303815290604052805190602001209050919050565b6000805460405161190160f01b6020820152602281019190915260428101839052606201611f62565b63ffffffff81166000818152600460209081526040918290206001810180546001600160c01b03191690555482519384526001600160a01b0316908301527fd7aa6a9fe8ec62f38eafaf1c43d6816defbf427c414d3b0e865cb5cdd52a647c910160405180910390a150565b60006020828403121561202657600080fd5b81356001600160e01b03198116811461203e57600080fd5b9392505050565b6001600160a01b0381168114610f8657600080fd5b803563ffffffff81168114610fc657600080fd5b60008060008060008060c0878903121561208757600080fd5b863561209281612045565b95506120a06020880161205a565b94506120ae6040880161205a565b9350606087013560ff811681146120c457600080fd5b9598949750929560808101359460a0909101359350915050565b60008083601f8401126120f057600080fd5b5081356001600160401b0381111561210757600080fd5b60208301915083602082850101111561211f57600080fd5b9250929050565b60008060008060006080868803121561213e57600080fd5b853561214981612045565b9450602086013561215981612045565b93506040860135925060608601356001600160401b0381111561217b57600080fd5b612187888289016120de565b969995985093965092949392505050565b6000602082840312156121aa57600080fd5b61203e8261205a565b600080604083850312156121c657600080fd5b6121cf8361205a565b915060208301356121df81612045565b809150509250929050565b600080604083850312156121fd57600080fd5b82356121cf81612045565b600060a0828403121561221a57600080fd5b50919050565b6000806000806060858703121561223657600080fd5b61223f8561205a565b9350602085013561224f81612045565b925060408501356001600160401b0381111561226a57600080fd5b612276878288016120de565b95989497509550505050565b6000806040838503121561229557600080fd5b82356122a081612045565b91506122ae6020840161205a565b90509250929050565b6000806000606084860312156122cc57600080fd5b83356122d781612045565b925060208401356122e781612045565b91506122f56040850161205a565b90509250925092565b6000806000806080858703121561231457600080fd5b843561231f81612045565b9350602085013561232f81612045565b925061233d6040860161205a565b9150606085013561234d81612045565b939692955090935050565b60006020828403121561236a57600080fd5b815161203e81612045565b6001600160a01b03929092168252602082015260400190565b6000602082840312156123a057600080fd5b8151801515811461203e57600080fd5b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b63ffffffff9390931683526001600160a01b03918216602084015216604082015260600190565b60006020828403121561244457600080fd5b813561203e81612045565b81546001600160a01b03908116825260019290920154918216602082015260a09190911c63ffffffff16604082015260600190565b6001600160a01b0387811682526020820187905285811660408301528416606082015260a06080820181905281018290526000828460c0840137600060c0848401015260c0601f19601f8501168301019050979650505050505050565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b60608201526080019056fe79ab5c29ad5c9805473ba77c80b4472dd5721aae58e97158a7bbc53205ee7180a264697066735822122019cacf6d4877c733e20ae34489bd7bd1259ca90a435356574a311b65270f156264736f6c634300080a0033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101075760003560e01c806301ffc9a71461010c57806303e7158b14610134578063150b7a02146101495780631f48f6ab1461017557806325a23982146101ab5780632ec14d3f146101be578063485cc955146101d15780634eeeaa32146101e457806360bbf4a4146101f7578063774a76271461020a578063942811b514610232578063af024ae614610245578063ba02481b14610274578063bf6346b714610287578063c72bed151461029a578063dab400f3146102cc578063dbd74309146102e3578063e7dc956b146102f6578063eaf8b32314610309578063ebc5eb071461031c578063ecb2c20b1461032f575b600080fd5b61011f61011a366004612014565b61035e565b60405190151581526020015b60405180910390f35b61014761014236600461206e565b610395565b005b61015c610157366004612126565b610451565b6040516001600160e01b0319909116815260200161012b565b610193610183366004612198565b506003546001600160a01b031690565b6040516001600160a01b03909116815260200161012b565b6101476101b93660046121b3565b610730565b6101476101cc3660046121b3565b61094f565b6101476101df3660046121ea565b610ad1565b6101476101f2366004612198565b610c1b565b610147610205366004612208565b610e47565b61021d610218366004612198565b610f89565b60405163ffffffff909116815260200161012b565b61011f610240366004612198565b610fcb565b610193610253366004612198565b63ffffffff166000908152600560205260409020546001600160a01b031690565b61011f610282366004612198565b611020565b610147610295366004612220565b61103c565b6102ad6102a8366004612198565b6115c2565b6040805192151583526001600160a01b0390911660208301520161012b565b6102d560005481565b60405190815260200161012b565b6101476102f136600461206e565b6115fe565b6102d5610304366004612198565b6117b0565b610147610317366004612198565b6117be565b61014761032a366004612282565b6118da565b61019361033d366004612198565b63ffffffff166000908152600460205260409020546001600160a01b031690565b60006001600160e01b0319821663162ff4f760e21b148061038f57506001600160e01b031982166301ffc9a760e01b145b92915050565b63ffffffff84166000908152600460205260409020546001600160a01b031661043b5760405162461bcd60e51b815260206004820152604760248201527f696d706f7365456e7469746c656d656e742d62656e6566696369616c206f776e60448201527f6572206d7573742062652073657420746f20696d706f736520616e20656e74696064820152661d1b195b595b9d60ca1b608482015260a4015b60405180910390fd5b6104498686868686866119e2565b505050505050565b600063ffffffff8411156104b85760405162461bcd60e51b815260206004820152602860248201527f6f6e45524337323152656365697665642d746f6b656e4964206973206f7574206044820152676f662072616e676560c01b6064820152608401610432565b6003546001600160a01b031633141561060757811561053c57606082141561050457600080806104ea858701876122b7565b9250925092506104fc87838386611a1a565b505050610546565b6000808080610515868801886122fe565b935093509350935061052988848487611a1a565b6105338189611bf7565b50505050610546565b6105468486611c64565b60405163ecb2c20b60e01b815263ffffffff851660048201527f7897fd58abdfc815ef9370ad67929a217fbaa4b692c4b1086930ae97e4a7fe2d90309063ecb2c20b90602401602060405180830381865afa1580156105a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105cd9190612358565b604080516001600160a01b0392831681529189166020830152339082015263ffffffff8616606082015260800160405180910390a161071e565b60065460035460405163194f672960e01b81526001600160a01b039283169263194f67299261065d929116907f0c6296a577719ee1835d23ea2ac4f93808edda4f265df0440caeecc95a06857590600401612375565b602060405180830381865afa15801561067a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061069e919061238e565b61071e5760405162461bcd60e51b815260206004820152604560248201527f6f6e45524337323152656365697665642d6e6f6e2d657363726f77206173736560448201527f742072657475726e6564207768656e2061697264726f7073206172652064697360648201526418589b195960da1b608482015260a401610432565b50630a85bd0160e11b95945050505050565b6002805414156107525760405162461bcd60e51b8152600401610432906123b0565b6002805563ffffffff82166000908152600460205260409020546001600160a01b038281169116146108025760405162461bcd60e51b815260206004820152604d60248201527f636c656172456e7469746c656d656e74416e64446973747269627574652d4f6e60448201527f6c79207468652062656e6566696369616c206f776e65722063616e207265636560648201526c1a5d99481d1a1948185cdcd95d609a1b608482015260a401610432565b6001600160a01b03811661088a5760405162461bcd60e51b815260206004820152604360248201527f636c656172456e7469746c656d656e74416e64446973747269627574652d617360448201527f736574732063616e6e6f742062652073656e7420746f206e756c6c206164647260648201526265737360e81b608482015260a401610432565b610893826117be565b6003546001600160a01b03166342842e0e308363ffffffff86166040518463ffffffff1660e01b81526004016108cb939291906123e7565b600060405180830381600087803b1580156108e557600080fd5b505af11580156108f9573d6000803e3d6000fd5b50505063ffffffff831660009081526004602052604090819020549051600080516020612530833981519152925061093e91859185916001600160a01b03169061240b565b60405180910390a150506001600255565b61095882610fcb565b15610a1e5763ffffffff82166000908152600460205260409020600101546001600160a01b03163314610a195760405162461bcd60e51b815260206004820152606060248201527f73657442656e6566696369616c4f776e65722d6f6e6c792074686520636f6e7460448201527f726163742077697468207468652061637469766520656e7469746c656d656e7460648201527f2063616e20757064617465207468652062656e6566696369616c206f776e6572608482015260a401610432565b610ac3565b63ffffffff82166000908152600460205260409020546001600160a01b03163314610ac35760405162461bcd60e51b815260206004820152604960248201527f73657442656e6566696369616c4f776e65722d6f6e6c7920746865206375727260448201527f656e74206f776e65722063616e20757064617465207468652062656e6566696360648201526834b0b61037bbb732b960b91b608482015260a401610432565b610acd8282611c64565b5050565b6000610add6001611d4b565b90508015610af5576001805461ff0019166101001790555b604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6020808301919091527fbefac456e8d3c7dcbe25358dd865ef756e23bdcd0f4f36a6e915a0f24b6849e9828401527f06c015bd22b4c69690933c1058878ebdfef31f9aaae40bbe86d8a09fe1b2972c60608301524660808301526001600160a01b03851660a0808401919091528351808403909101815260c09092019092528051910120600055600380546001600160a01b038086166001600160a01b03199283161790925560068054928516929091169190911790558015610c16576001805461ff00191681556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050565b600280541415610c3d5760405162461bcd60e51b8152600401610432906123b0565b60028055610c4a81610fcb565b15610cce5760405162461bcd60e51b815260206004820152604860248201527f7769746864726177616c41737365742d7468652061737365742063616e6e6f7460448201527f2062652077697468647261776e207769746820616e2061637469766520656e746064820152671a5d1b195b595b9d60c21b608482015260a401610432565b63ffffffff81166000908152600460205260409020546001600160a01b03163314610d6b5760405162461bcd60e51b815260206004820152604160248201527f7769746864726177616c41737365742d6f6e6c79207468652062656e6566696360448201527f69616c206f776e65722063616e207769746864726177616c20616e20617373656064820152601d60fa1b608482015260a401610432565b60035463ffffffff82166000908152600460205260409020546001600160a01b03918216916342842e0e91309116610da68563ffffffff1690565b6040518463ffffffff1660e01b8152600401610dc4939291906123e7565b600060405180830381600087803b158015610dde57600080fd5b505af1158015610df2573d6000803e3d6000fd5b50505063ffffffff8216600090815260046020526040908190205490516000805160206125308339815191529250610e3791849133916001600160a01b03169061240b565b60405180910390a1506001600255565b3360046000610e5c6080850160608601612198565b63ffffffff1681526020810191909152604001600020546001600160a01b03161480610eba57503360056000610e986080850160608601612198565b63ffffffff1681526020810191909152604001600020546001600160a01b0316145b610f4d5760405162461bcd60e51b815260206004820152605860248201527f6772616e74456e7469746c656d656e742d6f6e6c79207468652062656e65666960448201527f6369616c206f776e6572206f7220617070726f766564206f70657261746f722060648201527718d85b8819dc985b9d08185b88195b9d1a5d1b195b595b9d60421b608482015260a401610432565b610f86610f606080830160608401612198565b610f706040840160208501612432565b610f8060a0850160808601612198565b33611a1a565b50565b6000610f9482610fcb565b610fa057506000919050565b5063ffffffff908116600090815260046020526040902060010154600160a01b90041690565b919050565b63ffffffff8082166000908152600460205260408120600101549091600160a01b909104164210801561038f57505063ffffffff166000908152600460205260409020600101546001600160a01b0316151590565b60003061102c83611dd3565b6001600160a01b03161492915050565b60028054141561105e5760405162461bcd60e51b8152600401610432906123b0565b60028055826001600160a01b0381166110b25760405162461bcd60e51b8152602060048201526016602482015275666c6173684c6f616e2d7a65726f206164647265737360501b6044820152606401610432565b306110bc86611dd3565b6001600160a01b0316146111115760405162461bcd60e51b815260206004820152601c60248201527b199b185cda131bd85b8b585cdcd95d081b9bdd081a5b881d985d5b1d60221b6044820152606401610432565b63ffffffff85166000908152600460205260409020546001600160a01b0316331461118e5760405162461bcd60e51b815260206004820152602760248201527f666c6173684c6f616e2d6e6f742063616c6c65642062792074686520617373656044820152663a1037bbb732b960c91b6064820152608401610432565b60065460035460405163194f672960e01b81526001600160a01b039283169263194f6729926111e4929116907f7b2ae2881a80edd04dac29e0424a8afcb85d139d6204e45ea854215cb864990b90600401612375565b602060405180830381865afa158015611201573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611225919061238e565b156112915760405162461bcd60e51b815260206004820152603660248201527f666c6173684c6f616e2d666c6173684c6f616e20666561747572652064697361604482015275189b195908199bdc881d1a1a5cc818dbdb9d1c9858dd60521b6064820152608401610432565b63ffffffff8516600090815260046020908152604080832090516112b5920161244f565b60408051601f19818403018152908290528051602090910120600354632142170760e11b83529092506001600160a01b0316906342842e0e90611306903090899063ffffffff8c16906004016123e7565b600060405180830381600087803b15801561132057600080fd5b505af1158015611334573d6000803e3d6000fd5b505060035460405163817dc5e560e01b81526001600160a01b03868116945063817dc5e5935061137892169063ffffffff8b1690339030908b908b90600401612484565b6020604051808303816000875af1158015611397573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113bb919061238e565b6114225760405162461bcd60e51b815260206004820152603260248201527f666c6173684c6f616e2d74686520666c617368206c6f616e20636f6e7472616360448201527174206d7573742072657475726e207472756560701b6064820152608401610432565b6003546001600160a01b03166323b872dd863063ffffffff8a166040518463ffffffff1660e01b815260040161145a939291906123e7565b600060405180830381600087803b15801561147457600080fd5b505af1158015611488573d6000803e3d6000fd5b50505050306001600160a01b031661149f87611dd3565b6001600160a01b0316146114b257600080fd5b63ffffffff861660009081526004602090815260409182902091516114d892910161244f565b6040516020818303038152906040528051906020012081146115535760405162461bcd60e51b815260206004820152602e60248201527f666c6173684c6f616e2d656e7469746c656d656e742073746174652063616e6e60448201526d1bdd081899481b5bd91a599a595960921b6064820152608401610432565b63ffffffff86166000818152600460209081526040918290205482516001600160a01b0391821681529182019390935291871682820152517fb9a0fd57b76d147a2f98aed604908c2285d360c4349bf8c59e89365e39b2f9e29181900360600190a15050600160025550505050565b60008060006115d084610fcb565b63ffffffff90941660009081526004602052604090206001015493946001600160a01b039094169392505050565b6040805160a08101825263ffffffff808716600081815260046020908152858220546001600160a01b0390811686528c169085015230948401949094526060830152871660808201526116599061165490611e46565b611f7f565b6040805160008082526020820180845284905260ff88169282019290925260608101869052608081018590529192509060019060a0016020604051602081039080840390855afa1580156116b1573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166117105760405162461bcd60e51b81526020600482015260196024820152781c9958dbdd995c9959081859191c995cdcc81a5cc81b9d5b1b603a1b6044820152606401610432565b63ffffffff86166000908152600460205260409020546001600160a01b038281169116146117a65760405162461bcd60e51b815260206004820152603e60248201527f76616c6964617465456e7469746c656d656e745369676e6174757265202d2d2d60448201527f206e6f74207369676e65642062792062656e6566696369616c4f776e657200006064820152608401610432565b5050505050505050565b600063ffffffff821661038f565b6117c781610fcb565b61182d5760405162461bcd60e51b815260206004820152603160248201527f636c656172456e7469746c656d656e742d616e2061637469766520656e7469746044820152701b195b595b9d081b5d5cdd08195e1a5cdd607a1b6064820152608401610432565b63ffffffff81166000908152600460205260409020600101546001600160a01b031633146118d15760405162461bcd60e51b8152602060048201526044602482018190527f636c656172456e7469746c656d656e742d6f6e6c792074686520656e7469746c908201527f656420616464726573732063616e20636c6561722074686520656e7469746c656064820152631b595b9d60e21b608482015260a401610432565b610f8681611fa8565b63ffffffff81166000908152600460205260409020546001600160a01b039081169083168114156119615760405162461bcd60e51b815260206004820152602b60248201527f617070726f76652d617070726f76616c20746f2063757272656e742062656e6560448201526a3334b1b4b0b627bbb732b960a91b6064820152608401610432565b336001600160a01b038216146119d85760405162461bcd60e51b815260206004820152603660248201527f617070726f76652d617070726f76652063616c6c6572206973206e6f74206375604482015275393932b73a103132b732b334b1b4b0b61037bbb732b960511b6064820152608401610432565b610c168383611bf7565b6119f08686868686866115fe565b63ffffffff8416600090815260046020526040902054610449908590889088906001600160a01b03165b611a2384610fcb565b15611ab55760405162461bcd60e51b815260206004820152605660248201527f5f7265676973746572456e7469746c656d656e742d6578697374696e6720656e60448201527f7469746c656d656e74206d75737420626520636c6561726564206265666f7265606482015275207265676973746572696e672061206e6577206f6e6560501b608482015260a401610432565b428263ffffffff1611611b2d5760405162461bcd60e51b815260206004820152603a60248201527f5f7265676973746572456e7469746c656d656e742d656e7469746c656d656e74604482015279206d7573742065787069726520696e207468652066757475726560301b6064820152608401610432565b60408051606080820183526001600160a01b03848116808452878216602080860182815263ffffffff8a8116888a018181528e83166000818152600487528c90209a518b546001600160a01b031916908a16178b5593516001909a01805491519a9098166001600160c01b031990911617600160a01b9990921698909802179094558651938452830152938101929092528101919091527f595e586d44ea8c92723ac9fdd4897e8a5ee6d55e2bad6d386fe7ccbff5912b239060800160405180910390a150505050565b63ffffffff8116600081815260056020908152604080832080546001600160a01b0319166001600160a01b03888116918217909255600490935281842054915192939116917fe26b9b29b95cb706aaebdd86a3524de879b8590b0ba7e61b104f54196bab20319190a45050565b6001600160a01b038116611cd45760405162461bcd60e51b815260206004820152603160248201527f5f73657442656e6566696369616c4f776e65722d6e6577206f776e657220697360448201527020746865207a65726f206164647265737360781b6064820152608401610432565b63ffffffff8216600090815260046020526040812080546001600160a01b0319166001600160a01b038416179055611d0c9083611bf7565b7f183944454934706f9df1285ff0207d23600c244528512cdc01c94f5ec8f601b4828233604051611d3f9392919061240b565b60405180910390a15050565b600154600090610100900460ff1615611d94578160ff166001148015611d705750303b155b611d8c5760405162461bcd60e51b8152600401610432906124e1565b506000919050565b60015460ff808416911610611dbb5760405162461bcd60e51b8152600401610432906124e1565b506001805460ff191660ff9290921691909117815590565b6003546040516331a9108f60e11b815263ffffffff831660048201526000916001600160a01b031690636352211e90602401602060405180830381865afa158015611e22573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061038f9190612358565b6040516b08adce8d2e8d8cadacadce8560a31b6020820152771859191c995cdcc818995b99599a58da585b13dddb995c8b60421b602c820152701859191c995cdcc81bdc195c985d1bdc8b607a1b6044820152741859191c995cdcc81d985d5b1d1059191c995cdccb605a1b60558201526e1d5a5b9d0ccc88185cdcd95d12590b608a1b606a8201526c75696e7433322065787069727960981b6079820152602960f81b608682015260009060870160408051601f198184030181528282528051602091820120855186830151878501516060808a01516080808c0151978a01969096526001600160a01b039485169789019790975291831691870191909152169084015263ffffffff91821660a08401521660c082015260e0015b604051602081830303815290604052805190602001209050919050565b6000805460405161190160f01b6020820152602281019190915260428101839052606201611f62565b63ffffffff81166000818152600460209081526040918290206001810180546001600160c01b03191690555482519384526001600160a01b0316908301527fd7aa6a9fe8ec62f38eafaf1c43d6816defbf427c414d3b0e865cb5cdd52a647c910160405180910390a150565b60006020828403121561202657600080fd5b81356001600160e01b03198116811461203e57600080fd5b9392505050565b6001600160a01b0381168114610f8657600080fd5b803563ffffffff81168114610fc657600080fd5b60008060008060008060c0878903121561208757600080fd5b863561209281612045565b95506120a06020880161205a565b94506120ae6040880161205a565b9350606087013560ff811681146120c457600080fd5b9598949750929560808101359460a0909101359350915050565b60008083601f8401126120f057600080fd5b5081356001600160401b0381111561210757600080fd5b60208301915083602082850101111561211f57600080fd5b9250929050565b60008060008060006080868803121561213e57600080fd5b853561214981612045565b9450602086013561215981612045565b93506040860135925060608601356001600160401b0381111561217b57600080fd5b612187888289016120de565b969995985093965092949392505050565b6000602082840312156121aa57600080fd5b61203e8261205a565b600080604083850312156121c657600080fd5b6121cf8361205a565b915060208301356121df81612045565b809150509250929050565b600080604083850312156121fd57600080fd5b82356121cf81612045565b600060a0828403121561221a57600080fd5b50919050565b6000806000806060858703121561223657600080fd5b61223f8561205a565b9350602085013561224f81612045565b925060408501356001600160401b0381111561226a57600080fd5b612276878288016120de565b95989497509550505050565b6000806040838503121561229557600080fd5b82356122a081612045565b91506122ae6020840161205a565b90509250929050565b6000806000606084860312156122cc57600080fd5b83356122d781612045565b925060208401356122e781612045565b91506122f56040850161205a565b90509250925092565b6000806000806080858703121561231457600080fd5b843561231f81612045565b9350602085013561232f81612045565b925061233d6040860161205a565b9150606085013561234d81612045565b939692955090935050565b60006020828403121561236a57600080fd5b815161203e81612045565b6001600160a01b03929092168252602082015260400190565b6000602082840312156123a057600080fd5b8151801515811461203e57600080fd5b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b63ffffffff9390931683526001600160a01b03918216602084015216604082015260600190565b60006020828403121561244457600080fd5b813561203e81612045565b81546001600160a01b03908116825260019290920154918216602082015260a09190911c63ffffffff16604082015260600190565b6001600160a01b0387811682526020820187905285811660408301528416606082015260a06080820181905281018290526000828460c0840137600060c0848401015260c0601f19601f8501168301019050979650505050505050565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b60608201526080019056fe79ab5c29ad5c9805473ba77c80b4472dd5721aae58e97158a7bbc53205ee7180a264697066735822122019cacf6d4877c733e20ae34489bd7bd1259ca90a435356574a311b65270f156264736f6c634300080a0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.