ens-usernames/contracts/registry/ENSSubdomainRegistry.sol

279 lines
9.2 KiB
Solidity
Raw Normal View History

pragma solidity ^0.4.23;
2018-01-09 17:07:58 +00:00
2018-03-24 03:40:03 +00:00
import "../common/Controlled.sol";
import "../token/ERC20Token.sol";
import "../ens/ENS.sol";
import "../ens/PublicResolver.sol";
/**
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
* @notice Sell ENS subdomains of owned domains.
*/
contract ENSSubdomainRegistry is Controlled {
ERC20Token public token;
ENS public ens;
PublicResolver public resolver;
address public parentRegistry;
uint256 public releaseDelay = 1 years;
mapping (bytes32 => Domain) public domains;
mapping (bytes32 => Account) public accounts;
event Registered(bytes32 indexed _subDomainHash, address _owner);
event Released(bytes32 indexed _subDomainHash);
2018-05-07 03:37:56 +00:00
enum NodeState { Free, Owned, Moved }
2018-03-24 03:40:03 +00:00
struct Domain {
2018-05-07 03:37:56 +00:00
NodeState state;
2018-03-24 03:40:03 +00:00
uint256 price;
}
struct Account {
uint256 tokenBalance;
uint256 creationTime;
2018-05-06 23:42:06 +00:00
address backupOwner;
}
2018-03-24 03:40:03 +00:00
/**
* @notice Initializes a UserRegistry contract
* @param _token fee token base
* @param _ens Ethereum Name Service root address
* @param _resolver Default resolver to use in initial settings
* @param _parentRegistry Address of old registry (if any) for account migration.
*/
constructor(
ERC20Token _token,
ENS _ens,
PublicResolver _resolver,
address _parentRegistry
2018-03-24 03:40:03 +00:00
)
public
{
token = _token;
ens = _ens;
resolver = _resolver;
parentRegistry = _parentRegistry;
}
/**
* @notice Registers `_userHash` subdomain to `_domainHash` setting msg.sender as owner.
* @param _userHash choosen unowned subdomain hash
* @param _domianHash choosen contract owned domain hash
* @param _account optional address to set at public resolver
* @param _pubkeyA optional pubkey part A to set at public resolver
* @param _pubkeyB optional pubkey part B to set at public resolver
*/
2018-03-24 03:40:03 +00:00
function register(
bytes32 _userHash,
bytes32 _domainHash,
address _account,
bytes32 _pubkeyA,
bytes32 _pubkeyB
2018-03-24 03:40:03 +00:00
)
external
returns(bytes32 subdomainHash)
{
2018-03-24 03:40:03 +00:00
Domain memory domain = domains[_domainHash];
2018-05-07 03:37:56 +00:00
require(domain.state == NodeState.Owned);
subdomainHash = keccak256(_userHash, _domainHash);
require(ens.owner(subdomainHash) == address(0));
require(accounts[subdomainHash].creationTime == 0);
2018-05-06 23:42:06 +00:00
accounts[subdomainHash] = Account(domain.price, block.timestamp, msg.sender);
require(token.allowance(msg.sender, address(this)) >= domain.price);
bool resolvePubkey = _pubkeyA != 0 || _pubkeyB != 0;
bool resolveAccount = _account != address(0);
if(resolvePubkey || resolveAccount) {
//set to self the ownship to setup initial resolver
ens.setSubnodeOwner(_domainHash, _userHash, address(this));
ens.setResolver(subdomainHash, resolver); //default resolver
if(resolveAccount){
resolver.setAddr(subdomainHash, _account);
}
if(resolvePubkey) {
resolver.setPubkey(subdomainHash, _pubkeyA, _pubkeyB);
}
}
//transfer ownship of subdone to registrant
ens.setSubnodeOwner(_domainHash, _userHash, msg.sender);
//get payment
2018-03-24 03:40:03 +00:00
require(
token.transferFrom(
address(msg.sender),
address(this),
domain.price
)
);
emit Registered(subdomainHash, msg.sender);
}
/**
* @notice release subdomain and retrieve locked fee, needs to be called after `releasePeriod` from creation time.
* @param _userHash `msg.sender` owned subdomain hash
* @param _domianHash choosen contract owned domain hash
*/
function release(
bytes32 _userHash,
bytes32 _domainHash
)
external
{
2018-05-06 23:42:06 +00:00
bool isDomainController = ens.owner(_domainHash) == address(this);
bytes32 subdomainHash = keccak256(_userHash, _domainHash);
Account memory account = accounts[subdomainHash];
require(account.creationTime > 0);
2018-05-06 23:42:06 +00:00
if (isDomainController) {
require(msg.sender == ens.owner(subdomainHash));
require(account.creationTime + releaseDelay > block.timestamp);
ens.setSubnodeOwner(_domainHash, _userHash, address(this));
ens.setResolver(subdomainHash, address(0));
ens.setSubnodeOwner(_domainHash, _userHash, address(0));
2018-05-06 23:42:06 +00:00
} else {
require(msg.sender == account.backupOwner);
}
2018-05-06 23:42:06 +00:00
delete accounts[subdomainHash];
require(token.transfer(msg.sender, account.tokenBalance));
emit Released(subdomainHash);
2018-01-09 17:07:58 +00:00
}
2018-03-24 03:40:03 +00:00
/**
* @notice Controller include new domain available to register
* @param _domain domain owned by user registry being activated
* @param _price cost to register subnode from this node
*/
2018-03-24 03:40:03 +00:00
function addDomain(
bytes32 _domain,
uint256 _price
)
external
onlyController
{
2018-05-07 03:37:56 +00:00
require(domains[_domain].state == NodeState.Free);
require(ens.owner(_domain) == address(this));
2018-05-07 03:37:56 +00:00
domains[_domain] = Domain(NodeState.Owned, _price);
2018-01-09 17:07:58 +00:00
}
/**
* @notice updates domain price
* @param _domain active domain being defined price
* @param _price new price
*/
function setDomainPrice(
bytes32 _domain,
uint256 _price
)
external
onlyController
{
Domain storage domain = domains[_domain];
2018-05-07 03:37:56 +00:00
require(domain.state == NodeState.Owned);
domain.price = _price;
}
/**
2018-05-06 22:16:50 +00:00
* @notice moves a domain to other Registry (will not move subdomains accounts)
* @param _newRegistry new registry hodling this domain
* @param _domain domain being moved
*/
2018-05-06 22:16:50 +00:00
function moveDomain(
ENSSubdomainRegistry _newRegistry,
bytes32 _domain
2018-03-24 03:40:03 +00:00
)
external
onlyController
{
require(ens.owner(_domain) == address(this));
2018-05-07 03:37:56 +00:00
require(domains[_domain].state == NodeState.Owned);
2018-05-06 22:16:50 +00:00
uint256 price = domains[_domain].price;
2018-05-07 03:37:56 +00:00
domains[_domain].state = NodeState.Moved;
2018-05-06 22:16:50 +00:00
ens.setOwner(_domain, _newRegistry);
_newRegistry.migrateDomain(_domain, price);
2018-01-09 17:07:58 +00:00
}
2018-05-06 23:42:06 +00:00
/**
* @notice updates backup owner useful in case of opt-out domain move to new registry.
* @param _subdomainHash hash of the subdomain regarding this
**/
function updateBackupOwner(bytes32 _subdomainHash) external {
require(accounts[_subdomainHash].creationTime > 0);
require(msg.sender == ens.owner(_subdomainHash));
accounts[_subdomainHash].backupOwner == msg.sender;
}
/**
* @notice updates default public resolver for newly registred subdomains
* @param _resolver new default resolver
*/
2018-03-24 03:40:03 +00:00
function setResolver(
address _resolver
)
external
onlyController
{
resolver = PublicResolver(_resolver);
2018-03-24 03:40:03 +00:00
}
/**
* @notice Migrate account to new registry
* @param _newRegistry new registry address
* @param _userHash `msg.sender` owned subdomain hash
* @param _domianHash choosen contract owned domain hash
**/
2018-05-06 22:16:50 +00:00
function moveAccount(
ENSSubdomainRegistry _newRegistry,
bytes32 _userHash,
bytes32 _domainHash
)
external
{
require(ens.owner(_domainHash) == address(_newRegistry));
require(address(this) == _newRegistry.parentRegistry());
bytes32 subdomainHash = keccak256(_userHash, _domainHash);
require(msg.sender == ens.owner(subdomainHash));
Account memory account = accounts[subdomainHash];
delete accounts[subdomainHash];
token.approve(_newRegistry, account.tokenBalance);
2018-05-06 23:42:06 +00:00
_newRegistry.migrateAccount(_userHash, _domainHash, account.tokenBalance, account.creationTime, account.backupOwner);
}
2018-05-06 22:16:50 +00:00
/**
@dev callabe only by parent registry to continue migration of domain
*/
function migrateDomain(
bytes32 _domain,
uint256 _price
)
external
{
require(msg.sender == parentRegistry);
require(ens.owner(_domain) == address(this));
2018-05-07 03:37:56 +00:00
domains[_domain] = Domain(NodeState.Owned, _price);
2018-05-06 22:16:50 +00:00
}
/**
* @dev callable only by parent registry for continue user opt-in migration
* @param _userHash any subdomain hash coming from parent
* @param _domianHash choosen contract owned domain hash
* @param _tokenBalance amount being transferred
* @param _creationTime any value coming from parent
2018-05-06 23:42:06 +00:00
* @param _backupOwner backupOwner for opt-out/release at domain move
**/
2018-05-06 22:16:50 +00:00
function migrateAccount(
bytes32 _userHash,
bytes32 _domainHash,
uint256 _tokenBalance,
2018-05-06 23:42:06 +00:00
uint256 _creationTime,
address _backupOwner
)
external
{
require(msg.sender == parentRegistry);
bytes32 subdomainHash = keccak256(_userHash, _domainHash);
2018-05-06 23:42:06 +00:00
accounts[subdomainHash] = Account(_tokenBalance, _creationTime, _backupOwner);
require(token.transferFrom(parentRegistry, address(this), _tokenBalance));
}
2018-01-09 17:07:58 +00:00
}