// Contract: HomeWorkDeployer
// Target function: function getHomeAddressInformation(bytes32 key)
 can be checked by calling
   * `getHomeAddressInformation` and `isDeployable` with the key obtained by
   * calling `getDerivedKey`.
   * @param salt bytes32 The salt value that is used to derive the key.
   * @param initializationRuntimeStorageContract address The storage contract
   * with runtime code equal to the contract creation code that will be used to
   * deploy the contract to the home address.
   * @return The home address, derived key, and runtime code hash of the
   * deployed contract.
   * @dev When deploying a contract to a home address via this method, the
   * metamorphic initialization code will retrieve whatever initialization code
   * currently resides at the specified address and use it to set up and deploy
   * the desired contract to the home address. Bear in mind that the deployed
   * contract will interpret msg.sender as the address of THIS contract, and not
   * the address of the submitter - if the constructor of the deployed contract
   * uses msg.sender to set up ownership or other variables, you must modify it
   * to accept a constructor argument with the appropriate address, or
   * alternately to hard-code the intended address. Also, if your contract DOES
   * have constructor arguments, remember to include them as ABI-encoded
   * arguments at the end of the initialization code, just as you would when
   * performing a standard deploy. You may want to provide the salt and
   * submitter to `setDerivedReverseLookup` in order to find the salt,
   * submitter, and derived key using only the home address to prevent
   * accidentally losing them.
   */
  function deriveKeyAndDeployViaExistingRuntimeStorageContract(
    bytes32 salt,
    address initializationRuntimeStorageContract
  )
    external
    payable
    returns (address homeAddress, bytes32 key, bytes32 runtimeCodeHash)
  {
    // Ensure that the supplied runtime storage contract is not empty.
    _validateRuntimeStorageIsNotEmpty(initializationRuntimeStorageContract);

    // Derive key and prepare to deploy using supplied salt and calling address.
    key = _deriveKeyAndPrepareToDeploy(salt);

    // Set the initialization runtime storage contract in contract storage.
    _initializationRuntimeStorageContract = initializationRuntimeStorageContract;

    // Use metamorphic initialization code to deploy contract to home address.
    (homeAddress, runtimeCodeHash) = _deployToHomeAddress(key);
  }

  /**
   * @notice Mint multiple ERC721 tokens, designated by their keys, to the
   * specified owner. Keys that aren't controlled, or that point to home
   * addresses that are currently deployed, will be skipped.
   * @param owner address The account that will be granted ownership of the
   * ERC721 tokens.
   * @param keys bytes32[] An array of values used to derive each home address.
   * @dev If you plan to use this method regularly or want to keep gas costs to
   * an absolute minimum, and are willing to go without standard ABI encoding,
   * see `batchLock_63efZf` for a more efficient (and unforgiving)
   * implementation. For batch token minting with *derived* keys, see
   * `deriveKeysAndBatchLock`.
   */
  function batchLock(address owner, bytes32[] calldata keys) external {
    // Track each key in the array of keys.
    bytes32 key;

    // Ensure that the specified owner is a valid ERC721 receiver.
    if (keys.length > 0) {
      _validateOwner(owner, keys[0]);
    }

    // Iterate through each provided key argument.
    for (uint256 i; i < keys.length; i++) {
      key = keys[i];

      // Skip if the key currently has a contract deployed to its home address.
      if (!_isNotDeployed(key)) {
        continue;
      }

      // Skip if the caller is not the controller.
      if (_getController(key) != msg.sender) {
        continue;
      }

      // Set the exists flag to true and the controller to this contract.
      HomeAddress storage home = _home[key];
      home.exists = true;
      home.controller = address(this);

      // Emit an event signifying that this contract is now the controller. 
      emit NewController(key, address(this));

      // Mint the ERC721 token to the designated owner.
      _mint(owner, uint256(key));
    }
  }

  /**
   * @notice Mint multiple ERC721 tokens, designated by salts that are hashed
   * with the caller's address to derive each key, to the specified owner.
   * Derived keys that aren't controlled, or that point to home addresses that
   * are currently deployed, will be skipped.
   * @param owner address The account that will be granted ownership of the
   * ERC721 tokens.
   * @param salts bytes32[] An array of values used to derive each key and
   * corresponding home address.
   * @dev See `batchLock` for batch token minting with standard, non-derived
   * keys.
   */
  function deriveKeysAndBatchLock(address owner, bytes32[] calldata salts)
    external
  {
    // Track each key derived from the array of salts.
    bytes32 key;

    // Ensure that the specified owner is a valid ERC721 receiver.
    if (salts.length > 0) {
      _validateOwner(owner, _deriveKey(salts[0], msg.sender));
    }

    // Iterate through each provided salt argument.
    for (uint256 i; i < salts.length; i++) {
      // Derive the key using the supplied salt and the calling address.
      key = _deriveKey(salts[i], msg.sender);

      // Skip if the key currently has a contract deployed to its home address.
      if (!_isNotDeployed(key)) {
        continue;
      }

      // Skip if the caller is not the controller.
      HomeAddress storage home = _home[key];
      if (home.exists && home.controller != msg.sender) {
        continue;
      }

      // Set the exists flag to true and the controller to this contract.
      home.exists = true;
      home.controller = address(this);

      // Emit an event signifying that this contract is now the controller. 
      emit NewController(key, address(this));

      // Mint the ERC721 token to the designated owner.
      _mint(owner, uint256(key));
    }
  }

  /**
   * @notice Safely transfers the ownership of a group of token IDs to another
   * address in a batch. If the target address is a contract, it must implement
   * `onERC721Received`, called upon a safe transfer, and return the magic value
   * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;
   * otherwise, or if another error occurs, the entire batch is reverted.
   * Requires msg.sender to be the owner, approved, or operator of the tokens.
   * @param from address The current owner of the tokens.
   * @param to address The account to receive ownership of the given tokens.
   * @param tokenIds uint256[] ID of the tokens to be transferred.
   */
  function safeBatchTransferFrom(
    address from,
    address to,
    uint256[] calldata tokenIds
  )
    external
  {
    // Track each token ID in the batch.
    uint256 tokenId;

    // Iterate over each supplied token ID.
    for (uint256 i = 0; i < tokenIds.length; i++) {
      // Set the current token ID.
      tokenId = tokenIds[i];

      // Perform the token transfer.
      safeTransferFrom(from, to, tokenId);
    }
  }

  /**
   * @notice Safely transfers the ownership of a group of token IDs to another
   * address in a batch. If the target address is a contract, it must implement
   * `onERC721Received`, called upon a safe transfer, and return the magic value
   * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;
   * otherwise, or if another error occurs, the entire batch is reverted.
   * Requires msg.sender to be the owner, approved, or operator of the tokens.
   * @param from address The current owner of the tokens.
   * @param to address The account to receive ownership of the given tokens.
   * @param tokenIds uint256[] ID of the tokens to be transferred.
   * @param data bytes A data payload to include with each transfer.   
   */
  function safeBatchTransferFrom(
    address from,
    address to,
    uint256[] calldata tokenIds,
    bytes calldata data
  )
    external
  {
    // Track each token ID in the batch.
    uint256 tokenId;

    // Iterate over each supplied token ID.
    for (uint256 i = 0; i < tokenIds.length; i++) {
      // Set the current token ID.
      tokenId = tokenIds[i];

      // Perform the token transfer.
      safeTransferFrom(from, to, tokenId, data);
    }
  }

  /**
   * @notice Efficient version of `batchLock` that uses less gas. The first 20
   * bytes of each key are automatically populated using msg.sender, and the
   * remaining key segments are passed in as a packed byte array, using twelve
   * bytes per segment, with a function selector of 0x00000000 followed by a
   * twenty-byte segment for the desired owner of the minted ERC721 tokens. Note
   * that an attempt to lock a key that is not controlled or with its contract
   * already deployed will cause the entire batch to revert. Checks on whether
   * the owner is a valid ERC721 receiver are also skipped, similar to using
   * `transferFrom` instead of `safeTransferFrom`.
   */
  function batchLock_63efZf(/* packed owner and key segments */) external {
    // Get the owner from calldata, located at bytes 4-23 (sig is bytes 0-3).
    address owner;

    // Determine number of 12-byte key segments in calldata from byte 24 on.
    uint256 passedSaltSegments;

    // Get the owner and calculate the total number of key segments.
    assembly {
      owner := shr(0x60, calldataload(4))                  // comes after sig
      passedSaltSegments := div(sub(calldatasize, 24), 12) // after sig & owner
    }

    // Track each key, located at each 12-byte segment from byte 24 on.
    bytes32 key;

    // Iterate through each provided key segment argument.
    for (uint256 i; i < passedSaltSegments; i++) {
      // Construct keys by concatenating msg.sender with each key segment.
      assembly {
        key := add(                   // Combine msg.sender & provided key.
          shl(0x60, caller),          // Place msg.sender at start of word.
          shr(0xa0, calldataload(add(24, mul(i, 12))))   // Segment at end.
        )
      }

      // Ensure that the key does not currently have a deployed contract.
      require(_isNotDeployed(key), _ACCOUNT_EXISTS);

      // Ensure that the caller is the controller of the key.
      HomeAddress storage home = _home[key];
      if (home.exists) {
        require(home.controller == msg.sender, _ONLY_CONTROLLER);
      }

      // Set the exists flag to true and the controller to this contract.
      home.exists = true;
      home.controller = address(this);

      // Emit an event signifying that this contract is now the controller. 
      emit NewController(key, address(this));

      // Mint the ERC721 token to the designated owner.
      _mint(owner, uint256(key));
    }
  }

  /**
   * @notice Perform a dry-run of the deployment of a contract using a given key
   * and revert on successful deployment. It cannot be called from outside the
   * contract (even though it is marked as external).
   * @param key bytes32 The unique value used to derive the home address.
   * @dev This contract is called by `_isNotDeployable` in extreme cases where
   * the deployability of the contract cannot be determined conclusively.
   */
  function staticCreate2Check(bytes32 key) external {
    require(
      msg.sender == address(this),
      "This function can only be called by this contract."
    );

    assembly {
      // Write the 32-byte metamorphic initialization code to scratch space.
      mstore(
        0,
        0x5859385958601c335a585952fa1582838382515af43d3d93833e601e57fd5bf3
      )

      // Call `CREATE2` using metamorphic init code with supplied key as salt.
      let deploymentAddress := create2(0, 0, 32, key)

      // Revert and return the metamorphic init code on successful deployment.
      if deploymentAddress {        
        revert(0, 32)
      }
    }
  }

  /**
   * @notice Submit a key to claim the "high score" - the lower the uint160
   * value of the key's home address, the higher the score. The high score
   * holder has the exclusive right to recover lost ether and tokens on this
   * contract.
   * @param key bytes32 The unique value used to derive the home address that
   * will determine the resultant score.
   * @dev The high score must be claimed by a direct key (one that is submitted
   * by setting the first 20 bytes of the key to the address of the submitter)
   * and not by a derived key, and is non-transferrable. If you want to help
   * people recover their lost tokens, you might consider deploying a contract
   * to the high score address (probably a metamorphic one so that you can use
   * the home address later) with your contact information.
   */
  function claimHighScore(bytes32 key) external {
    require(
      msg.sender == address(bytes20(key)),
      "Only submitters directly encoded in a given key may claim a high score."
    );

    // Derive the "home address" of the current high score key.
    address currentHighScore = _getHomeAddress(_highScoreKey);

    // Derive the "home address" of the new high score key.
    address newHighScore = _getHomeAddress(key);

    // Use addresses to ensure that supplied key is in fact a new high score.
    require(
      uint160(newHighScore) < uint160(currentHighScore),
      "Submitted high score is not better than the current high score."
    );

    // Set the new high score to the supplied key.
    _highScoreKey = key;

    // The score is equal to (2^160 - 1) - ("home address" of high score key).
    uint256 score = uint256(uint160(-1) - uint160(newHighScore));

    // Emit an event to signify that a new high score has been reached.
    emit NewHighScore(key, msg.sender, score);
  }

  /**
   * @notice Transfer any ether or ERC20 tokens that have somehow ended up at
   * this contract by specifying a token address (set to the null address for
   * ether) as well as a recipient address. Only the high score holder can
   * recover lost ether and tokens on this contract.
   * @param token address The contract address of the ERC20 token to recover, or
   * the null address for recovering Ether.
   * @param recipient address payable The account where recovered funds should
   * be transferred.
   * @dev If you are trying to recover funds that were accidentally sent into
   * this contract, see if you can contact the holder of the current high score,
   * found by calling `getHighScore`. Better yet, try to find a new high score
   * yourself!
   */
  function recover(IERC20 token, address payable recipient) external {
    require(
      msg.sender == address(bytes20(_highScoreKey)),
      "Only the current high score holder may recover tokens."
    );

    if (address(token) == address(0)) {
      // Recover ETH if the token's contract address is set to the null address.
      recipient.transfer(address(this).balance);
    } else {
      // Determine the given ERC20 token balance and transfer to the recipient.
      uint256 balance = token.balanceOf(address(this));
      token.transfer(recipient, balance);
    }
  }

  /**
   * @notice "View" function to determine if a contract can currently be
   * deployed to a home address given the corresponding key. A contract is only
   * deployable if no account currently exists at the address - any existing
   * contract must be destroyed via `SELFDESTRUCT` before a new contract can be
   * deployed to a home address. This method does not modify state but is
   * inaccessible via staticcall.
   * @param key bytes32 The unique value used to derive the home address.
   * @return A boolean signifying if a contract can be deployed to the home
   * address that corresponds to the provided key.
   * @dev This will not detect if a contract is not deployable due control
   * having been relinquished on the key.
   */
  function isDeployable(bytes32 key)
    external
    /* view */
    returns (bool deployable)
  {
    deployable = _isNotDeployed(key);
  }

  /**
   * @notice View function to get the current "high score", or the lowest
   * uint160 value of a home address of all keys submitted. The high score
   * holder has the exclusive right to recover lost ether and tokens on this
   * contract.
   * @return The current high score holder, their score, and the submitted key.
   */
  function getHighScore()
    external
    view
    returns (address holder, uint256 score, bytes32 key)
  {
    // Get the key and subbmitter holding the current high score.
    key = _highScoreKey;
    holder = address(bytes20(key));

    // The score is equal to (2^160 - 1) - ("home address" of high score key).
    score = uint256(uint160(-1) - uint160(_getHomeAddress(key)));
  }