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
6 changes: 2 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,9 @@ jobs:
run: npm run build
- name: Check storage layout
run: npm run test-storage-layout
- name: Run deployment
# Basic deployment to make sure everything is ok.
# Could be removed in the future if not relevant.
- name: Test deployment
run: npm run deploy
- name: Test Timelock Deployment
- name: Test Timelock deployment
run: npm run deploy:timelock
- name: Run coverage
run: npm run coverage
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- [x] `IexecPoco2Delegate.sol`

### Features
- Refactor Factory deployer (#206)
- Enable native tests on CI (#204)
- Migrate to Ethers v6:
- Deployment scripts (#187, #203)
Expand Down
4 changes: 2 additions & 2 deletions deploy/0_deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import {
} from '../typechain';
import { Ownable__factory } from '../typechain/factories/@openzeppelin/contracts/access';
import config from '../utils/config';
import { FactoryDeployerHelper } from '../utils/FactoryDeployerHelper';
import { FactoryDeployer } from '../utils/FactoryDeployer';
import { linkContractToProxy } from '../utils/proxy-tools';

/**
Expand All @@ -57,7 +57,7 @@ export default async function deploy() {
const [owner] = await ethers.getSigners();
const deploymentOptions = config.getChainConfigOrDefault(chainId);
const salt = process.env.SALT || deploymentOptions.v5.salt || ethers.ZeroHash;
const factoryDeployer = new FactoryDeployerHelper(owner, salt);
const factoryDeployer = new FactoryDeployer(owner, salt);
// Deploy RLC
const isTokenMode = !config.isNativeChain(deploymentOptions);
let rlcInstanceAddress = isTokenMode
Expand Down
4 changes: 2 additions & 2 deletions scripts/deploy-timelock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import { duration } from '@nomicfoundation/hardhat-network-helpers/dist/src/helpers/time';
import { ethers } from 'hardhat';
import { TimelockController__factory } from '../typechain';
import { FactoryDeployerHelper } from '../utils/FactoryDeployerHelper';
import { FactoryDeployer } from '../utils/FactoryDeployer';
import config from '../utils/config';

/**
Expand All @@ -18,7 +18,7 @@ export const deploy = async () => {
const salt = process.env.SALT || config.getChainConfigOrDefault(chainId).v5.salt;

// Initialize factory deployer
const factoryDeployer = new FactoryDeployerHelper(owner, salt);
const factoryDeployer = new FactoryDeployer(owner, salt);

// Deploy TimelockController
const ONE_WEEK_IN_SECONDS = duration.days(7);
Expand Down
129 changes: 85 additions & 44 deletions utils/FactoryDeployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,98 @@
import factoryJson from '@amxx/factory/deployments/GenericFactory.json';
import factoryShanghaiJson from '@amxx/factory/deployments/GenericFactory_shanghai.json';
import { SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers';
import { Contract, ethers } from 'ethers';
import hre from 'hardhat';
import { ContractFactory } from 'ethers';
import hre, { deployments, ethers } from 'hardhat';
import { GenericFactory, GenericFactory__factory } from '../typechain';
import config from './config';
import { getBaseNameFromContractFactory } from './deploy-tools';

interface FactoryConfig {
address: string;
deployer: string;
cost: string;
tx: string;
abi: any[];
}

const factoryConfig: FactoryConfig =
!config.isNativeChain() && hre.network.name.includes('hardhat')
? factoryShanghaiJson
: factoryJson;
export class FactoryDeployer {
owner: SignerWithAddress;
salt: string;
genericFactory!: GenericFactory;

export class EthersDeployer {
private factory!: Contract;
private factoryAsPromise: Promise<Contract>;
constructor(owner: SignerWithAddress, salt: string) {
this.owner = owner;
this.salt = salt;
}

constructor(wallet: SignerWithAddress) {
this.factoryAsPromise = new Promise(async (resolve, reject) => {
if ((await wallet.provider!.getCode(factoryConfig.address)) !== '0x') {
console.log(`→ Factory is available on this network`);
} else {
try {
console.log(`→ Factory is not yet deployed on this network`);
await wallet
.sendTransaction({
to: factoryConfig.deployer,
value: factoryConfig.cost,
})
.then((tx) => tx.wait());
await wallet.provider
.broadcastTransaction(factoryConfig.tx)
.then((tx) => tx.wait());
console.log(`→ Factory successfully deployed`);
} catch (e) {
console.log(`→ Error deploying the factory`);
reject(e);
}
}
this.factory = new ethers.Contract(factoryConfig.address, factoryConfig.abi, wallet);
resolve(this.factory);
/**
* Deploy a contract through GenericFactory [and optionally trigger a call]
*/
async deployWithFactory(
contractFactory: ContractFactory,
constructorArgs?: any[],
call?: string,
) {
await this.init();
let bytecode = (await contractFactory.getDeployTransaction(...(constructorArgs ?? [])))
.data;
if (!bytecode) {
throw new Error('Failed to prepare bytecode');
}
let contractAddress = await (call
? this.genericFactory.predictAddressWithCall(bytecode, this.salt, call)
: this.genericFactory.predictAddress(bytecode, this.salt));
const previouslyDeployed = (await ethers.provider.getCode(contractAddress)) !== '0x';
if (!previouslyDeployed) {
await (
call
? this.genericFactory.createContractAndCall(bytecode, this.salt, call)
: this.genericFactory.createContract(bytecode, this.salt)
).then((tx) => tx.wait());
}
const contractName = getBaseNameFromContractFactory(contractFactory);
console.log(
`${contractName}: ${contractAddress} ${
previouslyDeployed ? ' (previously deployed)' : ''
}`,
);
await deployments.save(contractName, {
// abi field is not used but is a required arg. Empty abi would be fine
abi: (contractFactory as any).constructor.abi,
address: contractAddress,
bytecode: bytecode,
args: constructorArgs,
});
return contractAddress;
}

async ready(): Promise<void> {
await this.factoryAsPromise;
private async init() {
if (this.genericFactory) {
// Already initialized.
return;
}
const factoryConfig: FactoryConfig =
!config.isNativeChain() && hre.network.name.includes('hardhat')
? factoryShanghaiJson
: factoryJson;
this.genericFactory = GenericFactory__factory.connect(factoryConfig.address, this.owner);
if ((await ethers.provider.getCode(factoryConfig.address)) !== '0x') {
console.log(`→ Factory is available on this network`);
return;
}
try {
console.log(`→ Factory is not yet deployed on this network`);
await this.owner
.sendTransaction({
to: factoryConfig.deployer,
value: factoryConfig.cost,
})
.then((tx) => tx.wait());
await ethers.provider.broadcastTransaction(factoryConfig.tx).then((tx) => tx.wait());
console.log(`→ Factory successfully deployed`);
} catch (e) {
console.log(e);
throw new Error('→ Error deploying the factory');
}
}
}

export const factoryAddress = factoryConfig.address;
interface FactoryConfig {
address: string;
deployer: string;
cost: string;
tx: string;
abi: any[];
}
69 changes: 0 additions & 69 deletions utils/FactoryDeployerHelper.ts

This file was deleted.