Transaction Hash:
Block:
14800833 at May-18-2022 08:16:40 PM +UTC
Transaction Fee:
0.006885755882052216 ETH
$17.73
Gas Used:
182,108 Gas / 37.811386002 Gwei
Emitted Events:
401 |
ENSRegistryWithFallback.NewOwner( node=5C1EEE8C1557A09E02B06677FACE65FB4793BB77C36E84A980A5BA6849153624, label=EA6B4F2D39E69CB00345AE42B7CCCD50EA2E6B3098AD8F1BC3FAB201685B1671, owner=[Receiver] BaseENSManager )
|
402 |
ENSRegistryWithFallback.NewResolver( node=1F70C573D24CE00CD8A9574BC362B102592C80A90660E7A6BF9232C18C7AF049, resolver=BaseENSResolver )
|
403 |
ENSRegistryWithFallback.Transfer( node=1F70C573D24CE00CD8A9574BC362B102592C80A90660E7A6BF9232C18C7AF049, owner=0xbf7307ED...ba4d48E5f )
|
404 |
BaseENSResolver.AddrChanged( _node=1F70C573D24CE00CD8A9574BC362B102592C80A90660E7A6BF9232C18C7AF049, _addr=0xbf7307ED...ba4d48E5f )
|
405 |
BaseENSResolver.NameChanged( _node=0751E897CCFD95372585313F5E3B1CE9D6992C6CE76D8D7E97EB1D574FD18AA1, _name=blank.loopring.eth )
|
406 |
BaseENSManager.Registered( _wallet=0xbf7307ED...ba4d48E5f, _owner=0x9C0F5D3B...94998a004, _ens=blank.loopring.eth )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x00000000...A6C7d2e1e | (ENS: Registry with Fallback) | ||||
0xEA674fdD...16B898ec8
Miner
| (Ethermine) | 1,389.883210852980439492 Eth | 1,389.883575068980439492 Eth | 0.000364216 | |
0xeDEE915A...a0B2AD9e5 |
6.812401761737758436 Eth
Nonce: 22193
|
6.80551600585570622 Eth
Nonce: 22194
| 0.006885755882052216 | ||
0xF58D55F0...544f9B6a3 |
Execution Trace
BaseENSManager.register( _wallet=0xbf7307EDA6a10771EbCE9250803b225ba4d48E5f, _owner=0x9C0F5D3B3286405e3B40a2F5553f25494998a004, _label=blank, _approval=0xA1B60D79D2A4BF1480EE071DA19ECF13B232E964CE3EB678B37B8BDC9BDD8B464425C080A003B01300423400F4E23422BACB968B4ACB8EDB8A4AF8F6254EFA401B )
-
Null: 0x000...001.f02ec200( )
ENSRegistryWithFallback.owner( node=1F70C573D24CE00CD8A9574BC362B102592C80A90660E7A6BF9232C18C7AF049 ) => ( 0x0000000000000000000000000000000000000000 )
-
ENS.owner( node=1F70C573D24CE00CD8A9574BC362B102592C80A90660E7A6BF9232C18C7AF049 ) => ( 0x0000000000000000000000000000000000000000 )
-
-
ENSRegistryWithFallback.setSubnodeOwner( node=5C1EEE8C1557A09E02B06677FACE65FB4793BB77C36E84A980A5BA6849153624, label=EA6B4F2D39E69CB00345AE42B7CCCD50EA2E6B3098AD8F1BC3FAB201685B1671, owner=0xF61f3C9cEcB8d206DeA1faEd99A693e6d3BAAEf2 ) => ( 1F70C573D24CE00CD8A9574BC362B102592C80A90660E7A6BF9232C18C7AF049 )
-
ENSRegistryWithFallback.setResolver( node=1F70C573D24CE00CD8A9574BC362B102592C80A90660E7A6BF9232C18C7AF049, resolver=0xF58D55F06bB92f083E78bb5063A2DD3544f9B6a3 )
-
ENSRegistryWithFallback.setOwner( node=1F70C573D24CE00CD8A9574BC362B102592C80A90660E7A6BF9232C18C7AF049, owner=0xbf7307EDA6a10771EbCE9250803b225ba4d48E5f )
-
BaseENSResolver.setAddr( _node=1F70C573D24CE00CD8A9574BC362B102592C80A90660E7A6BF9232C18C7AF049, _addr=0xbf7307EDA6a10771EbCE9250803b225ba4d48E5f )
-
ENSRegistryWithFallback.owner( node=91D1777781884D03A6757A803996E38DE2A42967FB37EEACA72729271025A9E2 ) => ( 0x084b1c3C81545d370f3634392De611CaaBFf8148 )
-
ReverseRegistrar.node( addr=0xbf7307EDA6a10771EbCE9250803b225ba4d48E5f ) => ( 0751E897CCFD95372585313F5E3B1CE9D6992C6CE76D8D7E97EB1D574FD18AA1 )
-
BaseENSResolver.setName( _node=0751E897CCFD95372585313F5E3B1CE9D6992C6CE76D8D7E97EB1D574FD18AA1, _name=blank.loopring.eth )
register[BaseENSManager (ln:1774)]
verifyApproval[BaseENSManager (ln:1784)]
recoverECDSASigner[BaseENSManager (ln:1857)]
ecrecover[SignatureUtil (ln:1372)]
isManager[BaseENSManager (ln:1858)]
owner[BaseENSManager (ln:1788)]
getENSRegistry[BaseENSManager (ln:1788)]
setSubnodeOwner[BaseENSManager (ln:1792)]
getENSRegistry[BaseENSManager (ln:1792)]
setResolver[BaseENSManager (ln:1793)]
getENSRegistry[BaseENSManager (ln:1793)]
setOwner[BaseENSManager (ln:1794)]
getENSRegistry[BaseENSManager (ln:1794)]
setAddr[BaseENSManager (ln:1795)]
toSlice[BaseENSManager (ln:1799)]
toSlice[BaseENSManager (ln:1800)]
join[BaseENSManager (ln:1801)]
toSlice[BaseENSManager (ln:1801)]
node[BaseENSManager (ln:1802)]
getENSReverseRegistrar[BaseENSManager (ln:1802)]
setName[BaseENSManager (ln:1803)]
Registered[BaseENSManager (ln:1805)]
File 1 of 5: BaseENSManager
File 2 of 5: ENSRegistryWithFallback
File 3 of 5: BaseENSResolver
File 4 of 5: ENS
File 5 of 5: ReverseRegistrar
// SPDX-License-Identifier: Apache-2.0 // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; /// @title Ownable /// @author Brecht Devos - <[email protected]> /// @dev The Ownable contract has an owner address, and provides basic /// authorization control functions, this simplifies the implementation of /// "user permissions". contract Ownable { address public owner; event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /// @dev The Ownable constructor sets the original `owner` of the contract /// to the sender. constructor() { owner = msg.sender; } /// @dev Throws if called by any account other than the owner. modifier onlyOwner() { require(msg.sender == owner, "UNAUTHORIZED"); _; } /// @dev Allows the current owner to transfer control of the contract to a /// new owner. /// @param newOwner The address to transfer ownership to. function transferOwnership( address newOwner ) public virtual onlyOwner { require(newOwner != address(0), "ZERO_ADDRESS"); emit OwnershipTransferred(owner, newOwner); owner = newOwner; } function renounceOwnership() public onlyOwner { emit OwnershipTransferred(owner, address(0)); owner = address(0); } } // Copyright 2017 Loopring Technology Limited. /// @title AddressSet /// @author Daniel Wang - <[email protected]> contract AddressSet { struct Set { address[] addresses; mapping (address => uint) positions; uint count; } mapping (bytes32 => Set) private sets; function addAddressToSet( bytes32 key, address addr, bool maintainList ) internal { Set storage set = sets[key]; require(set.positions[addr] == 0, "ALREADY_IN_SET"); if (maintainList) { require(set.addresses.length == set.count, "PREVIOUSLY_NOT_MAINTAILED"); set.addresses.push(addr); } else { require(set.addresses.length == 0, "MUST_MAINTAIN"); } set.count += 1; set.positions[addr] = set.count; } function removeAddressFromSet( bytes32 key, address addr ) internal { Set storage set = sets[key]; uint pos = set.positions[addr]; require(pos != 0, "NOT_IN_SET"); delete set.positions[addr]; set.count -= 1; if (set.addresses.length > 0) { address lastAddr = set.addresses[set.count]; if (lastAddr != addr) { set.addresses[pos - 1] = lastAddr; set.positions[lastAddr] = pos; } set.addresses.pop(); } } function removeSet(bytes32 key) internal { delete sets[key]; } function isAddressInSet( bytes32 key, address addr ) internal view returns (bool) { return sets[key].positions[addr] != 0; } function numAddressesInSet(bytes32 key) internal view returns (uint) { Set storage set = sets[key]; return set.count; } function addressesInSet(bytes32 key) internal view returns (address[] memory) { Set storage set = sets[key]; require(set.count == set.addresses.length, "NOT_MAINTAINED"); return sets[key].addresses; } } /* * @title String & slice utility library for Solidity contracts. * @author Nick Johnson <[email protected]> * * @dev Functionality in this library is largely implemented using an * abstraction called a 'slice'. A slice represents a part of a string - * anything from the entire string to a single character, or even no * characters at all (a 0-length slice). Since a slice only has to specify * an offset and a length, copying and manipulating slices is a lot less * expensive than copying and manipulating the strings they reference. * * To further reduce gas costs, most functions on slice that need to return * a slice modify the original one instead of allocating a new one; for * instance, `s.split(".")` will return the text up to the first '.', * modifying s to only contain the remainder of the string after the '.'. * In situations where you do not want to modify the original slice, you * can make a copy first with `.copy()`, for example: * `s.copy().split(".")`. Try and avoid using this idiom in loops; since * Solidity has no memory management, it will result in allocating many * short-lived slices that are later discarded. * * Functions that return two slices come in two versions: a non-allocating * version that takes the second slice as an argument, modifying it in * place, and an allocating version that allocates and returns the second * slice; see `nextRune` for example. * * Functions that have to copy string data will return strings rather than * slices; these can be cast back to slices for further processing if * required. * * For convenience, some functions are provided with non-modifying * variants that create a new slice and return both; for instance, * `s.splitNew('.')` leaves s unmodified, and returns two values * corresponding to the left and right parts of the string. */ /* solium-disable */ library strings { struct slice { uint _len; uint _ptr; } function memcpy(uint dest, uint src, uint len) private pure { // Copy word-length chunks while possible for(; len >= 32; len -= 32) { assembly { mstore(dest, mload(src)) } dest += 32; src += 32; } // Copy remaining bytes uint mask = 256 ** (32 - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) let destpart := and(mload(dest), mask) mstore(dest, or(destpart, srcpart)) } } /* * @dev Returns a slice containing the entire string. * @param self The string to make a slice from. * @return A newly allocated slice containing the entire string. */ function toSlice(string memory self) internal pure returns (slice memory) { uint ptr; assembly { ptr := add(self, 0x20) } return slice(bytes(self).length, ptr); } /* * @dev Returns the length of a null-terminated bytes32 string. * @param self The value to find the length of. * @return The length of the string, from 0 to 32. */ function len(bytes32 self) internal pure returns (uint) { uint ret; if (self == 0) return 0; if (uint256(self) & 0xffffffffffffffffffffffffffffffff == 0) { ret += 16; self = bytes32(uint(self) / 0x100000000000000000000000000000000); } if (uint256(self) & 0xffffffffffffffff == 0) { ret += 8; self = bytes32(uint(self) / 0x10000000000000000); } if (uint256(self) & 0xffffffff == 0) { ret += 4; self = bytes32(uint(self) / 0x100000000); } if (uint256(self) & 0xffff == 0) { ret += 2; self = bytes32(uint(self) / 0x10000); } if (uint256(self) & 0xff == 0) { ret += 1; } return 32 - ret; } /* * @dev Returns a slice containing the entire bytes32, interpreted as a * null-terminated utf-8 string. * @param self The bytes32 value to convert to a slice. * @return A new slice containing the value of the input argument up to the * first null. */ function toSliceB32(bytes32 self) internal pure returns (slice memory ret) { // Allocate space for `self` in memory, copy it there, and point ret at it assembly { let ptr := mload(0x40) mstore(0x40, add(ptr, 0x20)) mstore(ptr, self) mstore(add(ret, 0x20), ptr) } ret._len = len(self); } /* * @dev Returns a new slice containing the same data as the current slice. * @param self The slice to copy. * @return A new slice containing the same data as `self`. */ function copy(slice memory self) internal pure returns (slice memory) { return slice(self._len, self._ptr); } /* * @dev Copies a slice to a new string. * @param self The slice to copy. * @return A newly allocated string containing the slice's text. */ function toString(slice memory self) internal pure returns (string memory) { string memory ret = new string(self._len); uint retptr; assembly { retptr := add(ret, 32) } memcpy(retptr, self._ptr, self._len); return ret; } /* * @dev Returns the length in runes of the slice. Note that this operation * takes time proportional to the length of the slice; avoid using it * in loops, and call `slice.empty()` if you only need to kblock.timestamp whether * the slice is empty or not. * @param self The slice to operate on. * @return The length of the slice in runes. */ function len(slice memory self) internal pure returns (uint l) { // Starting at ptr-31 means the LSB will be the byte we care about uint ptr = self._ptr - 31; uint end = ptr + self._len; for (l = 0; ptr < end; l++) { 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; } } } /* * @dev Returns true if the slice is empty (has a length of 0). * @param self The slice to operate on. * @return True if the slice is empty, False otherwise. */ function empty(slice memory self) internal pure returns (bool) { return self._len == 0; } /* * @dev Returns a positive number if `other` comes lexicographically after * `self`, a negative number if it comes before, or zero if the * contents of the two slices are equal. Comparison is done per-rune, * on unicode codepoints. * @param self The first slice to compare. * @param other The second slice to compare. * @return The result of the comparison. */ function compare(slice memory self, slice memory other) internal pure returns (int) { uint shortest = self._len; if (other._len < self._len) shortest = other._len; uint selfptr = self._ptr; uint otherptr = other._ptr; for (uint idx = 0; idx < shortest; idx += 32) { uint a; uint b; assembly { a := mload(selfptr) b := mload(otherptr) } if (a != b) { // Mask out irrelevant bytes and check again uint256 mask = uint256(-1); // 0xffff... if(shortest < 32) { mask = ~(2 ** (8 * (32 - shortest + idx)) - 1); } uint256 diff = (a & mask) - (b & mask); if (diff != 0) return int(diff); } selfptr += 32; otherptr += 32; } return int(self._len) - int(other._len); } /* * @dev Returns true if the two slices contain the same text. * @param self The first slice to compare. * @param self The second slice to compare. * @return True if the slices are equal, false otherwise. */ function equals(slice memory self, slice memory other) internal pure returns (bool) { return compare(self, other) == 0; } /* * @dev Extracts the first rune in the slice into `rune`, advancing the * slice to point to the next rune and returning `self`. * @param self The slice to operate on. * @param rune The slice that will contain the first rune. * @return `rune`. */ function nextRune(slice memory self, slice memory rune) internal pure returns (slice memory) { rune._ptr = self._ptr; if (self._len == 0) { rune._len = 0; return rune; } uint l; uint b; // Load the first byte of the rune into the LSBs of b assembly { b := and(mload(sub(mload(add(self, 32)), 31)), 0xFF) } if (b < 0x80) { l = 1; } else if(b < 0xE0) { l = 2; } else if(b < 0xF0) { l = 3; } else { l = 4; } // Check for truncated codepoints if (l > self._len) { rune._len = self._len; self._ptr += self._len; self._len = 0; return rune; } self._ptr += l; self._len -= l; rune._len = l; return rune; } /* * @dev Returns the first rune in the slice, advancing the slice to point * to the next rune. * @param self The slice to operate on. * @return A slice containing only the first rune from `self`. */ function nextRune(slice memory self) internal pure returns (slice memory ret) { nextRune(self, ret); } /* * @dev Returns the number of the first codepoint in the slice. * @param self The slice to operate on. * @return The number of the first codepoint in the slice. */ function ord(slice memory self) internal pure returns (uint ret) { if (self._len == 0) { return 0; } uint word; uint length; uint divisor = 2 ** 248; // Load the rune into the MSBs of b assembly { word:= mload(mload(add(self, 32))) } uint b = word / divisor; if (b < 0x80) { ret = b; length = 1; } else if(b < 0xE0) { ret = b & 0x1F; length = 2; } else if(b < 0xF0) { ret = b & 0x0F; length = 3; } else { ret = b & 0x07; length = 4; } // Check for truncated codepoints if (length > self._len) { return 0; } for (uint i = 1; i < length; i++) { divisor = divisor / 256; b = (word / divisor) & 0xFF; if (b & 0xC0 != 0x80) { // Invalid UTF-8 sequence return 0; } ret = (ret * 64) | (b & 0x3F); } return ret; } /* * @dev Returns the keccak-256 hash of the slice. * @param self The slice to hash. * @return The hash of the slice. */ function keccak(slice memory self) internal pure returns (bytes32 ret) { assembly { ret := keccak256(mload(add(self, 32)), mload(self)) } } /* * @dev Returns true if `self` starts with `needle`. * @param self The slice to operate on. * @param needle The slice to search for. * @return True if the slice starts with the provided text, false otherwise. */ function startsWith(slice memory self, slice memory needle) internal pure returns (bool) { if (self._len < needle._len) { return false; } if (self._ptr == needle._ptr) { return true; } bool equal; assembly { let length := mload(needle) let selfptr := mload(add(self, 0x20)) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } return equal; } /* * @dev If `self` starts with `needle`, `needle` is removed from the * beginning of `self`. Otherwise, `self` is unmodified. * @param self The slice to operate on. * @param needle The slice to search for. * @return `self` */ function beyond(slice memory self, slice memory needle) internal pure returns (slice memory) { if (self._len < needle._len) { return self; } bool equal = true; if (self._ptr != needle._ptr) { assembly { let length := mload(needle) let selfptr := mload(add(self, 0x20)) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } } if (equal) { self._len -= needle._len; self._ptr += needle._len; } return self; } /* * @dev Returns true if the slice ends with `needle`. * @param self The slice to operate on. * @param needle The slice to search for. * @return True if the slice starts with the provided text, false otherwise. */ function endsWith(slice memory self, slice memory needle) internal pure returns (bool) { if (self._len < needle._len) { return false; } uint selfptr = self._ptr + self._len - needle._len; if (selfptr == needle._ptr) { return true; } bool equal; assembly { let length := mload(needle) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } return equal; } /* * @dev If `self` ends with `needle`, `needle` is removed from the * end of `self`. Otherwise, `self` is unmodified. * @param self The slice to operate on. * @param needle The slice to search for. * @return `self` */ function until(slice memory self, slice memory needle) internal pure returns (slice memory) { if (self._len < needle._len) { return self; } uint selfptr = self._ptr + self._len - needle._len; bool equal = true; if (selfptr != needle._ptr) { assembly { let length := mload(needle) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } } if (equal) { self._len -= needle._len; } return self; } // Returns the memory address of the first byte of the first occurrence of // `needle` in `self`, or the first byte after `self` if not found. function findPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private pure returns (uint) { uint ptr = selfptr; uint idx; if (needlelen <= selflen) { if (needlelen <= 32) { bytes32 mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1)); bytes32 needledata; assembly { needledata := and(mload(needleptr), mask) } uint end = selfptr + selflen - needlelen; bytes32 ptrdata; assembly { ptrdata := and(mload(ptr), mask) } while (ptrdata != needledata) { if (ptr >= end) return selfptr + selflen; ptr++; assembly { ptrdata := and(mload(ptr), mask) } } return ptr; } else { // For long needles, use hashing bytes32 hash; assembly { hash := keccak256(needleptr, needlelen) } for (idx = 0; idx <= selflen - needlelen; idx++) { bytes32 testHash; assembly { testHash := keccak256(ptr, needlelen) } if (hash == testHash) return ptr; ptr += 1; } } } return selfptr + selflen; } // Returns the memory address of the first byte after the last occurrence of // `needle` in `self`, or the address of `self` if not found. function rfindPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private pure returns (uint) { uint ptr; if (needlelen <= selflen) { if (needlelen <= 32) { bytes32 mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1)); bytes32 needledata; assembly { needledata := and(mload(needleptr), mask) } ptr = selfptr + selflen - needlelen; bytes32 ptrdata; assembly { ptrdata := and(mload(ptr), mask) } while (ptrdata != needledata) { if (ptr <= selfptr) return selfptr; ptr--; assembly { ptrdata := and(mload(ptr), mask) } } return ptr + needlelen; } else { // For long needles, use hashing bytes32 hash; assembly { hash := keccak256(needleptr, needlelen) } ptr = selfptr + (selflen - needlelen); while (ptr >= selfptr) { bytes32 testHash; assembly { testHash := keccak256(ptr, needlelen) } if (hash == testHash) return ptr + needlelen; ptr -= 1; } } } return selfptr; } /* * @dev Modifies `self` to contain everything from the first occurrence of * `needle` to the end of the slice. `self` is set to the empty slice * if `needle` is not found. * @param self The slice to search and modify. * @param needle The text to search for. * @return `self`. */ function find(slice memory self, slice memory needle) internal pure returns (slice memory) { uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); self._len -= ptr - self._ptr; self._ptr = ptr; return self; } /* * @dev Modifies `self` to contain the part of the string from the start of * `self` to the end of the first occurrence of `needle`. If `needle` * is not found, `self` is set to the empty slice. * @param self The slice to search and modify. * @param needle The text to search for. * @return `self`. */ function rfind(slice memory self, slice memory needle) internal pure returns (slice memory) { uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); self._len = ptr - self._ptr; return self; } /* * @dev Splits the slice, setting `self` to everything after the first * occurrence of `needle`, and `token` to everything before it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and `token` is set to the entirety of `self`. * @param self The slice to split. * @param needle The text to search for in `self`. * @param token An output parameter to which the first token is written. * @return `token`. */ function split(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) { uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); token._ptr = self._ptr; token._len = ptr - self._ptr; if (ptr == self._ptr + self._len) { // Not found self._len = 0; } else { self._len -= token._len + needle._len; self._ptr = ptr + needle._len; } return token; } /* * @dev Splits the slice, setting `self` to everything after the first * occurrence of `needle`, and returning everything before it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and the entirety of `self` is returned. * @param self The slice to split. * @param needle The text to search for in `self`. * @return The part of `self` up to the first occurrence of `delim`. */ function split(slice memory self, slice memory needle) internal pure returns (slice memory token) { split(self, needle, token); } /* * @dev Splits the slice, setting `self` to everything before the last * occurrence of `needle`, and `token` to everything after it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and `token` is set to the entirety of `self`. * @param self The slice to split. * @param needle The text to search for in `self`. * @param token An output parameter to which the first token is written. * @return `token`. */ function rsplit(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) { uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); token._ptr = ptr; token._len = self._len - (ptr - self._ptr); if (ptr == self._ptr) { // Not found self._len = 0; } else { self._len -= token._len + needle._len; } return token; } /* * @dev Splits the slice, setting `self` to everything before the last * occurrence of `needle`, and returning everything after it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and the entirety of `self` is returned. * @param self The slice to split. * @param needle The text to search for in `self`. * @return The part of `self` after the last occurrence of `delim`. */ function rsplit(slice memory self, slice memory needle) internal pure returns (slice memory token) { rsplit(self, needle, token); } /* * @dev Counts the number of nonoverlapping occurrences of `needle` in `self`. * @param self The slice to search. * @param needle The text to search for in `self`. * @return The number of occurrences of `needle` found in `self`. */ function count(slice memory self, slice memory needle) internal pure returns (uint cnt) { uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr) + needle._len; while (ptr <= self._ptr + self._len) { cnt++; ptr = findPtr(self._len - (ptr - self._ptr), ptr, needle._len, needle._ptr) + needle._len; } } /* * @dev Returns True if `self` contains `needle`. * @param self The slice to search. * @param needle The text to search for in `self`. * @return True if `needle` is found in `self`, false otherwise. */ function contains(slice memory self, slice memory needle) internal pure returns (bool) { return rfindPtr(self._len, self._ptr, needle._len, needle._ptr) != self._ptr; } /* * @dev Returns a newly allocated string containing the concatenation of * `self` and `other`. * @param self The first slice to concatenate. * @param other The second slice to concatenate. * @return The concatenation of the two strings. */ function concat(slice memory self, slice memory other) internal pure returns (string memory) { string memory ret = new string(self._len + other._len); uint retptr; assembly { retptr := add(ret, 32) } memcpy(retptr, self._ptr, self._len); memcpy(retptr + self._len, other._ptr, other._len); return ret; } /* * @dev Joins an array of slices, using `self` as a delimiter, returning a * newly allocated string. * @param self The delimiter to use. * @param parts A list of slices to join. * @return A newly allocated string containing all the slices in `parts`, * joined with `self`. */ function join(slice memory self, slice[] memory parts) internal pure returns (string memory) { if (parts.length == 0) return ""; uint length = self._len * (parts.length - 1); for(uint i = 0; i < parts.length; i++) length += parts[i]._len; string memory ret = new string(length); uint retptr; assembly { retptr := add(ret, 32) } for(uint i = 0; i < parts.length; i++) { memcpy(retptr, parts[i]._ptr, parts[i]._len); retptr += parts[i]._len; if (i < parts.length - 1) { memcpy(retptr, self._ptr, self._len); retptr += self._len; } } return ret; } } // Taken from Argent's code base - https://github.com/argentlabs/argent-contracts/blob/develop/contracts/ens/ENS.sol // with few modifications. /** * ENS Registry interface. */ interface ENSRegistry { // 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); } /** * ENS Resolver interface. */ abstract contract ENSResolver { function addr(bytes32 _node) public view virtual returns (address); function setAddr(bytes32 _node, address _addr) public virtual; function name(bytes32 _node) public view virtual returns (string memory); function setName(bytes32 _node, string memory _name) public virtual; } /** * ENS Reverse Registrar interface. */ abstract contract ENSReverseRegistrar { function claim(address _owner) public virtual returns (bytes32 _node); function claimWithResolver(address _owner, address _resolver) public virtual returns (bytes32); function setName(string memory _name) public virtual returns (bytes32); function node(address _addr) public view virtual returns (bytes32); } // Copyright 2017 Loopring Technology Limited. /// @title Utility Functions for uint /// @author Daniel Wang - <[email protected]> library MathUint { function mul( uint a, uint b ) internal pure returns (uint c) { c = a * b; require(a == 0 || c / a == b, "MUL_OVERFLOW"); } function sub( uint a, uint b ) internal pure returns (uint) { require(b <= a, "SUB_UNDERFLOW"); return a - b; } function add( uint a, uint b ) internal pure returns (uint c) { c = a + b; require(c >= a, "ADD_OVERFLOW"); } } // Copyright 2017 Loopring Technology Limited. pragma experimental ABIEncoderV2; //Mainly taken from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol library BytesUtil { function slice( bytes memory _bytes, uint _start, uint _length ) internal pure returns (bytes memory) { require(_bytes.length >= (_start + _length)); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function toAddress(bytes memory _bytes, uint _start) internal pure returns (address) { require(_bytes.length >= (_start + 20)); address tempAddress; assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) } return tempAddress; } function toUint8(bytes memory _bytes, uint _start) internal pure returns (uint8) { require(_bytes.length >= (_start + 1)); uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } function toUint16(bytes memory _bytes, uint _start) internal pure returns (uint16) { require(_bytes.length >= (_start + 2)); uint16 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x2), _start)) } return tempUint; } function toUint24(bytes memory _bytes, uint _start) internal pure returns (uint24) { require(_bytes.length >= (_start + 3)); uint24 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x3), _start)) } return tempUint; } function toUint32(bytes memory _bytes, uint _start) internal pure returns (uint32) { require(_bytes.length >= (_start + 4)); uint32 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x4), _start)) } return tempUint; } function toUint64(bytes memory _bytes, uint _start) internal pure returns (uint64) { require(_bytes.length >= (_start + 8)); uint64 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x8), _start)) } return tempUint; } function toUint96(bytes memory _bytes, uint _start) internal pure returns (uint96) { require(_bytes.length >= (_start + 12)); uint96 tempUint; assembly { tempUint := mload(add(add(_bytes, 0xc), _start)) } return tempUint; } function toUint128(bytes memory _bytes, uint _start) internal pure returns (uint128) { require(_bytes.length >= (_start + 16)); uint128 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x10), _start)) } return tempUint; } function toUint(bytes memory _bytes, uint _start) internal pure returns (uint256) { require(_bytes.length >= (_start + 32)); uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } function toBytes4(bytes memory _bytes, uint _start) internal pure returns (bytes4) { require(_bytes.length >= (_start + 4)); bytes4 tempBytes4; assembly { tempBytes4 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes4; } function toBytes32(bytes memory _bytes, uint _start) internal pure returns (bytes32) { require(_bytes.length >= (_start + 32)); bytes32 tempBytes32; assembly { tempBytes32 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes32; } function fastSHA256( bytes memory data ) internal view returns (bytes32) { bytes32[] memory result = new bytes32[](1); bool success; assembly { let ptr := add(data, 32) success := staticcall(sub(gas(), 2000), 2, ptr, mload(data), add(result, 32), 32) } require(success, "SHA256_FAILED"); return result[0]; } } // Copyright 2017 Loopring Technology Limited. /// @title Utility Functions for addresses /// @author Daniel Wang - <[email protected]> /// @author Brecht Devos - <[email protected]> library AddressUtil { using AddressUtil for *; function isContract( address addr ) internal view returns (bool) { // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(addr) } return (codehash != 0x0 && codehash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470); } function toPayable( address addr ) internal pure returns (address payable) { return address(uint160(addr)); } // Works like address.send but with a customizable gas limit // Make sure your code is safe for reentrancy when using this function! function sendETH( address to, uint amount, uint gasLimit ) internal returns (bool success) { if (amount == 0) { return true; } address payable recipient = to.toPayable(); /* solium-disable-next-line */ (success,) = recipient.call{value: amount, gas: gasLimit}(""); } // Works like address.transfer but with a customizable gas limit // Make sure your code is safe for reentrancy when using this function! function sendETHAndVerify( address to, uint amount, uint gasLimit ) internal returns (bool success) { success = to.sendETH(amount, gasLimit); require(success, "TRANSFER_FAILURE"); } } /// @title SignatureUtil /// @author Daniel Wang - <[email protected]> /// @dev This method supports multihash standard. Each signature's first byte indicates /// the signature's type, the second byte indicates the signature's length, therefore, /// each signature will have 2 extra bytes prefix. Mulitple signatures are concatenated /// together. library SignatureUtil { using BytesUtil for bytes; using MathUint for uint; using AddressUtil for address; enum SignatureType { ILLEGAL, INVALID, EIP_712, ETH_SIGN, WALLET // deprecated } bytes4 constant internal ERC1271_MAGICVALUE = 0x20c13b0b; bytes4 constant internal ERC1271_FUNCTION_WITH_BYTES_SELECTOR = bytes4( keccak256(bytes("isValidSignature(bytes,bytes)")) ); bytes4 constant internal ERC1271_FUNCTION_WITH_BYTES32_SELECTOR = bytes4( keccak256(bytes("isValidSignature(bytes32,bytes)")) ); function verifySignatures( bytes32 signHash, address[] memory signers, bytes[] memory signatures ) internal view returns (bool) { return verifySignatures(abi.encodePacked(signHash), signers, signatures); } function verifySignatures( bytes memory data, address[] memory signers, bytes[] memory signatures ) internal view returns (bool) { require(signers.length == signatures.length, "BAD_SIGNATURE_DATA"); address lastSigner; for (uint i = 0; i < signers.length; i++) { require(signers[i] > lastSigner, "INVALID_SIGNERS_ORDER"); lastSigner = signers[i]; if (!verifySignature(data, signers[i], signatures[i])) { return false; } } return true; } function verifySignature( bytes memory data, address signer, bytes memory signature ) internal view returns (bool) { return signer.isContract() ? verifyERC1271Signature(data, signer, signature) : verifyEOASignature(data, signer, signature); } function verifySignature( bytes32 signHash, address signer, bytes memory signature ) internal view returns (bool) { return verifySignature(abi.encodePacked(signHash), signer, signature); } function recoverECDSASigner( bytes32 signHash, bytes memory signature ) internal pure returns (address) { if (signature.length != 65) { return address(0); } bytes32 r; bytes32 s; uint8 v; // we jump 32 (0x20) as the first slot of bytes contains the length // we jump 65 (0x41) per signature // for v we load 32 bytes ending with v (the first 31 come from s) then apply a mask assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := and(mload(add(signature, 0x41)), 0xff) } // See https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/cryptography/ECDSA.sol if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return address(0); } if (v == 27 || v == 28) { return ecrecover(signHash, v, r, s); } else { return address(0); } } function recoverECDSASigner( bytes memory data, bytes memory signature ) internal pure returns (address addr1, address addr2) { if (data.length == 32) { addr1 = recoverECDSASigner(data.toBytes32(0), signature); } addr2 = recoverECDSASigner(keccak256(data), signature); } function verifyEOASignature( bytes memory data, address signer, bytes memory signature ) private pure returns (bool) { if (signer == address(0)) { return false; } uint signatureTypeOffset = signature.length.sub(1); SignatureType signatureType = SignatureType(signature.toUint8(signatureTypeOffset)); bytes memory stripped = signature.slice(0, signatureTypeOffset); if (signatureType == SignatureType.EIP_712) { (address addr1, address addr2) = recoverECDSASigner(data, stripped); return addr1 == signer || addr2 == signer; } else if (signatureType == SignatureType.ETH_SIGN) { if (data.length == 32) { bytes32 hash = keccak256( abi.encodePacked("\x19Ethereum Signed Message:\n32", data.toBytes32(0)) ); if (recoverECDSASigner(hash, stripped) == signer) { return true; } } bytes32 hash = keccak256( abi.encodePacked("\x19Ethereum Signed Message:\n32", keccak256(data)) ); return recoverECDSASigner(hash, stripped) == signer; } else { return false; } } function verifyERC1271Signature( bytes memory data, address signer, bytes memory signature ) private view returns (bool) { return data.length == 32 && verifyERC1271WithBytes32(data.toBytes32(0), signer, signature) || verifyERC1271WithBytes(data, signer, signature); } function verifyERC1271WithBytes( bytes memory data, address signer, bytes memory signature ) private view returns (bool) { bytes memory callData = abi.encodeWithSelector( ERC1271_FUNCTION_WITH_BYTES_SELECTOR, data, signature ); (bool success, bytes memory result) = signer.staticcall(callData); return ( success && result.length == 32 && result.toBytes4(0) == ERC1271_MAGICVALUE ); } function verifyERC1271WithBytes32( bytes32 hash, address signer, bytes memory signature ) private view returns (bool) { bytes memory callData = abi.encodeWithSelector( ERC1271_FUNCTION_WITH_BYTES32_SELECTOR, hash, signature ); (bool success, bytes memory result) = signer.staticcall(callData); return ( success && result.length == 32 && result.toBytes4(0) == ERC1271_MAGICVALUE ); } } // Taken from Argent's code base - https://github.com/argentlabs/argent-contracts/blob/develop/contracts/ens/ENSConsumer.sol // with few modifications. /** * @title ENSConsumer * @dev Helper contract to resolve ENS names. * @author Julien Niset - <[email protected]> */ contract ENSConsumer { using strings for *; // namehash('addr.reverse') bytes32 constant public ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2; // the address of the ENS registry address ensRegistry; /** * @dev No address should be provided when deploying on Mainnet to avoid storage cost. The * contract will use the hardcoded value. */ constructor(address _ensRegistry) { ensRegistry = _ensRegistry; } /** * @dev Resolves an ENS name to an address. * @param _node The namehash of the ENS name. */ function resolveEns(bytes32 _node) public view returns (address) { address resolver = getENSRegistry().resolver(_node); return ENSResolver(resolver).addr(_node); } /** * @dev Gets the official ENS registry. */ function getENSRegistry() public view returns (ENSRegistry) { return ENSRegistry(ensRegistry); } /** * @dev Gets the official ENS reverse registrar. */ function getENSReverseRegistrar() public view returns (ENSReverseRegistrar) { return ENSReverseRegistrar(getENSRegistry().owner(ADDR_REVERSE_NODE)); } } // Taken from Argent's code base - https://github.com/argentlabs/argent-contracts/blob/develop/contracts/ens/ArgentENSManager.sol // with few modifications. // Copyright 2017 Loopring Technology Limited. // Copyright 2017 Loopring Technology Limited. /// @title Claimable /// @author Brecht Devos - <[email protected]> /// @dev Extension for the Ownable contract, where the ownership needs /// to be claimed. This allows the new owner to accept the transfer. contract Claimable is Ownable { address public pendingOwner; /// @dev Modifier throws if called by any account other than the pendingOwner. modifier onlyPendingOwner() { require(msg.sender == pendingOwner, "UNAUTHORIZED"); _; } /// @dev Allows the current owner to set the pendingOwner address. /// @param newOwner The address to transfer ownership to. function transferOwnership( address newOwner ) public override onlyOwner { require(newOwner != address(0) && newOwner != owner, "INVALID_ADDRESS"); pendingOwner = newOwner; } /// @dev Allows the pendingOwner address to finalize the transfer. function claimOwnership() public onlyPendingOwner { emit OwnershipTransferred(owner, pendingOwner); owner = pendingOwner; pendingOwner = address(0); } } contract OwnerManagable is Claimable, AddressSet { bytes32 internal constant MANAGER = keccak256("__MANAGED__"); event ManagerAdded (address manager); event ManagerRemoved(address manager); modifier onlyManager { require(isManager(msg.sender), "NOT_MANAGER"); _; } modifier onlyOwnerOrManager { require(msg.sender == owner || isManager(msg.sender), "NOT_OWNER_OR_MANAGER"); _; } constructor() Claimable() {} /// @dev Gets the managers. /// @return The list of managers. function managers() public view returns (address[] memory) { return addressesInSet(MANAGER); } /// @dev Gets the number of managers. /// @return The numer of managers. function numManagers() public view returns (uint) { return numAddressesInSet(MANAGER); } /// @dev Checks if an address is a manger. /// @param addr The address to check. /// @return True if the address is a manager, False otherwise. function isManager(address addr) public view returns (bool) { return isAddressInSet(MANAGER, addr); } /// @dev Adds a new manager. /// @param manager The new address to add. function addManager(address manager) public onlyOwner { addManagerInternal(manager); } /// @dev Removes a manager. /// @param manager The manager to remove. function removeManager(address manager) public onlyOwner { removeAddressFromSet(MANAGER, manager); emit ManagerRemoved(manager); } function addManagerInternal(address manager) internal { addAddressToSet(MANAGER, manager, true); emit ManagerAdded(manager); } } /** * @dev Interface for an ENS Mananger. */ interface IENSManager { function changeRootnodeOwner(address _newOwner) external; function isAvailable(bytes32 _subnode) external view returns (bool); function resolveName(address _wallet) external view returns (string memory); function register( address _wallet, address _owner, string calldata _label, bytes calldata _approval ) external; } /** * @title BaseENSManager * @dev Implementation of an ENS manager that orchestrates the complete * registration of subdomains for a single root (e.g. argent.eth). * The contract defines a manager role who is the only role that can trigger the registration of * a new subdomain. * @author Julien Niset - <[email protected]> */ contract BaseENSManager is IENSManager, OwnerManagable, ENSConsumer { using strings for *; using BytesUtil for bytes; using MathUint for uint; // The managed root name string public rootName; // The managed root node bytes32 public rootNode; // The address of the ENS resolver address public ensResolver; // *************** Events *************************** // event RootnodeOwnerChange(bytes32 indexed _rootnode, address indexed _newOwner); event ENSResolverChanged(address addr); event Registered(address indexed _wallet, address _owner, string _ens); event Unregistered(string _ens); // *************** Constructor ********************** // /** * @dev Constructor that sets the ENS root name and root node to manage. * @param _rootName The root name (e.g. argentx.eth). * @param _rootNode The node of the root name (e.g. namehash(argentx.eth)). */ constructor(string memory _rootName, bytes32 _rootNode, address _ensRegistry, address _ensResolver) ENSConsumer(_ensRegistry) { rootName = _rootName; rootNode = _rootNode; ensResolver = _ensResolver; } // *************** External Functions ********************* // /** * @dev This function must be called when the ENS Manager contract is replaced * and the address of the new Manager should be provided. * @param _newOwner The address of the new ENS manager that will manage the root node. */ function changeRootnodeOwner(address _newOwner) external override onlyOwner { getENSRegistry().setOwner(rootNode, _newOwner); emit RootnodeOwnerChange(rootNode, _newOwner); } /** * @dev Lets the owner change the address of the ENS resolver contract. * @param _ensResolver The address of the ENS resolver contract. */ function changeENSResolver(address _ensResolver) external onlyOwner { require(_ensResolver != address(0), "WF: address cannot be null"); ensResolver = _ensResolver; emit ENSResolverChanged(_ensResolver); } /** * @dev Lets the manager assign an ENS subdomain of the root node to a target address. * Registers both the forward and reverse ENS. * @param _wallet The wallet which owns the subdomain. * @param _owner The wallet's owner. * @param _label The subdomain label. * @param _approval The signature of _wallet, _owner and _label by a manager. */ function register( address _wallet, address _owner, string calldata _label, bytes calldata _approval ) external override onlyManager { verifyApproval(_wallet, _owner, _label, _approval); bytes32 labelNode = keccak256(abi.encodePacked(_label)); bytes32 node = keccak256(abi.encodePacked(rootNode, labelNode)); address currentOwner = getENSRegistry().owner(node); require(currentOwner == address(0), "AEM: _label is alrealdy owned"); // Forward ENS getENSRegistry().setSubnodeOwner(rootNode, labelNode, address(this)); getENSRegistry().setResolver(node, ensResolver); getENSRegistry().setOwner(node, _wallet); ENSResolver(ensResolver).setAddr(node, _wallet); // 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 = getENSReverseRegistrar().node(_wallet); ENSResolver(ensResolver).setName(reverseNode, name); emit Registered(_wallet, _owner, name); } // *************** Public Functions ********************* // /** * @dev Resolves an address to an ENS name * @param _wallet The ENS owner address */ function resolveName(address _wallet) public view override returns (string memory) { bytes32 reverseNode = getENSReverseRegistrar().node(_wallet); return ENSResolver(ensResolver).name(reverseNode); } /** * @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 override returns (bool) { bytes32 node = keccak256(abi.encodePacked(rootNode, _subnode)); address currentOwner = getENSRegistry().owner(node); if(currentOwner == address(0)) { return true; } return false; } function verifyApproval( address _wallet, address _owner, string memory _label, bytes memory _approval ) internal view { bytes32 messageHash = keccak256( abi.encodePacked( _wallet, _owner, _label ) ); bytes32 hash = keccak256( abi.encodePacked( "\x19Ethereum Signed Message:\n32", messageHash ) ); address signer = SignatureUtil.recoverECDSASigner(hash, _approval); require(isManager(signer), "UNAUTHORIZED"); } }
File 2 of 5: ENSRegistryWithFallback
// 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); // Logged when an operator is added or removed. event ApprovalForAll(address indexed owner, address indexed operator, bool approved); function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external; function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external; function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external returns(bytes32); function setResolver(bytes32 node, address resolver) external; function setOwner(bytes32 node, address owner) external; function setTTL(bytes32 node, uint64 ttl) external; function setApprovalForAll(address operator, bool approved) 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); function recordExists(bytes32 node) external view returns (bool); function isApprovedForAll(address owner, address operator) external view returns (bool); } // File: @ensdomains/ens/contracts/ENSRegistry.sol pragma solidity ^0.5.0; /** * The ENS registry contract. */ contract ENSRegistry is ENS { struct Record { address owner; address resolver; uint64 ttl; } mapping (bytes32 => Record) records; mapping (address => mapping(address => bool)) operators; // Permits modifications only by the owner of the specified node. modifier authorised(bytes32 node) { address owner = records[node].owner; require(owner == msg.sender || operators[owner][msg.sender]); _; } /** * @dev Constructs a new ENS registrar. */ constructor() public { records[0x0].owner = msg.sender; } /** * @dev Sets the record for a node. * @param node The node to update. * @param owner The address of the new owner. * @param resolver The address of the resolver. * @param ttl The TTL in seconds. */ function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external { setOwner(node, owner); _setResolverAndTTL(node, resolver, ttl); } /** * @dev Sets the record for a subnode. * @param node The parent node. * @param label The hash of the label specifying the subnode. * @param owner The address of the new owner. * @param resolver The address of the resolver. * @param ttl The TTL in seconds. */ function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external { bytes32 subnode = setSubnodeOwner(node, label, owner); _setResolverAndTTL(subnode, resolver, ttl); } /** * @dev Transfers ownership of a node to a new address. May only be called by the current owner of the node. * @param node The node to transfer ownership of. * @param owner The address of the new owner. */ function setOwner(bytes32 node, address owner) public authorised(node) { _setOwner(node, owner); emit Transfer(node, owner); } /** * @dev Transfers ownership of a subnode keccak256(node, label) to a new address. May only be called by the owner of the parent node. * @param node The parent node. * @param label The hash of the label specifying the subnode. * @param owner The address of the new owner. */ function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public authorised(node) returns(bytes32) { bytes32 subnode = keccak256(abi.encodePacked(node, label)); _setOwner(subnode, owner); emit NewOwner(node, label, owner); return subnode; } /** * @dev Sets the resolver address for the specified node. * @param node The node to update. * @param resolver The address of the resolver. */ function setResolver(bytes32 node, address resolver) public authorised(node) { emit NewResolver(node, resolver); records[node].resolver = resolver; } /** * @dev Sets the TTL for the specified node. * @param node The node to update. * @param ttl The TTL in seconds. */ function setTTL(bytes32 node, uint64 ttl) public authorised(node) { emit NewTTL(node, ttl); records[node].ttl = ttl; } /** * @dev Enable or disable approval for a third party ("operator") to manage * all of `msg.sender`'s ENS records. Emits the ApprovalForAll event. * @param operator Address to add to the set of authorized operators. * @param approved True if the operator is approved, false to revoke approval. */ function setApprovalForAll(address operator, bool approved) external { operators[msg.sender][operator] = approved; emit ApprovalForAll(msg.sender, operator, approved); } /** * @dev Returns the address that owns the specified node. * @param node The specified node. * @return address of the owner. */ function owner(bytes32 node) public view returns (address) { address addr = records[node].owner; if (addr == address(this)) { return address(0x0); } return addr; } /** * @dev Returns the address of the resolver for the specified node. * @param node The specified node. * @return address of the resolver. */ function resolver(bytes32 node) public view returns (address) { return records[node].resolver; } /** * @dev Returns the TTL of a node, and any records associated with it. * @param node The specified node. * @return ttl of the node. */ function ttl(bytes32 node) public view returns (uint64) { return records[node].ttl; } /** * @dev Returns whether a record has been imported to the registry. * @param node The specified node. * @return Bool if record exists */ function recordExists(bytes32 node) public view returns (bool) { return records[node].owner != address(0x0); } /** * @dev Query if an address is an authorized operator for another address. * @param owner The address that owns the records. * @param operator The address that acts on behalf of the owner. * @return True if `operator` is an approved operator for `owner`, false otherwise. */ function isApprovedForAll(address owner, address operator) external view returns (bool) { return operators[owner][operator]; } function _setOwner(bytes32 node, address owner) internal { records[node].owner = owner; } function _setResolverAndTTL(bytes32 node, address resolver, uint64 ttl) internal { if(resolver != records[node].resolver) { records[node].resolver = resolver; emit NewResolver(node, resolver); } if(ttl != records[node].ttl) { records[node].ttl = ttl; emit NewTTL(node, ttl); } } } // File: @ensdomains/ens/contracts/ENSRegistryWithFallback.sol pragma solidity ^0.5.0; /** * The ENS registry contract. */ contract ENSRegistryWithFallback is ENSRegistry { ENS public old; /** * @dev Constructs a new ENS registrar. */ constructor(ENS _old) public ENSRegistry() { old = _old; } /** * @dev Returns the address of the resolver for the specified node. * @param node The specified node. * @return address of the resolver. */ function resolver(bytes32 node) public view returns (address) { if (!recordExists(node)) { return old.resolver(node); } return super.resolver(node); } /** * @dev Returns the address that owns the specified node. * @param node The specified node. * @return address of the owner. */ function owner(bytes32 node) public view returns (address) { if (!recordExists(node)) { return old.owner(node); } return super.owner(node); } /** * @dev Returns the TTL of a node, and any records associated with it. * @param node The specified node. * @return ttl of the node. */ function ttl(bytes32 node) public view returns (uint64) { if (!recordExists(node)) { return old.ttl(node); } return super.ttl(node); } function _setOwner(bytes32 node, address owner) internal { address addr = owner; if (addr == address(0x0)) { addr = address(this); } super._setOwner(node, addr); } }
File 3 of 5: BaseENSResolver
/** Author: Loopring Foundation (Loopring Project Ltd) */ pragma solidity ^0.6.6; /* Copyright 2017 Loopring Project Ltd (Loopring Foundation). Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /// @title Ownable /// @author Brecht Devos - <[email protected]> /// @dev The Ownable contract has an owner address, and provides basic /// authorization control functions, this simplifies the implementation of /// "user permissions". contract Ownable { address public owner; event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /// @dev The Ownable constructor sets the original `owner` of the contract /// to the sender. constructor() public { owner = msg.sender; } /// @dev Throws if called by any account other than the owner. modifier onlyOwner() { require(msg.sender == owner, "UNAUTHORIZED"); _; } /// @dev Allows the current owner to transfer control of the contract to a /// new owner. /// @param newOwner The address to transfer ownership to. function transferOwnership( address newOwner ) public virtual onlyOwner { require(newOwner != address(0), "ZERO_ADDRESS"); emit OwnershipTransferred(owner, newOwner); owner = newOwner; } function renounceOwnership() public onlyOwner { emit OwnershipTransferred(owner, address(0)); owner = address(0); } } /* Copyright 2017 Loopring Project Ltd (Loopring Foundation). Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /// @title Claimable /// @author Brecht Devos - <[email protected]> /// @dev Extension for the Ownable contract, where the ownership needs /// to be claimed. This allows the new owner to accept the transfer. contract Claimable is Ownable { address public pendingOwner; /// @dev Modifier throws if called by any account other than the pendingOwner. modifier onlyPendingOwner() { require(msg.sender == pendingOwner, "UNAUTHORIZED"); _; } /// @dev Allows the current owner to set the pendingOwner address. /// @param newOwner The address to transfer ownership to. function transferOwnership( address newOwner ) public override onlyOwner { require(newOwner != address(0) && newOwner != owner, "INVALID_ADDRESS"); pendingOwner = newOwner; } /// @dev Allows the pendingOwner address to finalize the transfer. function claimOwnership() public onlyPendingOwner { emit OwnershipTransferred(owner, pendingOwner); owner = pendingOwner; pendingOwner = address(0); } } /* Copyright 2017 Loopring Project Ltd (Loopring Foundation). Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /// @title AddressSet /// @author Daniel Wang - <[email protected]> contract AddressSet { struct Set { address[] addresses; mapping (address => uint) positions; uint count; } mapping (bytes32 => Set) private sets; function addAddressToSet( bytes32 key, address addr, bool maintainList ) internal { Set storage set = sets[key]; require(set.positions[addr] == 0, "ALREADY_IN_SET"); if (maintainList) { require(set.addresses.length == set.count, "PREVIOUSLY_NOT_MAINTAILED"); set.addresses.push(addr); } else { require(set.addresses.length == 0, "MUST_MAINTAIN"); } set.count += 1; set.positions[addr] = set.count; } function removeAddressFromSet( bytes32 key, address addr ) internal { Set storage set = sets[key]; uint pos = set.positions[addr]; require(pos != 0, "NOT_IN_SET"); delete set.positions[addr]; set.count -= 1; if (set.addresses.length > 0) { address lastAddr = set.addresses[set.count]; if (lastAddr != addr) { set.addresses[pos - 1] = lastAddr; set.positions[lastAddr] = pos; } set.addresses.pop(); } } function removeSet(bytes32 key) internal { delete sets[key]; } function isAddressInSet( bytes32 key, address addr ) internal view returns (bool) { return sets[key].positions[addr] != 0; } function numAddressesInSet(bytes32 key) internal view returns (uint) { Set storage set = sets[key]; return set.count; } function addressesInSet(bytes32 key) internal view returns (address[] memory) { Set storage set = sets[key]; require(set.count == set.addresses.length, "NOT_MAINTAINED"); return sets[key].addresses; } } /* Copyright 2017 Loopring Project Ltd (Loopring Foundation). Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ contract OwnerManagable is Claimable, AddressSet { bytes32 internal constant MANAGER = keccak256("__MANAGED__"); event ManagerAdded (address indexed manager); event ManagerRemoved(address indexed manager); modifier onlyManager { require(isManager(msg.sender), "NOT_MANAGER"); _; } modifier onlyOwnerOrManager { require(msg.sender == owner || isManager(msg.sender), "NOT_OWNER_OR_MANAGER"); _; } constructor() public Claimable() {} /// @dev Gets the managers. /// @return The list of managers. function managers() public view returns (address[] memory) { return addressesInSet(MANAGER); } /// @dev Gets the number of managers. /// @return The numer of managers. function numManagers() public view returns (uint) { return numAddressesInSet(MANAGER); } /// @dev Checks if an address is a manger. /// @param addr The address to check. /// @return True if the address is a manager, False otherwise. function isManager(address addr) public view returns (bool) { return isAddressInSet(MANAGER, addr); } /// @dev Adds a new manager. /// @param manager The new address to add. function addManager(address manager) public onlyOwner { addManagerInternal(manager); } /// @dev Removes a manager. /// @param manager The manager to remove. function removeManager(address manager) public onlyOwner { removeAddressFromSet(MANAGER, manager); emit ManagerRemoved(manager); } function addManagerInternal(address manager) internal { addAddressToSet(MANAGER, manager, true); emit ManagerAdded(manager); } } // Taken from Argent's code base - https://github.com/argentlabs/argent-contracts/blob/develop/contracts/ens/ENS.sol // with few modifications. /** * ENS Registry interface. */ interface ENSRegistry { // 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); } /** * ENS Resolver interface. */ abstract contract ENSResolver { function addr(bytes32 _node) public view virtual returns (address); function setAddr(bytes32 _node, address _addr) public virtual; function name(bytes32 _node) public view virtual returns (string memory); function setName(bytes32 _node, string memory _name) public virtual; } /** * ENS Reverse Registrar interface. */ abstract contract ENSReverseRegistrar { function claim(address _owner) public virtual returns (bytes32 _node); function claimWithResolver(address _owner, address _resolver) public virtual returns (bytes32); function setName(string memory _name) public virtual returns (bytes32); function node(address _addr) public view virtual returns (bytes32); } // Taken from Argent's code base - https://github.com/argentlabs/argent-contracts/blob/develop/contracts/ens/ArgentENSResolver.sol // with few modifications. /** * @title BaseENSResolver * @dev Basic implementation of a Resolver. * The contract defines a manager role who is the only role that can add a new name * to the list of resolved names. * @author Julien Niset - <[email protected]> */ //contract BaseENSResolver is Owned, Managed, ENSResolver { contract BaseENSResolver is OwnerManagable, ENSResolver { bytes4 constant SUPPORT_INTERFACE_ID = 0x01ffc9a7; bytes4 constant ADDR_INTERFACE_ID = 0x3b3b57de; bytes4 constant NAME_INTERFACE_ID = 0x691f3431; // mapping between namehash and resolved records mapping (bytes32 => Record) records; struct Record { address addr; string name; } // *************** Events *************************** // event AddrChanged(bytes32 indexed _node, address _addr); event NameChanged(bytes32 indexed _node, string _name); // *************** Public Functions ********************* // /** * @dev Lets the manager set the address associated with an ENS node. * @param _node The node to update. * @param _addr The address to set. */ function setAddr(bytes32 _node, address _addr) public override onlyManager { records[_node].addr = _addr; emit AddrChanged(_node, _addr); } /** * @dev Lets the manager set the name associated with an ENS node. * @param _node The node to update. * @param _name The name to set. */ function setName(bytes32 _node, string memory _name) public override onlyManager { records[_node].name = _name; emit NameChanged(_node, _name); } /** * @dev Gets the address associated to an ENS node. * @param _node The target node. * @return the address of the target node. */ function addr(bytes32 _node) public view override returns (address) { return records[_node].addr; } /** * @dev Gets the name associated to an ENS node. * @param _node The target ENS node. * @return the name of the target ENS node. */ function name(bytes32 _node) public view override returns (string memory) { return records[_node].name; } /** * @dev Returns true if the resolver implements the interface specified by the provided hash. * @param _interfaceID The ID of the interface to check for. * @return True if the contract implements the requested interface. */ function supportsInterface(bytes4 _interfaceID) public pure returns (bool) { return _interfaceID == SUPPORT_INTERFACE_ID || _interfaceID == ADDR_INTERFACE_ID || _interfaceID == NAME_INTERFACE_ID; } }
File 4 of 5: ENS
;;; --------------------------------------------------------------------------- ;;; @title The Ethereum Name Service registry. ;;; @author Daniel Ellison <[email protected]> (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))) )
File 5 of 5: ReverseRegistrar
// 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); // Logged when an operator is added or removed. event ApprovalForAll(address indexed owner, address indexed operator, bool approved); function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external; function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external; function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external returns(bytes32); function setResolver(bytes32 node, address resolver) external; function setOwner(bytes32 node, address owner) external; function setTTL(bytes32 node, uint64 ttl) external; function setApprovalForAll(address operator, bool approved) 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); function recordExists(bytes32 node) external view returns (bool); function isApprovedForAll(address owner, address operator) external view returns (bool); } // File: @ensdomains/ens/contracts/ReverseRegistrar.sol pragma solidity ^0.5.0; contract Resolver { function setName(bytes32 node, string memory name) public; } contract ReverseRegistrar { // namehash('addr.reverse') bytes32 public constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2; ENS public ens; Resolver public defaultResolver; /** * @dev Constructor * @param ensAddr The address of the ENS registry. * @param resolverAddr The address of the default reverse resolver. */ constructor(ENS ensAddr, Resolver resolverAddr) public { ens = ensAddr; defaultResolver = resolverAddr; // Assign ownership of the reverse record to our deployer ReverseRegistrar oldRegistrar = ReverseRegistrar(ens.owner(ADDR_REVERSE_NODE)); if (address(oldRegistrar) != address(0x0)) { oldRegistrar.claim(msg.sender); } } /** * @dev Transfers ownership of the reverse ENS record associated with the * calling account. * @param owner The address to set as the owner of the reverse record in ENS. * @return The ENS node hash of the reverse record. */ function claim(address owner) public returns (bytes32) { return claimWithResolver(owner, address(0x0)); } /** * @dev Transfers ownership of the reverse ENS record associated with the * calling account. * @param owner The address to set as the owner of the reverse record in ENS. * @param resolver The address of the resolver to set; 0 to leave unchanged. * @return The ENS node hash of the reverse record. */ function claimWithResolver(address owner, address resolver) public returns (bytes32) { bytes32 label = sha3HexAddress(msg.sender); bytes32 node = keccak256(abi.encodePacked(ADDR_REVERSE_NODE, label)); address currentOwner = ens.owner(node); // Update the resolver if required if (resolver != address(0x0) && resolver != ens.resolver(node)) { // Transfer the name to us first if it's not already if (currentOwner != address(this)) { ens.setSubnodeOwner(ADDR_REVERSE_NODE, label, address(this)); currentOwner = address(this); } ens.setResolver(node, resolver); } // Update the owner if required if (currentOwner != owner) { ens.setSubnodeOwner(ADDR_REVERSE_NODE, label, owner); } return node; } /** * @dev Sets the `name()` record for the reverse ENS record associated with * the calling account. First updates the resolver to the default reverse * resolver if necessary. * @param name The name to set for this address. * @return The ENS node hash of the reverse record. */ function setName(string memory name) public returns (bytes32) { bytes32 node = claimWithResolver(address(this), address(defaultResolver)); defaultResolver.setName(node, name); return node; } /** * @dev Returns the node hash for a given account's reverse records. * @param addr The address to hash * @return The ENS node hash. */ function node(address addr) public pure returns (bytes32) { return keccak256(abi.encodePacked(ADDR_REVERSE_NODE, sha3HexAddress(addr))); } /** * @dev An optimised function to compute the sha3 of the lower-case * hexadecimal representation of an Ethereum address. * @param addr The address to hash * @return The SHA3 hash of the lower-case hexadecimal encoding of the * input address. */ function sha3HexAddress(address addr) private pure returns (bytes32 ret) { addr; ret; // Stop warning us about unused variables assembly { let lookup := 0x3031323334353637383961626364656600000000000000000000000000000000 for { let i := 40 } gt(i, 0) { } { i := sub(i, 1) mstore8(i, byte(and(addr, 0xf), lookup)) addr := div(addr, 0x10) i := sub(i, 1) mstore8(i, byte(and(addr, 0xf), lookup)) addr := div(addr, 0x10) } ret := keccak256(0, 40) } } }