Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return the address when deploying a mastercopy #98

Merged
merged 3 commits into from
Dec 22, 2022
Merged
Changes from 1 commit
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
90 changes: 77 additions & 13 deletions src/factory/mastercopyDeployer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BytesLike, ContractFactory } from "ethers";
import { BytesLike, ContractFactory, utils } from "ethers";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { getSingletonFactory } from "./singletonFactory";

Expand All @@ -7,51 +7,115 @@ import { getSingletonFactory } from "./singletonFactory";
*
* 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 undefined if it was already deployed
*/
export const deployMastercopy = async (
hre: HardhatRuntimeEnvironment,
mastercopyContractFactory: ContractFactory,
args: Array<any>,
salt: string
) => {
): Promise<string | undefined> => {
const deploymentTx = mastercopyContractFactory.getDeployTransaction(...args);
asgeir-s marked this conversation as resolved.
Show resolved Hide resolved

if (deploymentTx.data) {
await deployMastercopyWithInitData(hre, deploymentTx.data, salt);
return await deployMastercopyWithInitData(hre, deploymentTx.data, salt);
}
throw new Error("No deployment data found");
};
asgeir-s marked this conversation as resolved.
Show resolved Hide resolved

/**
* Compute a module's mastercopy address. Where it is or will be 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,
asgeir-s marked this conversation as resolved.
Show resolved Hide resolved
mastercopyContractFactory: ContractFactory,
args: Array<any>,
salt: string
): Promise<{ address: string; isDeployed: boolean }> => {
const deploymentTx = mastercopyContractFactory.getDeployTransaction(...args);
const singletonFactory = await getSingletonFactory(hre);

if (!deploymentTx.data) {
throw new Error("No deployment data found");
asgeir-s marked this conversation as resolved.
Show resolved Hide resolved
}

const initCodeHash = hre.ethers.utils.solidityKeccak256(
asgeir-s marked this conversation as resolved.
Show resolved Hide resolved
["bytes"],
[deploymentTx.data]
);
const computedAddress = hre.ethers.utils.getCreate2Address(
singletonFactory.address,
salt,
initCodeHash
);

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

// Sanity check
if (
computedAddress !== targetAddress &&
targetAddress !== "0x0000000000000000000000000000000000000000"
) {
asgeir-s marked this conversation as resolved.
Show resolved Hide resolved
throw new Error(
"The computed address does not match the target address and the target address is not 0x0."
);
}

return {
address: computedAddress,
isDeployed: targetAddress === "0x0000000000000000000000000000000000000000",
};
asgeir-s marked this conversation as resolved.
Show resolved Hide resolved
};

export const deployMastercopyWithInitData = async (
hre: HardhatRuntimeEnvironment,
initCode: BytesLike,
salt: string
) => {
): Promise<string | undefined> => {
const singletonFactory = await getSingletonFactory(hre);
asgeir-s marked this conversation as resolved.
Show resolved Hide resolved

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

const initCodeHash = await hre.ethers.utils.solidityKeccak256(
const initCodeHash = hre.ethers.utils.solidityKeccak256(
["bytes"],
asgeir-s marked this conversation as resolved.
Show resolved Hide resolved
[initCode]
);
const computedTargetAddress = await hre.ethers.utils.getCreate2Address(
const computedTargetAddress = hre.ethers.utils.getCreate2Address(
singletonFactory.address,
salt,
initCodeHash
);

if (targetAddress == "0x0000000000000000000000000000000000000000") {
if (targetAddress === "0x0000000000000000000000000000000000000000") {
console.log(
asgeir-s marked this conversation as resolved.
Show resolved Hide resolved
` ✔ Mastercopy already deployed to: ${computedTargetAddress}`
);
return;
}

// Sanity check
if (computedTargetAddress !== targetAddress) {
throw new Error("The computed address does not match the target address.");
asgeir-s marked this conversation as resolved.
Show resolved Hide resolved
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not know this assert variation.

Usually I import from "assert"

And then simply do assert(a === b)

let deployData;
switch (hre.network.name) {
case "optimism":
Expand Down