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

Add metadata #22

Merged
merged 11 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion crosschain-resolver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ On L1
// On L1
await ENS.setResolver(l1lresolver)
const l2resolverAddress = await DelegatableResolverFactory.predictAddress(OWNER_ADDRESS)
await L1Resolver.setTarget(node, l2resolverAddress)
await L1Resolver.setTarget(encodedname, l2resolverAddress)
// On L2
const l2resolverAddress = await DelegatableResolverFactory.predictAddress(OWNER_ADDRESS)
await DelegatableResolverFactory.create(OWNER_ADDRESS)
Expand Down
32 changes: 32 additions & 0 deletions crosschain-resolver/contracts/IMetadataResolver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

interface IMetadataResolver {
/*
* @notice Get metadata about the CCIP Resolver ENSIP 16 https://docs.ens.domains/ens-improvement-proposals/ensip-16-offchain-metadata
* @dev This function provides metadata about the CCIP Resolver, including its name, coin type, GraphQL URL, storage type, and encoded information.
* @param name The domain name in format (dnsEncoded)
* @return coinType The cointype of the chain the target contract locates such as Optimism, Base, Arb, etc
* @return graphqlUrl The GraphQL URL used by the resolver
* @return storageType 0 = EVM, 1 = Non blockchain, 2 = Starknet
* @return storageLocation The storage identifier. For EVM chains, this is the address of the resolver contract.
* @return context. An identifier used by l2 graph indexer for Domain schema id (`context-namehash`) allowing multiple resolver contracts to have own namespace.
*
*/
function metadata(bytes calldata name) external view returns (
uint256 coinType,
string memory graphqlUrl,
uint8 storageType,
bytes memory storageLocation,
bytes memory context
);

event MetadataChanged(
bytes name,
uint256 coinType,
string graphqlUrl,
uint8 storageType,
bytes storageLocation,
bytes context
);
}
73 changes: 61 additions & 12 deletions crosschain-resolver/contracts/L1Resolver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ import {ITextResolver} from "@ensdomains/ens-contracts/contracts/resolvers/profi
import {IContentHashResolver} from "@ensdomains/ens-contracts/contracts/resolvers/profiles/IContentHashResolver.sol";
import "@ensdomains/ens-contracts/contracts/resolvers/profiles/IExtendedResolver.sol";
import {ITargetResolver} from './ITargetResolver.sol';
import {IMetadataResolver} from './IMetadataResolver.sol';
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";

contract L1Resolver is EVMFetchTarget, ITargetResolver, IExtendedResolver, ERC165 {
contract L1Resolver is EVMFetchTarget, ITargetResolver, IMetadataResolver, IExtendedResolver, ERC165 {
using EVMFetcher for EVMFetcher.EVMFetchRequest;
using BytesUtils for bytes;
IEVMVerifier public immutable verifier;
Expand All @@ -27,9 +28,10 @@ contract L1Resolver is EVMFetchTarget, ITargetResolver, IExtendedResolver, ERC16
uint256 constant VERSIONABLE_ADDRESSES_SLOT = 2;
uint256 constant VERSIONABLE_HASHES_SLOT = 3;
uint256 constant VERSIONABLE_TEXTS_SLOT = 10;
string public graphqlUrl;
uint256 public l2ResolverCoinType;

event TargetSet(bytes32 indexed node, address target);

event TargetSet(bytes name, address target);
function isAuthorised(bytes32 node) internal view returns (bool) {
// TODO: Add support for
// trustedETHController
Expand All @@ -43,32 +45,54 @@ contract L1Resolver is EVMFetchTarget, ITargetResolver, IExtendedResolver, ERC16
return owner == msg.sender;
}

modifier authorised(bytes32 node) {
require(isAuthorised(node));
_;
}

/**
* @param _verifier The chain verifier address
* @param _ens The ENS registry address
* @param _nameWrapper The ENS name wrapper address
* @param _graphqlUrl The offchain/l2 graphql endpoint url
* @param _l2ResolverCoinType The chainId at which the resolver resolves data from. 0 if storageLocation is offChain
*/
constructor(
IEVMVerifier _verifier,
ENS _ens,
INameWrapper _nameWrapper
INameWrapper _nameWrapper,
string memory _graphqlUrl,
uint256 _l2ResolverCoinType
){
require(address(_nameWrapper) != address(0), "Name Wrapper address must be set");
require(address(_verifier) != address(0), "Verifier address must be set");
require(address(_ens) != address(0), "Registry address must be set");
verifier = _verifier;
ens = _ens;
nameWrapper = _nameWrapper;
graphqlUrl = _graphqlUrl;
l2ResolverCoinType = _l2ResolverCoinType;
}

/**
* Set target address to verify aagainst
* @param node The ENS node to query.
* @param name The encoded name to query.
* @param target The L2 resolver address to verify against.
*/
function setTarget(bytes32 node, address target) public authorised(node){
function setTarget(bytes calldata name, address target) public {
(bytes32 node,) = getTarget(name);
require(isAuthorised(node));
targets[node] = target;
emit TargetSet(node, target);
emit TargetSet(name, target);
(
,,
uint8 storageType,
bytes memory storageLocation,
bytes memory context
) = metadata(name);
emit MetadataChanged(
name,
l2ResolverCoinType,
graphqlUrl,
storageType,
storageLocation,
context
);
}

/**
Expand Down Expand Up @@ -218,12 +242,37 @@ contract L1Resolver is EVMFetchTarget, ITargetResolver, IExtendedResolver, ERC16
return abi.encode(values[1]);
}

/**
* @notice Get metadata about the L1 Resolver
* @dev This function provides metadata about the L1 Resolver, including its name, coin type, GraphQL URL, storage type, and encoded information.
* @param name The domain name in format (dnsEncoded)
* @return coinType The cointype of the chain the target contract locates such as Optimism, Base, Arb, etc
* @return graphqlUrl The GraphQL URL used by the resolver
* @return storageType Storage Type (0 for EVM)
* @return storageLocation The storage identifier. For EVM chains, this is the address of the resolver contract.
* @return context. An identifier used by l2 graph indexer for Domain schema id (`context-namehash`) allowing multiple resolver contracts to have own namespace.
*/
function metadata(
bytes calldata name
) public view returns (uint256, string memory, uint8, bytes memory, bytes memory) {
(, address target) = getTarget(name);

return (
l2ResolverCoinType,
graphqlUrl,
uint8(0), // storage Type 0 => EVM
abi.encodePacked(address(target)), // storage location => l2 resolver address
abi.encodePacked(address(target)) // context => l2 resolver address
);
}

function supportsInterface(
bytes4 interfaceId
) public override view returns (bool) {
return
interfaceId == type(IExtendedResolver).interfaceId ||
interfaceId == type(ITargetResolver).interfaceId ||
interfaceId == type(IMetadataResolver).interfaceId ||
super.supportsInterface(interfaceId);
}
}
15 changes: 12 additions & 3 deletions crosschain-resolver/deploy_l1/10_l1resolver.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {HardhatRuntimeEnvironment} from 'hardhat/types';
import {DeployFunction} from 'hardhat-deploy/types';

import {convertEVMChainIdToCoinType} from '@ensdomains/address-encoder'

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const {deployments, getNamedAccounts} = hre;
Expand All @@ -11,13 +11,22 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const VERIFIER_ADDRESS = process.env.VERIFIER_ADDRESS
const ENS_ADDRESS = process.env.ENS_ADDRESS
const WRAPPER_ADDRESS = process.env.WRAPPER_ADDRESS
const L2_GRAPHQL_URL = process.env.L2_GRAPHQL_URL
const L2_RESOLVER_NAME = process.env.L2_RESOLVER_NAME
const L2_CHAIN_ID = process.env.L2_CHAIN_ID

if(!VERIFIER_ADDRESS) throw ('Set $VERIFIER_ADDRESS')
if(!ENS_ADDRESS) throw ('Set $ENS_ADDRESS')
if(!WRAPPER_ADDRESS) throw ('Set $WRAPPER_ADDRESS')
console.log({VERIFIER_ADDRESS,ENS_ADDRESS, WRAPPER_ADDRESS})
if(!L2_GRAPHQL_URL) throw ('Set $L2_GRAPHQL_URL')
if(!L2_RESOLVER_NAME) throw ('Set $L2_RESOLVER_NAME')
if(!L2_CHAIN_ID) throw ('Set $L2_CHAIN_ID')

const L2_COINTYPE = convertEVMChainIdToCoinType(parseInt(L2_CHAIN_ID))
console.log({VERIFIER_ADDRESS,ENS_ADDRESS, WRAPPER_ADDRESS,L2_GRAPHQL_URL,L2_RESOLVER_NAME,L2_COINTYPE})
await deploy('L1Resolver', {
from: deployer,
args: [VERIFIER_ADDRESS,ENS_ADDRESS,WRAPPER_ADDRESS],
args: [VERIFIER_ADDRESS,ENS_ADDRESS,WRAPPER_ADDRESS,L2_GRAPHQL_URL,L2_RESOLVER_NAME,L2_COINTYPE],
log: true,
});
};
Expand Down
1 change: 1 addition & 0 deletions crosschain-resolver/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"typescript": "^5.2.2"
},
"dependencies": {
"@ensdomains/address-encoder": "^0.2.22",
"@ensdomains/ens-contracts": "ensdomains/ens-contracts#feature/crosschain-resolver-with-reverse-registrar",
"@ensdomains/evm-verifier": "^0.1.0",
"@eth-optimism/contracts": "^0.6.0"
Expand Down
10 changes: 6 additions & 4 deletions crosschain-resolver/scripts/setupl1.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import hre from 'hardhat';
import packet from 'dns-packet';
const ethers = hre.ethers;
const abi = [
"function predictAddress(address) view returns (address)"
]
const encodeName = (name) => '0x' + packet.name.encode(name).toString('hex')

export const main = async () => {
const [signer] = await hre.ethers.getSigners();
Expand All @@ -13,8 +15,8 @@ export const main = async () => {
const L1_RESOLVER_ADDRESS = process.env.L1_RESOLVER_ADDRESS;
const ENS_NAME = process.env.ENS_NAME;
const ETH_ADDRESS = signer.address
const node = ethers.namehash(ENS_NAME);
console.log({L2_RESOLVER_FACTORY_ADDRESS, L1_RESOLVER_ADDRESS, ENS_NAME, node, ETH_ADDRESS})
const encodedname = encodeName(ENS_NAME);
console.log({L2_RESOLVER_FACTORY_ADDRESS, L1_RESOLVER_ADDRESS, ENS_NAME, encodedname, ETH_ADDRESS})


if (!process.env.L2_RESOLVER_FACTORY_ADDRESS || !process.env.L1_PROVIDER_URL || !process.env.L2_PROVIDER_URL || !process.env.ENS_NAME)
Expand All @@ -23,7 +25,7 @@ export const main = async () => {
const provider = new ethers.JsonRpcProvider(L1_PROVIDER_URL);
const currentResolver = await provider.getResolver(ENS_NAME)
if(currentResolver.address !== L1_RESOLVER_ADDRESS){
console.log({ENS_NAME, node, CURRENT_RESOLVER_ADDRESS:currentResolver.address, L1_RESOLVER_ADDRESS})
console.log({ENS_NAME, CURRENT_RESOLVER_ADDRESS:currentResolver.address, L1_RESOLVER_ADDRESS})
throw(`Set the resolver of the parent name to ${L1_RESOLVER_ADDRESS}`)
}else{
console.log(`The resolver of ${ENS_NAME} is set to ${L1_RESOLVER_ADDRESS}`)
Expand All @@ -35,7 +37,7 @@ export const main = async () => {
const l2resolverAddress = await l2Factory.predictAddress(ETH_ADDRESS)
console.log({l2resolverAddress})
const l1resolver = (await ethers.getContractFactory('L1Resolver', signer)).attach(L1_RESOLVER_ADDRESS);
const tx2 = await l1resolver.setTarget(node, l2resolverAddress)
const tx2 = await l1resolver.setTarget(encodedname, l2resolverAddress)

console.log(`Setting l2 resolver ${l2resolverAddress} as a target`, (await tx2.wait()).hash)
console.log(`Set export L2_RESOLVER_ADDRESS=${l2resolverAddress}`)
Expand Down
Loading
Loading