From 4bb4e3ad1bc1826828ba7fc0adc8e1f5d08797eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= <33313130+jeremyjams@users.noreply.github.com> Date: Fri, 5 Jul 2024 11:45:38 +0200 Subject: [PATCH 1/2] Deploy and configure ENS with hardhat --- CHANGELOG.md | 1 + deploy/0_deploy.ts | 1 - deploy/1_deploy-ens.ts | 140 +++++++++++++++++++++++++++++++++++++++++ utils/deploy-tools.ts | 33 ++++++++++ 4 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 deploy/1_deploy-ens.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 48d97efe..5c25acc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## vNEXT +- Deploy and configure ENS with hardhat. (#93) - Fix contribute & finalize with callbacks. (#92) - [Deploy Poco sponsoring on local fork of Bellecour](./scripts/sponsoring/README.md). (#91) - Create slither smart contract entry point and run slither analysis on new contracts. (#87) diff --git a/deploy/0_deploy.ts b/deploy/0_deploy.ts index 51e250f1..fea06dd8 100644 --- a/deploy/0_deploy.ts +++ b/deploy/0_deploy.ts @@ -47,7 +47,6 @@ interface Category { workClockTimeRef: number; } const CONFIG = require('../config/config.json'); -// TODO: Deploy & setup ENS without hardhat-truffle /** * @dev Deploying contracts with `npx hardhat deploy` task brought by diff --git a/deploy/1_deploy-ens.ts b/deploy/1_deploy-ens.ts new file mode 100644 index 00000000..fa91d8b5 --- /dev/null +++ b/deploy/1_deploy-ens.ts @@ -0,0 +1,140 @@ +// SPDX-FileCopyrightText: 2024 IEXEC BLOCKCHAIN TECH +// SPDX-License-Identifier: Apache-2.0 + +import hre, { deployments, ethers } from 'hardhat'; +import { + ENS, + ENSIntegration__factory, + ENSRegistry__factory, + FIFSRegistrar, + FIFSRegistrar__factory, + IexecAccessors__factory, + PublicResolver, + PublicResolver__factory, + ReverseRegistrar, + ReverseRegistrar__factory, +} from '../typechain'; +import { deploy } from '../utils/deploy-tools'; + +module.exports = async function () { + console.log('Deploying and configuring ENS..'); + const chainId = (await ethers.provider.getNetwork()).chainId; + if (chainId < 1000) { + // skip ENS setup for mainnet and testnet + return; + } + const [owner] = await hre.ethers.getSigners(); + const erc1538ProxyAddress = (await deployments.get('ERC1538Proxy')).address; + const iexecAccessorsInstance = IexecAccessors__factory.connect(erc1538ProxyAddress, owner); + const appRegistryAddress = await iexecAccessorsInstance.appregistry(); + const datasetRegistryAddress = await iexecAccessorsInstance.datasetregistry(); + const workerpoolRegistryAddress = await iexecAccessorsInstance.workerpoolregistry(); + const ens = (await deploy(new ENSRegistry__factory(), owner, [])) as ENS; + const resolver = (await deploy(new PublicResolver__factory(), owner, [ + ens.address, + ])) as PublicResolver; + const reverseRegistrar = (await deploy(new ReverseRegistrar__factory(), owner, [ + ens.address, + resolver.address, + ])) as ReverseRegistrar; + const registrars: { [name: string]: FIFSRegistrar } = {}; + // root registrar + await registerDomain(''); + await registrars[''].register(labelhash('reverse'), owner.address).then((tx) => tx.wait()); + await ens + .setSubnodeOwner( + ethers.utils.namehash('reverse'), + labelhash('addr'), + reverseRegistrar.address, + ) + .then((tx) => tx.wait()); + await registerDomain('eth'); + await registerDomain('iexec', 'eth'); + await registerDomain('v5', 'iexec.eth'); + await registerDomain('users', 'iexec.eth'); + await registerDomain('apps', 'iexec.eth'); + await registerDomain('datasets', 'iexec.eth'); + await registerDomain('pools', 'iexec.eth'); + await registerAddress('admin', 'iexec.eth', owner.address); + await registerAddress('rlc', 'iexec.eth', await iexecAccessorsInstance.token()); + await registerAddress('core', 'v5.iexec.eth', erc1538ProxyAddress); + await registerAddress('apps', 'v5.iexec.eth', appRegistryAddress); + await registerAddress('datasets', 'v5.iexec.eth', datasetRegistryAddress); + await registerAddress('workerpools', 'v5.iexec.eth', workerpoolRegistryAddress); + await reverseRegistrar.setName('admin.iexec.eth').then((tx) => tx.wait()); + await setReverseName(erc1538ProxyAddress, 'core.v5.iexec.eth'); + await setReverseName(appRegistryAddress, 'apps.v5.iexec.eth'); + await setReverseName(datasetRegistryAddress, 'datasets.v5.iexec.eth'); + await setReverseName(workerpoolRegistryAddress, 'workerpools.v5.iexec.eth'); + + /** + * Register domain on ENS. + */ + async function registerDomain(label: string, domain: string = '') { + const name = domain ? `${label}.${domain}` : `${label}`; + const labelHash = label ? labelhash(label) : '0x0'; + const nameHash = name ? ethers.utils.namehash(name) : ethers.constants.HashZero; + const existingRegistrarAddress = await ens.owner(nameHash); + let registrar; + if ((await ethers.provider.getCode(existingRegistrarAddress)) == '0x') { + registrar = (await deploy( + new FIFSRegistrar__factory(), + owner, + [ens.address, nameHash], + { quiet: true }, + )) as FIFSRegistrar; + if (!!name) { + await registrars[domain] + .register(labelHash, registrar.address) + .then((tx) => tx.wait()); + } else { + await ens.setOwner(nameHash, registrar.address).then((tx) => tx.wait()); + } + } else { + registrar = FIFSRegistrar__factory.connect(existingRegistrarAddress, ethers.provider); + } + registrars[name] = registrar; + console.log(`FIFSRegistrar for domain ${name}: ${registrars[name].address}`); + return registrar; + } + + /** + * Register address on ENS. + */ + async function registerAddress(label: string, domain: string, address: string) { + const name = `${label}.${domain}`; + const labelHash = labelhash(label); + const nameHash = ethers.utils.namehash(name); + // register as subdomain + await registrars[domain] + .connect(owner) + .register(labelHash, owner.address) + .then((tx) => tx.wait()); + // link to ens (resolver & addr) + await ens + .connect(owner) + .setResolver(nameHash, resolver.address) + .then((tx) => tx.wait()); + await resolver + .connect(owner) + ['setAddr(bytes32,uint256,bytes)'](nameHash, 60, address) + .then((tx) => tx.wait()); + } + + /** + * Set ENS reverse name of contract. + */ + async function setReverseName(contractAddress: string, name: string) { + await ENSIntegration__factory.connect(contractAddress, owner) + .setName(ens.address, name) + .then((tx) => tx.wait()); + } + + /** + * Hash a label for the ENS. + * See: https://docs.ens.domains/resolution/names#labelhash + */ + function labelhash(label: string) { + return ethers.utils.id(label.toLowerCase()); + } +}; diff --git a/utils/deploy-tools.ts b/utils/deploy-tools.ts index 1d5dea4e..45e500e2 100644 --- a/utils/deploy-tools.ts +++ b/utils/deploy-tools.ts @@ -1,6 +1,39 @@ // SPDX-FileCopyrightText: 2024 IEXEC BLOCKCHAIN TECH // SPDX-License-Identifier: Apache-2.0 +import { ContractFactory } from '@ethersproject/contracts'; +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; +import { deployments } from 'hardhat'; + +/** + * Deploy a contract. + * @param contractFactory The contract to deploy + * @param deployer The signer to deploy the contract + * @param constructorArgs Arguments passed to the contract constructor at deployment + * @param opts Additional options + * @returns an instance of the deployed contract + */ +export async function deploy( + contractFactory: ContractFactory, + deployer: SignerWithAddress, + constructorArgs?: any[], + opts?: { quiet: boolean }, +) { + const contractInstance = await contractFactory + .connect(deployer) + .deploy(...(constructorArgs ?? [])) + .then((x) => x.deployed()); + const contractName = getBaseNameFromContractFactory(contractFactory); + await deployments.save(contractName, { + abi: (contractFactory as any).constructor.abi, + address: contractInstance.address, + }); + if (!opts || (opts && !opts.quiet)) { + console.log(`${contractName}: ${contractInstance.address}`); + } + return contractInstance; +} + /** * Extract base contract name from contract factory name. * Inputting `MyBoxContract__factory` returns `MyBoxContract`. From 33ec55da8ffbce25a76cc9670b0ad87632195a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= <33313130+jeremyjams@users.noreply.github.com> Date: Fri, 5 Jul 2024 15:53:35 +0200 Subject: [PATCH 2/2] Add log a,d use full empty hash --- deploy/1_deploy-ens.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deploy/1_deploy-ens.ts b/deploy/1_deploy-ens.ts index fa91d8b5..a8f1c934 100644 --- a/deploy/1_deploy-ens.ts +++ b/deploy/1_deploy-ens.ts @@ -21,6 +21,7 @@ module.exports = async function () { const chainId = (await ethers.provider.getNetwork()).chainId; if (chainId < 1000) { // skip ENS setup for mainnet and testnet + console.log('Skipping ENS for public networks'); return; } const [owner] = await hre.ethers.getSigners(); @@ -72,7 +73,7 @@ module.exports = async function () { */ async function registerDomain(label: string, domain: string = '') { const name = domain ? `${label}.${domain}` : `${label}`; - const labelHash = label ? labelhash(label) : '0x0'; + const labelHash = label ? labelhash(label) : ethers.constants.HashZero; const nameHash = name ? ethers.utils.namehash(name) : ethers.constants.HashZero; const existingRegistrarAddress = await ens.owner(nameHash); let registrar;