From f57a162d61c8d3134e9476c1e231d8e1c18a9f22 Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Sun, 18 Feb 2024 22:58:58 +0700 Subject: [PATCH 01/11] fix(RNSDomainPrice): add tier overriding functionalities --- ...gradeRNSAuctionAndDeployRNSOperation.s.sol | 17 +++++-- src/RNSAuction.sol | 1 - src/RNSDomainPrice.sol | 46 +++++++++++++++++++ src/interfaces/INSDomainPrice.sol | 31 +++++++++++++ test/RNSUnified/RNSUnified.namehash.t.sol | 4 +- 5 files changed, 93 insertions(+), 6 deletions(-) diff --git a/script/20231205-deploy-upgrade-auction-and-deploy-rns-operation/20231205_UpgradeRNSAuctionAndDeployRNSOperation.s.sol b/script/20231205-deploy-upgrade-auction-and-deploy-rns-operation/20231205_UpgradeRNSAuctionAndDeployRNSOperation.s.sol index d6851b42..aee0b91f 100644 --- a/script/20231205-deploy-upgrade-auction-and-deploy-rns-operation/20231205_UpgradeRNSAuctionAndDeployRNSOperation.s.sol +++ b/script/20231205-deploy-upgrade-auction-and-deploy-rns-operation/20231205_UpgradeRNSAuctionAndDeployRNSOperation.s.sol @@ -5,9 +5,20 @@ import { console2 as console } from "forge-std/console2.sol"; import { ITransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import { ProxyAdmin } from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import { ContractKey } from "foundry-deployment-kit/configs/ContractConfig.sol"; -import { Network, Config__Mainnet20231205 } from "script/20231205-deploy-upgrade-auction-and-deploy-rns-operation/20231205_MainnetConfig.s.sol"; -import { INSAuction, RNSAuction, RNSUnified, Migration__20231123_UpgradeAuctionClaimeUnbiddedNames as UpgradeAuctionScript } from "script/20231123-upgrade-auction-claim-unbidded-names/20231123_UpgradeAuctionClaimUnbiddedNames.s.sol"; -import { RNSOperation, Migration__20231124_DeployRNSOperation as DeployRNSOperationScript } from "script/20231124-deploy-rns-operation/20231124_DeployRNSOperation.s.sol"; +import { + Network, + Config__Mainnet20231205 +} from "script/20231205-deploy-upgrade-auction-and-deploy-rns-operation/20231205_MainnetConfig.s.sol"; +import { + INSAuction, + RNSAuction, + RNSUnified, + Migration__20231123_UpgradeAuctionClaimeUnbiddedNames as UpgradeAuctionScript +} from "script/20231123-upgrade-auction-claim-unbidded-names/20231123_UpgradeAuctionClaimUnbiddedNames.s.sol"; +import { + RNSOperation, + Migration__20231124_DeployRNSOperation as DeployRNSOperationScript +} from "script/20231124-deploy-rns-operation/20231124_DeployRNSOperation.s.sol"; contract Migration__20231205_UpgradeRNSAuctionAndDeployRNSOperation is Config__Mainnet20231205 { function run() public trySetUp onMainnet { diff --git a/src/RNSAuction.sol b/src/RNSAuction.sol index 86f66cfa..2a5d41a2 100644 --- a/src/RNSAuction.sol +++ b/src/RNSAuction.sol @@ -309,7 +309,6 @@ contract RNSAuction is Initializable, AccessControlEnumerable, INSAuction { /** * @inheritdoc INSAuction */ - function setBidGapRatio(uint256 ratio) external onlyRole(DEFAULT_ADMIN_ROLE) { _setBidGapRatio(ratio); } diff --git a/src/RNSDomainPrice.sol b/src/RNSDomainPrice.sol index 0187f2a6..144f3bdb 100644 --- a/src/RNSDomainPrice.sol +++ b/src/RNSDomainPrice.sol @@ -54,6 +54,8 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric mapping(bytes32 lbHash => TimestampWrapper usdPrice) internal _dp; /// @dev Mapping from name => inverse bitwise of renewal fee overriding. mapping(bytes32 lbHash => uint256 usdPrice) internal _rnFeeOverriding; + /// @dev Mapping from label hash to overriden tier + mapping(bytes32 lbHash => uint256 tier) _tierOverriding; constructor() payable { _disableInitializers(); @@ -167,6 +169,15 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric return ~usdFee; } + /** + * @inheritdoc INSDomainPrice + */ + function getOverriddenTier(string calldata label) external view returns (uint256 tier) { + tier = _tierOverriding[label.hashLabel()]; + if (tier == 0) revert TierIsNotOverriden(); + return ~tier; + } + /** * @inheritdoc INSDomainPrice */ @@ -190,6 +201,26 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric } } + /** + * @inheritdoc INSDomainPrice + */ + function bulkOverrideTiers(bytes32[] calldata lbHashes, uint256[] calldata tiers) external onlyRole(OVERRIDER_ROLE) { + uint256 length = lbHashes.length; + if (length == 0 || length != tiers.length) revert InvalidArrayLength(); + uint256 inverseBitwise; + address operator = _msgSender(); + + for (uint256 i; i < length;) { + inverseBitwise = ~tiers[i]; + _tierOverriding[lbHashes[i]] = inverseBitwise; + emit TierOverridingUpdated(operator, lbHashes[i], inverseBitwise); + + unchecked { + ++i; + } + } + } + /** * @inheritdoc INSDomainPrice */ @@ -240,6 +271,21 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric ronPrice = convertUSDToRON(usdPrice); } + /** + * @inheritdoc INSDomainPrice + */ + function getTier(string memory label) public view returns (uint256 tier) { + bytes32 lbHash = label.hashLabel(); + uint256 overriddenTier = _tierOverriding[lbHash]; + + if (overriddenTier != 0) return ~overriddenTier; + + uint256 renewalFeeByLength = _rnFee[Math.min(label.strlen(), _rnfMaxLength)]; + uint256 domainPrice = _getDomainPrice(lbHash); + + return renewalFeeByLength + domainPrice / 2; + } + /** * @inheritdoc INSDomainPrice */ diff --git a/src/interfaces/INSDomainPrice.sol b/src/interfaces/INSDomainPrice.sol index 376284fe..c66c8861 100644 --- a/src/interfaces/INSDomainPrice.sol +++ b/src/interfaces/INSDomainPrice.sol @@ -7,6 +7,7 @@ import { IPyth } from "@pythnetwork/IPyth.sol"; interface INSDomainPrice { error InvalidArrayLength(); error RenewalFeeIsNotOverriden(); + error TierIsNotOverriden(); error ExceedAuctionDomainExpiry(); struct RenewalFee { @@ -27,6 +28,8 @@ interface INSDomainPrice { event RenewalFeeByLengthUpdated(address indexed operator, uint256 indexed labelLength, uint256 renewalFee); /// @dev Emitted when the renew fee of a domain is overridden. Value of `inverseRenewalFee` is 0 when not overridden. event RenewalFeeOverridingUpdated(address indexed operator, bytes32 indexed labelHash, uint256 inverseRenewalFee); + /// @dev Emitted when the tier of a domain is overridden. + event TierOverridingUpdated(address indexed operator, bytes32 indexed labelHash, uint256 indexed tier); /// @dev Emitted when the domain price is updated. event DomainPriceUpdated( @@ -119,6 +122,13 @@ interface INSDomainPrice { view returns (UnitPrice memory basePrice, UnitPrice memory tax); + /** + * @dev Returns the tier of a label. + * @param label The domain label to register (Eg, 'foo' for 'foo.ron'). + * @return tier The tier of the label. + */ + function getTier(string calldata label) external view returns (uint256 tier); + /** * @dev Returns the renewal fee of a label. Reverts if not overridden. * @notice This method is to help developers check the domain renewal fee overriding. Consider using method @@ -126,6 +136,13 @@ interface INSDomainPrice { */ function getOverriddenRenewalFee(string memory label) external view returns (uint256 usdFee); + /** + * @dev Returns the tier of a label. Reverts if not overridden. + * @notice This method is to help developers check the domain tier overriding. Consider using method {getTier} instead + * for full handling of tiers. + */ + function getOverriddenTier(string memory label) external view returns (uint256 tier); + /** * @dev Bulk override renewal fees. * @@ -140,6 +157,20 @@ interface INSDomainPrice { */ function bulkOverrideRenewalFees(bytes32[] calldata lbHashes, uint256[] calldata usdPrices) external; + /** + * @dev Bulk override tiers. + * + * Requirements: + * - The method caller is operator. + * - The input array lengths must be larger than 0 and the same. + * + * Emits events {TierOverridingUpdated}. + * + * @param lbHashes Array of label hashes. (Eg, ['foo'].map(keccak256) for 'foo.ron') + * @param tiers Array of tiers. Leave 2^256 - 1 to remove overriding. + */ + function bulkOverrideTiers(bytes32[] calldata lbHashes, uint256[] calldata tiers) external; + /** * @dev Bulk try to set domain prices. Returns a boolean array indicating whether domain prices at the corresponding * indexes if set or not. diff --git a/test/RNSUnified/RNSUnified.namehash.t.sol b/test/RNSUnified/RNSUnified.namehash.t.sol index 6f2e8f70..2d3b537f 100644 --- a/test/RNSUnified/RNSUnified.namehash.t.sol +++ b/test/RNSUnified/RNSUnified.namehash.t.sol @@ -2,10 +2,10 @@ pragma solidity ^0.8.19; import "./RNSUnified.t.sol"; -import { LibString } from "solady/utils/LibString.sol"; +import { LibString as SoladyLibString } from "solady/utils/LibString.sol"; contract RNSUnified_NameHash_Test is RNSUnifiedTest { - using LibString for *; + using SoladyLibString for *; function testGas_namehash(string calldata domainName) external view { _rns.namehash(domainName); From fd00c6314dbff975def85fe9301db4dba48252fa Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Mon, 19 Feb 2024 00:18:26 +0700 Subject: [PATCH 02/11] fix(RNSDomainPrice): fix tier logic --- src/RNSDomainPrice.sol | 14 ++++++++++++-- src/interfaces/INSDomainPrice.sol | 8 ++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/RNSDomainPrice.sol b/src/RNSDomainPrice.sol index 144f3bdb..9a7d681a 100644 --- a/src/RNSDomainPrice.sol +++ b/src/RNSDomainPrice.sol @@ -21,6 +21,10 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric using LibPeriodScaler for PeriodScaler; using PythConverter for PythStructs.Price; + /// @dev The threshold tier value (in USD) for Tier 1 + uint256 private constant TIER_1_THRESHOLD = 200e18; + /// @dev The threshold tier value (in USD) for Tier 2 + uint256 private constant TIER_2_THRESHOLD = 50e18; /// @inheritdoc INSDomainPrice uint8 public constant USD_DECIMALS = 18; /// @inheritdoc INSDomainPrice @@ -281,9 +285,15 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric if (overriddenTier != 0) return ~overriddenTier; uint256 renewalFeeByLength = _rnFee[Math.min(label.strlen(), _rnfMaxLength)]; - uint256 domainPrice = _getDomainPrice(lbHash); + uint256 tierValue = renewalFeeByLength + _getDomainPrice(lbHash) / 2; - return renewalFeeByLength + domainPrice / 2; + if (tierValue > 200e18) { + return uint256(Tier.Tier1); + } else if (tierValue > 50e18) { + return uint256(Tier.Tier2); + } else { + return uint256(Tier.Tier3); + } } /** diff --git a/src/interfaces/INSDomainPrice.sol b/src/interfaces/INSDomainPrice.sol index c66c8861..6e444cb9 100644 --- a/src/interfaces/INSDomainPrice.sol +++ b/src/interfaces/INSDomainPrice.sol @@ -10,6 +10,14 @@ interface INSDomainPrice { error TierIsNotOverriden(); error ExceedAuctionDomainExpiry(); + /// @dev The tier of a domain. + enum Tier { + Unknown, + Tier1, + Tier2, + Tier3 + } + struct RenewalFee { uint256 labelLength; uint256 fee; From 773d80f6baf131832e6c6b87bf03acd37dd4228e Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Tue, 20 Feb 2024 13:27:49 +0700 Subject: [PATCH 03/11] fix(RNSDomainPrice): minor refactor --- src/RNSDomainPrice.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/RNSDomainPrice.sol b/src/RNSDomainPrice.sol index 9a7d681a..12caca0f 100644 --- a/src/RNSDomainPrice.sol +++ b/src/RNSDomainPrice.sol @@ -287,9 +287,9 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric uint256 renewalFeeByLength = _rnFee[Math.min(label.strlen(), _rnfMaxLength)]; uint256 tierValue = renewalFeeByLength + _getDomainPrice(lbHash) / 2; - if (tierValue > 200e18) { + if (tierValue > TIER_1_THRESHOLD) { return uint256(Tier.Tier1); - } else if (tierValue > 50e18) { + } else if (tierValue > TIER_2_THRESHOLD) { return uint256(Tier.Tier2); } else { return uint256(Tier.Tier3); From 61135baf1e683477c74148aa0a3ec654fa0dc3cd Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Tue, 20 Feb 2024 14:49:26 +0700 Subject: [PATCH 04/11] fix(RNSOperation): add bulkOverrideTiers --- src/utils/RNSOperation.sol | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/utils/RNSOperation.sol b/src/utils/RNSOperation.sol index cdfcbb0f..9d0c053b 100644 --- a/src/utils/RNSOperation.sol +++ b/src/utils/RNSOperation.sol @@ -55,7 +55,7 @@ contract RNSOperation is Ownable { bytes32[] memory lbHashes = new bytes32[](labels.length); for (uint256 i; i < lbHashes.length; ++i) { - lbHashes[i] = keccak256(bytes(labels[i])); + lbHashes[i] = LibRNSDomain.hashLabel(labels[i]); } uint256[] memory usdPrices = new uint256[](yearlyUSDPrices.length); for (uint256 i; i < usdPrices.length; ++i) { @@ -65,6 +65,23 @@ contract RNSOperation is Ownable { domainPrice.bulkOverrideRenewalFees(lbHashes, usdPrices); } + /** + * @dev Allows the owner to bulk override the tiers for specified RNS domains. + * @param labels The array of labels for the RNS domains. + * @param tiers The array of tiers for the corresponding RNS domains. + * @dev The `tiers` array should represent the tiers for each domain. + */ + function bulkOverrideTiers(string[] calldata labels, uint256[] calldata tiers) external onlyOwner { + require(labels.length == tiers.length, "RNSOperation: length mismatch"); + + bytes32[] memory lbHashes = new bytes32[](labels.length); + for (uint256 i; i < lbHashes.length; ++i) { + lbHashes[i] = LibRNSDomain.hashLabel(labels[i]); + } + + domainPrice.bulkOverrideTiers(lbHashes, tiers); + } + /** * @dev Allows the owner to reclaim unbidded RNS domain names and transfer them to specified addresses. * @param tos The array of addresses to which the unbidded domains will be transferred. From 087dab9510ef44ef06a3583a99938fba3ff0d170 Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Tue, 20 Feb 2024 15:48:44 +0700 Subject: [PATCH 05/11] fix(RNSDomainPrice): fix tier logic --- src/RNSDomainPrice.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RNSDomainPrice.sol b/src/RNSDomainPrice.sol index 12caca0f..a038fbc9 100644 --- a/src/RNSDomainPrice.sol +++ b/src/RNSDomainPrice.sol @@ -285,7 +285,7 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric if (overriddenTier != 0) return ~overriddenTier; uint256 renewalFeeByLength = _rnFee[Math.min(label.strlen(), _rnfMaxLength)]; - uint256 tierValue = renewalFeeByLength + _getDomainPrice(lbHash) / 2; + uint256 tierValue = renewalFeeByLength * 365 days + _getDomainPrice(lbHash) * 365 days / 2; if (tierValue > TIER_1_THRESHOLD) { return uint256(Tier.Tier1); From bc623c2b91b1f41ca2e3b0cba909dfa0769aaef5 Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Wed, 21 Feb 2024 10:26:12 +0700 Subject: [PATCH 06/11] fix(RNSDomainPrice): fix tier logic --- src/RNSDomainPrice.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/RNSDomainPrice.sol b/src/RNSDomainPrice.sol index a038fbc9..35522f37 100644 --- a/src/RNSDomainPrice.sol +++ b/src/RNSDomainPrice.sol @@ -284,8 +284,11 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric if (overriddenTier != 0) return ~overriddenTier; - uint256 renewalFeeByLength = _rnFee[Math.min(label.strlen(), _rnfMaxLength)]; - uint256 tierValue = renewalFeeByLength * 365 days + _getDomainPrice(lbHash) * 365 days / 2; + uint256 overriddenRenewalFee = _rnFeeOverriding[lbHash]; + uint256 yearlyRenewalFeeByLength = overriddenRenewalFee != 0 + ? 365 days * ~overriddenRenewalFee + : 365 days * _rnFee[Math.min(label.strlen(), _rnfMaxLength)]; + uint256 tierValue = yearlyRenewalFeeByLength + _getDomainPrice(lbHash) / 2; if (tierValue > TIER_1_THRESHOLD) { return uint256(Tier.Tier1); From aeb1a623c31f0efc52fedfda385f887c9a920aa0 Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Wed, 21 Feb 2024 15:39:20 +0700 Subject: [PATCH 07/11] fix: apply suggestions --- src/RNSDomainPrice.sol | 38 +++++++++++++++---------------- src/interfaces/INSDomainPrice.sol | 8 +++---- src/utils/RNSOperation.sol | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/RNSDomainPrice.sol b/src/RNSDomainPrice.sol index 35522f37..b7b8fd84 100644 --- a/src/RNSDomainPrice.sol +++ b/src/RNSDomainPrice.sol @@ -22,9 +22,9 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric using PythConverter for PythStructs.Price; /// @dev The threshold tier value (in USD) for Tier 1 - uint256 private constant TIER_1_THRESHOLD = 200e18; + uint256 private constant TIER_1_FROM_EXCLUDED_THRESHOLD = 200e18; /// @dev The threshold tier value (in USD) for Tier 2 - uint256 private constant TIER_2_THRESHOLD = 50e18; + uint256 private constant TIER_2_FROM_EXCLUDED_THRESHOLD = 50e18; /// @inheritdoc INSDomainPrice uint8 public constant USD_DECIMALS = 18; /// @inheritdoc INSDomainPrice @@ -59,7 +59,7 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric /// @dev Mapping from name => inverse bitwise of renewal fee overriding. mapping(bytes32 lbHash => uint256 usdPrice) internal _rnFeeOverriding; /// @dev Mapping from label hash to overriden tier - mapping(bytes32 lbHash => uint256 tier) _tierOverriding; + mapping(bytes32 lbHash => uint8 tier) internal _tierOverriding; constructor() payable { _disableInitializers(); @@ -176,10 +176,10 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric /** * @inheritdoc INSDomainPrice */ - function getOverriddenTier(string calldata label) external view returns (uint256 tier) { - tier = _tierOverriding[label.hashLabel()]; - if (tier == 0) revert TierIsNotOverriden(); - return ~tier; + function getOverriddenTier(string calldata label) external view returns (Tier tier) { + uint8 tierValue = _tierOverriding[label.hashLabel()]; + if (tierValue == 0) revert TierIsNotOverriden(); + return Tier(~tierValue); } /** @@ -208,16 +208,16 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric /** * @inheritdoc INSDomainPrice */ - function bulkOverrideTiers(bytes32[] calldata lbHashes, uint256[] calldata tiers) external onlyRole(OVERRIDER_ROLE) { + function bulkOverrideTiers(bytes32[] calldata lbHashes, Tier[] calldata tiers) external onlyRole(OVERRIDER_ROLE) { uint256 length = lbHashes.length; if (length == 0 || length != tiers.length) revert InvalidArrayLength(); - uint256 inverseBitwise; + uint8 inverseBitwise; address operator = _msgSender(); for (uint256 i; i < length;) { - inverseBitwise = ~tiers[i]; + inverseBitwise = ~uint8(tiers[i]); _tierOverriding[lbHashes[i]] = inverseBitwise; - emit TierOverridingUpdated(operator, lbHashes[i], inverseBitwise); + emit TierOverridingUpdated(operator, lbHashes[i], Tier(inverseBitwise)); unchecked { ++i; @@ -278,11 +278,11 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric /** * @inheritdoc INSDomainPrice */ - function getTier(string memory label) public view returns (uint256 tier) { + function getTier(string memory label) public view returns (Tier tier) { bytes32 lbHash = label.hashLabel(); - uint256 overriddenTier = _tierOverriding[lbHash]; + uint8 overriddenTier = _tierOverriding[lbHash]; - if (overriddenTier != 0) return ~overriddenTier; + if (overriddenTier != 0) return Tier(~overriddenTier); uint256 overriddenRenewalFee = _rnFeeOverriding[lbHash]; uint256 yearlyRenewalFeeByLength = overriddenRenewalFee != 0 @@ -290,12 +290,12 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric : 365 days * _rnFee[Math.min(label.strlen(), _rnfMaxLength)]; uint256 tierValue = yearlyRenewalFeeByLength + _getDomainPrice(lbHash) / 2; - if (tierValue > TIER_1_THRESHOLD) { - return uint256(Tier.Tier1); - } else if (tierValue > TIER_2_THRESHOLD) { - return uint256(Tier.Tier2); + if (tierValue > TIER_1_FROM_EXCLUDED_THRESHOLD) { + return Tier.Tier1; + } else if (tierValue > TIER_2_FROM_EXCLUDED_THRESHOLD) { + return Tier.Tier2; } else { - return uint256(Tier.Tier3); + return Tier.Tier3; } } diff --git a/src/interfaces/INSDomainPrice.sol b/src/interfaces/INSDomainPrice.sol index 6e444cb9..59586748 100644 --- a/src/interfaces/INSDomainPrice.sol +++ b/src/interfaces/INSDomainPrice.sol @@ -37,7 +37,7 @@ interface INSDomainPrice { /// @dev Emitted when the renew fee of a domain is overridden. Value of `inverseRenewalFee` is 0 when not overridden. event RenewalFeeOverridingUpdated(address indexed operator, bytes32 indexed labelHash, uint256 inverseRenewalFee); /// @dev Emitted when the tier of a domain is overridden. - event TierOverridingUpdated(address indexed operator, bytes32 indexed labelHash, uint256 indexed tier); + event TierOverridingUpdated(address indexed operator, bytes32 indexed labelHash, Tier indexed tier); /// @dev Emitted when the domain price is updated. event DomainPriceUpdated( @@ -135,7 +135,7 @@ interface INSDomainPrice { * @param label The domain label to register (Eg, 'foo' for 'foo.ron'). * @return tier The tier of the label. */ - function getTier(string calldata label) external view returns (uint256 tier); + function getTier(string calldata label) external view returns (Tier tier); /** * @dev Returns the renewal fee of a label. Reverts if not overridden. @@ -149,7 +149,7 @@ interface INSDomainPrice { * @notice This method is to help developers check the domain tier overriding. Consider using method {getTier} instead * for full handling of tiers. */ - function getOverriddenTier(string memory label) external view returns (uint256 tier); + function getOverriddenTier(string memory label) external view returns (Tier tier); /** * @dev Bulk override renewal fees. @@ -177,7 +177,7 @@ interface INSDomainPrice { * @param lbHashes Array of label hashes. (Eg, ['foo'].map(keccak256) for 'foo.ron') * @param tiers Array of tiers. Leave 2^256 - 1 to remove overriding. */ - function bulkOverrideTiers(bytes32[] calldata lbHashes, uint256[] calldata tiers) external; + function bulkOverrideTiers(bytes32[] calldata lbHashes, Tier[] calldata tiers) external; /** * @dev Bulk try to set domain prices. Returns a boolean array indicating whether domain prices at the corresponding diff --git a/src/utils/RNSOperation.sol b/src/utils/RNSOperation.sol index 9d0c053b..8365bb49 100644 --- a/src/utils/RNSOperation.sol +++ b/src/utils/RNSOperation.sol @@ -71,7 +71,7 @@ contract RNSOperation is Ownable { * @param tiers The array of tiers for the corresponding RNS domains. * @dev The `tiers` array should represent the tiers for each domain. */ - function bulkOverrideTiers(string[] calldata labels, uint256[] calldata tiers) external onlyOwner { + function bulkOverrideTiers(string[] calldata labels, INSDomainPrice.Tier[] calldata tiers) external onlyOwner { require(labels.length == tiers.length, "RNSOperation: length mismatch"); bytes32[] memory lbHashes = new bytes32[](labels.length); From e3d0e45fa4e62f777b6d90c4ba92824ad5fa7eec Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Wed, 21 Feb 2024 15:40:33 +0700 Subject: [PATCH 08/11] chore: update dev doc --- src/RNSDomainPrice.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/RNSDomainPrice.sol b/src/RNSDomainPrice.sol index b7b8fd84..57c18dde 100644 --- a/src/RNSDomainPrice.sol +++ b/src/RNSDomainPrice.sol @@ -21,9 +21,9 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric using LibPeriodScaler for PeriodScaler; using PythConverter for PythStructs.Price; - /// @dev The threshold tier value (in USD) for Tier 1 + /// @dev The threshold tier value (in USD) for Tier 1: > $200 uint256 private constant TIER_1_FROM_EXCLUDED_THRESHOLD = 200e18; - /// @dev The threshold tier value (in USD) for Tier 2 + /// @dev The threshold tier value (in USD) for Tier 2 in range of ($50; $200] uint256 private constant TIER_2_FROM_EXCLUDED_THRESHOLD = 50e18; /// @inheritdoc INSDomainPrice uint8 public constant USD_DECIMALS = 18; From 4374e605304420889026daad9981a2445c24b92b Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Wed, 21 Feb 2024 16:28:21 +0700 Subject: [PATCH 09/11] fix(RNSDomainPrice): refactor getRenewalFee logic --- src/RNSDomainPrice.sol | 80 ++++++++++++++++++++------------ test/RNSUnified/RNSUnified.t.sol | 8 +++- 2 files changed, 58 insertions(+), 30 deletions(-) diff --git a/src/RNSDomainPrice.sol b/src/RNSDomainPrice.sol index 57c18dde..be2cd075 100644 --- a/src/RNSDomainPrice.sol +++ b/src/RNSDomainPrice.sol @@ -284,11 +284,8 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric if (overriddenTier != 0) return Tier(~overriddenTier); - uint256 overriddenRenewalFee = _rnFeeOverriding[lbHash]; - uint256 yearlyRenewalFeeByLength = overriddenRenewalFee != 0 - ? 365 days * ~overriddenRenewalFee - : 365 days * _rnFee[Math.min(label.strlen(), _rnfMaxLength)]; - uint256 tierValue = yearlyRenewalFeeByLength + _getDomainPrice(lbHash) / 2; + (UnitPrice memory yearlyRenewalFeeByLength,,) = _tryGetRenewalFee({ label: label, duration: 365 days }); + uint256 tierValue = yearlyRenewalFeeByLength.usd + _getDomainPrice(lbHash) / 2; if (tierValue > TIER_1_FROM_EXCLUDED_THRESHOLD) { return Tier.Tier1; @@ -307,32 +304,14 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric view returns (UnitPrice memory basePrice, UnitPrice memory tax) { - uint256 nameLen = label.strlen(); - bytes32 lbHash = label.hashLabel(); - uint256 overriddenRenewalFee = _rnFeeOverriding[lbHash]; - - if (overriddenRenewalFee != 0) { - basePrice.usd = duration * ~overriddenRenewalFee; - } else { - uint256 renewalFeeByLength = _rnFee[Math.min(nameLen, _rnfMaxLength)]; - basePrice.usd = duration * renewalFeeByLength; - uint256 id = LibRNSDomain.toId(LibRNSDomain.RON_ID, label); - INSAuction auction = _auction; - if (auction.reserved(id)) { - INSUnified rns = auction.getRNSUnified(); - uint256 expiry = LibSafeRange.addWithUpperbound(rns.getRecord(id).mut.expiry, duration, type(uint64).max); - (INSAuction.DomainAuction memory domainAuction,) = auction.getAuction(id); - uint256 claimedAt = domainAuction.bid.claimedAt; - if (claimedAt != 0 && expiry - claimedAt > auction.MAX_AUCTION_DOMAIN_EXPIRY()) { - revert ExceedAuctionDomainExpiry(); - } - // Tax is added to the name reserved for the auction - tax.usd = Math.mulDiv(_taxRatio, _getDomainPrice(lbHash), MAX_PERCENTAGE); + bytes4 revertReason; + (basePrice, tax, revertReason) = _tryGetRenewalFee(label, duration); + if (revertReason != bytes4(0x0)) { + assembly ("memory-safe") { + mstore(0x0, revertReason) + revert(0x0, 0x04) } } - - tax.ron = convertUSDToRON(tax.usd); - basePrice.ron = convertUSDToRON(basePrice.usd); } /** @@ -457,6 +436,49 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric emit PythOracleConfigUpdated(_msgSender(), pyth, maxAcceptableAge, pythIdForRONUSD); } + /** + * @dev Tries to get the renewal fee for a given domain label and duration. + * It returns the base price, tax, and a revert reason if applicable. + * @param label The domain label. + * @param duration The duration for which the domain is being renewed. + * @return basePrice The base price in USD for ˝renewing the domain. + * @return tax The tax amount in USD for renewing the domain. + * @return revertReason The revert reason if the renewal fee exceeds the auction domain expiry. + */ + function _tryGetRenewalFee(string memory label, uint256 duration) + internal + view + returns (UnitPrice memory basePrice, UnitPrice memory tax, bytes4 revertReason) + { + uint256 nameLen = label.strlen(); + bytes32 lbHash = label.hashLabel(); + uint256 overriddenRenewalFee = _rnFeeOverriding[lbHash]; + + if (overriddenRenewalFee != 0) { + basePrice.usd = duration * ~overriddenRenewalFee; + } else { + uint256 renewalFeeByLength = _rnFee[Math.min(nameLen, _rnfMaxLength)]; + basePrice.usd = duration * renewalFeeByLength; + uint256 id = LibRNSDomain.toId(LibRNSDomain.RON_ID, label); + INSAuction auction = _auction; + if (auction.reserved(id)) { + INSUnified rns = auction.getRNSUnified(); + uint256 expiry = LibSafeRange.addWithUpperbound(rns.getRecord(id).mut.expiry, duration, type(uint64).max); + (INSAuction.DomainAuction memory domainAuction,) = auction.getAuction(id); + uint256 claimedAt = domainAuction.bid.claimedAt; + if (claimedAt != 0 && expiry - claimedAt > auction.MAX_AUCTION_DOMAIN_EXPIRY()) { + revertReason = ExceedAuctionDomainExpiry.selector; + return (basePrice, tax, revertReason); + } + // Tax is added to the name reserved for the auction + tax.usd = Math.mulDiv(_taxRatio, _getDomainPrice(lbHash), MAX_PERCENTAGE); + } + } + + tax.ron = convertUSDToRON(tax.usd); + basePrice.ron = convertUSDToRON(basePrice.usd); + } + /** * @dev Returns the current domain price applied the business rule: deduced x% each y seconds. */ diff --git a/test/RNSUnified/RNSUnified.t.sol b/test/RNSUnified/RNSUnified.t.sol index e541a2a3..7522c901 100644 --- a/test/RNSUnified/RNSUnified.t.sol +++ b/test/RNSUnified/RNSUnified.t.sol @@ -96,7 +96,13 @@ abstract contract RNSUnifiedTest is Test { address logic = address(new RNSUnified()); _rns = RNSUnified( address( - new TransparentUpgradeableProxy(logic, _proxyAdmin, abi.encodeCall(RNSUnified.initialize, (_admin, _pauser, _controller, _protectedSettler, GRACE_PERIOD, BASE_URI))) + new TransparentUpgradeableProxy( + logic, + _proxyAdmin, + abi.encodeCall( + RNSUnified.initialize, (_admin, _pauser, _controller, _protectedSettler, GRACE_PERIOD, BASE_URI) + ) + ) ) ); From 984ece9d182d886a2dba9ab294e02c03ae8e3ebe Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Wed, 21 Feb 2024 16:29:49 +0700 Subject: [PATCH 10/11] fix(RNSDomainPrice): minor refactor --- src/RNSDomainPrice.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/RNSDomainPrice.sol b/src/RNSDomainPrice.sol index be2cd075..3e83bc5a 100644 --- a/src/RNSDomainPrice.sol +++ b/src/RNSDomainPrice.sol @@ -467,8 +467,7 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric (INSAuction.DomainAuction memory domainAuction,) = auction.getAuction(id); uint256 claimedAt = domainAuction.bid.claimedAt; if (claimedAt != 0 && expiry - claimedAt > auction.MAX_AUCTION_DOMAIN_EXPIRY()) { - revertReason = ExceedAuctionDomainExpiry.selector; - return (basePrice, tax, revertReason); + return (basePrice, tax, ExceedAuctionDomainExpiry.selector); } // Tax is added to the name reserved for the auction tax.usd = Math.mulDiv(_taxRatio, _getDomainPrice(lbHash), MAX_PERCENTAGE); From 0c8cb0034c11a2d2a29eb4a5d4c18d4cf44baf4f Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Fri, 23 Feb 2024 15:03:55 +0700 Subject: [PATCH 11/11] fix(RNSDomainPrice): fix event TierOverridingUpdated --- src/RNSDomainPrice.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RNSDomainPrice.sol b/src/RNSDomainPrice.sol index 3e83bc5a..92f21057 100644 --- a/src/RNSDomainPrice.sol +++ b/src/RNSDomainPrice.sol @@ -217,7 +217,7 @@ contract RNSDomainPrice is Initializable, AccessControlEnumerable, INSDomainPric for (uint256 i; i < length;) { inverseBitwise = ~uint8(tiers[i]); _tierOverriding[lbHashes[i]] = inverseBitwise; - emit TierOverridingUpdated(operator, lbHashes[i], Tier(inverseBitwise)); + emit TierOverridingUpdated(operator, lbHashes[i], tiers[i]); unchecked { ++i;