// Contract: HomeWorkDeployer
// Target function: function _getHomeAddress(bytes32 key)
 }

  /**
   * @notice Pure function to determine the home address that corresponds to
   * a given key.
   * @param key bytes32 The unique value used to derive the home address.
   * @return The home address.
   */
  function getHomeAddress(bytes32 key)
    external
    pure
    returns (address homeAddress)
  {
    // Derive the home address using the supplied key.
    homeAddress = _getHomeAddress(key);
  }

  /**
   * @notice Pure function for retrieving the metamorphic initialization code
   * used to deploy arbitrary contracts to home addresses. Provided for easy
   * verification and for use in other applications.
   * @return The 32-byte metamorphic initialization code.
   * @dev This metamorphic init code works via the "metamorphic delegator"
   * mechanism, which is explained in greater detail at `_deployToHomeAddress`.
   */
  function getMetamorphicDelegatorInitializationCode()
    external
    pure
    returns (bytes32 metamorphicDelegatorInitializationCode)
  {
    metamorphicDelegatorInitializationCode = _HOME_INIT_CODE;
  }

  /**
   * @notice Pure function for retrieving the keccak256 of the metamorphic
   * initialization code used to deploy arbitrary contracts to home addresses.
   * This is the value that you should use, along with this contract's address
   * and a caller address that you control, to mine for an partucular type of
   * home address (such as one at a compact or gas-efficient address).
   * @return The keccak256 hash of the metamorphic initialization code.
   */
  function getMetamorphicDelegatorInitializationCodeHash()
    external
    pure
    returns (bytes32 metamorphicDelegatorInitializationCodeHash)
  {
    metamorphicDelegatorInitializationCodeHash = _HOME_INIT_CODE_HASH;
  }

  /**
   * @notice Pure function for retrieving the prelude that will be inserted
   * ahead of the code payload in order to deploy a runtime storage contract.
   * @return The 11-byte "arbitrary runtime" prelude.
   */
  function getArbitraryRuntimeCodePrelude()
    external
    pure
    returns (bytes11 prelude)
  {
    prelude = _ARBITRARY_RUNTIME_PRELUDE;
  }

  /**
   * @notice Internal function for deploying a runtime storage contract given a
   * particular payload.
   * @return The address of the runtime storage contract.
   * @dev To take the provided code payload and deploy a contract with that
   * payload as its runtime code, use the following prelude:
   *
   * 0x600b5981380380925939f3...
   *
   * 00  60  push1 0b      [11 -> offset]
   * 02  59  msize         [offset, 0]
   * 03  81  dup2          [offset, 0, offset]
   * 04  38  codesize      [offset, 0, offset, codesize]
   * 05  03  sub           [offset, 0, codesize - offset]
   * 06  80  dup1          [offset, 0, codesize - offset, codesize - offset]
   * 07  92  swap3         [codesize - offset, 0, codesize - offset, offset]
   * 08  59  msize         [codesize - offset, 0, codesize - offset, offset, 0]
   * 09  39  codecopy      [codesize - offset, 0] <init_code_in_runtime>
   * 10  f3  return        [] *init_code_in_runtime*
   * ... init_code
   */
  function _deployRuntimeStorageContract(bytes memory payload)
    internal
    returns (address runtimeStorageContract)
  {
    // Construct the contract creation code using the prelude and the payload.
    bytes memory runtimeStorageContractCreationCode = abi.encodePacked(
      _ARBITRARY_RUNTIME_PRELUDE,
      payload
    );

    assembly {
      // Get the location and length of the newly-constructed creation code.
      let encoded_data := add(0x20, runtimeStorageContractCreationCode)
      let encoded_size := mload(runtimeStorageContractCreationCode)

      // Deploy the runtime storage contract via standard `CREATE`.
      runtimeStorageContract := create(0, encoded_data, encoded_size)

      // Pass along revert message if the contract did not deploy successfully.
      if iszero(runtimeStorageContract) {
        returndatacopy(0, 0, returndatasize)
        revert(0, returndatasize)
      }
    }

    // Emit an event with address of newly-deployed runtime storage contract.
    emit NewRuntimeStorageContract(runtimeStorageContract, keccak256(payload));
  }

  /**
   * @notice Internal function for deploying arbitrary contract code to the home
   * address corresponding to a suppied key via metamorphic initialization code.
   * @return The home address and the hash of the deployed runtime code.
   * @dev This deployment method uses the "metamorphic delegator" pattern, where
   * it will retrieve the address of the contract that contains the target
   * initialization code, then delegatecall into it, which executes the
   * initialization code stored there and returns the runtime code (or reverts).
   * Then, the runtime code returned by the delegatecall is returned, and since
   * we are still in the initialization context, it will be set as the runtime
   * code of the metamorphic contract. The 32-byte metamorphic initialization
   * code is as follows:
   *
   * 0x5859385958601c335a585952fa1582838382515af43d3d93833e601e57fd5bf3
   *
   * 00  58  PC               [0]
   * 01  59  MSIZE            [0, 0]
   * 02  38  CODESIZE         [0, 0, codesize -> 32]
   * returndatac03  59  MSIZE            [0, 0, 32, 0]
   * 04  58  PC               [0, 0, 32, 0, 4]
   * 05  60  PUSH1 0x1c       [0, 0, 32, 0, 4, 28]
   * 07  33  CALLER           [0, 0, 32, 0, 4, 28, caller]
   * 08  5a  GAS              [0, 0, 32, 0, 4, 28, caller, gas]
   * 09  58  PC               [0, 0, 32, 0, 4, 28, caller, gas, 9 -> selector]
   * 10  59  MSIZE            [0, 0, 32, 0, 4, 28, caller, gas, selector, 0]
   * 11  52  MSTORE           [0, 0, 32, 0, 4, 28, caller, gas] <selector>
   * 12  fa  STATICCALL       [0, 0, 1 => success] <init_in_runtime_address>
   * 13  15  ISZERO           [0, 0, 0]
   * 14  82  DUP3             [0, 0, 0, 0]
   * 15  83  DUP4             [0, 0, 0, 0, 0]
   * 16  83  DUP4             [0, 0, 0, 0, 0, 0]
   * 17  82  DUP3             [0, 0, 0, 0, 0, 0, 0]
   * 18  51  MLOAD            [0, 0, 0, 0, 0, 0, init_in_runtime_address]
   * 19  5a  GAS              [0, 0, 0, 0, 0, 0, init_in_runtime_address, gas]
   * 20  f4  DELEGATECALL     [0, 0, 1 => success] {runtime_code}
   * 21  3d  RETURNDATASIZE   [0, 0, 1 => success, size]
   * 22  3d  RETURNDATASIZE   [0, 0, 1 => success, size, size]
   * 23  93  SWAP4            [size, 0, 1 => success, size, 0]
   * 24  83  DUP4             [size, 0, 1 => success, size, 0, 0]
   * 25  3e  RETURNDATACOPY   [size, 0, 1 => success] <runtime_code>
   * 26  60  PUSH1 0x1e       [size, 0, 1 => success, 30]
   * 28  57  JUMPI            [size, 0]
   * 29  fd  REVERT           [] *runtime_code*
   * 30  5b  JUMPDEST         [size, 0]
   * 31  f3  RETURN           []
   */
  function _deployToHomeAddress(bytes32 key)
    internal
    returns (address homeAddress, bytes32 runtimeCodeHash)
  {    
    assembly {
      // Write the 32-byte metamorphic initialization code to scratch space.
      mstore(
        0,
        0x5859385958601c335a585952fa1582838382515af43d3d93833e601e57fd5bf3
      )

      // Call `CREATE2` using above init code with the supplied key as the salt.
      homeAddress := create2(callvalue, 0, 32, key)

      // Pass along revert message if the contract did not deploy successfully.
      if iszero(homeAddress) {
        returndatacopy(0, 0, returndatasize)
        revert(0, returndatasize)
      }

      // Get the runtime hash of the deployed contract.
      runtimeCodeHash := extcodehash(homeAddress)
    }

    // Clear the address of the runtime storage contract from storage.
    delete _initializationRuntimeStorageContract;

    // Emit an event with home address, key, and runtime hash of new contract.
    emit NewResident(homeAddress, key, runtimeCodeHash);
  }

  /**
   * @notice Internal function for deriving a key given a particular salt and
   * caller and for performing verifications of, and modifications to, the
   * information set on that key.
   * @param salt bytes32 The value used to derive the key.
   * @return The derived key.
   */
  function _deriveKeyAndPrepareToDeploy(bytes32 salt)
    internal
    returns (bytes32 key)
  {
    // Derive the key using the supplied salt and the calling address.
    key = _deriveKey(salt, msg.sender);

    // Ensure that a contract is not currently deployed to the home address.
    require(_isNotDeployed(key), _ACCOUNT_EXISTS);

    // Set appropriate controller and increment contract deploy count at once.
    HomeAddress storage home = _home[key];
    if (!home.exists) {
      home.exists = true;
      home.controller = msg.sender;
      home.deploys += 1;

      // Emit an event signifying that this contract is now the controller. 
      emit NewController(key, msg.sender);
    
    } else {
      home.deploys += 1;
    }

    // Ensure that the caller is the designated controller before proceeding.
    require(home.controller == msg.sender, _ONLY_CONTROLLER);
  }

  /**
   * @notice Internal function for verifying that an owner that cannot accept
   * ERC721 tokens has not been supplied.
   * @param owner address The specified owner.
   * @param key bytes32 The unique value used to derive the home address.
   */
  function _validateOwner(address owner, bytes32 key) internal {
    // Ensure that the specified owner is a valid ERC721 receiver.
    require(
      _checkOnERC721Received(address(0), owner, uint256(key), bytes("")),
      "Owner must be an EOA or a contract that implements `onERC721Received`."
    );
  }

  /**
   * @notice Internal "view" function for determining if a contract currently
   * exists at a given home address corresponding to a particular key.
   * @param key bytes32 The unique value used to derive the home address.
   * @return A boolean signifying whether the home address has a contract
   * deployed or not.
   */
  function _isNotDeployed(bytes32 key)
    internal
    /* view */
    returns (bool notDeployed)
  {
    // Derive the home address using the supplied key.
    address homeAddress = _getHomeAddress(key);

    // Check whether account at home address is non-existent using EXTCODEHASH.
    bytes32 hash;
    assembly { hash := extcodehash(homeAddress) }

    // Account does not exist, and contract is not deployed, if hash equals 0.
    if (hash == bytes32(0)) {
      return true;
    }

    // Contract is deployed (notDeployed = false) if codesize is greater than 0.
    uint256 size;
    assembly { size := extcodesize(homeAddress) }
    if (size > 0) {
      return false;
    }

    // Declare variable to move current runtime storage from storage to memory.
    address currentStorage;

    // Set runtime storage contract to null address temporarily if necessary.
    if (_initializationRuntimeStorageContract != address(0)) {
      // Place the current runtime storage contract address in memory.
      currentStorage = _initializationRuntimeStorageContract;
      
      // Remove the existing runtime storage contract address from storage.
      delete _initializationRuntimeStorageContract;
    }

    // Set gas to use when performing dry-run deployment (future-proof a bit).
    uint256 checkGas = 27000 + (block.gaslimit / 1000);
    
    // As a last resort, deploy a contract to the address and revert on success.
    (bool contractExists, bytes memory code) = address(this).call.gas(checkGas)(
      abi.encodeWithSelector(this.staticCreate2Check.selector, key)
    );

    // Place runtime storage contract back in storage if necessary.
    if (currentStorage != address(0)) {
      _initializationRuntimeStorageContract = currentStorage;
    }

    // Check revert string to ensure failure is due to successful deployment.
    bytes32 revertMessage;
    assembly { revertMessage := mload(add(code, 32)) }

    // Contract is not deployed if `staticCreate2Check` reverted with message.
    notDeployed = !contractExists && revertMessage == _HOME_INIT_CODE;
  }

  /**
   * @notice Internal view function for verifying that a restricted controller
   * has not been supplied.
   * @param controller address The specified controller.
   * @param key bytes32 The unique value used to derive the home address.
   */
  function _validateController(address controller, bytes32 key) internal view {
    // Prevent the controller from being set to prohibited account values.
    require(
      controller != address(0),
      "The null address may not be set as the controller using this function."
    );
    require(
      controller != address(this),
      "This contract may not be set as the controller using this function."
    );
    require(
      controller != _getHomeAddress(key),
      "Home addresses cannot be set as the controller of themselves."
    );
  }

  /**
   * @notice Internal view function for verifying that a supplied runtime
   * storage contract is not empty.
   * @param target address The runtime storage contract.
   */
  function _validateRuntimeStorageIsNotEmpty(address target) internal view {
    // Ensure that the runtime storage contract is not empty.
    require(
      target.isContract(),
      "No runtime code found at the supplied runtime storage address."
    );
  }

  /**
   * @notice Internal view function for retrieving the controller of a home
   * address corresponding to a particular key.
   * @param key bytes32 The unique value used to derive the home address.
   * @return The controller of the home address corresponding to the supplied
   * key.
   */
  function _getController(bytes32 key)
    internal
    view
    returns (address controller)
  {
    // Get controller from mapping, defaulting to first 20 bytes of the key.
    HomeAddress memory home = _home[key];
    if (!home.exists) {
      controller = address(bytes20(key));
    } else {
      controller = home.controller;
    }
  }

  /**
   * @notice Internal view function for getting the runtime code at the tokenURI
   * data storage address.
   * @return The runtime code at the tokenURI storage address.
   */
  function _getTokenURIStorageRuntime()
    internal
    view
    returns (bytes memory runtime)
  {
    // Bring the tokenURI storage address into memory for use in assembly block.
    address target = _URI_END_SEGMENT_STORAGE;
    
    assembly {
      // Retrieve the size of the external code.
      let size := extcodesize(target)
      
      // Allocate output byte array.
      runtime := mload(0x40)
      
      // Set new "memory end" including padding.
      mstore(0x40, add(runtime, and(add(size, 0x3f), not(0x1f))))
      
      // Store length in memory.
      mstore(runtime, size)
      
      // Get the code using extcodecopy.
      extcodecopy(target, add(runtime, 0x20), 0, size)
    }
  }