Skip to content

Commit 8dc9ea3

Browse files
Deploy and configure ENS with hardhat (#93)
2 parents 247db2a + 33ec55d commit 8dc9ea3

4 files changed

Lines changed: 175 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Changelog
22

33
## vNEXT
4+
- Deploy and configure ENS with hardhat. (#93)
45
- Fix contribute & finalize with callbacks. (#92)
56
- [Deploy Poco sponsoring on local fork of Bellecour](./scripts/sponsoring/README.md). (#91)
67
- Create slither smart contract entry point and run slither analysis on new contracts. (#87)

deploy/0_deploy.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ interface Category {
4747
workClockTimeRef: number;
4848
}
4949
const CONFIG = require('../config/config.json');
50-
// TODO: Deploy & setup ENS without hardhat-truffle
5150

5251
/**
5352
* @dev Deploying contracts with `npx hardhat deploy` task brought by

deploy/1_deploy-ens.ts

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// SPDX-FileCopyrightText: 2024 IEXEC BLOCKCHAIN TECH <contact@iex.ec>
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import hre, { deployments, ethers } from 'hardhat';
5+
import {
6+
ENS,
7+
ENSIntegration__factory,
8+
ENSRegistry__factory,
9+
FIFSRegistrar,
10+
FIFSRegistrar__factory,
11+
IexecAccessors__factory,
12+
PublicResolver,
13+
PublicResolver__factory,
14+
ReverseRegistrar,
15+
ReverseRegistrar__factory,
16+
} from '../typechain';
17+
import { deploy } from '../utils/deploy-tools';
18+
19+
module.exports = async function () {
20+
console.log('Deploying and configuring ENS..');
21+
const chainId = (await ethers.provider.getNetwork()).chainId;
22+
if (chainId < 1000) {
23+
// skip ENS setup for mainnet and testnet
24+
console.log('Skipping ENS for public networks');
25+
return;
26+
}
27+
const [owner] = await hre.ethers.getSigners();
28+
const erc1538ProxyAddress = (await deployments.get('ERC1538Proxy')).address;
29+
const iexecAccessorsInstance = IexecAccessors__factory.connect(erc1538ProxyAddress, owner);
30+
const appRegistryAddress = await iexecAccessorsInstance.appregistry();
31+
const datasetRegistryAddress = await iexecAccessorsInstance.datasetregistry();
32+
const workerpoolRegistryAddress = await iexecAccessorsInstance.workerpoolregistry();
33+
const ens = (await deploy(new ENSRegistry__factory(), owner, [])) as ENS;
34+
const resolver = (await deploy(new PublicResolver__factory(), owner, [
35+
ens.address,
36+
])) as PublicResolver;
37+
const reverseRegistrar = (await deploy(new ReverseRegistrar__factory(), owner, [
38+
ens.address,
39+
resolver.address,
40+
])) as ReverseRegistrar;
41+
const registrars: { [name: string]: FIFSRegistrar } = {};
42+
// root registrar
43+
await registerDomain('');
44+
await registrars[''].register(labelhash('reverse'), owner.address).then((tx) => tx.wait());
45+
await ens
46+
.setSubnodeOwner(
47+
ethers.utils.namehash('reverse'),
48+
labelhash('addr'),
49+
reverseRegistrar.address,
50+
)
51+
.then((tx) => tx.wait());
52+
await registerDomain('eth');
53+
await registerDomain('iexec', 'eth');
54+
await registerDomain('v5', 'iexec.eth');
55+
await registerDomain('users', 'iexec.eth');
56+
await registerDomain('apps', 'iexec.eth');
57+
await registerDomain('datasets', 'iexec.eth');
58+
await registerDomain('pools', 'iexec.eth');
59+
await registerAddress('admin', 'iexec.eth', owner.address);
60+
await registerAddress('rlc', 'iexec.eth', await iexecAccessorsInstance.token());
61+
await registerAddress('core', 'v5.iexec.eth', erc1538ProxyAddress);
62+
await registerAddress('apps', 'v5.iexec.eth', appRegistryAddress);
63+
await registerAddress('datasets', 'v5.iexec.eth', datasetRegistryAddress);
64+
await registerAddress('workerpools', 'v5.iexec.eth', workerpoolRegistryAddress);
65+
await reverseRegistrar.setName('admin.iexec.eth').then((tx) => tx.wait());
66+
await setReverseName(erc1538ProxyAddress, 'core.v5.iexec.eth');
67+
await setReverseName(appRegistryAddress, 'apps.v5.iexec.eth');
68+
await setReverseName(datasetRegistryAddress, 'datasets.v5.iexec.eth');
69+
await setReverseName(workerpoolRegistryAddress, 'workerpools.v5.iexec.eth');
70+
71+
/**
72+
* Register domain on ENS.
73+
*/
74+
async function registerDomain(label: string, domain: string = '') {
75+
const name = domain ? `${label}.${domain}` : `${label}`;
76+
const labelHash = label ? labelhash(label) : ethers.constants.HashZero;
77+
const nameHash = name ? ethers.utils.namehash(name) : ethers.constants.HashZero;
78+
const existingRegistrarAddress = await ens.owner(nameHash);
79+
let registrar;
80+
if ((await ethers.provider.getCode(existingRegistrarAddress)) == '0x') {
81+
registrar = (await deploy(
82+
new FIFSRegistrar__factory(),
83+
owner,
84+
[ens.address, nameHash],
85+
{ quiet: true },
86+
)) as FIFSRegistrar;
87+
if (!!name) {
88+
await registrars[domain]
89+
.register(labelHash, registrar.address)
90+
.then((tx) => tx.wait());
91+
} else {
92+
await ens.setOwner(nameHash, registrar.address).then((tx) => tx.wait());
93+
}
94+
} else {
95+
registrar = FIFSRegistrar__factory.connect(existingRegistrarAddress, ethers.provider);
96+
}
97+
registrars[name] = registrar;
98+
console.log(`FIFSRegistrar for domain ${name}: ${registrars[name].address}`);
99+
return registrar;
100+
}
101+
102+
/**
103+
* Register address on ENS.
104+
*/
105+
async function registerAddress(label: string, domain: string, address: string) {
106+
const name = `${label}.${domain}`;
107+
const labelHash = labelhash(label);
108+
const nameHash = ethers.utils.namehash(name);
109+
// register as subdomain
110+
await registrars[domain]
111+
.connect(owner)
112+
.register(labelHash, owner.address)
113+
.then((tx) => tx.wait());
114+
// link to ens (resolver & addr)
115+
await ens
116+
.connect(owner)
117+
.setResolver(nameHash, resolver.address)
118+
.then((tx) => tx.wait());
119+
await resolver
120+
.connect(owner)
121+
['setAddr(bytes32,uint256,bytes)'](nameHash, 60, address)
122+
.then((tx) => tx.wait());
123+
}
124+
125+
/**
126+
* Set ENS reverse name of contract.
127+
*/
128+
async function setReverseName(contractAddress: string, name: string) {
129+
await ENSIntegration__factory.connect(contractAddress, owner)
130+
.setName(ens.address, name)
131+
.then((tx) => tx.wait());
132+
}
133+
134+
/**
135+
* Hash a label for the ENS.
136+
* See: https://docs.ens.domains/resolution/names#labelhash
137+
*/
138+
function labelhash(label: string) {
139+
return ethers.utils.id(label.toLowerCase());
140+
}
141+
};

utils/deploy-tools.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,39 @@
11
// SPDX-FileCopyrightText: 2024 IEXEC BLOCKCHAIN TECH <contact@iex.ec>
22
// SPDX-License-Identifier: Apache-2.0
33

4+
import { ContractFactory } from '@ethersproject/contracts';
5+
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
6+
import { deployments } from 'hardhat';
7+
8+
/**
9+
* Deploy a contract.
10+
* @param contractFactory The contract to deploy
11+
* @param deployer The signer to deploy the contract
12+
* @param constructorArgs Arguments passed to the contract constructor at deployment
13+
* @param opts Additional options
14+
* @returns an instance of the deployed contract
15+
*/
16+
export async function deploy(
17+
contractFactory: ContractFactory,
18+
deployer: SignerWithAddress,
19+
constructorArgs?: any[],
20+
opts?: { quiet: boolean },
21+
) {
22+
const contractInstance = await contractFactory
23+
.connect(deployer)
24+
.deploy(...(constructorArgs ?? []))
25+
.then((x) => x.deployed());
26+
const contractName = getBaseNameFromContractFactory(contractFactory);
27+
await deployments.save(contractName, {
28+
abi: (contractFactory as any).constructor.abi,
29+
address: contractInstance.address,
30+
});
31+
if (!opts || (opts && !opts.quiet)) {
32+
console.log(`${contractName}: ${contractInstance.address}`);
33+
}
34+
return contractInstance;
35+
}
36+
437
/**
538
* Extract base contract name from contract factory name.
639
* Inputting `MyBoxContract__factory` returns `MyBoxContract`.

0 commit comments

Comments
 (0)