Skip to content

Commit

Permalink
Merge pull request #103 from gnosis/better-logs_remove-remaining-depe…
Browse files Browse the repository at this point in the history
…ndencies-on-ModuleProxyFactory

Improve logging + Remove Module Proxy Factory artifact dependencies
  • Loading branch information
asgeir-s authored Jan 3, 2023
2 parents 4387adc + 7b42c17 commit 40c4137
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 38 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gnosis.pm/zodiac",
"version": "2.1.1",
"version": "2.1.2",
"description": "Zodiac is a composable design philosophy and collection of standards for building DAO ecosystem tooling.",
"author": "Auryn Macmillan <[email protected]>",
"license": "LGPL-3.0+",
Expand Down
23 changes: 15 additions & 8 deletions src/factory/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
# Module Factory
# Module Proxy Factory

The purpose of the Module Proxy Factory is to make the deployment of Zodiac Modules easier. Applying the Minimal Proxy Pattern, this factory reduces the gas cost of deployment and simplifies the tracking of deployed modules. The Minimal Proxy Pattern has been used because the modules do not need to be upgradeable, since a safe can cheaply deploy a module if needed.

It's worth mentioning that it costs roughly 5k additional gas for each transaction when using a proxy.
Thus, after a certain number of transactions (~700) it would likely be cheaper to deploy the module from the constructor rather than the proxy.

There's also a JS API, allowing the developers to interact with the ProxyFactory Contract more easily.
You can check the factory file to see more details, it consists of 5 methods, described individually in the following sections:
There's also a JS API, allowing the developers to easily:

- Deploy the Module Proxy Factory (and the Singleton Factory if it's not already deployed to the current chain). See `src/factory/deployModuleFactory.ts`.
- Deploy Module Mastercopys via the Singleton Factory. See `src/factory/mastercopyDeployer.ts`.
- Deploy Module minimal proxies (Clones) via the Module Proxy Factory. See `src/factory/moduleDeployer.ts`.

The deployments can either be for your own custom modules or any of the already created Zodiac modules listed in `src/factory/contracts.ts`.

Description of the module deployment functionality:

### 1. Deploy and set up a known module

Expand All @@ -16,11 +23,11 @@ This method is used to deploy contracts listed in `./constants.ts`.
- Arguments:
- `moduleName`: Name of the module to be deployed, note that it needs to exist as a key in the [CONTRACT_ADDRESSES](./constants.ts#L3-L12) object
- `setupArgs`: An object with two attributes: `value` and `types`
- In `value` it expects an array of the arguments of the `setUp` function of the module to deploy
- In `values` it expects an array of the arguments of the `setUp` function of the module to deploy
- In `types` it expects an array of the types of every value
- `provider`: Ethereum provider, expects an instance of `JsonRpcProvider` from `ethers`
- `chainId`: Number of network to interact with
- `salt`: For the Create2 op code
- `saltNonce`: Salt for the Create2 op code
- Returns: An object with the transaction built in order to be executed by the Safe, and the expected address of the new module, this will allow developers to batch the transaction of deployment + enable module on safe. Example:

```json
Expand All @@ -43,7 +50,7 @@ This method is similar to `deployAndSetUpModule`, however, it deals with the dep
- `mastercopyAddress`: The address of the module to be deployed
- `abi`: The ABI of the module to be deployed
- `setupArgs`: An object with two attributes: `value` and `types`
- In `value` it expects an array of the arguments of the `setUp` function of the module to deploy
- In `values` it expects an array of the arguments of the `setUp` function of the module to deploy
- In `types` it expects an array of the types of every value
- `provider`: Ethereum provider, expects an instance of `JsonRpcProvider` from `ethers`
- `chainId`: Number of network to interact with
Expand All @@ -67,7 +74,7 @@ This method is used to calculate the resulting address of a deployed module give

- Interface: `calculateProxyAddress(moduleFactory, mastercopyAddress, initData, saltNonce)`
- Arguments:
- `moduleFactory`: Module factory contract object of the Module Proxy Factory contract
- `moduleFactory`: The Module Proxy Factory contract object
- `mastercopyAddress`: Address of the Master Copy of the Module
- `initData`: Encoded function data that is used to set up the module
- `saltNonce`: Some salt to use for the deployment
Expand Down Expand Up @@ -109,4 +116,4 @@ This method returns an object with the an instance of the factory contract and t
We use a deterministic deployment to deploy the factory and mastercopies of each module, so that they can be deployed with the same address on supported networks. You can check which networks are supported in the
[constants file](./constants.ts#L14-L22)

The [Singleton Factory](https://eips.ethereum.org/EIPS/eip-2470) is used to deploy the Module Factory. If it is not already deployed at the correct address on your network, the [`singleton-deployment` script file](./singleton-deployment.ts) will attempt to deploy it before deploying the Module Proxy Factory. If the Singleton Factory cannot be deployed to the correct address, the script will fail.
The [Singleton Factory](https://eips.ethereum.org/EIPS/eip-2470) is used to deploy the Module Proxy Factory. If it is not already deployed at the correct address on your network, the [`singleton-deployment` script file](./singleton-deployment.ts) will attempt to deploy it before deploying the Module Proxy Factory. If the Singleton Factory cannot be deployed to the correct address, the script will fail.
38 changes: 17 additions & 21 deletions src/factory/deployModuleFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,27 @@ const FactoryInitCode = MasterCopyInitData[KnownContracts.FACTORY].initCode;
const FactorySalt = MasterCopyInitData[KnownContracts.FACTORY].salt;

/**
* Deploy a module factory via the singleton factory.
* Deploy the Module Proxy 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, or the zero address if it was already deployed
* @returns The address of the deployed Module Proxy Factory, or the zero address if it was already deployed
*/
export const deployModuleFactory = async (
hre: HardhatRuntimeEnvironment
): Promise<string> => {
console.log("Deploying the Module Proxy Factory...");
const singletonFactory = await getSingletonFactory(hre);
console.log(" Singleton Factory: ", singletonFactory.address);
console.log(
" Singleton factory used for deployment:",
singletonFactory.address
);

try {
const Factory = await hre.ethers.getContractFactory("ModuleProxyFactory");
if (Factory.bytecode !== FactoryInitCode) {
console.warn(
"The compiled ModuleProxyFactory (from src/factory/contracts.ts) is outdated, it does " +
" The compiled Module Proxy Factory (from src/factory/contracts.ts) is outdated, it does " +
"not match the bytecode stored at MasterCopyInitData[KnownContracts.FACTORY].initCode"
);
}
Expand All @@ -42,12 +46,12 @@ export const deployModuleFactory = async (
);
if (targetAddress === AddressZero) {
console.log(
" ModuleProxyFactory already deployed to target address on this network."
" ✔ Module Proxy Factory already deployed to target address on this network."
);
return AddressZero;
}

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

const transactionResponse = await singletonFactory.deploy(
FactoryInitCode,
Expand All @@ -56,27 +60,19 @@ export const deployModuleFactory = async (
);

const result = await transactionResponse.wait();
console.log(" Deploy transaction: ", result.transactionHash);

const factory = await hre.ethers.getContractAt(
"ModuleProxyFactory",
targetAddress
);

const factoryArtifact = await hre.artifacts.readArtifact(
"ModuleProxyFactory"
console.log(
" Deploy transaction hash: ",
result.transactionHash
);

if (
(await hre.ethers.provider.getCode(factory.address)) !==
factoryArtifact.deployedBytecode
) {
if ((await hre.ethers.provider.getCode(targetAddress)).length < 3) {
// will return "0x" when there is no code
throw new Error(
" Deployment unsuccessful: deployed bytecode does not match."
" \x1B[31m✘ Deployment unsuccessful: No code at target address.\x1B[0m"
);
} else {
console.log(
" Successfully deployed ModuleProxyFactory to target address! 🎉"
` \x1B[32m✔ Successfully deployed the Module Proxy Factory to: ${targetAddress}\x1B[0m 🎉`
);
}
return targetAddress;
Expand Down
8 changes: 3 additions & 5 deletions src/factory/mastercopyDeployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,7 @@ export const deployMastercopyWithInitData = async (
);

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

Expand Down Expand Up @@ -151,10 +149,10 @@ export const deployMastercopyWithInitData = async (

if ((await hre.ethers.provider.getCode(targetAddress)).length > 2) {
console.log(
` \x1B[32m✔ Mastercopy deployed to:\x1B[0m ${targetAddress}`
` \x1B[32m✔ Mastercopy deployed to: ${targetAddress} 🎉\x1B[0m `
);
} else {
console.log(" \x1B[31m✘ Deployment failed.\x1B[0m");
console.log(" \x1B[31m✘ Deployment failed.\x1B[0m");
}
return targetAddress;
};
4 changes: 2 additions & 2 deletions src/factory/moduleDeployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type TxAndExpectedAddress = {
};

/**
* Get the transaction for deploying a module proxy through the module factory.
* Get the transaction for deploying a module proxy through the Module Proxy Factory.
* This will also initialize the module proxy by calling the setup function.
*
* @param moduleName Name of the module to deploy (must be present in `KnownContracts`)
Expand Down Expand Up @@ -54,7 +54,7 @@ export const deployAndSetUpModule = (
};

/**
* Get the transaction for deploying a module proxy through the module factory.
* Get the transaction for deploying a module proxy through the Module Proxy Factory.
* This will also initialize the module proxy by calling the setup function.
*
* This method is for modules that do not have a mastercopy listed in the `KnownContracts`
Expand Down
4 changes: 3 additions & 1 deletion src/factory/singletonFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ export const getSingletonFactory = async (
"Singleton factory could not be deployed to correct address, deployment haulted."
);
}
console.log("Singleton factory deployed to " + singletonFactory.address);
console.log(
` \x1B[32m✔ Singleton factory deployed to: ${singletonFactory.address} 🎉\x1B[0m`
);
}
return singletonFactory;
};

0 comments on commit 40c4137

Please sign in to comment.