From 3e1879b8680f843ab5acf1d8ccf7f62616db3365 Mon Sep 17 00:00:00 2001 From: Asgeir Date: Wed, 9 Nov 2022 15:37:06 +0100 Subject: [PATCH 1/8] Add OpenZeppelin Governor for Goerli --- src/factory/constants.ts | 2 ++ src/factory/types.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/factory/constants.ts b/src/factory/constants.ts index beb36835..3744b730 100644 --- a/src/factory/constants.ts +++ b/src/factory/constants.ts @@ -29,6 +29,7 @@ const MasterCopyAddresses: Record = { [KnownContracts.ROLES]: "0x85388a8cd772b19a468F982Dc264C238856939C9", tellor: "", optimisticGovernor: "", + [KnownContracts.OZ_GOVERNOR]: "", }; export const CONTRACT_ADDRESSES: Record< @@ -51,6 +52,7 @@ export const CONTRACT_ADDRESSES: Record< ...MasterCopyAddresses, [KnownContracts.OPTIMISTIC_GOVERNOR]: "0x1340229DCF6e0bed7D9c2356929987C2A720F836", + [KnownContracts.OZ_GOVERNOR]: "0x119bAebEDCF2A32dFAc3868Cd2F446db744ab675", }, 56: { ...MasterCopyAddresses }, 100: { ...MasterCopyAddresses }, diff --git a/src/factory/types.ts b/src/factory/types.ts index df401fac..f3bbd416 100644 --- a/src/factory/types.ts +++ b/src/factory/types.ts @@ -13,6 +13,7 @@ export enum KnownContracts { SCOPE_GUARD = "scopeGuard", FACTORY = "factory", ROLES = "roles", + OZ_GOVERNOR = "ozGovernor", } type META_GUARD_VERSION = "v1.0.0"; From 36ec3010a94c82587879e296833cb202ef178df3 Mon Sep 17 00:00:00 2001 From: Asgeir Date: Wed, 9 Nov 2022 22:12:07 +0100 Subject: [PATCH 2/8] Export functionality for deploying mastercopies --- src/factory/mastercopy_deployment.ts | 47 ++++++++++++++++ src/factory/singleton-deployment.ts | 82 +++++++++++++++------------- 2 files changed, 90 insertions(+), 39 deletions(-) create mode 100644 src/factory/mastercopy_deployment.ts diff --git a/src/factory/mastercopy_deployment.ts b/src/factory/mastercopy_deployment.ts new file mode 100644 index 00000000..53990363 --- /dev/null +++ b/src/factory/mastercopy_deployment.ts @@ -0,0 +1,47 @@ +import { ContractFactory } from "ethers"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { getSingletonFactory } from "./singleton-deployment"; + +const salt = + "0xb0519c4c4b7945db302f69180b86f1a668153a476802c1c445fcb691ef23ef16"; + +/** + * + * @param hardhat Deploy a mastercopy via the singleton factory + * @param mastercopyContractFactory + * @param args + * @returns + */ +export const deployMastercopy = async ( + hardhat: HardhatRuntimeEnvironment, + mastercopyContractFactory: ContractFactory, + args: Array +) => { + const deploymentTx = await mastercopyContractFactory.getDeployTransaction( + ...args + ); + + console.log("initcode ready"); + + const singletonFactory = await getSingletonFactory(hardhat); + + const targetAddress = await singletonFactory.callStatic.deploy( + deploymentTx.data, + salt + ); + console.log("targetAddress", targetAddress); + + const deployData = await singletonFactory.deploy(deploymentTx.data, salt, { + gasLimit: 10000000, + }); + + const recept = await deployData.wait(); + console.log("recept", recept); + + if ((await hardhat.ethers.provider.getCode(targetAddress)).length > 2) { + console.log( + "Successfully deployed ModuleProxyFactory to target address! 🎉" + ); + } + return recept; +}; diff --git a/src/factory/singleton-deployment.ts b/src/factory/singleton-deployment.ts index ad7c733d..d2d13203 100644 --- a/src/factory/singleton-deployment.ts +++ b/src/factory/singleton-deployment.ts @@ -1,5 +1,6 @@ import "hardhat-deploy"; import "@nomiclabs/hardhat-ethers"; +import { Contract } from "ethers"; import { task } from "hardhat/config"; import { HardhatRuntimeEnvironment } from "hardhat/types"; @@ -12,44 +13,8 @@ const factorySalt = const AddressZero = "0x0000000000000000000000000000000000000000"; const deployFactory = async (_: null, hardhat: HardhatRuntimeEnvironment) => { - const [deployer] = await hardhat.ethers.getSigners(); - console.log("Deployer address: ", deployer.address); - - const singletonDeployer = "0xBb6e024b9cFFACB947A71991E386681B1Cd1477D"; - const singletonFactory = new hardhat.ethers.Contract( - singletonFactoryAddress, - singletonFactoryAbi, - deployer - ); - - // check if singleton factory is deployed. - if ( - (await hardhat.ethers.provider.getCode(singletonFactory.address)) === "0x" - ) { - // fund the singleton factory deployer account - await deployer.sendTransaction({ - to: singletonDeployer, - value: hardhat.ethers.utils.parseEther("0.0247"), - }); - - // deploy the singleton factory - await ( - await hardhat.ethers.provider.sendTransaction( - "0xf9016c8085174876e8008303c4d88080b90154608060405234801561001057600080fd5b50610134806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80634af63f0214602d575b600080fd5b60cf60048036036040811015604157600080fd5b810190602081018135640100000000811115605b57600080fd5b820183602082011115606c57600080fd5b80359060200191846001830284011164010000000083111715608d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550509135925060eb915050565b604080516001600160a01b039092168252519081900360200190f35b6000818351602085016000f5939250505056fea26469706673582212206b44f8a82cb6b156bfcc3dc6aadd6df4eefd204bc928a4397fd15dacf6d5320564736f6c634300060200331b83247000822470" - ) - ).wait(); - - if ( - (await hardhat.ethers.provider.getCode(singletonFactory.address)) == "0x" - ) { - console.log( - "Singleton factory could not be deployed to correct address, deployment haulted." - ); - return; - } - } + const singletonFactory = await getSingletonFactory(hardhat); console.log("Singleton Factory: ", singletonFactory.address); - const Factory = await hardhat.ethers.getContractFactory("ModuleProxyFactory"); // const singletonFactory = new hardhat.ethers.Contract(singletonFactoryAddress, singletonFactoryAbi) @@ -96,9 +61,48 @@ const deployFactory = async (_: null, hardhat: HardhatRuntimeEnvironment) => { } }; +export const getSingletonFactory = async ( + hardhat: HardhatRuntimeEnvironment +): Promise => { + const [deployer] = await hardhat.ethers.getSigners(); + console.log("Deployer address: ", deployer.address); + + const singletonDeployer = "0xBb6e024b9cFFACB947A71991E386681B1Cd1477D"; + const singletonFactory = new hardhat.ethers.Contract( + singletonFactoryAddress, + singletonFactoryAbi, + deployer + ); + + // check if singleton factory is deployed. + if ( + (await hardhat.ethers.provider.getCode(singletonFactory.address)) === "0x" + ) { + // fund the singleton factory deployer account + await deployer.sendTransaction({ + to: singletonDeployer, + value: hardhat.ethers.utils.parseEther("0.0247"), + }); + + // deploy the singleton factory + await ( + await hardhat.ethers.provider.sendTransaction( + "0xf9016c8085174876e8008303c4d88080b90154608060405234801561001057600080fd5b50610134806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80634af63f0214602d575b600080fd5b60cf60048036036040811015604157600080fd5b810190602081018135640100000000811115605b57600080fd5b820183602082011115606c57600080fd5b80359060200191846001830284011164010000000083111715608d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550509135925060eb915050565b604080516001600160a01b039092168252519081900360200190f35b6000818351602085016000f5939250505056fea26469706673582212206b44f8a82cb6b156bfcc3dc6aadd6df4eefd204bc928a4397fd15dacf6d5320564736f6c634300060200331b83247000822470" + ) + ).wait(); + + if ( + (await hardhat.ethers.provider.getCode(singletonFactory.address)) == "0x" + ) { + throw Error( + "Singleton factory could not be deployed to correct address, deployment haulted." + ); + } + } + return singletonFactory; +}; + task( "singleton-deployment", "Deploy factory through singleton factory" ).setAction(deployFactory); - -module.exports = {}; From 2f9665bf65529ae601e0d790ae1d9c2ad0b24b20 Mon Sep 17 00:00:00 2001 From: Asgeir Date: Wed, 9 Nov 2022 22:12:42 +0100 Subject: [PATCH 3/8] Update the OZGovernor mastercopy address --- src/factory/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/factory/constants.ts b/src/factory/constants.ts index 3744b730..ed90a85c 100644 --- a/src/factory/constants.ts +++ b/src/factory/constants.ts @@ -60,7 +60,7 @@ export const CONTRACT_ADDRESSES: Record< ...MasterCopyAddresses, [KnownContracts.TELLOR]: "0xEAB27A2Dc46431B96126f20bFC3197eD8247ed79", [KnownContracts.OPTIMISTIC_GOVERNOR]: - "0x923b1AfF7D67507A5Bdf528bD3086456FEba10cB", + "0x7E67f2D27B0193980F49DcCed96fE3Ac1Ce44641", }, 31337: { ...MasterCopyAddresses }, 80001: { From 9c939061a51c6e667e1eb6247d54a240971c022c Mon Sep 17 00:00:00 2001 From: Asgeir Date: Thu, 10 Nov 2022 08:47:22 +0100 Subject: [PATCH 4/8] Correct the addresses for OZ_GOVERNOR and OPTIMISTIC_GOVERNOR --- src/factory/constants.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/factory/constants.ts b/src/factory/constants.ts index ed90a85c..ff41d5f5 100644 --- a/src/factory/constants.ts +++ b/src/factory/constants.ts @@ -52,7 +52,7 @@ export const CONTRACT_ADDRESSES: Record< ...MasterCopyAddresses, [KnownContracts.OPTIMISTIC_GOVERNOR]: "0x1340229DCF6e0bed7D9c2356929987C2A720F836", - [KnownContracts.OZ_GOVERNOR]: "0x119bAebEDCF2A32dFAc3868Cd2F446db744ab675", + [KnownContracts.OZ_GOVERNOR]: "0x011Ad6A7FE4FB9226204dDBe2b6a5Fc109961dce", }, 56: { ...MasterCopyAddresses }, 100: { ...MasterCopyAddresses }, @@ -60,7 +60,7 @@ export const CONTRACT_ADDRESSES: Record< ...MasterCopyAddresses, [KnownContracts.TELLOR]: "0xEAB27A2Dc46431B96126f20bFC3197eD8247ed79", [KnownContracts.OPTIMISTIC_GOVERNOR]: - "0x7E67f2D27B0193980F49DcCed96fE3Ac1Ce44641", + "0x923b1AfF7D67507A5Bdf528bD3086456FEba10cB", }, 31337: { ...MasterCopyAddresses }, 80001: { From 9c303e7291433aa5e0b85113c50a646d80fa3c5d Mon Sep 17 00:00:00 2001 From: Asgeir Date: Thu, 10 Nov 2022 09:44:26 +0100 Subject: [PATCH 5/8] Add the missing OZ governor abi --- src/abi/oz_governor.ts | 688 +++++++++++++++++++++++++++++++++++++++ src/factory/constants.ts | 4 +- 2 files changed, 691 insertions(+), 1 deletion(-) create mode 100644 src/abi/oz_governor.ts diff --git a/src/abi/oz_governor.ts b/src/abi/oz_governor.ts new file mode 100644 index 00000000..a6ab6742 --- /dev/null +++ b/src/abi/oz_governor.ts @@ -0,0 +1,688 @@ +export default [ + { + inputs: [ + { internalType: "address", name: "_owner", type: "address" }, + { internalType: "address", name: "_target", type: "address" }, + { internalType: "address", name: "_multisend", type: "address" }, + { internalType: "address", name: "_token", type: "address" }, + { internalType: "string", name: "_name", type: "string" }, + { internalType: "uint256", name: "_votingDelay", type: "uint256" }, + { internalType: "uint256", name: "_votingPeriod", type: "uint256" }, + { + internalType: "uint256", + name: "_proposalThreshold", + type: "uint256", + }, + { internalType: "uint256", name: "_quorum", type: "uint256" }, + { + internalType: "uint64", + name: "_initialVoteExtension", + type: "uint64", + }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { inputs: [], name: "TransactionsFailed", type: "error" }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint64", + name: "oldVoteExtension", + type: "uint64", + }, + { + indexed: false, + internalType: "uint64", + name: "newVoteExtension", + type: "uint64", + }, + ], + name: "LateQuorumVoteExtensionSet", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "multisend", + type: "address", + }, + ], + name: "MultisendSet", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "target", + type: "address", + }, + ], + name: "OZGovernorModuleSetUp", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "previousOwner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "OwnershipTransferred", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "proposalId", + type: "uint256", + }, + ], + name: "ProposalCanceled", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "proposalId", + type: "uint256", + }, + { + indexed: false, + internalType: "address", + name: "proposer", + type: "address", + }, + { + indexed: false, + internalType: "address[]", + name: "targets", + type: "address[]", + }, + { + indexed: false, + internalType: "uint256[]", + name: "values", + type: "uint256[]", + }, + { + indexed: false, + internalType: "string[]", + name: "signatures", + type: "string[]", + }, + { + indexed: false, + internalType: "bytes[]", + name: "calldatas", + type: "bytes[]", + }, + { + indexed: false, + internalType: "uint256", + name: "startBlock", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "endBlock", + type: "uint256", + }, + { + indexed: false, + internalType: "string", + name: "description", + type: "string", + }, + ], + name: "ProposalCreated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "proposalId", + type: "uint256", + }, + ], + name: "ProposalExecuted", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "uint256", + name: "proposalId", + type: "uint256", + }, + { + indexed: false, + internalType: "uint64", + name: "extendedDeadline", + type: "uint64", + }, + ], + name: "ProposalExtended", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "oldProposalThreshold", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "newProposalThreshold", + type: "uint256", + }, + ], + name: "ProposalThresholdSet", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "oldQuorumNumerator", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "newQuorumNumerator", + type: "uint256", + }, + ], + name: "QuorumNumeratorUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "previousTarget", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "newTarget", + type: "address", + }, + ], + name: "TargetSet", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "voter", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "proposalId", + type: "uint256", + }, + { + indexed: false, + internalType: "uint8", + name: "support", + type: "uint8", + }, + { + indexed: false, + internalType: "uint256", + name: "weight", + type: "uint256", + }, + { + indexed: false, + internalType: "string", + name: "reason", + type: "string", + }, + ], + name: "VoteCast", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "oldVotingDelay", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "newVotingDelay", + type: "uint256", + }, + ], + name: "VotingDelaySet", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "oldVotingPeriod", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "newVotingPeriod", + type: "uint256", + }, + ], + name: "VotingPeriodSet", + type: "event", + }, + { + inputs: [], + name: "BALLOT_TYPEHASH", + outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "COUNTING_MODE", + outputs: [{ internalType: "string", name: "", type: "string" }], + stateMutability: "pure", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "proposalId", type: "uint256" }, + { internalType: "uint8", name: "support", type: "uint8" }, + ], + name: "castVote", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "proposalId", type: "uint256" }, + { internalType: "uint8", name: "support", type: "uint8" }, + { internalType: "uint8", name: "v", type: "uint8" }, + { internalType: "bytes32", name: "r", type: "bytes32" }, + { internalType: "bytes32", name: "s", type: "bytes32" }, + ], + name: "castVoteBySig", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "proposalId", type: "uint256" }, + { internalType: "uint8", name: "support", type: "uint8" }, + { internalType: "string", name: "reason", type: "string" }, + ], + name: "castVoteWithReason", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address[]", name: "targets", type: "address[]" }, + { internalType: "uint256[]", name: "values", type: "uint256[]" }, + { internalType: "bytes[]", name: "calldatas", type: "bytes[]" }, + { + internalType: "bytes32", + name: "descriptionHash", + type: "bytes32", + }, + ], + name: "execute", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "account", type: "address" }, + { internalType: "uint256", name: "blockNumber", type: "uint256" }, + ], + name: "getVotes", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "proposalId", type: "uint256" }, + { internalType: "address", name: "account", type: "address" }, + ], + name: "hasVoted", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address[]", name: "targets", type: "address[]" }, + { internalType: "uint256[]", name: "values", type: "uint256[]" }, + { internalType: "bytes[]", name: "calldatas", type: "bytes[]" }, + { + internalType: "bytes32", + name: "descriptionHash", + type: "bytes32", + }, + ], + name: "hashProposal", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "pure", + type: "function", + }, + { + inputs: [], + name: "lateQuorumVoteExtension", + outputs: [{ internalType: "uint64", name: "", type: "uint64" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "multisend", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "name", + outputs: [{ internalType: "string", name: "", type: "string" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "owner", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "proposalId", type: "uint256" }], + name: "proposalDeadline", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "proposalId", type: "uint256" }], + name: "proposalSnapshot", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "proposalThreshold", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "proposalId", type: "uint256" }], + name: "proposalVotes", + outputs: [ + { internalType: "uint256", name: "againstVotes", type: "uint256" }, + { internalType: "uint256", name: "forVotes", type: "uint256" }, + { internalType: "uint256", name: "abstainVotes", type: "uint256" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address[]", name: "targets", type: "address[]" }, + { internalType: "uint256[]", name: "values", type: "uint256[]" }, + { internalType: "bytes[]", name: "calldatas", type: "bytes[]" }, + { internalType: "string", name: "description", type: "string" }, + ], + name: "propose", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "blockNumber", type: "uint256" }], + name: "quorum", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "quorumDenominator", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "quorumNumerator", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "target", type: "address" }, + { internalType: "uint256", name: "value", type: "uint256" }, + { internalType: "bytes", name: "data", type: "bytes" }, + ], + name: "relay", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "uint64", name: "newVoteExtension", type: "uint64" }, + ], + name: "setLateQuorumVoteExtension", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "_multisend", type: "address" }], + name: "setMultisend", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "newProposalThreshold", + type: "uint256", + }, + ], + name: "setProposalThreshold", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "_target", type: "address" }], + name: "setTarget", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes", name: "initializeParams", type: "bytes" }, + ], + name: "setUp", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "newVotingDelay", type: "uint256" }, + ], + name: "setVotingDelay", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "newVotingPeriod", + type: "uint256", + }, + ], + name: "setVotingPeriod", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "proposalId", type: "uint256" }], + name: "state", + outputs: [ + { + internalType: "enum IGovernorUpgradeable.ProposalState", + name: "", + type: "uint8", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes4", name: "interfaceId", type: "bytes4" }], + name: "supportsInterface", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "target", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "token", + outputs: [ + { + internalType: "contract IVotesUpgradeable", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "_owner", type: "address" }], + name: "transferOwnership", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "newQuorumNumerator", + type: "uint256", + }, + ], + name: "updateQuorumNumerator", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "version", + outputs: [{ internalType: "string", name: "", type: "string" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "votingDelay", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "votingPeriod", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { stateMutability: "payable", type: "receive" }, +]; diff --git a/src/factory/constants.ts b/src/factory/constants.ts index ff41d5f5..35bfa071 100644 --- a/src/factory/constants.ts +++ b/src/factory/constants.ts @@ -1,3 +1,4 @@ +import OzGovernorAbi from "../abi/oz_governor"; import { KnownContracts } from "./types"; /* @@ -69,7 +70,7 @@ export const CONTRACT_ADDRESSES: Record< }, }; -export const CONTRACT_ABIS: Record = { +export const CONTRACT_ABIS: Record = { [KnownContracts.META_GUARD]: [ `function setUp(bytes memory initParams) public`, `function setAvatar(address _avatar) public`, @@ -266,4 +267,5 @@ export const CONTRACT_ABIS: Record = { "function transferOwnership(address newOwner)", "function unscopeParameter(uint16 role, address targetAddress, bytes4 functionSig, uint8 paramIndex)", ], + ozGovernor: OzGovernorAbi, }; From d914f8639f13ac8ae45273758fc576fdf396100c Mon Sep 17 00:00:00 2001 From: Asgeir Date: Thu, 10 Nov 2022 09:44:57 +0100 Subject: [PATCH 6/8] Refactoring - Make a clear distinction between the different factory forms: contract factory, singleton factory, and module factory. - Add function documentation. - Clean up other forms of unclear naming. --- hardhat.config.ts | 2 +- src/factory/deploy_module_factory.ts | 65 +++++++ src/factory/factory.ts | 135 ------------- src/factory/index.ts | 3 +- ...y_deployment.ts => mastercopy_deployer.ts} | 29 +-- src/factory/module_deployer.ts | 183 ++++++++++++++++++ ...ton-deployment.ts => singleton_factory.ts} | 69 +------ src/tasks/singleton-deployment.ts | 11 ++ 8 files changed, 287 insertions(+), 210 deletions(-) create mode 100644 src/factory/deploy_module_factory.ts delete mode 100644 src/factory/factory.ts rename src/factory/{mastercopy_deployment.ts => mastercopy_deployer.ts} (53%) create mode 100644 src/factory/module_deployer.ts rename src/factory/{singleton-deployment.ts => singleton_factory.ts} (54%) create mode 100644 src/tasks/singleton-deployment.ts diff --git a/hardhat.config.ts b/hardhat.config.ts index a27bf26e..062bd0d0 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -19,7 +19,7 @@ const { network } = yargs dotenv.config(); const { INFURA_KEY, MNEMONIC, ETHERSCAN_API_KEY, PK } = process.env; -import "./src/factory/singleton-deployment"; +import "./src/tasks/singleton-deployment"; const DEFAULT_MNEMONIC = "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat"; diff --git a/src/factory/deploy_module_factory.ts b/src/factory/deploy_module_factory.ts new file mode 100644 index 00000000..cd5e29f7 --- /dev/null +++ b/src/factory/deploy_module_factory.ts @@ -0,0 +1,65 @@ +import "hardhat-deploy"; +import "@nomiclabs/hardhat-ethers"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { getSingletonFactory } from "./singleton_factory"; + +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 + */ +export const deployModuleFactory = async (hre: HardhatRuntimeEnvironment) => { + 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 + ); + if (targetAddress == AddressZero) { + console.log( + "ModuleProxyFactory already deployed to target address on this network." + ); + return; + } else { + console.log("Target Factory Address:", targetAddress); + } + + const transactionResponse = await singletonFactory.deploy( + Factory.bytecode, + factorySalt + ); + + 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" + ); + + if ( + (await hre.ethers.provider.getCode(factory.address)) != + factoryArtifact.deployedBytecode + ) { + throw new Error( + "Deployment unsuccessful: deployed bytecode does not match." + ); + } else { + console.log( + "Successfully deployed ModuleProxyFactory to target address! 🎉" + ); + } + return targetAddress; +}; diff --git a/src/factory/factory.ts b/src/factory/factory.ts deleted file mode 100644 index 88578c1d..00000000 --- a/src/factory/factory.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { ethers, Contract, Signer, BigNumber } from "ethers"; -import { ABI } from "hardhat-deploy/dist/types"; - -import { CONTRACT_ADDRESSES, CONTRACT_ABIS } from "./constants"; -import { KnownContracts } from "./types"; - -export const deployAndSetUpModule = ( - contractName: KnownContracts, - args: { - types: Array; - values: Array; - }, - provider: ethers.providers.JsonRpcProvider, - chainId: number, - saltNonce: string -) => { - const { factory, module } = getFactoryAndMasterCopy( - contractName, - provider, - chainId - ); - return getDeployAndSetupTx(factory, module, args, saltNonce); -}; - -export const deployAndSetUpCustomModule = ( - masterCopyAddress: string, - abi: ABI, - setupArgs: { - types: Array; - values: Array; - }, - provider: ethers.providers.JsonRpcProvider, - chainId: number, - saltNonce: string -) => { - const chainContracts = CONTRACT_ADDRESSES[chainId]; - const factoryAddress = chainContracts.factory; - const factory = new Contract(factoryAddress, CONTRACT_ABIS.factory, provider); - const module = new Contract(masterCopyAddress, abi, provider); - - return getDeployAndSetupTx(factory, module, setupArgs, saltNonce); -}; - -const getDeployAndSetupTx = ( - factory: ethers.Contract, - module: ethers.Contract, - args: { - types: Array; - values: Array; - }, - saltNonce: string -) => { - const encodedInitParams = ethers.utils.defaultAbiCoder.encode( - args.types, - args.values - ); - const moduleSetupData = module.interface.encodeFunctionData("setUp", [ - encodedInitParams, - ]); - - const expectedModuleAddress = calculateProxyAddress( - factory, - module.address, - moduleSetupData, - saltNonce - ); - - const deployData = factory.interface.encodeFunctionData("deployModule", [ - module.address, - moduleSetupData, - saltNonce, - ]); - const transaction = { - data: deployData, - to: factory.address, - value: BigNumber.from(0), - }; - return { - transaction, - expectedModuleAddress, - }; -}; - -export const calculateProxyAddress = ( - factory: Contract, - masterCopy: string, - initData: string, - saltNonce: string -) => { - const masterCopyAddress = masterCopy.toLowerCase().replace(/^0x/, ""); - const byteCode = - "0x602d8060093d393df3363d3d373d3d3d363d73" + - masterCopyAddress + - "5af43d82803e903d91602b57fd5bf3"; - - const salt = ethers.utils.solidityKeccak256( - ["bytes32", "uint256"], - [ethers.utils.solidityKeccak256(["bytes"], [initData]), saltNonce] - ); - - return ethers.utils.getCreate2Address( - factory.address, - salt, - ethers.utils.keccak256(byteCode) - ); -}; - -export const getModuleInstance = ( - moduleName: KnownContracts, - address: string, - provider: ethers.providers.JsonRpcProvider | Signer -) => { - const moduleIsNotSupported = !Object.keys(CONTRACT_ABIS).includes(moduleName); - if (moduleIsNotSupported) { - throw new Error("Module " + moduleName + " not supported"); - } - return new Contract(address, CONTRACT_ABIS[moduleName], provider); -}; - -export const getFactoryAndMasterCopy = ( - moduleName: KnownContracts, - provider: ethers.providers.JsonRpcProvider, - chainId: number -) => { - const chainContracts = CONTRACT_ADDRESSES[chainId]; - const masterCopyAddress = chainContracts[moduleName]; - const factoryAddress = chainContracts.factory; - const module = getModuleInstance(moduleName, masterCopyAddress, provider); - const factory = new Contract(factoryAddress, CONTRACT_ABIS.factory, provider); - - return { - factory, - module, - }; -}; diff --git a/src/factory/index.ts b/src/factory/index.ts index fa1d97b9..abd87252 100644 --- a/src/factory/index.ts +++ b/src/factory/index.ts @@ -1,3 +1,4 @@ -export * from "./factory"; +export * from "./module_deployer"; +export * from "./mastercopy_deployer"; export * from "./types"; export * from "./constants"; diff --git a/src/factory/mastercopy_deployment.ts b/src/factory/mastercopy_deployer.ts similarity index 53% rename from src/factory/mastercopy_deployment.ts rename to src/factory/mastercopy_deployer.ts index 53990363..06fac676 100644 --- a/src/factory/mastercopy_deployment.ts +++ b/src/factory/mastercopy_deployer.ts @@ -1,34 +1,39 @@ import { ContractFactory } from "ethers"; import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { getSingletonFactory } from "./singleton-deployment"; +import { getSingletonFactory } from "./singleton_factory"; const salt = "0xb0519c4c4b7945db302f69180b86f1a668153a476802c1c445fcb691ef23ef16"; /** + * Deploy a module's mastercopy via the singleton factory. * - * @param hardhat Deploy a mastercopy via the singleton factory + * To get the same address on any chain. + * @param hre hardhat runtime environment * @param mastercopyContractFactory * @param args - * @returns + * @returns The address of the deployed module mastercopy */ export const deployMastercopy = async ( - hardhat: HardhatRuntimeEnvironment, + hre: HardhatRuntimeEnvironment, mastercopyContractFactory: ContractFactory, args: Array ) => { - const deploymentTx = await mastercopyContractFactory.getDeployTransaction( - ...args - ); - - console.log("initcode ready"); + const deploymentTx = mastercopyContractFactory.getDeployTransaction(...args); - const singletonFactory = await getSingletonFactory(hardhat); + const singletonFactory = await getSingletonFactory(hre); const targetAddress = await singletonFactory.callStatic.deploy( deploymentTx.data, salt ); + + if (targetAddress == "0x0000000000000000000000000000000000000000") { + throw new Error( + "Mastercopy already deployed to target address on this network." + ); + } + console.log("targetAddress", targetAddress); const deployData = await singletonFactory.deploy(deploymentTx.data, salt, { @@ -38,10 +43,10 @@ export const deployMastercopy = async ( const recept = await deployData.wait(); console.log("recept", recept); - if ((await hardhat.ethers.provider.getCode(targetAddress)).length > 2) { + if ((await hre.ethers.provider.getCode(targetAddress)).length > 2) { console.log( "Successfully deployed ModuleProxyFactory to target address! 🎉" ); } - return recept; + return targetAddress; }; diff --git a/src/factory/module_deployer.ts b/src/factory/module_deployer.ts new file mode 100644 index 00000000..3ecb1ec3 --- /dev/null +++ b/src/factory/module_deployer.ts @@ -0,0 +1,183 @@ +import { ethers, Contract, Signer, BigNumber } from "ethers"; +import { ABI } from "hardhat-deploy/dist/types"; + +import { CONTRACT_ADDRESSES, CONTRACT_ABIS } from "./constants"; +import { KnownContracts } from "./types"; + +/** + * Get the transaction for deploying a module proxy through the module factory. + * This will also initialize the module proxy by calling the setup function. + * + * @param contractName Name of the module to deploy (must be present in `KnownContracts`) + * @param setupArgs The arguments for the setup function of the module + * @param provider + * @param chainId + * @param saltNonce + * @returns the transaction and the expected address of the module proxy + */ +export const deployAndSetUpModule = ( + contractName: KnownContracts, + setupArgs: { + types: Array; + values: Array; + }, + provider: ethers.providers.JsonRpcProvider, + chainId: number, + saltNonce: string +) => { + const { moduleFactory, moduleMastercopy } = getModuleFactoryAndMasterCopy( + contractName, + provider, + chainId + ); + return getDeployAndSetupTx( + moduleFactory, + moduleMastercopy, + setupArgs, + saltNonce + ); +}; + +/** + * Get the transaction for deploying a module proxy through the module 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` + * @param masterCopyAddress address of the mastercopy to use + * @param abi abi of the module + * @param setupArgs The arguments for the setup function of the module + * @param provider + * @param chainId + * @param saltNonce + * @returns the transaction and the expected address of the module proxy + */ +export const deployAndSetUpCustomModule = ( + masterCopyAddress: string, + abi: ABI, + setupArgs: { + types: Array; + values: Array; + }, + provider: ethers.providers.JsonRpcProvider, + chainId: number, + saltNonce: string +) => { + const chainContracts = CONTRACT_ADDRESSES[chainId]; + const moduleFactoryAddress = chainContracts.factory; + const moduleFactory = new Contract( + moduleFactoryAddress, + CONTRACT_ABIS.factory, + provider + ); + const moduleMastercopy = new Contract(masterCopyAddress, abi, provider); + + return getDeployAndSetupTx( + moduleFactory, + moduleMastercopy, + setupArgs, + saltNonce + ); +}; + +const getDeployAndSetupTx = ( + moduleFactory: ethers.Contract, + moduleMastercopy: ethers.Contract, + setupArgs: { + types: Array; + values: Array; + }, + saltNonce: string +) => { + const encodedInitParams = ethers.utils.defaultAbiCoder.encode( + setupArgs.types, + setupArgs.values + ); + const moduleSetupData = moduleMastercopy.interface.encodeFunctionData( + "setUp", + [encodedInitParams] + ); + + const expectedModuleAddress = calculateProxyAddress( + moduleFactory, + moduleMastercopy.address, + moduleSetupData, + saltNonce + ); + + const deployData = moduleFactory.interface.encodeFunctionData( + "deployModule", + [moduleMastercopy.address, moduleSetupData, saltNonce] + ); + const transaction = { + data: deployData, + to: moduleFactory.address, + value: BigNumber.from(0), + }; + return { + transaction, + expectedModuleAddress, + }; +}; + +export const calculateProxyAddress = ( + moduleFactory: Contract, + mastercopyAddress: string, + initData: string, + saltNonce: string +) => { + const mastercopyAddressFormatted = mastercopyAddress + .toLowerCase() + .replace(/^0x/, ""); + const byteCode = + "0x602d8060093d393df3363d3d373d3d3d363d73" + + mastercopyAddressFormatted + + "5af43d82803e903d91602b57fd5bf3"; + + const salt = ethers.utils.solidityKeccak256( + ["bytes32", "uint256"], + [ethers.utils.solidityKeccak256(["bytes"], [initData]), saltNonce] + ); + + return ethers.utils.getCreate2Address( + moduleFactory.address, + salt, + ethers.utils.keccak256(byteCode) + ); +}; + +export const getModuleInstance = ( + moduleName: KnownContracts, + moduleAddress: string, + provider: ethers.providers.JsonRpcProvider | Signer +) => { + const moduleIsNotSupported = !Object.keys(CONTRACT_ABIS).includes(moduleName); + if (moduleIsNotSupported) { + throw new Error("Module " + moduleName + " not supported"); + } + return new Contract(moduleAddress, CONTRACT_ABIS[moduleName], provider); +}; + +export const getModuleFactoryAndMasterCopy = ( + moduleName: KnownContracts, + provider: ethers.providers.JsonRpcProvider, + chainId: number +) => { + const chainContracts = CONTRACT_ADDRESSES[chainId]; + const masterCopyAddress = chainContracts[moduleName]; + const factoryAddress = chainContracts.factory; + const moduleMastercopy = getModuleInstance( + moduleName, + masterCopyAddress, + provider + ); + const moduleFactory = new Contract( + factoryAddress, + CONTRACT_ABIS.factory, + provider + ); + + return { + moduleFactory, + moduleMastercopy, + }; +}; diff --git a/src/factory/singleton-deployment.ts b/src/factory/singleton_factory.ts similarity index 54% rename from src/factory/singleton-deployment.ts rename to src/factory/singleton_factory.ts index d2d13203..e3477cb2 100644 --- a/src/factory/singleton-deployment.ts +++ b/src/factory/singleton_factory.ts @@ -1,66 +1,18 @@ -import "hardhat-deploy"; -import "@nomiclabs/hardhat-ethers"; import { Contract } from "ethers"; -import { task } from "hardhat/config"; import { HardhatRuntimeEnvironment } from "hardhat/types"; - const singletonFactoryAbi = [ "function deploy(bytes memory _initCode, bytes32 _salt) public returns (address payable createdContract)", ]; const singletonFactoryAddress = "0xce0042b868300000d44a59004da54a005ffdcf9f"; -const factorySalt = - "0xb0519c4c4b7945db302f69180b86f1a668153a476802c1c445fcb691ef23ef16"; -const AddressZero = "0x0000000000000000000000000000000000000000"; - -const deployFactory = async (_: null, hardhat: HardhatRuntimeEnvironment) => { - const singletonFactory = await getSingletonFactory(hardhat); - console.log("Singleton Factory: ", singletonFactory.address); - const Factory = await hardhat.ethers.getContractFactory("ModuleProxyFactory"); - // const singletonFactory = new hardhat.ethers.Contract(singletonFactoryAddress, singletonFactoryAbi) - - const targetAddress = await singletonFactory.callStatic.deploy( - Factory.bytecode, - factorySalt - ); - if (targetAddress == AddressZero) { - console.log( - "ModuleProxyFactory already deployed to target address on this network." - ); - return; - } else { - console.log("Target Factory Address:", targetAddress); - } - - const transactionResponse = await singletonFactory.deploy( - Factory.bytecode, - factorySalt - ); - - const result = await transactionResponse.wait(); - console.log("Deploy transaction: ", result.transactionHash); - - const factory = await hardhat.ethers.getContractAt( - "ModuleProxyFactory", - targetAddress - ); - - const factoryArtifact = await hardhat.artifacts.readArtifact( - "ModuleProxyFactory" - ); - - if ( - (await hardhat.ethers.provider.getCode(factory.address)) != - factoryArtifact.deployedBytecode - ) { - console.log("Deployment unsuccessful: deployed bytecode does not match."); - return; - } else { - console.log( - "Successfully deployed ModuleProxyFactory to target address! 🎉" - ); - } -}; +/** + * Get the singleton factory contract (ERC-2470). + * If it is not deployed on the newtwork, then also deploy it. + * + * https://eips.ethereum.org/EIPS/eip-2470 + * @param hardhat + * @returns Singleton Factory contract + */ export const getSingletonFactory = async ( hardhat: HardhatRuntimeEnvironment ): Promise => { @@ -101,8 +53,3 @@ export const getSingletonFactory = async ( } return singletonFactory; }; - -task( - "singleton-deployment", - "Deploy factory through singleton factory" -).setAction(deployFactory); diff --git a/src/tasks/singleton-deployment.ts b/src/tasks/singleton-deployment.ts new file mode 100644 index 00000000..ec49b392 --- /dev/null +++ b/src/tasks/singleton-deployment.ts @@ -0,0 +1,11 @@ +import { task } from "hardhat/config"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { deployModuleFactory } from "../factory/deploy_module_factory"; + +export const deploy = async (_: null, hre: HardhatRuntimeEnvironment) => + deployModuleFactory(hre); + +task( + "singleton-deployment", + "Deploy factory through singleton factory" +).setAction(deploy); From b3ae579073fcae2e93e5ade1947afa49c3d18a5a Mon Sep 17 00:00:00 2001 From: Asgeir Date: Thu, 10 Nov 2022 10:03:51 +0100 Subject: [PATCH 7/8] Update naming --- src/factory/module_deployer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/factory/module_deployer.ts b/src/factory/module_deployer.ts index bf3aa860..254e2ef2 100644 --- a/src/factory/module_deployer.ts +++ b/src/factory/module_deployer.ts @@ -21,7 +21,7 @@ type TxAndExpectedAddress = { * Get the transaction for deploying a module proxy through the module factory. * This will also initialize the module proxy by calling the setup function. * - * @param contractName Name of the module to deploy (must be present in `KnownContracts`) + * @param moduleName Name of the module to deploy (must be present in `KnownContracts`) * @param setupArgs The arguments for the setup function of the module * @param provider * @param chainId @@ -29,7 +29,7 @@ type TxAndExpectedAddress = { * @returns the transaction and the expected address of the module proxy */ export const deployAndSetUpModule = ( - contractName: KnownContracts, + moduleName: KnownContracts, setupArgs: { types: Array; values: Array; @@ -39,7 +39,7 @@ export const deployAndSetUpModule = ( saltNonce: string ): TxAndExpectedAddress => { const { moduleFactory, moduleMastercopy } = getModuleFactoryAndMasterCopy( - contractName, + moduleName, provider, chainId ); From cfe73c7d423252b781551a93d991d74243d7e9d7 Mon Sep 17 00:00:00 2001 From: Asgeir Date: Thu, 10 Nov 2022 10:08:32 +0100 Subject: [PATCH 8/8] Update documentation (readme) --- src/factory/README.md | 28 +++++++++++++++------------- src/factory/module_deployer.ts | 6 +++--- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/factory/README.md b/src/factory/README.md index dd1ef784..681a7590 100644 --- a/src/factory/README.md +++ b/src/factory/README.md @@ -12,15 +12,15 @@ You can check the factory file to see more details, it consists of 5 methods, de This method is used to deploy contracts listed in `./constants.ts`. -- Interface: `deployAndSetUpModule(moduleName, args, provider, chainId, salt)` +- Interface: `deployAndSetUpModule(moduleName, setupArgs, provider, chainId, salt)` - 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 - - `args`: An object with two attributes: `value` and `types` + - `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 `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 + - `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 @@ -38,15 +38,16 @@ This method is used to deploy contracts listed in `./constants.ts`. This method is similar to `deployAndSetUpModule`, however, it deals with the deployment of contracts that is NOT listed in `./constants.ts`. -- Interface: `deployAndSetUpCustomModule(masterCopyAddress, abi, args, provider, chainId)` +- Interface: `deployAndSetUpCustomModule(mastercopyAddress, abi, setupArgs, provider, chainId, saltNonce)` - Arguments: - - `masterCopyAddress`: The address of the module to be deployed + - `mastercopyAddress`: The address of the module to be deployed - `abi`: The ABI of the module to be deployed - - `args`: An object with two attributes: `value` and `types` + - `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 `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 + - `saltNonce`: Some salt to use for the deployment - 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 @@ -64,22 +65,23 @@ This method is similar to `deployAndSetUpModule`, however, it deals with the dep This method is used to calculate the resulting address of a deployed module given the provided parameters. It is useful for building multisend transactions that both deploy a module and then make calls to that module or calls referencing the module's address. -- Interface: `calculateProxyAddress(factory, masterCopy, initData)` +- Interface: `calculateProxyAddress(moduleFactory, mastercopyAddress, initData, saltNonce)` - Arguments: - - `factory`: Factory contract object of the Module Proxy Factory contract - - `masterCopy`: Address of the Master Copy of the Module + - `moduleFactory`: Module factory contract object of the Module Proxy Factory contract + - `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 - Returns: A string with the expected address ### 4. Get Module This method returns an instance of a given module. -- Interface: `getModuleInstance(moduleName, address, provider)` +- Interface: `getModuleInstance(moduleName, moduleAddress, provider)` - 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 - - `address`: Address of the Module contract + - `moduleAddress`: Address of the Module contract - `provider`: Ethereum provider, expects an instance of `JsonRpcProvider` from `ethers` - Returns: A Contract instance of the Module @@ -97,8 +99,8 @@ This method returns an object with the an instance of the factory contract and t ```json { - "factory": Contract, - "module": Contract, + "moduleFactory": Contract, + "moduleMastercopy": Contract, } ``` diff --git a/src/factory/module_deployer.ts b/src/factory/module_deployer.ts index 254e2ef2..503b07f6 100644 --- a/src/factory/module_deployer.ts +++ b/src/factory/module_deployer.ts @@ -56,7 +56,7 @@ export const deployAndSetUpModule = ( * 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` - * @param masterCopyAddress address of the mastercopy to use + * @param mastercopyAddress address of the mastercopy to use * @param abi abi of the module * @param setupArgs The arguments for the setup function of the module * @param provider @@ -65,7 +65,7 @@ export const deployAndSetUpModule = ( * @returns the transaction and the expected address of the module proxy */ export const deployAndSetUpCustomModule = ( - masterCopyAddress: string, + mastercopyAddress: string, abi: ABI, setupArgs: { types: Array; @@ -82,7 +82,7 @@ export const deployAndSetUpCustomModule = ( CONTRACT_ABIS.factory, provider ); - const moduleMastercopy = new Contract(masterCopyAddress, abi, provider); + const moduleMastercopy = new Contract(mastercopyAddress, abi, provider); return getDeployAndSetupTx( moduleFactory,