Skip to content

Commit

Permalink
Deploy and configure ENS with hardhat (#93)
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremyjams authored Jul 5, 2024
2 parents 247db2a + 33ec55d commit 8dc9ea3
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
1 change: 0 additions & 1 deletion deploy/0_deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
141 changes: 141 additions & 0 deletions deploy/1_deploy-ens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// SPDX-FileCopyrightText: 2024 IEXEC BLOCKCHAIN TECH <contact@iex.ec>
// 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
console.log('Skipping ENS for public networks');
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) : ethers.constants.HashZero;
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());
}
};
33 changes: 33 additions & 0 deletions utils/deploy-tools.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,39 @@
// SPDX-FileCopyrightText: 2024 IEXEC BLOCKCHAIN TECH <contact@iex.ec>
// 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`.
Expand Down

0 comments on commit 8dc9ea3

Please sign in to comment.