Transaction Hash:
Block:
8288888 at Aug-05-2019 05:55:50 AM +UTC
Transaction Fee:
0.000086339 ETH
$0.18
Gas Used:
78,490 Gas / 1.1 Gwei
Emitted Events:
| 66 |
0xa511bf5fdcccf99a5c58b410e2f2e7d922f772ae.0xbb2ce2f51803bba16bc85282b47deeea9a5c6223eabea1077be696b3f265cf13( 0xbb2ce2f51803bba16bc85282b47deeea9a5c6223eabea1077be696b3f265cf13 )
|
| 67 |
BaseRegistrarImplementation.Transfer( from=0x00000000...000000000, to=[Sender] 0xa7f3659c53820346176f7e0e350780df304db179, tokenId=60282764003703677034823941395812607948718466778362980799824522720089233797074 )
|
| 68 |
ENS.NewOwner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE, label=8546D5D63AD186E84D4DAD34B2327E5F11F341F99E49DC711679A4C256FDAFD2, owner=[Sender] 0xa7f3659c53820346176f7e0e350780df304db179 )
|
| 69 |
BaseRegistrarImplementation.NameMigrated( id=60282764003703677034823941395812607948718466778362980799824522720089233797074, owner=[Sender] 0xa7f3659c53820346176f7e0e350780df304db179, expires=1588550400 )
|
| 70 |
BaseRegistrarImplementation.NameRegistered( id=60282764003703677034823941395812607948718466778362980799824522720089233797074, owner=[Sender] 0xa7f3659c53820346176f7e0e350780df304db179, expires=1588550400 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
| 0x6090A6e4...cd15878Ef | (ENS: Old Registrar) | ||||
| 0xa511Bf5F...922f772ae |
0 Eth
Nonce: 0
|
0 Eth
Nonce: 0
| |||
| 0xA7F3659c...F304db179 |
196.93200803360460163 Eth
Nonce: 97021
|
196.94192169460460163 Eth
Nonce: 97022
| 0.009913661 | ||
|
0xEA674fdD...16B898ec8
Miner
| (Ethermine) | 516.475233881850885831 Eth | 516.475320220850885831 Eth | 0.000086339 | |
| 0xFaC7BEA2...556b31e04 |
Execution Trace
Registrar.transferRegistrars( _hash=8546D5D63AD186E84D4DAD34B2327E5F11F341F99E49DC711679A4C256FDAFD2 )
-
0xa511bf5fdcccf99a5c58b410e2f2e7d922f772ae.CALL( ) -
ENS.owner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE ) => ( 0xFaC7BEA255a6990f749363002136aF6556b31e04 ) -
0xa511bf5fdcccf99a5c58b410e2f2e7d922f772ae.faab9d39( )
BaseRegistrarImplementation.acceptRegistrarTransfer( label=8546D5D63AD186E84D4DAD34B2327E5F11F341F99E49DC711679A4C256FDAFD2, deed=0xa511Bf5FdccCf99A5C58B410E2F2E7D922f772ae, 1502820983 )
-
ENS.owner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE ) => ( 0xFaC7BEA255a6990f749363002136aF6556b31e04 ) -
Registrar.entries( _hash=8546D5D63AD186E84D4DAD34B2327E5F11F341F99E49DC711679A4C256FDAFD2 ) => ( 2, 0xa511Bf5FdccCf99A5C58B410E2F2E7D922f772ae, 1502820983, 10000000000000000, 2201000000000000000 ) -
0xa511bf5fdcccf99a5c58b410e2f2e7d922f772ae.STATICCALL( ) 0xa511bf5fdcccf99a5c58b410e2f2e7d922f772ae.bbe42771( )-
Null: 0x00...dEaD.CALL( ) - ETH 0.01
0xa7f3659c53820346176f7e0e350780df304db179.CALL( ) -
Null: 0x00...dEaD.SELFDESTRUCT( )
-
-
ENS.setSubnodeOwner( node=93CDEB708B7545DC668EB9280176169D1C33CFD8ED6F04690A0BCC88A93FC4AE, label=8546D5D63AD186E84D4DAD34B2327E5F11F341F99E49DC711679A4C256FDAFD2, owner=0xA7F3659c53820346176f7E0E350780DF304db179 )
-
transferRegistrars[Registrar (ln:578)]
owner[Registrar (ln:579)]setRegistrar[Registrar (ln:585)]acceptRegistrarTransfer[Registrar (ln:588)]
File 1 of 3: Registrar
File 2 of 3: BaseRegistrarImplementation
File 3 of 3: ENS
pragma solidity ^0.4.0;
/*
Temporary Hash Registrar
========================
This is a simplified version of a hash registrar. It is purporsefully limited:
names cannot be six letters or shorter, new auctions will stop after 4 years.
The plan is to test the basic features and then move to a new contract in at most
2 years, when some sort of renewal mechanism will be enabled.
*/
contract AbstractENS {
function owner(bytes32 node) constant returns(address);
function resolver(bytes32 node) constant returns(address);
function ttl(bytes32 node) constant returns(uint64);
function setOwner(bytes32 node, address owner);
function setSubnodeOwner(bytes32 node, bytes32 label, address owner);
function setResolver(bytes32 node, address resolver);
function setTTL(bytes32 node, uint64 ttl);
// Logged when the owner of a node assigns a new owner to a subnode.
event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
// Logged when the owner of a node transfers ownership to a new account.
event Transfer(bytes32 indexed node, address owner);
// Logged when the resolver for a node changes.
event NewResolver(bytes32 indexed node, address resolver);
// Logged when the TTL of a node changes
event NewTTL(bytes32 indexed node, uint64 ttl);
}
/**
* @title Deed to hold ether in exchange for ownership of a node
* @dev The deed can be controlled only by the registrar and can only send ether back to the owner.
*/
contract Deed {
address public registrar;
address constant burn = 0xdead;
uint public creationDate;
address public owner;
address public previousOwner;
uint public value;
event OwnerChanged(address newOwner);
event DeedClosed();
bool active;
modifier onlyRegistrar {
if (msg.sender != registrar) throw;
_;
}
modifier onlyActive {
if (!active) throw;
_;
}
function Deed(address _owner) payable {
owner = _owner;
registrar = msg.sender;
creationDate = now;
active = true;
value = msg.value;
}
function setOwner(address newOwner) onlyRegistrar {
if (newOwner == 0) throw;
previousOwner = owner; // This allows contracts to check who sent them the ownership
owner = newOwner;
OwnerChanged(newOwner);
}
function setRegistrar(address newRegistrar) onlyRegistrar {
registrar = newRegistrar;
}
function setBalance(uint newValue, bool throwOnFailure) onlyRegistrar onlyActive {
// Check if it has enough balance to set the value
if (value < newValue) throw;
value = newValue;
// Send the difference to the owner
if (!owner.send(this.balance - newValue) && throwOnFailure) throw;
}
/**
* @dev Close a deed and refund a specified fraction of the bid value
* @param refundRatio The amount*1/1000 to refund
*/
function closeDeed(uint refundRatio) onlyRegistrar onlyActive {
active = false;
if (! burn.send(((1000 - refundRatio) * this.balance)/1000)) throw;
DeedClosed();
destroyDeed();
}
/**
* @dev Close a deed and refund a specified fraction of the bid value
*/
function destroyDeed() {
if (active) throw;
// Instead of selfdestruct(owner), invoke owner fallback function to allow
// owner to log an event if desired; but owner should also be aware that
// its fallback function can also be invoked by setBalance
if(owner.send(this.balance)) {
selfdestruct(burn);
}
}
}
/**
* @title Registrar
* @dev The registrar handles the auction process for each subnode of the node it owns.
*/
contract Registrar {
AbstractENS public ens;
bytes32 public rootNode;
mapping (bytes32 => entry) _entries;
mapping (address => mapping(bytes32 => Deed)) public sealedBids;
enum Mode { Open, Auction, Owned, Forbidden, Reveal, NotYetAvailable }
uint32 constant totalAuctionLength = 5 days;
uint32 constant revealPeriod = 48 hours;
uint32 public constant launchLength = 8 weeks;
uint constant minPrice = 0.01 ether;
uint public registryStarted;
event AuctionStarted(bytes32 indexed hash, uint registrationDate);
event NewBid(bytes32 indexed hash, address indexed bidder, uint deposit);
event BidRevealed(bytes32 indexed hash, address indexed owner, uint value, uint8 status);
event HashRegistered(bytes32 indexed hash, address indexed owner, uint value, uint registrationDate);
event HashReleased(bytes32 indexed hash, uint value);
event HashInvalidated(bytes32 indexed hash, string indexed name, uint value, uint registrationDate);
struct entry {
Deed deed;
uint registrationDate;
uint value;
uint highestBid;
}
// State transitions for names:
// Open -> Auction (startAuction)
// Auction -> Reveal
// Reveal -> Owned
// Reveal -> Open (if nobody bid)
// Owned -> Open (releaseDeed or invalidateName)
function state(bytes32 _hash) constant returns (Mode) {
var entry = _entries[_hash];
if(!isAllowed(_hash, now)) {
return Mode.NotYetAvailable;
} else if(now < entry.registrationDate) {
if (now < entry.registrationDate - revealPeriod) {
return Mode.Auction;
} else {
return Mode.Reveal;
}
} else {
if(entry.highestBid == 0) {
return Mode.Open;
} else {
return Mode.Owned;
}
}
}
modifier inState(bytes32 _hash, Mode _state) {
if(state(_hash) != _state) throw;
_;
}
modifier onlyOwner(bytes32 _hash) {
if (state(_hash) != Mode.Owned || msg.sender != _entries[_hash].deed.owner()) throw;
_;
}
modifier registryOpen() {
if(now < registryStarted || now > registryStarted + 4 years || ens.owner(rootNode) != address(this)) throw;
_;
}
function entries(bytes32 _hash) constant returns (Mode, address, uint, uint, uint) {
entry h = _entries[_hash];
return (state(_hash), h.deed, h.registrationDate, h.value, h.highestBid);
}
/**
* @dev Constructs a new Registrar, with the provided address as the owner of the root node.
* @param _ens The address of the ENS
* @param _rootNode The hash of the rootnode.
*/
function Registrar(AbstractENS _ens, bytes32 _rootNode, uint _startDate) {
ens = _ens;
rootNode = _rootNode;
registryStarted = _startDate > 0 ? _startDate : now;
}
/**
* @dev Returns the maximum of two unsigned integers
* @param a A number to compare
* @param b A number to compare
* @return The maximum of two unsigned integers
*/
function max(uint a, uint b) internal constant returns (uint max) {
if (a > b)
return a;
else
return b;
}
/**
* @dev Returns the minimum of two unsigned integers
* @param a A number to compare
* @param b A number to compare
* @return The minimum of two unsigned integers
*/
function min(uint a, uint b) internal constant returns (uint min) {
if (a < b)
return a;
else
return b;
}
/**
* @dev Returns the length of a given string
* @param s The string to measure the length of
* @return The length of the input string
*/
function strlen(string s) internal constant returns (uint) {
// Starting here means the LSB will be the byte we care about
uint ptr;
uint end;
assembly {
ptr := add(s, 1)
end := add(mload(s), ptr)
}
for (uint len = 0; ptr < end; len++) {
uint8 b;
assembly { b := and(mload(ptr), 0xFF) }
if (b < 0x80) {
ptr += 1;
} else if(b < 0xE0) {
ptr += 2;
} else if(b < 0xF0) {
ptr += 3;
} else if(b < 0xF8) {
ptr += 4;
} else if(b < 0xFC) {
ptr += 5;
} else {
ptr += 6;
}
}
return len;
}
/**
* @dev Determines if a name is available for registration yet
*
* Each name will be assigned a random date in which its auction
* can be started, from 0 to 13 weeks
*
* @param _hash The hash to start an auction on
* @param _timestamp The timestamp to query about
*/
function isAllowed(bytes32 _hash, uint _timestamp) constant returns (bool allowed){
return _timestamp > getAllowedTime(_hash);
}
/**
* @dev Returns available date for hash
*
* @param _hash The hash to start an auction on
*/
function getAllowedTime(bytes32 _hash) constant returns (uint timestamp) {
return registryStarted + (launchLength*(uint(_hash)>>128)>>128);
// right shift operator: a >> b == a / 2**b
}
/**
* @dev Assign the owner in ENS, if we're still the registrar
* @param _hash hash to change owner
* @param _newOwner new owner to transfer to
*/
function trySetSubnodeOwner(bytes32 _hash, address _newOwner) internal {
if(ens.owner(rootNode) == address(this))
ens.setSubnodeOwner(rootNode, _hash, _newOwner);
}
/**
* @dev Start an auction for an available hash
*
* Anyone can start an auction by sending an array of hashes that they want to bid for.
* Arrays are sent so that someone can open up an auction for X dummy hashes when they
* are only really interested in bidding for one. This will increase the cost for an
* attacker to simply bid blindly on all new auctions. Dummy auctions that are
* open but not bid on are closed after a week.
*
* @param _hash The hash to start an auction on
*/
function startAuction(bytes32 _hash) registryOpen() {
var mode = state(_hash);
if(mode == Mode.Auction) return;
if(mode != Mode.Open) throw;
entry newAuction = _entries[_hash];
newAuction.registrationDate = now + totalAuctionLength;
newAuction.value = 0;
newAuction.highestBid = 0;
AuctionStarted(_hash, newAuction.registrationDate);
}
/**
* @dev Start multiple auctions for better anonymity
* @param _hashes An array of hashes, at least one of which you presumably want to bid on
*/
function startAuctions(bytes32[] _hashes) {
for (uint i = 0; i < _hashes.length; i ++ ) {
startAuction(_hashes[i]);
}
}
/**
* @dev Hash the values required for a secret bid
* @param hash The node corresponding to the desired namehash
* @param value The bid amount
* @param salt A random value to ensure secrecy of the bid
* @return The hash of the bid values
*/
function shaBid(bytes32 hash, address owner, uint value, bytes32 salt) constant returns (bytes32 sealedBid) {
return sha3(hash, owner, value, salt);
}
/**
* @dev Submit a new sealed bid on a desired hash in a blind auction
*
* Bids are sent by sending a message to the main contract with a hash and an amount. The hash
* contains information about the bid, including the bidded hash, the bid amount, and a random
* salt. Bids are not tied to any one auction until they are revealed. The value of the bid
* itself can be masqueraded by sending more than the value of your actual bid. This is
* followed by a 48h reveal period. Bids revealed after this period will be burned and the ether unrecoverable.
* Since this is an auction, it is expected that most public hashes, like known domains and common dictionary
* words, will have multiple bidders pushing the price up.
*
* @param sealedBid A sealedBid, created by the shaBid function
*/
function newBid(bytes32 sealedBid) payable {
if (address(sealedBids[msg.sender][sealedBid]) > 0 ) throw;
if (msg.value < minPrice) throw;
// creates a new hash contract with the owner
Deed newBid = (new Deed).value(msg.value)(msg.sender);
sealedBids[msg.sender][sealedBid] = newBid;
NewBid(sealedBid, msg.sender, msg.value);
}
/**
* @dev Start a set of auctions and bid on one of them
*
* This method functions identically to calling `startAuctions` followed by `newBid`,
* but all in one transaction.
* @param hashes A list of hashes to start auctions on.
* @param sealedBid A sealed bid for one of the auctions.
*/
function startAuctionsAndBid(bytes32[] hashes, bytes32 sealedBid) payable {
startAuctions(hashes);
newBid(sealedBid);
}
/**
* @dev Submit the properties of a bid to reveal them
* @param _hash The node in the sealedBid
* @param _value The bid amount in the sealedBid
* @param _salt The sale in the sealedBid
*/
function unsealBid(bytes32 _hash, uint _value, bytes32 _salt) {
bytes32 seal = shaBid(_hash, msg.sender, _value, _salt);
Deed bid = sealedBids[msg.sender][seal];
if (address(bid) == 0 ) throw;
sealedBids[msg.sender][seal] = Deed(0);
entry h = _entries[_hash];
uint value = min(_value, bid.value());
bid.setBalance(value, true);
var auctionState = state(_hash);
if(auctionState == Mode.Owned) {
// Too late! Bidder loses their bid. Get's 0.5% back.
bid.closeDeed(5);
BidRevealed(_hash, msg.sender, value, 1);
} else if(auctionState != Mode.Reveal) {
// Invalid phase
throw;
} else if (value < minPrice || bid.creationDate() > h.registrationDate - revealPeriod) {
// Bid too low or too late, refund 99.5%
bid.closeDeed(995);
BidRevealed(_hash, msg.sender, value, 0);
} else if (value > h.highestBid) {
// new winner
// cancel the other bid, refund 99.5%
if(address(h.deed) != 0) {
Deed previousWinner = h.deed;
previousWinner.closeDeed(995);
}
// set new winner
// per the rules of a vickery auction, the value becomes the previous highestBid
h.value = h.highestBid; // will be zero if there's only 1 bidder
h.highestBid = value;
h.deed = bid;
BidRevealed(_hash, msg.sender, value, 2);
} else if (value > h.value) {
// not winner, but affects second place
h.value = value;
bid.closeDeed(995);
BidRevealed(_hash, msg.sender, value, 3);
} else {
// bid doesn't affect auction
bid.closeDeed(995);
BidRevealed(_hash, msg.sender, value, 4);
}
}
/**
* @dev Cancel a bid
* @param seal The value returned by the shaBid function
*/
function cancelBid(address bidder, bytes32 seal) {
Deed bid = sealedBids[bidder][seal];
// If a sole bidder does not `unsealBid` in time, they have a few more days
// where they can call `startAuction` (again) and then `unsealBid` during
// the revealPeriod to get back their bid value.
// For simplicity, they should call `startAuction` within
// 9 days (2 weeks - totalAuctionLength), otherwise their bid will be
// cancellable by anyone.
if (address(bid) == 0
|| now < bid.creationDate() + totalAuctionLength + 2 weeks) throw;
// Send the canceller 0.5% of the bid, and burn the rest.
bid.setOwner(msg.sender);
bid.closeDeed(5);
sealedBids[bidder][seal] = Deed(0);
BidRevealed(seal, bidder, 0, 5);
}
/**
* @dev Finalize an auction after the registration date has passed
* @param _hash The hash of the name the auction is for
*/
function finalizeAuction(bytes32 _hash) onlyOwner(_hash) {
entry h = _entries[_hash];
// handles the case when there's only a single bidder (h.value is zero)
h.value = max(h.value, minPrice);
h.deed.setBalance(h.value, true);
trySetSubnodeOwner(_hash, h.deed.owner());
HashRegistered(_hash, h.deed.owner(), h.value, h.registrationDate);
}
/**
* @dev The owner of a domain may transfer it to someone else at any time.
* @param _hash The node to transfer
* @param newOwner The address to transfer ownership to
*/
function transfer(bytes32 _hash, address newOwner) onlyOwner(_hash) {
if (newOwner == 0) throw;
entry h = _entries[_hash];
h.deed.setOwner(newOwner);
trySetSubnodeOwner(_hash, newOwner);
}
/**
* @dev After some time, or if we're no longer the registrar, the owner can release
* the name and get their ether back.
* @param _hash The node to release
*/
function releaseDeed(bytes32 _hash) onlyOwner(_hash) {
entry h = _entries[_hash];
Deed deedContract = h.deed;
if(now < h.registrationDate + 1 years && ens.owner(rootNode) == address(this)) throw;
h.value = 0;
h.highestBid = 0;
h.deed = Deed(0);
_tryEraseSingleNode(_hash);
deedContract.closeDeed(1000);
HashReleased(_hash, h.value);
}
/**
* @dev Submit a name 6 characters long or less. If it has been registered,
* the submitter will earn 50% of the deed value. We are purposefully
* handicapping the simplified registrar as a way to force it into being restructured
* in a few years.
* @param unhashedName An invalid name to search for in the registry.
*
*/
function invalidateName(string unhashedName) inState(sha3(unhashedName), Mode.Owned) {
if (strlen(unhashedName) > 6 ) throw;
bytes32 hash = sha3(unhashedName);
entry h = _entries[hash];
_tryEraseSingleNode(hash);
if(address(h.deed) != 0) {
// Reward the discoverer with 50% of the deed
// The previous owner gets 50%
h.value = max(h.value, minPrice);
h.deed.setBalance(h.value/2, false);
h.deed.setOwner(msg.sender);
h.deed.closeDeed(1000);
}
HashInvalidated(hash, unhashedName, h.value, h.registrationDate);
h.value = 0;
h.highestBid = 0;
h.deed = Deed(0);
}
/**
* @dev Allows anyone to delete the owner and resolver records for a (subdomain of) a
* name that is not currently owned in the registrar. If passing, eg, 'foo.bar.eth',
* the owner and resolver fields on 'foo.bar.eth' and 'bar.eth' will all be cleared.
* @param labels A series of label hashes identifying the name to zero out, rooted at the
* registrar's root. Must contain at least one element. For instance, to zero
* 'foo.bar.eth' on a registrar that owns '.eth', pass an array containing
* [sha3('foo'), sha3('bar')].
*/
function eraseNode(bytes32[] labels) {
if(labels.length == 0) throw;
if(state(labels[labels.length - 1]) == Mode.Owned) throw;
_eraseNodeHierarchy(labels.length - 1, labels, rootNode);
}
function _tryEraseSingleNode(bytes32 label) internal {
if(ens.owner(rootNode) == address(this)) {
ens.setSubnodeOwner(rootNode, label, address(this));
var node = sha3(rootNode, label);
ens.setResolver(node, 0);
ens.setOwner(node, 0);
}
}
function _eraseNodeHierarchy(uint idx, bytes32[] labels, bytes32 node) internal {
// Take ownership of the node
ens.setSubnodeOwner(node, labels[idx], address(this));
node = sha3(node, labels[idx]);
// Recurse if there's more labels
if(idx > 0)
_eraseNodeHierarchy(idx - 1, labels, node);
// Erase the resolver and owner records
ens.setResolver(node, 0);
ens.setOwner(node, 0);
}
/**
* @dev Transfers the deed to the current registrar, if different from this one.
* Used during the upgrade process to a permanent registrar.
* @param _hash The name hash to transfer.
*/
function transferRegistrars(bytes32 _hash) onlyOwner(_hash) {
var registrar = ens.owner(rootNode);
if(registrar == address(this))
throw;
// Migrate the deed
entry h = _entries[_hash];
h.deed.setRegistrar(registrar);
// Call the new registrar to accept the transfer
Registrar(registrar).acceptRegistrarTransfer(_hash, h.deed, h.registrationDate);
// Zero out the entry
h.deed = Deed(0);
h.registrationDate = 0;
h.value = 0;
h.highestBid = 0;
}
/**
* @dev Accepts a transfer from a previous registrar; stubbed out here since there
* is no previous registrar implementing this interface.
* @param hash The sha3 hash of the label to transfer.
* @param deed The Deed object for the name being transferred in.
* @param registrationDate The date at which the name was originally registered.
*/
function acceptRegistrarTransfer(bytes32 hash, Deed deed, uint registrationDate) {}
}File 2 of 3: BaseRegistrarImplementation
// File: @ensdomains/ens/contracts/ENS.sol
pragma solidity >=0.4.24;
interface ENS {
// Logged when the owner of a node assigns a new owner to a subnode.
event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
// Logged when the owner of a node transfers ownership to a new account.
event Transfer(bytes32 indexed node, address owner);
// Logged when the resolver for a node changes.
event NewResolver(bytes32 indexed node, address resolver);
// Logged when the TTL of a node changes
event NewTTL(bytes32 indexed node, uint64 ttl);
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external;
function setResolver(bytes32 node, address resolver) external;
function setOwner(bytes32 node, address owner) external;
function setTTL(bytes32 node, uint64 ttl) external;
function owner(bytes32 node) external view returns (address);
function resolver(bytes32 node) external view returns (address);
function ttl(bytes32 node) external view returns (uint64);
}
// File: @ensdomains/ens/contracts/Deed.sol
pragma solidity >=0.4.24;
interface Deed {
function setOwner(address payable newOwner) external;
function setRegistrar(address newRegistrar) external;
function setBalance(uint newValue, bool throwOnFailure) external;
function closeDeed(uint refundRatio) external;
function destroyDeed() external;
function owner() external view returns (address);
function previousOwner() external view returns (address);
function value() external view returns (uint);
function creationDate() external view returns (uint);
}
// File: @ensdomains/ens/contracts/Registrar.sol
pragma solidity >=0.4.24;
interface Registrar {
enum Mode { Open, Auction, Owned, Forbidden, Reveal, NotYetAvailable }
event AuctionStarted(bytes32 indexed hash, uint registrationDate);
event NewBid(bytes32 indexed hash, address indexed bidder, uint deposit);
event BidRevealed(bytes32 indexed hash, address indexed owner, uint value, uint8 status);
event HashRegistered(bytes32 indexed hash, address indexed owner, uint value, uint registrationDate);
event HashReleased(bytes32 indexed hash, uint value);
event HashInvalidated(bytes32 indexed hash, string indexed name, uint value, uint registrationDate);
function startAuction(bytes32 _hash) external;
function startAuctions(bytes32[] calldata _hashes) external;
function newBid(bytes32 sealedBid) external payable;
function startAuctionsAndBid(bytes32[] calldata hashes, bytes32 sealedBid) external payable;
function unsealBid(bytes32 _hash, uint _value, bytes32 _salt) external;
function cancelBid(address bidder, bytes32 seal) external;
function finalizeAuction(bytes32 _hash) external;
function transfer(bytes32 _hash, address payable newOwner) external;
function releaseDeed(bytes32 _hash) external;
function invalidateName(string calldata unhashedName) external;
function eraseNode(bytes32[] calldata labels) external;
function transferRegistrars(bytes32 _hash) external;
function acceptRegistrarTransfer(bytes32 hash, Deed deed, uint registrationDate) external;
function entries(bytes32 _hash) external view returns (Mode, address, uint, uint, uint);
}
// File: @ensdomains/ens/contracts/DeedImplementation.sol
pragma solidity ^0.5.0;
/**
* @title Deed to hold ether in exchange for ownership of a node
* @dev The deed can be controlled only by the registrar and can only send ether back to the owner.
*/
contract DeedImplementation is Deed {
address payable constant burn = address(0xdead);
address payable private _owner;
address private _previousOwner;
address private _registrar;
uint private _creationDate;
uint private _value;
bool active;
event OwnerChanged(address newOwner);
event DeedClosed();
modifier onlyRegistrar {
require(msg.sender == _registrar);
_;
}
modifier onlyActive {
require(active);
_;
}
constructor(address payable initialOwner) public payable {
_owner = initialOwner;
_registrar = msg.sender;
_creationDate = now;
active = true;
_value = msg.value;
}
function setOwner(address payable newOwner) external onlyRegistrar {
require(newOwner != address(0x0));
_previousOwner = _owner; // This allows contracts to check who sent them the ownership
_owner = newOwner;
emit OwnerChanged(newOwner);
}
function setRegistrar(address newRegistrar) external onlyRegistrar {
_registrar = newRegistrar;
}
function setBalance(uint newValue, bool throwOnFailure) external onlyRegistrar onlyActive {
// Check if it has enough balance to set the value
require(_value >= newValue);
_value = newValue;
// Send the difference to the owner
require(_owner.send(address(this).balance - newValue) || !throwOnFailure);
}
/**
* @dev Close a deed and refund a specified fraction of the bid value
*
* @param refundRatio The amount*1/1000 to refund
*/
function closeDeed(uint refundRatio) external onlyRegistrar onlyActive {
active = false;
require(burn.send(((1000 - refundRatio) * address(this).balance)/1000));
emit DeedClosed();
_destroyDeed();
}
/**
* @dev Close a deed and refund a specified fraction of the bid value
*/
function destroyDeed() external {
_destroyDeed();
}
function owner() external view returns (address) {
return _owner;
}
function previousOwner() external view returns (address) {
return _previousOwner;
}
function value() external view returns (uint) {
return _value;
}
function creationDate() external view returns (uint) {
_creationDate;
}
function _destroyDeed() internal {
require(!active);
// Instead of selfdestruct(owner), invoke owner fallback function to allow
// owner to log an event if desired; but owner should also be aware that
// its fallback function can also be invoked by setBalance
if (_owner.send(address(this).balance)) {
selfdestruct(burn);
}
}
}
// File: @ensdomains/ens/contracts/HashRegistrar.sol
pragma solidity ^0.5.0;
/*
Temporary Hash Registrar
========================
This is a simplified version of a hash registrar. It is purporsefully limited:
names cannot be six letters or shorter, new auctions will stop after 4 years.
The plan is to test the basic features and then move to a new contract in at most
2 years, when some sort of renewal mechanism will be enabled.
*/
/**
* @title Registrar
* @dev The registrar handles the auction process for each subnode of the node it owns.
*/
contract HashRegistrar is Registrar {
ENS public ens;
bytes32 public rootNode;
mapping (bytes32 => Entry) _entries;
mapping (address => mapping (bytes32 => Deed)) public sealedBids;
uint32 constant totalAuctionLength = 5 days;
uint32 constant revealPeriod = 48 hours;
uint32 public constant launchLength = 8 weeks;
uint constant minPrice = 0.01 ether;
uint public registryStarted;
struct Entry {
Deed deed;
uint registrationDate;
uint value;
uint highestBid;
}
modifier inState(bytes32 _hash, Mode _state) {
require(state(_hash) == _state);
_;
}
modifier onlyOwner(bytes32 _hash) {
require(state(_hash) == Mode.Owned && msg.sender == _entries[_hash].deed.owner());
_;
}
modifier registryOpen() {
require(now >= registryStarted && now <= registryStarted + (365 * 4) * 1 days && ens.owner(rootNode) == address(this));
_;
}
/**
* @dev Constructs a new Registrar, with the provided address as the owner of the root node.
*
* @param _ens The address of the ENS
* @param _rootNode The hash of the rootnode.
*/
constructor(ENS _ens, bytes32 _rootNode, uint _startDate) public {
ens = _ens;
rootNode = _rootNode;
registryStarted = _startDate > 0 ? _startDate : now;
}
/**
* @dev Start an auction for an available hash
*
* @param _hash The hash to start an auction on
*/
function startAuction(bytes32 _hash) external {
_startAuction(_hash);
}
/**
* @dev Start multiple auctions for better anonymity
*
* Anyone can start an auction by sending an array of hashes that they want to bid for.
* Arrays are sent so that someone can open up an auction for X dummy hashes when they
* are only really interested in bidding for one. This will increase the cost for an
* attacker to simply bid blindly on all new auctions. Dummy auctions that are
* open but not bid on are closed after a week.
*
* @param _hashes An array of hashes, at least one of which you presumably want to bid on
*/
function startAuctions(bytes32[] calldata _hashes) external {
_startAuctions(_hashes);
}
/**
* @dev Submit a new sealed bid on a desired hash in a blind auction
*
* Bids are sent by sending a message to the main contract with a hash and an amount. The hash
* contains information about the bid, including the bidded hash, the bid amount, and a random
* salt. Bids are not tied to any one auction until they are revealed. The value of the bid
* itself can be masqueraded by sending more than the value of your actual bid. This is
* followed by a 48h reveal period. Bids revealed after this period will be burned and the ether unrecoverable.
* Since this is an auction, it is expected that most public hashes, like known domains and common dictionary
* words, will have multiple bidders pushing the price up.
*
* @param sealedBid A sealedBid, created by the shaBid function
*/
function newBid(bytes32 sealedBid) external payable {
_newBid(sealedBid);
}
/**
* @dev Start a set of auctions and bid on one of them
*
* This method functions identically to calling `startAuctions` followed by `newBid`,
* but all in one transaction.
*
* @param hashes A list of hashes to start auctions on.
* @param sealedBid A sealed bid for one of the auctions.
*/
function startAuctionsAndBid(bytes32[] calldata hashes, bytes32 sealedBid) external payable {
_startAuctions(hashes);
_newBid(sealedBid);
}
/**
* @dev Submit the properties of a bid to reveal them
*
* @param _hash The node in the sealedBid
* @param _value The bid amount in the sealedBid
* @param _salt The sale in the sealedBid
*/
function unsealBid(bytes32 _hash, uint _value, bytes32 _salt) external {
bytes32 seal = shaBid(_hash, msg.sender, _value, _salt);
Deed bid = sealedBids[msg.sender][seal];
require(address(bid) != address(0x0));
sealedBids[msg.sender][seal] = Deed(address(0x0));
Entry storage h = _entries[_hash];
uint value = min(_value, bid.value());
bid.setBalance(value, true);
Mode auctionState = state(_hash);
if (auctionState == Mode.Owned) {
// Too late! Bidder loses their bid. Gets 0.5% back.
bid.closeDeed(5);
emit BidRevealed(_hash, msg.sender, value, 1);
} else if (auctionState != Mode.Reveal) {
// Invalid phase
revert();
} else if (value < minPrice || bid.creationDate() > h.registrationDate - revealPeriod) {
// Bid too low or too late, refund 99.5%
bid.closeDeed(995);
emit BidRevealed(_hash, msg.sender, value, 0);
} else if (value > h.highestBid) {
// New winner
// Cancel the other bid, refund 99.5%
if (address(h.deed) != address(0x0)) {
Deed previousWinner = h.deed;
previousWinner.closeDeed(995);
}
// Set new winner
// Per the rules of a vickery auction, the value becomes the previous highestBid
h.value = h.highestBid; // will be zero if there's only 1 bidder
h.highestBid = value;
h.deed = bid;
emit BidRevealed(_hash, msg.sender, value, 2);
} else if (value > h.value) {
// Not winner, but affects second place
h.value = value;
bid.closeDeed(995);
emit BidRevealed(_hash, msg.sender, value, 3);
} else {
// Bid doesn't affect auction
bid.closeDeed(995);
emit BidRevealed(_hash, msg.sender, value, 4);
}
}
/**
* @dev Cancel a bid
*
* @param seal The value returned by the shaBid function
*/
function cancelBid(address bidder, bytes32 seal) external {
Deed bid = sealedBids[bidder][seal];
// If a sole bidder does not `unsealBid` in time, they have a few more days
// where they can call `startAuction` (again) and then `unsealBid` during
// the revealPeriod to get back their bid value.
// For simplicity, they should call `startAuction` within
// 9 days (2 weeks - totalAuctionLength), otherwise their bid will be
// cancellable by anyone.
require(address(bid) != address(0x0) && now >= bid.creationDate() + totalAuctionLength + 2 weeks);
// Send the canceller 0.5% of the bid, and burn the rest.
bid.setOwner(msg.sender);
bid.closeDeed(5);
sealedBids[bidder][seal] = Deed(0);
emit BidRevealed(seal, bidder, 0, 5);
}
/**
* @dev Finalize an auction after the registration date has passed
*
* @param _hash The hash of the name the auction is for
*/
function finalizeAuction(bytes32 _hash) external onlyOwner(_hash) {
Entry storage h = _entries[_hash];
// Handles the case when there's only a single bidder (h.value is zero)
h.value = max(h.value, minPrice);
h.deed.setBalance(h.value, true);
trySetSubnodeOwner(_hash, h.deed.owner());
emit HashRegistered(_hash, h.deed.owner(), h.value, h.registrationDate);
}
/**
* @dev The owner of a domain may transfer it to someone else at any time.
*
* @param _hash The node to transfer
* @param newOwner The address to transfer ownership to
*/
function transfer(bytes32 _hash, address payable newOwner) external onlyOwner(_hash) {
require(newOwner != address(0x0));
Entry storage h = _entries[_hash];
h.deed.setOwner(newOwner);
trySetSubnodeOwner(_hash, newOwner);
}
/**
* @dev After some time, or if we're no longer the registrar, the owner can release
* the name and get their ether back.
*
* @param _hash The node to release
*/
function releaseDeed(bytes32 _hash) external onlyOwner(_hash) {
Entry storage h = _entries[_hash];
Deed deedContract = h.deed;
require(now >= h.registrationDate + 365 days || ens.owner(rootNode) != address(this));
h.value = 0;
h.highestBid = 0;
h.deed = Deed(0);
_tryEraseSingleNode(_hash);
deedContract.closeDeed(1000);
emit HashReleased(_hash, h.value);
}
/**
* @dev Submit a name 6 characters long or less. If it has been registered,
* the submitter will earn 50% of the deed value.
*
* We are purposefully handicapping the simplified registrar as a way
* to force it into being restructured in a few years.
*
* @param unhashedName An invalid name to search for in the registry.
*/
function invalidateName(string calldata unhashedName)
external
inState(keccak256(abi.encode(unhashedName)), Mode.Owned)
{
require(strlen(unhashedName) <= 6);
bytes32 hash = keccak256(abi.encode(unhashedName));
Entry storage h = _entries[hash];
_tryEraseSingleNode(hash);
if (address(h.deed) != address(0x0)) {
// Reward the discoverer with 50% of the deed
// The previous owner gets 50%
h.value = max(h.value, minPrice);
h.deed.setBalance(h.value/2, false);
h.deed.setOwner(msg.sender);
h.deed.closeDeed(1000);
}
emit HashInvalidated(hash, unhashedName, h.value, h.registrationDate);
h.value = 0;
h.highestBid = 0;
h.deed = Deed(0);
}
/**
* @dev Allows anyone to delete the owner and resolver records for a (subdomain of) a
* name that is not currently owned in the registrar. If passing, eg, 'foo.bar.eth',
* the owner and resolver fields on 'foo.bar.eth' and 'bar.eth' will all be cleared.
*
* @param labels A series of label hashes identifying the name to zero out, rooted at the
* registrar's root. Must contain at least one element. For instance, to zero
* 'foo.bar.eth' on a registrar that owns '.eth', pass an array containing
* [keccak256('foo'), keccak256('bar')].
*/
function eraseNode(bytes32[] calldata labels) external {
require(labels.length != 0);
require(state(labels[labels.length - 1]) != Mode.Owned);
_eraseNodeHierarchy(labels.length - 1, labels, rootNode);
}
/**
* @dev Transfers the deed to the current registrar, if different from this one.
*
* Used during the upgrade process to a permanent registrar.
*
* @param _hash The name hash to transfer.
*/
function transferRegistrars(bytes32 _hash) external onlyOwner(_hash) {
address registrar = ens.owner(rootNode);
require(registrar != address(this));
// Migrate the deed
Entry storage h = _entries[_hash];
h.deed.setRegistrar(registrar);
// Call the new registrar to accept the transfer
Registrar(registrar).acceptRegistrarTransfer(_hash, h.deed, h.registrationDate);
// Zero out the Entry
h.deed = Deed(0);
h.registrationDate = 0;
h.value = 0;
h.highestBid = 0;
}
/**
* @dev Accepts a transfer from a previous registrar; stubbed out here since there
* is no previous registrar implementing this interface.
*
* @param hash The sha3 hash of the label to transfer.
* @param deed The Deed object for the name being transferred in.
* @param registrationDate The date at which the name was originally registered.
*/
function acceptRegistrarTransfer(bytes32 hash, Deed deed, uint registrationDate) external {
hash; deed; registrationDate; // Don't warn about unused variables
}
function entries(bytes32 _hash) external view returns (Mode, address, uint, uint, uint) {
Entry storage h = _entries[_hash];
return (state(_hash), address(h.deed), h.registrationDate, h.value, h.highestBid);
}
// State transitions for names:
// Open -> Auction (startAuction)
// Auction -> Reveal
// Reveal -> Owned
// Reveal -> Open (if nobody bid)
// Owned -> Open (releaseDeed or invalidateName)
function state(bytes32 _hash) public view returns (Mode) {
Entry storage entry = _entries[_hash];
if (!isAllowed(_hash, now)) {
return Mode.NotYetAvailable;
} else if (now < entry.registrationDate) {
if (now < entry.registrationDate - revealPeriod) {
return Mode.Auction;
} else {
return Mode.Reveal;
}
} else {
if (entry.highestBid == 0) {
return Mode.Open;
} else {
return Mode.Owned;
}
}
}
/**
* @dev Determines if a name is available for registration yet
*
* Each name will be assigned a random date in which its auction
* can be started, from 0 to 8 weeks
*
* @param _hash The hash to start an auction on
* @param _timestamp The timestamp to query about
*/
function isAllowed(bytes32 _hash, uint _timestamp) public view returns (bool allowed) {
return _timestamp > getAllowedTime(_hash);
}
/**
* @dev Returns available date for hash
*
* The available time from the `registryStarted` for a hash is proportional
* to its numeric value.
*
* @param _hash The hash to start an auction on
*/
function getAllowedTime(bytes32 _hash) public view returns (uint) {
return registryStarted + ((launchLength * (uint(_hash) >> 128)) >> 128);
// Right shift operator: a >> b == a / 2**b
}
/**
* @dev Hash the values required for a secret bid
*
* @param hash The node corresponding to the desired namehash
* @param value The bid amount
* @param salt A random value to ensure secrecy of the bid
* @return The hash of the bid values
*/
function shaBid(bytes32 hash, address owner, uint value, bytes32 salt) public pure returns (bytes32) {
return keccak256(abi.encodePacked(hash, owner, value, salt));
}
function _tryEraseSingleNode(bytes32 label) internal {
if (ens.owner(rootNode) == address(this)) {
ens.setSubnodeOwner(rootNode, label, address(this));
bytes32 node = keccak256(abi.encodePacked(rootNode, label));
ens.setResolver(node, address(0x0));
ens.setOwner(node, address(0x0));
}
}
function _startAuction(bytes32 _hash) internal registryOpen() {
Mode mode = state(_hash);
if (mode == Mode.Auction) return;
require(mode == Mode.Open);
Entry storage newAuction = _entries[_hash];
newAuction.registrationDate = now + totalAuctionLength;
newAuction.value = 0;
newAuction.highestBid = 0;
emit AuctionStarted(_hash, newAuction.registrationDate);
}
function _startAuctions(bytes32[] memory _hashes) internal {
for (uint i = 0; i < _hashes.length; i ++) {
_startAuction(_hashes[i]);
}
}
function _newBid(bytes32 sealedBid) internal {
require(address(sealedBids[msg.sender][sealedBid]) == address(0x0));
require(msg.value >= minPrice);
// Creates a new hash contract with the owner
Deed bid = (new DeedImplementation).value(msg.value)(msg.sender);
sealedBids[msg.sender][sealedBid] = bid;
emit NewBid(sealedBid, msg.sender, msg.value);
}
function _eraseNodeHierarchy(uint idx, bytes32[] memory labels, bytes32 node) internal {
// Take ownership of the node
ens.setSubnodeOwner(node, labels[idx], address(this));
node = keccak256(abi.encodePacked(node, labels[idx]));
// Recurse if there are more labels
if (idx > 0) {
_eraseNodeHierarchy(idx - 1, labels, node);
}
// Erase the resolver and owner records
ens.setResolver(node, address(0x0));
ens.setOwner(node, address(0x0));
}
/**
* @dev Assign the owner in ENS, if we're still the registrar
*
* @param _hash hash to change owner
* @param _newOwner new owner to transfer to
*/
function trySetSubnodeOwner(bytes32 _hash, address _newOwner) internal {
if (ens.owner(rootNode) == address(this))
ens.setSubnodeOwner(rootNode, _hash, _newOwner);
}
/**
* @dev Returns the maximum of two unsigned integers
*
* @param a A number to compare
* @param b A number to compare
* @return The maximum of two unsigned integers
*/
function max(uint a, uint b) internal pure returns (uint) {
if (a > b)
return a;
else
return b;
}
/**
* @dev Returns the minimum of two unsigned integers
*
* @param a A number to compare
* @param b A number to compare
* @return The minimum of two unsigned integers
*/
function min(uint a, uint b) internal pure returns (uint) {
if (a < b)
return a;
else
return b;
}
/**
* @dev Returns the length of a given string
*
* @param s The string to measure the length of
* @return The length of the input string
*/
function strlen(string memory s) internal pure returns (uint) {
s; // Don't warn about unused variables
// Starting here means the LSB will be the byte we care about
uint ptr;
uint end;
assembly {
ptr := add(s, 1)
end := add(mload(s), ptr)
}
uint len = 0;
for (len; ptr < end; len++) {
uint8 b;
assembly { b := and(mload(ptr), 0xFF) }
if (b < 0x80) {
ptr += 1;
} else if (b < 0xE0) {
ptr += 2;
} else if (b < 0xF0) {
ptr += 3;
} else if (b < 0xF8) {
ptr += 4;
} else if (b < 0xFC) {
ptr += 5;
} else {
ptr += 6;
}
}
return len;
}
}
// File: openzeppelin-solidity/contracts/introspection/IERC165.sol
pragma solidity ^0.5.0;
/**
* @title IERC165
* @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
*/
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);
}
// File: openzeppelin-solidity/contracts/token/ERC721/IERC721.sol
pragma solidity ^0.5.0;
/**
* @title ERC721 Non-Fungible Token Standard basic interface
* @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contract IERC721 is IERC165 {
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) public view returns (uint256 balance);
function ownerOf(uint256 tokenId) public view returns (address owner);
function approve(address to, uint256 tokenId) public;
function getApproved(uint256 tokenId) public view returns (address operator);
function setApprovalForAll(address operator, bool _approved) public;
function isApprovedForAll(address owner, address operator) public view returns (bool);
function transferFrom(address from, address to, uint256 tokenId) public;
function safeTransferFrom(address from, address to, uint256 tokenId) public;
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
}
// File: openzeppelin-solidity/contracts/token/ERC721/IERC721Receiver.sol
pragma solidity ^0.5.0;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
contract 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(keccak256("onERC721Received(address,address,uint256,bytes)"))`
*/
function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
public returns (bytes4);
}
// File: openzeppelin-solidity/contracts/math/SafeMath.sol
pragma solidity ^0.5.0;
/**
* @title SafeMath
* @dev Unsigned math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two unsigned integers, reverts on 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);
return c;
}
/**
* @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two unsigned integers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
// File: openzeppelin-solidity/contracts/utils/Address.sol
pragma solidity ^0.5.0;
/**
* 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;
}
}
// File: openzeppelin-solidity/contracts/introspection/ERC165.sol
pragma solidity ^0.5.0;
/**
* @title ERC165
* @author Matt Condon (@shrugs)
* @dev Implements ERC165 using a lookup table.
*/
contract ERC165 is IERC165 {
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* 0x01ffc9a7 ===
* bytes4(keccak256('supportsInterface(bytes4)'))
*/
/**
* @dev a mapping of interface id to whether or not it's supported
*/
mapping(bytes4 => bool) private _supportedInterfaces;
/**
* @dev A contract implementing SupportsInterfaceWithLookup
* implement ERC165 itself
*/
constructor () internal {
_registerInterface(_INTERFACE_ID_ERC165);
}
/**
* @dev implement supportsInterface(bytes4) using a lookup table
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev internal method for registering an interface
*/
function _registerInterface(bytes4 interfaceId) internal {
require(interfaceId != 0xffffffff);
_supportedInterfaces[interfaceId] = true;
}
}
// File: openzeppelin-solidity/contracts/token/ERC721/ERC721.sol
pragma solidity ^0.5.0;
/**
* @title ERC721 Non-Fungible Token Standard basic implementation
* @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contract ERC721 is ERC165, IERC721 {
using SafeMath for uint256;
using Address for address;
// 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 => uint256) 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];
}
/**
* @dev Gets the owner of the specified token ID
* @param tokenId uint256 ID of the token to query the owner of
* @return owner 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 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] = _ownedTokensCount[to].add(1);
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] = _ownedTokensCount[owner].sub(1);
_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] = _ownedTokensCount[from].sub(1);
_ownedTokensCount[to] = _ownedTokensCount[to].add(1);
_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 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);
}
}
}
// File: openzeppelin-solidity/contracts/ownership/Ownable.sol
pragma solidity ^0.5.0;
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @return the address of the owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
_;
}
/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* @notice Renouncing to ownership will leave the contract without an owner.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
// File: contracts/BaseRegistrar.sol
pragma solidity >=0.4.24;
contract BaseRegistrar is ERC721, Ownable {
uint constant public GRACE_PERIOD = 90 days;
event ControllerAdded(address indexed controller);
event ControllerRemoved(address indexed controller);
event NameMigrated(uint256 indexed id, address indexed owner, uint expires);
event NameRegistered(uint256 indexed id, address indexed owner, uint expires);
event NameRenewed(uint256 indexed id, uint expires);
// Expiration timestamp for migrated domains.
uint public transferPeriodEnds;
// The ENS registry
ENS public ens;
// The namehash of the TLD this registrar owns (eg, .eth)
bytes32 public baseNode;
// The interim registrar
HashRegistrar public previousRegistrar;
// A map of addresses that are authorised to register and renew names.
mapping(address=>bool) public controllers;
// Authorises a controller, who can register and renew domains.
function addController(address controller) external;
// Revoke controller permission for an address.
function removeController(address controller) external;
// Set the resolver for the TLD this registrar manages.
function setResolver(address resolver) external;
// Returns the expiration timestamp of the specified label hash.
function nameExpires(uint256 id) external view returns(uint);
// Returns true iff the specified name is available for registration.
function available(uint256 id) public view returns(bool);
/**
* @dev Register a name.
*/
function register(uint256 id, address owner, uint duration) external returns(uint);
function renew(uint256 id, uint duration) external returns(uint);
/**
* @dev Reclaim ownership of a name in ENS, if you own it in the registrar.
*/
function reclaim(uint256 id, address owner) external;
/**
* @dev Transfers a registration from the initial registrar.
* This function is called by the initial registrar when a user calls `transferRegistrars`.
*/
function acceptRegistrarTransfer(bytes32 label, Deed deed, uint) external;
}
// File: contracts/BaseRegistrarImplementation.sol
pragma solidity ^0.5.0;
contract BaseRegistrarImplementation is BaseRegistrar {
// A map of expiry times
mapping(uint256=>uint) expiries;
uint constant public MIGRATION_LOCK_PERIOD = 28 days;
bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)"));
bytes4 constant private ERC721_ID = bytes4(
keccak256("balanceOf(uint256)") ^
keccak256("ownerOf(uint256)") ^
keccak256("approve(address,uint256)") ^
keccak256("getApproved(uint256)") ^
keccak256("setApprovalForAll(address,bool)") ^
keccak256("isApprovedForAll(address,address)") ^
keccak256("transferFrom(address,address,uint256)") ^
keccak256("safeTransferFrom(address,address,uint256)") ^
keccak256("safeTransferFrom(address,address,uint256,bytes)")
);
bytes4 constant private RECLAIM_ID = bytes4(keccak256("reclaim(uint256,address)"));
constructor(ENS _ens, HashRegistrar _previousRegistrar, bytes32 _baseNode, uint _transferPeriodEnds) public {
// Require that people have time to transfer names over.
require(_transferPeriodEnds > now + 2 * MIGRATION_LOCK_PERIOD);
ens = _ens;
baseNode = _baseNode;
previousRegistrar = _previousRegistrar;
transferPeriodEnds = _transferPeriodEnds;
}
modifier live {
require(ens.owner(baseNode) == address(this));
_;
}
modifier onlyController {
require(controllers[msg.sender]);
_;
}
/**
* @dev Gets the owner of the specified token ID. Names become unowned
* when their registration expires.
* @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) {
require(expiries[tokenId] > now);
return super.ownerOf(tokenId);
}
// Authorises a controller, who can register and renew domains.
function addController(address controller) external onlyOwner {
controllers[controller] = true;
emit ControllerAdded(controller);
}
// Revoke controller permission for an address.
function removeController(address controller) external onlyOwner {
controllers[controller] = false;
emit ControllerRemoved(controller);
}
// Set the resolver for the TLD this registrar manages.
function setResolver(address resolver) external onlyOwner {
ens.setResolver(baseNode, resolver);
}
// Returns the expiration timestamp of the specified id.
function nameExpires(uint256 id) external view returns(uint) {
return expiries[id];
}
// Returns true iff the specified name is available for registration.
function available(uint256 id) public view returns(bool) {
// Not available if it's registered here or in its grace period.
if(expiries[id] + GRACE_PERIOD >= now) {
return false;
}
// Available if we're past the transfer period, or the name isn't
// registered in the legacy registrar.
return now > transferPeriodEnds || previousRegistrar.state(bytes32(id)) == Registrar.Mode.Open;
}
/**
* @dev Register a name.
*/
function register(uint256 id, address owner, uint duration) external live onlyController returns(uint) {
require(available(id));
require(now + duration + GRACE_PERIOD > now + GRACE_PERIOD); // Prevent future overflow
expiries[id] = now + duration;
if(_exists(id)) {
// Name was previously owned, and expired
_burn(id);
}
_mint(owner, id);
ens.setSubnodeOwner(baseNode, bytes32(id), owner);
emit NameRegistered(id, owner, now + duration);
return now + duration;
}
function renew(uint256 id, uint duration) external live onlyController returns(uint) {
require(expiries[id] + GRACE_PERIOD >= now); // Name must be registered here or in grace period
require(expiries[id] + duration + GRACE_PERIOD > duration + GRACE_PERIOD); // Prevent future overflow
expiries[id] += duration;
emit NameRenewed(id, expiries[id]);
return expiries[id];
}
/**
* @dev Reclaim ownership of a name in ENS, if you own it in the registrar.
*/
function reclaim(uint256 id, address owner) external live {
require(_isApprovedOrOwner(msg.sender, id));
ens.setSubnodeOwner(baseNode, bytes32(id), owner);
}
/**
* @dev Transfers a registration from the initial registrar.
* This function is called by the initial registrar when a user calls `transferRegistrars`.
*/
function acceptRegistrarTransfer(bytes32 label, Deed deed, uint) external live {
uint256 id = uint256(label);
require(msg.sender == address(previousRegistrar));
require(expiries[id] == 0);
require(transferPeriodEnds > now);
uint registrationDate;
(,,registrationDate,,) = previousRegistrar.entries(label);
require(registrationDate < now - MIGRATION_LOCK_PERIOD);
address owner = deed.owner();
// Destroy the deed and transfer the funds back to the registrant.
deed.closeDeed(1000);
// Register the name
expiries[id] = transferPeriodEnds;
_mint(owner, id);
ens.setSubnodeOwner(baseNode, label, owner);
emit NameMigrated(id, owner, transferPeriodEnds);
emit NameRegistered(id, owner, transferPeriodEnds);
}
function supportsInterface(bytes4 interfaceID) external view returns (bool) {
return interfaceID == INTERFACE_META_ID ||
interfaceID == ERC721_ID ||
interfaceID == RECLAIM_ID;
}
}File 3 of 3: ENS
;;; ---------------------------------------------------------------------------
;;; @title The Ethereum Name Service registry.
;;; @author Daniel Ellison <daniel@syrinx.net>
(seq
;; --------------------------------------------------------------------------
;; Constant definitions.
;; Memory layout.
(def 'node-bytes 0x00)
(def 'label-bytes 0x20)
(def 'call-result 0x40)
;; Struct: Record
(def 'resolver 0x00) ; address
(def 'owner 0x20) ; address
(def 'ttl 0x40) ; uint64
;; Precomputed function IDs.
(def 'get-node-owner 0x02571be3) ; owner(bytes32)
(def 'get-node-resolver 0x0178b8bf) ; resolver(bytes32)
(def 'get-node-ttl 0x16a25cbd) ; ttl(bytes32)
(def 'set-node-owner 0x5b0fc9c3) ; setOwner(bytes32,address)
(def 'set-subnode-owner 0x06ab5923) ; setSubnodeOwner(bytes32,bytes32,address)
(def 'set-node-resolver 0x1896f70a) ; setResolver(bytes32,address)
(def 'set-node-ttl 0x14ab9038) ; setTTL(bytes32,uint64)
;; Jumping here causes an EVM error.
(def 'invalid-location 0x02)
;; --------------------------------------------------------------------------
;; @notice Shifts the leftmost 4 bytes of a 32-byte number right by 28 bytes.
;; @param input A 32-byte number.
(def 'shift-right (input)
(div input (exp 2 224)))
;; --------------------------------------------------------------------------
;; @notice Determines whether the supplied function ID matches a known
;; function hash and executes <code-body> if so.
;; @dev The function ID is in the leftmost four bytes of the call data.
;; @param function-hash The four-byte hash of a known function signature.
;; @param code-body The code to run in the case of a match.
(def 'function (function-hash code-body)
(when (= (shift-right (calldataload 0x00)) function-hash)
code-body))
;; --------------------------------------------------------------------------
;; @notice Calculates record location for the node and label passed in.
;; @param node The parent node.
;; @param label The hash of the subnode label.
(def 'get-record (node label)
(seq
(mstore node-bytes node)
(mstore label-bytes label)
(sha3 node-bytes 64)))
;; --------------------------------------------------------------------------
;; @notice Retrieves owner from node record.
;; @param node Get owner of this node.
(def 'get-owner (node)
(sload (+ node owner)))
;; --------------------------------------------------------------------------
;; @notice Stores new owner in node record.
;; @param node Set owner of this node.
;; @param new-owner New owner of this node.
(def 'set-owner (node new-owner)
(sstore (+ node owner) new-owner))
;; --------------------------------------------------------------------------
;; @notice Stores new subnode owner in node record.
;; @param node Set owner of this node.
;; @param label The hash of the label specifying the subnode.
;; @param new-owner New owner of the subnode.
(def 'set-subowner (node label new-owner)
(sstore (+ (get-record node label) owner) new-owner))
;; --------------------------------------------------------------------------
;; @notice Retrieves resolver from node record.
;; @param node Get resolver of this node.
(def 'get-resolver (node)
(sload node))
;; --------------------------------------------------------------------------
;; @notice Stores new resolver in node record.
;; @param node Set resolver of this node.
;; @param new-resolver New resolver for this node.
(def 'set-resolver (node new-resolver)
(sstore node new-resolver))
;; --------------------------------------------------------------------------
;; @notice Retrieves TTL From node record.
;; @param node Get TTL of this node.
(def 'get-ttl (node)
(sload (+ node ttl)))
;; --------------------------------------------------------------------------
;; @notice Stores new TTL in node record.
;; @param node Set TTL of this node.
;; @param new-resolver New TTL for this node.
(def 'set-ttl (node new-ttl)
(sstore (+ node ttl) new-ttl))
;; --------------------------------------------------------------------------
;; @notice Checks that the caller is the node owner.
;; @param node Check owner of this node.
(def 'only-node-owner (node)
(when (!= (caller) (get-owner node))
(jump invalid-location)))
;; --------------------------------------------------------------------------
;; INIT
;; Set the owner of the root node (0x00) to the deploying account.
(set-owner 0x00 (caller))
;; --------------------------------------------------------------------------
;; CODE
(returnlll
(seq
;; ----------------------------------------------------------------------
;; @notice Returns the address of the resolver for the specified node.
;; @dev Signature: resolver(bytes32)
;; @param node Return this node's resolver.
;; @return The associated resolver.
(def 'node (calldataload 0x04))
(function get-node-resolver
(seq
;; Get the node's resolver and save it.
(mstore call-result (get-resolver node))
;; Return result.
(return call-result 32)))
;; ----------------------------------------------------------------------
;; @notice Returns the address that owns the specified node.
;; @dev Signature: owner(bytes32)
;; @param node Return this node's owner.
;; @return The associated address.
(def 'node (calldataload 0x04))
(function get-node-owner
(seq
;; Get the node's owner and save it.
(mstore call-result (get-owner node))
;; Return result.
(return call-result 32)))
;; ----------------------------------------------------------------------
;; @notice Returns the TTL of a node and any records associated with it.
;; @dev Signature: ttl(bytes32)
;; @param node Return this node's TTL.
;; @return The node's TTL.
(def 'node (calldataload 0x04))
(function get-node-ttl
(seq
;; Get the node's TTL and save it.
(mstore call-result (get-ttl node))
;; Return result.
(return call-result 32)))
;; ----------------------------------------------------------------------
;; @notice Transfers ownership of a node to a new address. May only be
;; called by the current owner of the node.
;; @dev Signature: setOwner(bytes32,address)
;; @param node The node to transfer ownership of.
;; @param new-owner The address of the new owner.
(def 'node (calldataload 0x04))
(def 'new-owner (calldataload 0x24))
(function set-node-owner
(seq (only-node-owner node)
;; Transfer ownership by storing passed-in address.
(set-owner node new-owner)
;; Emit an event about the transfer.
;; Transfer(bytes32 indexed node, address owner);
(mstore call-result new-owner)
(log2 call-result 32
(sha3 0x00 (lit 0x00 "Transfer(bytes32,address)")) node)
;; Nothing to return.
(stop)))
;; ----------------------------------------------------------------------
;; @notice Transfers ownership of a subnode to a new address. May only be
;; called by the owner of the parent node.
;; @dev Signature: setSubnodeOwner(bytes32,bytes32,address)
;; @param node The parent node.
;; @param label The hash of the label specifying the subnode.
;; @param new-owner The address of the new owner.
(def 'node (calldataload 0x04))
(def 'label (calldataload 0x24))
(def 'new-owner (calldataload 0x44))
(function set-subnode-owner
(seq (only-node-owner node)
;; Transfer ownership by storing passed-in address.
(set-subowner node label new-owner)
;; Emit an event about the transfer.
;; NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
(mstore call-result new-owner)
(log3 call-result 32
(sha3 0x00 (lit 0x00 "NewOwner(bytes32,bytes32,address)"))
node label)
;; Nothing to return.
(stop)))
;; ----------------------------------------------------------------------
;; @notice Sets the resolver address for the specified node.
;; @dev Signature: setResolver(bytes32,address)
;; @param node The node to update.
;; @param new-resolver The address of the resolver.
(def 'node (calldataload 0x04))
(def 'new-resolver (calldataload 0x24))
(function set-node-resolver
(seq (only-node-owner node)
;; Transfer ownership by storing passed-in address.
(set-resolver node new-resolver)
;; Emit an event about the change of resolver.
;; NewResolver(bytes32 indexed node, address resolver);
(mstore call-result new-resolver)
(log2 call-result 32
(sha3 0x00 (lit 0x00 "NewResolver(bytes32,address)")) node)
;; Nothing to return.
(stop)))
;; ----------------------------------------------------------------------
;; @notice Sets the TTL for the specified node.
;; @dev Signature: setTTL(bytes32,uint64)
;; @param node The node to update.
;; @param ttl The TTL in seconds.
(def 'node (calldataload 0x04))
(def 'new-ttl (calldataload 0x24))
(function set-node-ttl
(seq (only-node-owner node)
;; Set new TTL by storing passed-in time.
(set-ttl node new-ttl)
;; Emit an event about the change of TTL.
;; NewTTL(bytes32 indexed node, uint64 ttl);
(mstore call-result new-ttl)
(log2 call-result 32
(sha3 0x00 (lit 0x00 "NewTTL(bytes32,uint64)")) node)
;; Nothing to return.
(stop)))
;; ----------------------------------------------------------------------
;; @notice Fallback: No functions matched the function ID provided.
(jump invalid-location)))
)