gwei-names — contract diff

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.

What actually changed

Line-by-line, annotated

src/NameNFT.sol+38 −92
@@ -3 +3 @@
33
44 import {Base64} from "solady/utils/Base64.sol";
55 import {ERC721} from "solady/tokens/ERC721.sol";
6-import {Ownable} from "solady/auth/Ownable.sol";
76 import {LibString} from "solady/utils/LibString.sol";
87 import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
98 import {ReentrancyGuard} from "soledge/utils/ReentrancyGuard.sol";
109
1110 /// @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
1312 /// @dev Token ID = uint256(namehash). ENS-compatible resolution.
1413 ///
1514 /// Unicode Support:
@@ -18 +17 @@
1817 /// - For proper Unicode normalization, callers SHOULD pre-normalize using ENSIP-15
1918 /// - Off-chain: use adraffy/ens-normalize library or equivalent before calling
2019 /// - Example: normalize("RaFFY🚴‍♂️") => "raffy🚴‍♂" (do this off-chain, then call contract)
21-contract NameNFT is ERC721, Ownable, ReentrancyGuard {
20+contract NameNFT is ERC721, ReentrancyGuard {
2221 using LibString for uint256;
2322
2423 /*//////////////////////////////////////////////////////////////
@@ -29 +28 @@
2928 error TooDeep();
3029 error EmptyLabel();
3130 error InvalidName();
31+ error Unauthorized();
3232 error InvalidLength();
33- error LengthMismatch();
3433 error NotParentOwner();
35- error PremiumTooHigh();
3634 error InsufficientFee();
3735 error AlreadyCommitted();
3836 error CommitmentTooNew();
3937 error CommitmentTooOld();
4038 error AlreadyRegistered();
4139 error CommitmentNotFound();
42- error DecayPeriodTooLong();
4340
4441 /*//////////////////////////////////////////////////////////////
4542 EVENTS
@@ -59 +56 @@
5956 event AddressChanged(bytes32 indexed node, uint256 coinType, bytes addr);
6057 event TextChanged(bytes32 indexed node, string indexed key, string value);
6158
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-
6859 /*//////////////////////////////////////////////////////////////
6960 CONSTANTS
7061 //////////////////////////////////////////////////////////////*/
7162
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;
7566
7667 uint256 constant MAX_LABEL_LENGTH = 255;
7768 uint256 constant MIN_LABEL_LENGTH = 1;
@@ -81 +72 @@
8172 uint256 constant GRACE_PERIOD = 90 days;
8273 uint256 constant MAX_SUBDOMAIN_DEPTH = 10;
8374 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;
8782
8883 /*//////////////////////////////////////////////////////////////
8984 STORAGE
@@ -96 +91 @@
9691 uint64 epoch;
9792 uint64 parentEpoch;
9893 }
99-
100- uint256 public defaultFee;
101- uint256 public maxPremium;
102- uint256 public premiumDecayPeriod;
10394
104- mapping(uint256 => uint256) public lengthFees;
105- mapping(uint256 => bool) public lengthFeeSet;
10695 mapping(uint256 => NameRecord) public records;
10796 mapping(uint256 => uint256) public recordVersion;
10897 mapping(bytes32 => uint256) public commitments;
@@ -115 +104 @@
115104 mapping(uint256 => mapping(uint256 => mapping(string => string))) internal _text;
116105
117106 /*//////////////////////////////////////////////////////////////
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- /*//////////////////////////////////////////////////////////////
129107 ERC721 METADATA
130108 //////////////////////////////////////////////////////////////*/
131109
132110 function name() public pure override(ERC721) returns (string memory) {
133- return "Wei Name Service";
111+ return "Gwei Name Service";
134112 }
135113
136114 function symbol() public pure override(ERC721) returns (string memory) {
137- return "WEI";
115+ return "GWEI";
138116 }
139117
140118 /// @dev Blocks transfers of inactive tokens, but allows mint (from==0) and burn (to==0)
@@ -183 +161 @@
183161 }
184162
185163 string memory fullName = _buildFullName(tokenId);
186- fullName = string.concat(fullName, ".wei");
164+ fullName = string.concat(fullName, ".gwei");
187165 string memory displayName = bytes(fullName).length <= 20
188166 ? fullName
189167 : string.concat(_truncateUTF8(fullName, 17), "...");
@@ -211 +189 @@
211189 string.concat(
212190 '{"name":"',
213191 escapedName,
214- '","description":"Wei Name Service: ',
192+ '","description":"Gwei Name Service: ',
215193 escapedName,
216194 '","image":"data:image/svg+xml;base64,',
217195 Base64.encode(bytes(_generateSVG(displayName))),
@@ -257 +235 @@
257235 uint256 fee = getFee(bytes(label).length);
258236 bytes memory normalized = _validateAndNormalize(bytes(label));
259237
260- tokenId = uint256(keccak256(abi.encodePacked(WEI_NODE, keccak256(normalized))));
238+ tokenId = uint256(keccak256(abi.encodePacked(GWEI_NODE, keccak256(normalized))));
261239 uint256 premium = getPremium(tokenId);
262240 uint256 total = fee + premium;
263241
@@ -370 +348 @@
370348 function reverseResolve(address addr) public view returns (string memory) {
371349 uint256 tokenId = primaryName[addr];
372350 if (tokenId == 0 || !_isActive(tokenId) || resolve(tokenId) != addr) return "";
373- return string.concat(_buildFullName(tokenId), ".wei");
351+ return string.concat(_buildFullName(tokenId), ".gwei");
374352 }
375353
376354 /*//////////////////////////////////////////////////////////////
@@ -463 +441 @@
463441 return uint256(computeNamehash(fullName));
464442 }
465443
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")
467445 /// @dev This function is intentionally permissive - it lowercases and hashes any input.
468446 /// Registration enforces validation: valid UTF-8, no space/control chars/dot.
469447 /// Use normalize() to check if a label is valid for registration.
470448 function computeNamehash(string calldata fullName) public pure returns (bytes32 node) {
471449 bytes memory b = bytes(fullName);
472- if (b.length == 0) return WEI_NODE;
450+ if (b.length == 0) return GWEI_NODE;
473451
474452 uint256 len = b.length;
475453
476- // Strip .wei suffix if present
454+ // Strip .gwei suffix if present
477455 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)
479458 && (b[len - 2] == 0x65 || b[len - 2] == 0x45)
480459 && (b[len - 1] == 0x69 || b[len - 1] == 0x49)
481460 ) {
482- len -= 4;
461+ len -= 5;
483462 }
484463
485- if (len == 0) return WEI_NODE;
464+ if (len == 0) return GWEI_NODE;
486465 if (b[0] == 0x2e || b[len - 1] == 0x2e) revert EmptyLabel();
487466
488- node = WEI_NODE;
467+ node = GWEI_NODE;
489468 uint256 labelEnd = len;
490469
491470 for (uint256 i = len; i > 0; --i) {
@@ -566 +545 @@
566545 // Hyphen rules
567546 if (normalized[0] == 0x2d || normalized[b.length - 1] == 0x2d) return false;
568547
569- bytes32 parentNode = parentId == 0 ? WEI_NODE : bytes32(parentId);
548+ bytes32 parentNode = parentId == 0 ? GWEI_NODE : bytes32(parentId);
570549 uint256 tokenId = uint256(keccak256(abi.encodePacked(parentNode, keccak256(normalized))));
571550
572551 if (parentId != 0 && !_isActive(parentId)) return false;
@@ -585 +564 @@
585564 function getFullName(uint256 tokenId) public view returns (string memory) {
586565 string memory baseName = _buildFullName(tokenId);
587566 if (bytes(baseName).length == 0) return "";
588- return string.concat(baseName, ".wei");
567+ return string.concat(baseName, ".gwei");
589568 }
590569
591570 /// @notice On-chain normalization (lowercases ASCII only)
@@ -608 +587 @@
608587 FEE MANAGEMENT
609588 //////////////////////////////////////////////////////////////*/
610589
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;
613596 }
614597
615598 function getPremium(uint256 tokenId) public view returns (uint256) {
616599 NameRecord storage record = records[tokenId];
617600 if (bytes(record.label).length == 0 || record.parent != 0) return 0;
618- if (maxPremium == 0 || premiumDecayPeriod == 0) return 0;
619601
620602 uint256 gracePeriodEnd = record.expiresAt + GRACE_PERIOD;
621603 if (block.timestamp <= gracePeriodEnd) return 0;
622604
623605 uint256 elapsed = block.timestamp - gracePeriodEnd;
624- if (elapsed >= premiumDecayPeriod) return 0;
606+ if (elapsed >= PREMIUM_DECAY_PERIOD) return 0;
625607
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;
636609 }
637610
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-
665611 /*//////////////////////////////////////////////////////////////
666612 INTERNAL FUNCTIONS
667613 //////////////////////////////////////////////////////////////*/
@@ -671 +617 @@
671617 returns (uint256 tokenId)
672618 {
673619 bytes memory normalized = _validateAndNormalize(bytes(label));
674- bytes32 parentNode = parentId == 0 ? WEI_NODE : bytes32(parentId);
620+ bytes32 parentNode = parentId == 0 ? GWEI_NODE : bytes32(parentId);
675621 tokenId = uint256(keccak256(abi.encodePacked(parentNode, keccak256(normalized))));
676622
677623 // Invariant: subdomain registration requires parent ownership
678624
src/SubdomainRegistrar.sol+4 −2
@@ -92 +92 @@
9292 address payout; // receives ERC20 directly; ETH via ethBalance
9393 }
9494
95- INameNFT public constant name = INameNFT(0x0000000000696760E15f265e828DB644A0c242EB);
95+ INameNFT public immutable name;
9696
9797 mapping(uint256 => Config) public config;
9898 mapping(uint256 => address) public escrowedController; // nonzero => escrowed, controller recorded
@@ -101 +101 @@
101101
102102 uint256 constant _REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268;
103103
104- constructor() payable {}
104+ constructor(INameNFT nameNFT) payable {
105+ name = nameNFT;
106+ }
105107
106108 modifier nonReentrant() virtual {
107109 assembly ("memory-safe") {
108110