Last active
October 22, 2019 18:06
-
-
Save shanefontaine/18f3075b6984f25c431d3fbfc63531e2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// NOTE: This is unaudited code. Do not use in production. | |
pragma solidity ^0.5.8; | |
import "./AuthereumEnsResolver.sol"; | |
import "../base/Owned.sol"; | |
import "../utils/strings.sol"; | |
/** | |
* @title AuthereumEnsManager | |
* @author Authereum, Inc. | |
* @dev Used to manage all subdomains. | |
* @dev This is also known as the Authereum registrar. | |
* @dev The public ENS registry is used. The resolver is custom. | |
*/ | |
contract AuthereumEnsManager is Owned { | |
using strings for *; | |
// namehash('addr.reverse') | |
bytes32 constant public ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2; | |
address ensRegistry; | |
// The managed root name | |
string public rootName; | |
// The managed root node | |
bytes32 public rootNode; | |
// The address of the authereumEnsResolver | |
address public authereumEnsResolver; | |
// The address of the Authereum factory | |
address public authereumFactoryAddress; | |
// A mapping of the runtimeCodeHash to creationCodeHash | |
mapping(bytes32 => bytes32) public authereumProxyBytecodeHashMapping; | |
event RootnodeOwnerChanged(bytes32 indexed rootnode, address indexed newOwner); | |
event RootnodeResolverChanged(bytes32 indexed rootnode, address indexed newResolver); | |
event RootnodeTTLChanged(bytes32 indexed rootnode, uint64 indexed newTtl); | |
event AuthereumEnsResolverChanged(address indexed authereumEnsResolver); | |
event AuthereumFactoryAddressChanged(address indexed authereumFactoryAddress); | |
event AuthereumProxyBytecodeHashChanged(bytes32 indexed authereumProxyRuntimeCodeHash, bytes32 indexed authereumProxyCreationCodeHash); | |
event Registered(address indexed owner, string ens); | |
/// @dev Throws if the sender is not a contract from our proxy. | |
/// @param salt Salt for the contract creation | |
modifier onlyAuthereumUser(uint256 salt) { | |
require(calculateExpectedAddress(salt, msg.sender) == msg.sender, "Must be an Authereum account"); | |
_; | |
} | |
/// @dev Constructor that sets the ENS root name and root node to manage | |
/// @param _rootName The root name (e.g. authereum.eth) | |
/// @param _rootNode The node of the root name (e.g. namehash(authereum.eth)) | |
/// @param _ensRegistry Custom ENS Registry address | |
/// @param _authereumEnsResolver Custom Autheruem ENS Resolver address | |
constructor( | |
string memory _rootName, | |
bytes32 _rootNode, | |
address _ensRegistry, | |
address _authereumEnsResolver | |
) | |
public | |
{ | |
rootName = _rootName; | |
rootNode = _rootNode; | |
ensRegistry = _ensRegistry; | |
authereumEnsResolver = _authereumEnsResolver; | |
} | |
/// @dev Resolves an ENS name to an address | |
/// @param _node The namehash of the ENS name | |
/// @return The address associated with an ENS node | |
function resolveEns(bytes32 _node) public returns (address) { | |
address resolver = getEnsRegistry().resolver(_node); | |
return AuthereumEnsResolver(resolver).addr(_node); | |
} | |
/// @dev Gets the official ENS registry | |
/// @return The official ENS registry address | |
function getEnsRegistry() public view returns (EnsRegistry) { | |
return EnsRegistry(ensRegistry); | |
} | |
/// @dev Gets the official ENS reverse registrar | |
/// @return The official ENS reverse registrar address | |
function getEnsReverseRegistrar() public view returns (EnsReverseRegistrar) { | |
return EnsReverseRegistrar(getEnsRegistry().owner(ADDR_REVERSE_NODE)); | |
} | |
/// @dev Calculate the expected create2 address | |
/// @param _salt Salt (uint) of the newly created address | |
/// @param _runtimeCodeAddress Address of the runtimeCode used to generate the creationCodeHash | |
/// @return create2 calculcated address | |
function calculateExpectedAddress( | |
uint256 _salt, | |
address _runtimeCodeAddress | |
) | |
public | |
view | |
returns (address) | |
{ | |
bytes32 saltHash = getSaltHash(_salt); | |
bytes32 creationCodeHash = getCreationCodeHash(_runtimeCodeAddress); | |
bytes32 _data = keccak256( | |
abi.encodePacked( | |
bytes1(0xff), | |
authereumFactoryAddress, | |
saltHash, | |
creationCodeHash | |
) | |
); | |
return address(bytes20(_data << 96)); | |
} | |
/// @dev Calculate the saltHash given the salt | |
/// @param _salt Salt of the newly created address | |
/// @return Hash of the salt (uint) and the original sender | |
function getSaltHash(uint256 _salt) public view returns (bytes32) { | |
return keccak256(abi.encodePacked(_salt, tx.origin)); | |
} | |
/// @dev Calculate the cretionCodeHash of the msg.sender (caller in assembly) | |
/// @param _runtimeCodeAddress Address of the runtimeCode used to generate the creationCodeHash | |
/// @return Creation code of the passed in address' runtime code | |
function getCreationCodeHash(address _runtimeCodeAddress) public view returns (bytes32) { | |
bytes32 runtimeCodeHash; | |
assembly { | |
runtimeCodeHash := extcodehash(_runtimeCodeAddress) | |
} | |
return authereumProxyBytecodeHashMapping[runtimeCodeHash]; | |
} | |
/** | |
* External functions | |
*/ | |
/// @dev This function is used when the rootnode owner is updated | |
/// @param _newOwner The address of the new ENS manager that will manage the root node. | |
function changeRootnodeOwner(address _newOwner) external onlyOwner { | |
require(_newOwner != address(0), "Address cannot be null"); | |
getEnsRegistry().setOwner(rootNode, _newOwner); | |
emit RootnodeOwnerChanged(rootNode, _newOwner); | |
} | |
/// @dev This function is used when the rootnode resolver is updated | |
/// @param _newResolver The address of the new ENS Resolver that will manage the root node. | |
function changeRootnodeResolver(address _newResolver) external onlyOwner { | |
require(_newResolver != address(0), "Address cannot be null"); | |
getEnsRegistry().setResolver(rootNode, _newResolver); | |
emit RootnodeResolverChanged(rootNode, _newResolver); | |
} | |
/// @dev This function is used when the rootnode TTL is updated | |
/// @param _newTtl The address of the new TTL that will manage the root node | |
function changeRootnodeTTL(uint64 _newTtl) external onlyOwner { | |
getEnsRegistry().setTTL(rootNode, _newTtl); | |
emit RootnodeTTLChanged(rootNode, _newTtl); | |
} | |
/// @dev Lets the owner change the address of the Authereum ENS resolver contract | |
/// @param _authereumEnsResolver The address of the Authereun ENS resolver contract | |
function changeEnsResolver(address _authereumEnsResolver) external onlyOwner { | |
require(_authereumEnsResolver != address(0), "Address cannot be null"); | |
authereumEnsResolver = _authereumEnsResolver; | |
emit AuthereumEnsResolverChanged(_authereumEnsResolver); | |
} | |
/// @dev Lets the owner change the address of the Authereum factory | |
/// @param _authereumFactoryAddress The address of the Authereum factory | |
function changeAuthereumFactoryAddress(address _authereumFactoryAddress) external onlyOwner { | |
require(_authereumFactoryAddress != address(0), "Address cannot be null"); | |
authereumFactoryAddress = _authereumFactoryAddress; | |
emit AuthereumFactoryAddressChanged(authereumFactoryAddress); | |
} | |
/// @dev Lets the owner change the hash of the runtime code of the Authereum proxy | |
/// @param _authereumProxyRuntimeCodeHash The hash of the runtime code of the Authereum proxy | |
/// @param _authereumProxyCreationCodeHash The hash of the creation code of the Authereum proxy | |
function changeAuthereumProxyBytecodeHashMapping( | |
bytes32 _authereumProxyRuntimeCodeHash, | |
bytes32 _authereumProxyCreationCodeHash | |
) | |
external | |
onlyOwner | |
{ | |
authereumProxyBytecodeHashMapping[_authereumProxyRuntimeCodeHash] = _authereumProxyCreationCodeHash; | |
emit AuthereumProxyBytecodeHashChanged(_authereumProxyRuntimeCodeHash, _authereumProxyCreationCodeHash); | |
} | |
/// @dev Lets the manager assign an ENS subdomain of the root node to a target address. | |
/// @notice Registers both the forward and reverse ENS | |
/// @param _label The subdomain label | |
/// @param _owner The owner of the subdomain | |
/// @param _salt Salt of the newly created address | |
function register( | |
string calldata _label, | |
address _owner, | |
uint256 _salt | |
) | |
external | |
onlyAuthereumUser(_salt) | |
{ | |
bytes32 labelNode = keccak256(abi.encodePacked(_label)); | |
bytes32 node = keccak256(abi.encodePacked(rootNode, labelNode)); | |
address currentOwner = getEnsRegistry().owner(node); | |
require(currentOwner == address(0), "Label is already owned"); | |
// Forward ENS | |
getEnsRegistry().setSubnodeOwner(rootNode, labelNode, address(this)); | |
getEnsRegistry().setResolver(node, authereumEnsResolver); | |
getEnsRegistry().setOwner(node, _owner); | |
AuthereumEnsResolver(authereumEnsResolver).setAddr(node, _owner); | |
// Reverse ENS | |
strings.slice[] memory parts = new strings.slice[](2); | |
parts[0] = _label.toSlice(); | |
parts[1] = rootName.toSlice(); | |
string memory name = ".".toSlice().join(parts); | |
bytes32 reverseNode = EnsReverseRegistrar(getEnsReverseRegistrar()).node(_owner); | |
AuthereumEnsResolver(authereumEnsResolver).setName(reverseNode, name); | |
emit Registered(_owner, name); | |
} | |
/** | |
* Public functions | |
*/ | |
/// @dev Returns true is a given subnode is available | |
/// @param _subnode The target subnode | |
/// @return True if the subnode is available | |
function isAvailable(bytes32 _subnode) public view returns (bool) { | |
bytes32 node = keccak256(abi.encodePacked(rootNode, _subnode)); | |
address currentOwner = getEnsRegistry().owner(node); | |
if(currentOwner == address(0)) { | |
return true; | |
} | |
return false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment