Skip to content

Commit

Permalink
Merge pull request #7 from gnosisguild/bump-dependencies
Browse files Browse the repository at this point in the history
Bump-dependencies
  • Loading branch information
auryn-macmillan authored Jul 17, 2024
2 parents 649adef + db37958 commit a68ca70
Show file tree
Hide file tree
Showing 13 changed files with 2,029 additions and 6,331 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
node-version: 18
- uses: actions/cache@v2
with:
path: "**/node_modules"
Expand Down
4 changes: 2 additions & 2 deletions contracts/MyModule.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.9;
pragma solidity >=0.7.0 <0.9.0;

import "@gnosis.pm/zodiac/contracts/core/Module.sol";

Expand All @@ -14,7 +14,7 @@ contract MyModule is Module {
/// @dev Initialize function, will be triggered when a new proxy is deployed
/// @param initializeParams Parameters of initialization encoded
function setUp(bytes memory initializeParams) public override initializer {
__Ownable_init();
__Ownable_init(msg.sender);
(address _owner, address _button) = abi.decode(initializeParams, (address, address));

button = _button;
Expand Down
2 changes: 2 additions & 0 deletions contracts/test/Button.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pragma solidity ^0.8.9;
import "@openzeppelin/contracts/access/Ownable.sol";

contract Button is Ownable {
constructor() Ownable(msg.sender) {}

event ButtonPushed(address pusher, uint256 pushes);

uint256 public pushes;
Expand Down
17 changes: 17 additions & 0 deletions contracts/test/IModuleProxyFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: LGPL-3.0-only
// !! THIS FILE WAS AUTOGENERATED BY abi-to-sol v0.8.0. !!
pragma solidity ^0.8.4;

interface IModuleProxyFactory {
error FailedInitialization();
error TakenAddress(address address_);
error TargetHasNoCode(address target);
error ZeroAddress(address target);
event ModuleProxyCreation(address indexed proxy, address indexed masterCopy);

function deployModule(
address masterCopy,
bytes memory initializer,
uint256 saltNonce
) external returns (address proxy);
}
44 changes: 14 additions & 30 deletions deploy/01_mastercopy_module.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,27 @@
import { ethers } from "hardhat"
import "hardhat-deploy"
import { ZeroHash } from "ethers"
import { DeployFunction } from "hardhat-deploy/types"
import { HardhatRuntimeEnvironment } from "hardhat/types"
import { computeTargetAddress, deployMastercopy } from "@gnosis.pm/zodiac"
import { createFactory, deployViaFactory } from "../factories/eip2470"

import MODULE_CONTRACT_ARTIFACT from "../artifacts/contracts/MyModule.sol/MyModule.json"

const FirstAddress = "0x0000000000000000000000000000000000000001"
const Salt = "0x0000000000000000000000000000000000000000000000000000000000000000"

const deploy: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const contract = await ethers.getContractFactory("MyModule")

let address = await deployMastercopy(
hre,
contract,
[
FirstAddress, // owner
FirstAddress, // button
],
Salt,
)

if (address === ethers.constants.AddressZero) {
// the mastercopy was already deployed
const target = await computeTargetAddress(
hre,
contract,
[
FirstAddress, // owner
FirstAddress, // button
],
Salt,
)
address = target.address
}
const { getNamedAccounts, ethers } = hre
const { deployer: deployerAddress } = await getNamedAccounts()
const deployer = await ethers.getSigner(deployerAddress)

await createFactory(deployer)

const MyModule = await ethers.getContractFactory("MyModule")
const tx = await MyModule.getDeployTransaction(FirstAddress, FirstAddress)

const mastercopy = await deployViaFactory({ bytecode: tx.data, salt: ZeroHash }, deployer)

hre.deployments.save("MyModuleMastercopy", {
abi: MODULE_CONTRACT_ARTIFACT.abi,
address,
address: mastercopy,
})
}

Expand Down
15 changes: 5 additions & 10 deletions deploy/02_test_dependencies.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,25 @@
import "hardhat-deploy"
import { DeployFunction } from "hardhat-deploy/types"
import { HardhatRuntimeEnvironment } from "hardhat/types"
import { deployModuleFactory } from "@gnosis.pm/zodiac/dist/src/factory/deployModuleFactory"

const deploy: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
console.log("Deploying 'external' dependencies (Button and Avatar)")
const { deployments, getNamedAccounts, ethers } = hre
const { deploy } = deployments
const { deployer } = await getNamedAccounts()

// Deploys the ModuleFactory (and the Singleton factory) if it is not already deployed
await deployModuleFactory(hre)
const { deployer: deployerAddress } = await getNamedAccounts()
const deployer = await ethers.getSigner(deployerAddress)

const testAvatarDeployment = await deploy("TestAvatar", {
from: deployer,
from: deployerAddress,
})
console.log("TestAvatar deployed to:", testAvatarDeployment.address)

const buttonDeployment = await deploy("Button", {
from: deployer,
from: deployerAddress,
})
console.log("Button deployed to:", buttonDeployment.address)

// Make the TestAvatar the owner of the button
const dependenciesDeployerSigner = await ethers.getSigner(deployer)
const buttonContract = await ethers.getContractAt("Button", buttonDeployment.address, dependenciesDeployerSigner)
const buttonContract = await ethers.getContractAt("Button", buttonDeployment.address, deployer)
const currentOwner = await buttonContract.owner()
if (currentOwner !== testAvatarDeployment.address) {
const tx = await buttonContract.transferOwnership(testAvatarDeployment.address)
Expand Down
41 changes: 21 additions & 20 deletions deploy/03_proxy_module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import "hardhat-deploy"
import { ZeroHash } from "ethers"
import { DeployFunction } from "hardhat-deploy/types"
import { HardhatRuntimeEnvironment } from "hardhat/types"
import { deployAndSetUpCustomModule, ContractAddresses, KnownContracts, SupportedNetworks } from "@gnosis.pm/zodiac"

import { createFactory, deployModAsProxy } from "../factories/moduleProxyFactory"

import MODULE_CONTRACT_ARTIFACT from "../artifacts/contracts/MyModule.sol/MyModule.json"

const deploy: DeployFunction = async function ({
Expand All @@ -11,38 +13,37 @@ const deploy: DeployFunction = async function ({
getChainId,
}: HardhatRuntimeEnvironment) {
console.log("Deploying MyModule Proxy")
const { deployer } = await getNamedAccounts()
const deployerSigner = await ethers.getSigner(deployer)
const { deployer: deployerAddress } = await getNamedAccounts()
const deployer = await ethers.getSigner(deployerAddress)

const buttonDeployment = await deployments.get("Button")
const testAvatarDeployment = await deployments.get("TestAvatar")

const myModuleMastercopyDeployment = await deployments.get("MyModuleMastercopy")

const chainId = await getChainId()
const network: SupportedNetworks = Number(chainId)

if ((await ethers.provider.getCode(ContractAddresses[network][KnownContracts.FACTORY])) === "0x") {
// the Module Factory should already be deployed to all supported chains
// if you are deploying to a chain where its not deployed yet (most likely locale test chains), run deployModuleFactory from the zodiac package
throw Error("The Module Factory is not deployed on this network. Please deploy it first.")
}
/// const chainId = await getChainId()
// const network: SupportedNetworks = Number(chainId)
// if ((await ethers.provider.getCode(ContractAddresses[network][KnownContracts.FACTORY])) === "0x") {
// // the Module Factory should already be deployed to all supported chains
// // if you are deploying to a chain where its not deployed yet (most likely locale test chains), run deployModuleFactory from the zodiac package
// throw Error("The Module Factory is not deployed on this network. Please deploy it first.")
// }

console.log("buttonDeployment.address:", buttonDeployment.address)

const { transaction } = deployAndSetUpCustomModule(
// Deploys the ModuleFactory (and the Singleton factory) if it is not already deployed
const factory = await createFactory(deployer)
const { transaction } = await deployModAsProxy(
factory,
myModuleMastercopyDeployment.address,
myModuleMastercopyDeployment.abi,
{
values: [testAvatarDeployment.address, buttonDeployment.address],
types: ["address", "address"],
},
ethers.provider,
Number(chainId),
Date.now().toString(),
ZeroHash,
)
const deploymentTransaction = await deployerSigner.sendTransaction(transaction)
const receipt = await deploymentTransaction.wait()
const deploymentTransaction = await deployer.sendTransaction(transaction)
const receipt = (await deploymentTransaction.wait())!
const myModuleProxyAddress = receipt.logs[1].address
console.log("MyModule minimal proxy deployed to:", myModuleProxyAddress)

Expand All @@ -52,7 +53,7 @@ const deploy: DeployFunction = async function ({
})

// Enable MyModule as a module on the safe to give it access to the safe's execTransactionFromModule() function
const testAvatarContract = await ethers.getContractAt("TestAvatar", testAvatarDeployment.address, deployerSigner)
const testAvatarContract = await ethers.getContractAt("TestAvatar", testAvatarDeployment.address, deployer)
const currentActiveModule = await testAvatarContract.module()
if (currentActiveModule !== myModuleProxyAddress) {
const tx = await testAvatarContract.enableModule(myModuleProxyAddress)
Expand Down
56 changes: 56 additions & 0 deletions factories/eip2470.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"

import { Contract, Signer, ZeroHash, getCreate2Address, keccak256, parseEther } from "ethers"

export async function deployViaFactory(
{ bytecode, salt = ZeroHash }: { bytecode: string; salt?: string },
deployer: Signer,
displayName?: string,
): Promise<string> {
const provider = deployer.provider!

const factory = new Contract(
factoryInfo.address,
["function deploy(bytes memory _initCode, bytes32 _salt) public returns (address payable createdContract)"],
deployer,
)

const computedAddress = getCreate2Address(factoryInfo.address, salt, keccak256(bytecode))

if ((await provider.getCode(computedAddress)) == "0x") {
const receipt = await (await factory.deploy(bytecode, salt, { gasLimit: 10000000 })).wait()

if (receipt?.status != 1) {
throw new Error(`Couldn't deploy ${displayName || "contract"} eip-2470 factory`)
}
}

return computedAddress
}

const factoryInfo = {
address: "0xce0042b868300000d44a59004da54a005ffdcf9f",
deployer: "0xBb6e024b9cFFACB947A71991E386681B1Cd1477D",
transaction:
"0xf9016c8085174876e8008303c4d88080b90154608060405234801561001057600080fd5b50610134806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80634af63f0214602d575b600080fd5b60cf60048036036040811015604157600080fd5b810190602081018135640100000000811115605b57600080fd5b820183602082011115606c57600080fd5b80359060200191846001830284011164010000000083111715608d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550509135925060eb915050565b604080516001600160a01b039092168252519081900360200190f35b6000818351602085016000f5939250505056fea26469706673582212206b44f8a82cb6b156bfcc3dc6aadd6df4eefd204bc928a4397fd15dacf6d5320564736f6c634300060200331b83247000822470",
}

/**
* If it is not deployed on the network, deploys the singleton factory contract
*
* https://eips.ethereum.org/EIPS/eip-2470
*/
export async function createFactory(signer: SignerWithAddress) {
const { address, deployer } = factoryInfo

// fund the singleton factory deployer account
await signer.sendTransaction({
to: deployer,
value: parseEther("0.0247"),
})

// deploy the singleton factory
await signer.provider.broadcastTransaction(factoryInfo.transaction)

return address
}
58 changes: 58 additions & 0 deletions factories/moduleProxyFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { AbiCoder, getCreate2Address, keccak256, solidityPackedKeccak256 } from "ethers"
import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"

import { deployViaFactory } from "./eip2470"

import { IModuleProxyFactory__factory, Module__factory } from "../typechain-types"

export function deployModAsProxy(
factory: string,
mastercopy: string,
setupArgs: {
types: Array<string>
values: Array<any>
},
saltNonce: string,
) {
function predictProxyAddress(initData: string): string {
const mastercopyAddressFormatted = mastercopy.toLowerCase().replace(/^0x/, "")
const byteCode =
"0x602d8060093d393df3363d3d373d3d3d363d73" + mastercopyAddressFormatted + "5af43d82803e903d91602b57fd5bf3"

const salt = solidityPackedKeccak256(
["bytes32", "uint256"],
[solidityPackedKeccak256(["bytes"], [initData]), saltNonce],
)

return getCreate2Address(factory, salt, keccak256(byteCode))
}

const abiCoder = new AbiCoder()

const encodedInitParams = abiCoder.encode(setupArgs.types, setupArgs.values)
const moduleSetupData = Module__factory.createInterface().encodeFunctionData("setUp", [encodedInitParams])

const deployData = IModuleProxyFactory__factory.createInterface().encodeFunctionData("deployModule", [
mastercopy,
moduleSetupData,
saltNonce,
])
const transaction = {
data: deployData,
to: factory,
value: 0,
}
return {
transaction,
expectedModuleAddress: predictProxyAddress(moduleSetupData),
}
}

export async function createFactory(signer: SignerWithAddress) {
const address = await deployViaFactory({ bytecode: creationBytecode, salt }, signer)
return address
}

const creationBytecode =
"0x608060405234801561001057600080fd5b506107fe806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f1ab873c14610030575b600080fd5b61004a600480360381019061004591906103b6565b610060565b6040516100579190610564565b60405180910390f35b600061009b848480519060200120846040516020016100809291906104f0565b604051602081830303815290604052805190602001206101a3565b905060008173ffffffffffffffffffffffffffffffffffffffff16846040516100c4919061051c565b6000604051808303816000865af19150503d8060008114610101576040519150601f19603f3d011682016040523d82523d6000602084013e610106565b606091505b5050905080610141576040517f7dabd39900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f2150ada912bf189ed721c44211199e270903fc88008c2a1e1e889ef30fe67c5f60405160405180910390a3509392505050565b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561021657826040517fc80b883400000000000000000000000000000000000000000000000000000000815260040161020d9190610564565b60405180910390fd5b60008373ffffffffffffffffffffffffffffffffffffffff163b141561027357826040517ffff2336100000000000000000000000000000000000000000000000000000000815260040161026a9190610564565b60405180910390fd5b6000836040516020016102869190610533565b6040516020818303038152906040529050828151602083016000f59150600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561031557816040517fdc7a7a2400000000000000000000000000000000000000000000000000000000815260040161030c9190610564565b60405180910390fd5b5092915050565b600061032f61032a846105a4565b61057f565b90508281526020810184848401111561034b5761034a61071b565b5b61035684828561063c565b509392505050565b60008135905061036d8161079a565b92915050565b600082601f83011261038857610387610716565b5b813561039884826020860161031c565b91505092915050565b6000813590506103b0816107b1565b92915050565b6000806000606084860312156103cf576103ce610725565b5b60006103dd8682870161035e565b935050602084013567ffffffffffffffff8111156103fe576103fd610720565b5b61040a86828701610373565b925050604061041b868287016103a1565b9150509250925092565b61042e816105f6565b82525050565b610445610440826105f6565b6106af565b82525050565b61045c61045782610608565b6106c1565b82525050565b600061046d826105d5565b61047781856105e0565b935061048781856020860161064b565b80840191505092915050565b60006104a0600f836105eb565b91506104ab82610748565b600f82019050919050565b60006104c36013836105eb565b91506104ce82610771565b601382019050919050565b6104ea6104e582610632565b6106dd565b82525050565b60006104fc828561044b565b60208201915061050c82846104d9565b6020820191508190509392505050565b60006105288284610462565b915081905092915050565b600061053e826104b6565b915061054a8284610434565b60148201915061055982610493565b915081905092915050565b60006020820190506105796000830184610425565b92915050565b600061058961059a565b9050610595828261067e565b919050565b6000604051905090565b600067ffffffffffffffff8211156105bf576105be6106e7565b5b6105c88261072a565b9050602081019050919050565b600081519050919050565b600081905092915050565b600081905092915050565b600061060182610612565b9050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b82818337600083830152505050565b60005b8381101561066957808201518184015260208101905061064e565b83811115610678576000848401525b50505050565b6106878261072a565b810181811067ffffffffffffffff821117156106a6576106a56106e7565b5b80604052505050565b60006106ba826106cb565b9050919050565b6000819050919050565b60006106d68261073b565b9050919050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b60008160601b9050919050565b7f5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000600082015250565b7f602d8060093d393df3363d3d373d3d3d363d7300000000000000000000000000600082015250565b6107a3816105f6565b81146107ae57600080fd5b50565b6107ba81610632565b81146107c557600080fd5b5056fea264697066735822122068cfe87f5242e3087c0b46218f6f8e6b1a5960ed6b562cb03d27ec764881916064736f6c63430008060033"
const salt = "0xb8db55294bb8fea809aee38a26aad7ce1ab40d019486aa51af3a3de7e7434530"
10 changes: 4 additions & 6 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import * as dotenv from "dotenv"
import { HardhatUserConfig } from "hardhat/config"
import "@nomiclabs/hardhat-etherscan"
import "@nomiclabs/hardhat-ethers"
import "@nomiclabs/hardhat-waffle"
import "@typechain/hardhat"
import { HardhatUserConfig } from "hardhat/types"

import "@nomicfoundation/hardhat-toolbox"
import "hardhat-gas-reporter"
import "solidity-coverage"
import "hardhat-deploy"

dotenv.config()

const config: HardhatUserConfig = {
solidity: "0.8.9",
solidity: "0.8.22",
networks: {
goerli: {
url: process.env.GOERLI_URL || "",
Expand Down
Loading

0 comments on commit a68ca70

Please sign in to comment.