From e96aee3995dfdf118037856ea6e2baf6eb72bbda Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Wed, 16 Oct 2024 17:45:05 +0700 Subject: [PATCH 01/12] feat(RONRegistrarController): add bulkRenew to batch extend duration for rns names --- src/RONRegistrarController.sol | 29 ++++++++++++++++++++++ src/interfaces/IRONRegistrarController.sol | 13 ++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/RONRegistrarController.sol b/src/RONRegistrarController.sol index 9bb061c..18ce089 100644 --- a/src/RONRegistrarController.sol +++ b/src/RONRegistrarController.sol @@ -228,6 +228,35 @@ contract RONRegistrarController is _transferRONToTreasury(); } + /** + * @inheritdoc IRONRegistrarController + */ + function bulkRenew(string[] calldata names, uint64[] calldata durations) external payable whenNotPaused nonReentrant { + uint256 length = names.length; + if (length == 0 || length != durations.length) revert InvalidArrayLength(); + + uint256 id; + uint256 totalPrice; + uint64 expiryTime; + + for (uint256 i; i < length; ++i) { + (, uint256 ronPrice) = rentPrice(names[i], durations[i]); + totalPrice += ronPrice; + + // Require id to be > previous id to prevent duplicate names + require(id < (id = computeId(names[i])), "BulkRenew: Invalid order of names"); + + expiryTime = _rnsUnified.renew(id, durations[i]); + emit NameRenewed(names[i], id, ronPrice, expiryTime); + } + + if (msg.value < totalPrice) revert InsufficientValue(); + uint256 remainAmount = msg.value - totalPrice; + + if (remainAmount != 0) RONTransferHelper.safeTransfer(payable(_msgSender()), remainAmount); + _transferRONToTreasury(); + } + /** * @inheritdoc IRONRegistrarController */ diff --git a/src/interfaces/IRONRegistrarController.sol b/src/interfaces/IRONRegistrarController.sol index a4355f4..0f74141 100644 --- a/src/interfaces/IRONRegistrarController.sol +++ b/src/interfaces/IRONRegistrarController.sol @@ -40,6 +40,8 @@ interface IRONRegistrarController { error InvalidArrayLength(); /// @dev Thrown when treasury address is set to null error NullAddress(); + /// @dev Thrown when the names is not sorted in ascending order + error InvalidOrderOfNames(); /** * @dev Emitted when the min registration duration is updated. @@ -176,6 +178,17 @@ interface IRONRegistrarController { */ function renew(string calldata name, uint64 duration) external payable; + /** + * @dev Renew multiple names in a single transaction. + * Requirements: + * - `names` and `duration` arrays must have the same length. + * - The caller must provide enough value to cover the total renewal cost. + * - `names` must be sorted in ascending order. + * @param names The array of names to be renewed. + * @param duration The duration of the renewal. + */ + function bulkRenew(string[] calldata names, uint64 duration) external payable; + /** * @dev Registers a protected name. * From c300ffb72a30f71f76aafe6decdb8879f9457966 Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Wed, 16 Oct 2024 17:58:19 +0700 Subject: [PATCH 02/12] fix(RONRegistrarController): minor fix --- src/RONRegistrarController.sol | 2 +- src/interfaces/IRONRegistrarController.sol | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/RONRegistrarController.sol b/src/RONRegistrarController.sol index 18ce089..4d67b4b 100644 --- a/src/RONRegistrarController.sol +++ b/src/RONRegistrarController.sol @@ -244,7 +244,7 @@ contract RONRegistrarController is totalPrice += ronPrice; // Require id to be > previous id to prevent duplicate names - require(id < (id = computeId(names[i])), "BulkRenew: Invalid order of names"); + if (id >= (id = computeId(names[i]))) revert InvalidOrderOfNames(); expiryTime = _rnsUnified.renew(id, durations[i]); emit NameRenewed(names[i], id, ronPrice, expiryTime); diff --git a/src/interfaces/IRONRegistrarController.sol b/src/interfaces/IRONRegistrarController.sol index 0f74141..0600b44 100644 --- a/src/interfaces/IRONRegistrarController.sol +++ b/src/interfaces/IRONRegistrarController.sol @@ -185,9 +185,9 @@ interface IRONRegistrarController { * - The caller must provide enough value to cover the total renewal cost. * - `names` must be sorted in ascending order. * @param names The array of names to be renewed. - * @param duration The duration of the renewal. + * @param duration The array of durations for the renewal. */ - function bulkRenew(string[] calldata names, uint64 duration) external payable; + function bulkRenew(string[] calldata names, uint64[] calldata durations) external payable; /** * @dev Registers a protected name. From 275ca1935868e876a6b20ae6fd93dd9cba1bcd6c Mon Sep 17 00:00:00 2001 From: "tu-do.ron" Date: Wed, 16 Oct 2024 17:58:56 +0700 Subject: [PATCH 03/12] Update src/RONRegistrarController.sol --- src/RONRegistrarController.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RONRegistrarController.sol b/src/RONRegistrarController.sol index 4d67b4b..ece1b8c 100644 --- a/src/RONRegistrarController.sol +++ b/src/RONRegistrarController.sol @@ -243,7 +243,7 @@ contract RONRegistrarController is (, uint256 ronPrice) = rentPrice(names[i], durations[i]); totalPrice += ronPrice; - // Require id to be > previous id to prevent duplicate names + // Require next id to be > previous id to prevent duplicate names if (id >= (id = computeId(names[i]))) revert InvalidOrderOfNames(); expiryTime = _rnsUnified.renew(id, durations[i]); From ec0fb58bce6c4e8dd5ca591f5202e65b769614c9 Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Wed, 16 Oct 2024 18:02:51 +0700 Subject: [PATCH 04/12] ci: update ci --- foundry.toml | 20 ++++++++++++++++++++ soldeer.lock | 19 +++++++++++-------- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/foundry.toml b/foundry.toml index 525a604..8a9743f 100644 --- a/foundry.toml +++ b/foundry.toml @@ -33,3 +33,23 @@ runs = 256 "@fdk" = { version = "0.3.0-beta", url = "https://github.com/axieinfinity/foundry-deployment-kit/archive/refs/tags/v0.3.0-beta.zip" } "@pythnetwork-pyth-sdk-solidity" = { version = "2.2.0" } "@openzeppelin-contracts" = { version = "4.9.3" } + +[soldeer] +# whether soldeer manages remappings +remappings_generate = false + +# whether soldeer re-generates all remappings when installing, updating or uninstalling deps +remappings_regenerate = false + +# whether to suffix the remapping with the version: `name-a.b.c` +remappings_version = true + +# a prefix to add to the remappings ("@" would give `@name`) +remappings_prefix = "@" + +# where to store the remappings ("txt" for `remappings.txt` or "config" for `foundry.toml`) +# ignored when `soldeer.toml` is used as config (uses `remappings.txt`) +remappings_location = "txt" + +# whether to install sub-dependencies or not. If true this wil install the dependencies of dependencies 1 level down. +recursive_deps = true \ No newline at end of file diff --git a/soldeer.lock b/soldeer.lock index 026df44..43fb493 100644 --- a/soldeer.lock +++ b/soldeer.lock @@ -1,17 +1,20 @@ [[dependencies]] name = "@fdk" version = "0.3.0-beta" -source = "https://github.com/axieinfinity/foundry-deployment-kit/archive/refs/tags/v0.3.0-beta.zip" +url = "https://github.com/axieinfinity/foundry-deployment-kit/archive/refs/tags/v0.3.0-beta.zip" checksum = "aabeda6cc1fe02227d26f3edd86d4af6c91e2167e8b9f1971cc1ea7ce33d34f9" - -[[dependencies]] -name = "@pythnetwork-pyth-sdk-solidity" -version = "2.2.0" -source = "https://soldeer-revisions.s3.amazonaws.com/@pythnetwork-pyth-sdk-solidity/2_2_0_15-04-2024_18:50:54_pyth-sdk-solidity.zip" -checksum = "54e3bda3b27467f84c1605722f58e1d2b5a19d6ca3c24840550f1d6cf3bc2231" +integrity = "436f06791b10ed0ce2363d3dde8e520e79376df1b81b00f0787670ad03941661" [[dependencies]] name = "@openzeppelin-contracts" version = "4.9.3" -source = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/4_9_3_22-01-2024_13:13:53_contracts.zip" +url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/4_9_3_22-01-2024_13:13:53_contracts.zip" checksum = "95886307069cf73310b41396c49df51801a73f31f18f62e7d05adfc2031e7725" +integrity = "6daa959732c1bceabda7f2823d1baa097b96509b48db78cb2e3ddd38bd4fd030" + +[[dependencies]] +name = "@pythnetwork-pyth-sdk-solidity" +version = "2.2.0" +url = "https://soldeer-revisions.s3.amazonaws.com/@pythnetwork-pyth-sdk-solidity/2_2_0_15-04-2024_18:50:54_pyth-sdk-solidity.zip" +checksum = "54e3bda3b27467f84c1605722f58e1d2b5a19d6ca3c24840550f1d6cf3bc2231" +integrity = "4a13c0cb110ee72e9f453835fcd63af6f13d5bbf4b102340ed454971d84c40fd" From b6a11a15f6e2b584a89803455ae3ee987d255ec0 Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Wed, 16 Oct 2024 18:03:26 +0700 Subject: [PATCH 05/12] ci: update ci --- .github/workflows/test.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0c53828..835bf13 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,14 +5,14 @@ on: branches: - mainnet - testnet - - 'feature/*' - - 'features/*' + - "feature/*" + - "features/*" pull_request: branches: - mainnet - testnet - - 'feature/*' - - 'features/*' + - "feature/*" + - "features/*" env: FOUNDRY_PROFILE: ci @@ -37,11 +37,6 @@ jobs: - name: Update package with soldeer run: forge soldeer update - - name: Recursively update dependencies - run: | - chmod +x ./update-deps.sh - ./update-deps.sh - - name: Run Forge build run: | forge --version From a5de680dd68bf97a151a480c8c5cded29eba03dd Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Wed, 16 Oct 2024 18:05:05 +0700 Subject: [PATCH 06/12] doc(RONRegistrarController): fix doc --- src/interfaces/IRONRegistrarController.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/IRONRegistrarController.sol b/src/interfaces/IRONRegistrarController.sol index 0600b44..e7dac94 100644 --- a/src/interfaces/IRONRegistrarController.sol +++ b/src/interfaces/IRONRegistrarController.sol @@ -185,7 +185,7 @@ interface IRONRegistrarController { * - The caller must provide enough value to cover the total renewal cost. * - `names` must be sorted in ascending order. * @param names The array of names to be renewed. - * @param duration The array of durations for the renewal. + * @param durations The array of durations for the renewal. */ function bulkRenew(string[] calldata names, uint64[] calldata durations) external payable; From 4d6fa4f48ec50a0053ace3d9171ce8a3d963a7cf Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Thu, 17 Oct 2024 11:30:23 +0700 Subject: [PATCH 07/12] fix(RONRegistrarController): remove duplicate check --- src/RONRegistrarController.sol | 7 +++---- src/interfaces/IRONRegistrarController.sol | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/RONRegistrarController.sol b/src/RONRegistrarController.sol index ece1b8c..f38b04a 100644 --- a/src/RONRegistrarController.sol +++ b/src/RONRegistrarController.sol @@ -241,12 +241,11 @@ contract RONRegistrarController is for (uint256 i; i < length; ++i) { (, uint256 ronPrice) = rentPrice(names[i], durations[i]); - totalPrice += ronPrice; - - // Require next id to be > previous id to prevent duplicate names - if (id >= (id = computeId(names[i]))) revert InvalidOrderOfNames(); + totalPrice += ronPrice; + id = computeId(names[i]); expiryTime = _rnsUnified.renew(id, durations[i]); + emit NameRenewed(names[i], id, ronPrice, expiryTime); } diff --git a/src/interfaces/IRONRegistrarController.sol b/src/interfaces/IRONRegistrarController.sol index e7dac94..bf3dd3e 100644 --- a/src/interfaces/IRONRegistrarController.sol +++ b/src/interfaces/IRONRegistrarController.sol @@ -181,9 +181,9 @@ interface IRONRegistrarController { /** * @dev Renew multiple names in a single transaction. * Requirements: - * - `names` and `duration` arrays must have the same length. + * - `names` and `durations` arrays must have the same length. * - The caller must provide enough value to cover the total renewal cost. - * - `names` must be sorted in ascending order. + * WARNING: The function does not check for duplicate names. * @param names The array of names to be renewed. * @param durations The array of durations for the renewal. */ From 81ea8cf0b326afdd33e5c2755a0b12eb01b649cf Mon Sep 17 00:00:00 2001 From: "tu-do.ron" Date: Thu, 17 Oct 2024 12:23:51 +0700 Subject: [PATCH 08/12] Update foundry.toml Co-authored-by: HuyHuynh <63286199+huyhuynh3103@users.noreply.github.com> --- foundry.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/foundry.toml b/foundry.toml index 8a9743f..9c7bd39 100644 --- a/foundry.toml +++ b/foundry.toml @@ -30,7 +30,7 @@ runs = 256 runs = 256 [dependencies] -"@fdk" = { version = "0.3.0-beta", url = "https://github.com/axieinfinity/foundry-deployment-kit/archive/refs/tags/v0.3.0-beta.zip" } +"@fdk" = { version = "0.3.4-beta", url = "https://github.com/axieinfinity/foundry-deployment-kit/archive/refs/tags/v0.3.4-beta.zip" } "@pythnetwork-pyth-sdk-solidity" = { version = "2.2.0" } "@openzeppelin-contracts" = { version = "4.9.3" } From bbf5b4d71c1dbb6066f92083fca8190fa1210c1d Mon Sep 17 00:00:00 2001 From: tringuyenskymavis Date: Thu, 17 Oct 2024 13:30:12 +0700 Subject: [PATCH 09/12] test(RONRegistrarController): unit test for bulkRenew func --- foundry.toml | 2 +- .../RONRegistrarController.bulkRenew.t.sol | 216 ++++++++++++++++++ .../RONRegistrarController.t.sol | 97 ++++++++ 3 files changed, 314 insertions(+), 1 deletion(-) create mode 100644 test/RONRegistrarController/RONRegistrarController.bulkRenew.t.sol create mode 100644 test/RONRegistrarController/RONRegistrarController.t.sol diff --git a/foundry.toml b/foundry.toml index 9c7bd39..cb2b249 100644 --- a/foundry.toml +++ b/foundry.toml @@ -7,7 +7,7 @@ ffi = true solc = '0.8.21' extra_output = ["devdoc", "userdoc", "storagelayout"] -evm_version = 'istanbul' +evm_version = 'london' use_literal_content = true fs_permissions = [{ access = "read-write", path = "./" }] diff --git a/test/RONRegistrarController/RONRegistrarController.bulkRenew.t.sol b/test/RONRegistrarController/RONRegistrarController.bulkRenew.t.sol new file mode 100644 index 0000000..b92a4e7 --- /dev/null +++ b/test/RONRegistrarController/RONRegistrarController.bulkRenew.t.sol @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import { console2, Test } from "forge-std/Test.sol"; +import { RNSUnified } from "@rns-contracts/RNSUnified.sol"; +import { + RONRegistrarController, + IRONRegistrarController, + INSUnified, + INameChecker, + INSDomainPrice, + INSReverseRegistrar +} from "@rns-contracts/RONRegistrarController.sol"; +import { RONTransferHelper } from "@rns-contracts/libraries/transfers/RONTransferHelper.sol"; +import { LibString } from "@rns-contracts/libraries/LibString.sol"; +import { LibRNSDomain } from "@rns-contracts/libraries/LibRNSDomain.sol"; +import { RONRegistrarControllerTest } from "./RONRegistrarController.t.sol"; + +contract RONRegistrarControllerBulkRenewTest is RONRegistrarControllerTest { + event NameRenewed(string name, uint256 indexed id, uint256 cost, uint64 expires); + + function testConcreate_Renew_2names_Check_CorrectFeeCharged() external { + string[] memory names = new string[](2); + names[0] = "tori.ron"; + names[1] = "test.ron"; + + (uint256[] memory ids, string[] memory sortedNames) = _sortNames(names); + uint64[] memory expires = new uint64[](ids.length); + uint64 duration = 30 days; + for (uint256 i; i < ids.length; ++i) { + console2.log("id", ids[i]); + console2.log("name", sortedNames[i]); + expires[i] = _rnsUnified.getRecord(ids[i]).mut.expiry + duration; + } + + uint256[] memory fees = new uint256[](2); + fees[0] = _calFee(names[0], duration); + fees[1] = _calFee(names[1], duration); + + uint256 totalFee = fees[0] + fees[1]; + + uint256 sendValue = 10 ether; + uint256 balanceBefore = address(_caller).balance; + + uint64[] memory durations = new uint64[](2); + durations[0] = duration; + durations[1] = duration; + _expectEmit(sortedNames, ids, fees, expires); + vm.prank(_caller); + _controller.bulkRenew{ value: sendValue }(sortedNames, durations); + assertEq(address(_caller).balance, balanceBefore - totalFee); + + durations[0] = duration; + durations[1] = duration; + vm.prank(_caller); + _controller.bulkRenew{ value: sendValue }(sortedNames, durations); + } + + function testConcreate_bulkRenew_TheSameName_CorrectFeeCharged() external { + string[] memory names = new string[](2); + names[0] = "tori.vip.ron"; + names[1] = "tori.vip.ron"; + + uint64 duration = 30 days; + uint64[] memory durations = new uint64[](2); + durations[0] = duration; + durations[1] = duration; + uint256 balanceBefore = address(_caller).balance; + + uint256 fee = _calFee(names[0], duration) * 2; + + vm.prank(_caller); + _controller.bulkRenew{ value: 10 ether }(names, durations); + + assertEq(address(_caller).balance, balanceBefore - fee); + assertEq(_treasury.balance, fee); + } + + function testRevert_bulkRenew_InsufficientValue() external { + string[] memory names = new string[](2); + names[0] = "test.ron"; + names[1] = "tori.ron"; + + uint64 duration = 30 days; + uint64[] memory durations = new uint64[](2); + durations[0] = duration; + durations[1] = duration; + + uint256 name0Fee = _calFee(names[0], duration); + uint256 name1Fee = _calFee(names[1], duration); + + vm.prank(_caller); + vm.expectRevert(IRONRegistrarController.InsufficientValue.selector); + _controller.bulkRenew{ value: name0Fee + name1Fee - 1 }(names, durations); + + // Call success + _controller.bulkRenew{ value: name0Fee + name1Fee }(names, durations); + } + + function testRevert_bulkRenew_ExpiryNotLargerThanOldOne() external { + string[] memory names = new string[](1); + names[0] = "test.ron"; + + uint64[] memory durations = new uint64[](1); + durations[0] = 0; + + vm.prank(_caller); + vm.expectRevert(INSUnified.ExpiryTimeMustBeLargerThanTheOldOne.selector); + _controller.bulkRenew{ value: 10 ether }(names, durations); + } + + function testBenchMark_bulkRenew(uint8 times) external { + vm.pauseGasMetering(); + vm.assume(times > 0); + string[] memory names = new string[](times); + uint64[] memory durations = new uint64[](times); + uint256 totalFee; + for (uint256 i; i < times; ++i) { + names[i] = string.concat("test", vm.toString(i), ".ron"); + durations[i] = 30 days; + totalFee += _calFee(names[i], durations[i]); + } + vm.deal(_caller, totalFee + 10 ether); + (, string[] memory sortedNames) = _sortNames(names); + vm.resumeGasMetering(); + vm.prank(_caller); + _controller.bulkRenew{ value: totalFee + 10 ether }(sortedNames, durations); + + assertEq(address(_caller).balance, 10 ether); + assertEq(_treasury.balance, totalFee); + } + + function testRevert_bulkRenew_InvalidArrayLength() external { + string[] memory names = new string[](2); + names[0] = "test.ron"; + names[1] = "tori.ron"; + + uint64[] memory durations = new uint64[](1); + durations[0] = 30 days; + + vm.prank(_caller); + vm.expectRevert(IRONRegistrarController.InvalidArrayLength.selector); + _controller.bulkRenew{ value: 10 ether }(names, durations); + + names = new string[](0); + durations = new uint64[](0); + vm.expectRevert(IRONRegistrarController.InvalidArrayLength.selector); + _controller.bulkRenew{ value: 10 ether }(names, durations); + } + + function testConcreate_DuplicateNames_With_TheSameDuration() external { + string[] memory names = new string[](2); + names[0] = "test.ron"; + names[1] = "test.ron"; + + uint64[] memory durations = new uint64[](2); + durations[0] = 30 days; + durations[1] = 30 days; + + vm.prank(_caller); + // vm.expectRevert(IRONRegistrarController.InvalidArrayLength.selector); + _controller.bulkRenew{ value: 10 ether }(names, durations); + } + + function testConcreate_DuplicateNames_With_DifferentDuration() external { + string[] memory names = new string[](2); + names[0] = "test.ron"; + names[1] = "test.ron"; + + uint64[] memory durations = new uint64[](2); + durations[0] = 30 days; + durations[1] = 10 days; + + vm.prank(_caller); + // vm.expectRevert(IRONRegistrarController.InvalidArrayLength.selector); + _controller.bulkRenew{ value: 10 ether }(names, durations); + } + + function _calFee(string memory name, uint64 duration) internal returns (uint256) { + (, uint256 ronPrice) = _controller.rentPrice(name, duration); + return ronPrice; + } + + function _sortNames(string[] memory names) internal returns (uint256[] memory, string[] memory) { + uint256[] memory ids = new uint256[](names.length); + for (uint256 i = 0; i < names.length; i++) { + ids[i] = _controller.computeId(names[i]); + } + + for (uint256 i = 0; i < names.length; i++) { + for (uint256 j = i + 1; j < names.length; j++) { + if (ids[i] > ids[j]) { + uint256 tempId = ids[i]; + ids[i] = ids[j]; + ids[j] = tempId; + + string memory tempName = names[i]; + names[i] = names[j]; + names[j] = tempName; + } + } + } + return (ids, names); + } + + function _expectEmit(string[] memory names, uint256[] memory ids, uint256[] memory fees, uint64[] memory expires) + internal + { + for (uint256 i; i < names.length; ++i) { + vm.expectEmit(false, true, false, true); + emit NameRenewed(names[i], ids[i], fees[i], expires[i]); + } + } +} diff --git a/test/RONRegistrarController/RONRegistrarController.t.sol b/test/RONRegistrarController/RONRegistrarController.t.sol new file mode 100644 index 0000000..620bb9f --- /dev/null +++ b/test/RONRegistrarController/RONRegistrarController.t.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import { console2, Test } from "forge-std/Test.sol"; +import { RNSUnified } from "@rns-contracts/RNSUnified.sol"; +import { + RONRegistrarController, + INSUnified, + INameChecker, + INSDomainPrice, + INSReverseRegistrar +} from "@rns-contracts/RONRegistrarController.sol"; +import { RONTransferHelper } from "@rns-contracts/libraries/transfers/RONTransferHelper.sol"; +import { LibString } from "@rns-contracts/libraries/LibString.sol"; +import { LibRNSDomain } from "@rns-contracts/libraries/LibRNSDomain.sol"; +import { RNSUnifiedDeploy } from "script/contracts/RNSUnifiedDeploy.s.sol"; + +contract RONRegistrarControllerTest is Test { + using LibString for string; + using LibRNSDomain for string; + + address internal _caller; + address internal _admin; + address internal _pauser; + address internal _proxyAdmin; + address payable internal _treasury; + uint256 internal _maxCommitmentAge; + uint256 internal _minCommitmentAge; + uint256 internal _minRegistrationDuration; + INSUnified internal _rnsUnified; + INameChecker internal _nameChecker; + INSDomainPrice internal _priceOracle; + INSReverseRegistrar internal _reverseRegistrar; + + RONRegistrarController internal _controller; + + function setUp() external { + vm.warp(block.timestamp + 10 days); + _caller = makeAddr("caller"); + _admin = makeAddr("admin"); + _pauser = makeAddr("pauser"); + _proxyAdmin = makeAddr("proxyAdmin"); + _treasury = payable(makeAddr("treasury")); + _maxCommitmentAge = 1 days; + _minCommitmentAge = 10 seconds; + _minRegistrationDuration = 1 days; + _rnsUnified = INSUnified(address(new RNSUnifiedDeploy().run())); + _nameChecker = INameChecker(makeAddr("nameChecker")); + _priceOracle = INSDomainPrice(address(new PriceOracleMock())); + _reverseRegistrar = INSReverseRegistrar(makeAddr("reverseRegistrar")); + vm.deal(_caller, 100 ether); + + address logic = address(new RONRegistrarController()); + _controller = RONRegistrarController( + address( + new TransparentUpgradeableProxy( + logic, + _proxyAdmin, + abi.encodeCall( + RONRegistrarController.initialize, + ( + _admin, + _pauser, + _treasury, + _maxCommitmentAge, + _minCommitmentAge, + _minRegistrationDuration, + _rnsUnified, + _nameChecker, + _priceOracle, + _reverseRegistrar + ) + ) + ) + ) + ); + + RNSUnified _rns = RNSUnified(address(_rnsUnified)); + address admin = _rns.getRoleMember(_rns.DEFAULT_ADMIN_ROLE(), 0); + bytes32 controllerRole = _rns.CONTROLLER_ROLE(); + vm.prank(admin); + _rns.grantRole(controllerRole, address(_controller)); + } +} + +contract PriceOracleMock { + function getRenewalFee(string calldata label, uint256 duration) + external + view + returns (INSDomainPrice.UnitPrice memory basePrice, INSDomainPrice.UnitPrice memory tax) + { + basePrice = INSDomainPrice.UnitPrice({ usd: 1.5 ether, ron: 1 ether }); + tax = INSDomainPrice.UnitPrice({ usd: 0.15 ether, ron: 0.1 ether }); + } +} From 3a1e815171e5f714f5a0471c2ec49a56c18699e1 Mon Sep 17 00:00:00 2001 From: "tu-do.ron" Date: Thu, 17 Oct 2024 15:13:59 +0700 Subject: [PATCH 10/12] Update src/interfaces/IRONRegistrarController.sol --- src/interfaces/IRONRegistrarController.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/interfaces/IRONRegistrarController.sol b/src/interfaces/IRONRegistrarController.sol index bf3dd3e..89f6fd7 100644 --- a/src/interfaces/IRONRegistrarController.sol +++ b/src/interfaces/IRONRegistrarController.sol @@ -40,8 +40,6 @@ interface IRONRegistrarController { error InvalidArrayLength(); /// @dev Thrown when treasury address is set to null error NullAddress(); - /// @dev Thrown when the names is not sorted in ascending order - error InvalidOrderOfNames(); /** * @dev Emitted when the min registration duration is updated. From 35db78ad3ee9c2c2b1ac9b347653589a565b9643 Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Mon, 4 Nov 2024 16:02:55 +0700 Subject: [PATCH 11/12] chore: use link from official github repo --- foundry.toml | 6 +++--- soldeer.lock | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/foundry.toml b/foundry.toml index cb2b249..0f04dc3 100644 --- a/foundry.toml +++ b/foundry.toml @@ -31,8 +31,8 @@ runs = 256 [dependencies] "@fdk" = { version = "0.3.4-beta", url = "https://github.com/axieinfinity/foundry-deployment-kit/archive/refs/tags/v0.3.4-beta.zip" } -"@pythnetwork-pyth-sdk-solidity" = { version = "2.2.0" } -"@openzeppelin-contracts" = { version = "4.9.3" } +"@pythnetwork-pyth-sdk-solidity" = { version = "2.2.0", url = "https://github.com/pyth-network/pyth-sdk-solidity/archive/refs/tags/v2.2.0.zip" } +"@openzeppelin-contracts" = { version = "4.9.3", url = "https://github.com/OpenZeppelin/openzeppelin-contracts/archive/refs/tags/v4.9.3.zip" } [soldeer] # whether soldeer manages remappings @@ -52,4 +52,4 @@ remappings_prefix = "@" remappings_location = "txt" # whether to install sub-dependencies or not. If true this wil install the dependencies of dependencies 1 level down. -recursive_deps = true \ No newline at end of file +recursive_deps = true diff --git a/soldeer.lock b/soldeer.lock index 43fb493..8b93159 100644 --- a/soldeer.lock +++ b/soldeer.lock @@ -1,20 +1,20 @@ [[dependencies]] name = "@fdk" -version = "0.3.0-beta" -url = "https://github.com/axieinfinity/foundry-deployment-kit/archive/refs/tags/v0.3.0-beta.zip" -checksum = "aabeda6cc1fe02227d26f3edd86d4af6c91e2167e8b9f1971cc1ea7ce33d34f9" -integrity = "436f06791b10ed0ce2363d3dde8e520e79376df1b81b00f0787670ad03941661" +version = "0.3.4-beta" +url = "https://github.com/axieinfinity/foundry-deployment-kit/archive/refs/tags/v0.3.4-beta.zip" +checksum = "d93cfa76dee3a227b2ee24d41c4afe2141fb25498eed9352d036ab13bbc16c13" +integrity = "4f0417853a563024747cba0dd88d501717c13d4387132e5b1b854da69b7486b2" [[dependencies]] name = "@openzeppelin-contracts" version = "4.9.3" -url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/4_9_3_22-01-2024_13:13:53_contracts.zip" -checksum = "95886307069cf73310b41396c49df51801a73f31f18f62e7d05adfc2031e7725" -integrity = "6daa959732c1bceabda7f2823d1baa097b96509b48db78cb2e3ddd38bd4fd030" +url = "https://github.com/OpenZeppelin/openzeppelin-contracts/archive/refs/tags/v4.9.3.zip" +checksum = "94270990c32ff2d00c06a9dabaf5b7a8e36773e017254acbb39d13733eb82960" +integrity = "5cca086987dbce97760f8a337c0d29e6383fcafaffa9662897695f17566756d0" [[dependencies]] name = "@pythnetwork-pyth-sdk-solidity" version = "2.2.0" -url = "https://soldeer-revisions.s3.amazonaws.com/@pythnetwork-pyth-sdk-solidity/2_2_0_15-04-2024_18:50:54_pyth-sdk-solidity.zip" -checksum = "54e3bda3b27467f84c1605722f58e1d2b5a19d6ca3c24840550f1d6cf3bc2231" -integrity = "4a13c0cb110ee72e9f453835fcd63af6f13d5bbf4b102340ed454971d84c40fd" +url = "https://github.com/pyth-network/pyth-sdk-solidity/archive/refs/tags/v2.2.0.zip" +checksum = "71431ac3fe4e61ce2b8abd649d3e741277ca4dba287c2a4291d040190b8fb8da" +integrity = "845f9e662935eb347e9189da9b156f0062bcab1d372c7397777dbda609776a5c" From bba6b729691a66acbc45106edd3923d3efb81700 Mon Sep 17 00:00:00 2001 From: tringuyenskymavis Date: Mon, 4 Nov 2024 22:00:27 +0700 Subject: [PATCH 12/12] script: upgrade controller mainnet script and postcheck --- .../20231104_UpgradeController_Mainnet.s.sol | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 script/20241104-upgrade-controller-mainnet/20231104_UpgradeController_Mainnet.s.sol diff --git a/script/20241104-upgrade-controller-mainnet/20231104_UpgradeController_Mainnet.s.sol b/script/20241104-upgrade-controller-mainnet/20231104_UpgradeController_Mainnet.s.sol new file mode 100644 index 0000000..3c1d9f6 --- /dev/null +++ b/script/20241104-upgrade-controller-mainnet/20231104_UpgradeController_Mainnet.s.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import { Contract } from "script/utils/Contract.sol"; +import { Migration } from "script/Migration.s.sol"; +import { RONRegistrarController } from "@rns-contracts/RONRegistrarController.sol"; +import { RNSUnified } from "@rns-contracts/RNSUnified.sol"; +import { console2 } from "forge-std/console2.sol"; + +contract Migration__20231104_UpgradeController_Mainnet is Migration { + RONRegistrarController internal _controller; + RNSUnified internal _rns; + + struct MintParam { + address owner; + string name; + address resolver; + uint64 duration; + } + + function run() public { + _controller = RONRegistrarController(_upgradeProxy(Contract.RONRegistrarController.key())); + } + + function _postCheck() internal override { + _validateBulkRenew(); + } + + function _validateBulkRenew() internal logFn("_validateBulkRenew") { + _rns = RNSUnified(loadContract(Contract.RNSUnified.key())); + string[] memory names = new string[](2); + uint64[] memory durations = new uint64[](2); + address resolver = makeAddr("resolver"); + address owner = makeAddr("owner"); + names[0] = "minhtri0901"; + names[1] = "minhtri09012"; + durations[0] = 365 days; + durations[1] = 365 days; + MintParam[] memory mintParams = new MintParam[](2); + mintParams[0] = MintParam({ owner: owner, name: names[0], resolver: resolver, duration: 0 }); + mintParams[1] = MintParam({ owner: owner, name: names[1], resolver: resolver, duration: 0 }); + + uint256 ronId = 0xba69923fa107dbf5a25a073a10b7c9216ae39fbadc95dc891d460d9ae315d688; + _mint(ronId, mintParams[0]); + _mint(ronId, mintParams[1]); + + (, uint256 rentPrice1) = _controller.rentPrice(names[0], durations[0]); + (, uint256 rentPrice2) = _controller.rentPrice(names[1], durations[1]); + + vm.deal(owner, 10 ether); + vm.prank(owner); + _controller.bulkRenew{ value: 10 ether }(names, durations); + + assertEq(owner.balance, 10 ether - rentPrice1 - rentPrice2); + assertEq(_rns.getRecord(_controller.computeId(names[0])).mut.expiry, block.timestamp + durations[0]); + assertEq(_rns.getRecord(_controller.computeId(names[1])).mut.expiry, block.timestamp + durations[1]); + assertEq(_rns.getRecord(_controller.computeId(names[0])).mut.owner, owner); + assertEq(_rns.getRecord(_controller.computeId(names[1])).mut.owner, owner); + assertEq(_rns.getRecord(_controller.computeId(names[0])).immut.label, names[0]); + assertEq(_rns.getRecord(_controller.computeId(names[1])).immut.label, names[1]); + } + + function _mint(uint256 parentId, MintParam memory mintParam) internal returns (uint64 expiry, uint256 id) { + vm.prank(address(_controller)); + (expiry, id) = _rns.mint(parentId, mintParam.name, mintParam.resolver, mintParam.owner, mintParam.duration); + } +}