Skip to content

Commit

Permalink
Merge pull request #98 from gnosis/deployMasterCopy-return-address
Browse files Browse the repository at this point in the history
Return the address when deploying a mastercopy
  • Loading branch information
asgeir-s committed Dec 22, 2022
2 parents fb01527 + 3ab13ad commit 0b67170
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 32 deletions.
28 changes: 16 additions & 12 deletions src/factory/deployModuleFactory.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,44 @@
import "hardhat-deploy";
import "@nomiclabs/hardhat-ethers";
import { constants as ethersConstants } from "ethers";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { getSingletonFactory } from "./singletonFactory";

const factorySalt =
const { AddressZero } = ethersConstants;

const FactorySalt =
"0xb0519c4c4b7945db302f69180b86f1a668153a476802c1c445fcb691ef23ef16";
const AddressZero = "0x0000000000000000000000000000000000000000";

/**
* Deploy a module factory via the singleton factory.
* It will therefore get the same address on any chain.
*
* @param hre hardhat runtime environment
* @returns The address of the deployed module factory
* @returns The address of the deployed module factory, or the zero address if it was already deployed
*/
export const deployModuleFactory = async (hre: HardhatRuntimeEnvironment) => {
export const deployModuleFactory = async (
hre: HardhatRuntimeEnvironment
): Promise<string> => {
const singletonFactory = await getSingletonFactory(hre);
console.log(" Singleton Factory: ", singletonFactory.address);
const Factory = await hre.ethers.getContractFactory("ModuleProxyFactory");
// const singletonFactory = new hardhat.ethers.Contract(singletonFactoryAddress, singletonFactoryAbi)

const targetAddress = await singletonFactory.callStatic.deploy(
Factory.bytecode,
factorySalt
FactorySalt
);
if (targetAddress == AddressZero) {
if (targetAddress === AddressZero) {
console.log(
" ModuleProxyFactory already deployed to target address on this network."
);
return;
} else {
console.log(" Target Factory Address:", targetAddress);
return AddressZero;
}

console.log(" Target Factory Address:", targetAddress);

const transactionResponse = await singletonFactory.deploy(
Factory.bytecode,
factorySalt,
FactorySalt,
{ gasLimit: 1000000 }
);

Expand All @@ -51,7 +55,7 @@ export const deployModuleFactory = async (hre: HardhatRuntimeEnvironment) => {
);

if (
(await hre.ethers.provider.getCode(factory.address)) !=
(await hre.ethers.provider.getCode(factory.address)) !==
factoryArtifact.deployedBytecode
) {
throw new Error(
Expand Down
102 changes: 84 additions & 18 deletions src/factory/mastercopyDeployer.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,123 @@
import { BytesLike, ContractFactory } from "ethers";
import {
BytesLike,
ContractFactory,
constants as ethersConstants,
} from "ethers";
import { keccak256, getCreate2Address, getAddress } from "ethers/lib/utils";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import assert from "node:assert";
import { getSingletonFactory } from "./singletonFactory";

const { AddressZero } = ethersConstants;

/**
* Deploy a module's mastercopy via the singleton factory.
*
* To get the same address on any chain.
* @param hre hardhat runtime environment
* @param mastercopyContractFactory
* @param args
* @returns The address of the deployed module mastercopy
* @param mastercopyContractFactory mastercopy to deploy
* @param args the arguments to pass to the mastercopy's constructor
* @returns The address of the deployed module mastercopy or the zero address if it was already deployed
*/
export const deployMastercopy = async (
hre: HardhatRuntimeEnvironment,
mastercopyContractFactory: ContractFactory,
args: Array<any>,
salt: string
) => {
): Promise<string> => {
const deploymentTx = mastercopyContractFactory.getDeployTransaction(...args);
if (deploymentTx.data) {
await deployMastercopyWithInitData(hre, deploymentTx.data, salt);

if (Array.isArray(deploymentTx.data) && deploymentTx.data.length > 0) {
return await deployMastercopyWithInitData(hre, deploymentTx.data, salt);
}
throw new Error("Unable to create the deployment data (no init code).");
};

/**
* Compute a module's mastercopy address. Where it is or will be deployed. And checks if it is already deployed.
*
* @param hre hardhat runtime environment
* @param mastercopyContractFactory mastercopy to get address for
* @param args the arguments passed to the mastercopy's constructor
* @returns {
* address: string; // the address where the module mastercopy will be deployed or was already deployed
* isDeployed: boolean; // true if the module mastercopy was already deployed on this chain
* }
*/
export const computeTargetAddress = async (
hre: HardhatRuntimeEnvironment,
mastercopyContractFactory: ContractFactory,
args: Array<any>,
salt: string
): Promise<{ address: string; isDeployed: boolean }> => {
const deploymentTx = mastercopyContractFactory.getDeployTransaction(...args);
const singletonFactory = await getSingletonFactory(hre);

if (!Array.isArray(deploymentTx.data) || deploymentTx.data.length === 0) {
throw new Error("Unable to create the deployment data (no init code).");
}

const initCodeHash = keccak256(deploymentTx.data);

const computedAddress = getCreate2Address(
singletonFactory.address,
salt,
initCodeHash
);

const targetAddress = getAddress(
(await singletonFactory.callStatic.deploy(
deploymentTx.data,
salt
)) as string
);

// Sanity check
assert(
computedAddress === targetAddress || targetAddress === AddressZero,
"The computed address does not match the target address and the target address is not 0x0."
);

return {
address: computedAddress,
isDeployed: targetAddress === AddressZero,
};
};

export const deployMastercopyWithInitData = async (
hre: HardhatRuntimeEnvironment,
initCode: BytesLike,
salt: string
) => {
): Promise<string> => {
const singletonFactory = await getSingletonFactory(hre);

const targetAddress = await singletonFactory.callStatic.deploy(
initCode,
salt
// throws if this for some reason is not a valid address
const targetAddress = getAddress(
(await singletonFactory.callStatic.deploy(initCode, salt)) as string
);

const initCodeHash = await hre.ethers.utils.solidityKeccak256(
["bytes"],
[initCode]
);
const computedTargetAddress = await hre.ethers.utils.getCreate2Address(
const initCodeHash = keccak256(initCode);

const computedTargetAddress = getCreate2Address(
singletonFactory.address,
salt,
initCodeHash
);

if (targetAddress == "0x0000000000000000000000000000000000000000") {
if (targetAddress === AddressZero) {
console.log(
` ✔ Mastercopy already deployed to: ${computedTargetAddress}`
);
return;
return AddressZero;
}

// Sanity check
assert.equal(
targetAddress,
computedTargetAddress,
"The computed address does not match the target address."
);

let deployData;
switch (hre.network.name) {
case "optimism":
Expand Down
4 changes: 2 additions & 2 deletions src/factory/singletonFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { HardhatRuntimeEnvironment } from "hardhat/types";
const singletonFactoryAbi = [
"function deploy(bytes memory _initCode, bytes32 _salt) public returns (address payable createdContract)",
];
const singletonFactoryAddress = "0xce0042b868300000d44a59004da54a005ffdcf9f";
const SingletonFactoryAddress = "0xce0042b868300000d44a59004da54a005ffdcf9f";

/**
* Get the singleton factory contract (ERC-2470).
Expand All @@ -20,7 +20,7 @@ export const getSingletonFactory = async (

const singletonDeployer = "0xBb6e024b9cFFACB947A71991E386681B1Cd1477D";
const singletonFactory = new hardhat.ethers.Contract(
singletonFactoryAddress,
SingletonFactoryAddress,
singletonFactoryAbi,
deployer
);
Expand Down

0 comments on commit 0b67170

Please sign in to comment.