Every line that differs between the ownerless gwei-names contracts and the upstream wei-names they fork (z0r0z/wei-names@bb47188). Raw diff -u — nothing hidden — with a plain-English note beside each change.
Ownable (import, inheritance, and the constructor's _initializeOwner(tx.origin)) and every owner-only function: setDefaultFee, setLengthFees, clearLengthFee, setPremiumSettings, and withdraw. There is no privileged role left.constants and getFee goes view → pure. With no withdraw and no owner, paid ETH is locked in the contract forever — effectively burned.Unauthorized is now declared directly (it used to come from Ownable).WEI_NODE → GWEI_NODE, name()/symbol() → "Gwei Name Service"/"GWEI", and the suffix strings + suffix-stripping (4-byte .wei → 5-byte .gwei).name address becomes immutable, injected via the constructor — gwei deploys a fresh NameNFT instead of pointing at a known mainnet address.| … | @@ -3 +3 @@ | |
| 3 | 3 | |
| 4 | 4 | import {Base64} from "solady/utils/Base64.sol"; |
| 5 | 5 | import {ERC721} from "solady/tokens/ERC721.sol"; |
| 6 | -import {Ownable} from "solady/auth/Ownable.sol"; | |
| 7 | 6 | import {LibString} from "solady/utils/LibString.sol"; |
| 8 | 7 | import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; |
| 9 | 8 | import {ReentrancyGuard} from "soledge/utils/ReentrancyGuard.sol"; |
| 10 | 9 | |
| 11 | 10 | /// @title NameNFT |
| 12 | -/// @notice ENS-style naming system for .wei TLD with ERC721 ownership | |
| 11 | +/// @notice ENS-style naming system for .gwei TLD with ERC721 ownership | |
| 13 | 12 | /// @dev Token ID = uint256(namehash). ENS-compatible resolution. |
| 14 | 13 | /// |
| 15 | 14 | /// Unicode Support: |
| … | @@ -18 +17 @@ | |
| 18 | 17 | /// - For proper Unicode normalization, callers SHOULD pre-normalize using ENSIP-15 |
| 19 | 18 | /// - Off-chain: use adraffy/ens-normalize library or equivalent before calling |
| 20 | 19 | /// - Example: normalize("RaFFY🚴♂️") => "raffy🚴♂" (do this off-chain, then call contract) |
| 21 | -contract NameNFT is ERC721, Ownable, ReentrancyGuard { | |
| 20 | +contract NameNFT is ERC721, ReentrancyGuard { | |
| 22 | 21 | using LibString for uint256; |
| 23 | 22 | |
| 24 | 23 | /*////////////////////////////////////////////////////////////// |
| … | @@ -29 +28 @@ | |
| 29 | 28 | error TooDeep(); |
| 30 | 29 | error EmptyLabel(); |
| 31 | 30 | error InvalidName(); |
| 31 | + error Unauthorized(); | |
| 32 | 32 | error InvalidLength(); |
| 33 | - error LengthMismatch(); | |
| 34 | 33 | error NotParentOwner(); |
| 35 | - error PremiumTooHigh(); | |
| 36 | 34 | error InsufficientFee(); |
| 37 | 35 | error AlreadyCommitted(); |
| 38 | 36 | error CommitmentTooNew(); |
| 39 | 37 | error CommitmentTooOld(); |
| 40 | 38 | error AlreadyRegistered(); |
| 41 | 39 | error CommitmentNotFound(); |
| 42 | - error DecayPeriodTooLong(); | |
| 43 | 40 | |
| 44 | 41 | /*////////////////////////////////////////////////////////////// |
| 45 | 42 | EVENTS |
| … | @@ -59 +56 @@ | |
| 59 | 56 | event AddressChanged(bytes32 indexed node, uint256 coinType, bytes addr); |
| 60 | 57 | event TextChanged(bytes32 indexed node, string indexed key, string value); |
| 61 | 58 | |
| 62 | - // Admin events | |
| 63 | - event DefaultFeeChanged(uint256 fee); | |
| 64 | - event LengthFeeChanged(uint256 indexed length, uint256 fee); | |
| 65 | - event LengthFeeCleared(uint256 indexed length); | |
| 66 | - event PremiumSettingsChanged(uint256 maxPremium, uint256 decayPeriod); | |
| 67 | - | |
| 68 | 59 | /*////////////////////////////////////////////////////////////// |
| 69 | 60 | CONSTANTS |
| 70 | 61 | //////////////////////////////////////////////////////////////*/ |
| 71 | 62 | |
| 72 | - /// @dev Namehash of "wei" TLD - kept public for off-chain tooling | |
| 73 | - bytes32 public constant WEI_NODE = | |
| 74 | - 0xa82820059d5df798546bcc2985157a77c3eef25eba9ba01899927333efacbd6f; | |
| 63 | + /// @dev Namehash of "gwei" TLD - kept public for off-chain tooling | |
| 64 | + bytes32 public constant GWEI_NODE = | |
| 65 | + 0xcca9c7f2dbe2808af0de2982fc84314bfa68a82a6a60ad5cd757f91a233d7d7f; | |
| 75 | 66 | |
| 76 | 67 | uint256 constant MAX_LABEL_LENGTH = 255; |
| 77 | 68 | uint256 constant MIN_LABEL_LENGTH = 1; |
| … | @@ -81 +72 @@ | |
| 81 | 72 | uint256 constant GRACE_PERIOD = 90 days; |
| 82 | 73 | uint256 constant MAX_SUBDOMAIN_DEPTH = 10; |
| 83 | 74 | uint256 constant COIN_TYPE_ETH = 60; |
| 84 | - uint256 constant MAX_PREMIUM_CAP = 10000 ether; | |
| 85 | - uint256 constant MAX_DECAY_PERIOD = 3650 days; | |
| 86 | - uint256 constant DEFAULT_FEE = 0.001 ether; | |
| 75 | + uint256 constant FEE_LEN1 = 0.5 ether; // 1-byte labels | |
| 76 | + uint256 constant FEE_LEN2 = 0.1 ether; // 2-byte labels | |
| 77 | + uint256 constant FEE_LEN3 = 0.05 ether; // 3-byte labels | |
| 78 | + uint256 constant FEE_LEN4 = 0.01 ether; // 4-byte labels | |
| 79 | + uint256 constant DEFAULT_FEE = 0.0005 ether; // 5+ byte labels | |
| 80 | + uint256 constant MAX_PREMIUM = 100 ether; | |
| 81 | + uint256 constant PREMIUM_DECAY_PERIOD = 21 days; | |
| 87 | 82 | |
| 88 | 83 | /*////////////////////////////////////////////////////////////// |
| 89 | 84 | STORAGE |
| … | @@ -96 +91 @@ | |
| 96 | 91 | uint64 epoch; |
| 97 | 92 | uint64 parentEpoch; |
| 98 | 93 | } |
| 99 | - | |
| 100 | - uint256 public defaultFee; | |
| 101 | - uint256 public maxPremium; | |
| 102 | - uint256 public premiumDecayPeriod; | |
| 103 | 94 | |
| 104 | - mapping(uint256 => uint256) public lengthFees; | |
| 105 | - mapping(uint256 => bool) public lengthFeeSet; | |
| 106 | 95 | mapping(uint256 => NameRecord) public records; |
| 107 | 96 | mapping(uint256 => uint256) public recordVersion; |
| 108 | 97 | mapping(bytes32 => uint256) public commitments; |
| … | @@ -115 +104 @@ | |
| 115 | 104 | mapping(uint256 => mapping(uint256 => mapping(string => string))) internal _text; |
| 116 | 105 | |
| 117 | 106 | /*////////////////////////////////////////////////////////////// |
| 118 | - CONSTRUCTOR | |
| 119 | - //////////////////////////////////////////////////////////////*/ | |
| 120 | - | |
| 121 | - constructor() payable { | |
| 122 | - _initializeOwner(tx.origin); | |
| 123 | - defaultFee = DEFAULT_FEE; | |
| 124 | - maxPremium = 100 ether; | |
| 125 | - premiumDecayPeriod = 21 days; | |
| 126 | - } | |
| 127 | - | |
| 128 | - /*////////////////////////////////////////////////////////////// | |
| 129 | 107 | ERC721 METADATA |
| 130 | 108 | //////////////////////////////////////////////////////////////*/ |
| 131 | 109 | |
| 132 | 110 | function name() public pure override(ERC721) returns (string memory) { |
| 133 | - return "Wei Name Service"; | |
| 111 | + return "Gwei Name Service"; | |
| 134 | 112 | } |
| 135 | 113 | |
| 136 | 114 | function symbol() public pure override(ERC721) returns (string memory) { |
| 137 | - return "WEI"; | |
| 115 | + return "GWEI"; | |
| 138 | 116 | } |
| 139 | 117 | |
| 140 | 118 | /// @dev Blocks transfers of inactive tokens, but allows mint (from==0) and burn (to==0) |
| … | @@ -183 +161 @@ | |
| 183 | 161 | } |
| 184 | 162 | |
| 185 | 163 | string memory fullName = _buildFullName(tokenId); |
| 186 | - fullName = string.concat(fullName, ".wei"); | |
| 164 | + fullName = string.concat(fullName, ".gwei"); | |
| 187 | 165 | string memory displayName = bytes(fullName).length <= 20 |
| 188 | 166 | ? fullName |
| 189 | 167 | : string.concat(_truncateUTF8(fullName, 17), "..."); |
| … | @@ -211 +189 @@ | |
| 211 | 189 | string.concat( |
| 212 | 190 | '{"name":"', |
| 213 | 191 | escapedName, |
| 214 | - '","description":"Wei Name Service: ', | |
| 192 | + '","description":"Gwei Name Service: ', | |
| 215 | 193 | escapedName, |
| 216 | 194 | '","image":"data:image/svg+xml;base64,', |
| 217 | 195 | Base64.encode(bytes(_generateSVG(displayName))), |
| … | @@ -257 +235 @@ | |
| 257 | 235 | uint256 fee = getFee(bytes(label).length); |
| 258 | 236 | bytes memory normalized = _validateAndNormalize(bytes(label)); |
| 259 | 237 | |
| 260 | - tokenId = uint256(keccak256(abi.encodePacked(WEI_NODE, keccak256(normalized)))); | |
| 238 | + tokenId = uint256(keccak256(abi.encodePacked(GWEI_NODE, keccak256(normalized)))); | |
| 261 | 239 | uint256 premium = getPremium(tokenId); |
| 262 | 240 | uint256 total = fee + premium; |
| 263 | 241 | |
| … | @@ -370 +348 @@ | |
| 370 | 348 | function reverseResolve(address addr) public view returns (string memory) { |
| 371 | 349 | uint256 tokenId = primaryName[addr]; |
| 372 | 350 | if (tokenId == 0 || !_isActive(tokenId) || resolve(tokenId) != addr) return ""; |
| 373 | - return string.concat(_buildFullName(tokenId), ".wei"); | |
| 351 | + return string.concat(_buildFullName(tokenId), ".gwei"); | |
| 374 | 352 | } |
| 375 | 353 | |
| 376 | 354 | /*////////////////////////////////////////////////////////////// |
| … | @@ -463 +441 @@ | |
| 463 | 441 | return uint256(computeNamehash(fullName)); |
| 464 | 442 | } |
| 465 | 443 | |
| 466 | - /// @notice Compute namehash for a full name (e.g. "sub.name.wei" or "name") | |
| 444 | + /// @notice Compute namehash for a full name (e.g. "sub.name.gwei" or "name") | |
| 467 | 445 | /// @dev This function is intentionally permissive - it lowercases and hashes any input. |
| 468 | 446 | /// Registration enforces validation: valid UTF-8, no space/control chars/dot. |
| 469 | 447 | /// Use normalize() to check if a label is valid for registration. |
| 470 | 448 | function computeNamehash(string calldata fullName) public pure returns (bytes32 node) { |
| 471 | 449 | bytes memory b = bytes(fullName); |
| 472 | - if (b.length == 0) return WEI_NODE; | |
| 450 | + if (b.length == 0) return GWEI_NODE; | |
| 473 | 451 | |
| 474 | 452 | uint256 len = b.length; |
| 475 | 453 | |
| 476 | - // Strip .wei suffix if present | |
| 454 | + // Strip .gwei suffix if present | |
| 477 | 455 | if ( |
| 478 | - len >= 4 && b[len - 4] == 0x2e && (b[len - 3] == 0x77 || b[len - 3] == 0x57) | |
| 456 | + len >= 5 && b[len - 5] == 0x2e && (b[len - 4] == 0x67 || b[len - 4] == 0x47) | |
| 457 | + && (b[len - 3] == 0x77 || b[len - 3] == 0x57) | |
| 479 | 458 | && (b[len - 2] == 0x65 || b[len - 2] == 0x45) |
| 480 | 459 | && (b[len - 1] == 0x69 || b[len - 1] == 0x49) |
| 481 | 460 | ) { |
| 482 | - len -= 4; | |
| 461 | + len -= 5; | |
| 483 | 462 | } |
| 484 | 463 | |
| 485 | - if (len == 0) return WEI_NODE; | |
| 464 | + if (len == 0) return GWEI_NODE; | |
| 486 | 465 | if (b[0] == 0x2e || b[len - 1] == 0x2e) revert EmptyLabel(); |
| 487 | 466 | |
| 488 | - node = WEI_NODE; | |
| 467 | + node = GWEI_NODE; | |
| 489 | 468 | uint256 labelEnd = len; |
| 490 | 469 | |
| 491 | 470 | for (uint256 i = len; i > 0; --i) { |
| … | @@ -566 +545 @@ | |
| 566 | 545 | // Hyphen rules |
| 567 | 546 | if (normalized[0] == 0x2d || normalized[b.length - 1] == 0x2d) return false; |
| 568 | 547 | |
| 569 | - bytes32 parentNode = parentId == 0 ? WEI_NODE : bytes32(parentId); | |
| 548 | + bytes32 parentNode = parentId == 0 ? GWEI_NODE : bytes32(parentId); | |
| 570 | 549 | uint256 tokenId = uint256(keccak256(abi.encodePacked(parentNode, keccak256(normalized)))); |
| 571 | 550 | |
| 572 | 551 | if (parentId != 0 && !_isActive(parentId)) return false; |
| … | @@ -585 +564 @@ | |
| 585 | 564 | function getFullName(uint256 tokenId) public view returns (string memory) { |
| 586 | 565 | string memory baseName = _buildFullName(tokenId); |
| 587 | 566 | if (bytes(baseName).length == 0) return ""; |
| 588 | - return string.concat(baseName, ".wei"); | |
| 567 | + return string.concat(baseName, ".gwei"); | |
| 589 | 568 | } |
| 590 | 569 | |
| 591 | 570 | /// @notice On-chain normalization (lowercases ASCII only) |
| … | @@ -608 +587 @@ | |
| 608 | 587 | FEE MANAGEMENT |
| 609 | 588 | //////////////////////////////////////////////////////////////*/ |
| 610 | 589 | |
| 611 | - function getFee(uint256 length) public view returns (uint256) { | |
| 612 | - return lengthFeeSet[length] ? lengthFees[length] : defaultFee; | |
| 590 | + function getFee(uint256 length) public pure returns (uint256) { | |
| 591 | + if (length == 1) return FEE_LEN1; | |
| 592 | + if (length == 2) return FEE_LEN2; | |
| 593 | + if (length == 3) return FEE_LEN3; | |
| 594 | + if (length == 4) return FEE_LEN4; | |
| 595 | + return DEFAULT_FEE; | |
| 613 | 596 | } |
| 614 | 597 | |
| 615 | 598 | function getPremium(uint256 tokenId) public view returns (uint256) { |
| 616 | 599 | NameRecord storage record = records[tokenId]; |
| 617 | 600 | if (bytes(record.label).length == 0 || record.parent != 0) return 0; |
| 618 | - if (maxPremium == 0 || premiumDecayPeriod == 0) return 0; | |
| 619 | 601 | |
| 620 | 602 | uint256 gracePeriodEnd = record.expiresAt + GRACE_PERIOD; |
| 621 | 603 | if (block.timestamp <= gracePeriodEnd) return 0; |
| 622 | 604 | |
| 623 | 605 | uint256 elapsed = block.timestamp - gracePeriodEnd; |
| 624 | - if (elapsed >= premiumDecayPeriod) return 0; | |
| 606 | + if (elapsed >= PREMIUM_DECAY_PERIOD) return 0; | |
| 625 | 607 | |
| 626 | - return maxPremium * (premiumDecayPeriod - elapsed) / premiumDecayPeriod; | |
| 627 | - } | |
| 628 | - | |
| 629 | - /*////////////////////////////////////////////////////////////// | |
| 630 | - ADMIN FUNCTIONS | |
| 631 | - //////////////////////////////////////////////////////////////*/ | |
| 632 | - | |
| 633 | - function setDefaultFee(uint256 fee) public onlyOwner { | |
| 634 | - defaultFee = fee; | |
| 635 | - emit DefaultFeeChanged(fee); | |
| 608 | + return MAX_PREMIUM * (PREMIUM_DECAY_PERIOD - elapsed) / PREMIUM_DECAY_PERIOD; | |
| 636 | 609 | } |
| 637 | 610 | |
| 638 | - function setLengthFees(uint256[] calldata lengths, uint256[] calldata fees) public onlyOwner { | |
| 639 | - if (lengths.length != fees.length) revert LengthMismatch(); | |
| 640 | - for (uint256 i; i < lengths.length; ++i) { | |
| 641 | - lengthFees[lengths[i]] = fees[i]; | |
| 642 | - lengthFeeSet[lengths[i]] = true; | |
| 643 | - emit LengthFeeChanged(lengths[i], fees[i]); | |
| 644 | - } | |
| 645 | - } | |
| 646 | - | |
| 647 | - function clearLengthFee(uint256 length) public onlyOwner { | |
| 648 | - delete lengthFees[length]; | |
| 649 | - delete lengthFeeSet[length]; | |
| 650 | - emit LengthFeeCleared(length); | |
| 651 | - } | |
| 652 | - | |
| 653 | - function setPremiumSettings(uint256 _maxPremium, uint256 _decayPeriod) public onlyOwner { | |
| 654 | - if (_maxPremium > MAX_PREMIUM_CAP) revert PremiumTooHigh(); | |
| 655 | - if (_decayPeriod > MAX_DECAY_PERIOD) revert DecayPeriodTooLong(); | |
| 656 | - maxPremium = _maxPremium; | |
| 657 | - premiumDecayPeriod = _decayPeriod; | |
| 658 | - emit PremiumSettingsChanged(_maxPremium, _decayPeriod); | |
| 659 | - } | |
| 660 | - | |
| 661 | - function withdraw() public onlyOwner nonReentrant { | |
| 662 | - SafeTransferLib.safeTransferAllETH(msg.sender); | |
| 663 | - } | |
| 664 | - | |
| 665 | 611 | /*////////////////////////////////////////////////////////////// |
| 666 | 612 | INTERNAL FUNCTIONS |
| 667 | 613 | //////////////////////////////////////////////////////////////*/ |
| … | @@ -671 +617 @@ | |
| 671 | 617 | returns (uint256 tokenId) |
| 672 | 618 | { |
| 673 | 619 | bytes memory normalized = _validateAndNormalize(bytes(label)); |
| 674 | - bytes32 parentNode = parentId == 0 ? WEI_NODE : bytes32(parentId); | |
| 620 | + bytes32 parentNode = parentId == 0 ? GWEI_NODE : bytes32(parentId); | |
| 675 | 621 | tokenId = uint256(keccak256(abi.encodePacked(parentNode, keccak256(normalized)))); |
| 676 | 622 | |
| 677 | 623 | // Invariant: subdomain registration requires parent ownership |
| 678 | 624 | |
| … | @@ -92 +92 @@ | |
| 92 | 92 | address payout; // receives ERC20 directly; ETH via ethBalance |
| 93 | 93 | } |
| 94 | 94 | |
| 95 | - INameNFT public constant name = INameNFT(0x0000000000696760E15f265e828DB644A0c242EB); | |
| 95 | + INameNFT public immutable name; | |
| 96 | 96 | |
| 97 | 97 | mapping(uint256 => Config) public config; |
| 98 | 98 | mapping(uint256 => address) public escrowedController; // nonzero => escrowed, controller recorded |
| … | @@ -101 +101 @@ | |
| 101 | 101 | |
| 102 | 102 | uint256 constant _REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268; |
| 103 | 103 | |
| 104 | - constructor() payable {} | |
| 104 | + constructor(INameNFT nameNFT) payable { | |
| 105 | + name = nameNFT; | |
| 106 | + } | |
| 105 | 107 | |
| 106 | 108 | modifier nonReentrant() virtual { |
| 107 | 109 | assembly ("memory-safe") { |
| 108 | 110 | |