Skip to content

Commit

Permalink
Merge branch 'release-testnet/v0.2.0' into feature/reverse-registrar
Browse files Browse the repository at this point in the history
  • Loading branch information
ducthotran2010 authored Oct 10, 2023
2 parents f03cd7c + 1ce5f17 commit 5d2ad62
Show file tree
Hide file tree
Showing 10 changed files with 866 additions and 30 deletions.
76 changes: 47 additions & 29 deletions src/RNSUnified.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ contract RNSUnified is Initializable, RNSToken {
_setBaseURI(baseTokenURI);
_setGracePeriod(gracePeriod);

_mint(admin, 0x00);
_mint(admin, 0x0);
Record memory record;
_recordOf[0x0].mut.expiry = record.mut.expiry = MAX_EXPIRY;
emit RecordUpdated(0x0, ModifyingField.Expiry.indicator(), record);
}

/// @inheritdoc INSUnified
Expand Down Expand Up @@ -126,12 +129,17 @@ contract RNSUnified is Initializable, RNSToken {

/// @inheritdoc INSUnified
function renew(uint256 id, uint64 duration) external whenNotPaused onlyRole(CONTROLLER_ROLE) {
_setExpiry(id, uint64(LibSafeRange.addWithUpperbound(_recordOf[id].mut.expiry, duration, MAX_EXPIRY)));
Record memory record;
record.mut.expiry = uint64(LibSafeRange.addWithUpperbound(_recordOf[id].mut.expiry, duration, MAX_EXPIRY));
_setExpiry(id, record.mut.expiry);
emit RecordUpdated(id, ModifyingField.Expiry.indicator(), record);
}

/// @inheritdoc INSUnified
function setExpiry(uint256 id, uint64 expiry) external whenNotPaused onlyRole(CONTROLLER_ROLE) {
_setExpiry(id, expiry);
Record memory record;
_setExpiry(id, record.mut.expiry = expiry);
emit RecordUpdated(id, ModifyingField.Expiry.indicator(), record);
}

/// @inheritdoc INSUnified
Expand All @@ -143,6 +151,7 @@ contract RNSUnified is Initializable, RNSToken {

for (uint256 i; i < ids.length;) {
id = ids[i];
if (!_exists(id)) revert Unexists();
if (_recordOf[id].mut.protected != protected) {
_recordOf[id].mut.protected = protected;
emit RecordUpdated(id, indicator, record);
Expand All @@ -161,8 +170,23 @@ contract RNSUnified is Initializable, RNSToken {
onlyAuthorized(id, indicator)
{
Record memory record;
_recordOf[id].mut = record.mut = mutRecord;
MutableRecord storage sMutRecord = _recordOf[id].mut;

if (indicator.hasAny(ModifyingField.Protected.indicator())) {
sMutRecord.protected = record.mut.protected = mutRecord.protected;
}
if (indicator.hasAny(ModifyingField.Expiry.indicator())) {
_setExpiry(id, record.mut.expiry = mutRecord.expiry);
}
if (indicator.hasAny(ModifyingField.Resolver.indicator())) {
sMutRecord.resolver = record.mut.resolver = mutRecord.resolver;
}
emit RecordUpdated(id, indicator, record);

// Updating owner might emit more {RecordUpdated} events. See method {_transfer}.
if (indicator.hasAny(ModifyingField.Owner.indicator())) {
_safeTransfer(_recordOf[id].mut.owner, mutRecord.owner, id, "");
}
}

/**
Expand All @@ -181,6 +205,7 @@ contract RNSUnified is Initializable, RNSToken {
if (indicator.hasAny(IMMUTABLE_FIELDS_INDICATOR)) {
return (false, CannotSetImmutableField.selector);
}
if (!_exists(id)) return (false, Unexists.selector);
if (indicator.hasAny(ModifyingField.Protected.indicator()) && !hasRole(PROTECTED_SETTLER_ROLE, requester)) {
return (false, MissingProtectedSettlerRole.selector);
}
Expand All @@ -192,12 +217,12 @@ contract RNSUnified is Initializable, RNSToken {
return (false, Unauthorized.selector);
}

return (true, 0x00);
return (true, 0x0);
}

/// @dev Override {ERC721-ownerOf}.
function ownerOf(uint256 tokenId) public view override(ERC721, IERC721) returns (address) {
if (_isExpired(tokenId)) revert Expired();
if (_isExpired(tokenId)) return address(0x0);
return super.ownerOf(tokenId);
}

Expand Down Expand Up @@ -252,8 +277,8 @@ contract RNSUnified is Initializable, RNSToken {
(bool allowed, bytes4 errorCode) = canSetRecord(_msgSender(), id, indicator);
if (!allowed) {
assembly ("memory-safe") {
mstore(0x00, errorCode)
revert(0x1c, 0x04)
mstore(0x0, errorCode)
revert(0x0, 0x04)
}
}
}
Expand Down Expand Up @@ -281,7 +306,6 @@ contract RNSUnified is Initializable, RNSToken {

Record memory record;
_recordOf[id].mut.expiry = record.mut.expiry = expiry;
emit RecordUpdated(id, ModifyingField.Expiry.indicator(), record);
}

/**
Expand All @@ -294,30 +318,24 @@ contract RNSUnified is Initializable, RNSToken {
emit GracePeriodUpdated(_msgSender(), gracePeriod);
}

/// @dev Override {ERC721-_afterTokenTransfer}.
function _afterTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize)
internal
virtual
override
{
super._afterTokenTransfer(from, to, firstTokenId, batchSize);
/// @dev Override {ERC721-_transfer}.
function _transfer(address from, address to, uint256 id) internal override {
super._transfer(from, to, id);

Record memory record;
record.mut.owner = to;
ModifyingIndicator indicator = ModifyingField.Owner.indicator();
bool shouldUpdateProtected = !hasRole(PROTECTED_SETTLER_ROLE, _msgSender());
if (shouldUpdateProtected) indicator = indicator | ModifyingField.Protected.indicator();

for (uint256 id = firstTokenId; id < firstTokenId + batchSize;) {
_recordOf[id].mut.owner = to;
if (shouldUpdateProtected) {
_recordOf[id].mut.protected = false;
emit RecordUpdated(id, indicator, record);
}

unchecked {
id++;
}
_recordOf[id].mut.owner = record.mut.owner = to;
if (!hasRole(PROTECTED_SETTLER_ROLE, _msgSender()) && _recordOf[id].mut.protected) {
_recordOf[id].mut.protected = false;
indicator = indicator | ModifyingField.Protected.indicator();
}
emit RecordUpdated(id, indicator, record);
}

/// @dev Override {ERC721-_burn}.
function _burn(uint256 id) internal override {
super._burn(id);
delete _recordOf[id].mut;
}
}
7 changes: 7 additions & 0 deletions src/interfaces/INSUnified.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { ModifyingIndicator } from "../types/ModifyingIndicator.sol";
interface INSUnified is IAccessControlEnumerable, IERC721Metadata {
/// @dev Error: The provided token id is expired.
error Expired();
/// @dev Error: The provided token id is unexists.
error Unexists();
/// @dev Error: The provided id expiry is greater than parent id expiry.
error ExceedParentExpiry();
/// @dev Error: The provided name is unavailable for registration.
Expand Down Expand Up @@ -97,6 +99,11 @@ interface INSUnified is IAccessControlEnumerable, IERC721Metadata {
*/
function RESERVATION_ROLE() external pure returns (bytes32);

/**
* @dev Returns the max expiry value.
*/
function MAX_EXPIRY() external pure returns (uint64);

/**
* @dev Returns true if the specified name is available for registration.
* Note: Only available after passing the grace period.
Expand Down
22 changes: 21 additions & 1 deletion src/types/ModifyingIndicator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ type ModifyingIndicator is uint256;

using { hasAny } for ModifyingIndicator global;
using { or as | } for ModifyingIndicator global;
using { and as & } for ModifyingIndicator global;
using { eq as == } for ModifyingIndicator global;
using { not as ~ } for ModifyingIndicator global;
using { neq as != } for ModifyingIndicator global;

/// @dev Indicator for modifying immutable fields: Depth, ParentId, Label. See struct {INSUnified.ImmutableRecord}.
ModifyingIndicator constant IMMUTABLE_FIELDS_INDICATOR = ModifyingIndicator.wrap(0x7);
Expand All @@ -15,10 +19,26 @@ ModifyingIndicator constant USER_FIELDS_INDICATOR = ModifyingIndicator.wrap(0x18
/// @dev Indicator when modifying all of the fields in {ModifyingField}.
ModifyingIndicator constant ALL_FIELDS_INDICATOR = ModifyingIndicator.wrap(type(uint256).max);

function eq(ModifyingIndicator self, ModifyingIndicator other) pure returns (bool) {
return ModifyingIndicator.unwrap(self) == ModifyingIndicator.unwrap(other);
}

function neq(ModifyingIndicator self, ModifyingIndicator other) pure returns (bool) {
return !eq(self, other);
}

function not(ModifyingIndicator self) pure returns (ModifyingIndicator) {
return ModifyingIndicator.wrap(~ModifyingIndicator.unwrap(self));
}

function or(ModifyingIndicator self, ModifyingIndicator other) pure returns (ModifyingIndicator) {
return ModifyingIndicator.wrap(ModifyingIndicator.unwrap(self) | ModifyingIndicator.unwrap(other));
}

function and(ModifyingIndicator self, ModifyingIndicator other) pure returns (ModifyingIndicator) {
return ModifyingIndicator.wrap(ModifyingIndicator.unwrap(self) & ModifyingIndicator.unwrap(other));
}

function hasAny(ModifyingIndicator self, ModifyingIndicator other) pure returns (bool) {
return ModifyingIndicator.unwrap(or(self, other)) != 0;
return self & other != ModifyingIndicator.wrap(0);
}
67 changes: 67 additions & 0 deletions test/RNSUnified/RNSUnified.ERC721.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "./RNSUnified.t.sol";

contract RNSUnified_ERC721_Test is RNSUnifiedTest {
function test_TokenMetadata() external {
assertEq(_rns.name(), "Ronin Name Service");
assertEq(_rns.symbol(), "RNS");
}

function testFuzz_WhenExpired_RevokeOwnership_ownerOf(MintParam calldata mintParam) external mintAs(_controller) {
(uint64 expiry, uint256 id) = _mint(_ronId, mintParam, _noError);
_warpToExpire(expiry);
assertEq(_rns.ownerOf(id), address(0x00));
}

function testFuzz_WhenExpired_RevokeApproval_getApproved(address approved, MintParam calldata mintParam)
external
validAccount(approved)
mintAs(_controller)
{
vm.assume(approved != mintParam.owner && approved != _admin);
(uint64 expiry, uint256 id) = _mint(_ronId, mintParam, _noError);
vm.prank(mintParam.owner);
_rns.setApprovalForAll(approved, true);
_warpToExpire(expiry);
address actualApproved = _rns.getApproved(id);
assertEq(actualApproved, address(0x00));
}

function testFuzz_UpdateRecordOwner_transferFrom(address newOwner, MintParam calldata mintParam)
external
validAccount(newOwner)
mintAs(_controller)
{
vm.assume(newOwner != _admin && newOwner != mintParam.owner);
(, uint256 id) = _mint(_ronId, mintParam, _noError);
vm.prank(mintParam.owner);
_rns.transferFrom(mintParam.owner, newOwner, id);
INSUnified.Record memory record = _rns.getRecord(id);
assertEq(record.mut.owner, newOwner);
}

function testFuzz_WhenTransfered_LostProtected(address newOwner, MintParam calldata mintParam)
external
validAccount(newOwner)
mintAs(_controller)
{
vm.assume(newOwner != mintParam.owner);
(, uint256 id) = _mint(_ronId, mintParam, _noError);

uint256[] memory ids = new uint256[](1);
ids[0] = id;
vm.prank(_protectedSettler);
_rns.bulkSetProtected(ids, true);

INSUnified.Record memory record = _rns.getRecord(id);
assertTrue(record.mut.protected);

vm.prank(mintParam.owner);
_rns.transferFrom(mintParam.owner, newOwner, id);

record = _rns.getRecord(id);
assertFalse(record.mut.protected);
}
}
71 changes: 71 additions & 0 deletions test/RNSUnified/RNSUnified.bulkSetProtected.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "./RNSUnified.t.sol";

contract RNSUnified_BulkSetProtected_Test is RNSUnifiedTest {
using LibModifyingField for ModifyingField;

uint256 public constant MAX_FUZZ_INPUT = 100;

modifier boundFuzzArrLength(uint256 length) {
vm.assume(length <= MAX_FUZZ_INPUT);
_;
}

function testFuzz_RevertWhenNotMinted_bulkSetProtected(bool protected, MintParam calldata mintParam) external {
uint256 id = _toId(_ronId, mintParam.name);
uint256[] memory ids = new uint256[](1);
ids[0] = id;
vm.expectRevert(INSUnified.Unexists.selector);
vm.prank(_protectedSettler);
_rns.bulkSetProtected(ids, protected);
}

function testGas_WhenMinted_AsProtectedSettler_bulkSetProtected(MintParam[] calldata mintParams)
external
mintAs(_controller)
boundFuzzArrLength(mintParams.length)
{
uint256[] memory ids = _mintBulk(mintParams);

vm.prank(_protectedSettler);
_rns.bulkSetProtected(ids, true);

vm.pauseGasMetering();
for (uint256 i; i < ids.length;) {
assertTrue(_rns.getRecord(ids[i]).mut.protected);

unchecked {
++i;
}
}
vm.resumeGasMetering();
}

function testGas_WhenMinted_AsProtectedSettler_bulkSetUnprotected(MintParam[] calldata mintParams)
external
mintAs(_controller)
boundFuzzArrLength(mintParams.length)
{
uint256[] memory ids = _mintBulk(mintParams);

vm.pauseGasMetering();
vm.prank(_protectedSettler);
_rns.bulkSetProtected(ids, true);

vm.resumeGasMetering();
vm.prank(_protectedSettler);
_rns.bulkSetProtected(ids, false);
vm.pauseGasMetering();

for (uint256 i; i < ids.length;) {
assertFalse(_rns.getRecord(ids[i]).mut.protected);

unchecked {
++i;
}
}
vm.resumeGasMetering();
}
}
Loading

0 comments on commit 5d2ad62

Please sign in to comment.