// Contract: HomeWorkDeployer
// Target function: function deploy(bytes32 key, bytes calldata initializationCode)
 ID to another address.
     * As opposed to transferFrom, this imposes no restrictions on msg.sender.
     * @param from current owner of the token
     * @param to address to receive the ownership of the given token ID
     * @param tokenId uint256 ID of the token to be transferred
     */
    function _transferFrom(address from, address to, uint256 tokenId) internal {
        super._transferFrom(from, to, tokenId);

        _removeTokenFromOwnerEnumeration(from, tokenId);

        _addTokenToOwnerEnumeration(to, tokenId);
    }

    /**
     * @dev Internal function to mint a new token.
     * Reverts if the given token ID already exists.
     * @param to address the beneficiary that will own the minted token
     * @param tokenId uint256 ID of the token to be minted
     */
    function _mint(address to, uint256 tokenId) internal {
        super._mint(to, tokenId);

        _addTokenToOwnerEnumeration(to, tokenId);

        _addTokenToAllTokensEnumeration(tokenId);
    }

    /**
     * @dev Internal function to burn a specific token.
     * Reverts if the token does not exist.
     * Deprecated, use _burn(uint256) instead.
     * @param owner owner of the token to burn
     * @param tokenId uint256 ID of the token being burned
     */
    function _burn(address owner, uint256 tokenId) internal {
        super._burn(owner, tokenId);

        _removeTokenFromOwnerEnumeration(owner, tokenId);
        // Since tokenId will be deleted, we can clear its slot in _ownedTokensIndex to trigger a gas refund
        _ownedTokensIndex[tokenId] = 0;

        _removeTokenFromAllTokensEnumeration(tokenId);
    }

    /**
     * @dev Private function to add a token to this extension's ownership-tracking data structures.
     * @param to address representing the new owner of the given token ID
     * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
     */
    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
        _ownedTokensIndex[tokenId] = _ownedTokens[to].length;
        _ownedTokens[to].push(tokenId);
    }

    /**
     * @dev Private function to add a token to this extension's token tracking data structures.
     * @param tokenId uint256 ID of the token to be added to the tokens list
     */
    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
        _allTokensIndex[tokenId] = _allTokens.length;
        _allTokens.push(tokenId);
    }

    /**
     * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
     * while the token is not assigned a new owner, the _ownedTokensIndex mapping is _not_ updated: this allows for
     * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
     * This has O(1) time complexity, but alters the order of the _ownedTokens array.
     * @param from address representing the previous owner of the given token ID
     * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
     */
    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
        // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = _ownedTokens[from].length.sub(1);
        uint256 tokenIndex = _ownedTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary
        if (tokenIndex != lastTokenIndex) {
            uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];

            _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
            _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
        }

        // This also deletes the contents at the last position of the array
        _ownedTokens[from].length--;

        // Note that _ownedTokensIndex[tokenId] hasn't been cleared: it still points to the old slot (now occupied by
        // lastTokenId, or just over the end of the array if the token was the last one).
    }

    /**
     * @dev Private function to remove a token from this extension's token tracking data structures.
     * This has O(1) time complexity, but alters the order of the _allTokens array.
     * @param tokenId uint256 ID of the token to be removed from the tokens list
     */
    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
        // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = _allTokens.length.sub(1);
        uint256 tokenIndex = _allTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
        // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
        // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
        uint256 lastTokenId = _allTokens[lastTokenIndex];

        _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
        _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index

        // This also deletes the contents at the last position of the array
        _allTokens.length--;
        _allTokensIndex[tokenId] = 0;
    }
}


/**
 * @title HomeWork (version 1)
 * @author 0age
 * @notice Homework is a utility to find, share, and reuse "home" addresses for
 * contracts. Anyone can work to find a new home address by searching for keys,
 * a 32-byte value with the first 20 bytes equal to the finder's calling address
 * (or derived by hashing an arbitrary 32-byte salt and the caller's address),
 * and can then deploy any contract they like (even one with a constructor) to
 * the address, or mint an ERC721 token that the owner can redeem that will then
 * allow them to do the same. Also, if the contract is `SELFDESTRUCT`ed, a new
 * contract can be redeployed by the current controller to the same address!
 * @dev This contract allows contract addresses to be located ahead of time, and
 * for arbitrary bytecode to be deployed (and redeployed if so desired, i.e.
 * metamorphic contracts) to the located address by a designated controller. To
 * enable this, the contract first deploys an "initialization-code-in-runtime"
 * contract, with the creation code of the contract you want to deploy stored in
 * RUNTIME code. Then, to deploy the actual contract, it retrieves the address
 * of the storage contract and `DELEGATECALL`s into it to execute the init code
 * and, if successful, retrieves and returns the contract runtime code. Rather
 * than using a located address directly, you can also lock it in the contract
 * and mint and ERC721 token for it, which can then be redeemed in order to gain
 * control over deployment to the address (note that tokens may not be minted if
 * the contract they control currently has a deployed contract at that address).
 * Once a contract undergoes metamorphosis, all existing storage will be deleted
 * and any existing contract code will be replaced with the deployed contract
 * code of the new implementation contract. The mechanisms behind this contract 
 * are highly experimental - proceed with caution and please share any exploits
 * or optimizations you discover.
 */
contract HomeWork is IHomeWork, ERC721Enumerable, IERC721Metadata, IERC1412 {
  // Allocate storage to track the current initialization-in-runtime contract.
  address private _initializationRuntimeStorageContract;

  // Finder of home address with lowest uint256 value can recover lost funds.
  bytes32 private _highScoreKey;

  // Track information on the Home address corresponding to each key.
  mapping (bytes32 => HomeAddress) private _home;

  // Provide optional reverse-lookup for key derivation of a given home address.
  mapping (address => KeyInformation) private _key;

  // Set 0xff + address(this) as a constant to use when deriving home addresses.
  bytes21 private constant _FF_AND_THIS_CONTRACT = bytes21(
    0xff0000000000001b84b1cb32787B0D64758d019317
  );

  // Set the address of the tokenURI runtime storage contract as a constant.
  address private constant _URI_END_SEGMENT_STORAGE = address(
    0x000000000071C1c84915c17BF21728BfE4Dac3f3
  );

  // Deploy arbitrary contracts to home addresses using metamorphic init code.
  bytes32 private constant _HOME_INIT_CODE = bytes32(
    0x5859385958601c335a585952fa1582838382515af43d3d93833e601e57fd5bf3
  );

  // Compute hash of above metamorphic init code in order to compute addresses.
  bytes32 private constant _HOME_INIT_CODE_HASH = bytes32(
    0x7816562e7f85866cae07183593075f3b5ec32aeff914a0693e20aaf39672babc
  );

  // Write arbitrary code to a contract's runtime using the following prelude.
  bytes11 private constant _ARBITRARY_RUNTIME_PRELUDE = bytes11(
    0x600b5981380380925939f3
  );

  // Set EIP165 interface IDs as constants (already set 165 and 721+enumerable).
  bytes4 private constant _INTERFACE_ID_HOMEWORK = 0xe5399799;
  /* this.deploy.selector ^ this.lock.selector ^ this.redeem.selector ^
     this.assignController.selector ^ this.relinquishControl.selector ^
     this.redeemAndDeploy.selector ^ this.deriveKey.selector ^
     this.deriveKeyAndLock.selector ^
     this.deriveKeyAndAssignController.selector ^
     this.deriveKeyAndRelinquishControl.selector ^
     this.setReverseLookup.selector ^ this.setDerivedReverseLookup.selector ^
     this.deployRuntimeStorageContract.selector ^
     this.deployViaExistingRuntimeStorageContract.selector ^
     this.redeemAndDeployViaExistingRuntimeStorageContract.selector ^
     this.deriveKeyAndDeploy.selector ^
     this.deriveKeyAndDeployViaExistingRuntimeStorageContract.selector ^
     this.batchLock.selector ^ this.deriveKeysAndBatchLock.selector ^
     this.batchLock_63efZf.selector ^ this.claimHighScore.selector ^
     this.recover.selector ^ this.isDeployable.selector ^
     this.getHighScore.selector ^ this.getHomeAddressInformation.selector ^
     this.hasNeverBeenDeployed.selector ^ this.reverseLookup.selector ^
     this.getDerivedKey.selector ^ this.getHomeAddress.selector ^
     this.getMetamorphicDelegatorInitializationCode.selector ^
     this.getMetamorphicDelegatorInitializationCodeHash.selector ^
     this.getArbitraryRuntimeCodePrelude.selector == 0xe5399799
  */

  bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;

  bytes4 private constant _INTERFACE_ID_ERC1412_BATCH_TRANSFERS = 0x2b89bcaa;

  // Set name of this contract as a constant (hex encoding is to support emoji).
  string private constant _NAME = (
    hex"486f6d65576f726b20f09f8fa0f09f9ba0efb88f"
  );

  // Set symbol of this contract as a constant.
  string private constant _SYMBOL = "HWK";

  // Set the start of each token URI for issued ERC721 tokens as a constant.
  bytes private constant _URI_START_SEGMENT = abi.encodePacked(
    hex"646174613a6170706c69636174696f6e2f6a736f6e2c7b226e616d65223a22486f6d65",
    hex"253230416464726573732532302d2532303078"
  ); /* data:application/json,{"name":"Home%20Address%20-%200x */

  // Store reused revert messages as constants.
  string private constant _ACCOUNT_EXISTS = string(
    "Only non-existent accounts can be deployed or used to mint tokens."
  );

  string private constant _ONLY_CONTROLLER = string(
    "Only the designated controller can call this function."
  );

  string private constant _NO_INIT_CODE_SUPPLIED = string(
    "Cannot deploy a contract with no initialization code supplied."
  );

  /**
   * @notice In the constructor, verify that deployment addresses are correct
   * and that supplied constant hash value of the contract creation code used to
   * deploy arbitrary contracts to home addresses is valid, and set an initial
   * high score key with the null address as the high score "holder". ERC165
   * supported interfaces are all registered during initizialization as well.
   */
  constructor() public {
    // Verify that the deployment address is set correctly as a constant.
    assert(address(this) == address(uint160(uint168(_FF_AND_THIS_CONTRACT))));

    // Verify the derivation of the deployment address.
    bytes32 initialDeployKey = bytes32(
      0x486f6d65576f726b20f09f8fa0f09f9ba0efb88faa3c548a76f9bd3c000c0000
    );    
    assert(address(this) == address(
      uint160(                      // Downcast to match the address type.
        uint256(                    // Convert to uint to truncate upper digits.
          keccak256(                // Compute the CREATE2 hash using 4 inputs.
            abi.encodePacked(       // Pack all inputs to the hash together.
              bytes1(0xff),         // Start with 0xff to distinguish from RLP.
              msg.sender,           // The deployer will be the caller.
              initialDeployKey,     // Pass in the supplied key as the salt.
              _HOME_INIT_CODE_HASH  // The metamorphic initialization code hash.
            )
          )
        )
      )
    ));

    // Verify the derivation of the tokenURI runtime storage address.
    bytes32 uriDeployKey = bytes32(
      0x486f6d65576f726b202d20746f6b656e55524920c21352fee5a62228db000000
    );
    bytes32 uriInitCodeHash = bytes32(
      0xdea98294867e3fdc48eb5975ecc53a79e2e1ea6e7e794137a9c34c4dd1565ba2
    );
    assert(_URI_END_SEGMENT_STORAGE == address(
      uint160(                      // Downcast to match the address type.
        uint256(                    // Convert to uint to truncate upper digits.
          keccak256(                // Compute the CREATE2 hash using 4 inputs.
            abi.encodePacked(       // Pack all inputs to the hash together.
              bytes1(0xff),         // Start with 0xff to distinguish from RLP.
              msg.sender,           // The deployer will be the caller.
              uriDeployKey,         // Pass in the supplied key as the salt.
              uriInitCodeHash       // The storage contract init code hash.
            )
          )
        )
      )
    ));

    // Verify that the correct runtime code is at the tokenURI storage contract.
    bytes32 expectedRuntimeStorageHash = bytes32(
      0x8834602968080bb1df9c44c9834c0a93533b72bbfa3865ee2c5be6a0c4125fc3
    );
    address runtimeStorage = _URI_END_SEGMENT_STORAGE;
    bytes32 runtimeStorageHash;
    assembly { runtimeStorageHash := extcodehash(runtimeStorage) }
    assert(runtimeStorageHash == expectedRuntimeStorageHash);

    // Verify that the supplied hash for the metamorphic init code is valid.
    assert(keccak256(abi.encode(_HOME_INIT_CODE)) == _HOME_INIT_CODE_HASH);

    // Set an initial high score key with the null address as the submitter.
    _highScoreKey = bytes32(
      0x0000000000000000000000000000000000000000ffffffffffffffffffffffff
    );

    // Register EIP165 interface for HomeWork.
    _registerInterface(_INTERFACE_ID_HOMEWORK);

    // Register EIP165 interface for ERC721 metadata.
    _registerInterface(_INTERFACE_ID_ERC721_METADATA);

    // Register EIP165 interface for ERC1412 (batch transfers).
    _registerInterface(_INTERFACE_ID_ERC1412_BATCH_TRANSFERS);
  }