Commit 3c188103 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

Merge pull request #2845 from ethereum-optimism/develop

Merge develop into master
parents 3cbf56d3 df5eb9e7
---
'@eth-optimism/contracts-bedrock': patch
---
Separate the owner and sequencer roles in the OutputOracle
---
'@eth-optimism/contracts-bedrock': patch
---
Fix bug in bedrock deploy scripts
---
'@eth-optimism/contracts-bedrock': patch
---
Add genesis script
---
'@eth-optimism/contracts-bedrock': minor
---
Add separate sequencer role to Oracle
---
'@eth-optimism/sdk': minor
---
Have SDK use L2 chain ID as the source of truth.
---
'@eth-optimism/contracts-bedrock': patch
---
Fix order of args to L2OO constructor
---
'@eth-optimism/core-utils': minor
---
Update geth's Genesis type to work with modern geth
---
'@eth-optimism/contracts-bedrock': patch
---
Fix for incorrect constructor args in deploy config
---
'@eth-optimism/integration-tests': patch
'@eth-optimism/contracts-governance': patch
'@eth-optimism/fault-detector': patch
'@eth-optimism/message-relayer': patch
---
Update SDK version and usage to account for new constructor
---
'@eth-optimism/contracts-bedrock': patch
---
Make the output oracle upgradeable.
---
'@eth-optimism/fault-detector': patch
---
Fix bug where FD would try to sync beyond local tip
---
'@eth-optimism/go-builder': patch
---
Upgrade golangci-lint version for go 1.18
---
'@eth-optimism/common-ts': minor
---
Add version to healthz for convenience
...@@ -410,6 +410,10 @@ jobs: ...@@ -410,6 +410,10 @@ jobs:
forge --version forge --version
forge snapshot --check forge snapshot --check
working_directory: packages/contracts-bedrock working_directory: packages/contracts-bedrock
- run:
name: storage snapshot
command: yarn storage-snapshot && git diff --exit-code .storage-layout
working_directory: packages/contracts-bedrock
- run: - run:
name: check go bindings name: check go bindings
command: make && git diff --exit-code command: make && git diff --exit-code
......
...@@ -7,6 +7,10 @@ coverage.json ...@@ -7,6 +7,10 @@ coverage.json
*.tsbuildinfo *.tsbuildinfo
yarn-error.log yarn-error.log
.yarn/*
!.yarn/releases
!.yarn/plugins
.pnp.*
dist dist
artifacts artifacts
......
This diff is collapsed.
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
yarn-path ".yarn/releases/yarn-1.22.19.cjs"
...@@ -3,7 +3,6 @@ COMPOSE_FILE=replica.yml:replica-shared.yml:replica-toml.yml ...@@ -3,7 +3,6 @@ COMPOSE_FILE=replica.yml:replica-shared.yml:replica-toml.yml
ETH_NETWORK=kovan ETH_NETWORK=kovan
DATA_TRANSPORT_LAYER__L1_RPC_ENDPOINT=WONT_WORK_UNLESS_YOU_PROVIDE_A_VALID_ETHEREUM_L1_ENDPOINT DATA_TRANSPORT_LAYER__L1_RPC_ENDPOINT=WONT_WORK_UNLESS_YOU_PROVIDE_A_VALID_ETHEREUM_L1_ENDPOINT
DATA_TRANSPORT_LAYER__L2_RPC_ENDPOINT=https://kovan.optimism.io DATA_TRANSPORT_LAYER__L2_RPC_ENDPOINT=https://kovan.optimism.io
REPLICA_HEALTHCHECK__ETH_NETWORK_RPC_PROVIDER=https://kovan.optimism.io
SEQUENCER_CLIENT_HTTP=https://kovan.optimism.io SEQUENCER_CLIENT_HTTP=https://kovan.optimism.io
SHARED_ENV_PATH=../envs/kovan SHARED_ENV_PATH=../envs/kovan
GCMODE=archive GCMODE=archive
...@@ -14,5 +13,7 @@ L2GETH_HTTP_PORT=9991 ...@@ -14,5 +13,7 @@ L2GETH_HTTP_PORT=9991
L2GETH_WS_PORT=9992 L2GETH_WS_PORT=9992
DTL_PORT=7878 DTL_PORT=7878
GETH_INIT_SCRIPT=check-for-chaindata-berlin.sh GETH_INIT_SCRIPT=check-for-chaindata-berlin.sh
HEALTHCHECK__REFERENCE_RPC_PROVIDER=https://kovan.optimism.io
HEALTHCHECK__TARGET_RPC_PROVIDER=http://l2geth-replica:8545
RESTART=unless-stopped RESTART=unless-stopped
...@@ -2,18 +2,19 @@ COMPOSE_PROJECT_NAME=op-replica ...@@ -2,18 +2,19 @@ COMPOSE_PROJECT_NAME=op-replica
COMPOSE_FILE=replica.yml:replica-shared.yml:replica-toml.yml COMPOSE_FILE=replica.yml:replica-shared.yml:replica-toml.yml
ETH_NETWORK=mainnet ETH_NETWORK=mainnet
DATA_TRANSPORT_LAYER__L1_RPC_ENDPOINT=WONT_WORK_UNLESS_YOU_PROVIDE_A_VALID_ETHEREUM_L1_ENDPOINT DATA_TRANSPORT_LAYER__L1_RPC_ENDPOINT=WONT_WORK_UNLESS_YOU_PROVIDE_A_VALID_ETHEREUM_L1_ENDPOINT
DATA_TRANSPORT_LAYER__L2_RPC_ENDPOINT=https://mainnet.optimism.io DATA_TRANSPORT_LAYER__L2_RPC_ENDPOINT=<infra-provider-l2-endpoint>
REPLICA_HEALTHCHECK__ETH_NETWORK_RPC_PROVIDER=https://mainnet.optimism.io
SEQUENCER_CLIENT_HTTP=https://mainnet.optimism.io SEQUENCER_CLIENT_HTTP=https://mainnet.optimism.io
SHARED_ENV_PATH=../envs/mainnet SHARED_ENV_PATH=../envs/mainnet
GCMODE=archive GCMODE=archive
L2GETH_IMAGE_TAG=0.5.14 L2GETH_IMAGE_TAG=0.5.22
DTL_IMAGE_TAG=0.5.21 DTL_IMAGE_TAG=0.5.36
HC_IMAGE_TAG=1.0.6 HC_IMAGE_TAG=1.0.6
L2GETH_HTTP_PORT=9991 L2GETH_HTTP_PORT=9991
L2GETH_WS_PORT=9992 L2GETH_WS_PORT=9992
DTL_PORT=7878 DTL_PORT=7878
GETH_INIT_SCRIPT=check-for-chaindata-berlin.sh GETH_INIT_SCRIPT=check-for-chaindata-berlin.sh
HEALTHCHECK__REFERENCE_RPC_PROVIDER=<infra-provider-l2-endpoiont>
HEALTHCHECK__TARGET_RPC_PROVIDER=http://l2geth-replica:8545
RESTART=unless-stopped RESTART=unless-stopped
...@@ -22,4 +23,4 @@ ROLLUP_BACKEND=l2 ...@@ -22,4 +23,4 @@ ROLLUP_BACKEND=l2
DATA_TRANSPORT_LAYER__DEFAULT_BACKEND=l2 DATA_TRANSPORT_LAYER__DEFAULT_BACKEND=l2
DATA_TRANSPORT_LAYER__L1_GAS_PRICE_BACKEND=l2 DATA_TRANSPORT_LAYER__L1_GAS_PRICE_BACKEND=l2
DATA_TRANSPORT_LAYER__SYNC_FROM_L1=false DATA_TRANSPORT_LAYER__SYNC_FROM_L1=false
DATA_TRANSPORT_LAYER__SYNC_FROM_L2=true DATA_TRANSPORT_LAYER__SYNC_FROM_L2=true
\ No newline at end of file
...@@ -8,9 +8,6 @@ configMapGenerator: ...@@ -8,9 +8,6 @@ configMapGenerator:
- name: l2geth-replica - name: l2geth-replica
envs: envs:
- ./l2geth-replica.env - ./l2geth-replica.env
- name: replica-healthcheck
envs:
- ./replica-healthcheck.env
- name: geth-scripts - name: geth-scripts
files: files:
- ./check-for-chaindata.sh - ./check-for-chaindata.sh
\ No newline at end of file
REPLICA_HEALTHCHECK__ETH_NETWORK=goerli-nightly
REPLICA_HEALTHCHECK__L2GETH_IMAGE_TAG=0.4.9
REPLICA_HEALTHCHECK__ETH_REPLICA_RPC_PROVIDER=http://l2geth-replica:8545
\ No newline at end of file
...@@ -8,6 +8,3 @@ configMapGenerator: ...@@ -8,6 +8,3 @@ configMapGenerator:
- name: l2geth-replica - name: l2geth-replica
envs: envs:
- ./l2geth-replica.env - ./l2geth-replica.env
- name: replica-healthcheck
envs:
- ./replica-healthcheck.env
REPLICA_HEALTHCHECK__ETH_NETWORK=kovan
REPLICA_HEALTHCHECK__L2GETH_IMAGE_TAG=0.4.9
REPLICA_HEALTHCHECK__ETH_NETWORK_RPC_PROVIDER=http://sequencer.default:8545
REPLICA_HEALTHCHECK__ETH_REPLICA_RPC_PROVIDER=http://l2geth-replica:8545
\ No newline at end of file
...@@ -8,6 +8,3 @@ configMapGenerator: ...@@ -8,6 +8,3 @@ configMapGenerator:
- name: l2geth-replica - name: l2geth-replica
envs: envs:
- ./l2geth-replica.env - ./l2geth-replica.env
- name: replica-healthcheck
envs:
- ./replica-healthcheck.env
REPLICA_HEALTHCHECK__ETH_NETWORK=mainnet
REPLICA_HEALTHCHECK__L2GETH_IMAGE_TAG=0.4.8
REPLICA_HEALTHCHECK__ETH_NETWORK_RPC_PROVIDER=http://sequencer.default:8545
REPLICA_HEALTHCHECK__ETH_REPLICA_RPC_PROVIDER=http://l2geth-replica:8545
\ No newline at end of file
...@@ -4,7 +4,7 @@ import { ...@@ -4,7 +4,7 @@ import {
TransactionResponse, TransactionResponse,
TransactionReceipt, TransactionReceipt,
} from '@ethersproject/providers' } from '@ethersproject/providers'
import { sleep } from '@eth-optimism/core-utils' import { getChainId, sleep } from '@eth-optimism/core-utils'
import { import {
CrossChainMessenger, CrossChainMessenger,
MessageStatus, MessageStatus,
...@@ -58,8 +58,6 @@ export class OptimismEnv { ...@@ -58,8 +58,6 @@ export class OptimismEnv {
} }
static async new(): Promise<OptimismEnv> { static async new(): Promise<OptimismEnv> {
const network = await l1Provider.getNetwork()
let bridgeOverrides: BridgeAdapterData let bridgeOverrides: BridgeAdapterData
if (envConfig.L1_STANDARD_BRIDGE) { if (envConfig.L1_STANDARD_BRIDGE) {
bridgeOverrides = { bridgeOverrides = {
...@@ -79,7 +77,8 @@ export class OptimismEnv { ...@@ -79,7 +77,8 @@ export class OptimismEnv {
const messenger = new CrossChainMessenger({ const messenger = new CrossChainMessenger({
l1SignerOrProvider: l1Wallet, l1SignerOrProvider: l1Wallet,
l2SignerOrProvider: l2Wallet, l2SignerOrProvider: l2Wallet,
l1ChainId: network.chainId, l1ChainId: await getChainId(l1Provider),
l2ChainId: await getChainId(l2Provider),
contracts: { contracts: {
l1: { l1: {
AddressManager: envConfig.ADDRESS_MANAGER, AddressManager: envConfig.ADDRESS_MANAGER,
......
...@@ -1391,6 +1391,7 @@ func TestFakedSyncProgress64Fast(t *testing.T) { testFakedSyncProgress(t, 64, F ...@@ -1391,6 +1391,7 @@ func TestFakedSyncProgress64Fast(t *testing.T) { testFakedSyncProgress(t, 64, F
func TestFakedSyncProgress64Light(t *testing.T) { testFakedSyncProgress(t, 64, LightSync) } func TestFakedSyncProgress64Light(t *testing.T) { testFakedSyncProgress(t, 64, LightSync) }
func testFakedSyncProgress(t *testing.T, protocol int, mode SyncMode) { func testFakedSyncProgress(t *testing.T, protocol int, mode SyncMode) {
t.Skip("Flakey tests unused by Optimism")
t.Parallel() t.Parallel()
tester := newTester() tester := newTester()
......
This diff is collapsed.
...@@ -402,7 +402,7 @@ func (cfg SystemConfig) start() (*System, error) { ...@@ -402,7 +402,7 @@ func (cfg SystemConfig) start() (*System, error) {
// Deploy Deposit Contract // Deploy Deposit Contract
deployerPrivKey, err := sys.wallet.PrivateKey(accounts.Account{ deployerPrivKey, err := sys.wallet.PrivateKey(accounts.Account{
URL: accounts.URL{ URL: accounts.URL{
Path: sys.cfg.DeployerHDPath, Path: cfg.DeployerHDPath,
}, },
}) })
if err != nil { if err != nil {
...@@ -425,6 +425,7 @@ func (cfg SystemConfig) start() (*System, error) { ...@@ -425,6 +425,7 @@ func (cfg SystemConfig) start() (*System, error) {
sys.cfg.L2OOCfg.L2StartingTimeStamp, sys.cfg.L2OOCfg.L2StartingTimeStamp,
sys.cfg.L2OOCfg.L2BlockTime, sys.cfg.L2OOCfg.L2BlockTime,
l2OutputSubmitterAddr, l2OutputSubmitterAddr,
crypto.PubkeyToAddress(deployerPrivKey.PublicKey),
) )
sys.cfg.DepositCFG.L2Oracle = sys.L2OOContractAddr sys.cfg.DepositCFG.L2Oracle = sys.L2OOContractAddr
if err != nil { if err != nil {
......
...@@ -57,6 +57,7 @@ const ( ...@@ -57,6 +57,7 @@ const (
l2OutputHDPath = "m/44'/60'/0'/0/3" l2OutputHDPath = "m/44'/60'/0'/0/3"
bssHDPath = "m/44'/60'/0'/0/4" bssHDPath = "m/44'/60'/0'/0/4"
p2pSignerHDPath = "m/44'/60'/0'/0/5" p2pSignerHDPath = "m/44'/60'/0'/0/5"
deployerHDPath = "m/44'/60'/0'/0/6"
) )
var ( var (
...@@ -81,19 +82,21 @@ func defaultSystemConfig(t *testing.T) SystemConfig { ...@@ -81,19 +82,21 @@ func defaultSystemConfig(t *testing.T) SystemConfig {
transactorHDPath: 10000000, transactorHDPath: 10000000,
l2OutputHDPath: 10000000, l2OutputHDPath: 10000000,
bssHDPath: 10000000, bssHDPath: 10000000,
deployerHDPath: 10000000,
}, },
DepositCFG: DepositContractConfig{ DepositCFG: DepositContractConfig{
FinalizationPeriod: big.NewInt(60 * 60 * 24), FinalizationPeriod: big.NewInt(60 * 60 * 24),
}, },
L2OOCfg: L2OOContractConfig{ L2OOCfg: L2OOContractConfig{
// L2 Start time is set based off of the L2 Genesis time // L2 Start time is set based off of the L2 Genesis time
SubmissionFrequency: big.NewInt(2), SubmissionFrequency: big.NewInt(4),
L2BlockTime: big.NewInt(2),
HistoricalTotalBlocks: big.NewInt(0), HistoricalTotalBlocks: big.NewInt(0),
}, },
L2OutputHDPath: l2OutputHDPath, L2OutputHDPath: l2OutputHDPath,
BatchSubmitterHDPath: bssHDPath, BatchSubmitterHDPath: bssHDPath,
P2PSignerHDPath: p2pSignerHDPath, P2PSignerHDPath: p2pSignerHDPath,
DeployerHDPath: l2OutputHDPath, DeployerHDPath: deployerHDPath,
CliqueSignerDerivationPath: cliqueSignerHDPath, CliqueSignerDerivationPath: cliqueSignerHDPath,
L1InfoPredeployAddress: predeploys.L1BlockAddr, L1InfoPredeployAddress: predeploys.L1BlockAddr,
L1BlockTime: 2, L1BlockTime: 2,
...@@ -789,7 +792,7 @@ func TestWithdrawals(t *testing.T) { ...@@ -789,7 +792,7 @@ func TestWithdrawals(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
// Wait for finalization and then create the Finalized Withdrawal Transaction // Wait for finalization and then create the Finalized Withdrawal Transaction
ctx, cancel = context.WithTimeout(context.Background(), 10*time.Duration(cfg.L1BlockTime)*time.Second) ctx, cancel = context.WithTimeout(context.Background(), 20*time.Duration(cfg.L1BlockTime)*time.Second)
defer cancel() defer cancel()
blockNumber, err := withdrawals.WaitForFinalizationPeriod(ctx, l1Client, sys.DepositContractAddr, receipt.BlockNumber) blockNumber, err := withdrawals.WaitForFinalizationPeriod(ctx, l1Client, sys.DepositContractAddr, receipt.BlockNumber)
require.Nil(t, err) require.Nil(t, err)
......
...@@ -33,6 +33,7 @@ L1_URL="http://localhost:8545" ...@@ -33,6 +33,7 @@ L1_URL="http://localhost:8545"
L2_URL="http://localhost:9545" L2_URL="http://localhost:9545"
CONTRACTS_BEDROCK=./packages/contracts-bedrock CONTRACTS_BEDROCK=./packages/contracts-bedrock
NETWORK=devnetL1
# Helper method that waits for a given URL to be up. Can't use # Helper method that waits for a given URL to be up. Can't use
# cURL's built-in retry logic because connection reset errors # cURL's built-in retry logic because connection reset errors
...@@ -46,7 +47,7 @@ function wait_up { ...@@ -46,7 +47,7 @@ function wait_up {
sleep 0.25 sleep 0.25
((i=i+1)) ((i=i+1))
if [ "$i" -eq 120 ]; then if [ "$i" -eq 200 ]; then
echo " Timeout!" >&2 echo " Timeout!" >&2
exit 0 exit 0
fi fi
...@@ -54,15 +55,24 @@ function wait_up { ...@@ -54,15 +55,24 @@ function wait_up {
echo "Done!" echo "Done!"
} }
mkdir -p ./.devnet
if [ ! -f ./.devnet/rollup.json ]; then
GENESIS_TIMESTAMP=$(date +%s | xargs printf "0x%x")
else
GENESIS_TIMESTAMP=$(jq '.genesis.l2_time' < .devnet/rollup.json)
fi
# 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/genesis-l1.json ]; then
echo "Regenerating L1 genesis." echo "Regenerating L1 genesis."
mkdir -p ./.devnet (
GENESIS_TIMESTAMP=$(date +%s | xargs printf "0x%x") cd $CONTRACTS_BEDROCK
jq ". | .timestamp = \"$GENESIS_TIMESTAMP\" " < ./ops-bedrock/genesis-l1.json > ./.devnet/genesis-l1.json L2OO_STARTING_BLOCK_TIMESTAMP=$GENESIS_TIMESTAMP npx hardhat genesis-l1 \
else --outfile genesis-l1.json
GENESIS_TIMESTAMP=$(jq -r '.timestamp' < ./.devnet/genesis-l1.json) mv genesis-l1.json ../../.devnet/genesis-l1.json
)
fi fi
# Bring up L1. # Bring up L1.
...@@ -75,43 +85,27 @@ fi ...@@ -75,43 +85,27 @@ fi
) )
# Deploy contracts using Hardhat. # Deploy contracts using Hardhat.
if [ ! -f $CONTRACTS_BEDROCK/deployments/devnetL1/OptimismPortal.json ]; then if [ ! -d $CONTRACTS_BEDROCK/deployments/$NETWORK ]; then
echo "Deploying contracts."
( (
echo "Deploying contracts."
cd $CONTRACTS_BEDROCK cd $CONTRACTS_BEDROCK
L2OO_STARTING_BLOCK_TIMESTAMP=$GENESIS_TIMESTAMP yarn hardhat --network devnetL1 deploy L2OO_STARTING_BLOCK_TIMESTAMP=$GENESIS_TIMESTAMP yarn hardhat --network $NETWORK deploy
) )
else else
echo "Contracts already deployed, skipping." echo "Contracts already deployed, skipping."
fi fi
function get_deployed_bytecode() { if [ ! -f ./.devnet/genesis-l2.json ]; then
echo $(jq -r .deployedBytecode $CONTRACTS_BEDROCK/artifacts/contracts/$1) (
} echo "Creating L2 genesis file."
cd $CONTRACTS_BEDROCK
# Pull out the necessary bytecode/addresses from the artifacts/deployments. L2OO_STARTING_BLOCK_TIMESTAMP=$GENESIS_TIMESTAMP npx hardhat --network $NETWORK genesis-l2
L2_TO_L1_MESSAGE_PASSER_BYTECODE=$(get_deployed_bytecode L2/L2ToL1MessagePasser.sol/L2ToL1MessagePasser.json) mv genesis.json ../../.devnet/genesis-l2.json
L2_CROSS_DOMAIN_MESSENGER_BYTECODE=$(get_deployed_bytecode L2/L2CrossDomainMessenger.sol/L2CrossDomainMessenger.json) echo "Created L2 genesis."
OPTIMISM_MINTABLE_TOKEN_FACTORY_BYTECODE=$(get_deployed_bytecode universal/OptimismMintableTokenFactory.sol/OptimismMintableTokenFactory.json) )
L2_STANDARD_BRIDGE_BYTECODE=$(get_deployed_bytecode L2/L2StandardBridge.sol/L2StandardBridge.json) else
L1_BLOCK_INFO_BYTECODE=$(get_deployed_bytecode L2/L1Block.sol/L1Block.json) echo "L2 genesis already exists."
fi
DEPOSIT_CONTRACT_ADDRESS=$(jq -r .address < $CONTRACTS_BEDROCK/deployments/devnetL1/OptimismPortal.json)
L2OO_ADDRESS=$(jq -r .address < $CONTRACTS_BEDROCK/deployments/devnetL1/L2OutputOracle.json)
# Replace values in the L2 genesis file. It doesn't matter if this gets run every time,
# since the replaced values will be the same.
jq ". | .alloc.\"4200000000000000000000000000000000000015\".code = \"$L1_BLOCK_INFO_BYTECODE\"" < ./ops-bedrock/genesis-l2.json | \
jq ". | .alloc.\"4200000000000000000000000000000000000015\".balance = \"0x0\"" | \
jq ". | .alloc.\"4200000000000000000000000000000000000000\".code = \"$L2_TO_L1_MESSAGE_PASSER_BYTECODE\"" | \
jq ". | .alloc.\"4200000000000000000000000000000000000000\".balance = \"0x0\"" | \
jq ". | .alloc.\"4200000000000000000000000000000000000007\".code = \"$L2_CROSS_DOMAIN_MESSENGER_BYTECODE\"" | \
jq ". | .alloc.\"4200000000000000000000000000000000000007\".balance = \"0x0\"" | \
jq ". | .alloc.\"4200000000000000000000000000000000000012\".code = \"$OPTIMISM_MINTABLE_TOKEN_FACTORY_BYTECODE\"" | \
jq ". | .alloc.\"4200000000000000000000000000000000000012\".balance = \"0x0\"" | \
jq ". | .alloc.\"4200000000000000000000000000000000000010\".code = \"$L2_STANDARD_BRIDGE_BYTECODE\"" | \
jq ". | .alloc.\"4200000000000000000000000000000000000010\".balance = \"0x0\"" | \
jq ". | .timestamp = \"$GENESIS_TIMESTAMP\" " > ./.devnet/genesis-l2.json
# Bring up L2. # Bring up L2.
( (
...@@ -122,40 +116,19 @@ jq ". | .alloc.\"4200000000000000000000000000000000000015\".code = \"$L1_BLOCK_I ...@@ -122,40 +116,19 @@ jq ". | .alloc.\"4200000000000000000000000000000000000015\".code = \"$L1_BLOCK_I
) )
# Start putting together the rollup config. # Start putting together the rollup config.
echo "Building rollup config..." if [ ! -f ./.devnet/rollup.json ]; then
(
# Grab the L1 genesis. We can use cURL here to retry. echo "Building rollup config..."
L1_GENESIS=$(curl \ cd $CONTRACTS_BEDROCK
--silent \ L2OO_STARTING_BLOCK_TIMESTAMP=$GENESIS_TIMESTAMP npx hardhat rollup-config --network $NETWORK
--fail \ mv rollup.json ../../.devnet/rollup.json
--retry 10 \ )
--retry-delay 2 \ else
--retry-connrefused \ echo "Rollup config already exists"
-X POST \ fi
-H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x0", false],"id":1}' \ L2OO_ADDRESS=$(jq -r .address < $CONTRACTS_BEDROCK/deployments/$NETWORK/L2OutputOracleProxy.json)
$L1_URL) SEQUENCER_GENESIS_HASH="$(jq -r '.l2.hash' < .devnet/rollup.json)"
# Grab the L2 genesis. We can use cURL here to retry.
L2_GENESIS=$(curl \
--silent \
--fail \
--retry 10 \
--retry-delay 2 \
--retry-connrefused \
-X POST \
-H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x0", false],"id":1}' \
$L2_URL)
# Generate the rollup config.
jq ". | .genesis.l1.hash = \"$(echo $L1_GENESIS | jq -r '.result.hash')\"" < ./ops-bedrock/rollup.json | \
jq ". | .genesis.l2.hash = \"$(echo $L2_GENESIS | jq -r '.result.hash')\"" | \
jq ". | .genesis.l2_time = $(echo $L2_GENESIS | jq -r '.result.timestamp' | xargs printf "%d")" | \
jq ". | .deposit_contract_address = \"$DEPOSIT_CONTRACT_ADDRESS\"" > ./.devnet/rollup.json
SEQUENCER_GENESIS_HASH="$(echo $L2_GENESIS | jq -r '.result.hash')"
SEQUENCER_BATCH_INBOX_ADDRESS="$(cat ./ops-bedrock/rollup.json | jq -r '.batch_inbox_address')" SEQUENCER_BATCH_INBOX_ADDRESS="$(cat ./ops-bedrock/rollup.json | jq -r '.batch_inbox_address')"
# Bring up everything else. # Bring up everything else.
......
This diff is collapsed.
This diff is collapsed.
...@@ -6,6 +6,6 @@ COPY --from=geth /usr/local/bin/abigen /usr/local/bin/abigen ...@@ -6,6 +6,6 @@ COPY --from=geth /usr/local/bin/abigen /usr/local/bin/abigen
RUN apk add --no-cache make gcc musl-dev linux-headers git jq curl bash gzip ca-certificates openssh && \ RUN apk add --no-cache make gcc musl-dev linux-headers git jq curl bash gzip ca-certificates openssh && \
go install gotest.tools/gotestsum@latest && \ go install gotest.tools/gotestsum@latest && \
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.45.2 curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.46.2
CMD ["bash"] CMD ["bash"]
...@@ -41,7 +41,8 @@ ...@@ -41,7 +41,8 @@
"postinstall": "patch-package", "postinstall": "patch-package",
"ready": "yarn lint && yarn test", "ready": "yarn lint && yarn test",
"prepare": "husky install", "prepare": "husky install",
"release": "yarn build && yarn changeset publish" "release": "yarn build && yarn changeset publish",
"update:yarn": "yarn set version 1.x"
}, },
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "^7.18.2", "@babel/eslint-parser": "^7.18.2",
......
...@@ -144,17 +144,19 @@ export abstract class BaseServiceV2< ...@@ -144,17 +144,19 @@ export abstract class BaseServiceV2<
* @param params.port Port for the app server. Defaults to 7300. * @param params.port Port for the app server. Defaults to 7300.
* @param params.hostname Hostname for the app server. Defaults to 0.0.0.0. * @param params.hostname Hostname for the app server. Defaults to 0.0.0.0.
*/ */
constructor(params: { constructor(
name: string private readonly params: {
version: string name: string
optionsSpec: OptionsSpec<TOptions> version: string
metricsSpec: MetricsSpec<TMetrics> optionsSpec: OptionsSpec<TOptions>
options?: Partial<TOptions> metricsSpec: MetricsSpec<TMetrics>
loop?: boolean options?: Partial<TOptions>
loopIntervalMs?: number loop?: boolean
port?: number loopIntervalMs?: number
hostname?: string port?: number
}) { hostname?: string
}
) {
this.loop = params.loop !== undefined ? params.loop : true this.loop = params.loop !== undefined ? params.loop : true
this.state = {} as TServiceState this.state = {} as TServiceState
...@@ -404,6 +406,7 @@ export abstract class BaseServiceV2< ...@@ -404,6 +406,7 @@ export abstract class BaseServiceV2<
app.get('/healthz', async (req, res) => { app.get('/healthz', async (req, res) => {
return res.json({ return res.json({
ok: this.healthy, ok: this.healthy,
version: this.params.version,
}) })
}) })
...@@ -429,7 +432,7 @@ export abstract class BaseServiceV2< ...@@ -429,7 +432,7 @@ export abstract class BaseServiceV2<
} }
return '/invalid_path_not_a_real_route' return '/invalid_path_not_a_real_route'
} },
}) })
) )
......
...@@ -8,3 +8,4 @@ forge-artifacts ...@@ -8,3 +8,4 @@ forge-artifacts
cache cache
typechain typechain
coverage* coverage*
deployments
...@@ -39,7 +39,7 @@ L1StandardBridge_Test:test_depositERC20() (gas: 452856) ...@@ -39,7 +39,7 @@ L1StandardBridge_Test:test_depositERC20() (gas: 452856)
L1StandardBridge_Test:test_depositERC20To() (gas: 454632) L1StandardBridge_Test:test_depositERC20To() (gas: 454632)
L1StandardBridge_Test:test_depositETH() (gas: 247077) L1StandardBridge_Test:test_depositETH() (gas: 247077)
L1StandardBridge_Test:test_depositETHTo() (gas: 204961) L1StandardBridge_Test:test_depositETHTo() (gas: 204961)
L1StandardBridge_Test:test_finalizeERC20Withdrawal() (gas: 438752) L1StandardBridge_Test:test_finalizeERC20Withdrawal() (gas: 438745)
L1StandardBridge_Test:test_finalizeETHWithdrawal() (gas: 48005) L1StandardBridge_Test:test_finalizeETHWithdrawal() (gas: 48005)
L1StandardBridge_Test:test_initialize() (gas: 14885) L1StandardBridge_Test:test_initialize() (gas: 14885)
L1StandardBridge_Test:test_onlyEOADepositERC20() (gas: 12085) L1StandardBridge_Test:test_onlyEOADepositERC20() (gas: 12085)
...@@ -57,21 +57,27 @@ L2CrossDomainMessenger_Test:test_L2MessengerSendMessage() (gas: 119682) ...@@ -57,21 +57,27 @@ L2CrossDomainMessenger_Test:test_L2MessengerSendMessage() (gas: 119682)
L2CrossDomainMessenger_Test:test_L2MessengerTwiceSendMessage() (gas: 133142) L2CrossDomainMessenger_Test:test_L2MessengerTwiceSendMessage() (gas: 133142)
L2CrossDomainMessenger_Test:test_L2MessengerXDomainSenderReverts() (gas: 10599) L2CrossDomainMessenger_Test:test_L2MessengerXDomainSenderReverts() (gas: 10599)
L2CrossDomainMessenger_Test:test_L2MessengerxDomainMessageSenderResets() (gas: 54881) L2CrossDomainMessenger_Test:test_L2MessengerxDomainMessageSenderResets() (gas: 54881)
L2OutputOracleTest:testCannot_appendEmptyOutput() (gas: 18442) L2OutputOracleTest:testCannot_appendEmptyOutput() (gas: 18216)
L2OutputOracleTest:testCannot_appendFutureTimetamp() (gas: 20072) L2OutputOracleTest:testCannot_appendFutureTimetamp() (gas: 20183)
L2OutputOracleTest:testCannot_appendOnWrongFork() (gas: 20710) L2OutputOracleTest:testCannot_appendOnWrongFork() (gas: 20462)
L2OutputOracleTest:testCannot_appendOutputIfNotSequencer() (gas: 17829) L2OutputOracleTest:testCannot_appendOutputIfNotSequencer() (gas: 17607)
L2OutputOracleTest:testCannot_appendUnexpectedBlockNumber() (gas: 20313) L2OutputOracleTest:testCannot_appendUnexpectedBlockNumber() (gas: 20086)
L2OutputOracleTest:testCannot_deleteL2Output_ifNotSequencer() (gas: 18805) L2OutputOracleTest:testCannot_deleteL2Output_ifNotOwner() (gas: 18893)
L2OutputOracleTest:testCannot_deleteWrongL2Output() (gas: 79498) L2OutputOracleTest:testCannot_deleteL2Output_withWrongRoot() (gas: 83353)
L2OutputOracleTest:test_appendWithBlockhashAndHeight() (gas: 69365) L2OutputOracleTest:testCannot_deleteL2Output_withWrongTime() (gas: 79346)
L2OutputOracleTest:test_appendingAnotherOutput() (gas: 70714) L2OutputOracleTest:test_appendWithBlockhashAndHeight() (gas: 69126)
L2OutputOracleTest:test_computeL2Timestamp() (gas: 19230) L2OutputOracleTest:test_appendingAnotherOutput() (gas: 70475)
L2OutputOracleTest:test_constructor() (gas: 33908) L2OutputOracleTest:test_changeSequencer() (gas: 35473)
L2OutputOracleTest:test_deleteL2Output() (gas: 66081) L2OutputOracleTest:test_computeL2Timestamp() (gas: 19323)
L2OutputOracleTest:test_getL2Output() (gas: 76274) L2OutputOracleTest:test_constructor() (gas: 40152)
L2OutputOracleTest:test_latestBlockNumber() (gas: 70075) L2OutputOracleTest:test_deleteL2Output() (gas: 69237)
L2OutputOracleTest:test_nextBlockNumber() (gas: 9279) L2OutputOracleTest:test_getL2Output() (gas: 76099)
L2OutputOracleTest:test_latestBlockNumber() (gas: 69878)
L2OutputOracleTest:test_nextBlockNumber() (gas: 9281)
L2OutputOracleTest:test_updateOwner() (gas: 24185)
L2OutputOracleUpgradeable_Test:test_cannotInitImpl() (gas: 8431)
L2OutputOracleUpgradeable_Test:test_cannotInitProxy() (gas: 13430)
L2OutputOracleUpgradeable_Test:test_initValuesOnProxy() (gas: 38906)
L2StandardBridge_Test:test_ERC20BridgeFailed_whenLocalTokenIsBridge() (gas: 133097) L2StandardBridge_Test:test_ERC20BridgeFailed_whenLocalTokenIsBridge() (gas: 133097)
L2StandardBridge_Test:test_cannotWithdrawEthWithoutSendingIt() (gas: 21611) L2StandardBridge_Test:test_cannotWithdrawEthWithoutSendingIt() (gas: 21611)
L2StandardBridge_Test:test_finalizeDeposit() (gas: 93100) L2StandardBridge_Test:test_finalizeDeposit() (gas: 93100)
...@@ -110,7 +116,7 @@ OptimismMintableTokenFactory_Test:test_initializeShouldRevert() (gas: 12696) ...@@ -110,7 +116,7 @@ OptimismMintableTokenFactory_Test:test_initializeShouldRevert() (gas: 12696)
OptimismPortal_Test:test_OptimismPortalConstructor() (gas: 11413) OptimismPortal_Test:test_OptimismPortalConstructor() (gas: 11413)
OptimismPortal_Test:test_OptimismPortalContractCreationReverts() (gas: 9214) OptimismPortal_Test:test_OptimismPortalContractCreationReverts() (gas: 9214)
OptimismPortal_Test:test_OptimismPortalReceiveEth() (gas: 121706) OptimismPortal_Test:test_OptimismPortalReceiveEth() (gas: 121706)
OptimismPortal_Test:test_cannotVerifyRecentWithdrawal() (gas: 21886) OptimismPortal_Test:test_cannotVerifyRecentWithdrawal() (gas: 21908)
OptimismPortal_Test:test_depositTransaction_NoValueContract() (gas: 70746) OptimismPortal_Test:test_depositTransaction_NoValueContract() (gas: 70746)
OptimismPortal_Test:test_depositTransaction_NoValueEOA() (gas: 71114) OptimismPortal_Test:test_depositTransaction_NoValueEOA() (gas: 71114)
OptimismPortal_Test:test_depositTransaction_createWithZeroValueForContract() (gas: 70773) OptimismPortal_Test:test_depositTransaction_createWithZeroValueForContract() (gas: 70773)
...@@ -119,7 +125,7 @@ OptimismPortal_Test:test_depositTransaction_withEthValueAndContractContractCreat ...@@ -119,7 +125,7 @@ OptimismPortal_Test:test_depositTransaction_withEthValueAndContractContractCreat
OptimismPortal_Test:test_depositTransaction_withEthValueAndEOAContractCreation() (gas: 69947) OptimismPortal_Test:test_depositTransaction_withEthValueAndEOAContractCreation() (gas: 69947)
OptimismPortal_Test:test_depositTransaction_withEthValueFromContract() (gas: 77478) OptimismPortal_Test:test_depositTransaction_withEthValueFromContract() (gas: 77478)
OptimismPortal_Test:test_depositTransaction_withEthValueFromEOA() (gas: 78049) OptimismPortal_Test:test_depositTransaction_withEthValueFromEOA() (gas: 78049)
OptimismPortal_Test:test_invalidWithdrawalProof() (gas: 33702) OptimismPortal_Test:test_invalidWithdrawalProof() (gas: 33769)
Proxy_Test:test_clashingFunctionSignatures() (gas: 101427) Proxy_Test:test_clashingFunctionSignatures() (gas: 101427)
Proxy_Test:test_implementationKey() (gas: 20942) Proxy_Test:test_implementationKey() (gas: 20942)
Proxy_Test:test_implementationProxyCallIfNotAdmin() (gas: 30021) Proxy_Test:test_implementationProxyCallIfNotAdmin() (gas: 30021)
......
...@@ -9,6 +9,7 @@ forge-artifacts ...@@ -9,6 +9,7 @@ forge-artifacts
cache cache
typechain typechain
coverage* coverage*
deployments
contracts/L2/WETH9.sol contracts/L2/WETH9.sol
......
This diff is collapsed.
//SPDX-License-Identifier: MIT //SPDX-License-Identifier: MIT
pragma solidity 0.8.10; pragma solidity 0.8.10;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import {
OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
/** /**
* @title L2OutputOracle * @title L2OutputOracle
...@@ -10,7 +12,12 @@ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; ...@@ -10,7 +12,12 @@ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
* This contract should be deployed behind an upgradable proxy * This contract should be deployed behind an upgradable proxy
*/ */
// slither-disable-next-line locked-ether // slither-disable-next-line locked-ether
contract L2OutputOracle is Ownable { contract L2OutputOracle is OwnableUpgradeable {
/**
* @notice Contract version number.
*/
uint8 public constant L2_OUTPUT_ORACLE_VERSION = 1;
/** /**
* @notice OutputProposal represents a commitment to the L2 state. * @notice OutputProposal represents a commitment to the L2 state.
* The timestamp is the L1 timestamp that the output root is posted. * The timestamp is the L1 timestamp that the output root is posted.
...@@ -48,6 +55,14 @@ contract L2OutputOracle is Ownable { ...@@ -48,6 +55,14 @@ contract L2OutputOracle is Ownable {
uint256 indexed _l2BlockNumber uint256 indexed _l2BlockNumber
); );
/**
* @notice Emitted when the sequencer address is changed.
*
* @param previousSequencer The previous sequencer address.
* @param newSequencer The new sequencer address.
*/
event SequencerChanged(address indexed previousSequencer, address indexed newSequencer);
/** /**
* @notice The interval in L2 blocks at which checkpoints must be submitted. * @notice The interval in L2 blocks at which checkpoints must be submitted.
*/ */
...@@ -73,6 +88,11 @@ contract L2OutputOracle is Ownable { ...@@ -73,6 +88,11 @@ contract L2OutputOracle is Ownable {
*/ */
uint256 public immutable L2_BLOCK_TIME; uint256 public immutable L2_BLOCK_TIME;
/**
* @notice The address of the sequencer;
*/
address public sequencer;
/** /**
* @notice The number of the most recent L2 block recorded in this contract. * @notice The number of the most recent L2 block recorded in this contract.
*/ */
...@@ -85,6 +105,14 @@ contract L2OutputOracle is Ownable { ...@@ -85,6 +105,14 @@ contract L2OutputOracle is Ownable {
*/ */
mapping(uint256 => OutputProposal) internal l2Outputs; mapping(uint256 => OutputProposal) internal l2Outputs;
/**
* @notice Reverts if called by any account other than the sequencer.
*/
modifier onlySequencer() {
require(sequencer == msg.sender, "OutputOracle: caller is not the sequencer");
_;
}
/** /**
* @notice Initialize the L2OutputOracle contract. * @notice Initialize the L2OutputOracle contract.
* *
...@@ -97,6 +125,7 @@ contract L2OutputOracle is Ownable { ...@@ -97,6 +125,7 @@ contract L2OutputOracle is Ownable {
* @param _startingTimestamp The timestamp of the first L2 block. * @param _startingTimestamp The timestamp of the first L2 block.
* @param _l2BlockTime The timestamp of the first L2 block. * @param _l2BlockTime The timestamp of the first L2 block.
* @param _sequencer The address of the sequencer. * @param _sequencer The address of the sequencer.
* @param _owner The address of the owner.
*/ */
constructor( constructor(
uint256 _submissionInterval, uint256 _submissionInterval,
...@@ -105,7 +134,8 @@ contract L2OutputOracle is Ownable { ...@@ -105,7 +134,8 @@ contract L2OutputOracle is Ownable {
uint256 _startingBlockNumber, uint256 _startingBlockNumber,
uint256 _startingTimestamp, uint256 _startingTimestamp,
uint256 _l2BlockTime, uint256 _l2BlockTime,
address _sequencer address _sequencer,
address _owner
) { ) {
require( require(
_l2BlockTime < block.timestamp, _l2BlockTime < block.timestamp,
...@@ -117,10 +147,29 @@ contract L2OutputOracle is Ownable { ...@@ -117,10 +147,29 @@ contract L2OutputOracle is Ownable {
STARTING_TIMESTAMP = _startingTimestamp; STARTING_TIMESTAMP = _startingTimestamp;
L2_BLOCK_TIME = _l2BlockTime; L2_BLOCK_TIME = _l2BlockTime;
initialize(_genesisL2Output, _startingBlockNumber, _sequencer, _owner);
}
/**
* @notice Initialize the L2OutputOracle contract.
*
* @param _genesisL2Output The initial L2 output of the L2 chain.
* @param _startingBlockNumber The timestamp to start L2 block at.
* @param _sequencer The address of the sequencer.
* @param _owner The address of the owner.
*/
function initialize(
bytes32 _genesisL2Output,
uint256 _startingBlockNumber,
address _sequencer,
address _owner
) public reinitializer(L2_OUTPUT_ORACLE_VERSION) {
l2Outputs[_startingBlockNumber] = OutputProposal(_genesisL2Output, block.timestamp); l2Outputs[_startingBlockNumber] = OutputProposal(_genesisL2Output, block.timestamp);
latestBlockNumber = _startingBlockNumber; latestBlockNumber = _startingBlockNumber;
__Ownable_init();
_transferOwnership(_sequencer); changeSequencer(_sequencer);
_transferOwnership(_owner);
} }
/** /**
...@@ -139,7 +188,7 @@ contract L2OutputOracle is Ownable { ...@@ -139,7 +188,7 @@ contract L2OutputOracle is Ownable {
uint256 _l2BlockNumber, uint256 _l2BlockNumber,
bytes32 _l1Blockhash, bytes32 _l1Blockhash,
uint256 _l1BlockNumber uint256 _l1BlockNumber
) external payable onlyOwner { ) external payable onlySequencer {
require( require(
_l2BlockNumber == nextBlockNumber(), _l2BlockNumber == nextBlockNumber(),
"OutputOracle: Block number must be equal to next expected block number." "OutputOracle: Block number must be equal to next expected block number."
...@@ -172,8 +221,11 @@ contract L2OutputOracle is Ownable { ...@@ -172,8 +221,11 @@ contract L2OutputOracle is Ownable {
} }
/** /**
* @notice Deletes the most recent output. * @notice Deletes the most recent output. This is used to remove the most recent output in the
* * event that an erreneous output is submitted. It can only be called by the contract's
* owner, not the sequencer. Longer term, this should be replaced with a more robust
* mechanism which will allow deletion of proposals shown to be invalid by a fault
* proof.
* @param _proposal Represents the output proposal to delete * @param _proposal Represents the output proposal to delete
*/ */
function deleteL2Output(OutputProposal memory _proposal) external onlyOwner { function deleteL2Output(OutputProposal memory _proposal) external onlyOwner {
...@@ -230,4 +282,15 @@ contract L2OutputOracle is Ownable { ...@@ -230,4 +282,15 @@ contract L2OutputOracle is Ownable {
return return
STARTING_TIMESTAMP + ((_l2BlockNumber - STARTING_BLOCK_NUMBER) * SUBMISSION_INTERVAL); STARTING_TIMESTAMP + ((_l2BlockNumber - STARTING_BLOCK_NUMBER) * SUBMISSION_INTERVAL);
} }
/**
* @notice Transfers the sequencer role to a new account (`newSequencer`).
* Can only be called by the current owner.
*/
function changeSequencer(address _newSequencer) public onlyOwner {
require(_newSequencer != address(0), "OutputOracle: new sequencer is the zero address");
require(_newSequencer != owner(), "OutputOracle: sequencer cannot be same as the owner");
emit SequencerChanged(sequencer, _newSequencer);
sequencer = _newSequencer;
}
} }
...@@ -42,18 +42,20 @@ contract CommonTest is Test { ...@@ -42,18 +42,20 @@ contract CommonTest is Test {
vm.fee(1000000000); vm.fee(1000000000);
} }
} }
contract L2OutputOracle_Initializer is CommonTest { contract L2OutputOracle_Initializer is CommonTest {
// Test target // Test target
L2OutputOracle oracle; L2OutputOracle oracle;
// Constructor arguments // Constructor arguments
uint256 submissionInterval = 42; address sequencer = 0x000000000000000000000000000000000000AbBa;
address owner = 0x000000000000000000000000000000000000ACDC;
uint256 submissionInterval = 1800;
uint256 l2BlockTime = 2;
bytes32 genesisL2Output = keccak256(abi.encode(0)); bytes32 genesisL2Output = keccak256(abi.encode(0));
uint256 historicalTotalBlocks = 199; uint256 historicalTotalBlocks = 199;
uint256 startingBlockNumber = 200; uint256 startingBlockNumber = 200;
uint256 startingTimestamp = 1000; uint256 startingTimestamp = 1000;
uint256 l2BlockTime = 2;
address sequencer = 0x000000000000000000000000000000000000AbBa;
// Test data // Test data
uint256 initL1Time; uint256 initL1Time;
...@@ -74,7 +76,8 @@ contract L2OutputOracle_Initializer is CommonTest { ...@@ -74,7 +76,8 @@ contract L2OutputOracle_Initializer is CommonTest {
startingBlockNumber, startingBlockNumber,
startingTimestamp, startingTimestamp,
l2BlockTime, l2BlockTime,
sequencer sequencer,
owner
); );
} }
} }
...@@ -82,8 +85,10 @@ contract L2OutputOracle_Initializer is CommonTest { ...@@ -82,8 +85,10 @@ contract L2OutputOracle_Initializer is CommonTest {
contract Messenger_Initializer is L2OutputOracle_Initializer { contract Messenger_Initializer is L2OutputOracle_Initializer {
OptimismPortal op; OptimismPortal op;
L1CrossDomainMessenger L1Messenger; L1CrossDomainMessenger L1Messenger;
L2CrossDomainMessenger L2Messenger = L2CrossDomainMessenger(Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER); L2CrossDomainMessenger L2Messenger =
L2ToL1MessagePasser messagePasser = L2ToL1MessagePasser(payable(Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER)); L2CrossDomainMessenger(Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER);
L2ToL1MessagePasser messagePasser =
L2ToL1MessagePasser(payable(Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER));
event SentMessage( event SentMessage(
address indexed target, address indexed target,
...@@ -139,20 +144,11 @@ contract Messenger_Initializer is L2OutputOracle_Initializer { ...@@ -139,20 +144,11 @@ contract Messenger_Initializer is L2OutputOracle_Initializer {
address(new L2ToL1MessagePasser()).code address(new L2ToL1MessagePasser()).code
); );
vm.label( vm.label(Lib_PredeployAddresses.OVM_ETH, "OVM_ETH");
Lib_PredeployAddresses.OVM_ETH,
"OVM_ETH"
);
vm.label( vm.label(Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER, "L2ToL1MessagePasser");
Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER,
"L2ToL1MessagePasser"
);
vm.label( vm.label(Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER, "L2CrossDomainMessenger");
Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER,
"L2CrossDomainMessenger"
);
vm.label( vm.label(
AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger)), AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger)),
...@@ -274,14 +270,8 @@ contract Bridge_Initializer is Messenger_Initializer { ...@@ -274,14 +270,8 @@ contract Bridge_Initializer is Messenger_Initializer {
function setUp() public virtual override { function setUp() public virtual override {
super.setUp(); super.setUp();
vm.label( vm.label(Lib_PredeployAddresses.L2_STANDARD_BRIDGE, "L2StandardBridge");
Lib_PredeployAddresses.L2_STANDARD_BRIDGE, vm.label(Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY, "L2StandardTokenFactory");
"L2StandardBridge"
);
vm.label(
Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY,
"L2StandardTokenFactory"
);
// Deploy the L1 bridge and initialize it with the address of the // Deploy the L1 bridge and initialize it with the address of the
// L1CrossDomainMessenger // L1CrossDomainMessenger
...@@ -299,32 +289,34 @@ contract Bridge_Initializer is Messenger_Initializer { ...@@ -299,32 +289,34 @@ contract Bridge_Initializer is Messenger_Initializer {
// Set up the L2 mintable token factory // Set up the L2 mintable token factory
OptimismMintableTokenFactory factory = new OptimismMintableTokenFactory(); OptimismMintableTokenFactory factory = new OptimismMintableTokenFactory();
vm.etch(Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY, address(factory).code); vm.etch(Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY, address(factory).code);
L2TokenFactory = OptimismMintableTokenFactory(Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY); L2TokenFactory = OptimismMintableTokenFactory(
Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY
);
L2TokenFactory.initialize(Lib_PredeployAddresses.L2_STANDARD_BRIDGE); L2TokenFactory.initialize(Lib_PredeployAddresses.L2_STANDARD_BRIDGE);
vm.etch( vm.etch(Lib_PredeployAddresses.OVM_ETH, address(new OVM_ETH()).code);
Lib_PredeployAddresses.OVM_ETH,
address(new OVM_ETH()).code
);
L1Token = new ERC20("Native L1 Token", "L1T"); L1Token = new ERC20("Native L1 Token", "L1T");
// Deploy the L2 ERC20 now // Deploy the L2 ERC20 now
L2Token = OptimismMintableERC20(L2TokenFactory.createStandardL2Token( L2Token = OptimismMintableERC20(
address(L1Token), L2TokenFactory.createStandardL2Token(
string(abi.encodePacked("L2-", L1Token.name())), address(L1Token),
string(abi.encodePacked("L2-", L1Token.symbol())) string(abi.encodePacked("L2-", L1Token.name())),
)); string(abi.encodePacked("L2-", L1Token.symbol()))
)
);
NativeL2Token = new ERC20("Native L2 Token", "L2T"); NativeL2Token = new ERC20("Native L2 Token", "L2T");
L1TokenFactory = new OptimismMintableTokenFactory(); L1TokenFactory = new OptimismMintableTokenFactory();
L1TokenFactory.initialize(address(L1Bridge)); L1TokenFactory.initialize(address(L1Bridge));
RemoteL1Token = OptimismMintableERC20(L1TokenFactory.createStandardL2Token( RemoteL1Token = OptimismMintableERC20(
address(NativeL2Token), L1TokenFactory.createStandardL2Token(
string(abi.encodePacked("L1-", NativeL2Token.name())), address(NativeL2Token),
string(abi.encodePacked("L1-", NativeL2Token.symbol())) string(abi.encodePacked("L1-", NativeL2Token.name())),
)); string(abi.encodePacked("L1-", NativeL2Token.symbol()))
)
);
} }
} }
...@@ -19,8 +19,7 @@ contract OptimismPortal_Test is L2OutputOracle_Initializer { ...@@ -19,8 +19,7 @@ contract OptimismPortal_Test is L2OutputOracle_Initializer {
bytes data bytes data
); );
// Dependencies // Test target
// L2OutputOracle oracle;
OptimismPortal op; OptimismPortal op;
function setUp() public override { function setUp() public override {
......
import { ethers } from 'ethers' import { ethers } from 'ethers'
const { env } = process
const startingTimestamp =
typeof env.L2OO_STARTING_BLOCK_TIMESTAMP === 'string'
? ethers.BigNumber.from(env.L2OO_STARTING_BLOCK_TIMESTAMP).toNumber()
: Date.now() / 1000
const config = { const config = {
submissionInterval: 6, submissionInterval: 6,
l2BlockTime: 2,
genesisOutput: ethers.constants.HashZero, genesisOutput: ethers.constants.HashZero,
historicalBlocks: 0, historicalBlocks: 0,
startingBlockTimestamp: startingBlockNumber: 0,
parseInt(process.env.L2OO_STARTING_BLOCK_TIMESTAMP, 10) || Date.now(), l2BlockTime: 2,
startingTimestamp,
sequencerAddress: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', sequencerAddress: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
l2CrossDomainMessengerOwner: ethers.constants.AddressZero,
gasPriceOracleOwner: ethers.constants.AddressZero,
gasPriceOracleOverhead: 2100,
gasPriceOracleScalar: 1000000,
gasPriceOracleDecimals: 6,
l1BlockInitialNumber: 0,
l1BlockInitialTimestamp: 0,
l1BlockInitialBasefee: 10,
l1BlockInitialHash: ethers.constants.HashZero,
l1BlockInitialSequenceNumber: 0,
proxyAdmin: '0x829BD824B016326A401d083B33D092293333A830',
genesisBlockExtradata: ethers.utils.hexConcat([
ethers.constants.HashZero,
'0xca062b0fd91172d89bcd4bb084ac4e21972cc467',
ethers.utils.hexZeroPad('0x', 65),
]),
genesisBlockGasLimit: ethers.BigNumber.from(15000000).toHexString(),
genesisBlockChainid: 901,
fundDevAccounts: true,
optimsismBaseFeeRecipient: '0xBcd4042DE499D14e55001CcbB24a551F3b954096',
optimismL1FeeRecipient: '0x71bE63f3384f5fb98995898A86B02Fb2426c5788',
deploymentWaitConfirmations: 1,
maxSequencerDrift: 10,
sequencerWindowSize: 2,
ownerAddress: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
} }
export default config export default config
...@@ -5,7 +5,7 @@ const config = { ...@@ -5,7 +5,7 @@ const config = {
l2BlockTime: 2, l2BlockTime: 2,
genesisOutput: ethers.constants.HashZero, genesisOutput: ethers.constants.HashZero,
historicalBlocks: 0, historicalBlocks: 0,
startingBlockTimestamp: 1652907966, startingTimestamp: 1652907966,
sequencerAddress: '0x7431310e026B69BFC676C0013E12A1A11411EEc9', sequencerAddress: '0x7431310e026B69BFC676C0013E12A1A11411EEc9',
} }
......
import { ethers } from 'ethers' import { ethers } from 'ethers'
const { env } = process
const startingTimestamp =
typeof env.L2OO_STARTING_BLOCK_TIMESTAMP === 'string'
? ethers.BigNumber.from(env.L2OO_STARTING_BLOCK_TIMESTAMP).toNumber()
: Date.now() / 1000
const config = { const config = {
submissionInterval: 6, submissionInterval: 6,
l2BlockTime: 2,
genesisOutput: ethers.constants.HashZero, genesisOutput: ethers.constants.HashZero,
historicalBlocks: 0, historicalBlocks: 0,
startingBlockTimestamp: startingBlockNumber: 0,
parseInt(process.env.L2OO_STARTING_BLOCK_TIMESTAMP, 10) || Date.now(), l2BlockTime: 2,
startingTimestamp,
sequencerAddress: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', sequencerAddress: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
maxSequencerDrift: 10,
sequencerWindowSize: 2,
ownerAddress: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
} }
export default config export default config
/* Imports: Internal */ /* Imports: Internal */
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import { BigNumber } from 'ethers'
import 'hardhat-deploy' import 'hardhat-deploy'
import '@nomiclabs/hardhat-ethers'
import '@eth-optimism/hardhat-deploy-config'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const { deploy } = hre.deployments const { deploy } = hre.deployments
const { deployer } = await hre.getNamedAccounts() const { deployer } = await hre.getNamedAccounts()
const { deployConfig } = hre
if ( if (
typeof hre.deployConfig.startingBlockTimestamp !== 'number' || typeof deployConfig.startingTimestamp !== 'number' ||
isNaN(hre.deployConfig.startingBlockTimestamp) isNaN(deployConfig.startingTimestamp)
) { ) {
throw new Error( throw new Error(
'Cannot deploy L2OutputOracle without specifying a valid startingBlockTimestamp.' 'Cannot deploy L2OutputOracle without specifying a valid startingTimestamp.'
) )
} }
await deploy('L2OutputOracleProxy', {
contract: 'Proxy',
from: deployer,
args: [deployer],
log: true,
waitConfirmations: deployConfig.deploymentWaitConfirmations,
})
await deploy('L2OutputOracle', { await deploy('L2OutputOracle', {
from: deployer, from: deployer,
args: [ args: [
hre.deployConfig.submissionInterval, deployConfig.submissionInterval,
hre.deployConfig.l2BlockTime, deployConfig.genesisOutput,
hre.deployConfig.genesisOutput, deployConfig.historicalBlocks,
hre.deployConfig.historicalBlocks, deployConfig.startingBlockNumber,
hre.deployConfig.startingBlockTimestamp, deployConfig.startingTimestamp,
hre.deployConfig.sequencerAddress, deployConfig.l2BlockTime,
deployConfig.sequencerAddress,
deployConfig.ownerAddress,
], ],
log: true, log: true,
waitConfirmations: 1, waitConfirmations: deployConfig.deploymentWaitConfirmations,
}) })
const oracle = await hre.deployments.get('L2OutputOracle')
const proxy = await hre.deployments.get('L2OutputOracleProxy')
const Proxy = await hre.ethers.getContractAt('Proxy', proxy.address)
const L2OutputOracle = await hre.ethers.getContractAt(
'L2OutputOracle',
proxy.address
)
const tx = await Proxy.upgradeToAndCall(
oracle.address,
L2OutputOracle.interface.encodeFunctionData(
'initialize(bytes32,uint256,address,address)',
[
deployConfig.genesisOutput,
deployConfig.startingBlockNumber,
deployConfig.sequencerAddress,
deployConfig.ownerAddress,
]
)
)
await tx.wait()
const submissionInterval = await L2OutputOracle.SUBMISSION_INTERVAL()
if (!submissionInterval.eq(BigNumber.from(deployConfig.submissionInterval))) {
throw new Error('submission internal misconfigured')
}
const historicalBlocks = await L2OutputOracle.HISTORICAL_TOTAL_BLOCKS()
if (!historicalBlocks.eq(BigNumber.from(deployConfig.historicalBlocks))) {
throw new Error('historal total blocks misconfigured')
}
const startingBlockNumber = await L2OutputOracle.STARTING_BLOCK_NUMBER()
if (
!startingBlockNumber.eq(BigNumber.from(deployConfig.startingBlockNumber))
) {
throw new Error('starting block number misconfigured')
}
const startingTimestamp = await L2OutputOracle.STARTING_TIMESTAMP()
if (!startingTimestamp.eq(BigNumber.from(deployConfig.startingTimestamp))) {
throw new Error('starting timestamp misconfigured')
}
const l2BlockTime = await L2OutputOracle.L2_BLOCK_TIME()
if (!l2BlockTime.eq(BigNumber.from(deployConfig.l2BlockTime))) {
throw new Error('L2 block time misconfigured')
}
} }
deployFn.tags = ['L2OutputOracle'] deployFn.tags = ['L2OutputOracle']
......
/* Imports: Internal */ /* Imports: Internal */
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import 'hardhat-deploy' import 'hardhat-deploy'
import '@nomiclabs/hardhat-ethers'
import '@eth-optimism/hardhat-deploy-config'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const { deploy, get } = hre.deployments const { deploy, get } = hre.deployments
const { deployer } = await hre.getNamedAccounts() const { deployer } = await hre.getNamedAccounts()
const { deployConfig } = hre
await deploy('OptimismPortalProxy', {
contract: 'Proxy',
from: deployer,
args: [deployer],
log: true,
waitConfirmations: deployConfig.deploymentWaitConfirmations,
})
const oracle = await get('L2OutputOracle') const oracle = await get('L2OutputOracle')
await deploy('OptimismPortal', { await deploy('OptimismPortal', {
from: deployer, from: deployer,
args: [oracle.address, 2], args: [oracle.address, 2],
log: true, log: true,
waitConfirmations: 1, waitConfirmations: deployConfig.deploymentWaitConfirmations,
}) })
const proxy = await hre.deployments.get('OptimismPortalProxy')
const Proxy = await hre.ethers.getContractAt('Proxy', proxy.address)
const portal = await hre.deployments.get('OptimismPortal')
const tx = await Proxy.upgradeTo(portal.address)
await tx.wait()
const OptimismPortal = await hre.ethers.getContractAt(
'OptimismPortal',
proxy.address
)
const l2Oracle = await OptimismPortal.L2_ORACLE()
if (l2Oracle !== oracle.address) {
throw new Error('L2 Oracle mismatch')
}
} }
deployFn.tags = ['OptimismPortal'] deployFn.tags = ['OptimismPortal']
......
/* Imports: Internal */ /* Imports: Internal */
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import { Contract } from 'ethers'
import 'hardhat-deploy' import 'hardhat-deploy'
import '@nomiclabs/hardhat-ethers'
import '@eth-optimism/hardhat-deploy-config'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const { deploy } = hre.deployments const { deploy } = hre.deployments
const { deployer } = await hre.getNamedAccounts() const { deployer } = await hre.getNamedAccounts()
const { deployConfig } = hre
await deploy('L1CrossDomainMessengerProxy', {
contract: 'Proxy',
from: deployer,
args: [deployer],
log: true,
waitConfirmations: deployConfig.deploymentWaitConfirmations,
})
await deploy('L1CrossDomainMessenger', { await deploy('L1CrossDomainMessenger', {
from: deployer, from: deployer,
args: [], args: [],
log: true, log: true,
waitConfirmations: 1, waitConfirmations: deployConfig.deploymentWaitConfirmations,
}) })
const provider = hre.ethers.provider.getSigner(deployer) const proxy = await hre.deployments.get('L1CrossDomainMessengerProxy')
const oracle = await hre.deployments.get('L2OutputOracle') const Proxy = await hre.ethers.getContractAt('Proxy', proxy.address)
const messenger = await hre.deployments.get('L1CrossDomainMessenger') const messenger = await hre.deployments.get('L1CrossDomainMessenger')
const portal = await hre.deployments.get('OptimismPortal')
const L1CrossDomainMessenger = await hre.ethers.getContractAt(
'L1CrossDomainMessenger',
proxy.address
)
const L1CrossDomainMessenger = new Contract( const upgradeTx = await Proxy.upgradeToAndCall(
messenger.address, messenger.address,
messenger.abi, L1CrossDomainMessenger.interface.encodeFunctionData('initialize(address)', [
provider portal.address,
])
) )
await upgradeTx.wait()
const tx = await L1CrossDomainMessenger.initialize(oracle.address) const portalAddress = await L1CrossDomainMessenger.portal()
const receipt = await tx.wait() if (portalAddress !== portal.address) {
console.log(`${receipt.transactionHash}: initialize(${oracle.address})`) throw new Error('portal misconfigured')
}
} }
deployFn.tags = ['L1CrossDomainMessenger'] deployFn.tags = ['L1CrossDomainMessenger']
......
/* Imports: Internal */ /* Imports: Internal */
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import { Contract } from 'ethers'
import 'hardhat-deploy' import 'hardhat-deploy'
import '@nomiclabs/hardhat-ethers'
import '@eth-optimism/hardhat-deploy-config'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const { deploy } = hre.deployments const { deploy } = hre.deployments
const { deployer } = await hre.getNamedAccounts() const { deployer } = await hre.getNamedAccounts()
const { deployConfig } = hre
await deploy('L1StandardBridgeProxy', {
contract: 'Proxy',
from: deployer,
args: [deployer],
log: true,
waitConfirmations: deployConfig.deploymentWaitConfirmations,
})
await deploy('L1StandardBridge', { await deploy('L1StandardBridge', {
from: deployer, from: deployer,
args: [], args: [],
log: true, log: true,
waitConfirmations: 1, waitConfirmations: deployConfig.deploymentWaitConfirmations,
}) })
const provider = hre.ethers.provider.getSigner(deployer) const proxy = await hre.deployments.get('L1StandardBridgeProxy')
const Proxy = await hre.ethers.getContractAt('Proxy', proxy.address)
const messenger = await hre.deployments.get('L1CrossDomainMessenger')
const bridge = await hre.deployments.get('L1StandardBridge') const bridge = await hre.deployments.get('L1StandardBridge')
const messenger = await hre.deployments.get('L1CrossDomainMessengerProxy')
const L1StandardBridge = new Contract(bridge.address, bridge.abi, provider)
const L1StandardBridge = await hre.ethers.getContractAt(
const tx = await L1StandardBridge.initialize(messenger.address) 'L1StandardBridge',
const receipt = await tx.wait() proxy.address
console.log(`${receipt.transactionHash}: initialize(${messenger.address})`) )
const upgradeTx = await Proxy.upgradeToAndCall(
bridge.address,
L1StandardBridge.interface.encodeFunctionData('initialize(address)', [
messenger.address,
])
)
await upgradeTx.wait()
if (messenger.address !== (await L1StandardBridge.messenger())) {
throw new Error('misconfigured messenger')
}
} }
deployFn.tags = ['L1StandardBridge'] deployFn.tags = ['L1StandardBridge']
......
/* Imports: Internal */ /* Imports: Internal */
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import { Contract } from 'ethers'
import 'hardhat-deploy' import 'hardhat-deploy'
import '@nomiclabs/hardhat-ethers'
import '@eth-optimism/hardhat-deploy-config'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const { deploy } = hre.deployments const { deploy } = hre.deployments
const { deployer } = await hre.getNamedAccounts() const { deployer } = await hre.getNamedAccounts()
const { deployConfig } = hre
await deploy('OptimismMintableTokenFactoryProxy', {
contract: 'Proxy',
from: deployer,
args: [deployer],
log: true,
waitConfirmations: deployConfig.deploymentWaitConfirmations,
})
await deploy('OptimismMintableTokenFactory', { await deploy('OptimismMintableTokenFactory', {
from: deployer, from: deployer,
args: [], args: [],
log: true, log: true,
waitConfirmations: 1, waitConfirmations: deployConfig.deploymentWaitConfirmations,
}) })
const provider = hre.ethers.provider.getSigner(deployer)
const factory = await hre.deployments.get('OptimismMintableTokenFactory') const factory = await hre.deployments.get('OptimismMintableTokenFactory')
const bridge = await hre.deployments.get('L1StandardBridge') const bridge = await hre.deployments.get('L1StandardBridgeProxy')
const proxy = await hre.deployments.get('OptimismMintableTokenFactoryProxy')
const Proxy = await hre.ethers.getContractAt('Proxy', proxy.address)
const OptimismMintableTokenFactory = await hre.ethers.getContractAt(
'OptimismMintableTokenFactory',
proxy.address
)
const OptimismMintableTokenFactory = new Contract( const upgradeTx = await Proxy.upgradeToAndCall(
factory.address, factory.address,
factory.abi, OptimismMintableTokenFactory.interface.encodeFunctionData(
provider 'initialize(address)',
[bridge.address]
)
) )
await upgradeTx.wait()
const tx = await OptimismMintableTokenFactory.initialize(bridge.address) if (bridge.address !== (await OptimismMintableTokenFactory.bridge())) {
const receipt = await tx.wait() throw new Error('bridge misconfigured')
console.log(`${receipt.transactionHash}: initialize(${bridge.address})`) }
} }
deployFn.tags = ['OptimismMintableTokenFactory'] deployFn.tags = ['OptimismMintableTokenFactory']
......
...@@ -13,4 +13,6 @@ remappings = [ ...@@ -13,4 +13,6 @@ remappings = [
'ds-test/=node_modules/ds-test/src' 'ds-test/=node_modules/ds-test/src'
] ]
extra_output = ['devdoc', 'userdoc', 'metadata', 'storageLayout'] extra_output = ['devdoc', 'userdoc', 'metadata', 'storageLayout']
bytecode_hash = "none" bytecode_hash = 'none'
# build_info = true
...@@ -3,7 +3,7 @@ import { HardhatUserConfig, task, subtask } from 'hardhat/config' ...@@ -3,7 +3,7 @@ import { HardhatUserConfig, task, subtask } from 'hardhat/config'
import { TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS } from 'hardhat/builtin-tasks/task-names' import { TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS } from 'hardhat/builtin-tasks/task-names'
// Hardhat plugins // Hardhat plugins
import '@nomiclabs/hardhat-waffle' import '@nomiclabs/hardhat-ethers'
import '@typechain/hardhat' import '@typechain/hardhat'
import 'solidity-coverage' import 'solidity-coverage'
import 'hardhat-deploy' import 'hardhat-deploy'
...@@ -11,7 +11,10 @@ import '@foundry-rs/hardhat-forge' ...@@ -11,7 +11,10 @@ import '@foundry-rs/hardhat-forge'
import '@eth-optimism/hardhat-deploy-config' import '@eth-optimism/hardhat-deploy-config'
// Hardhat tasks // Hardhat tasks
import './tasks/genesis-l1'
import './tasks/genesis-l2'
import './tasks/deposits' import './tasks/deposits'
import './tasks/rollup-config'
subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS).setAction( subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS).setAction(
async (_, __, runSuper) => { async (_, __, runSuper) => {
...@@ -39,10 +42,8 @@ const config: HardhatUserConfig = { ...@@ -39,10 +42,8 @@ const config: HardhatUserConfig = {
}, },
goerli: { goerli: {
chainId: 5, chainId: 5,
url: (process.env.L1_RPC || ''), url: process.env.L1_RPC || '',
accounts: [ accounts: [process.env.PRIVATE_KEY_DEPLOYER || ethers.constants.HashZero],
(process.env.PRIVATE_KEY_DEPLOYER || ethers.constants.HashZero),
],
}, },
}, },
paths: { paths: {
...@@ -73,12 +74,31 @@ const config: HardhatUserConfig = { ...@@ -73,12 +74,31 @@ const config: HardhatUserConfig = {
historicalBlocks: { historicalBlocks: {
type: 'number', type: 'number',
}, },
startingBlockTimestamp: { startingBlockNumber: {
type: 'number',
},
startingTimestamp: {
type: 'number', type: 'number',
}, },
sequencerAddress: { sequencerAddress: {
type: 'address', type: 'address',
}, },
ownerAddress: {
type: 'address',
},
},
external: {
contracts: [
{
artifacts: '../contracts/artifacts',
},
{
artifacts: '../contracts-governance/artifacts',
},
],
deployments: {
goerli: ['../contracts/deployments/goerli'],
},
}, },
solidity: { solidity: {
compilers: [ compilers: [
......
...@@ -20,9 +20,10 @@ ...@@ -20,9 +20,10 @@
"build:ts": "tsc", "build:ts": "tsc",
"test": "forge test", "test": "forge test",
"gas-snapshot": "forge snapshot", "gas-snapshot": "forge snapshot",
"storage-snapshot": "./scripts/storage-snapshot.sh",
"slither": "slither .", "slither": "slither .",
"clean": "rm -rf ./dist ./artifacts ./forge-artifacts ./cache ./coverage ./tsconfig.tsbuildinfo", "clean": "rm -rf ./dist ./artifacts ./forge-artifacts ./cache ./coverage ./tsconfig.tsbuildinfo",
"lint:ts:check": "eslint .", "lint:ts:check": "eslint . --max-warnings=0",
"lint:contracts:check": "yarn solhint -f table 'contracts/**/*.sol'", "lint:contracts:check": "yarn solhint -f table 'contracts/**/*.sol'",
"lint:check": "yarn lint:contracts:check && yarn lint:ts:check", "lint:check": "yarn lint:contracts:check && yarn lint:ts:check",
"lint:ts:fix": "yarn prettier --write .", "lint:ts:fix": "yarn prettier --write .",
...@@ -44,6 +45,7 @@ ...@@ -44,6 +45,7 @@
}, },
"devDependencies": { "devDependencies": {
"@eth-optimism/hardhat-deploy-config": "^0.2.0", "@eth-optimism/hardhat-deploy-config": "^0.2.0",
"@defi-wonderland/smock": "^2.0.2",
"@foundry-rs/hardhat-forge": "^0.1.7", "@foundry-rs/hardhat-forge": "^0.1.7",
"@nomiclabs/hardhat-ethers": "^2.0.0", "@nomiclabs/hardhat-ethers": "^2.0.0",
"@nomiclabs/hardhat-waffle": "^2.0.0", "@nomiclabs/hardhat-waffle": "^2.0.0",
......
#!/usr/bin/env bash
set -e
if ! command -v forge &> /dev/null
then
echo "forge could not be found. Please install forge by running:"
echo "curl -L https://foundry.paradigm.xyz | bash"
exit
fi
contracts=(
L1CrossDomainMessenger
L1StandardBridge
L2OutputOracle
OptimismPortal
DeployerWhitelist
GasPriceOracle
L1Block
L1BlockNumber
L2CrossDomainMessenger
L2StandardBridge
L2ToL1MessagePasser
OVM_ETH
SequencerFeeVault
WETH9
ProxyAdmin
Proxy
L1ChugSplashProxy
OptimismMintableERC20
OptimismMintableTokenFactory
)
dir=$(dirname "$0")
echo "Creating storage layout diagrams.."
echo "=======================" > $dir/../.storage-layout
echo "👁👁 STORAGE LAYOUT snapshot 👁👁" >> $dir/../.storage-layout
echo "=======================" >> $dir/../.storage-layout
for contract in ${contracts[@]}
do
echo -e "\n=======================" >> $dir/../.storage-layout
echo "➡ $contract">> $dir/../.storage-layout
echo -e "=======================\n" >> $dir/../.storage-layout
forge inspect --pretty $contract storage-layout >> $dir/../.storage-layout
done
echo "Storage layout snapshot stored at $dir/../.storage-layout"
/** /**
* Predeploys are Solidity contracts that are injected into the initial L2 state and provide * Predeploys are Solidity contracts that are injected into the initial L2 state and provide
* various useful functions. * various useful functions.
* Notes:
* 0x42...04 was the address of the OVM_ProxySequencerEntrypoint. This contract is no longer in
* use and has therefore been removed. We may place a new predeployed contract at this address
* in the future. See https://github.com/ethereum-optimism/optimism/pull/549 for more info.
*/ */
export const bedrockPredeploys = { export const predeploys = {
L1_BLOCK_ATTRIBUTES: '0x4200000000000000000000000000000000000015', L2ToL1MessagePasser: '0x4200000000000000000000000000000000000000',
WITHDRAWER: '0x4200000000000000000000000000000000000016', DeployerWhitelist: '0x4200000000000000000000000000000000000002',
L2CrossDomainMessenger: '0x4200000000000000000000000000000000000007',
GasPriceOracle: '0x420000000000000000000000000000000000000F',
L2StandardBridge: '0x4200000000000000000000000000000000000010',
SequencerFeeVault: '0x4200000000000000000000000000000000000011',
OptimismMintableTokenFactory: '0x4200000000000000000000000000000000000012',
L1BlockNumber: '0x4200000000000000000000000000000000000013',
L1Block: '0x4200000000000000000000000000000000000015',
OVM_ETH: '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000',
WETH9: '0x4200000000000000000000000000000000000006',
GovernanceToken: '0x4200000000000000000000000000000000000042',
}
export const futurePredeploys = {
System1: '0x4200000000000000000000000000000000000014',
} }
...@@ -2,7 +2,7 @@ import { ethers } from 'ethers' ...@@ -2,7 +2,7 @@ import { ethers } from 'ethers'
import { toHexString } from '@eth-optimism/core-utils' import { toHexString } from '@eth-optimism/core-utils'
import { TrieTestGenerator } from './trie-test-generator' import { TrieTestGenerator } from './trie-test-generator'
import { bedrockPredeploys } from './constants' import { predeploys } from './constants'
interface WithdrawalArgs { interface WithdrawalArgs {
nonce: number nonce: number
...@@ -63,7 +63,7 @@ export const generateMockWithdrawalProof = async ( ...@@ -63,7 +63,7 @@ export const generateMockWithdrawalProof = async (
const generator = await TrieTestGenerator.fromAccounts({ const generator = await TrieTestGenerator.fromAccounts({
accounts: [ accounts: [
{ {
address: bedrockPredeploys.WITHDRAWER, address: predeploys.L2ToL1MessagePasser,
nonce: 0, nonce: 0,
balance: 0, balance: 0,
codeHash: ethers.utils.keccak256('0x1234'), codeHash: ethers.utils.keccak256('0x1234'),
......
import fs from 'fs'
import { ethers } from 'ethers'
import { task } from 'hardhat/config'
import { Genesis, State } from '@eth-optimism/core-utils'
task('genesis-l1', 'create a genesis config')
.addOptionalParam(
'outfile',
'The file to write the output JSON to',
'genesis.json'
)
.setAction(async (args, hre) => {
// TODO: type needs to be updated to work with modern geth
const alloc: State | any = {}
// Give each predeploy a single wei
for (let i = 0; i <= 0xff; i++) {
const buf = Buffer.alloc(2)
buf.writeUInt16BE(i, 0)
const addr = ethers.utils.hexConcat([
'0x000000000000000000000000000000000000',
ethers.utils.hexZeroPad(buf, 2),
])
alloc[addr] = {
balance: '0x1',
}
}
// Prefund dev accounts
const accounts = [
'0x14dC79964da2C08b23698B3D3cc7Ca32193d9955',
'0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65',
'0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec',
'0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f',
'0x2546BcD3c84621e976D8185a91A922aE77ECEc30',
'0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC',
'0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
'0x71bE63f3384f5fb98995898A86B02Fb2426c5788',
'0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199',
'0x90F79bf6EB2c4f870365E785982E1f101E93b906',
'0x976EA74026E726554dB657fA54763abd0C3a0aa9',
'0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc',
'0xBcd4042DE499D14e55001CcbB24a551F3b954096',
'0xFABB0ac9d68B0B445fB7357272Ff202C5651694a',
'0xa0Ee7A142d267C1f36714E4a8F75612F20a79720',
'0xbDA5747bFD65F08deb54cb465eB87D40e51B197E',
'0xcd3B766CCDd6AE721141F452C550Ca635964ce71',
'0xdD2FD4581271e230360230F9337D5c0430Bf44C0',
'0xdF3e18d64BC6A983f673Ab319CCaE4f1a57C7097',
'0xde3829a23df1479438622a08a116e8eb3f620bb5',
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
]
const signers = await hre.ethers.getSigners()
for (const signer of signers) {
accounts.push(signer.address)
}
for (const account of accounts) {
alloc[ethers.utils.getAddress(account)] = {
balance:
'0x200000000000000000000000000000000000000000000000000000000000000',
}
}
const timestamp = hre.deployConfig.startingTimestamp
if (timestamp === undefined) {
throw new Error('Must configure starting block timestamp')
}
const genesis: Genesis = {
config: {
chainId: 900,
homesteadBlock: 0,
eip150Block: 0,
eip150Hash:
'0x0000000000000000000000000000000000000000000000000000000000000000',
eip155Block: 0,
eip158Block: 0,
byzantiumBlock: 0,
constantinopleBlock: 0,
petersburgBlock: 0,
istanbulBlock: 0,
muirGlacierBlock: 0,
berlinBlock: 0,
londonBlock: 0,
clique: {
period: 15,
epoch: 30000,
},
},
nonce: '0x0',
timestamp: ethers.BigNumber.from(timestamp).toHexString(),
extraData:
'0x0000000000000000000000000000000000000000000000000000000000000000ca062b0fd91172d89bcd4bb084ac4e21972cc4670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
gasLimit: ethers.BigNumber.from(15_000_000).toHexString(),
difficulty: '0x1',
mixHash:
'0x0000000000000000000000000000000000000000000000000000000000000000',
coinbase: '0x0000000000000000000000000000000000000000',
alloc,
}
fs.writeFileSync(args.outfile, JSON.stringify(genesis, null, 2))
})
import fs from 'fs'
import assert from 'assert'
import { OptimismGenesis, State } from '@eth-optimism/core-utils'
import { ethers } from 'ethers'
import { task } from 'hardhat/config'
import { HardhatRuntimeEnvironment } from 'hardhat/types'
import { predeploys } from '../src'
const prefix = '0x420000000000000000000000000000000000'
const implementationSlot =
'0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc'
const adminSlot =
'0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103'
const toCodeAddr = (addr: string) => {
const address = ethers.utils.hexConcat([
'0xc0d3c0d3c03dc03dc03dc03dc03dc03dc03d',
'0x' + addr.slice(prefix.length),
])
return ethers.utils.getAddress(address)
}
const assertEvenLength = (str: string) => {
assert(str.length % 2 === 0, str)
}
// TODO: this can be replaced with the smock version after
// a new release of foundry-rs/hardhat
const getStorageLayout = async (
hre: HardhatRuntimeEnvironment,
name: string
) => {
const buildInfo = await hre.artifacts.getBuildInfo(name)
const key = Object.keys(buildInfo.output.contracts)[0]
return (buildInfo.output.contracts[key][name] as any).storageLayout
}
task('genesis-l2', 'create a genesis config')
.addOptionalParam(
'outfile',
'The file to write the output JSON to',
'genesis.json'
)
.setAction(async (args, hre) => {
const {
computeStorageSlots,
// eslint-disable-next-line @typescript-eslint/no-var-requires
} = require('@defi-wonderland/smock/dist/src/utils')
const { deployConfig } = hre
// Use the addresses of the proxies here instead of the implementations
// Be backwards compatible
let ProxyL1CrossDomainMessenger = await hre.deployments.getOrNull(
'Proxy__OVM_L1CrossDomainMessenger'
)
if (ProxyL1CrossDomainMessenger === undefined) {
ProxyL1CrossDomainMessenger = await hre.deployments.get(
'L1CrossDomainMessengerProxy'
)
}
// Be backwards compatible
let ProxyL1StandardBridge = await hre.deployments.getOrNull(
'Proxy__OVM_L1StandardBridge'
)
if (ProxyL1StandardBridge === undefined) {
ProxyL1StandardBridge = await hre.deployments.get('L1StandardBridgeProxy')
}
const variables = {
L2ToL1MessagePasser: {
nonce: 0,
},
L2CrossDomainMessenger: {
_initialized: 1,
_owner: deployConfig.l2CrossDomainMessengerOwner,
xDomainMsgSender: '0x000000000000000000000000000000000000dEaD',
msgNonce: 0,
otherMessenger: ProxyL1CrossDomainMessenger.address,
// TODO: handle blockedSystemAddresses mapping
// blockedSystemAddresses: [{key: '', value: ''}],
},
GasPriceOracle: {
_owner: deployConfig.gasPriceOracleOwner,
overhead: deployConfig.gasPriceOracleOverhead,
scalar: deployConfig.gasPriceOracleScalar,
decimals: deployConfig.gasPriceOracleDecimals,
},
L2StandardBridge: {
messenger: predeploys.L2CrossDomainMessenger,
otherBridge: ProxyL1StandardBridge.address,
},
SequencerFeeVault: {
l1FeeWallet: ethers.constants.AddressZero,
},
OptimismMintableTokenFactory: {
bridge: ethers.constants.AddressZero,
},
L1Block: {
number: deployConfig.l1BlockInitialNumber,
timestamp: deployConfig.l1BlockInitialTimestamp,
basefee: deployConfig.l1BlockInitialBasefee,
hash: deployConfig.l1BlockInitialHash,
sequenceNumber: deployConfig.l1BlockInitialSequenceNumber,
},
OVM_ETH: {
bridge: predeploys.L2StandardBridge,
remoteToken: ethers.constants.AddressZero,
_name: 'Ether',
_symbol: 'ETH',
},
WETH9: {
name: 'Wrapped Ether',
symbol: 'WETH',
decimals: 18,
},
GovernanceToken: {
name: 'Optimism',
symbol: 'OP',
_owner: deployConfig.proxyAdmin,
},
}
assertEvenLength(implementationSlot)
assertEvenLength(adminSlot)
assertEvenLength(deployConfig.proxyAdmin)
const predeployAddrs = new Set()
for (const addr of Object.values(predeploys)) {
predeployAddrs.add(ethers.utils.getAddress(addr))
}
// TODO: geth likes strings for nonce and balance now
const alloc: State = {}
// Set a proxy at each predeploy address
const proxy = await hre.artifacts.readArtifact('Proxy')
for (let i = 0; i <= 0xffff; i++) {
const num = ethers.utils.hexZeroPad('0x' + i.toString(16), 2)
const addr = ethers.utils.getAddress(
ethers.utils.hexConcat([prefix, num])
)
// There is no proxy at OVM_ETH or the GovernanceToken
if (
addr === ethers.utils.getAddress(predeploys.OVM_ETH) ||
addr === ethers.utils.getAddress(predeploys.GovernanceToken)
) {
continue
}
alloc[addr] = {
nonce: '0x0',
balance: '0x0',
code: proxy.deployedBytecode,
storage: {
[adminSlot]: deployConfig.proxyAdmin,
},
}
if (predeployAddrs.has(ethers.utils.getAddress(addr))) {
alloc[addr].storage[implementationSlot] = toCodeAddr(addr)
}
}
// Set the GovernanceToken in the state
// Cannot easily set storage due to no easy access to compiler
// output
const governanceToken = await hre.deployments.getArtifact('GovernanceToken')
alloc[predeploys.GovernanceToken] = {
nonce: '0x0',
balance: '0x0',
code: governanceToken.deployedBytecode,
}
// Give each predeploy a single wei
for (let i = 0; i <= 0xff; i++) {
const buf = Buffer.alloc(2)
buf.writeUInt16BE(i, 0)
const addr = ethers.utils.hexConcat([
'0x000000000000000000000000000000000000',
ethers.utils.hexZeroPad(buf, 2),
])
alloc[addr] = {
balance: '0x1',
}
}
if (deployConfig.fundDevAccounts) {
const accounts = [
'0xde3829a23df1479438622a08a116e8eb3f620bb5',
'0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
'0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
'0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC',
]
const signers = await hre.ethers.getSigners()
for (const signer of signers) {
accounts.push(signer.address)
}
for (const account of accounts) {
alloc[account] = {
balance:
'0x200000000000000000000000000000000000000000000000000000000000000',
}
}
}
// Set the predeploys in the state
for (const [name, addr] of Object.entries(predeploys)) {
if (name === 'GovernanceToken') {
continue
}
const artifact = await hre.artifacts.readArtifact(name)
assertEvenLength(artifact.deployedBytecode)
const allocAddr = name === 'OVM_ETH' ? addr : toCodeAddr(addr)
assertEvenLength(allocAddr)
alloc[allocAddr] = {
nonce: '0x00',
balance: '0x00',
code: artifact.deployedBytecode,
storage: {},
}
const storageLayout = await getStorageLayout(hre, name)
const slots = computeStorageSlots(storageLayout, variables[name])
for (const slot of slots) {
alloc[allocAddr].storage[slot.key] = slot.val
}
}
const genesis: OptimismGenesis = {
config: {
chainId: deployConfig.genesisBlockChainid,
homesteadBlock: 0,
eip150Block: 0,
eip155Block: 0,
eip158Block: 0,
byzantiumBlock: 0,
constantinopleBlock: 0,
petersburgBlock: 0,
istanbulBlock: 0,
muirGlacierBlock: 0,
berlinBlock: 0,
londonBlock: 0,
mergeForkBlock: 0,
terminalTotalDifficulty: 0,
clique: {
period: 0,
epoch: 30000,
},
},
nonce: '0x1234',
difficulty: '0x1',
timestamp: ethers.BigNumber.from(
deployConfig.startingTimestamp
).toHexString(),
gasLimit: deployConfig.genesisBlockGasLimit,
extraData: deployConfig.genesisBlockExtradata,
optimism: {
enabled: true,
baseFeeRecipient: deployConfig.optimsismBaseFeeRecipient,
l1FeeRecipient: deployConfig.optimismL1FeeRecipient,
},
alloc,
}
fs.writeFileSync(args.outfile, JSON.stringify(genesis, null, 2))
})
import fs from 'fs'
import { task } from 'hardhat/config'
import { OpNodeConfig, getChainId } from '@eth-optimism/core-utils'
import { ethers } from 'ethers'
task('rollup-config', 'create a genesis config')
.addOptionalParam(
'outfile',
'The file to write the output JSON to',
'rollup.json'
)
.addOptionalParam('l1RpcUrl', 'The L1 RPC URL', 'http://127.0.0.1:8545')
.addOptionalParam('l2RpcUrl', 'The L2 RPC URL', 'http://127.0.0.1:9545')
.setAction(async (args, hre) => {
const { deployConfig } = hre
const l1 = new ethers.providers.StaticJsonRpcProvider(args.l1RpcUrl)
const l2 = new ethers.providers.StaticJsonRpcProvider(args.l2RpcUrl)
const l1Genesis = await l1.getBlock('earliest')
const l2Genesis = await l2.getBlock('earliest')
const portal = await hre.deployments.get('OptimismPortalProxy')
const config: OpNodeConfig = {
genesis: {
l1: {
hash: l1Genesis.hash,
number: 0,
},
l2: {
hash: l2Genesis.hash,
number: 0,
},
l2_time: deployConfig.startingTimestamp,
},
block_time: deployConfig.l2BlockTime,
max_sequencer_drift: deployConfig.maxSequencerDrift,
seq_window_size: deployConfig.sequencerWindowSize,
l1_chain_id: await getChainId(l1),
l2_chain_id: await getChainId(l2),
p2p_sequencer_address: '0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc',
fee_recipient_address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
batch_inbox_address: '0xff00000000000000000000000000000000000002',
batch_sender_address: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC',
deposit_contract_address: portal.address,
}
fs.writeFileSync(args.outfile, JSON.stringify(config, null, 2))
})
...@@ -7,6 +7,7 @@ import 'solidity-coverage' ...@@ -7,6 +7,7 @@ import 'solidity-coverage'
import { task, types } from 'hardhat/config' import { task, types } from 'hardhat/config'
import { providers, utils, Wallet } from 'ethers' import { providers, utils, Wallet } from 'ethers'
import { CrossChainMessenger } from '@eth-optimism/sdk' import { CrossChainMessenger } from '@eth-optimism/sdk'
import { getChainId } from '@eth-optimism/core-utils'
import './scripts/deploy-token' import './scripts/deploy-token'
import './scripts/multi-send' import './scripts/multi-send'
...@@ -42,11 +43,13 @@ task('deposit', 'Deposits funds onto Optimism.') ...@@ -42,11 +43,13 @@ task('deposit', 'Deposits funds onto Optimism.')
} }
const l1Provider = new providers.JsonRpcProvider(l1ProviderUrl) const l1Provider = new providers.JsonRpcProvider(l1ProviderUrl)
const l2Provider = new providers.JsonRpcProvider(l2ProviderUrl)
const l1Wallet = new Wallet(privateKey, l1Provider) const l1Wallet = new Wallet(privateKey, l1Provider)
const messenger = new CrossChainMessenger({ const messenger = new CrossChainMessenger({
l1SignerOrProvider: l1Wallet, l1SignerOrProvider: l1Wallet,
l2SignerOrProvider: l2ProviderUrl, l2SignerOrProvider: l2Provider,
l1ChainId: (await l1Provider.getNetwork()).chainId, l1ChainId: await getChainId(l1Provider),
l2ChainId: await getChainId(l2Provider),
}) })
const amountWei = utils.parseEther(amountEth) const amountWei = utils.parseEther(amountEth)
...@@ -57,8 +60,6 @@ task('deposit', 'Deposits funds onto Optimism.') ...@@ -57,8 +60,6 @@ task('deposit', 'Deposits funds onto Optimism.')
console.log(`Got TX hash ${tx.hash}. Waiting...`) console.log(`Got TX hash ${tx.hash}. Waiting...`)
await tx.wait() await tx.wait()
const l2Provider = new providers.JsonRpcProvider(l2ProviderUrl)
const l1WalletOnL2 = new Wallet(privateKey, l2Provider) const l1WalletOnL2 = new Wallet(privateKey, l2Provider)
await l1WalletOnL2.sendTransaction({ await l1WalletOnL2.sendTransaction({
to, to,
...@@ -76,7 +77,16 @@ const privKey = process.env.PRIVATE_KEY || '0x' + '11'.repeat(32) ...@@ -76,7 +77,16 @@ const privKey = process.env.PRIVATE_KEY || '0x' + '11'.repeat(32)
* @type import("hardhat/config").HardhatUserConfig * @type import("hardhat/config").HardhatUserConfig
*/ */
module.exports = { module.exports = {
solidity: '0.8.12', solidity: {
version: '0.8.12',
settings: {
outputSelection: {
'*': {
'*': ['metadata', 'storageLayout'],
},
},
},
},
networks: { networks: {
optimism: { optimism: {
chainId: 17, chainId: 17,
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
"deploy:mainnet": "hardhat deploy-token --network 'optimism-mainnet'" "deploy:mainnet": "hardhat deploy-token --network 'optimism-mainnet'"
}, },
"dependencies": { "dependencies": {
"@eth-optimism/core-utils": "^0.8.7",
"@eth-optimism/sdk": "^1.1.9", "@eth-optimism/sdk": "^1.1.9",
"@ethersproject/hardware-wallets": "^5.6.1", "@ethersproject/hardware-wallets": "^5.6.1",
"@nomiclabs/hardhat-ethers": "^2.0.2", "@nomiclabs/hardhat-ethers": "^2.0.2",
...@@ -37,8 +38,8 @@ ...@@ -37,8 +38,8 @@
"@openzeppelin/contracts": "4.5.0", "@openzeppelin/contracts": "4.5.0",
"commander": "^9.3.0", "commander": "^9.3.0",
"csv-parse": "^5.0.4", "csv-parse": "^5.0.4",
"ethereumjs-util": "^7.1.4",
"eth-sig-util": "^3.0.1", "eth-sig-util": "^3.0.1",
"ethereumjs-util": "^7.1.4",
"ethers": "^5.6.8", "ethers": "^5.6.8",
"hardhat": "^2.9.6" "hardhat": "^2.9.6"
}, },
......
...@@ -181,8 +181,8 @@ contract L1StandardBridge is IL1StandardBridge, CrossDomainEnabled { ...@@ -181,8 +181,8 @@ contract L1StandardBridge is IL1StandardBridge, CrossDomainEnabled {
bytes calldata _data bytes calldata _data
) internal { ) internal {
// When a deposit is initiated on L1, the L1 Bridge transfers the funds to itself for future // When a deposit is initiated on L1, the L1 Bridge transfers the funds to itself for future
// withdrawals. safeTransferFrom also checks if the contract has code, so this will fail if // withdrawals. The use of safeTransferFrom enables support of "broken tokens" which do not
// _from is an EOA or address(0). // return a boolean value.
// slither-disable-next-line reentrancy-events, reentrancy-benign // slither-disable-next-line reentrancy-events, reentrancy-benign
IERC20(_l1Token).safeTransferFrom(_from, address(this), _amount); IERC20(_l1Token).safeTransferFrom(_from, address(this), _amount);
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
*/ */
export interface State { export interface State {
[address: string]: { [address: string]: {
nonce: number nonce?: string
balance: string balance?: string
codeHash?: string codeHash?: string
root?: string root?: string
code?: string code?: string
...@@ -24,6 +24,7 @@ export interface ChainConfig { ...@@ -24,6 +24,7 @@ export interface ChainConfig {
chainId: number chainId: number
homesteadBlock: number homesteadBlock: number
eip150Block: number eip150Block: number
eip150Hash?: string
eip155Block: number eip155Block: number
eip158Block: number eip158Block: number
byzantiumBlock: number byzantiumBlock: number
...@@ -34,6 +35,7 @@ export interface ChainConfig { ...@@ -34,6 +35,7 @@ export interface ChainConfig {
berlinBlock: number berlinBlock: number
londonBlock?: number londonBlock?: number
arrowGlacierBlock?: number arrowGlacierBlock?: number
grayGlacierBlock?: number
mergeForkBlock?: number mergeForkBlock?: number
terminalTotalDifficulty?: number terminalTotalDifficulty?: number
clique?: { clique?: {
...@@ -48,12 +50,31 @@ export interface ChainConfig { ...@@ -48,12 +50,31 @@ export interface ChainConfig {
*/ */
export interface Genesis { export interface Genesis {
config: ChainConfig config: ChainConfig
nonce?: number nonce?: string
timestamp?: number timestamp?: string
difficulty: string difficulty: string
mixHash?: string mixHash?: string
coinbase?: string coinbase?: string
number?: string
gasLimit: string gasLimit: string
gasUsed?: string
parentHash?: string
extraData: string extraData: string
alloc: State alloc: State
} }
/**
* Represents the chain config for an Optimism chain
*/
export interface OptimismChainConfig {
enabled: boolean
baseFeeRecipient: string
l1FeeRecipient: string
}
/**
* Represents the Genesis file format for an Optimism chain
*/
export interface OptimismGenesis extends Genesis {
optimism: OptimismChainConfig
}
...@@ -6,3 +6,4 @@ export * from './alias' ...@@ -6,3 +6,4 @@ export * from './alias'
export * from './batch-encoding' export * from './batch-encoding'
export * from './fees' export * from './fees'
export * from './rollup-types' export * from './rollup-types'
export * from './op-node'
export interface OpNodeConfig {
genesis: {
l1: {
hash: string
number: number
}
l2: {
hash: string
number: number
}
l2_time: number
}
block_time: number
max_sequencer_drift: number
seq_window_size: number
l1_chain_id: number
l2_chain_id: number
p2p_sequencer_address: string
fee_recipient_address: string
batch_inbox_address: string
batch_sender_address: string
deposit_contract_address: string
}
...@@ -81,6 +81,7 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> { ...@@ -81,6 +81,7 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
l1SignerOrProvider: this.options.l1RpcProvider, l1SignerOrProvider: this.options.l1RpcProvider,
l2SignerOrProvider: this.options.l2RpcProvider, l2SignerOrProvider: this.options.l2RpcProvider,
l1ChainId: await getChainId(this.options.l1RpcProvider), l1ChainId: await getChainId(this.options.l1RpcProvider),
l2ChainId: await getChainId(this.options.l2RpcProvider),
}) })
// We use this a lot, a bit cleaner to pull out to the top level of the state object. // We use this a lot, a bit cleaner to pull out to the top level of the state object.
...@@ -135,6 +136,16 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> { ...@@ -135,6 +136,16 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
const batchStart = event.args._prevTotalElements.toNumber() + 1 const batchStart = event.args._prevTotalElements.toNumber() + 1
const batchSize = event.args._batchSize.toNumber() const batchSize = event.args._batchSize.toNumber()
const batchEnd = batchStart + batchSize
const latestBlock = await this.options.l2RpcProvider.getBlockNumber()
if (latestBlock < batchEnd) {
this.logger.info(`node is behind, waiting for sync`, {
batchEnd,
latestBlock,
})
return
}
// `getBlockRange` has a limit of 1000 blocks, so we have to break this request out into // `getBlockRange` has a limit of 1000 blocks, so we have to break this request out into
// multiple requests of maximum 1000 blocks in the case that batchSize > 1000. // multiple requests of maximum 1000 blocks in the case that batchSize > 1000.
......
...@@ -33,8 +33,8 @@ const getTargetOutput = async ( ...@@ -33,8 +33,8 @@ const getTargetOutput = async (
withdrawalTimestamp: number withdrawalTimestamp: number
) => { ) => {
const submissionInterval = (await oracle.SUBMISSION_INTERVAL()).toNumber() const submissionInterval = (await oracle.SUBMISSION_INTERVAL()).toNumber()
const startingBlockTimestamp = ( const startingTimestamp = (
await oracle.STARTING_BLOCK_TIMESTAMP() await oracle.STARTING_TIMESTAMP()
).toNumber() ).toNumber()
const nextTimestamp = (await oracle.nextTimestamp()).toNumber() const nextTimestamp = (await oracle.nextTimestamp()).toNumber()
let targetOutputTimestamp let targetOutputTimestamp
...@@ -45,10 +45,10 @@ const getTargetOutput = async ( ...@@ -45,10 +45,10 @@ const getTargetOutput = async (
// Calculate the first timestamp greater than the burnBlock which will be appended. // Calculate the first timestamp greater than the burnBlock which will be appended.
targetOutputTimestamp = targetOutputTimestamp =
Math.ceil( Math.ceil(
(withdrawalTimestamp - startingBlockTimestamp) / submissionInterval (withdrawalTimestamp - startingTimestamp) / submissionInterval
) * ) *
submissionInterval + submissionInterval +
startingBlockTimestamp startingTimestamp
} }
return targetOutputTimestamp return targetOutputTimestamp
......
...@@ -89,6 +89,7 @@ export class MessageRelayerService extends BaseServiceV2< ...@@ -89,6 +89,7 @@ export class MessageRelayerService extends BaseServiceV2<
l1SignerOrProvider: this.state.wallet, l1SignerOrProvider: this.state.wallet,
l2SignerOrProvider: this.options.l2RpcProvider, l2SignerOrProvider: this.options.l2RpcProvider,
l1ChainId: await getChainId(this.state.wallet.provider), l1ChainId: await getChainId(this.state.wallet.provider),
l2ChainId: await getChainId(this.options.l2RpcProvider),
}) })
this.state.highestCheckedL2Tx = this.options.fromL2TransactionIndex || 1 this.state.highestCheckedL2Tx = this.options.fromL2TransactionIndex || 1
......
...@@ -54,6 +54,7 @@ export class CrossChainMessenger implements ICrossChainMessenger { ...@@ -54,6 +54,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
public l1SignerOrProvider: Signer | Provider public l1SignerOrProvider: Signer | Provider
public l2SignerOrProvider: Signer | Provider public l2SignerOrProvider: Signer | Provider
public l1ChainId: number public l1ChainId: number
public l2ChainId: number
public contracts: OEContracts public contracts: OEContracts
public bridges: BridgeAdapters public bridges: BridgeAdapters
public depositConfirmationBlocks: number public depositConfirmationBlocks: number
...@@ -66,6 +67,7 @@ export class CrossChainMessenger implements ICrossChainMessenger { ...@@ -66,6 +67,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
* @param opts.l1SignerOrProvider Signer or Provider for the L1 chain, or a JSON-RPC url. * @param opts.l1SignerOrProvider Signer or Provider for the L1 chain, or a JSON-RPC url.
* @param opts.l2SignerOrProvider Signer or Provider for the L2 chain, or a JSON-RPC url. * @param opts.l2SignerOrProvider Signer or Provider for the L2 chain, or a JSON-RPC url.
* @param opts.l1ChainId Chain ID for the L1 chain. * @param opts.l1ChainId Chain ID for the L1 chain.
* @param opts.l2ChainId Chain ID for the L2 chain.
* @param opts.depositConfirmationBlocks Optional number of blocks before a deposit is confirmed. * @param opts.depositConfirmationBlocks Optional number of blocks before a deposit is confirmed.
* @param opts.l1BlockTimeSeconds Optional estimated block time in seconds for the L1 chain. * @param opts.l1BlockTimeSeconds Optional estimated block time in seconds for the L1 chain.
* @param opts.contracts Optional contract address overrides. * @param opts.contracts Optional contract address overrides.
...@@ -75,6 +77,7 @@ export class CrossChainMessenger implements ICrossChainMessenger { ...@@ -75,6 +77,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
l1SignerOrProvider: SignerOrProviderLike l1SignerOrProvider: SignerOrProviderLike
l2SignerOrProvider: SignerOrProviderLike l2SignerOrProvider: SignerOrProviderLike
l1ChainId: NumberLike l1ChainId: NumberLike
l2ChainId: NumberLike
depositConfirmationBlocks?: NumberLike depositConfirmationBlocks?: NumberLike
l1BlockTimeSeconds?: NumberLike l1BlockTimeSeconds?: NumberLike
contracts?: DeepPartial<OEContractsLike> contracts?: DeepPartial<OEContractsLike>
...@@ -83,24 +86,25 @@ export class CrossChainMessenger implements ICrossChainMessenger { ...@@ -83,24 +86,25 @@ export class CrossChainMessenger implements ICrossChainMessenger {
this.l1SignerOrProvider = toSignerOrProvider(opts.l1SignerOrProvider) this.l1SignerOrProvider = toSignerOrProvider(opts.l1SignerOrProvider)
this.l2SignerOrProvider = toSignerOrProvider(opts.l2SignerOrProvider) this.l2SignerOrProvider = toSignerOrProvider(opts.l2SignerOrProvider)
this.l1ChainId = toNumber(opts.l1ChainId) this.l1ChainId = toNumber(opts.l1ChainId)
this.l2ChainId = toNumber(opts.l2ChainId)
this.depositConfirmationBlocks = this.depositConfirmationBlocks =
opts?.depositConfirmationBlocks !== undefined opts?.depositConfirmationBlocks !== undefined
? toNumber(opts.depositConfirmationBlocks) ? toNumber(opts.depositConfirmationBlocks)
: DEPOSIT_CONFIRMATION_BLOCKS[this.l1ChainId] || 0 : DEPOSIT_CONFIRMATION_BLOCKS[this.l2ChainId] || 0
this.l1BlockTimeSeconds = this.l1BlockTimeSeconds =
opts?.l1BlockTimeSeconds !== undefined opts?.l1BlockTimeSeconds !== undefined
? toNumber(opts.l1BlockTimeSeconds) ? toNumber(opts.l1BlockTimeSeconds)
: CHAIN_BLOCK_TIMES[this.l1ChainId] || 1 : CHAIN_BLOCK_TIMES[this.l1ChainId] || 1
this.contracts = getAllOEContracts(this.l1ChainId, { this.contracts = getAllOEContracts(this.l2ChainId, {
l1SignerOrProvider: this.l1SignerOrProvider, l1SignerOrProvider: this.l1SignerOrProvider,
l2SignerOrProvider: this.l2SignerOrProvider, l2SignerOrProvider: this.l2SignerOrProvider,
overrides: opts.contracts, overrides: opts.contracts,
}) })
this.bridges = getBridgeAdapters(this.l1ChainId, this, { this.bridges = getBridgeAdapters(this.l2ChainId, this, {
overrides: opts.bridges, overrides: opts.bridges,
}) })
} }
......
...@@ -46,6 +46,11 @@ export interface ICrossChainMessenger { ...@@ -46,6 +46,11 @@ export interface ICrossChainMessenger {
*/ */
l1ChainId: number l1ChainId: number
/**
* Chain ID for the L2 network.
*/
l2ChainId: number
/** /**
* Contract objects attached to their respective providers and addresses. * Contract objects attached to their respective providers and addresses.
*/ */
......
...@@ -10,15 +10,26 @@ import { ICrossChainMessenger } from './cross-chain-messenger' ...@@ -10,15 +10,26 @@ import { ICrossChainMessenger } from './cross-chain-messenger'
import { IBridgeAdapter } from './bridge-adapter' import { IBridgeAdapter } from './bridge-adapter'
/** /**
* Commonly used Chain IDs * L1 network chain IDs
*/ */
export enum Chain { export enum L1ChainID {
MAINNET = 1, MAINNET = 1,
GOERLI = 5, GOERLI = 5,
KOVAN = 42, KOVAN = 42,
HARDHAT_LOCAL = 31337, HARDHAT_LOCAL = 31337,
} }
/**
* L2 network chain IDs
*/
export enum L2ChainID {
OPTIMISM = 10,
OPTIMISM_GOERLI = 420,
OPTIMISM_KOVAN = 69,
OPTIMISM_HARDHAT_LOCAL = 31337,
OPTIMISM_HARDHAT_DEVNET = 17,
}
/** /**
* L1 contract references. * L1 contract references.
*/ */
......
import { Chain } from '../interfaces' import { L1ChainID, L2ChainID } from '../interfaces'
export const DEPOSIT_CONFIRMATION_BLOCKS = { export const DEPOSIT_CONFIRMATION_BLOCKS: {
[Chain.MAINNET]: 50 as const, [ChainID in L2ChainID]: number
[Chain.GOERLI]: 12 as const, } = {
[Chain.KOVAN]: 12 as const, [L2ChainID.OPTIMISM]: 50 as const,
// 2 just for testing purposes [L2ChainID.OPTIMISM_GOERLI]: 12 as const,
[Chain.HARDHAT_LOCAL]: 2 as const, [L2ChainID.OPTIMISM_KOVAN]: 12 as const,
[L2ChainID.OPTIMISM_HARDHAT_LOCAL]: 2 as const,
[L2ChainID.OPTIMISM_HARDHAT_DEVNET]: 2 as const,
} }
export const CHAIN_BLOCK_TIMES = { export const CHAIN_BLOCK_TIMES: {
[Chain.MAINNET]: 13 as const, [ChainID in L1ChainID]: number
[Chain.GOERLI]: 15 as const, } = {
[Chain.KOVAN]: 4 as const, [L1ChainID.MAINNET]: 13 as const,
[Chain.HARDHAT_LOCAL]: 1 as const, [L1ChainID.GOERLI]: 15 as const,
[L1ChainID.KOVAN]: 4 as const,
[L1ChainID.HARDHAT_LOCAL]: 1 as const,
} }
This diff is collapsed.
This diff is collapsed.
...@@ -231,9 +231,7 @@ func (m *MockBackend) Requests() []*RecordedRequest { ...@@ -231,9 +231,7 @@ func (m *MockBackend) Requests() []*RecordedRequest {
m.mtx.RLock() m.mtx.RLock()
defer m.mtx.RUnlock() defer m.mtx.RUnlock()
out := make([]*RecordedRequest, len(m.requests)) out := make([]*RecordedRequest, len(m.requests))
for i := 0; i < len(m.requests); i++ { copy(out, m.requests)
out[i] = m.requests[i]
}
return out return out
} }
......
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