Skip to content

Commit

Permalink
Add deploy and verify scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
cristovaoth committed Nov 2, 2023
1 parent 708760c commit eb71272
Show file tree
Hide file tree
Showing 10 changed files with 227 additions and 206 deletions.
48 changes: 10 additions & 38 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import '@nomicfoundation/hardhat-toolbox'
import '@nomicfoundation/hardhat-verify'
import 'hardhat-gas-reporter'
import 'hardhat-deploy'

import './src/deploy/verify'

import dotenv from 'dotenv'
import { HttpNetworkUserConfig } from 'hardhat/types'
import { DeterministicDeploymentInfo } from 'hardhat-deploy/dist/types'
import { getSingletonFactoryInfo } from '@safe-global/safe-singleton-factory'

import './tasks/deploy-instance'
import './tasks/deploy-mastercopy'

dotenv.config()

Expand All @@ -25,11 +24,16 @@ export default {
paths: {
artifacts: 'build/artifacts',
cache: 'build/cache',
deploy: 'src/deploy',
sources: 'contracts',
},
solidity: {
compilers: [{ version: '0.8.20' }],
settings: {
optimizer: {
enabled: true,
runs: 100,
},
},
},
defaultNetwork: 'hardhat',
networks: {
Expand All @@ -46,10 +50,6 @@ export default {
...sharedNetworkConfig,
url: 'https://rpc.gnosischain.com',
},
ewc: {
...sharedNetworkConfig,
url: `https://rpc.energyweb.org`,
},
goerli: {
...sharedNetworkConfig,
url: `https://goerli.infura.io/v3/${INFURA_KEY}`,
Expand All @@ -74,10 +74,6 @@ export default {
...sharedNetworkConfig,
url: `https://arb1.arbitrum.io/rpc`,
},
fantomTestnet: {
...sharedNetworkConfig,
url: `https://rpc.testnet.fantom.network/`,
},
avalanche: {
...sharedNetworkConfig,
url: `https://api.avax.network/ext/bc/C/rpc`,
Expand All @@ -91,34 +87,10 @@ export default {
url: 'https://rpc-mainnet.maticvigil.com',
},
},
deterministicDeployment,
namedAccounts: {
deployer: 0,
},
etherscan: {
apiKey: ETHERSCAN_API_KEY,
},
gasReporter: {
enabled: true,
},
}

function deterministicDeployment(network: string): DeterministicDeploymentInfo {
const info = getSingletonFactoryInfo(parseInt(network))
if (!info) {
throw new Error(`
Safe factory not found for network ${network}. You can request a new deployment at https://github.com/safe-global/safe-singleton-factory.
For more information, see https://github.com/safe-global/safe-contracts#replay-protection-eip-155
`)
}

const gasLimit = BigInt(info.gasLimit)
const gasPrice = BigInt(info.gasPrice)

return {
factory: info.address,
deployer: info.signerAddress,
funding: String(gasLimit * gasPrice),
signedTx: info.transaction,
}
}
11 changes: 5 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
"build": "hardhat compile",
"test": "hardhat test",
"coverage": "hardhat coverage",
"deploy": "yarn hardhat deploy-verify --network",
"deploy": "yarn hardhat deploy:mastercopy --network",
"prepack": "yarn run clean && yarn run build",
"prepare": "yarn run clean && yarn run build",
"lint": "yarn lint:sol && yarn lint:ts",
"lint:sol": "solhint 'contracts/**/*.sol'",
"lint:ts": "eslint 'src/**/*.ts' 'test/**/*.ts' --max-warnings 0 --fix",
"lint:ts": "eslint 'tasks/**/*.ts' 'test/**/*.ts' --max-warnings 0 --fix",
"fmt": "yarn fmt:sol && yarn fmt:ts",
"fmt:sol": "prettier 'contracts/**/*.sol' -w",
"fmt:ts": "prettier 'src/**/*.ts' 'test/**/*.ts' -w"
Expand All @@ -32,7 +32,7 @@
"@nomicfoundation/hardhat-ethers": "^3.0.4",
"@nomicfoundation/hardhat-network-helpers": "^1.0.7",
"@nomicfoundation/hardhat-toolbox": "3.0.0",
"@nomicfoundation/hardhat-verify": "^1.1.1",
"@nomicfoundation/hardhat-verify": "2.0.0",
"@openzeppelin/contracts": "^5.0.0",
"@safe-global/safe-singleton-factory": "^1.0.15",
"@typechain/ethers-v6": "^0.5.0",
Expand All @@ -48,10 +48,9 @@
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-prettier": "^5.0.0",
"eslint": "^8.6.0",
"ethers": "^6.7.1",
"hardhat-deploy": "^0.11.37",
"ethers": "^6.8.1",
"hardhat-gas-reporter": "^1.0.9",
"hardhat": "^2.12.5",
"hardhat": "^2.19.0",
"prettier-plugin-solidity": "^1.1.3",
"prettier": "^3.0.2",
"rimraf": "^5.0.1",
Expand Down
21 changes: 0 additions & 21 deletions src/deploy/deploy_module.ts

This file was deleted.

32 changes: 0 additions & 32 deletions src/deploy/verify.ts

This file was deleted.

90 changes: 90 additions & 0 deletions tasks/EIP_2470.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import assert from 'assert'

import {
Contract,
getCreate2Address,
keccak256,
parseEther,
Signer,
} from 'ethers'

export async function deployViaFactory(
creationBytecode: string,
salt: string,
deployer: Signer
): Promise<string> {
await maybeDeployFactory(deployer)

const provider = deployer.provider
assert(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(creationBytecode)
)

if ((await provider.getCode(computedAddress)) != '0x') {
console.log(`✔ Mastercopy already deployed to: ${computedAddress}`)
return computedAddress
}

const receipt = await (
await factory.deploy(creationBytecode, salt, { gasLimit: 10000000 })
).wait()

if (receipt?.status == 1) {
console.log(
`\x1B[32m✔ Mastercopy deployed to: ${computedAddress} 🎉\x1B[0m `
)
} else {
console.log('\x1B[31m✘ Mastercopy deployment failed.\x1B[0m')
}

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
*/
async function maybeDeployFactory(signer: Signer) {
const { provider } = signer
assert(provider)

// check if singleton factory is deployed.
if ((await provider.getCode(factoryInfo.address)) === '0x') {
// fund the singleton factory deployer account
await signer.sendTransaction({
to: factoryInfo.deployer,
value: parseEther('0.0247'),
})

// deploy the singleton factory
const receipt = await (
await provider.broadcastTransaction(factoryInfo.transaction)
).wait()

if (receipt?.status != 1) {
throw Error(
'EIP2470 SingletonFactory could not be deployed to correct address, deployment haulted.'
)
}
}
}
58 changes: 58 additions & 0 deletions tasks/deploy-instance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { task, types } from 'hardhat/config'

task('deploy:instance', 'Deploys a Delay modifier')
.addParam('owner', 'Address of the owner', undefined, types.string)
.addParam(
'avatar',
'Address of the avatar (e.g. Safe)',
undefined,
types.string
)
.addParam('target', 'Address of the target', undefined, types.string)
.addParam(
'cooldown',
'Cooldown in seconds that should be required after a oracle provided answer',
24 * 3600,
types.int,
true
)
.addParam(
'expiration',
'Time duration in seconds for which a positive answer is valid. After this time the answer is expired',
7 * 24 * 3600,
types.int,
true
)
.setAction(async (taskArgs, hre) => {
const [deployer] = await hre.ethers.getSigners()

const Delay = await hre.ethers.getContractFactory('Delay')
const delay = await (
await Delay.connect(deployer).deploy(
taskArgs.owner,
taskArgs.avatar,
taskArgs.target,
taskArgs.cooldown,
taskArgs.expiration
)
).waitForDeployment()

const address = await delay.getAddress()

console.log(`\x1B[32m✔ Instance deployed to: ${address} 🎉\x1B[0m `)

console.log('Waiting 1 minute before etherscan verification start...')
// Etherscan needs some time to process before trying to verify.
await new Promise((resolve) => setTimeout(resolve, 60000))

await hre.run('verify:verify', {
address: address,
constructorArguments: [
taskArgs.owner,
taskArgs.avatar,
taskArgs.target,
taskArgs.cooldown,
taskArgs.expiration,
],
})
})
38 changes: 38 additions & 0 deletions tasks/deploy-mastercopy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { AbiCoder, ZeroHash } from 'ethers'
import { task } from 'hardhat/config'

import { deployViaFactory } from './EIP_2470'

const AddressOne = '0x0000000000000000000000000000000000000001'

task('deploy:mastercopy', 'Deploys and verifies Delay mastercopy').setAction(
async (_, hre) => {
const [deployer] = await hre.ethers.getSigners()

const Delay = await hre.ethers.getContractFactory('Delay')

const args = AbiCoder.defaultAbiCoder().encode(
['address', 'address', 'address', 'uint256', 'uint256'],
[AddressOne, AddressOne, AddressOne, 0, 0]
)

const creationBytecode = `${Delay.bytecode}${args.substring(2)}`
const salt = ZeroHash
const address = await deployViaFactory(creationBytecode, salt, deployer)

if (hre.network.name == 'hardhat') {
return
}

console.log('Waiting 1 minute before etherscan verification start...')
// Etherscan needs some time to process before trying to verify.
await new Promise((resolve) => setTimeout(resolve, 60000))

await hre.run('verify:verify', {
address,
constructorArguments: [AddressOne, AddressOne, AddressOne, 0, 0],
})
}
)

export {}
Loading

0 comments on commit eb71272

Please sign in to comment.