Skip to content

Commit

Permalink
Merge pull request #8 from Consensys/feat/slot-for-erc721-upgradeable
Browse files Browse the repository at this point in the history
feat: made contract more configurable to adapt for NFT proxy contracts
  • Loading branch information
Julink-eth authored Sep 4, 2024
2 parents 1595d5e + bd44431 commit 963a20e
Show file tree
Hide file tree
Showing 14 changed files with 199,366 additions and 142 deletions.
36 changes: 23 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,44 @@ More precisely it is ID based, meaning the resolution of an address for a subdom
If you own an ENS domain that you want its subdomains to resolve depending on a NFT collection deployed on Linea, simply follow those steps:

1 - Get the DNS encoded name of your ENS name, you can use etherJs dnsEncode function to get it: https://docs.ethers.org/v6/api/hashing/#dnsEncode
For example for `efrogs.eth`, the DNS encoded name is `0x066566726f67730365746800`
For example for `efrogs.eth`, the DNS encoded name is `0x066566726f67730365746800`

2 - Get the target NFT contract you want to use to resolve the subdomains depending on the NFT's owner addresses.
For example: [0x194395587d7b169e63eaf251e86b1892fa8f1960](https://lineascan.build/address/0x194395587d7b169e63eaf251e86b1892fa8f1960)
For example: [0x194395587d7b169e63eaf251e86b1892fa8f1960](https://lineascan.build/address/0x194395587d7b169e63eaf251e86b1892fa8f1960)

3 - Go to the [NFT resolver contract](https://etherscan.io/address/0xed24dDCB660c83803126DAAbFAfCE3b114936C58#writeContract)
3 - Go to the [NFT resolver contract](https://etherscan.io/address/0x8210077e031302C41aCD7FccC38628CA1788A999#writeContract)

4 - Click on `Connect to Web3`with the wallet that owns the chosen ENS name (Example `efrogs.eth`)

5 - Click on `setTarget` and add the parameters:
name: [DNS encoded name of your ENS name] (Example `0x066566726f67730365746800`)
target: [NFT contrac address on Linea] (Example `0x194395587d7b169e63eaf251e86b1892fa8f1960`)
`name`: [DNS encoded name of your ENS name] (Example `0x066566726f67730365746800`)
`target`: [NFT contrac address on Linea] (Example `0x194395587d7b169e63eaf251e86b1892fa8f1960`)
Click on write and approve the transaction.

6 - Click on write and approve the transaction
6 - Click on `setBaseNodeResolver` and add the parameters:
`name`: [DNS encoded name of your ENS name] (Example `0x066566726f67730365746800`)
`resolverAddr`: [The resolver you want to keep using for you base domain] (Example `0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63`)
Click on write and approve the transaction.

7 - Go the [ENS app website](https://app.ens.domains/)
7 - Click on `setTargetAddrSlot` and add the parameters:
`name`: [DNS encoded name of your ENS name] (Example `0x066566726f67730365746800`)
`slot`: [The slot in the target NFT contract that contains the owner addresses] (Example `122`)
Click on write and approve the transaction.

8 - Go to your ENS name profile page (Example: https://app.ens.domains/efrogs.eth)
8 - Go the [ENS app website](https://app.ens.domains/)

9 - Go to the tab `More` (Example: https://app.ens.domains/efrogs.eth?tab=more)
9 - Go to your ENS name profile page (Example: https://app.ens.domains/efrogs.eth)

10 - In the `Resolver` section at the bottom, click on `Edit`
10 - Go to the tab `More` (Example: https://app.ens.domains/efrogs.eth?tab=more)

11 - Click on `Custom Resolver`
11 - In the `Resolver` section at the bottom, click on `Edit`

12 - Add the NFT Resolver's address: `0xed24dDCB660c83803126DAAbFAfCE3b114936C58`
12 - Click on `Custom Resolver`

13 - Click on `Update`, `Open Wallet`and approve the transaction.
13 - Add the NFT Resolver's address: `0x8210077e031302C41aCD7FccC38628CA1788A999`
You'll get a warning but this is normal.

14 - Click on `Update`, `Open Wallet`and approve the transaction.

Once all those steps are done you can start resolving subdomains of your ENS domain using the NFT IDs minted on Linea.

Expand Down
100 changes: 69 additions & 31 deletions contracts/NFTResolver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,18 @@ contract NFTResolver is
ENS public immutable ens;
INameWrapper public immutable nameWrapper;
uint256 public immutable l2ChainId;
// PublicResolver used to resolve a base domain such as xxx.eth when queried
address public immutable publicResolver;
// The targeted NFT contract on L2
mapping(bytes32 => address) targets;
uint256 constant OWNERS_SLOT = 2;
// The resolver for the base nodes(if any)
mapping(bytes32 => address) baseNodeResolvers;
// The owner slots in target contract containing the addresses to resolve to
mapping(address => uint256) targetAddrSlots;
// To check how old is the value/proof returned and is in the acceptable range
uint256 constant ACCEPTED_L2_BLOCK_RANGE_LENGTH = 86400;

event TargetSet(bytes name, address target);
event BaseNodeResolverSet(bytes32 node, address resolverAddr);
event TargetAddrSlotSet(address target, uint256 slot);

function isAuthorised(bytes32 node) internal view returns (bool) {
address owner = ens.owner(node);
Expand All @@ -53,18 +57,16 @@ contract NFTResolver is
error StorageHandledByL2(uint256 chainId, address contractAddress);

/**
* @param _verifier The chain verifier address
* @param _ens The ENS registry address
* @param _nameWrapper The ENS name wrapper address
* @param _l2ChainId The chainId at which the resolver resolves data from
* @param _publicResolver The PublicResolver address to use to resolve base domains
* @param _verifier The chain verifier address.
* @param _ens The ENS registry address.
* @param _nameWrapper The ENS name wrapper address.
* @param _l2ChainId The chainId at which the resolver resolves data from.
*/
constructor(
IEVMVerifier _verifier,
ENS _ens,
INameWrapper _nameWrapper,
uint256 _l2ChainId,
address _publicResolver
uint256 _l2ChainId
) {
require(
address(_nameWrapper) != address(0),
Expand All @@ -79,7 +81,6 @@ contract NFTResolver is
ens = _ens;
nameWrapper = _nameWrapper;
l2ChainId = _l2ChainId;
publicResolver = _publicResolver;
}

/**
Expand All @@ -95,8 +96,8 @@ contract NFTResolver is
}

/**
* Set target address to verify against
* @param name The encoded name to query.
* Set target address to verify against.
* @param name The DNS encoded name to set the target for.
* @param target The L2 resolver address to verify against.
*/
function setTarget(bytes calldata name, address target) external {
Expand All @@ -109,10 +110,44 @@ contract NFTResolver is
emit TargetSet(name, target);
}

/**
* Set base node resolver address.
* @param name The DNS encoded name to set the base node resolver.
* @param resolverAddr The resolver address to use.
*/
function setBaseNodeResolver(
bytes calldata name,
address resolverAddr
) external {
(bytes32 node, ) = getTarget(name);
require(
isAuthorised(node),
"Not authorized to set resolver for this node"
);
baseNodeResolvers[node] = resolverAddr;
emit BaseNodeResolverSet(node, resolverAddr);
}

/**
* Set the slot to query by ccip to get the address from the target contract.
* @param name The DNS encoded name to set the target address slot to query.
* @param slot The slot to set.
*/
function setTargetAddrSlot(bytes calldata name, uint256 slot) external {
(bytes32 node, ) = getTarget(name);
require(
isAuthorised(node),
"Not authorized to set target address slot for this node"
);
address target = targets[node];
targetAddrSlots[target] = slot;
emit TargetAddrSlotSet(target, slot);
}

/**
* @dev Returns the L2 target address that can answer queries for `name`.
* @param name DNS encoded ENS name to query
* @return node The node of the name
* @param name DNS encoded ENS name to query.
* @return node The node of the name.
* @return target The L2 resolver address to verify against.
*/
function getTarget(
Expand Down Expand Up @@ -142,9 +177,9 @@ contract NFTResolver is

/**
* @dev Resolve and verify a record stored in l2 target address. It supports subname by fetching target recursively to the nearest parent.
* @param name DNS encoded ENS name to query
* @param data The actual calldata
* @return result result of the call
* @param name DNS encoded ENS name to query.
* @param data The actual calldata.
* @return result Result of the call.
*/
function resolve(
bytes calldata name,
Expand All @@ -157,7 +192,8 @@ contract NFTResolver is

// If trying to resolve the base domain, we use the PublicResolver
if (isBaseDomain) {
return _resolve(name, data);
address baseNodeResolver = baseNodeResolvers[node];
return _resolve(baseNodeResolver, data);
}

// Only accept 1 level subdomain
Expand All @@ -168,19 +204,20 @@ contract NFTResolver is
bytes4 selector = bytes4(data);

if (selector == IAddrResolver.addr.selector) {
// Get NFT Index from the
// Get NFT Index from the DNS encoded name
uint256 nftId = extractNFTId(name);
return _addr(nftId, target);
uint256 slot = targetAddrSlots[target];
return _addr(nftId, slot, target);
}

// None selector has been found it reverts
revert("invalid selector");
}

/**
* Get the NFT Id from the ENS name's label
* @param name DNS encoded ENS name
* @return id the NFT id
* Get the NFT Id from the ENS name's label.
* @param name DNS encoded ENS name.
* @return id The NFT id.
*/
function extractNFTId(bytes calldata name) public pure returns (uint256) {
bytes memory firstLabel = LabelUtils.extractFirstLabel(name);
Expand All @@ -190,10 +227,10 @@ contract NFTResolver is
}

/**
* @dev Resolve and throws an EIP 3559 compliant error
* @param name DNS encoded ENS name to query
* @param _addr The actual calldata
* @return result result of the call
* @dev Resolve and throws an EIP 3559 compliant error.
* @param name DNS encoded ENS name to query.
* @param _addr The actual calldata.
* @return result Result of the call.
*/
function setAddr(
bytes calldata name,
Expand All @@ -212,10 +249,10 @@ contract NFTResolver is
* @return The return data, ABI encoded identically to the underlying function.
*/
function _resolve(
bytes memory,
address baseNodeResolver,
bytes memory data
) internal view returns (bytes memory) {
(bool success, bytes memory result) = publicResolver.staticcall(data);
(bool success, bytes memory result) = baseNodeResolver.staticcall(data);
if (success) {
return result;
} else {
Expand All @@ -228,11 +265,12 @@ contract NFTResolver is

function _addr(
uint256 tokenId,
uint256 slot,
address target
) private view returns (bytes memory) {
EVMFetcher
.newFetchRequest(verifier, target)
.getStatic(OWNERS_SLOT)
.getStatic(slot)
.element(tokenId)
.fetch(this.addrCallback.selector, ""); // recordVersions
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../build-info/b81f40333ccc4acb691c7be2c38e0dd4.json"
"buildInfo": "../build-info/f7390dbaa4b479c35b2e57fbc1c9bc92.json"
}

Large diffs are not rendered by default.

Loading

0 comments on commit 963a20e

Please sign in to comment.