Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FuseController #318

Open
wants to merge 15 commits into
base: l2-registry
Choose a base branch
from

Conversation

nxt3d
Copy link
Contributor

@nxt3d nxt3d commented Jan 29, 2024

Starting with SimpleController, this PR takes some of the features of NameWrapper and integrates them into a new controller called FuseController.

Copy link
Member

@Arachnid Arachnid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fuses and expiry don't do anything yet, but I assume that's intentional?

@@ -3,7 +3,11 @@
pragma solidity ^0.8.17;

interface IController {
function ownerOf(bytes calldata tokenData) external view returns (address);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this change? Requiring controllers to implement both methods complicates them, and many will have to implement ownerOf simply by calling the registry back to fetch their data from it anywy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It saves an external call.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In what circumstances?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

function ownerOf(bytes32 node) external view returns (address) {
        //get the tokenData
        bytes memory tokenData = registry.getData(uint256(node));
        (address owner, , , , ) = _unpack(tokenData);
        return owner;
    }


import "./IController.sol";

interface IControllerUpgrade is IController {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be something like IUpgradeTarget?


function upgrade(bytes32 node, bytes calldata extraData) external;

function setUpgradeController(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this an upgrade target, rather than any kind of controller?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setUpgradeControllerTarget? It's a controller to upgrade to.

import "@openzeppelin/contracts/interfaces/IERC1155.sol";
import "@openzeppelin/contracts/interfaces/IERC165.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import {IMetadataService} from "../wrapper/IMetadataService.sol";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should definitely be its own PR. However, I think it makes more sense to generate metadata onchain, to remove the centralised ownership here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting.

mapping(address => mapping(address => bool)) approvals;
mapping(address => uint256) tokenApprovalsNonce;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also be in a separate PR.

* - Byte 20: owner (address)
* - Byte 40: resolver (address)
* _ Byte 60: expiry (uint64)
* - Byte 68: fuses (uint96)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you reduce this to a uint64, the whole thing will be 96 bytes - saving a storage slot.

return resolver;
}

function expiryOf(bytes32 node) external view returns (uint64) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A generic data method may be more useful here, as well as being more gas efficient.

revert nameExpired(node);
}

// Change the controller to the upgrade contract.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to instead allow the upgrade contract to specify the new data, so that a data format migration could take place at the same time as a contract upgrade.

Copy link
Contributor Author

@nxt3d nxt3d Jan 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The upgrade target contract won't have permissions to make any changes if we don't change the controller here? There might be a way to make this more efficient, however.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but we could have a function on the upgrade contract that we call with the old data, and it returns the new data to set on the registry.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have an opinion on this, could be either way.

address(this),
subnodeOwner,
subnodeResolver,
subnodeExpiry,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't there be some enforcement here that a subnode can't expire after its parent?

return expiry <= block.timestamp;
}

function _unpack(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I mentioned before - just use calldata instead of memory and you don't need assembly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using memory allows for the function ownerOfWithData, which saves an external call.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? ownerOfWithData can specify that its argument is calldata too, and pass it straight to _unpack.

Copy link
Contributor Author

@nxt3d nxt3d Jan 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example in this function in the controller:

function ownerOf(bytes32 node) external view returns (address) {
        //get the tokenData
        bytes memory tokenData = registry.getData(uint256(node));
        (address owner, , , , ) = _unpack(tokenData);
        return owner;
    }

I just get the data from the registry and then call _unpack, but tokenData in this case is memory and not calldata.

@nxt3d
Copy link
Contributor Author

nxt3d commented Jan 29, 2024

The fuses and expiry don't do anything yet, but I assume that's intentional?

This is for sure incomplete. I wanted to get some feedback, on the direction, to make sure we are on the same page. I will make changes, and add some more commits.

@nxt3d nxt3d force-pushed the feature/l2-registry-updates branch from 3d8ea04 to e139421 Compare January 30, 2024 14:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants