Skip to content

Instantly share code, notes, and snippets.

@shanefontaine
Last active October 22, 2019 18:06
Show Gist options
  • Save shanefontaine/18f3075b6984f25c431d3fbfc63531e2 to your computer and use it in GitHub Desktop.
Save shanefontaine/18f3075b6984f25c431d3fbfc63531e2 to your computer and use it in GitHub Desktop.
// 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