Skip to content

Instantly share code, notes, and snippets.

@0age
Created June 9, 2019 14:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 0age/06bfe1fb268984a21899780a39f30971 to your computer and use it in GitHub Desktop.
Save 0age/06bfe1fb268984a21899780a39f30971 to your computer and use it in GitHub Desktop.
HomeWork V1 source (flattened)
pragma solidity 0.5.9; // optimization enabled, runs: 10000, evm: constantinople
/**
* @title HomeWork Interface (version 1) - EIP165 ID 0xe5399799
* @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.
*/
interface IHomeWork {
// Fires when a contract is deployed or redeployed to a given home address.
event NewResident(
address indexed homeAddress,
bytes32 key,
bytes32 runtimeCodeHash
);
// Fires when a new runtime storage contract is deployed.
event NewRuntimeStorageContract(
address runtimeStorageContract,
bytes32 runtimeCodeHash
);
// Fires when a controller is changed from the default controller.
event NewController(bytes32 indexed key, address newController);
// Fires when a new high score is submitted.
event NewHighScore(bytes32 key, address submitter, uint256 score);
// Track total contract deploys and current controller for each home address.
struct HomeAddress {
bool exists;
address controller;
uint88 deploys;
}
// Track derivation of key for a given home address based on salt & submitter.
struct KeyInformation {
bytes32 key;
bytes32 salt;
address submitter;
}
/**
* @notice Deploy a new contract with the desired initialization code to the
* home address corresponding to a given key. Two conditions must be met: the
* submitter must be designated as the controller of the home address (with
* the initial controller set to the address corresponding to the first twenty
* bytes of the key), and there must not be a contract currently deployed at
* the home address. These conditions can be checked by calling
* `getHomeAddressInformation` and `isDeployable` with the same key.
* @param key bytes32 The unique value used to derive the home address.
* @param initializationCode bytes The contract creation code that will be
* used to deploy the contract to the home address.
* @return The home address of the deployed contract.
* @dev In order to deploy the contract to the home address, a new contract
* will be deployed with runtime code set to the initialization code of the
* contract that will be deployed to the home address. Then, metamorphic
* initialization code will retrieve that initialization code 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 also want to provide the
* key to `setReverseLookup` in order to find it again using only the home
* address to prevent accidentally losing the key.
*/
function deploy(bytes32 key, bytes calldata initializationCode)
external
payable
returns (address homeAddress, bytes32 runtimeCodeHash);
/**
* @notice Mint an ERC721 token to the supplied owner that can be redeemed in
* order to gain control of a home address corresponding to a given key. Two
* conditions must be met: the submitter must be designated as the controller
* of the home address (with the initial controller set to the address
* corresponding to the first 20 bytes of the key), and there must not be a
* contract currently deployed at the home address. These conditions can be
* checked by calling `getHomeAddressInformation` and `isDeployable` with the
* same key.
* @param key bytes32 The unique value used to derive the home address.
* @param owner address The account that will be granted ownership of the
* ERC721 token.
* @dev In order to mint an ERC721 token, the assocated home address cannot be
* in use, or else the token will not be able to deploy to the home address.
* The controller is set to this contract until the token is redeemed, at
* which point the redeemer designates a new controller for the home address.
* The key of the home address and the tokenID of the ERC721 token are the
* same value, but different types (bytes32 vs. uint256).
*/
function lock(bytes32 key, address owner) external;
/**
* @notice Burn an ERC721 token to allow the supplied controller to gain the
* ability to deploy to the home address corresponding to the key matching the
* burned token. The submitter must be designated as either the owner of the
* token or as an approved spender.
* @param tokenId uint256 The ID of the ERC721 token to redeem.
* @param controller address The account that will be granted control of the
* home address corresponding to the given token.
* @dev The controller cannot be designated as the address of this contract,
* the null address, or the home address (the restriction on setting the home
* address as the controller is due to the fact that the home address will not
* be able to deploy to itself, as it needs to be empty before a contract can
* be deployed to it).
*/
function redeem(uint256 tokenId, address controller) external;
/**
* @notice Transfer control over deployment to the home address corresponding
* to a given key. The caller must be designated as the current controller of
* the home address (with the initial controller set to the address
* corresponding to the first 20 bytes of the key) - This condition can be
* checked by calling `getHomeAddressInformation` with the same key.
* @param key bytes32 The unique value used to derive the home address.
* @param controller address The account that will be granted control of the
* home address corresponding to the given key.
* @dev The controller cannot be designated as the address of this contract,
* the null address, or the home address (the restriction on setting the home
* address as the controller is due to the fact that the home address will not
* be able to deploy to itself, as it needs to be empty before a contract can
* be deployed to it).
*/
function assignController(bytes32 key, address controller) external;
/**
* @notice Transfer control over deployment to the home address corresponding
* to a given key to the null address, which will prevent it from being
* deployed to again in the future. The caller must be designated as the
* current controller of the corresponding home address (with the initial
* controller set to the address corresponding to the first 20 bytes of the
* key) - This condition can be checked by calling `getHomeAddressInformation`
* with the same key.
* @param key bytes32 The unique value used to derive the home address.
*/
function relinquishControl(bytes32 key) external;
/**
* @notice Burn an ERC721 token, set a supplied controller, and deploy a new
* contract with the supplied initialization code to the corresponding home
* address for the given token. The submitter must be designated as either the
* owner of the token or as an approved spender.
* @param tokenId uint256 The ID of the ERC721 token to redeem.
* @param controller address The account that will be granted control of the
* home address corresponding to the given token.
* @param initializationCode bytes The contract creation code that will be
* used to deploy the contract to the home address.
* @return The home address and runtime code hash of the deployed contract.
* @dev In order to deploy the contract to the home address, a new contract
* will be deployed with runtime code set to the initialization code of the
* contract that will be deployed to the home address. Then, metamorphic
* initialization code will retrieve that initialization code 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 also want to provide the
* key to `setReverseLookup` in order to find it again using only the home
* address to prevent accidentally losing the key. The controller cannot be
* designated as the address of this contract, the null address, or the home
* address (the restriction on setting the home address as the controller is
* due to the fact that the home address will not be able to deploy to itself,
* as it needs to be empty before a contract can be deployed to it). Also,
* checks on the contract at the home address being empty or not having the
* correct controller are unnecessary, as they are performed when minting the
* token and cannot be altered until the token is redeemed.
*/
function redeemAndDeploy(
uint256 tokenId,
address controller,
bytes calldata initializationCode
)
external
payable
returns (address homeAddress, bytes32 runtimeCodeHash);
/**
* @notice Derive a new key by concatenating an arbitrary 32-byte salt value
* and the address of the caller and performing a keccak256 hash. This allows
* for the creation of keys with additional entropy where desired while also
* preventing collisions with standard keys. The caller will be set as the
* controller of the derived key.
* @param salt bytes32 The desired salt value to use (along with the address
* of the caller) when deriving the resultant key and corresponding home
* address.
* @return The derived key.
* @dev Home addresses from derived keys will take longer to "mine" or locate,
* as an additional hash must be performed when computing the corresponding
* home address for each given salt input. Each caller will derive a different
* key even if they are supplying the same salt value.
*/
function deriveKey(bytes32 salt) external returns (bytes32 key);
/**
* @notice Mint an ERC721 token to the supplied owner that can be redeemed in
* order to gain control of a home address corresponding to a given derived
* key. Two conditions must be met: the submitter must be designated as the
* current controller of the home address, and there must not be a contract
* currently deployed at the home address. These conditions can be checked by
* calling `getHomeAddressInformation` and `isDeployable` with the key
* determined by calling `getDerivedKey`.
* @param salt bytes32 The salt value that is used to derive the key.
* @param owner address The account that will be granted ownership of the
* ERC721 token.
* @return The derived key.
* @dev In order to mint an ERC721 token, the assocated home address cannot be
* in use, or else the token will not be able to deploy to the home address.
* The controller is set to this contract until the token is redeemed, at
* which point the redeemer designates a new controller for the home address.
* The key of the home address and the tokenID of the ERC721 token are the
* same value, but different types (bytes32 vs. uint256).
*/
function deriveKeyAndLock(bytes32 salt, address owner)
external
returns (bytes32 key);
/**
* @notice Transfer control over deployment to the home address corresponding
* to a given derived key. The caller must be designated as the current
* controller of the home address - This condition can be checked by calling
* `getHomeAddressInformation` with the key obtained via `getDerivedKey`.
* @param salt bytes32 The salt value that is used to derive the key.
* @param controller address The account that will be granted control of the
* home address corresponding to the given derived key.
* @return The derived key.
* @dev The controller cannot be designated as the address of this contract,
* the null address, or the home address (the restriction on setting the home
* address as the controller is due to the fact that the home address will not
* be able to deploy to itself, as it needs to be empty before a contract can
* be deployed to it).
*/
function deriveKeyAndAssignController(bytes32 salt, address controller)
external
returns (bytes32 key);
/**
* @notice Transfer control over deployment to the home address corresponding
* to a given derived key to the null address, which will prevent it from
* being deployed to again in the future. The caller must be designated as the
* current controller of the home address - This condition can be checked by
* calling `getHomeAddressInformation` with the key determined by calling
* `getDerivedKey`.
* @param salt bytes32 The salt value that is used to derive the key.
* @return The derived key.
*/
function deriveKeyAndRelinquishControl(bytes32 salt)
external
returns (bytes32 key);
/**
* @notice Record a key that corresponds to a given home address by supplying
* said key and using it to derive the address. This enables reverse lookup
* of a key using only the home address in question. This method may be called
* by anyone - control of the key is not required.
* @param key bytes32 The unique value used to derive the home address.
* @dev This does not set the salt or submitter fields, as those apply only to
* derived keys (although a derived key may also be set with this method, just
* without the derived fields).
*/
function setReverseLookup(bytes32 key) external;
/**
* @notice Record the derived key that corresponds to a given home address by
* supplying the salt and submitter that were used to derive the key. This
* facititates reverse lookup of the derivation method of a key using only the
* home address in question. This method may be called by anyone - control of
* the derived key is not required.
* @param salt bytes32 The salt value that is used to derive the key.
* @param submitter address The account that submits the salt that is used to
* derive the key.
*/
function setDerivedReverseLookup(bytes32 salt, address submitter) external;
/**
* @notice Deploy a new storage contract with the supplied code as runtime
* code without deploying a contract to a home address. This can be used to
* store the contract creation code for use in future deployments of contracts
* to home addresses.
* @param codePayload bytes The code to set as the runtime code of the
* deployed contract.
* @return The address of the deployed storage contract.
* @dev Consider placing adequate protections on the storage contract to
* prevent unwanted callers from modifying or destroying it. Also, if you are
* placing contract contract creation code into the runtime storage contract,
* remember to include any constructor parameters as ABI-encoded arguments at
* the end of the contract creation code, similar to how you would perform a
* standard deployment.
*/
function deployRuntimeStorageContract(bytes calldata codePayload)
external
returns (address runtimeStorageContract);
/**
* @notice Deploy a new contract with the initialization code stored in the
* runtime code at the specified initialization runtime storage contract to
* the home address corresponding to a given key. Two conditions must be met:
* the submitter must be designated as the controller of the home address
* (with the initial controller set to the address corresponding to the first
* 20 bytes of the key), and there must not be a contract currently deployed
* at the home address. These conditions can be checked by calling
* `getHomeAddressInformation` and `isDeployable` with the same key.
* @param key bytes32 The unique value used to derive the home address.
* @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 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 also want to provide the key to
* `setReverseLookup` in order to find it again using only the home address to
* prevent accidentally losing the key.
*/
function deployViaExistingRuntimeStorageContract(
bytes32 key,
address initializationRuntimeStorageContract
)
external
payable
returns (address homeAddress, bytes32 runtimeCodeHash);
/**
* @notice Burn an ERC721 token, set a supplied controller, and deploy a new
* contract with the initialization code stored in the runtime code at the
* specified initialization runtime storage contract to the home address
* corresponding to a given key. The submitter must be designated as either
* the owner of the token or as an approved spender.
* @param tokenId uint256 The ID of the ERC721 token to redeem.
* @param controller address The account that will be granted control of the
* home address corresponding to the given token.
* @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 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 also want to provide the key to
* `setReverseLookup` in order to find it again using only the home address to
* prevent accidentally losing the key. The controller cannot be designated as
* the address of this contract, the null address, or the home address (the
* restriction on setting the home address as the controller is due to the
* fact that the home address will not be able to deploy to itself, as it
* needs to be empty before a contract can be deployed to it). Also, checks on
* the contract at the home address being empty or not having the correct
* controller are unnecessary, as they are performed when minting the token
* and cannot be altered until the token is redeemed.
*/
function redeemAndDeployViaExistingRuntimeStorageContract(
uint256 tokenId,
address controller,
address initializationRuntimeStorageContract
)
external
payable
returns (address homeAddress, bytes32 runtimeCodeHash);
/**
* @notice Deploy a new contract with the desired initialization code to the
* home address corresponding to a given derived key. Two conditions must be
* met: the submitter must be designated as the controller of the home
* address, and there must not be a contract currently deployed at the home
* address. These conditions 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 initializationCode bytes 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 In order to deploy the contract to the home address, a new contract
* will be deployed with runtime code set to the initialization code of the
* contract that will be deployed to the home address. Then, metamorphic
* initialization code will retrieve that initialization code 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 deriveKeyAndDeploy(bytes32 salt, bytes calldata initializationCode)
external
payable
returns (address homeAddress, bytes32 key, bytes32 runtimeCodeHash);
/**
* @notice Deploy a new contract with the initialization code stored in the
* runtime code at the specified initialization runtime storage contract to
* the home address corresponding to a given derived key. Two conditions must
* be met: the submitter must be designated as the controller of the home
* address, and there must not be a contract currently deployed at the home
* address. These conditions 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);
/**
* @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;
/**
* @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;
/**
* @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;
/**
* @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;
/**
* @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;
/**
* @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);
/**
* @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);
/**
* @notice View function to get information on a home address given the
* corresponding key.
* @param key bytes32 The unique value used to derive the home address.
* @return The home address, the current controller of the address, the number
* of times the home address has been deployed to, and the code hash of the
* runtime currently found at the home address, if any.
* @dev There is also an `isDeployable` method for determining if a contract
* can be deployed to the address, but in extreme cases it must actually
* perform a dry-run to determine if the contract is deployable, which means
* that it does not support staticcalls. There is also a convenience method,
* `hasNeverBeenDeployed`, but the information it conveys can be determined
* from this method alone as well.
*/
function getHomeAddressInformation(bytes32 key)
external
view
returns (
address homeAddress,
address controller,
uint256 deploys,
bytes32 currentRuntimeCodeHash
);
/**
* @notice View function to determine if no contract has ever been deployed to
* a home address given the corresponding key. This can be used to ensure that
* a given key or corresponding token is "new" or not.
* @param key bytes32 The unique value used to derive the home address.
* @return A boolean signifying if a contract has never been deployed using
* the supplied key before.
*/
function hasNeverBeenDeployed(bytes32 key)
external
view
returns (bool neverBeenDeployed);
/**
* @notice View function to search for a known key, salt, and/or submitter
* given a supplied home address. Keys can be controlled directly by an
* address that matches the first 20 bytes of the key, or they can be derived
* from a salt and a submitter - if the key is not a derived key, the salt and
* submitter fields will both have a value of zero.
* @param homeAddress address The home address to check for key information.
* @return The key, salt, and/or submitter used to deploy to the home address,
* assuming they have been submitted to the reverse lookup.
* @dev To populate these values, call `setReverseLookup` for cases where keys
* are used directly or are the only value known, or `setDerivedReverseLookup`
* for cases where keys are derived from a known salt and submitter.
*/
function reverseLookup(address homeAddress)
external
view
returns (bytes32 key, bytes32 salt, address submitter);
/**
* @notice Pure function to determine the key that is derived from a given
* salt and submitting address.
* @param salt bytes32 The salt value that is used to derive the key.
* @param submitter address The submitter of the salt value used to derive the
* key.
* @return The derived key.
*/
function getDerivedKey(bytes32 salt, address submitter)
external
pure
returns (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);
/**
* @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);
/**
* @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);
/**
* @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);
}
/**
* @title ERC721 Non-Fungible Token Standard basic interface
* @dev see https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function setApprovalForAll(address operator, bool _approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
function transferFrom(address from, address to, uint256 tokenId) external;
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
}
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Enumerable {
function totalSupply() external view returns (uint256);
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);
function tokenByIndex(uint256 index) external view returns (uint256);
}
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Metadata {
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function tokenURI(uint256 tokenId) external view returns (string memory);
}
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @notice Handle the receipt of an NFT
* @dev The ERC721 smart contract calls this function on the recipient
* after a `safeTransfer`. This function MUST return the function selector,
* otherwise the caller will revert the transaction. The selector to be
* returned can be obtained as `this.onERC721Received.selector`. This
* function MAY throw to revert and reject the transfer.
* Note: the ERC721 contract address is always the message sender.
* @param operator The address which called `safeTransferFrom` function
* @param from The address which previously owned the token
* @param tokenId The NFT identifier which is being transferred
* @param data Additional data with no specified format
* @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
*/
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data)
external
returns (bytes4);
}
/**
* @title ERC1412 Batch Transfers For Non-Fungible Tokens
* @dev the ERC-165 identifier for this interface is 0x2b89bcaa
*/
interface IERC1412 {
/// @notice Transfers the ownership of multiple NFTs from one address to another address
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenIds The NFTs to transfer
/// @param _data Additional data with no specified format, sent in call to `_to`
function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _tokenIds, bytes calldata _data) external;
/// @notice Transfers the ownership of multiple NFTs from one address to another address
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenIds The NFTs to transfer
function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _tokenIds) external;
}
/**
* @title IERC165
* @dev https://eips.ethereum.org/EIPS/eip-165
*/
interface IERC165 {
/**
* @notice Query if a contract implements an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @dev Interface identification is specified in ERC-165. This function
* uses less than 30,000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
/**
* @title ERC20 interface
* @dev see https://eips.ethereum.org/EIPS/eip-20
*/
interface IERC20 {
function transfer(address to, uint256 value) external returns (bool);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
function totalSupply() external view returns (uint256);
function balanceOf(address who) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
/**
* Utility library of inline functions on addresses
*/
library Address {
/**
* Returns whether the target address is a contract
* @dev This function will return false if invoked during the constructor of a contract,
* as the code is not actually created until after the constructor finishes.
* @param account address of the account to check
* @return whether the target address is a contract
*/
function isContract(address account) internal view returns (bool) {
uint256 size;
// XXX Currently there is no better way to check if there is a contract in an address
// than to check the size of the code at that address.
// See https://ethereum.stackexchange.com/a/14016/36603
// for more details about how this works.
// TODO Check this again before the Serenity release, because all addresses will be
// contracts then.
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
}
/**
* @title Counters
* @author Matt Condon (@shrugs)
* @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
* of elements in a mapping, issuing ERC721 ids, or counting request ids
*
* Include with `using Counters for Counters.Counter;`
* Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the SafeMath
* overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
* directly accessed.
*/
library Counters {
using SafeMath for uint256;
struct Counter {
// This variable should never be directly accessed by users of the library: interactions must be restricted to
// the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
// this feature: see https://github.com/ethereum/solidity/issues/4637
uint256 _value; // default: 0
}
function current(Counter storage counter) internal view returns (uint256) {
return counter._value;
}
function increment(Counter storage counter) internal {
counter._value += 1;
}
function decrement(Counter storage counter) internal {
counter._value = counter._value.sub(1);
}
}
/**
* @dev Implementation of the `IERC165` interface.
*
* Contracts may inherit from this and call `_registerInterface` to declare
* their support of an interface.
*/
contract ERC165 is IERC165 {
/*
* bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
*/
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* @dev Mapping of interface ids to whether or not it's supported.
*/
mapping(bytes4 => bool) private _supportedInterfaces;
constructor () internal {
// Derived contracts need only register support for their own interfaces,
// we register support for ERC165 itself here
_registerInterface(_INTERFACE_ID_ERC165);
}
/**
* @dev See `IERC165.supportsInterface`.
*
* Time complexity O(1), guaranteed to always use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev Registers the contract as an implementer of the interface defined by
* `interfaceId`. Support of the actual ERC165 interface is automatic and
* registering its interface id is not required.
*
* See `IERC165.supportsInterface`.
*
* Requirements:
*
* - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
*/
function _registerInterface(bytes4 interfaceId) internal {
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
_supportedInterfaces[interfaceId] = true;
}
}
/**
* @title ERC721 Non-Fungible Token Standard basic implementation
* @dev see https://eips.ethereum.org/EIPS/eip-721
*/
contract ERC721 is ERC165, IERC721 {
using SafeMath for uint256;
using Address for address;
using Counters for Counters.Counter;
// Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
// which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
// Mapping from token ID to owner
mapping (uint256 => address) private _tokenOwner;
// Mapping from token ID to approved address
mapping (uint256 => address) private _tokenApprovals;
// Mapping from owner to number of owned token
mapping (address => Counters.Counter) private _ownedTokensCount;
// Mapping from owner to operator approvals
mapping (address => mapping (address => bool)) private _operatorApprovals;
bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
/*
* 0x80ac58cd ===
* bytes4(keccak256('balanceOf(address)')) ^
* bytes4(keccak256('ownerOf(uint256)')) ^
* bytes4(keccak256('approve(address,uint256)')) ^
* bytes4(keccak256('getApproved(uint256)')) ^
* bytes4(keccak256('setApprovalForAll(address,bool)')) ^
* bytes4(keccak256('isApprovedForAll(address,address)')) ^
* bytes4(keccak256('transferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
*/
constructor () public {
// register the supported interfaces to conform to ERC721 via ERC165
_registerInterface(_INTERFACE_ID_ERC721);
}
/**
* @dev Gets the balance of the specified address
* @param owner address to query the balance of
* @return uint256 representing the amount owned by the passed address
*/
function balanceOf(address owner) public view returns (uint256) {
require(owner != address(0));
return _ownedTokensCount[owner].current();
}
/**
* @dev Gets the owner of the specified token ID
* @param tokenId uint256 ID of the token to query the owner of
* @return address currently marked as the owner of the given token ID
*/
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _tokenOwner[tokenId];
require(owner != address(0));
return owner;
}
/**
* @dev Approves another address to transfer the given token ID
* The zero address indicates there is no approved address.
* There can only be one approved address per token at a given time.
* Can only be called by the token owner or an approved operator.
* @param to address to be approved for the given token ID
* @param tokenId uint256 ID of the token to be approved
*/
function approve(address to, uint256 tokenId) public {
address owner = ownerOf(tokenId);
require(to != owner);
require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
_tokenApprovals[tokenId] = to;
emit Approval(owner, to, tokenId);
}
/**
* @dev Gets the approved address for a token ID, or zero if no address set
* Reverts if the token ID does not exist.
* @param tokenId uint256 ID of the token to query the approval of
* @return address currently approved for the given token ID
*/
function getApproved(uint256 tokenId) public view returns (address) {
require(_exists(tokenId));
return _tokenApprovals[tokenId];
}
/**
* @dev Sets or unsets the approval of a given operator
* An operator is allowed to transfer all tokens of the sender on their behalf
* @param to operator address to set the approval
* @param approved representing the status of the approval to be set
*/
function setApprovalForAll(address to, bool approved) public {
require(to != msg.sender);
_operatorApprovals[msg.sender][to] = approved;
emit ApprovalForAll(msg.sender, to, approved);
}
/**
* @dev Tells whether an operator is approved by a given owner
* @param owner owner address which you want to query the approval of
* @param operator operator address which you want to query the approval of
* @return bool whether the given operator is approved by the given owner
*/
function isApprovedForAll(address owner, address operator) public view returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev Transfers the ownership of a given token ID to another address
* Usage of this method is discouraged, use `safeTransferFrom` whenever possible
* Requires the msg.sender to be the owner, approved, or operator
* @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) public {
require(_isApprovedOrOwner(msg.sender, tokenId));
_transferFrom(from, to, tokenId);
}
/**
* @dev Safely transfers the ownership of a given token ID to another address
* If the target address is a contract, it must implement `onERC721Received`,
* which is called upon a safe transfer, and return the magic value
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
* the transfer is reverted.
* Requires the msg.sender to be the owner, approved, or operator
* @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 safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev Safely transfers the ownership of a given token ID to another address
* If the target address is a contract, it must implement `onERC721Received`,
* which is called upon a safe transfer, and return the magic value
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
* the transfer is reverted.
* Requires the msg.sender to be the owner, approved, or operator
* @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
* @param _data bytes data to send along with a safe transfer check
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
transferFrom(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, _data));
}
/**
* @dev Returns whether the specified token exists
* @param tokenId uint256 ID of the token to query the existence of
* @return bool whether the token exists
*/
function _exists(uint256 tokenId) internal view returns (bool) {
address owner = _tokenOwner[tokenId];
return owner != address(0);
}
/**
* @dev Returns whether the given spender can transfer a given token ID
* @param spender address of the spender to query
* @param tokenId uint256 ID of the token to be transferred
* @return bool whether the msg.sender is approved for the given token ID,
* is an operator of the owner, or is the owner of the token
*/
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
address owner = ownerOf(tokenId);
return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
}
/**
* @dev Internal function to mint a new token
* Reverts if the given token ID already exists
* @param to The address that will own the minted token
* @param tokenId uint256 ID of the token to be minted
*/
function _mint(address to, uint256 tokenId) internal {
require(to != address(0));
require(!_exists(tokenId));
_tokenOwner[tokenId] = to;
_ownedTokensCount[to].increment();
emit Transfer(address(0), to, 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 {
require(ownerOf(tokenId) == owner);
_clearApproval(tokenId);
_ownedTokensCount[owner].decrement();
_tokenOwner[tokenId] = address(0);
emit Transfer(owner, address(0), tokenId);
}
/**
* @dev Internal function to burn a specific token
* Reverts if the token does not exist
* @param tokenId uint256 ID of the token being burned
*/
function _burn(uint256 tokenId) internal {
_burn(ownerOf(tokenId), tokenId);
}
/**
* @dev Internal function to transfer ownership of a given token 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 {
require(ownerOf(tokenId) == from);
require(to != address(0));
_clearApproval(tokenId);
_ownedTokensCount[from].decrement();
_ownedTokensCount[to].increment();
_tokenOwner[tokenId] = to;
emit Transfer(from, to, tokenId);
}
/**
* @dev Internal function to invoke `onERC721Received` on a target address
* The call is not executed if the target address is not a contract
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param _data bytes optional data to send along with the call
* @return bool whether the call correctly returned the expected magic value
*/
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
internal returns (bool)
{
if (!to.isContract()) {
return true;
}
bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data);
return (retval == _ERC721_RECEIVED);
}
/**
* @dev Private function to clear current approval of a given token ID
* @param tokenId uint256 ID of the token to be transferred
*/
function _clearApproval(uint256 tokenId) private {
if (_tokenApprovals[tokenId] != address(0)) {
_tokenApprovals[tokenId] = address(0);
}
}
}
/**
* @title ERC-721 Non-Fungible Token with optional enumeration extension logic
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
contract ERC721Enumerable is ERC165, ERC721, IERC721Enumerable {
// Mapping from owner to list of owned token IDs
mapping(address => uint256[]) private _ownedTokens;
// Mapping from token ID to index of the owner tokens list
mapping(uint256 => uint256) private _ownedTokensIndex;
// Array with all token ids, used for enumeration
uint256[] private _allTokens;
// Mapping from token id to position in the allTokens array
mapping(uint256 => uint256) private _allTokensIndex;
/*
* bytes4(keccak256('totalSupply()')) == 0x18160ddd
* bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59
* bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7
*
* => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63
*/
bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
/**
* @dev Constructor function.
*/
constructor () public {
// register the supported interface to conform to ERC721Enumerable via ERC165
_registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
}
/**
* @dev Gets the token ID at a given index of the tokens list of the requested owner.
* @param owner address owning the tokens list to be accessed
* @param index uint256 representing the index to be accessed of the requested tokens list
* @return uint256 token ID at the given index of the tokens list owned by the requested address
*/
function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256) {
require(index < balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
return _ownedTokens[owner][index];
}
/**
* @dev Gets the total amount of tokens stored by the contract.
* @return uint256 representing the total amount of tokens
*/
function totalSupply() public view returns (uint256) {
return _allTokens.length;
}
/**
* @dev Gets the token ID at a given index of all the tokens in this contract
* Reverts if the index is greater or equal to the total number of tokens.
* @param index uint256 representing the index to be accessed of the tokens list
* @return uint256 token ID at the given index of the tokens list
*/
function tokenByIndex(uint256 index) public view returns (uint256) {
require(index < totalSupply(), "ERC721Enumerable: global index out of bounds");
return _allTokens[index];
}
/**
* @dev Internal function to transfer ownership of a given token 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);
}
/**
* @notice Deploy a new contract with the desired initialization code to the
* home address corresponding to a given key. Two conditions must be met: the
* submitter must be designated as the controller of the home address (with
* the initial controller set to the address corresponding to the first twenty
* bytes of the key), and there must not be a contract currently deployed at
* the home address. These conditions can be checked by calling
* `getHomeAddressInformation` and `isDeployable` with the same key.
* @param key bytes32 The unique value used to derive the home address.
* @param initializationCode bytes The contract creation code that will be
* used to deploy the contract to the home address.
* @return The home address of the deployed contract.
* @dev In order to deploy the contract to the home address, a new contract
* will be deployed with runtime code set to the initialization code of the
* contract that will be deployed to the home address. Then, metamorphic
* initialization code will retrieve that initialization code 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 also want to provide the
* key to `setReverseLookup` in order to find it again using only the home
* address to prevent accidentally losing the key.
*/
function deploy(bytes32 key, bytes calldata initializationCode)
external
payable
onlyEmpty(key)
onlyControllerDeployer(key)
returns (address homeAddress, bytes32 runtimeCodeHash)
{
// Ensure that initialization code was supplied.
require(initializationCode.length > 0, _NO_INIT_CODE_SUPPLIED);
// Deploy the initialization storage contract and set address in storage.
_initializationRuntimeStorageContract = _deployRuntimeStorageContract(
initializationCode
);
// Use metamorphic initialization code to deploy contract to home address.
(homeAddress, runtimeCodeHash) = _deployToHomeAddress(key);
}
/**
* @notice Mint an ERC721 token to the supplied owner that can be redeemed in
* order to gain control of a home address corresponding to a given key. Two
* conditions must be met: the submitter must be designated as the controller
* of the home address (with the initial controller set to the address
* corresponding to the first 20 bytes of the key), and there must not be a
* contract currently deployed at the home address. These conditions can be
* checked by calling `getHomeAddressInformation` and `isDeployable` with the
* same key.
* @param key bytes32 The unique value used to derive the home address.
* @param owner address The account that will be granted ownership of the
* ERC721 token.
* @dev In order to mint an ERC721 token, the assocated home address cannot be
* in use, or else the token will not be able to deploy to the home address.
* The controller is set to this contract until the token is redeemed, at
* which point the redeemer designates a new controller for the home address.
* The key of the home address and the tokenID of the ERC721 token are the
* same value, but different types (bytes32 vs. uint256).
*/
function lock(bytes32 key, address owner)
external
onlyEmpty(key)
onlyController(key)
{
// Ensure that the specified owner is a valid ERC721 receiver.
_validateOwner(owner, key);
// Get the HomeAddress storage struct from the mapping using supplied key.
HomeAddress storage home = _home[key];
// 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 Burn an ERC721 token to allow the supplied controller to gain the
* ability to deploy to the home address corresponding to the key matching the
* burned token. The submitter must be designated as either the owner of the
* token or as an approved spender.
* @param tokenId uint256 The ID of the ERC721 token to redeem.
* @param controller address The account that will be granted control of the
* home address corresponding to the given token.
* @dev The controller cannot be designated as the address of this contract,
* the null address, or the home address (the restriction on setting the home
* address as the controller is due to the fact that the home address will not
* be able to deploy to itself, as it needs to be empty before a contract can
* be deployed to it).
*/
function redeem(uint256 tokenId, address controller)
external
onlyTokenOwnerOrApprovedSpender(tokenId)
{
// Convert the token ID to a bytes32 key.
bytes32 key = bytes32(tokenId);
// Prevent the controller from being set to prohibited account values.
_validateController(controller, key);
// Burn the ERC721 token in question.
_burn(tokenId);
// Assign the new controller to the corresponding home address.
_home[key].controller = controller;
// Emit an event with the new controller.
emit NewController(key, controller);
}
/**
* @notice Transfer control over deployment to the home address corresponding
* to a given key. The caller must be designated as the current controller of
* the home address (with the initial controller set to the address
* corresponding to the first 20 bytes of the key) - This condition can be
* checked by calling `getHomeAddressInformation` with the same key.
* @param key bytes32 The unique value used to derive the home address.
* @param controller address The account that will be granted control of the
* home address corresponding to the given key.
* @dev The controller cannot be designated as the address of this contract,
* the null address, or the home address (the restriction on setting the home
* address as the controller is due to the fact that the home address will not
* be able to deploy to itself, as it needs to be empty before a contract can
* be deployed to it).
*/
function assignController(bytes32 key, address controller)
external
onlyController(key)
{
// Prevent the controller from being set to prohibited account values.
_validateController(controller, key);
// Assign the new controller to the corresponding home address.
HomeAddress storage home = _home[key];
home.exists = true;
home.controller = controller;
// Emit an event with the new controller.
emit NewController(key, controller);
}
/**
* @notice Transfer control over deployment to the home address corresponding
* to a given key to the null address, which will prevent it from being
* deployed to again in the future. The caller must be designated as the
* current controller of the corresponding home address (with the initial
* controller set to the address corresponding to the first 20 bytes of the
* key) - This condition can be checked by calling `getHomeAddressInformation`
* with the same key.
* @param key bytes32 The unique value used to derive the home address.
*/
function relinquishControl(bytes32 key)
external
onlyController(key)
{
// Assign the null address as the controller of the given key.
HomeAddress storage home = _home[key];
home.exists = true;
home.controller = address(0);
// Emit an event with the null address as the controller.
emit NewController(key, address(0));
}
/**
* @notice Burn an ERC721 token, set a supplied controller, and deploy a new
* contract with the supplied initialization code to the corresponding home
* address for the given token. The submitter must be designated as either the
* owner of the token or as an approved spender.
* @param tokenId uint256 The ID of the ERC721 token to redeem.
* @param controller address The account that will be granted control of the
* home address corresponding to the given token.
* @param initializationCode bytes The contract creation code that will be
* used to deploy the contract to the home address.
* @return The home address and runtime code hash of the deployed contract.
* @dev In order to deploy the contract to the home address, a new contract
* will be deployed with runtime code set to the initialization code of the
* contract that will be deployed to the home address. Then, metamorphic
* initialization code will retrieve that initialization code 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 also want to provide the
* key to `setReverseLookup` in order to find it again using only the home
* address to prevent accidentally losing the key. The controller cannot be
* designated as the address of this contract, the null address, or the home
* address (the restriction on setting the home address as the controller is
* due to the fact that the home address will not be able to deploy to itself,
* as it needs to be empty before a contract can be deployed to it). Also,
* checks on the contract at the home address being empty or not having the
* correct controller are unnecessary, as they are performed when minting the
* token and cannot be altered until the token is redeemed.
*/
function redeemAndDeploy(
uint256 tokenId,
address controller,
bytes calldata initializationCode
)
external
payable
onlyTokenOwnerOrApprovedSpender(tokenId)
returns (address homeAddress, bytes32 runtimeCodeHash)
{
// Ensure that initialization code was supplied.
require(initializationCode.length > 0, _NO_INIT_CODE_SUPPLIED);
// Convert the token ID to a bytes32 key.
bytes32 key = bytes32(tokenId);
// Prevent the controller from being set to prohibited account values.
_validateController(controller, key);
// Burn the ERC721 token in question.
_burn(tokenId);
// Deploy the initialization storage contract and set address in storage.
_initializationRuntimeStorageContract = _deployRuntimeStorageContract(
initializationCode
);
// Set provided controller and increment contract deploy count at once.
HomeAddress storage home = _home[key];
home.exists = true;
home.controller = controller;
home.deploys += 1;
// Emit an event with the new controller.
emit NewController(key, controller);
// Use metamorphic initialization code to deploy contract to home address.
(homeAddress, runtimeCodeHash) = _deployToHomeAddress(key);
}
/**
* @notice Derive a new key by concatenating an arbitrary 32-byte salt value
* and the address of the caller and performing a keccak256 hash. This allows
* for the creation of keys with additional entropy where desired while also
* preventing collisions with standard keys. The caller will be set as the
* controller of the derived key.
* @param salt bytes32 The desired salt value to use (along with the address
* of the caller) when deriving the resultant key and corresponding home
* address.
* @return The derived key.
* @dev Home addresses from derived keys will take longer to "mine" or locate,
* as an additional hash must be performed when computing the corresponding
* home address for each given salt input. Each caller will derive a different
* key even if they are supplying the same salt value.
*/
function deriveKey(bytes32 salt) external returns (bytes32 key) {
// Derive the key using the supplied salt and the calling address.
key = _deriveKey(salt, msg.sender);
// Register key and set caller as controller if it is not yet registered.
HomeAddress storage home = _home[key];
if (!home.exists) {
home.exists = true;
home.controller = msg.sender;
// Emit an event with the sender as the new controller.
emit NewController(key, msg.sender);
}
}
/**
* @notice Mint an ERC721 token to the supplied owner that can be redeemed in
* order to gain control of a home address corresponding to a given derived
* key. Two conditions must be met: the submitter must be designated as the
* current controller of the home address, and there must not be a contract
* currently deployed at the home address. These conditions can be checked by
* calling `getHomeAddressInformation` and `isDeployable` with the key
* determined by calling `getDerivedKey`.
* @param salt bytes32 The salt value that is used to derive the key.
* @param owner address The account that will be granted ownership of the
* ERC721 token.
* @return The derived key.
* @dev In order to mint an ERC721 token, the assocated home address cannot be
* in use, or else the token will not be able to deploy to the home address.
* The controller is set to this contract until the token is redeemed, at
* which point the redeemer designates a new controller for the home address.
* The key of the home address and the tokenID of the ERC721 token are the
* same value, but different types (bytes32 vs. uint256).
*/
function deriveKeyAndLock(bytes32 salt, address owner)
external
returns (bytes32 key)
{
// Derive the key using the supplied salt and the calling address.
key = _deriveKey(salt, msg.sender);
// Ensure that the specified owner is a valid ERC721 receiver.
_validateOwner(owner, key);
// Ensure that a contract is not currently deployed to the home address.
require(_isNotDeployed(key), _ACCOUNT_EXISTS);
// Ensure that the caller is the controller of the derived 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);
// Mint the ERC721 token to the designated owner.
_mint(owner, uint256(key));
// Emit an event signifying that this contract is now the controller.
emit NewController(key, address(this));
}
/**
* @notice Transfer control over deployment to the home address corresponding
* to a given derived key. The caller must be designated as the current
* controller of the home address - This condition can be checked by calling
* `getHomeAddressInformation` with the key obtained via `getDerivedKey`.
* @param salt bytes32 The salt value that is used to derive the key.
* @param controller address The account that will be granted control of the
* home address corresponding to the given derived key.
* @return The derived key.
* @dev The controller cannot be designated as the address of this contract,
* the null address, or the home address (the restriction on setting the home
* address as the controller is due to the fact that the home address will not
* be able to deploy to itself, as it needs to be empty before a contract can
* be deployed to it).
*/
function deriveKeyAndAssignController(bytes32 salt, address controller)
external
returns (bytes32 key)
{
// Derive the key using the supplied salt and the calling address.
key = _deriveKey(salt, msg.sender);
// Prevent the controller from being set to prohibited account values.
_validateController(controller, key);
// Ensure that the caller is the controller of the derived key.
HomeAddress storage home = _home[key];
if (home.exists) {
require(home.controller == msg.sender, _ONLY_CONTROLLER);
}
// Assign the new controller to the corresponding home address.
home.exists = true;
home.controller = controller;
// Emit an event with the new controller.
emit NewController(key, controller);
}
/**
* @notice Transfer control over deployment to the home address corresponding
* to a given derived key to the null address, which will prevent it from
* being deployed to again in the future. The caller must be designated as the
* current controller of the home address - This condition can be checked by
* calling `getHomeAddressInformation` with the key determined by calling
* `getDerivedKey`.
* @param salt bytes32 The salt value that is used to derive the key.
* @return The derived key.
*/
function deriveKeyAndRelinquishControl(bytes32 salt)
external
returns (bytes32 key)
{
// Derive the key using the supplied salt and the calling address.
key = _deriveKey(salt, msg.sender);
// Ensure that the caller is the controller of the derived key.
HomeAddress storage home = _home[key];
if (home.exists) {
require(home.controller == msg.sender, _ONLY_CONTROLLER);
}
// Assign the null address as the controller of the given derived key.
home.exists = true;
home.controller = address(0);
// Emit an event with the null address as the controller.
emit NewController(key, address(0));
}
/**
* @notice Record a key that corresponds to a given home address by supplying
* said key and using it to derive the address. This enables reverse lookup
* of a key using only the home address in question. This method may be called
* by anyone - control of the key is not required.
* @param key bytes32 The unique value used to derive the home address.
* @dev This does not set the salt or submitter fields, as those apply only to
* derived keys (although a derived key may also be set with this method, just
* without the derived fields).
*/
function setReverseLookup(bytes32 key) external {
// Derive home address of given key and set home address and key in mapping.
_key[_getHomeAddress(key)].key = key;
}
/**
* @notice Record the derived key that corresponds to a given home address by
* supplying the salt and submitter that were used to derive the key. This
* facititates reverse lookup of the derivation method of a key using only the
* home address in question. This method may be called by anyone - control of
* the derived key is not required.
* @param salt bytes32 The salt value that is used to derive the key.
* @param submitter address The account that submits the salt that is used to
* derive the key.
*/
function setDerivedReverseLookup(bytes32 salt, address submitter) external {
// Derive the key using the supplied salt and submitter.
bytes32 key = _deriveKey(salt, submitter);
// Derive home address and set it along with all other relevant information.
_key[_getHomeAddress(key)] = KeyInformation({
key: key,
salt: salt,
submitter: submitter
});
}
/**
* @notice Deploy a new storage contract with the supplied code as runtime
* code without deploying a contract to a home address. This can be used to
* store the contract creation code for use in future deployments of contracts
* to home addresses.
* @param codePayload bytes The code to set as the runtime code of the
* deployed contract.
* @return The address of the deployed storage contract.
* @dev Consider placing adequate protections on the storage contract to
* prevent unwanted callers from modifying or destroying it. Also, if you are
* placing contract contract creation code into the runtime storage contract,
* remember to include any constructor parameters as ABI-encoded arguments at
* the end of the contract creation code, similar to how you would perform a
* standard deployment.
*/
function deployRuntimeStorageContract(bytes calldata codePayload)
external
returns (address runtimeStorageContract)
{
// Ensure that a code payload was supplied.
require(codePayload.length > 0, "No runtime code payload supplied.");
// Deploy payload to the runtime storage contract and return the address.
runtimeStorageContract = _deployRuntimeStorageContract(codePayload);
}
/**
* @notice Deploy a new contract with the initialization code stored in the
* runtime code at the specified initialization runtime storage contract to
* the home address corresponding to a given key. Two conditions must be met:
* the submitter must be designated as the controller of the home address
* (with the initial controller set to the address corresponding to the first
* 20 bytes of the key), and there must not be a contract currently deployed
* at the home address. These conditions can be checked by calling
* `getHomeAddressInformation` and `isDeployable` with the same key.
* @param key bytes32 The unique value used to derive the home address.
* @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 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 also want to provide the key to
* `setReverseLookup` in order to find it again using only the home address to
* prevent accidentally losing the key.
*/
function deployViaExistingRuntimeStorageContract(
bytes32 key,
address initializationRuntimeStorageContract
)
external
payable
onlyEmpty(key)
onlyControllerDeployer(key)
returns (address homeAddress, bytes32 runtimeCodeHash)
{
// Ensure that the supplied runtime storage contract is not empty.
_validateRuntimeStorageIsNotEmpty(initializationRuntimeStorageContract);
// Set initialization runtime storage contract address in contract storage.
_initializationRuntimeStorageContract = initializationRuntimeStorageContract;
// Use metamorphic initialization code to deploy contract to home address.
(homeAddress, runtimeCodeHash) = _deployToHomeAddress(key);
}
/**
* @notice Burn an ERC721 token, set a supplied controller, and deploy a new
* contract with the initialization code stored in the runtime code at the
* specified initialization runtime storage contract to the home address
* corresponding to a given key. The submitter must be designated as either
* the owner of the token or as an approved spender.
* @param tokenId uint256 The ID of the ERC721 token to redeem.
* @param controller address The account that will be granted control of the
* home address corresponding to the given token.
* @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 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 also want to provide the key to
* `setReverseLookup` in order to find it again using only the home address to
* prevent accidentally losing the key. The controller cannot be designated as
* the address of this contract, the null address, or the home address (the
* restriction on setting the home address as the controller is due to the
* fact that the home address will not be able to deploy to itself, as it
* needs to be empty before a contract can be deployed to it). Also, checks on
* the contract at the home address being empty or not having the correct
* controller are unnecessary, as they are performed when minting the token
* and cannot be altered until the token is redeemed.
*/
function redeemAndDeployViaExistingRuntimeStorageContract(
uint256 tokenId,
address controller,
address initializationRuntimeStorageContract
)
external
payable
onlyTokenOwnerOrApprovedSpender(tokenId)
returns (address homeAddress, bytes32 runtimeCodeHash)
{
// Ensure that the supplied runtime storage contract is not empty.
_validateRuntimeStorageIsNotEmpty(initializationRuntimeStorageContract);
// Convert the token ID to a bytes32 key.
bytes32 key = bytes32(tokenId);
// Prevent the controller from being set to prohibited account values.
_validateController(controller, key);
// Burn the ERC721 token in question.
_burn(tokenId);
// Set initialization runtime storage contract address in contract storage.
_initializationRuntimeStorageContract = initializationRuntimeStorageContract;
// Set provided controller and increment contract deploy count at once.
HomeAddress storage home = _home[key];
home.exists = true;
home.controller = controller;
home.deploys += 1;
// Emit an event with the new controller.
emit NewController(key, controller);
// Use metamorphic initialization code to deploy contract to home address.
(homeAddress, runtimeCodeHash) = _deployToHomeAddress(key);
}
/**
* @notice Deploy a new contract with the desired initialization code to the
* home address corresponding to a given derived key. Two conditions must be
* met: the submitter must be designated as the controller of the home
* address, and there must not be a contract currently deployed at the home
* address. These conditions 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 initializationCode bytes 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 In order to deploy the contract to the home address, a new contract
* will be deployed with runtime code set to the initialization code of the
* contract that will be deployed to the home address. Then, metamorphic
* initialization code will retrieve that initialization code 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 deriveKeyAndDeploy(bytes32 salt, bytes calldata initializationCode)
external
payable
returns (address homeAddress, bytes32 key, bytes32 runtimeCodeHash)
{
// Ensure that initialization code was supplied.
require(initializationCode.length > 0, _NO_INIT_CODE_SUPPLIED);
// Derive key and prepare to deploy using supplied salt and calling address.
key = _deriveKeyAndPrepareToDeploy(salt);
// Deploy the initialization storage contract and set address in storage.
_initializationRuntimeStorageContract = _deployRuntimeStorageContract(
initializationCode
);
// Use metamorphic initialization code to deploy contract to home address.
(homeAddress, runtimeCodeHash) = _deployToHomeAddress(key);
}
/**
* @notice Deploy a new contract with the initialization code stored in the
* runtime code at the specified initialization runtime storage contract to
* the home address corresponding to a given derived key. Two conditions must
* be met: the submitter must be designated as the controller of the home
* address, and there must not be a contract currently deployed at the home
* address. These conditions 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)));
}
/**
* @notice View function to get information on a home address given the
* corresponding key.
* @param key bytes32 The unique value used to derive the home address.
* @return The home address, the current controller of the address, the number
* of times the home address has been deployed to, and the code hash of the
* runtime currently found at the home address, if any.
* @dev There is also an `isDeployable` method for determining if a contract
* can be deployed to the address, but in extreme cases it must actually
* perform a dry-run to determine if the contract is deployable, which means
* that it does not support staticcalls. There is also a convenience method,
* `hasNeverBeenDeployed`, but the information it conveys can be determined
* from this method alone as well.
*/
function getHomeAddressInformation(bytes32 key)
external
view
returns (
address homeAddress,
address controller,
uint256 deploys,
bytes32 currentRuntimeCodeHash
)
{
// Derive home address and retrieve other information using supplied key.
homeAddress = _getHomeAddress(key);
HomeAddress memory home = _home[key];
// If the home address has not been seen before, use the default controller.
if (!home.exists) {
controller = address(bytes20(key));
} else {
controller = home.controller;
}
// Retrieve the count of total deploys to the home address.
deploys = home.deploys;
// Retrieve keccak256 hash of runtime code currently at the home address.
assembly { currentRuntimeCodeHash := extcodehash(homeAddress) }
}
/**
* @notice View function to determine if no contract has ever been deployed to
* a home address given the corresponding key. This can be used to ensure that
* a given key or corresponding token is "new" or not.
* @param key bytes32 The unique value used to derive the home address.
* @return A boolean signifying if a contract has never been deployed using
* the supplied key before.
*/
function hasNeverBeenDeployed(bytes32 key)
external
view
returns (bool neverBeenDeployed)
{
neverBeenDeployed = (_home[key].deploys == 0);
}
/**
* @notice View function to search for a known key, salt, and/or submitter
* given a supplied home address. Keys can be controlled directly by an
* address that matches the first 20 bytes of the key, or they can be derived
* from a salt and a submitter - if the key is not a derived key, the salt and
* submitter fields will both have a value of zero.
* @param homeAddress address The home address to check for key information.
* @return The key, salt, and/or submitter used to deploy to the home address,
* assuming they have been submitted to the reverse lookup.
* @dev To populate these values, call `setReverseLookup` for cases where keys
* are used directly or are the only value known, or `setDerivedReverseLookup`
* for cases where keys are derived from a known salt and submitter.
*/
function reverseLookup(address homeAddress)
external
view
returns (bytes32 key, bytes32 salt, address submitter)
{
KeyInformation memory keyInformation = _key[homeAddress];
key = keyInformation.key;
salt = keyInformation.salt;
submitter = keyInformation.submitter;
}
/**
* @notice View function used by the metamorphic initialization code when
* deploying a contract to a home address. It returns the address of the
* runtime storage contract that holds the contract creation code, which the
* metamorphic creation code then `DELEGATECALL`s into in order to set up the
* contract and deploy the target runtime code.
* @return The current runtime storage contract that contains the target
* contract creation code.
* @dev This method is not meant to be part of the user-facing contract API,
* but is rather a mechanism for enabling the deployment of arbitrary code via
* fixed initialization code. The odd naming is chosen so that function
* selector will be 0x00000009 - that way, the metamorphic contract can simply
* use the `PC` opcode in order to push the selector to the stack.
*/
function getInitializationCodeFromContractRuntime_6CLUNS()
external
view
returns (address initializationRuntimeStorageContract)
{
// Return address of contract with initialization code set as runtime code.
initializationRuntimeStorageContract = _initializationRuntimeStorageContract;
}
/**
* @notice View function to return an URI for a given token ID. Throws if the
* token ID does not exist.
* @param tokenId uint256 ID of the token to query.
* @return String representing the URI data encoding of JSON metadata.
* @dev The URI returned by this method takes the following form (with all
* returns and initial whitespace removed - it's just here for clarity):
*
* data:application/json,{
* "name":"Home%20Address%20-%200x********************",
* "description":"< ... HomeWork NFT desription ... >",
* "image":"data:image/svg+xml;charset=utf-8;base64,< ... Image ... >"}
*
* where ******************** represents the checksummed home address that the
* token confers control over.
*/
function tokenURI(uint256 tokenId)
external
view
returns (string memory)
{
// Only return a URI for tokens that exist.
require(_exists(tokenId), "A token with the given ID does not exist.");
// Get the home address that the given tokenId corresponds to.
address homeAddress = _getHomeAddress(bytes32(tokenId));
// Get the checksummed, ascii-encoded representation of the home address.
string memory asciiHomeAddress = _toChecksummedAsciiString(homeAddress);
bytes memory uriEndSegment = _getTokenURIStorageRuntime();
// Insert checksummed address into URI in name and image fields and return.
return string(
abi.encodePacked( // Concatenate all the string segments together.
_URI_START_SEGMENT, // Data URI ID and initial formatting is constant.
asciiHomeAddress, // Checksummed home address is in the name field.
uriEndSegment // Description, image, and formatting is constant.
)
);
}
/**
* @notice Pure function to get the token name.
* @return String representing the token name.
*/
function name() external pure returns (string memory) {
return _NAME;
}
/**
* @notice Pure function to get the token symbol.
* @return String representing the token symbol.
*/
function symbol() external pure returns (string memory) {
return _SYMBOL;
}
/**
* @notice Pure function to determine the key that is derived from a given
* salt and submitting address.
* @param salt bytes32 The salt value that is used to derive the key.
* @param submitter address The submitter of the salt value used to derive the
* key.
* @return The derived key.
*/
function getDerivedKey(bytes32 salt, address submitter)
external
pure
returns (bytes32 key)
{
// Derive the key using the supplied salt and submitter.
key = _deriveKey(salt, submitter);
}
/**
* @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)
}
}
/**
* @notice Internal pure function for calculating a home address given a
* particular key.
* @param key bytes32 The unique value used to derive the home address.
* @return The home address corresponding to the supplied key.
*/
function _getHomeAddress(bytes32 key)
internal
pure
returns (address homeAddress)
{
// Determine the home address by replicating CREATE2 logic.
homeAddress = address(
uint160( // Downcast to match the address type.
uint256( // Cast to uint to truncate upper digits.
keccak256( // Compute CREATE2 hash using 4 inputs.
abi.encodePacked( // Pack all inputs to the hash together.
_FF_AND_THIS_CONTRACT, // This contract will be the caller.
key, // Pass in the supplied key as the salt.
_HOME_INIT_CODE_HASH // The metamorphic init code hash.
)
)
)
)
);
}
/**
* @notice Internal pure function for deriving a key given a particular salt
* and caller.
* @param salt bytes32 The value used to derive the key.
* @param submitter address The submitter of the salt used to derive the key.
* @return The derived key.
*/
function _deriveKey(bytes32 salt, address submitter)
internal
pure
returns (bytes32 key)
{
// Set the key as the keccak256 hash of the salt and submitter.
key = keccak256(abi.encodePacked(salt, submitter));
}
/**
* @notice Internal pure function for converting the bytes representation of
* an address to an ASCII string. This function is derived from the function
* at https://ethereum.stackexchange.com/a/56499/48410
* @param data bytes20 The account address to be converted.
* @return The account string in ASCII format. Note that leading "0x" is not
* included.
*/
function _toAsciiString(bytes20 data)
internal
pure
returns (string memory asciiString)
{
// Create an in-memory fixed-size bytes array.
bytes memory asciiBytes = new bytes(40);
// Declare variable types.
uint8 oneByte;
uint8 leftNibble;
uint8 rightNibble;
// Iterate over bytes, processing left and right nibble in each iteration.
for (uint256 i = 0; i < data.length; i++) {
// locate the byte and extract each nibble.
oneByte = uint8(uint160(data) / (2 ** (8 * (19 - i))));
leftNibble = oneByte / 16;
rightNibble = oneByte - 16 * leftNibble;
// To convert to ascii characters, add 48 to 0-9 and 87 to a-f.
asciiBytes[2 * i] = byte(leftNibble + (leftNibble < 10 ? 48 : 87));
asciiBytes[2 * i + 1] = byte(rightNibble + (rightNibble < 10 ? 48 : 87));
}
asciiString = string(asciiBytes);
}
/**
* @notice Internal pure function for getting a fixed-size array of whether or
* not each character in an account will be capitalized in the checksum.
* @param account address The account to get the checksum capitalization
* information for.
* @return A fixed-size array of booleans that signify if each character or
* "nibble" of the hex encoding of the address will be capitalized by the
* checksum.
*/
function _getChecksumCapitalizedCharacters(address account)
internal
pure
returns (bool[40] memory characterIsCapitalized)
{
// Convert the address to bytes.
bytes20 addressBytes = bytes20(account);
// Hash the address (used to calculate checksum).
bytes32 hash = keccak256(abi.encodePacked(_toAsciiString(addressBytes)));
// Declare variable types.
uint8 leftNibbleAddress;
uint8 rightNibbleAddress;
uint8 leftNibbleHash;
uint8 rightNibbleHash;
// Iterate over bytes, processing left and right nibble in each iteration.
for (uint256 i; i < addressBytes.length; i++) {
// locate the byte and extract each nibble for the address and the hash.
rightNibbleAddress = uint8(addressBytes[i]) % 16;
leftNibbleAddress = (uint8(addressBytes[i]) - rightNibbleAddress) / 16;
rightNibbleHash = uint8(hash[i]) % 16;
leftNibbleHash = (uint8(hash[i]) - rightNibbleHash) / 16;
// Set the capitalization flags based on the characters and the checksums.
characterIsCapitalized[2 * i] = (
leftNibbleAddress > 9 &&
leftNibbleHash > 7
);
characterIsCapitalized[2 * i + 1] = (
rightNibbleAddress > 9 &&
rightNibbleHash > 7
);
}
}
/**
* @notice Internal pure function for converting the bytes representation of
* an address to a checksummed ASCII string.
* @param account address The account address to be converted.
* @return The checksummed account string in ASCII format. Note that leading
* "0x" is not included.
*/
function _toChecksummedAsciiString(address account)
internal
pure
returns (string memory checksummedAsciiString)
{
// Get capitalized characters in the checksum.
bool[40] memory caps = _getChecksumCapitalizedCharacters(account);
// Create an in-memory fixed-size bytes array.
bytes memory asciiBytes = new bytes(40);
// Declare variable types.
uint8 oneByte;
uint8 leftNibble;
uint8 rightNibble;
uint8 leftNibbleOffset;
uint8 rightNibbleOffset;
// Convert account to bytes20.
bytes20 data = bytes20(account);
// Iterate over bytes, processing left and right nibble in each iteration.
for (uint256 i = 0; i < data.length; i++) {
// locate the byte and extract each nibble.
oneByte = uint8(uint160(data) / (2 ** (8 * (19 - i))));
leftNibble = oneByte / 16;
rightNibble = oneByte - 16 * leftNibble;
// To convert to ascii characters, add 48 to 0-9, 55 to A-F, & 87 to a-f.
if (leftNibble < 10) {
leftNibbleOffset = 48;
} else if (caps[i * 2]) {
leftNibbleOffset = 55;
} else {
leftNibbleOffset = 87;
}
if (rightNibble < 10) {
rightNibbleOffset = 48;
} else {
rightNibbleOffset = caps[(i * 2) + 1] ? 55 : 87; // instrumentation fix
}
asciiBytes[2 * i] = byte(leftNibble + leftNibbleOffset);
asciiBytes[2 * i + 1] = byte(rightNibble + rightNibbleOffset);
}
checksummedAsciiString = string(asciiBytes);
}
/**
* @notice Modifier to ensure that a contract is not currently deployed to the
* home address corresponding to a given key on the decorated function.
* @param key bytes32 The unique value used to derive the home address.
*/
modifier onlyEmpty(bytes32 key) {
require(_isNotDeployed(key), _ACCOUNT_EXISTS);
_;
}
/**
* @notice Modifier to ensure that the caller of the decorated function is the
* controller of the home address corresponding to a given key.
* @param key bytes32 The unique value used to derive the home address.
*/
modifier onlyController(bytes32 key) {
require(_getController(key) == msg.sender, _ONLY_CONTROLLER);
_;
}
/**
* @notice Modifier to track initial controllers and to count deploys, and to
* validate that only the designated controller has access to the decorated
* function.
* @param key bytes32 The unique value used to derive the home address.
*/
modifier onlyControllerDeployer(bytes32 key) {
HomeAddress storage home = _home[key];
// Set appropriate controller and increment contract deploy count at once.
if (!home.exists) {
home.exists = true;
home.controller = address(bytes20(key));
home.deploys += 1;
} else {
home.deploys += 1;
}
require(home.controller == msg.sender, _ONLY_CONTROLLER);
_;
}
/**
* @notice Modifier to ensure that only the owner of the supplied ERC721
* token, or an approved spender, can access the decorated function.
* @param tokenId uint256 The ID of the ERC721 token.
*/
modifier onlyTokenOwnerOrApprovedSpender(uint256 tokenId) {
require(
_isApprovedOrOwner(msg.sender, tokenId),
"Only the token owner or an approved spender may call this function."
);
_;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment