Commit da669074 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

op-chain-ops,op-node,devnet: Deploy contracts into L1 genesis (#3333)

parent 58975283
...@@ -221,7 +221,7 @@ jobs: ...@@ -221,7 +221,7 @@ jobs:
name: Check if we should run name: Check if we should run
command: | command: |
shopt -s inherit_errexit shopt -s inherit_errexit
CHANGED=$(check-changed "op-(batcher|bindings|e2e|node|proposer)") CHANGED=$(check-changed "op-(batcher|bindings|e2e|node|proposer|chain-ops)")
if [[ "$CHANGED" = "FALSE" ]]; then if [[ "$CHANGED" = "FALSE" ]]; then
circleci step halt circleci step halt
fi fi
...@@ -255,6 +255,11 @@ jobs: ...@@ -255,6 +255,11 @@ jobs:
command: | command: |
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell ./... golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell ./...
working_directory: op-service working_directory: op-service
- run:
name: lint op-chain-ops
command: |
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell ./...
working_directory: op-chain-ops
- run: - run:
name: prep results dir name: prep results dir
command: mkdir -p /test-results command: mkdir -p /test-results
...@@ -283,6 +288,11 @@ jobs: ...@@ -283,6 +288,11 @@ jobs:
command: | command: |
gotestsum --junitfile /test-results/op-service.xml -- -coverpkg=github.com/ethereum-optimism/optimism/... -coverprofile=coverage.out -covermode=atomic ./... gotestsum --junitfile /test-results/op-service.xml -- -coverpkg=github.com/ethereum-optimism/optimism/... -coverprofile=coverage.out -covermode=atomic ./...
working_directory: op-service working_directory: op-service
- run:
name: test op-chain-ops
command: |
gotestsum --junitfile /test-results/op-chain-ops.xml -- -coverpkg=github.com/ethereum-optimism/optimism/... -coverprofile=coverage.out -covermode=atomic ./...
working_directory: op-chain-ops
- store_test_results: - store_test_results:
path: /test-results path: /test-results
- run: - run:
...@@ -497,19 +507,6 @@ jobs: ...@@ -497,19 +507,6 @@ jobs:
name: Bring up the stack name: Bring up the stack
command: | command: |
make devnet-up make devnet-up
- run:
name: Check L2 Config
command: npx hardhat check-l2-config --network devnetL1
working_directory: packages/contracts-bedrock
- run:
name: Do a deposit
command: |
timeout 5m npx hardhat deposit \
--to 0xB79f76EF2c5F0286176833E7B2eEe103b1CC3244 \
--amount-eth 1 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
--network devnetL1
working_directory: packages/contracts-bedrock
- run: - run:
name: Deposit ERC20 through the bridge name: Deposit ERC20 through the bridge
command: timeout 5m npx hardhat deposit-erc20 --network devnetL1 command: timeout 5m npx hardhat deposit-erc20 --network devnetL1
...@@ -809,6 +806,17 @@ workflows: ...@@ -809,6 +806,17 @@ workflows:
- gcr - gcr
requires: requires:
- contracts-bedrock-tests - contracts-bedrock-tests
- docker-publish:
name: contract-artifacts-bedrock-publish-dev
docker_file: ops/docker/Dockerfile.packages
docker_tags: us-central1-docker.pkg.dev/bedrock-goerli-development/images/contract-artifacts-bedrock:<<pipeline.git.revision>>
target: contract-artifacts-bedrock
docker_context: .
repo: us-central1-docker.pkg.dev
context:
- gcr
requires:
- contracts-bedrock-tests
- hive-test: - hive-test:
name: hive-test-rpc name: hive-test-rpc
version: <<pipeline.git.revision>> version: <<pipeline.git.revision>>
......
...@@ -126,10 +126,12 @@ tag-bedrock-go-modules: ...@@ -126,10 +126,12 @@ tag-bedrock-go-modules:
git tag "op-bindings/$(VERSION)" git tag "op-bindings/$(VERSION)"
git tag "op-batcher/$(VERSION)" git tag "op-batcher/$(VERSION)"
git tag "op-service/$(VERSION)" git tag "op-service/$(VERSION)"
git tag "op-chain-ops/$(VERSION)"
git push $(BEDROCK_TAGS_REMOTE) "op-proposer/$(VERSION)" git push $(BEDROCK_TAGS_REMOTE) "op-proposer/$(VERSION)"
git push $(BEDROCK_TAGS_REMOTE) "op-node/$(VERSION)" git push $(BEDROCK_TAGS_REMOTE) "op-node/$(VERSION)"
git push $(BEDROCK_TAGS_REMOTE) "op-e2e/$(VERSION)" git push $(BEDROCK_TAGS_REMOTE) "op-e2e/$(VERSION)"
git push $(BEDROCK_TAGS_REMOTE) "op-bindings/$(VERSION)" git push $(BEDROCK_TAGS_REMOTE) "op-bindings/$(VERSION)"
git push $(BEDROCK_TAGS_REMOTE) "op-batcher/$(VERSION)" git push $(BEDROCK_TAGS_REMOTE) "op-batcher/$(VERSION)"
git push $(BEDROCK_TAGS_REMOTE) "op-service/$(VERSION)" git push $(BEDROCK_TAGS_REMOTE) "op-service/$(VERSION)"
git push $(BEDROCK_TAGS_REMOTE) "op-chain-ops/$(VERSION)"
...@@ -5,7 +5,8 @@ pkg := bindings ...@@ -5,7 +5,8 @@ pkg := bindings
all: mkdir bindings deployed all: mkdir bindings deployed
bindings: l1block-bindings \ bindings: l1block-bindings \
l1-standard-bridge-bindings \ l1-cross-domain-messenger-bindings \
l1-standard-bridge-bindings \
l2-to-l1-message-passer-bindings \ l2-to-l1-message-passer-bindings \
optimism-portal-bindings \ optimism-portal-bindings \
l2-output-oracle-bindings \ l2-output-oracle-bindings \
...@@ -16,6 +17,8 @@ bindings: l1block-bindings \ ...@@ -16,6 +17,8 @@ bindings: l1block-bindings \
sequencer-fee-vault-bindings \ sequencer-fee-vault-bindings \
optimism-mintable-erc20-factory-bindings \ optimism-mintable-erc20-factory-bindings \
optimism-mintable-erc20-bindings \ optimism-mintable-erc20-bindings \
proxy-bindings \
proxy-admin-bindings \
erc20-bindings \ erc20-bindings \
weth9-bindings weth9-bindings
...@@ -27,6 +30,9 @@ deployed: l1-block-deployed \ ...@@ -27,6 +30,9 @@ deployed: l1-block-deployed \
l1-block-deployed: l1block-bindings l1-block-deployed: l1block-bindings
./gen_deployed_bytecode.sh L1Block $(pkg) ./gen_deployed_bytecode.sh L1Block $(pkg)
l1-cross-domain-messenger-bindings:
./gen_bindings.sh contracts/L1/L1CrossDomainMessenger.sol:L1CrossDomainMessenger $(pkg)
l1-standard-bridge-bindings: l1-standard-bridge-bindings:
./gen_bindings.sh contracts/L1/L1StandardBridge.sol:L1StandardBridge $(pkg) ./gen_bindings.sh contracts/L1/L1StandardBridge.sol:L1StandardBridge $(pkg)
...@@ -72,6 +78,12 @@ optimism-mintable-erc20-factory-bindings: ...@@ -72,6 +78,12 @@ optimism-mintable-erc20-factory-bindings:
optimism-mintable-erc20-bindings: optimism-mintable-erc20-bindings:
./gen_bindings.sh contracts/universal/OptimismMintableERC20.sol:OptimismMintableERC20 $(pkg) ./gen_bindings.sh contracts/universal/OptimismMintableERC20.sol:OptimismMintableERC20 $(pkg)
proxy-bindings:
./gen_bindings.sh contracts/universal/Proxy.sol:Proxy $(pkg)
proxy-admin-bindings:
./gen_bindings.sh contracts/universal/ProxyAdmin.sol:ProxyAdmin $(pkg)
erc20-bindings: erc20-bindings:
./gen_bindings.sh ERC20 $(pkg) ./gen_bindings.sh ERC20 $(pkg)
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
package predeploys
import "github.com/ethereum/go-ethereum/common"
const (
DevL2OutputOracle = "0x6900000000000000000000000000000000000000"
DevOptimismPortal = "0x6900000000000000000000000000000000000001"
DevL1CrossDomainMessenger = "0x6900000000000000000000000000000000000002"
DevL1StandardBridge = "0x6900000000000000000000000000000000000003"
DevOptimismMintableERC20Factory = "0x6900000000000000000000000000000000000004"
DevAddressManager = "0x6900000000000000000000000000000000000005"
DevProxyAdmin = "0x6900000000000000000000000000000000000006"
)
var (
DevL2OutputOracleAddr = common.HexToAddress(DevL2OutputOracle)
DevOptimismPortalAddr = common.HexToAddress(DevOptimismPortal)
DevL1CrossDomainMessengerAddr = common.HexToAddress(DevL1CrossDomainMessenger)
DevL1StandardBridgeAddr = common.HexToAddress(DevL1StandardBridge)
DevOptimismMintableERC20FactoryAddr = common.HexToAddress(DevOptimismMintableERC20Factory)
DevAddressManagerAddr = common.HexToAddress(DevAddressManager)
DevProxyAdminAddr = common.HexToAddress(DevProxyAdmin)
DevPredeploys = make(map[string]*common.Address)
)
func init() {
DevPredeploys["L2OutputOracle"] = &DevL2OutputOracleAddr
DevPredeploys["OptimismPortal"] = &DevOptimismPortalAddr
DevPredeploys["L1CrossDomainMessenger"] = &DevL1CrossDomainMessengerAddr
DevPredeploys["L1StandardBridge"] = &DevL1StandardBridgeAddr
DevPredeploys["OptimismMintableERC20Factory"] = &DevOptimismMintableERC20FactoryAddr
DevPredeploys["AddressManager"] = &DevAddressManagerAddr
DevPredeploys["Admin"] = &DevProxyAdminAddr
}
...@@ -49,14 +49,14 @@ func DumpAddresses(dataDir string, outFile string) error { ...@@ -49,14 +49,14 @@ func DumpAddresses(dataDir string, outFile string) error {
// Migrate performs the actual state migration. It does quite a lot: // Migrate performs the actual state migration. It does quite a lot:
// //
// 1. It uses address lists, allowance lists, Mint events, and address preimages in // 1. It uses address lists, allowance lists, Mint events, and address preimages in
// the input state database to create a comprehensive list of storage slots in the // the input state database to create a comprehensive list of storage slots in the
// OVM ETH contract. // OVM ETH contract.
// 2. It iterates over the slots in OVM ETH, and compares then against the list in (1). // 2. It iterates over the slots in OVM ETH, and compares then against the list in (1).
// If the list doesn't match, or the total supply of OVM ETH doesn't match the sum of // If the list doesn't match, or the total supply of OVM ETH doesn't match the sum of
// all balance storage slots, it panics. // all balance storage slots, it panics.
// 3. It performs the actual migration by copying the input state DB into a new state DB. // 3. It performs the actual migration by copying the input state DB into a new state DB.
// 4. It imports the provided genesis into the new state DB like Geth would during geth init. // 4. It imports the provided genesis into the new state DB like Geth would during geth init.
// //
// It takes the following arguments: // It takes the following arguments:
// //
...@@ -64,9 +64,9 @@ func DumpAddresses(dataDir string, outFile string) error { ...@@ -64,9 +64,9 @@ func DumpAddresses(dataDir string, outFile string) error {
// - outDir: A directory to output the migrated database to. // - outDir: A directory to output the migrated database to.
// - genesis: The new chain's genesis configuration. // - genesis: The new chain's genesis configuration.
// - addrLists: A list of address list file paths. These address lists are used to populate // - addrLists: A list of address list file paths. These address lists are used to populate
// balances from previous regenesis events. // balances from previous regenesis events.
// - allowanceLists: A list of allowance list file paths. These allowance lists are used // - allowanceLists: A list of allowance list file paths. These allowance lists are used
// to calculate allowance storage slots from previous regenesis events. // to calculate allowance storage slots from previous regenesis events.
// - chainID: The chain ID of the chain being migrated. // - chainID: The chain ID of the chain being migrated.
func Migrate(dataDir, outDir string, genesis *core.Genesis, addrLists, allowanceLists []string, chainID, levelDBCacheSize, levelDBHandles int) error { func Migrate(dataDir, outDir string, genesis *core.Genesis, addrLists, allowanceLists []string, chainID, levelDBCacheSize, levelDBHandles int) error {
db := MustOpenDBWithCacheOpts(dataDir, levelDBCacheSize, levelDBHandles) db := MustOpenDBWithCacheOpts(dataDir, levelDBCacheSize, levelDBHandles)
...@@ -243,6 +243,9 @@ func Migrate(dataDir, outDir string, genesis *core.Genesis, addrLists, allowance ...@@ -243,6 +243,9 @@ func Migrate(dataDir, outDir string, genesis *core.Genesis, addrLists, allowance
log.Info("trie dumping started", "root", root) log.Info("trie dumping started", "root", root)
tr, err := backingStateDB.OpenTrie(root) tr, err := backingStateDB.OpenTrie(root)
if err != nil {
return err
}
it := trie.NewIterator(tr.NodeIterator(nil)) it := trie.NewIterator(tr.NodeIterator(nil))
totalMigrated := new(big.Int) totalMigrated := new(big.Int)
logAccountProgress := ProgressLogger(1000, "imported accounts") logAccountProgress := ProgressLogger(1000, "imported accounts")
......
package deployer
import (
"context"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
)
// TestKey is the same test key that geth uses
var TestKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
// ChainID is the chain id used for simulated backends
var ChainID = big.NewInt(1337)
var TestAddress = crypto.PubkeyToAddress(TestKey.PublicKey)
var thousandETH = new(big.Int).Mul(big.NewInt(params.Ether), big.NewInt(1000))
type Constructor struct {
Name string
Args []interface{}
}
type Deployment struct {
Name string
Bytecode hexutil.Bytes
Address common.Address
}
type Deployer func(*backends.SimulatedBackend, *bind.TransactOpts, Constructor) (common.Address, error)
func NewBackend() *backends.SimulatedBackend {
return backends.NewSimulatedBackendWithCacheConfig(
&core.CacheConfig{
Preimages: true,
},
core.GenesisAlloc{
crypto.PubkeyToAddress(TestKey.PublicKey): {Balance: thousandETH},
},
15000000,
)
}
func Deploy(backend *backends.SimulatedBackend, constructors []Constructor, cb Deployer) ([]Deployment, error) {
results := make([]Deployment, len(constructors))
opts, err := bind.NewKeyedTransactorWithChainID(TestKey, ChainID)
if err != nil {
return nil, err
}
opts.GasLimit = 15_000_000
for i, deployment := range constructors {
addr, err := cb(backend, opts, deployment)
if err != nil {
return nil, err
}
backend.Commit()
if addr == (common.Address{}) {
return nil, fmt.Errorf("no address for %s", deployment.Name)
}
code, err := backend.CodeAt(context.Background(), addr, nil)
if len(code) == 0 {
return nil, fmt.Errorf("no code found for %s", deployment.Name)
}
if err != nil {
return nil, fmt.Errorf("cannot fetch code for %s", deployment.Name)
}
results[i] = Deployment{
Name: deployment.Name,
Bytecode: code,
Address: addr,
}
}
return results, nil
}
...@@ -2,66 +2,73 @@ package genesis ...@@ -2,66 +2,73 @@ package genesis
import ( import (
"encoding/json" "encoding/json"
"math/big"
"os" "os"
"path/filepath" "path/filepath"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/hardhat"
"github.com/ethereum-optimism/optimism/op-chain-ops/state" "github.com/ethereum-optimism/optimism/op-chain-ops/state"
"github.com/ethereum/go-ethereum/common"
) )
// DeployConfig represents the deployment configuration for Optimism // DeployConfig represents the deployment configuration for Optimism
type DeployConfig struct { type DeployConfig struct {
L1StartingBlockTag rpc.BlockNumberOrHash `json:"l1StartingBlockTag"` L1StartingBlockTag *rpc.BlockNumberOrHash `json:"l1StartingBlockTag"`
L1ChainID *big.Int `json:"l1ChainID"` L1ChainID uint64 `json:"l1ChainID"`
L2ChainID *big.Int `json:"l2ChainID"` L2ChainID uint64 `json:"l2ChainID"`
L2BlockTime uint `json:"l2BlockTime"` L2BlockTime uint64 `json:"l2BlockTime"`
MaxSequencerDrift uint `json:"maxSequencerDrift"`
SequencerWindowSize uint `json:"sequencerWindowSize"` FinalizationPeriodSeconds uint64 `json:"finalizationPeriodSeconds"`
ChannelTimeout uint `json:"channelTimeout"` MaxSequencerDrift uint64 `json:"maxSequencerDrift"`
P2PSequencerAddress common.Address `json:"p2pSequencerAddress"` SequencerWindowSize uint64 `json:"sequencerWindowSize"`
OptimismL2FeeRecipient common.Address `json:"optimismL2FeeRecipient"` ChannelTimeout uint64 `json:"channelTimeout"`
BatchInboxAddress common.Address `json:"batchInboxAddress"` P2PSequencerAddress common.Address `json:"p2pSequencerAddress"`
BatchSenderAddress common.Address `json:"batchSenderAddress"` OptimismL2FeeRecipient common.Address `json:"optimismL2FeeRecipient"`
L2OutputOracleSubmissionInterval uint `json:"l2OutputOracleSubmissionInterval"` BatchInboxAddress common.Address `json:"batchInboxAddress"`
L2OutputOracleStartingTimestamp int `json:"l2OutputOracleStartingTimestamp"` BatchSenderAddress common.Address `json:"batchSenderAddress"`
L2OutputOracleProposer common.Address `json:"l2OutputOracleProposer"`
L2OutputOracleOwner common.Address `json:"l2OutputOracleOwner"` L2OutputOracleSubmissionInterval uint64 `json:"l2OutputOracleSubmissionInterval"`
L1BlockTime uint64 `json:"l1BlockTime"` L2OutputOracleStartingTimestamp int `json:"l2OutputOracleStartingTimestamp"`
CliqueSignerAddress common.Address `json:"cliqueSignerAddress"` L2OutputOracleProposer common.Address `json:"l2OutputOracleProposer"`
OptimismBaseFeeRecipient common.Address `json:"optimismBaseFeeRecipient"` L2OutputOracleOwner common.Address `json:"l2OutputOracleOwner"`
OptimismL1FeeRecipient common.Address `json:"optimismL1FeeRecipient"` L2OutputOracleGenesisL2Output common.Hash `json:"l2OutputOracleGenesisL2Output"`
GasPriceOracleOwner common.Address `json:"gasPriceOracleOwner"`
GasPriceOracleOverhead uint `json:"gasPriceOracleOverhead"` L1BlockTime uint64 `json:"l1BlockTime"`
GasPriceOracleScalar uint `json:"gasPriceOracleScalar"` L1GenesisBlockTimestamp hexutil.Uint64 `json:"l1GenesisBlockTimestamp"`
GasPriceOracleDecimals uint `json:"gasPriceOracleDecimals"` L1GenesisBlockNonce hexutil.Uint64 `json:"l1GenesisBlockNonce"`
L2CrossDomainMessengerOwner common.Address `json:"l2CrossDomainMessengerOwner"` CliqueSignerAddress common.Address `json:"cliqueSignerAddress"`
L2GenesisBlockNonce uint64 `json:"l2GenesisBlockNonce"` L1GenesisBlockGasLimit hexutil.Uint64 `json:"l1GenesisBlockGasLimit"`
L2GenesisBlockExtraData hexutil.Bytes `json:"l2GenesisBlockExtraData"` L1GenesisBlockDifficulty *hexutil.Big `json:"l1GenesisBlockDifficulty"`
L2GenesisBlockGasLimit uint64 `json:"l2GenesisBlockGasLimit"` L1GenesisBlockMixHash common.Hash `json:"l1GenesisBlockMixHash"`
L2GenesisBlockDifficulty *big.Int `json:"l2GenesisBlockDifficulty"` L1GenesisBlockCoinbase common.Address `json:"l1GenesisBlockCoinbase"`
L2GenesisBlockMixHash common.Hash `json:"l2GenesisBlockMixHash"` L1GenesisBlockNumber hexutil.Uint64 `json:"l1GenesisBlockNumber"`
L2GenesisBlockCoinbase common.Address `json:"l2GenesisBlockCoinbase"` L1GenesisBlockGasUsed hexutil.Uint64 `json:"l1GenesisBlockGasUsed"`
L2GenesisBlockNumber uint64 `json:"l2GenesisBlockNumber"` L1GenesisBlockParentHash common.Hash `json:"l1GenesisBlockParentHash"`
L2GenesisBlockGasUsed uint64 `json:"l2GenesisBlockGasUsed"` L1GenesisBlockBaseFeePerGas *hexutil.Big `json:"l1GenesisBlockBaseFeePerGas"`
L2GenesisBlockParentHash common.Hash `json:"l2GenesisBlockParentHash"`
L2GenesisBlockBaseFeePerGas *big.Int `json:"l2GenesisBlockBaseFeePerGas"` L2GenesisBlockNonce hexutil.Uint64 `json:"l2GenesisBlockNonce"`
L1GenesisBlockTimestamp uint64 `json:"l1GenesisBlockTimestamp"` L2GenesisBlockExtraData hexutil.Bytes `json:"l2GenesisBlockExtraData"`
L1GenesisBlockNonce uint64 `json:"l1GenesisBlockNonce"` L2GenesisBlockGasLimit hexutil.Uint64 `json:"l2GenesisBlockGasLimit"`
L1GenesisBlockGasLimit uint64 `json:"l1GenesisBlockGasLimit"` L2GenesisBlockDifficulty *hexutil.Big `json:"l2GenesisBlockDifficulty"`
L1GenesisBlockDifficulty *big.Int `json:"l1GenesisBlockDifficulty"` L2GenesisBlockMixHash common.Hash `json:"l2GenesisBlockMixHash"`
L1GenesisBlockMixHash common.Hash `json:"l1GenesisBlockMixHash"` L2GenesisBlockCoinbase common.Address `json:"l2GenesisBlockCoinbase"`
L1GenesisBlockCoinbase common.Address `json:"l1GenesisBlockCoinbase"` L2GenesisBlockNumber hexutil.Uint64 `json:"l2GenesisBlockNumber"`
L1GenesisBlockNumber uint64 `json:"l1GenesisBlockNumber"` L2GenesisBlockGasUsed hexutil.Uint64 `json:"l2GenesisBlockGasUsed"`
L1GenesisBlockGasUsed uint64 `json:"l1GenesisBlockGasUsed"` L2GenesisBlockParentHash common.Hash `json:"l2GenesisBlockParentHash"`
L1GenesisBlockParentHash common.Hash `json:"l1GenesisBlockParentHash"` L2GenesisBlockBaseFeePerGas *hexutil.Big `json:"l2GenesisBlockBaseFeePerGas"`
L1GenesisBlockBaseFeePerGas *big.Int `json:"l1GenesisBlockBaseFeePerGas"`
L2CrossDomainMessengerOwner common.Address `json:"l2CrossDomainMessengerOwner"`
OptimismBaseFeeRecipient common.Address `json:"optimismBaseFeeRecipient"`
OptimismL1FeeRecipient common.Address `json:"optimismL1FeeRecipient"`
GasPriceOracleOwner common.Address `json:"gasPriceOracleOwner"`
GasPriceOracleOverhead uint `json:"gasPriceOracleOverhead"`
GasPriceOracleScalar uint `json:"gasPriceOracleScalar"`
GasPriceOracleDecimals uint `json:"gasPriceOracleDecimals"`
DeploymentWaitConfirmations int `json:"deploymentWaitConfirmations"`
} }
// NewDeployConfig reads a config file given a path on the filesystem. // NewDeployConfig reads a config file given a path on the filesystem.
...@@ -91,25 +98,11 @@ func NewDeployConfigWithNetwork(network, path string) (*DeployConfig, error) { ...@@ -91,25 +98,11 @@ func NewDeployConfigWithNetwork(network, path string) (*DeployConfig, error) {
// contracts. // contracts.
type StorageConfig map[string]state.StorageValues type StorageConfig map[string]state.StorageValues
// NewStorageConfig will create a StorageConfig given an instance of a // NewL2StorageConfig will create a StorageConfig given an instance of a
// Hardhat and a DeployConfig. // Hardhat and a DeployConfig.
func NewStorageConfig(hh *hardhat.Hardhat, config *DeployConfig, chain ethereum.ChainReader) (StorageConfig, error) { func NewL2StorageConfig(config *DeployConfig, block *types.Block, proxyL1StandardBridge common.Address, proxyL1CrossDomainMessenger common.Address) (StorageConfig, error) {
storage := make(StorageConfig) storage := make(StorageConfig)
proxyL1StandardBridge, err := hh.GetDeployment("L1StandardBridgeProxy")
if err != nil {
return storage, err
}
proxyL1CrossDomainMessenger, err := hh.GetDeployment("L1CrossDomainMessengerProxy")
if err != nil {
return storage, err
}
block, err := getBlockFromTag(chain, config.L1StartingBlockTag)
if err != nil {
return storage, err
}
storage["L2ToL1MessagePasser"] = state.StorageValues{ storage["L2ToL1MessagePasser"] = state.StorageValues{
"nonce": 0, "nonce": 0,
} }
...@@ -122,7 +115,7 @@ func NewStorageConfig(hh *hardhat.Hardhat, config *DeployConfig, chain ethereum. ...@@ -122,7 +115,7 @@ func NewStorageConfig(hh *hardhat.Hardhat, config *DeployConfig, chain ethereum.
"_paused": false, "_paused": false,
"xDomainMsgSender": "0x000000000000000000000000000000000000dEaD", "xDomainMsgSender": "0x000000000000000000000000000000000000dEaD",
"msgNonce": 0, "msgNonce": 0,
"otherMessenger": proxyL1CrossDomainMessenger.Address, "otherMessenger": proxyL1CrossDomainMessenger,
"blockedSystemAddresses": map[any]any{ "blockedSystemAddresses": map[any]any{
predeploys.L2CrossDomainMessenger: true, predeploys.L2CrossDomainMessenger: true,
predeploys.L2ToL1MessagePasser: true, predeploys.L2ToL1MessagePasser: true,
...@@ -138,7 +131,7 @@ func NewStorageConfig(hh *hardhat.Hardhat, config *DeployConfig, chain ethereum. ...@@ -138,7 +131,7 @@ func NewStorageConfig(hh *hardhat.Hardhat, config *DeployConfig, chain ethereum.
"_initialized": true, "_initialized": true,
"_initializing": false, "_initializing": false,
"messenger": predeploys.L2CrossDomainMessenger, "messenger": predeploys.L2CrossDomainMessenger,
"otherBridge": proxyL1StandardBridge.Address, "otherBridge": proxyL1StandardBridge,
} }
storage["SequencerFeeVault"] = state.StorageValues{ storage["SequencerFeeVault"] = state.StorageValues{
"l1FeeWallet": config.OptimismL1FeeRecipient, "l1FeeWallet": config.OptimismL1FeeRecipient,
......
package genesis
import (
"bytes"
"encoding/json"
"fmt"
"os"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require"
)
func TestConfigMarshalUnmarshal(t *testing.T) {
// NOTE: the l1 starting block tag is set to null
// in the test since the type is not JSON serializable.
// Rather than bloat the code by introducing a marshalable
// block tag type that's only used in test, I created a separate
// test to validate that the starting block tag unmarshals
// correctly.
b, err := os.ReadFile("testdata/test-deploy-config-full.json")
require.NoError(t, err)
dec := json.NewDecoder(bytes.NewReader(b))
decoded := new(DeployConfig)
require.NoError(t, dec.Decode(decoded))
encoded, err := json.MarshalIndent(decoded, "", " ")
require.NoError(t, err)
require.JSONEq(t, string(b), string(encoded))
}
func TestUnmarshalL1StartingBlockTag(t *testing.T) {
decoded := new(DeployConfig)
require.NoError(t, json.Unmarshal([]byte(`{"l1StartingBlockTag": "earliest"}`), decoded))
require.EqualValues(t, rpc.EarliestBlockNumber, *decoded.L1StartingBlockTag.BlockNumber)
h := "0x86c7263d87140ca7cd9bf1bc9e95a435a7a0efc0ae2afaf64920c5b59a6393d4"
require.NoError(t, json.Unmarshal([]byte(fmt.Sprintf(`{"l1StartingBlockTag": "%s"}`, h)), decoded))
require.EqualValues(t, common.HexToHash(h), *decoded.L1StartingBlockTag.BlockHash)
}
...@@ -5,7 +5,9 @@ import ( ...@@ -5,7 +5,9 @@ import (
"math/big" "math/big"
"time" "time"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
...@@ -13,13 +15,13 @@ import ( ...@@ -13,13 +15,13 @@ import (
) )
// NewL2Genesis will create a new L2 genesis // NewL2Genesis will create a new L2 genesis
func NewL2Genesis(config *DeployConfig, chain ethereum.ChainReader) (*core.Genesis, error) { func NewL2Genesis(config *DeployConfig, block *types.Block) (*core.Genesis, error) {
if config.L2ChainID == nil { if config.L2ChainID == 0 {
return nil, errors.New("must define L2 ChainID") return nil, errors.New("must define L2 ChainID")
} }
optimismChainConfig := params.ChainConfig{ optimismChainConfig := params.ChainConfig{
ChainID: config.L2ChainID, ChainID: new(big.Int).SetUint64(config.L2ChainID),
HomesteadBlock: big.NewInt(0), HomesteadBlock: big.NewInt(0),
DAOForkBlock: nil, DAOForkBlock: nil,
DAOForkSupport: false, DAOForkSupport: false,
...@@ -53,42 +55,37 @@ func NewL2Genesis(config *DeployConfig, chain ethereum.ChainReader) (*core.Genes ...@@ -53,42 +55,37 @@ func NewL2Genesis(config *DeployConfig, chain ethereum.ChainReader) (*core.Genes
} }
gasLimit := config.L2GenesisBlockGasLimit gasLimit := config.L2GenesisBlockGasLimit
if gasLimit == 0 { if gasLimit == 0 {
gasLimit = uint64(15_000_000) gasLimit = 15_000_000
} }
baseFee := config.L2GenesisBlockBaseFeePerGas baseFee := config.L2GenesisBlockBaseFeePerGas
if baseFee == nil { if baseFee == nil {
baseFee = big.NewInt(params.InitialBaseFee) baseFee = newHexBig(params.InitialBaseFee)
} }
difficulty := config.L2GenesisBlockDifficulty difficulty := config.L2GenesisBlockDifficulty
if difficulty == nil { if difficulty == nil {
difficulty = big.NewInt(1) difficulty = newHexBig(1)
}
block, err := getBlockFromTag(chain, config.L1StartingBlockTag)
if err != nil {
return nil, err
} }
return &core.Genesis{ return &core.Genesis{
Config: &optimismChainConfig, Config: &optimismChainConfig,
Nonce: config.L2GenesisBlockNonce, Nonce: uint64(config.L2GenesisBlockNonce),
Timestamp: block.Time(), Timestamp: block.Time(),
ExtraData: extraData, ExtraData: extraData,
GasLimit: gasLimit, GasLimit: uint64(gasLimit),
Difficulty: difficulty, Difficulty: difficulty.ToInt(),
Mixhash: config.L2GenesisBlockMixHash, Mixhash: config.L2GenesisBlockMixHash,
Coinbase: config.L2GenesisBlockCoinbase, Coinbase: config.L2GenesisBlockCoinbase,
Number: config.L2GenesisBlockNumber, Number: uint64(config.L2GenesisBlockNumber),
GasUsed: config.L2GenesisBlockGasUsed, GasUsed: uint64(config.L2GenesisBlockGasUsed),
ParentHash: config.L2GenesisBlockParentHash, ParentHash: config.L2GenesisBlockParentHash,
BaseFee: baseFee, BaseFee: baseFee.ToInt(),
Alloc: map[common.Address]core.GenesisAccount{}, Alloc: map[common.Address]core.GenesisAccount{},
}, nil }, nil
} }
// NewL1Genesis will create a new L1 genesis config // NewL1Genesis will create a new L1 genesis config
func NewL1Genesis(config *DeployConfig) (*core.Genesis, error) { func NewL1Genesis(config *DeployConfig) (*core.Genesis, error) {
if config.L1ChainID == nil { if config.L1ChainID == 0 {
return nil, errors.New("must define L1 ChainID") return nil, errors.New("must define L1 ChainID")
} }
...@@ -97,40 +94,40 @@ func NewL1Genesis(config *DeployConfig) (*core.Genesis, error) { ...@@ -97,40 +94,40 @@ func NewL1Genesis(config *DeployConfig) (*core.Genesis, error) {
Period: config.L1BlockTime, Period: config.L1BlockTime,
Epoch: 30000, Epoch: 30000,
} }
chainConfig.ChainID = config.L1ChainID chainConfig.ChainID = uint642Big(config.L1ChainID)
gasLimit := config.L1GenesisBlockGasLimit gasLimit := config.L1GenesisBlockGasLimit
if gasLimit == 0 { if gasLimit == 0 {
gasLimit = uint64(15_000_000) gasLimit = 15_000_000
} }
baseFee := config.L1GenesisBlockBaseFeePerGas baseFee := config.L1GenesisBlockBaseFeePerGas
if baseFee == nil { if baseFee == nil {
baseFee = big.NewInt(params.InitialBaseFee) baseFee = newHexBig(params.InitialBaseFee)
} }
difficulty := config.L1GenesisBlockDifficulty difficulty := config.L1GenesisBlockDifficulty
if difficulty == nil { if difficulty == nil {
difficulty = big.NewInt(1) difficulty = newHexBig(1)
} }
timestamp := config.L1GenesisBlockTimestamp timestamp := config.L1GenesisBlockTimestamp
if timestamp == 0 { if timestamp == 0 {
timestamp = uint64(time.Now().Unix()) timestamp = hexutil.Uint64(time.Now().Unix())
} }
extraData := append(append(make([]byte, 32), config.CliqueSignerAddress[:]...), make([]byte, crypto.SignatureLength)...) extraData := append(append(make([]byte, 32), config.CliqueSignerAddress[:]...), make([]byte, crypto.SignatureLength)...)
return &core.Genesis{ return &core.Genesis{
Config: &chainConfig, Config: &chainConfig,
Nonce: config.L1GenesisBlockNonce, Nonce: uint64(config.L1GenesisBlockNonce),
Timestamp: timestamp, Timestamp: uint64(timestamp),
ExtraData: extraData, ExtraData: extraData,
GasLimit: gasLimit, GasLimit: uint64(gasLimit),
Difficulty: difficulty, Difficulty: difficulty.ToInt(),
Mixhash: config.L1GenesisBlockMixHash, Mixhash: config.L1GenesisBlockMixHash,
Coinbase: config.L1GenesisBlockCoinbase, Coinbase: config.L1GenesisBlockCoinbase,
Number: config.L1GenesisBlockNumber, Number: uint64(config.L1GenesisBlockNumber),
GasUsed: config.L1GenesisBlockGasUsed, GasUsed: uint64(config.L1GenesisBlockGasUsed),
ParentHash: config.L1GenesisBlockParentHash, ParentHash: config.L1GenesisBlockParentHash,
BaseFee: baseFee, BaseFee: baseFee.ToInt(),
Alloc: map[common.Address]core.GenesisAccount{}, Alloc: map[common.Address]core.GenesisAccount{},
}, nil }, nil
} }
...@@ -16,10 +16,14 @@ import ( ...@@ -16,10 +16,14 @@ import (
var ( var (
// codeNamespace represents the namespace of implementations of predeploys // codeNamespace represents the namespace of implementations of predeploys
codeNamespace = common.HexToAddress("0xc0D3C0d3C0d3C0D3c0d3C0d3c0D3C0d3c0d30000") codeNamespace = common.HexToAddress("0xc0D3C0d3C0d3C0D3c0d3C0d3c0D3C0d3c0d30000")
// predeployNamespace represents the namespace of predeploys // l2PredeployNamespace represents the namespace of L2 predeploys
predeployNamespace = common.HexToAddress("0x4200000000000000000000000000000000000000") l2PredeployNamespace = common.HexToAddress("0x4200000000000000000000000000000000000000")
// bigPredeployNamespace represents the predeploy namespace as a big.Int // l1PredeployNamespace represents the namespace of L1 predeploys
bigPredeployNamespace = new(big.Int).SetBytes(predeployNamespace.Bytes()) l1PredeployNamespace = common.HexToAddress("0x6900000000000000000000000000000000000000")
// bigL2PredeployNamespace represents the predeploy namespace as a big.Int
bigL2PredeployNamespace = new(big.Int).SetBytes(l2PredeployNamespace.Bytes())
// bigL1PredeployNamespace represents the predeploy namespace as a big.Int
bigL1PredeployNamespace = new(big.Int).SetBytes(l1PredeployNamespace.Bytes())
// bigCodeNamespace represents the predeploy namespace as a big.Int // bigCodeNamespace represents the predeploy namespace as a big.Int
bigCodeNameSpace = new(big.Int).SetBytes(codeNamespace.Bytes()) bigCodeNameSpace = new(big.Int).SetBytes(codeNamespace.Bytes())
// implementationSlot represents the EIP 1967 implementation storage slot // implementationSlot represents the EIP 1967 implementation storage slot
...@@ -59,19 +63,26 @@ var DevAccounts = []common.Address{ ...@@ -59,19 +63,26 @@ var DevAccounts = []common.Address{
var devBalance = hexutil.MustDecodeBig("0x200000000000000000000000000000000000000000000000000000000000000") var devBalance = hexutil.MustDecodeBig("0x200000000000000000000000000000000000000000000000000000000000000")
// AddressToCodeNamespace takes a predeploy address and computes // AddressToCodeNamespace takes a predeploy address and computes
// the implmentation address that the implementation should be deployed at // the implementation address that the implementation should be deployed at
func AddressToCodeNamespace(addr common.Address) (common.Address, error) { func AddressToCodeNamespace(addr common.Address) (common.Address, error) {
bytesAddr := addr.Bytes() if !IsL1DevPredeploy(addr) && !IsL2DevPredeploy(addr) {
if !bytes.Equal(bytesAddr[0:2], []byte{0x42, 0x00}) {
return common.Address{}, fmt.Errorf("cannot handle non predeploy: %s", addr) return common.Address{}, fmt.Errorf("cannot handle non predeploy: %s", addr)
} }
bigAddress := new(big.Int).SetBytes(bytesAddr[18:]) bigAddress := new(big.Int).SetBytes(addr[18:])
num := new(big.Int).Or(bigCodeNameSpace, bigAddress) num := new(big.Int).Or(bigCodeNameSpace, bigAddress)
return common.BigToAddress(num), nil return common.BigToAddress(num), nil
} }
// getBlockFromTag will resolve a Block given an rpc block tag func IsL1DevPredeploy(addr common.Address) bool {
func getBlockFromTag(chain ethereum.ChainReader, tag rpc.BlockNumberOrHash) (*types.Block, error) { return bytes.Equal(addr[0:2], []byte{0x69, 0x00})
}
func IsL2DevPredeploy(addr common.Address) bool {
return bytes.Equal(addr[0:2], []byte{0x42, 0x00})
}
// GetBlockFromTag will resolve a Block given an rpc block tag
func GetBlockFromTag(chain ethereum.ChainReader, tag *rpc.BlockNumberOrHash) (*types.Block, error) {
if hash, ok := tag.Hash(); ok { if hash, ok := tag.Hash(); ok {
block, err := chain.BlockByHash(context.Background(), hash) block, err := chain.BlockByHash(context.Background(), hash)
if err != nil { if err != nil {
...@@ -89,3 +100,14 @@ func getBlockFromTag(chain ethereum.ChainReader, tag rpc.BlockNumberOrHash) (*ty ...@@ -89,3 +100,14 @@ func getBlockFromTag(chain ethereum.ChainReader, tag rpc.BlockNumberOrHash) (*ty
return nil, fmt.Errorf("invalid block tag: %v", tag) return nil, fmt.Errorf("invalid block tag: %v", tag)
} }
} }
// uint642Big creates a new *big.Int from a uint64.
func uint642Big(in uint64) *big.Int {
return new(big.Int).SetUint64(in)
}
func newHexBig(in uint64) *hexutil.Big {
b := new(big.Int).SetUint64(in)
hb := hexutil.Big(*b)
return &hb
}
package genesis package genesis
import ( import (
"errors"
"fmt"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum-optimism/optimism/op-chain-ops/hardhat"
"github.com/ethereum-optimism/optimism/op-chain-ops/state" "github.com/ethereum-optimism/optimism/op-chain-ops/state"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/trie"
) )
// TODO(tynes): need bindings for all of the L1 contracts to be able var proxies = []string{
// to create a genesis file with the L1 contracts predeployed. "L2OutputOracleProxy",
// This would speed up testing as deployments take time when "L1CrossDomainMessengerProxy",
// running tests. "L1StandardBridgeProxy",
func BuildL1DeveloperGenesis(config *DeployConfig) (*core.Genesis, error) { "OptimismPortalProxy",
"OptimismMintableERC20FactoryProxy",
}
var portalMeteringSlot = common.Hash{31: 0x01}
var zeroHash common.Hash
func BuildL1DeveloperGenesis(hh *hardhat.Hardhat, config *DeployConfig) (*core.Genesis, error) {
if config.L2OutputOracleStartingTimestamp != -1 {
return nil, errors.New("l2oo starting timestamp must be -1")
}
if config.L1GenesisBlockTimestamp == 0 {
return nil, errors.New("must specify l1 genesis block timestamp")
}
genesis, err := NewL1Genesis(config) genesis, err := NewL1Genesis(config)
if err != nil { if err != nil {
return nil, err return nil, err
} }
db := state.NewMemoryStateDB(genesis) backend := deployer.NewBackend()
deployments, err := deployL1Contracts(config, backend)
if err != nil {
return nil, err
}
depsByName := make(map[string]deployer.Deployment)
depsByAddr := make(map[common.Address]deployer.Deployment)
for _, dep := range deployments {
depsByName[dep.Name] = dep
depsByAddr[dep.Address] = dep
}
FundDevAccounts(db) opts, err := bind.NewKeyedTransactorWithChainID(deployer.TestKey, deployer.ChainID)
SetPrecompileBalances(db) if err != nil {
return nil, err
}
l2ooABI, err := bindings.L2OutputOracleMetaData.GetAbi()
if err != nil {
return nil, err
}
data, err := l2ooABI.Pack(
"initialize",
config.L2OutputOracleGenesisL2Output,
big.NewInt(0),
config.L2OutputOracleProposer,
config.L2OutputOracleOwner,
)
if err != nil {
return nil, err
}
if err := upgradeProxy(
backend,
opts,
depsByName["L2OutputOracleProxy"].Address,
depsByName["L2OutputOracle"].Address,
data,
); err != nil {
return nil, err
}
return db.Genesis(), nil portalABI, err := bindings.OptimismPortalMetaData.GetAbi()
if err != nil {
return nil, err
}
data, err = portalABI.Pack("initialize")
if err != nil {
return nil, err
}
if err := upgradeProxy(
backend,
opts,
depsByName["OptimismPortalProxy"].Address,
depsByName["OptimismPortal"].Address,
data,
); err != nil {
return nil, err
}
l1XDMABI, err := bindings.L1CrossDomainMessengerMetaData.GetAbi()
if err != nil {
return nil, err
}
data, err = l1XDMABI.Pack("initialize")
if err != nil {
return nil, err
}
if err := upgradeProxy(
backend,
opts,
depsByName["L1CrossDomainMessengerProxy"].Address,
depsByName["L1CrossDomainMessenger"].Address,
data,
); err != nil {
return nil, err
}
l1SBrABI, err := bindings.L1StandardBridgeMetaData.GetAbi()
if err != nil {
return nil, err
}
data, err = l1SBrABI.Pack("initialize", predeploys.DevL1CrossDomainMessengerAddr)
if err != nil {
return nil, err
}
if err := upgradeProxy(
backend,
opts,
depsByName["L1StandardBridgeProxy"].Address,
depsByName["L1StandardBridge"].Address,
data,
); err != nil {
return nil, err
}
if err := upgradeProxy(
backend,
opts,
depsByName["OptimismMintableERC20FactoryProxy"].Address,
depsByName["OptimismMintableERC20Factory"].Address,
nil,
); err != nil {
return nil, err
}
backend.Commit()
memDB := state.NewMemoryStateDB(genesis)
if err := SetL1Proxies(hh, memDB, predeploys.DevProxyAdminAddr); err != nil {
return nil, err
}
FundDevAccounts(memDB)
SetPrecompileBalances(memDB)
for name, proxyAddr := range predeploys.DevPredeploys {
memDB.SetState(*proxyAddr, ImplementationSlot, depsByName[name].Address.Hash())
}
stateDB, err := backend.Blockchain().State()
if err != nil {
return nil, err
}
for _, dep := range deployments {
st := stateDB.StorageTrie(dep.Address)
iter := trie.NewIterator(st.NodeIterator(nil))
depAddr := dep.Address
if strings.HasSuffix(dep.Name, "Proxy") {
depAddr = *predeploys.DevPredeploys[strings.TrimSuffix(dep.Name, "Proxy")]
}
memDB.CreateAccount(depAddr)
memDB.SetCode(depAddr, dep.Bytecode)
for iter.Next() {
_, data, _, err := rlp.Split(iter.Value)
if err != nil {
return nil, err
}
key := common.BytesToHash(st.GetKey(iter.Key))
value := common.BytesToHash(data)
if depAddr == predeploys.DevOptimismPortalAddr && key == portalMeteringSlot {
// We need to manually set the block number in the resource
// metering storage slot to zero. Otherwise, deposits will
// revert.
copy(value[:24], zeroHash[:])
}
memDB.SetState(depAddr, key, value)
}
}
return memDB.Genesis(), nil
}
func deployL1Contracts(config *DeployConfig, backend *backends.SimulatedBackend) ([]deployer.Deployment, error) {
constructors := make([]deployer.Constructor, 0)
for _, proxy := range proxies {
constructors = append(constructors, deployer.Constructor{
Name: proxy,
})
}
constructors = append(constructors, []deployer.Constructor{
{
Name: "L2OutputOracle",
Args: []interface{}{
uint642Big(config.L2OutputOracleSubmissionInterval),
[32]byte(config.L2OutputOracleGenesisL2Output),
big.NewInt(0),
big.NewInt(0),
uint642Big(uint64(config.L1GenesisBlockTimestamp)),
uint642Big(config.L2BlockTime),
config.L2OutputOracleProposer,
config.L2OutputOracleOwner,
},
},
{
Name: "OptimismPortal",
Args: []interface{}{
uint642Big(config.FinalizationPeriodSeconds),
},
},
{
Name: "L1CrossDomainMessenger",
},
{
Name: "L1StandardBridge",
},
{
Name: "OptimismMintableERC20Factory",
},
{
Name: "AddressManager",
},
{
Name: "ProxyAdmin",
Args: []interface{}{
common.Address{19: 0x01},
},
},
}...)
return deployer.Deploy(backend, constructors, l1Deployer)
}
func l1Deployer(backend *backends.SimulatedBackend, opts *bind.TransactOpts, deployment deployer.Constructor) (common.Address, error) {
var addr common.Address
var err error
switch deployment.Name {
case "L2OutputOracle":
addr, _, _, err = bindings.DeployL2OutputOracle(
opts,
backend,
deployment.Args[0].(*big.Int),
deployment.Args[1].([32]byte),
deployment.Args[2].(*big.Int),
deployment.Args[3].(*big.Int),
deployment.Args[4].(*big.Int),
deployment.Args[5].(*big.Int),
deployment.Args[6].(common.Address),
deployment.Args[7].(common.Address),
)
case "OptimismPortal":
addr, _, _, err = bindings.DeployOptimismPortal(
opts,
backend,
predeploys.DevL2OutputOracleAddr,
deployment.Args[0].(*big.Int),
)
case "L1CrossDomainMessenger":
addr, _, _, err = bindings.DeployL1CrossDomainMessenger(
opts,
backend,
predeploys.DevOptimismPortalAddr,
)
case "L1StandardBridge":
addr, _, _, err = bindings.DeployL1StandardBridge(
opts,
backend,
predeploys.DevL1CrossDomainMessengerAddr,
)
case "OptimismMintableERC20Factory":
addr, _, _, err = bindings.DeployOptimismMintableERC20Factory(
opts,
backend,
predeploys.DevL1StandardBridgeAddr,
)
case "AddressManager":
addr, _, _, err = bindings.DeployAddressManager(
opts,
backend,
)
case "ProxyAdmin":
addr, _, _, err = bindings.DeployProxyAdmin(
opts,
backend,
common.Address{},
)
default:
if strings.HasSuffix(deployment.Name, "Proxy") {
addr, _, _, err = bindings.DeployProxy(opts, backend, deployer.TestAddress)
} else {
err = fmt.Errorf("unknown contract %s", deployment.Name)
}
}
return addr, err
}
func upgradeProxy(backend *backends.SimulatedBackend, opts *bind.TransactOpts, proxyAddr common.Address, implAddr common.Address, callData []byte) error {
proxy, err := bindings.NewProxy(proxyAddr, backend)
if err != nil {
return err
}
if callData == nil {
_, err = proxy.UpgradeTo(opts, implAddr)
} else {
_, err = proxy.UpgradeToAndCall(
opts,
implAddr,
callData,
)
}
if err == nil {
backend.Commit()
}
return err
} }
package genesis
import (
"bytes"
"encoding/json"
"fmt"
"math/big"
"os"
"path/filepath"
"testing"
"time"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/hardhat"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/stretchr/testify/require"
)
func TestBuildL1DeveloperGenesis(t *testing.T) {
b, err := os.ReadFile("testdata/test-deploy-config-full.json")
require.NoError(t, err)
dec := json.NewDecoder(bytes.NewReader(b))
config := new(DeployConfig)
require.NoError(t, dec.Decode(config))
config.L1GenesisBlockTimestamp = hexutil.Uint64(time.Now().Unix())
tmpdir := filepath.Join(os.TempDir(), fmt.Sprintf("l2-test-%d", time.Now().Unix()))
require.NoError(t, Untar("testdata/artifacts.tar.gz", tmpdir))
hh, err := hardhat.New(
"goerli",
[]string{
filepath.Join(tmpdir, "contracts-bedrock"),
filepath.Join(tmpdir, "contracts-governance"),
},
[]string{"../../packages/contracts-bedrock/deployments"},
)
require.Nil(t, err)
genesis, err := BuildL1DeveloperGenesis(hh, config)
require.NoError(t, err)
sim := backends.NewSimulatedBackend(
genesis.Alloc,
15000000,
)
callOpts := &bind.CallOpts{}
oracle, err := bindings.NewL2OutputOracle(predeploys.DevL2OutputOracleAddr, sim)
require.NoError(t, err)
portal, err := bindings.NewOptimismPortal(predeploys.DevOptimismPortalAddr, sim)
require.NoError(t, err)
proposer, err := oracle.Proposer(callOpts)
require.NoError(t, err)
require.Equal(t, config.L2OutputOracleProposer, proposer)
owner, err := oracle.Owner(callOpts)
require.NoError(t, err)
require.Equal(t, config.L2OutputOracleOwner, owner)
// Same set of tests as exist in the deployment scripts
interval, err := oracle.SUBMISSIONINTERVAL(callOpts)
require.NoError(t, err)
require.EqualValues(t, config.L2OutputOracleSubmissionInterval, interval.Uint64())
histBlocks, err := oracle.HISTORICALTOTALBLOCKS(callOpts)
require.NoError(t, err)
require.EqualValues(t, 0, histBlocks.Uint64())
startBlock, err := oracle.STARTINGBLOCKNUMBER(callOpts)
require.NoError(t, err)
require.EqualValues(t, 0, startBlock.Uint64())
l2BlockTime, err := oracle.L2BLOCKTIME(callOpts)
require.NoError(t, err)
require.EqualValues(t, 2, l2BlockTime.Uint64())
oracleAddr, err := portal.L2ORACLE(callOpts)
require.NoError(t, err)
require.EqualValues(t, predeploys.DevL2OutputOracleAddr, oracleAddr)
msgr, err := bindings.NewL1CrossDomainMessenger(predeploys.DevL1CrossDomainMessengerAddr, sim)
require.NoError(t, err)
portalAddr, err := msgr.Portal(callOpts)
require.NoError(t, err)
require.Equal(t, predeploys.DevOptimismPortalAddr, portalAddr)
bridge, err := bindings.NewL1StandardBridge(predeploys.DevL1StandardBridgeAddr, sim)
require.NoError(t, err)
msgrAddr, err := bridge.Messenger(callOpts)
require.NoError(t, err)
require.Equal(t, predeploys.DevL1CrossDomainMessengerAddr, msgrAddr)
otherBridge, err := bridge.OtherBridge(callOpts)
require.NoError(t, err)
require.Equal(t, predeploys.L2StandardBridgeAddr, otherBridge)
factory, err := bindings.NewOptimismMintableERC20(predeploys.DevOptimismMintableERC20FactoryAddr, sim)
require.NoError(t, err)
bridgeAddr, err := factory.Bridge(callOpts)
require.NoError(t, err)
require.Equal(t, predeploys.DevL1StandardBridgeAddr, bridgeAddr)
// test that we can do deposits, etc.
priv, err := crypto.HexToECDSA("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80")
require.NoError(t, err)
tOpts, err := bind.NewKeyedTransactorWithChainID(priv, deployer.ChainID)
require.NoError(t, err)
tOpts.Value = big.NewInt(0.001 * params.Ether)
tOpts.GasLimit = 1_000_000
_, err = bridge.DepositETH(tOpts, 200000, nil)
require.NoError(t, err)
}
...@@ -3,15 +3,22 @@ package genesis ...@@ -3,15 +3,22 @@ package genesis
import ( import (
"github.com/ethereum-optimism/optimism/op-chain-ops/hardhat" "github.com/ethereum-optimism/optimism/op-chain-ops/hardhat"
"github.com/ethereum-optimism/optimism/op-chain-ops/state" "github.com/ethereum-optimism/optimism/op-chain-ops/state"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
) )
// BuildOptimismDeveloperGenesis will build the developer Optimism Genesis type L2Addresses struct {
ProxyAdmin common.Address
L1StandardBridgeProxy common.Address
L1CrossDomainMessengerProxy common.Address
}
// BuildL2DeveloperGenesis will build the developer Optimism Genesis
// Block. Suitable for devnets. // Block. Suitable for devnets.
func BuildOptimismDeveloperGenesis(hh *hardhat.Hardhat, config *DeployConfig, chain ethereum.ChainReader) (*core.Genesis, error) { func BuildL2DeveloperGenesis(hh *hardhat.Hardhat, config *DeployConfig, l1StartBlock *types.Block, l2Addrs *L2Addresses) (*core.Genesis, error) {
genspec, err := NewL2Genesis(config, chain) genspec, err := NewL2Genesis(config, l1StartBlock)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -21,20 +28,25 @@ func BuildOptimismDeveloperGenesis(hh *hardhat.Hardhat, config *DeployConfig, ch ...@@ -21,20 +28,25 @@ func BuildOptimismDeveloperGenesis(hh *hardhat.Hardhat, config *DeployConfig, ch
FundDevAccounts(db) FundDevAccounts(db)
SetPrecompileBalances(db) SetPrecompileBalances(db)
return BuildOptimismGenesis(db, hh, config, chain) return BuildL2Genesis(db, hh, config, l1StartBlock, l2Addrs)
} }
// BuildOptimismGenesis will build the L2 Optimism Genesis Block // BuildL2Genesis will build the L2 Optimism Genesis Block
func BuildOptimismGenesis(db *state.MemoryStateDB, hh *hardhat.Hardhat, config *DeployConfig, chain ethereum.ChainReader) (*core.Genesis, error) { func BuildL2Genesis(db *state.MemoryStateDB, hh *hardhat.Hardhat, config *DeployConfig, l1Block *types.Block, l2Addrs *L2Addresses) (*core.Genesis, error) {
// TODO(tynes): need a function for clearing old, unused storage slots. // TODO(tynes): need a function for clearing old, unused storage slots.
// Each deployed contract on L2 needs to have its existing storage // Each deployed contract on L2 needs to have its existing storage
// inspected and then cleared if they are no longer used. // inspected and then cleared if they are no longer used.
if err := SetProxies(hh, db); err != nil { if err := SetL2Proxies(hh, db, l2Addrs.ProxyAdmin); err != nil {
return nil, err return nil, err
} }
storage, err := NewStorageConfig(hh, config, chain) storage, err := NewL2StorageConfig(
config,
l1Block,
l2Addrs.L1StandardBridgeProxy,
l2Addrs.L1CrossDomainMessengerProxy,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
package genesis_test package genesis_test
import ( import (
"context"
"encoding/json" "encoding/json"
"flag" "flag"
"io/ioutil" "fmt"
"math/big" "math/big"
"os"
"path/filepath"
"testing" "testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
...@@ -25,18 +31,26 @@ func init() { ...@@ -25,18 +31,26 @@ func init() {
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
func TestBuildOptimismGenesis(t *testing.T) { func TestBuildL2DeveloperGenesis(t *testing.T) {
tmpdir := filepath.Join(os.TempDir(), fmt.Sprintf("l2-test-%d", time.Now().Unix()))
require.NoError(t, genesis.Untar("testdata/artifacts.tar.gz", tmpdir))
hh, err := hardhat.New( hh, err := hardhat.New(
"goerli", "goerli",
[]string{ []string{
"../../packages/contracts-bedrock/artifacts", filepath.Join(tmpdir, "contracts-bedrock"),
"../../packages/contracts-governance/artifacts", filepath.Join(tmpdir, "contracts-governance"),
}, },
[]string{"../../packages/contracts-bedrock/deployments"}, []string{"../../packages/contracts-bedrock/deployments"},
) )
require.Nil(t, err) require.Nil(t, err)
config, err := genesis.NewDeployConfig("../../packages/contracts-bedrock/deploy-config/devnetL1.json") config, err := genesis.NewDeployConfig("./testdata/test-deploy-config-devnet-l1.json")
require.Nil(t, err)
proxyAdmin, err := hh.GetDeployment("ProxyAdmin")
require.Nil(t, err)
proxy, err := hh.GetArtifact("Proxy")
require.Nil(t, err) require.Nil(t, err)
backend := backends.NewSimulatedBackend( backend := backends.NewSimulatedBackend(
...@@ -45,16 +59,14 @@ func TestBuildOptimismGenesis(t *testing.T) { ...@@ -45,16 +59,14 @@ func TestBuildOptimismGenesis(t *testing.T) {
}, },
15000000, 15000000,
) )
block, err := backend.BlockByNumber(context.Background(), common.Big0)
gen, err := genesis.BuildOptimismDeveloperGenesis(hh, config, backend) require.NoError(t, err)
gen, err := genesis.BuildL2DeveloperGenesis(hh, config, block, &genesis.L2Addresses{
ProxyAdmin: proxyAdmin.Address,
})
require.Nil(t, err) require.Nil(t, err)
require.NotNil(t, gen) require.NotNil(t, gen)
proxyAdmin, err := hh.GetDeployment("ProxyAdmin")
require.Nil(t, err)
proxy, err := hh.GetArtifact("Proxy")
require.Nil(t, err)
for name, address := range predeploys.Predeploys { for name, address := range predeploys.Predeploys {
addr := *address addr := *address
...@@ -74,6 +86,6 @@ func TestBuildOptimismGenesis(t *testing.T) { ...@@ -74,6 +86,6 @@ func TestBuildOptimismGenesis(t *testing.T) {
if writeFile { if writeFile {
file, _ := json.MarshalIndent(gen, "", " ") file, _ := json.MarshalIndent(gen, "", " ")
_ = ioutil.WriteFile("genesis.json", file, 0644) _ = os.WriteFile("genesis.json", file, 0644)
} }
} }
...@@ -20,22 +20,30 @@ func FundDevAccounts(db vm.StateDB) { ...@@ -20,22 +20,30 @@ func FundDevAccounts(db vm.StateDB) {
} }
} }
// SetProxies will set each of the proxies in the state. It requires // SetL2Proxies will set each of the proxies in the state. It requires
// a Proxy and ProxyAdmin deployment present so that the Proxy bytecode // a Proxy and ProxyAdmin deployment present so that the Proxy bytecode
// can be set in state and the ProxyAdmin can be set as the admin of the // can be set in state and the ProxyAdmin can be set as the admin of the
// Proxy. // Proxy.
func SetProxies(hh *hardhat.Hardhat, db vm.StateDB) error { func SetL2Proxies(hh *hardhat.Hardhat, db vm.StateDB, proxyAdminAddr common.Address) error {
return setProxies(hh, db, proxyAdminAddr, bigL2PredeployNamespace, 2048)
}
// SetL1Proxies will set each of the proxies in the state. It requires
// a Proxy and ProxyAdmin deployment present so that the Proxy bytecode
// can be set in state and the ProxyAdmin can be set as the admin of the
// Proxy.
func SetL1Proxies(hh *hardhat.Hardhat, db vm.StateDB, proxyAdminAddr common.Address) error {
return setProxies(hh, db, proxyAdminAddr, bigL1PredeployNamespace, 2048)
}
func setProxies(hh *hardhat.Hardhat, db vm.StateDB, proxyAdminAddr common.Address, namespace *big.Int, count uint64) error {
proxy, err := hh.GetArtifact("Proxy") proxy, err := hh.GetArtifact("Proxy")
if err != nil { if err != nil {
return err return err
} }
proxyAdmin, err := hh.GetDeployment("ProxyAdmin")
if err != nil {
return err
}
for i := uint64(0); i <= 2048; i++ { for i := uint64(0); i <= count; i++ {
bigAddr := new(big.Int).Or(bigPredeployNamespace, new(big.Int).SetUint64(i)) bigAddr := new(big.Int).Or(namespace, new(big.Int).SetUint64(i))
addr := common.BigToAddress(bigAddr) addr := common.BigToAddress(bigAddr)
// There is no proxy at the governance token address // There is no proxy at the governance token address
...@@ -45,7 +53,7 @@ func SetProxies(hh *hardhat.Hardhat, db vm.StateDB) error { ...@@ -45,7 +53,7 @@ func SetProxies(hh *hardhat.Hardhat, db vm.StateDB) error {
db.CreateAccount(addr) db.CreateAccount(addr)
db.SetCode(addr, proxy.DeployedBytecode) db.SetCode(addr, proxy.DeployedBytecode)
db.SetState(addr, AdminSlot, proxyAdmin.Address.Hash()) db.SetState(addr, AdminSlot, proxyAdminAddr.Hash())
} }
return nil return nil
} }
...@@ -78,7 +86,7 @@ func SetImplementations(hh *hardhat.Hardhat, db vm.StateDB, storage StorageConfi ...@@ -78,7 +86,7 @@ func SetImplementations(hh *hardhat.Hardhat, db vm.StateDB, storage StorageConfi
if err != nil { if err != nil {
return err return err
} }
// Set the implmentation slot in the predeploy proxy // Set the implementation slot in the predeploy proxy
db.SetState(*address, ImplementationSlot, addr.Hash()) db.SetState(*address, ImplementationSlot, addr.Hash())
} }
...@@ -138,14 +146,13 @@ func MigrateDepositHashes(hh *hardhat.Hardhat, db vm.StateDB) error { ...@@ -138,14 +146,13 @@ func MigrateDepositHashes(hh *hardhat.Hardhat, db vm.StateDB) error {
ignore[encoded] = true ignore[encoded] = true
} }
db.ForEachStorage(predeploys.L2ToL1MessagePasserAddr, func(key, value common.Hash) bool { return db.ForEachStorage(predeploys.L2ToL1MessagePasserAddr, func(key, value common.Hash) bool {
if _, ok := ignore[key]; ok { if _, ok := ignore[key]; ok {
return true return true
} }
// TODO(tynes): Do the value migration here // TODO(tynes): Do the value migration here
return true return true
}) })
return nil
} }
// SetPrecompileBalances will set a single wei at each precompile address. // SetPrecompileBalances will set a single wei at each precompile address.
......
package genesis
import (
"archive/tar"
"compress/gzip"
"io"
"os"
"path/filepath"
)
func Untar(tarball, target string) error {
f, err := os.Open(tarball)
if err != nil {
return err
}
defer f.Close()
r, err := gzip.NewReader(f)
if err != nil {
return err
}
tarReader := tar.NewReader(r)
for {
header, err := tarReader.Next()
if err == io.EOF {
break
} else if err != nil {
return err
}
path := filepath.Join(target, header.Name)
info := header.FileInfo()
if info.IsDir() {
if err = os.MkdirAll(path, info.Mode()); err != nil {
return err
}
continue
}
file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode())
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(file, tarReader)
if err != nil {
return err
}
}
return nil
}
{
"l1StartingBlockTag": "earliest",
"l1ChainID": 900,
"l2ChainID": 901,
"l2BlockTime": 2,
"maxSequencerDrift": 100,
"sequencerWindowSize": 4,
"channelTimeout": 40,
"p2pSequencerAddress": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc",
"optimismL2FeeRecipient": "0xd9c09e21b57c98e58a80552c170989b426766aa7",
"batchInboxAddress": "0xff00000000000000000000000000000000000000",
"batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC",
"l2OutputOracleSubmissionInterval": 20,
"l2OutputOracleStartingTimestamp": -1,
"l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"l2OutputOracleOwner": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"l1BlockTime": 15,
"cliqueSignerAddress": "0xca062b0fd91172d89bcd4bb084ac4e21972cc467",
"optimismBaseFeeRecipient": "0xBcd4042DE499D14e55001CcbB24a551F3b954096",
"optimismL1FeeRecipient": "0x71bE63f3384f5fb98995898A86B02Fb2426c5788",
"deploymentWaitConfirmations": 1
}
{
"l1StartingBlockTag": null,
"l1ChainID": 901,
"l2ChainID": 902,
"l2BlockTime": 2,
"maxSequencerDrift": 20,
"sequencerWindowSize": 100,
"channelTimeout": 30,
"p2pSequencerAddress": "0x0000000000000000000000000000000000000000",
"optimismL2FeeRecipient": "0x42000000000000000000000000000000000000f0",
"batchInboxAddress": "0x42000000000000000000000000000000000000ff",
"batchSenderAddress": "0x0000000000000000000000000000000000000000",
"l2OutputOracleSubmissionInterval": 6,
"l2OutputOracleStartingTimestamp": -1,
"l2OutputOracleProposer": "0x7770000000000000000000000000000000000001",
"l2OutputOracleOwner": "0x7770000000000000000000000000000000000002",
"l1BlockTime": 15,
"l1GenesisBlockNonce": "0x0",
"cliqueSignerAddress": "0x0000000000000000000000000000000000000000",
"l1GenesisBlockGasLimit": "0xe4e1c0",
"l1GenesisBlockDifficulty": "0x1",
"finalizationPeriodSeconds": 2,
"l1GenesisBlockMixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"l1GenesisBlockCoinbase": "0x0000000000000000000000000000000000000000",
"l1GenesisBlockNumber": "0x0",
"l1GenesisBlockGasUsed": "0x0",
"l1GenesisBlockParentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"l1GenesisBlockTimestamp": "0x0",
"l1GenesisBlockBaseFeePerGas": "0x3b9aca00",
"l2GenesisBlockNonce": "0x0",
"l2GenesisBlockExtraData": "0x",
"l2GenesisBlockGasLimit": "0xe4e1c0",
"l2GenesisBlockDifficulty": "0x1",
"l2GenesisBlockMixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"l2GenesisBlockCoinbase": "0x42000000000000000000000000000000000000f0",
"l2OutputOracleGenesisL2Output": "0x0000000000000000000000000000000000000000000000000000000000000000",
"l2GenesisBlockNumber": "0x0",
"l2GenesisBlockGasUsed": "0x0",
"l2GenesisBlockParentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"l2GenesisBlockBaseFeePerGas": "0x3b9aca00",
"optimismBaseFeeRecipient": "0x42000000000000000000000000000000000000f1",
"optimismL1FeeRecipient": "0x0000000000000000000000000000000000000000",
"l2CrossDomainMessengerOwner": "0x42000000000000000000000000000000000000f2",
"gasPriceOracleOwner": "0x42000000000000000000000000000000000000f3",
"gasPriceOracleOverhead": 2100,
"gasPriceOracleScalar": 1000000,
"gasPriceOracleDecimals": 6,
"deploymentWaitConfirmations": 1
}
\ No newline at end of file
...@@ -52,3 +52,5 @@ require ( ...@@ -52,3 +52,5 @@ require (
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )
replace github.com/ethereum/go-ethereum v1.10.21 => github.com/ethereum-optimism/op-geth v0.0.0-20220904174542-4311f9d2cead
...@@ -26,7 +26,7 @@ type Hardhat struct { ...@@ -26,7 +26,7 @@ type Hardhat struct {
artifacts []*Artifact artifacts []*Artifact
deployments []*Deployment deployments []*Deployment
buildInfos []*BuildInfo buildInfos []*BuildInfo //nolint:unused
} }
// New creates a new `Hardhat` struct and reads all of the files from // New creates a new `Hardhat` struct and reads all of the files from
...@@ -202,7 +202,7 @@ func (h *Hardhat) GetBuildInfo(name string) (*BuildInfo, error) { ...@@ -202,7 +202,7 @@ func (h *Hardhat) GetBuildInfo(name string) (*BuildInfo, error) {
for _, artifactPath := range h.ArtifactPaths { for _, artifactPath := range h.ArtifactPaths {
fileSystem := os.DirFS(artifactPath) fileSystem := os.DirFS(artifactPath)
fs.WalkDir(fileSystem, ".", func(path string, d fs.DirEntry, err error) error { err := fs.WalkDir(fileSystem, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil { if err != nil {
return err return err
} }
...@@ -249,6 +249,9 @@ func (h *Hardhat) GetBuildInfo(name string) (*BuildInfo, error) { ...@@ -249,6 +249,9 @@ func (h *Hardhat) GetBuildInfo(name string) (*BuildInfo, error) {
return nil return nil
}) })
if err != nil {
return nil, err
}
} }
// TODO(tynes): handle multiple contracts with same name when required // TODO(tynes): handle multiple contracts with same name when required
......
...@@ -57,7 +57,7 @@ type Log struct { ...@@ -57,7 +57,7 @@ type Log struct {
} }
// Artifact represents a hardhat compilation artifact // Artifact represents a hardhat compilation artifact
// The Bytecode and DeployedBytecode are not guranteed // The Bytecode and DeployedBytecode are not guaranteed
// to be hexutil.Bytes when there are link references. // to be hexutil.Bytes when there are link references.
// In the future, custom json marshalling can be used // In the future, custom json marshalling can be used
// to place the link reference values in the correct location. // to place the link reference values in the correct location.
......
package immutables package immutables
import ( import (
"context"
"fmt" "fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
) )
// testKey is the same test key that geth uses
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
// chainID is the chain id used for simulated backends
var chainID = big.NewInt(1337)
// TODO(tynes): we are planning on making some constructor arguments
// into immutables before the final deployment of the system. This
// means that this struct will need to be updated with an additional
// parameter: Args []interface{}{} and each step will need to typecast
// each argument before doing the simulated deployment
type Deployment struct {
Name string
}
// DeploymentResults represents the output of deploying each of the // DeploymentResults represents the output of deploying each of the
// contracts so that the immutables can be set properly in the bytecode. // contracts so that the immutables can be set properly in the bytecode.
type DeploymentResults map[string]hexutil.Bytes type DeploymentResults map[string]hexutil.Bytes
...@@ -40,7 +21,7 @@ type DeploymentResults map[string]hexutil.Bytes ...@@ -40,7 +21,7 @@ type DeploymentResults map[string]hexutil.Bytes
// a JSON file/cli flags and then populate the Deployment // a JSON file/cli flags and then populate the Deployment
// Args. // Args.
func BuildOptimism() (DeploymentResults, error) { func BuildOptimism() (DeploymentResults, error) {
deployments := []Deployment{ deployments := []deployer.Constructor{
{ {
Name: "GasPriceOracle", Name: "GasPriceOracle",
}, },
...@@ -63,81 +44,56 @@ func BuildOptimism() (DeploymentResults, error) { ...@@ -63,81 +44,56 @@ func BuildOptimism() (DeploymentResults, error) {
Name: "OptimismMintableERC20Factory", Name: "OptimismMintableERC20Factory",
}, },
} }
return Build(deployments) return BuildL2(deployments)
} }
// Build will deploy contracts to a simulated backend so that their immutables // BuildL2 will deploy contracts to a simulated backend so that their immutables
// can be properly set. The bytecode returned in the results is suitable to be // can be properly set. The bytecode returned in the results is suitable to be
// inserted into the state via state surgery. // inserted into the state via state surgery.
func Build(deployments []Deployment) (DeploymentResults, error) { func BuildL2(constructors []deployer.Constructor) (DeploymentResults, error) {
backend := backends.NewSimulatedBackend( deployments, err := deployer.Deploy(deployer.NewBackend(), constructors, l2Deployer)
core.GenesisAlloc{
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
},
15000000,
)
results := make(DeploymentResults)
opts, err := bind.NewKeyedTransactorWithChainID(testKey, chainID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
results := make(DeploymentResults)
for _, dep := range deployments {
results[dep.Name] = dep.Bytecode
}
return results, nil
}
for _, deployment := range deployments { func l2Deployer(backend *backends.SimulatedBackend, opts *bind.TransactOpts, deployment deployer.Constructor) (common.Address, error) {
var addr common.Address var addr common.Address
switch deployment.Name { var err error
case "GasPriceOracle": switch deployment.Name {
// The owner of the gas price oracle is not immutable, not required case "GasPriceOracle":
// to be set here. It cannot be `address(0)` // The owner of the gas price oracle is not immutable, not required
owner := common.Address{1} // to be set here. It cannot be `address(0)`
addr, _, _, err = bindings.DeployGasPriceOracle(opts, backend, owner) owner := common.Address{1}
if err != nil { addr, _, _, err = bindings.DeployGasPriceOracle(opts, backend, owner)
return nil, err case "L1Block":
} // No arguments required for the L1Block contract
case "L1Block": addr, _, _, err = bindings.DeployL1Block(opts, backend)
// No arguments required for the L1Block contract case "L2CrossDomainMessenger":
addr, _, _, err = bindings.DeployL1Block(opts, backend) // The L1CrossDomainMessenger value is not immutable, no need to set
if err != nil { // it here correctly
return nil, err l1CrossDomainMessenger := common.Address{}
} addr, _, _, err = bindings.DeployL2CrossDomainMessenger(opts, backend, l1CrossDomainMessenger)
case "L2CrossDomainMessenger": case "L2StandardBridge":
// The L1CrossDomainMessenger value is not immutable, no need to set // The OtherBridge value is not immutable, no need to set
// it here correctly otherBridge := common.Address{}
l1CrossDomainMessenger := common.Address{} addr, _, _, err = bindings.DeployL2StandardBridge(opts, backend, otherBridge)
addr, _, _, err = bindings.DeployL2CrossDomainMessenger(opts, backend, l1CrossDomainMessenger) case "L2ToL1MessagePasser":
if err != nil { // No arguments required for L2ToL1MessagePasser
return nil, err addr, _, _, err = bindings.DeployL2ToL1MessagePasser(opts, backend)
} case "SequencerFeeVault":
case "L2StandardBridge": // No arguments to SequencerFeeVault
// The OtherBridge value is not immutable, no need to set addr, _, _, err = bindings.DeploySequencerFeeVault(opts, backend)
otherBridge := common.Address{} case "OptimismMintableERC20Factory":
addr, _, _, err = bindings.DeployL2StandardBridge(opts, backend, otherBridge) addr, _, _, err = bindings.DeployOptimismMintableERC20Factory(opts, backend, predeploys.L2StandardBridgeAddr)
case "L2ToL1MessagePasser": default:
// No arguments required for L2ToL1MessagePasser return addr, fmt.Errorf("unknown contract: %s", deployment.Name)
addr, _, _, err = bindings.DeployL2ToL1MessagePasser(opts, backend)
case "SequencerFeeVault":
// No arguments to SequencerFeeVault
addr, _, _, err = bindings.DeploySequencerFeeVault(opts, backend)
case "OptimismMintableERC20Factory":
addr, _, _, err = bindings.DeployOptimismMintableERC20Factory(opts, backend, predeploys.L2StandardBridgeAddr)
default:
return nil, fmt.Errorf("unknown contract: %s", deployment.Name)
}
backend.Commit()
if addr == (common.Address{}) {
return nil, fmt.Errorf("no address for %s", deployment.Name)
}
code, err := backend.CodeAt(context.Background(), addr, nil)
if len(code) == 0 {
return nil, fmt.Errorf("no code found for %s", deployment.Name)
}
if err != nil {
return nil, fmt.Errorf("cannot fetch code for %s", deployment.Name)
}
results[deployment.Name] = code
} }
return results, nil return addr, err
} }
...@@ -55,7 +55,7 @@ type StorageLayoutEntry struct { ...@@ -55,7 +55,7 @@ type StorageLayoutEntry struct {
Label string `json:"label"` Label string `json:"label"`
Offset uint `json:"offset"` Offset uint `json:"offset"`
Slot uint `json:"slot,string"` Slot uint `json:"slot,string"`
Type string `json"type"` Type string `json:"type"`
} }
type StorageLayoutType struct { type StorageLayoutType struct {
...@@ -73,7 +73,7 @@ type CompilerOutputEvm struct { ...@@ -73,7 +73,7 @@ type CompilerOutputEvm struct {
MethodIdentifiers map[string]string `json:"methodIdentifiers"` MethodIdentifiers map[string]string `json:"methodIdentifiers"`
} }
// Object must be a string because its not guranteed to be // Object must be a string because its not guaranteed to be
// a hex string // a hex string
type CompilerOutputBytecode struct { type CompilerOutputBytecode struct {
Object string `json:"object"` Object string `json:"object"`
......
...@@ -21,7 +21,7 @@ type EncodedStorage struct { ...@@ -21,7 +21,7 @@ type EncodedStorage struct {
Value common.Hash Value common.Hash
} }
// EncodedStorage will encode a storage layout // EncodeStorage will encode a storage layout
func EncodeStorage(entry solc.StorageLayoutEntry, value any, storageType solc.StorageLayoutType) ([]*EncodedStorage, error) { func EncodeStorage(entry solc.StorageLayoutEntry, value any, storageType solc.StorageLayoutType) ([]*EncodedStorage, error) {
if storageType.NumberOfBytes > 32 { if storageType.NumberOfBytes > 32 {
return nil, fmt.Errorf("%s is larger than 32 bytes", storageType.Encoding) return nil, fmt.Errorf("%s is larger than 32 bytes", storageType.Encoding)
......
...@@ -624,4 +624,3 @@ func (_Testdata *TestdataSession) SetStorage(key [32]byte, value [32]byte) (*typ ...@@ -624,4 +624,3 @@ func (_Testdata *TestdataSession) SetStorage(key [32]byte, value [32]byte) (*typ
func (_Testdata *TestdataTransactorSession) SetStorage(key [32]byte, value [32]byte) (*types.Transaction, error) { func (_Testdata *TestdataTransactorSession) SetStorage(key [32]byte, value [32]byte) (*types.Transaction, error) {
return _Testdata.Contract.SetStorage(&_Testdata.TransactOpts, key, value) return _Testdata.Contract.SetStorage(&_Testdata.TransactOpts, key, value)
} }
...@@ -2,147 +2,120 @@ package genesis ...@@ -2,147 +2,120 @@ package genesis
import ( import (
"encoding/json" "encoding/json"
"fmt" "math/big"
"os" "os"
"strings" "strings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/urfave/cli" "github.com/urfave/cli"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-chain-ops/hardhat" "github.com/ethereum-optimism/optimism/op-chain-ops/hardhat"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/ethclient"
) )
var Subcommands = cli.Commands{ var Subcommands = cli.Commands{
{ {
Name: "devnet-l2", Name: "devnet",
Usage: "Initialized a new L2 devnet genesis file", Usage: "Initialize new L1 and L2 genesis files and rollup config suitable for a local devnet",
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "artifacts", Name: "artifacts",
Usage: "Comma delimeted list of hardhat artifact directories", Usage: "Comma delimited list of hardhat artifact directories",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "network", Name: "deploy-config",
Usage: "Name of hardhat deploy network", Usage: "Path to hardhat deploy config file",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "deployments", Name: "outfile.l1",
Usage: "Comma delimated list of hardhat deploy artifact directories", Usage: "Path to L1 genesis output file",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "deploy-config", Name: "outfile.l2",
Usage: "Path to hardhat deploy config directory", Usage: "Path to L2 genesis output file",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "rpc-url", Name: "outfile.rollup",
Usage: "L1 RPC URL", Usage: "Path to rollup output file",
},
cli.StringFlag{
Name: "outfile",
Usage: "Path to file to write output to",
}, },
}, },
Action: func(ctx *cli.Context) error { Action: func(ctx *cli.Context) error {
// Turn off logging for this command unless it is a critical
// error so that the output can be piped to jq
log.Root().SetHandler(
log.LvlFilterHandler(
log.LvlCrit,
log.StreamHandler(os.Stdout, log.TerminalFormat(true)),
),
)
artifact := ctx.String("artifacts") artifact := ctx.String("artifacts")
artifacts := strings.Split(artifact, ",") artifacts := strings.Split(artifact, ",")
deployment := ctx.String("deployments") hh, err := hardhat.New("", artifacts, nil)
deployments := strings.Split(deployment, ",")
network := ctx.String("network")
hh, err := hardhat.New(network, artifacts, deployments)
if err != nil { if err != nil {
return err return err
} }
deployConfig := ctx.String("deploy-config") deployConfig := ctx.String("deploy-config")
config, err := genesis.NewDeployConfigWithNetwork(network, deployConfig) config, err := genesis.NewDeployConfig(deployConfig)
if err != nil { if err != nil {
return err return err
} }
rpcUrl := ctx.String("rpc-url") l1Genesis, err := genesis.BuildL1DeveloperGenesis(hh, config)
client, err := ethclient.Dial(rpcUrl)
if err != nil { if err != nil {
return err return err
} }
gen, err := genesis.BuildOptimismDeveloperGenesis(hh, config, client) l1StartBlock := l1Genesis.ToBlock()
if err != nil { l2Addrs := &genesis.L2Addresses{
return err ProxyAdmin: predeploys.DevProxyAdminAddr,
L1StandardBridgeProxy: predeploys.DevL1StandardBridgeAddr,
L1CrossDomainMessengerProxy: predeploys.DevL1CrossDomainMessengerAddr,
} }
l2Genesis, err := genesis.BuildL2DeveloperGenesis(hh, config, l1StartBlock, l2Addrs)
file, err := json.MarshalIndent(gen, "", " ")
if err != nil { if err != nil {
return err return err
} }
outfile := ctx.String("outfile") rollupConfig := &rollup.Config{
if outfile == "" { Genesis: rollup.Genesis{
fmt.Println(string(file)) L1: eth.BlockID{
} else { Hash: l1StartBlock.Hash(),
if err := os.WriteFile(outfile, file, 0644); err != nil { Number: 0,
return err },
} L2: eth.BlockID{
} Hash: l2Genesis.ToBlock().Hash(),
return nil Number: 0,
}, },
}, L2Time: uint64(config.L1GenesisBlockTimestamp),
{ },
Name: "devnet-l1", BlockTime: config.L2BlockTime,
Usage: "Initialized a new L1 devnet genesis file", MaxSequencerDrift: config.MaxSequencerDrift,
Flags: []cli.Flag{ SeqWindowSize: config.SequencerWindowSize,
cli.StringFlag{ ChannelTimeout: config.ChannelTimeout,
Name: "network", L1ChainID: new(big.Int).SetUint64(config.L1ChainID),
Usage: "Name of hardhat deploy network", L2ChainID: new(big.Int).SetUint64(config.L2ChainID),
}, P2PSequencerAddress: config.P2PSequencerAddress,
cli.StringFlag{ FeeRecipientAddress: config.OptimismL2FeeRecipient,
Name: "deploy-config", BatchInboxAddress: config.BatchInboxAddress,
Usage: "Path to hardhat deploy config directory", BatchSenderAddress: config.BatchSenderAddress,
}, DepositContractAddress: predeploys.DevOptimismPortalAddr,
cli.StringFlag{
Name: "outfile",
Usage: "Path to file to write output to",
},
},
Action: func(ctx *cli.Context) error {
network := ctx.String("network")
deployConfig := ctx.String("deploy-config")
config, err := genesis.NewDeployConfigWithNetwork(network, deployConfig)
if err != nil {
return err
} }
gen, err := genesis.BuildL1DeveloperGenesis(config) if err := writeGenesisFile(ctx.String("outfile.l1"), l1Genesis); err != nil {
if err != nil {
return err return err
} }
if err := writeGenesisFile(ctx.String("outfile.l2"), l2Genesis); err != nil {
file, err := json.MarshalIndent(gen, "", " ")
if err != nil {
return err return err
} }
return writeGenesisFile(ctx.String("outfile.rollup"), rollupConfig)
outfile := ctx.String("outfile")
if outfile == "" {
fmt.Println(string(file))
} else {
if err := os.WriteFile(outfile, file, 0644); err != nil {
return err
}
}
return nil
}, },
}, },
} }
func writeGenesisFile(outfile string, input interface{}) error {
f, err := os.OpenFile(outfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o755)
if err != nil {
return err
}
defer f.Close()
enc := json.NewEncoder(f)
enc.SetIndent("", " ")
return enc.Encode(input)
}
...@@ -62,14 +62,21 @@ mkdir -p ./.devnet ...@@ -62,14 +62,21 @@ mkdir -p ./.devnet
# Regenerate the L1 genesis file if necessary. The existence of the genesis # Regenerate the L1 genesis file if necessary. The existence of the genesis
# file is used to determine if we need to recreate the devnet's state folder. # file is used to determine if we need to recreate the devnet's state folder.
if [ ! -f $DEVNET/genesis-l1.json ]; then if [ ! -f "$DEVNET/done" ]; then
echo "Regenerating L1 genesis." echo "Regenerating genesis files"
TIMESTAMP=$(date +%s | xargs printf '0x%x')
cat "$CONTRACTS_BEDROCK/deploy-config/devnetL1.json" | jq -r ".l1GenesisBlockTimestamp = \"$TIMESTAMP\"" > /tmp/bedrock-devnet-deploy-config.json
( (
cd "$OP_NODE" cd "$OP_NODE"
go run cmd/main.go genesis devnet-l1 \ go run cmd/main.go genesis devnet \
--network $NETWORK \ --artifacts "$CONTRACTS_BEDROCK/artifacts,$CONTRACTS_GOVERNANCE/artifacts" \
--deploy-config $CONTRACTS_BEDROCK/deploy-config \ --deploy-config /tmp/bedrock-devnet-deploy-config.json \
--outfile $DEVNET/genesis-l1.json --outfile.l1 $DEVNET/genesis-l1.json \
--outfile.l2 $DEVNET/genesis-l2.json \
--outfile.rollup $DEVNET/rollup.json
touch "$DEVNET/done"
) )
fi fi
...@@ -82,34 +89,6 @@ fi ...@@ -82,34 +89,6 @@ fi
wait_up $L1_URL wait_up $L1_URL
) )
# Deploy contracts using Hardhat.
if [ ! -d "$CONTRACTS_BEDROCK/deployments/$NETWORK" ]; then
(
echo "Deploying contracts."
cd "$CONTRACTS_BEDROCK"
yarn hardhat --network $NETWORK deploy
)
else
echo "Contracts already deployed, skipping."
fi
if [ ! -f "$DEVNET/genesis-l2.json" ]; then
(
echo "Creating L2 genesis file."
cd "$OP_NODE"
go run cmd/main.go genesis devnet-l2 \
--artifacts "$CONTRACTS_BEDROCK/artifacts,$CONTRACTS_GOVERNANCE/artifacts" \
--network $NETWORK \
--deployments "$CONTRACTS_BEDROCK/deployments" \
--deploy-config "$CONTRACTS_BEDROCK/deploy-config" \
--rpc-url http://localhost:8545 \
--outfile "$DEVNET/genesis-l2.json"
echo "Created L2 genesis."
)
else
echo "L2 genesis already exists."
fi
# Bring up L2. # Bring up L2.
( (
cd ops-bedrock cd ops-bedrock
...@@ -118,19 +97,7 @@ fi ...@@ -118,19 +97,7 @@ fi
wait_up $L2_URL wait_up $L2_URL
) )
# Start putting together the rollup config. L2OO_ADDRESS="0x6900000000000000000000000000000000000000"
if [ ! -f "$DEVNET/rollup.json" ]; then
(
echo "Building rollup config..."
cd "$CONTRACTS_BEDROCK"
npx hardhat --network $NETWORK rollup-config
mv rollup.json "$DEVNET/rollup.json"
)
else
echo "Rollup config already exists"
fi
L2OO_ADDRESS=$(jq -r .address < "$CONTRACTS_BEDROCK/deployments/$NETWORK/L2OutputOracleProxy.json")
SEQUENCER_GENESIS_HASH="$(jq -r '.l2.hash' < $DEVNET/rollup.json)" SEQUENCER_GENESIS_HASH="$(jq -r '.l2.hash' < $DEVNET/rollup.json)"
SEQUENCER_BATCH_INBOX_ADDRESS="$(cat $DEVNET/rollup.json | jq -r '.batch_inbox_address')" SEQUENCER_BATCH_INBOX_ADDRESS="$(cat $DEVNET/rollup.json | jq -r '.batch_inbox_address')"
......
...@@ -67,6 +67,13 @@ WORKDIR /opt/optimism/packages/contracts ...@@ -67,6 +67,13 @@ WORKDIR /opt/optimism/packages/contracts
COPY ./ops/scripts/deployer.sh . COPY ./ops/scripts/deployer.sh .
CMD ["yarn", "run", "deploy"] CMD ["yarn", "run", "deploy"]
FROM alpine:3.16.2 as contract-artifacts-bedrock
RUN mkdir -p /artifacts
WORKDIR /artifacts
COPY --from=base /opt/optimism/packages/contracts-bedrock/artifacts /artifacts/contracts-bedrock
COPY --from=base /opt/optimism/packages/contracts-governance/artifacts /artifacts/contracts-governance
CMD ["echo", "0"]
FROM base as deployer-bedrock FROM base as deployer-bedrock
WORKDIR /opt/optimism/packages/contracts-bedrock WORKDIR /opt/optimism/packages/contracts-bedrock
CMD ["yarn", "run", "deploy"] CMD ["yarn", "run", "deploy"]
......
{ {
"l1StartingBlockTag": "earliest",
"l1ChainID": 900, "l1ChainID": 900,
"l2ChainID": 901, "l2ChainID": 901,
"l2BlockTime": 2, "l2BlockTime": 2,
...@@ -16,6 +15,7 @@ ...@@ -16,6 +15,7 @@
"l2OutputOracleStartingTimestamp": -1, "l2OutputOracleStartingTimestamp": -1,
"l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"l2OutputOracleOwner": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "l2OutputOracleOwner": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"l2GenesisBlockCoinbase": "0x42000000000000000000000000000000000000f0",
"l1BlockTime": 15, "l1BlockTime": 15,
"cliqueSignerAddress": "0xca062b0fd91172d89bcd4bb084ac4e21972cc467", "cliqueSignerAddress": "0xca062b0fd91172d89bcd4bb084ac4e21972cc467",
......
...@@ -130,17 +130,17 @@ export const CONTRACT_ADDRESSES: { ...@@ -130,17 +130,17 @@ export const CONTRACT_ADDRESSES: {
}, },
[L2ChainID.OPTIMISM_BEDROCK_LOCAL_DEVNET]: { [L2ChainID.OPTIMISM_BEDROCK_LOCAL_DEVNET]: {
l1: { l1: {
AddressManager: '0x610178dA211FEF7D417bC0e6FeD39F05609AD788' as const, AddressManager: '0x6900000000000000000000000000000000000005' as const,
L1CrossDomainMessenger: L1CrossDomainMessenger:
'0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512' as const, '0x6900000000000000000000000000000000000002' as const,
L1StandardBridge: '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0' as const, L1StandardBridge: '0x6900000000000000000000000000000000000003' as const,
StateCommitmentChain: StateCommitmentChain:
'0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9' as const, '0x0000000000000000000000000000000000000000' as const,
CanonicalTransactionChain: CanonicalTransactionChain:
'0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9' as const, '0x0000000000000000000000000000000000000000' as const,
BondManager: '0x5FC8d32690cc91D4c39d9d3abcBD16989F875707' as const, BondManager: '0x0000000000000000000000000000000000000000' as const,
OptimismPortal: '0x0165878A594ca255338adfa4d48449f69242Eb8F' as const, OptimismPortal: '0x6900000000000000000000000000000000000001' as const,
L2OutputOracle: '0x5FbDB2315678afecb367f032d93F642f64180aa3' as const, L2OutputOracle: '0x6900000000000000000000000000000000000000' as const,
}, },
l2: DEFAULT_L2_CONTRACT_ADDRESSES, l2: DEFAULT_L2_CONTRACT_ADDRESSES,
}, },
......
...@@ -2,14 +2,13 @@ import { task, types } from 'hardhat/config' ...@@ -2,14 +2,13 @@ import { task, types } from 'hardhat/config'
import { HardhatRuntimeEnvironment } from 'hardhat/types' import { HardhatRuntimeEnvironment } from 'hardhat/types'
import '@nomiclabs/hardhat-ethers' import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy' import 'hardhat-deploy'
import { predeploys } from '@eth-optimism/contracts-bedrock' import {
predeploys,
getContractDefinition,
} from '@eth-optimism/contracts-bedrock'
import { Event, Contract, Wallet, providers, utils } from 'ethers' import { Event, Contract, Wallet, providers, utils } from 'ethers'
import { import { CrossChainMessenger, MessageStatus, CONTRACT_ADDRESSES } from '../src'
CrossChainMessenger,
StandardBridgeAdapter,
MessageStatus,
} from '../src'
const deployWETH9 = async ( const deployWETH9 = async (
hre: HardhatRuntimeEnvironment, hre: HardhatRuntimeEnvironment,
...@@ -18,7 +17,7 @@ const deployWETH9 = async ( ...@@ -18,7 +17,7 @@ const deployWETH9 = async (
const signers = await hre.ethers.getSigners() const signers = await hre.ethers.getSigners()
const signer = signers[0] const signer = signers[0]
const Artifact__WETH9 = await hre.deployments.getArtifact('WETH9') const Artifact__WETH9 = await getContractDefinition('WETH9')
const Factory__WETH9 = new hre.ethers.ContractFactory( const Factory__WETH9 = new hre.ethers.ContractFactory(
Artifact__WETH9.abi, Artifact__WETH9.abi,
Artifact__WETH9.bytecode, Artifact__WETH9.bytecode,
...@@ -44,15 +43,16 @@ const createOptimismMintableERC20 = async ( ...@@ -44,15 +43,16 @@ const createOptimismMintableERC20 = async (
L1ERC20: Contract, L1ERC20: Contract,
l2Signer: Wallet l2Signer: Wallet
): Promise<Contract> => { ): Promise<Contract> => {
const Deployment__OptimismMintableERC20TokenFactory = const Artifact__OptimismMintableERC20Token = await getContractDefinition(
await hre.deployments.get('OptimismMintableERC20Factory') 'OptimismMintableERC20'
)
const Artifact__OptimismMintableERC20Token = const Artifact__OptimismMintableERC20TokenFactory =
await hre.deployments.getArtifact('OptimismMintableERC20') await getContractDefinition('OptimismMintableERC20Factory')
const OptimismMintableERC20TokenFactory = await hre.ethers.getContractAt( const OptimismMintableERC20TokenFactory = new Contract(
Deployment__OptimismMintableERC20TokenFactory.abi,
predeploys.OptimismMintableERC20Factory, predeploys.OptimismMintableERC20Factory,
Artifact__OptimismMintableERC20TokenFactory.abi,
l2Signer l2Signer
) )
...@@ -80,13 +80,11 @@ const createOptimismMintableERC20 = async ( ...@@ -80,13 +80,11 @@ const createOptimismMintableERC20 = async (
const l2WethAddress = event.args.remoteToken const l2WethAddress = event.args.remoteToken
console.log(`Deployed to ${l2WethAddress}`) console.log(`Deployed to ${l2WethAddress}`)
const contract = new Contract( return new Contract(
l2WethAddress, l2WethAddress,
Artifact__OptimismMintableERC20Token.abi, Artifact__OptimismMintableERC20Token.abi,
l2Signer l2Signer
) )
return contract
} }
// TODO(tynes): this task could be modularized in the future // TODO(tynes): this task could be modularized in the future
...@@ -130,61 +128,48 @@ task('deposit-erc20', 'Deposits WETH9 onto L2.') ...@@ -130,61 +128,48 @@ task('deposit-erc20', 'Deposits WETH9 onto L2.')
l2Provider l2Provider
) )
const Deployment__L2OutputOracleProxy = await hre.deployments.get( const l2ChainId = await l2Signer.getChainId()
'L2OutputOracleProxy' const contractAddrs = CONTRACT_ADDRESSES[l2ChainId]
)
const Artifact__L2ToL1MessagePasser = await hre.deployments.getArtifact( const Artifact__L2ToL1MessagePasser = await getContractDefinition(
'L2ToL1MessagePasser' 'L2ToL1MessagePasser'
) )
const Artifact__L2CrossDomainMessenger = await hre.deployments.getArtifact( const Artifact__L2CrossDomainMessenger = await getContractDefinition(
'L2CrossDomainMessenger' 'L2CrossDomainMessenger'
) )
const Artifact__L2StandardBridge = await hre.deployments.getArtifact( const Artifact__L2StandardBridge = await getContractDefinition(
'L2StandardBridge' 'L2StandardBridge'
) )
const Deployment__OptimismPortal = await hre.deployments.get( const Artifact__OptimismPortal = await getContractDefinition(
'OptimismPortal' 'OptimismPortal'
) )
const Deployment__OptimismPortalProxy = await hre.deployments.get( const Artifact__L1CrossDomainMessenger = await getContractDefinition(
'OptimismPortalProxy'
)
const Deployment__L1StandardBridgeProxy = await hre.deployments.get(
'L1StandardBridgeProxy'
)
const Deployment__L1CrossDomainMessenger = await hre.deployments.get(
'L1CrossDomainMessenger' 'L1CrossDomainMessenger'
) )
const Deployment__L1CrossDomainMessengerProxy = await hre.deployments.get( const Artifact__L1StandardBridge = await getContractDefinition(
'L1CrossDomainMessengerProxy'
)
const Deployment__L1StandardBridge = await hre.deployments.get(
'L1StandardBridge' 'L1StandardBridge'
) )
const OptimismPortal = new hre.ethers.Contract( const OptimismPortal = new hre.ethers.Contract(
Deployment__OptimismPortalProxy.address, contractAddrs.l1.OptimismPortal,
Deployment__OptimismPortal.abi, Artifact__OptimismPortal.abi,
signer signer
) )
const L1CrossDomainMessenger = new hre.ethers.Contract( const L1CrossDomainMessenger = new hre.ethers.Contract(
Deployment__L1CrossDomainMessengerProxy.address, contractAddrs.l1.L1CrossDomainMessenger,
Deployment__L1CrossDomainMessenger.abi, Artifact__L1CrossDomainMessenger.abi,
signer signer
) )
const L1StandardBridge = new hre.ethers.Contract( const L1StandardBridge = new hre.ethers.Contract(
Deployment__L1StandardBridgeProxy.address, contractAddrs.l1.L1StandardBridge,
Deployment__L1StandardBridge.abi, Artifact__L1StandardBridge.abi,
signer signer
) )
...@@ -207,23 +192,7 @@ task('deposit-erc20', 'Deposits WETH9 onto L2.') ...@@ -207,23 +192,7 @@ task('deposit-erc20', 'Deposits WETH9 onto L2.')
l1SignerOrProvider: signer, l1SignerOrProvider: signer,
l2SignerOrProvider: l2Signer, l2SignerOrProvider: l2Signer,
l1ChainId: await signer.getChainId(), l1ChainId: await signer.getChainId(),
l2ChainId: await l2Signer.getChainId(), l2ChainId,
bridges: {
Standard: {
Adapter: StandardBridgeAdapter,
l1Bridge: Deployment__L1StandardBridgeProxy.address,
l2Bridge: predeploys.L2StandardBridge,
},
},
contracts: {
l1: {
L1StandardBridge: Deployment__L1StandardBridgeProxy.address,
L1CrossDomainMessenger:
Deployment__L1CrossDomainMessengerProxy.address,
L2OutputOracle: Deployment__L2OutputOracleProxy.address,
OptimismPortal: Deployment__OptimismPortalProxy.address,
},
},
bedrock: true, bedrock: true,
}) })
......
import { task, types } from 'hardhat/config' import { task, types } from 'hardhat/config'
import '@nomiclabs/hardhat-ethers' import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy' import 'hardhat-deploy'
import { predeploys } from '@eth-optimism/contracts-bedrock' import {
predeploys,
getContractDefinition,
} from '@eth-optimism/contracts-bedrock'
import { providers, utils } from 'ethers' import { providers, utils } from 'ethers'
import { import { CrossChainMessenger, MessageStatus, CONTRACT_ADDRESSES } from '../src'
CrossChainMessenger,
StandardBridgeAdapter,
MessageStatus,
} from '../src'
task('deposit-eth', 'Deposits WETH9 onto L2.') task('deposit-eth', 'Deposits WETH9 onto L2.')
.addParam( .addParam(
...@@ -56,10 +55,6 @@ task('deposit-eth', 'Deposits WETH9 onto L2.') ...@@ -56,10 +55,6 @@ task('deposit-eth', 'Deposits WETH9 onto L2.')
const l2Provider = new providers.StaticJsonRpcProvider(args.l2ProviderUrl) const l2Provider = new providers.StaticJsonRpcProvider(args.l2ProviderUrl)
const Deployment__L2OutputOracleProxy = await hre.deployments.get(
'L2OutputOracleProxy'
)
// send to self if not specified // send to self if not specified
const to = args.to ? args.to : address const to = args.to ? args.to : address
const amount = args.amount const amount = args.amount
...@@ -74,57 +69,48 @@ task('deposit-eth', 'Deposits WETH9 onto L2.') ...@@ -74,57 +69,48 @@ task('deposit-eth', 'Deposits WETH9 onto L2.')
l2Provider l2Provider
) )
const Artifact__L2ToL1MessagePasser = await hre.deployments.getArtifact( const l2ChainId = await l2Signer.getChainId()
const contractAddrs = CONTRACT_ADDRESSES[l2ChainId]
const Artifact__L2ToL1MessagePasser = await getContractDefinition(
'L2ToL1MessagePasser' 'L2ToL1MessagePasser'
) )
const Artifact__L2CrossDomainMessenger = await hre.deployments.getArtifact( const Artifact__L2CrossDomainMessenger = await getContractDefinition(
'L2CrossDomainMessenger' 'L2CrossDomainMessenger'
) )
const Artifact__L2StandardBridge = await hre.deployments.getArtifact( const Artifact__L2StandardBridge = await getContractDefinition(
'L2StandardBridge' 'L2StandardBridge'
) )
const Deployment__OptimismPortal = await hre.deployments.get( const Artifact__OptimismPortal = await getContractDefinition(
'OptimismPortal' 'OptimismPortal'
) )
const Deployment__OptimismPortalProxy = await hre.deployments.get( const Artifact__L1CrossDomainMessenger = await getContractDefinition(
'OptimismPortalProxy'
)
const Deployment__L1StandardBridgeProxy = await hre.deployments.get(
'L1StandardBridgeProxy'
)
const Deployment__L1CrossDomainMessenger = await hre.deployments.get(
'L1CrossDomainMessenger' 'L1CrossDomainMessenger'
) )
const Deployment__L1CrossDomainMessengerProxy = await hre.deployments.get( const Artifact__L1StandardBridge = await getContractDefinition(
'L1CrossDomainMessengerProxy'
)
const Deployment__L1StandardBridge = await hre.deployments.get(
'L1StandardBridge' 'L1StandardBridge'
) )
const OptimismPortal = new hre.ethers.Contract( const OptimismPortal = new hre.ethers.Contract(
Deployment__OptimismPortalProxy.address, contractAddrs.l1.OptimismPortal,
Deployment__OptimismPortal.abi, Artifact__OptimismPortal.abi,
signer signer
) )
const L1CrossDomainMessenger = new hre.ethers.Contract( const L1CrossDomainMessenger = new hre.ethers.Contract(
Deployment__L1CrossDomainMessengerProxy.address, contractAddrs.l1.L1CrossDomainMessenger,
Deployment__L1CrossDomainMessenger.abi, Artifact__L1CrossDomainMessenger.abi,
signer signer
) )
const L1StandardBridge = new hre.ethers.Contract( const L1StandardBridge = new hre.ethers.Contract(
Deployment__L1StandardBridgeProxy.address, contractAddrs.l1.L1StandardBridge,
Deployment__L1StandardBridge.abi, Artifact__L1StandardBridge.abi,
signer signer
) )
...@@ -147,23 +133,7 @@ task('deposit-eth', 'Deposits WETH9 onto L2.') ...@@ -147,23 +133,7 @@ task('deposit-eth', 'Deposits WETH9 onto L2.')
l1SignerOrProvider: signer, l1SignerOrProvider: signer,
l2SignerOrProvider: l2Signer, l2SignerOrProvider: l2Signer,
l1ChainId: await signer.getChainId(), l1ChainId: await signer.getChainId(),
l2ChainId: await l2Signer.getChainId(), l2ChainId,
bridges: {
Standard: {
Adapter: StandardBridgeAdapter,
l1Bridge: Deployment__L1StandardBridgeProxy.address,
l2Bridge: predeploys.L2StandardBridge,
},
},
contracts: {
l1: {
L1StandardBridge: Deployment__L1StandardBridgeProxy.address,
L1CrossDomainMessenger:
Deployment__L1CrossDomainMessengerProxy.address,
L2OutputOracle: Deployment__L2OutputOracleProxy.address,
OptimismPortal: Deployment__OptimismPortalProxy.address,
},
},
bedrock: true, bedrock: true,
}) })
...@@ -238,15 +208,19 @@ task('deposit-eth', 'Deposits WETH9 onto L2.') ...@@ -238,15 +208,19 @@ task('deposit-eth', 'Deposits WETH9 onto L2.')
) )
console.log('Waiting to be able to withdraw') console.log('Waiting to be able to withdraw')
setInterval(async () => { const interval = setInterval(async () => {
const currentStatus = await messenger.getMessageStatus(ethWithdrawReceipt) const currentStatus = await messenger.getMessageStatus(ethWithdrawReceipt)
console.log(`Message status: ${MessageStatus[currentStatus]}`) console.log(`Message status: ${MessageStatus[currentStatus]}`)
}, 3000) }, 3000)
await messenger.waitForMessageStatus( try {
ethWithdrawReceipt, await messenger.waitForMessageStatus(
MessageStatus.READY_FOR_RELAY ethWithdrawReceipt,
) MessageStatus.READY_FOR_RELAY
)
} finally {
clearInterval(interval)
}
const ethFinalize = await messenger.finalizeMessage(ethWithdrawReceipt) const ethFinalize = await messenger.finalizeMessage(ethWithdrawReceipt)
const ethFinalizeReceipt = await ethFinalize.wait() const ethFinalizeReceipt = await ethFinalize.wait()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment