1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import * as path from 'path'
import * as fs from 'fs'
import { exec } from 'child_process'
import { promisify } from 'util'
import * as mkdirp from 'mkdirp'
import { ethers } from 'ethers'
import { task } from 'hardhat/config'
import { remove0x } from '@eth-optimism/core-utils'
import '@eth-optimism/hardhat-deploy-config'
import { predeploys } from '../src/predeploys'
import { getContractFromArtifact } from '../src/deploy-utils'
import { names } from '../src/address-names'
task('take-dump').setAction(async ({}, hre) => {
/* eslint-disable @typescript-eslint/no-var-requires */
// Needs to be imported here or hardhat will throw a fit about hardhat being imported from
// within the configuration file.
const {
computeStorageSlots,
getStorageLayout,
} = require('@defi-wonderland/smock/dist/src/utils')
// Needs to be imported here because the artifacts can only be generated after the contracts have
// been compiled, but compiling the contracts will import the config file which, as a result,
// will import this file.
const { getContractArtifact } = require('../src/contract-artifacts')
/* eslint-enable @typescript-eslint/no-var-requires */
// Basic warning so users know that the whitelist will be disabled if the owner is the zero address.
if (
hre.deployConfig.ovmWhitelistOwner === undefined ||
hre.deployConfig.ovmWhitelistOwner === ethers.constants.AddressZero
) {
console.log(
'WARNING: whitelist owner is undefined or address(0), whitelist will be disabled'
)
}
const variables = {
OVM_DeployerWhitelist: {
owner: hre.deployConfig.ovmWhitelistOwner,
},
OVM_GasPriceOracle: {
_owner: hre.deployConfig.ovmGasPriceOracleOwner,
gasPrice: hre.deployConfig.gasPriceOracleL2GasPrice,
l1BaseFee: hre.deployConfig.gasPriceOracleL1BaseFee,
overhead: hre.deployConfig.gasPriceOracleOverhead,
scalar: hre.deployConfig.gasPriceOracleScalar,
decimals: hre.deployConfig.gasPriceOracleDecimals,
},
L2StandardBridge: {
l1TokenBridge: (
await getContractFromArtifact(
hre,
names.managed.contracts.Proxy__OVM_L1StandardBridge
)
).address,
messenger: predeploys.L2CrossDomainMessenger,
},
OVM_SequencerFeeVault: {
l1FeeWallet: hre.deployConfig.ovmFeeWalletAddress,
},
OVM_ETH: {
l2Bridge: predeploys.L2StandardBridge,
l1Token: ethers.constants.AddressZero,
_name: 'Ether',
_symbol: 'ETH',
},
L2CrossDomainMessenger: {
// We default the xDomainMsgSender to this value to save gas.
// See usage of this default in the L2CrossDomainMessenger contract.
xDomainMsgSender: '0x000000000000000000000000000000000000dEaD',
l1CrossDomainMessenger: (
await getContractFromArtifact(
hre,
names.managed.contracts.Proxy__OVM_L1CrossDomainMessenger
)
).address,
// Set the messageNonce to a high value to avoid overwriting old sent messages.
messageNonce: 100000,
},
WETH9: {
name: 'Wrapped Ether',
symbol: 'WETH',
decimals: 18,
},
}
const dump = {}
for (const predeployName of Object.keys(predeploys)) {
const predeployAddress = predeploys[predeployName]
dump[predeployAddress] = {
balance: '00',
storage: {},
}
if (predeployName === 'OVM_L1BlockNumber') {
// OVM_L1BlockNumber is a special case where we just inject a specific bytecode string.
// We do this because it uses the custom L1BLOCKNUMBER opcode (0x4B) which cannot be
// directly used in Solidity (yet). This bytecode string simply executes the 0x4B opcode
// and returns the address given by that opcode.
dump[predeployAddress].code = '0x4B60005260206000F3'
} else {
const artifact = getContractArtifact(predeployName)
dump[predeployAddress].code = artifact.deployedBytecode
}
// Compute and set the required storage slots for each contract that needs it.
if (predeployName in variables) {
const storageLayout = await getStorageLayout(predeployName)
const slots = computeStorageSlots(storageLayout, variables[predeployName])
for (const slot of slots) {
dump[predeployAddress].storage[slot.key] = slot.val
}
}
}
// Grab the commit hash so we can stick it in the genesis file.
let commit: string
try {
const { stdout } = await promisify(exec)('git rev-parse HEAD')
commit = stdout.replace('\n', '')
} catch {
console.log('unable to get commit hash, using empty hash instead')
commit = '0000000000000000000000000000000000000000'
}
const genesis = {
commit,
config: {
chainId: hre.deployConfig.l2ChainId,
homesteadBlock: 0,
eip150Block: 0,
eip155Block: 0,
eip158Block: 0,
byzantiumBlock: 0,
constantinopleBlock: 0,
petersburgBlock: 0,
istanbulBlock: 0,
muirGlacierBlock: 0,
berlinBlock: hre.deployConfig.hfBerlinBlock,
clique: {
period: 0,
epoch: 30000,
},
},
difficulty: '1',
gasLimit: hre.deployConfig.l2BlockGasLimit.toString(10),
extradata:
'0x' +
'00'.repeat(32) +
remove0x(hre.deployConfig.ovmBlockSignerAddress) +
'00'.repeat(65),
alloc: dump,
}
// Make sure the output location exists
const outdir = path.resolve(__dirname, '../genesis')
const outfile = path.join(outdir, `${hre.network.name}.json`)
mkdirp.sync(outdir)
// Write the genesis file
fs.writeFileSync(outfile, JSON.stringify(genesis, null, 4))
})