Skip to content
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
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