Commit 8823c67b authored by Mark Tyneway's avatar Mark Tyneway Committed by GitHub

Merge pull request #2627 from ethereum-optimism/develop

Develop -> Master PR
parents 34b89c38 3c7f3be9
---
'@eth-optimism/go-builder': patch
'@eth-optimism/js-builder': patch
---
Bump versions
---
'@eth-optimism/common-ts': patch
---
Have legacy BaseService metrics bind to 0.0.0.0 by default
---
'@eth-optimism/integration-tests-bedrock': patch
---
Bump integration test dependencies to latest for changesets
---
'@eth-optimism/contracts-periphery': minor
---
Releases the first version of the contracts-periphery package
---
'@eth-optimism/js-builder': patch
---
Add abigen
---
---
fix: op-exporter build
---
'@eth-optimism/batch-submitter-service': patch
'@eth-optimism/gas-oracle': patch
'@eth-optimism/indexer': patch
'@eth-optimism/l2geth-exporter': patch
'@eth-optimism/proxyd': patch
'@eth-optimism/teleportr': patch
---
Bump go-ethereum to 1.10.17
---
'@eth-optimism/integration-tests': patch
---
integration-tests: Override default bridge adapters
---
'@eth-optimism/go-builder': patch
---
Add abigen and gotestsum to go-builder
---
'@eth-optimism/data-transport-layer': patch
---
Fix race condition
---
'@eth-optimism/teleportr': patch
---
Only do 5 disbursements at a time
---
'@eth-optimism/integration-tests': patch
'@eth-optimism/common-ts': patch
'@eth-optimism/contracts': patch
'@eth-optimism/contracts-periphery': patch
'@eth-optimism/core-utils': patch
'@eth-optimism/data-transport-layer': patch
'@eth-optimism/fault-detector': patch
'@eth-optimism/integration-tests-bedrock': patch
'@eth-optimism/message-relayer': patch
'@eth-optimism/replica-healthcheck': patch
'@eth-optimism/sdk': patch
---
Updates all ethers versions in response to BN.js bug
---
'@eth-optimism/l2geth': patch
---
Patch for L1 syncing nodes that got stuck after DTL batch sync errors
version: 2.1 version: 2.1
orbs:
node: circleci/node@5.0.2
executors: executors:
go-builder: go-builder:
docker: docker:
...@@ -84,6 +87,7 @@ jobs: ...@@ -84,6 +87,7 @@ jobs:
- node_modules - node_modules
- packages/common-ts/node_modules - packages/common-ts/node_modules
- packages/contracts/node_modules - packages/contracts/node_modules
- packages/contracts-periphery/node_modules
- packages/core-utils/node_modules - packages/core-utils/node_modules
- packages/data-transport-layer/node_modules - packages/data-transport-layer/node_modules
- packages/fault-detector/node_modules - packages/fault-detector/node_modules
...@@ -138,6 +142,24 @@ jobs: ...@@ -138,6 +142,24 @@ jobs:
command: yarn test:coverage command: yarn test:coverage
working_directory: packages/contracts working_directory: packages/contracts
contracts-periphery-tests:
docker:
- image: ethereumoptimism/js-builder:latest
resource_class: xlarge
steps:
- restore_cache:
keys:
- v2-cache-yarn-build-{{ .Revision }}
- checkout
- run:
name: Lint
command: yarn lint:check
working_directory: packages/contracts-periphery
- run:
name: Test
command: yarn test:coverage
working_directory: packages/contracts-periphery
dtl-tests: dtl-tests:
docker: docker:
- image: ethereumoptimism/js-builder:latest - image: ethereumoptimism/js-builder:latest
...@@ -175,6 +197,10 @@ jobs: ...@@ -175,6 +197,10 @@ jobs:
name: Check contracts name: Check contracts
command: npx depcheck command: npx depcheck
working_directory: packages/contracts working_directory: packages/contracts
- run:
name: Check contracts-periphery
command: npx depcheck
working_directory: packages/contracts-periphery
- run: - run:
name: Check core-utils name: Check core-utils
command: npx depcheck command: npx depcheck
...@@ -307,10 +333,168 @@ jobs: ...@@ -307,10 +333,168 @@ jobs:
echo "$DOCKER_PASS" | docker login -u "$DOCKER_USERNAME" --password-stdin echo "$DOCKER_PASS" | docker login -u "$DOCKER_USERNAME" --password-stdin
docker push <<parameters.docker_tags>> docker push <<parameters.docker_tags>>
bedrock-markdown:
machine:
image: ubuntu-2004:202111-02
steps:
- checkout
- run:
name: markdown lint
command: |
docker run -v `pwd`:/workdir davidanson/markdownlint-cli2:0.4.0 "op-node/README.md" "./specs/**/*.md" "#**/node_modules"
- run:
name: link lint
command: |
docker run --init -it -v `pwd`:/input lycheeverse/lychee --verbose --no-progress --exclude-loopback --exclude twitter.com --exclude-mail /input/README.md "/input/specs/**/*.md" "/input/meta/**/*.md" "/input/op-node/**/*.md" || exit 0
bedrock-solidity:
docker:
- image: ethereumoptimism/js-builder:0.0.4
steps:
- checkout
- run:
name: init submodules
command: |
git submodule sync --recursive
git submodule update --recursive --init
- run:
name: install
command: yarn install
working_directory: contracts-bedrock
- run:
name: lint
command: |
# remove prettierrc in root of repo since it doesn't work with non-Lerna packages
mv ../.eslintrc.js ../.eslintrc.bak
yarn lint:check
mv ../.eslintrc.bak ../.eslintrc.js
working_directory: contracts-bedrock
- run:
name: slither
command: yarn slither || exit 0
working_directory: contracts-bedrock
- run:
name: build forge
command: yarn build:forge
working_directory: contracts-bedrock
- run:
name: test forge
command: yarn test:forge
working_directory: contracts-bedrock
- run:
name: gas snapshot
command: forge snapshot
working_directory: contracts-bedrock
- run:
name: check go bindings
command: |
make && git diff --exit-code
working_directory: op-bindings
bedrock-go-tests:
docker:
- image: ethereumoptimism/go-builder:latest
steps:
- checkout
- run:
name: lint op-bindings
command: |
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell ./...
working_directory: op-bindings
- run:
name: lint op-node
command: |
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell ./...
working_directory: op-node
- run:
name: lint op-proposer
command: |
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell ./...
working_directory: op-proposer
- run:
name: lint op-batcher
command: |
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell ./...
working_directory: op-batcher
- run:
name: lint op-e2e
command: |
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell ./...
working_directory: op-e2e
- run:
name: prep results dir
command: mkdir -p /test-results
- run:
name: test op-node
command: |
gotestsum --junitfile /test-results/op-node.xml -- -coverpkg=github.com/ethereum-optimism/optimism/... -coverprofile=coverage.out -covermode=atomic ./...
working_directory: op-node
- run:
name: test op-proposer
command: |
gotestsum --junitfile /test-results/op-proposer.xml -- -coverpkg=github.com/ethereum-optimism/optimism/... -coverprofile=coverage.out -covermode=atomic ./...
working_directory: op-proposer
- run:
name: test op-batcher
command: |
gotestsum --junitfile /test-results/op-batcher.xml -- -coverpkg=github.com/ethereum-optimism/optimism/... -coverprofile=coverage.out -covermode=atomic ./...
working_directory: op-batcher
- run:
name: test op-e2e
command: |
gotestsum --junitfile /test-results/op-e2e.xml -- -coverpkg=github.com/ethereum-optimism/optimism/... -coverprofile=coverage.out -covermode=atomic ./...
working_directory: op-e2e
- store_test_results:
path: /test-results
fuzz-op-node:
docker:
- image: ethereumoptimism/go-builder:latest
steps:
- checkout
- run:
name: Fuzz
command: make fuzz
working_directory: op-node
bedrock-integration-tests:
machine:
image: ubuntu-2004:202111-02
docker_layer_caching: true
steps:
- checkout
- run:
name: init submodules
command: |
git submodule sync --recursive
git submodule update --recursive --init
- node/install:
install-yarn: true
node-version: '16.13'
- run:
name: install Foundry
command: |
curl -L https://foundry.paradigm.xyz | bash
source /home/circleci/.bashrc
foundryup
- run:
name: build typescript
command: make build-ts
- run:
name: start devnet
command: make devnet-up
- run:
name: run itests
command: make test-integration
workflows: workflows:
main: main:
jobs: jobs:
- yarn-monorepo - yarn-monorepo
- bedrock-go-tests
- bedrock-solidity
- bedrock-markdown
- fuzz-op-node
- go-lint-test-build: - go-lint-test-build:
name: batch-submitter-tests name: batch-submitter-tests
binary_name: batch-submitter binary_name: batch-submitter
...@@ -337,6 +521,9 @@ workflows: ...@@ -337,6 +521,9 @@ workflows:
- contracts-tests: - contracts-tests:
requires: requires:
- yarn-monorepo - yarn-monorepo
- contracts-periphery-tests:
requires:
- yarn-monorepo
- js-lint-test: - js-lint-test:
name: dtl-tests name: dtl-tests
package_name: data-transport-layer package_name: data-transport-layer
...@@ -481,7 +668,7 @@ workflows: ...@@ -481,7 +668,7 @@ workflows:
- optimism - optimism
- docker-publish: - docker-publish:
name: batch-submitter-service-release name: batch-submitter-service-release
docker_file: ops/docker/Dockerfile docker_file: batch-submitter/Dockerfile
docker_tags: ethereumoptimism/batch-submitter-service:nightly docker_tags: ethereumoptimism/batch-submitter-service:nightly
docker_context: . docker_context: .
context: context:
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
- 'ops/**/*' - 'ops/**/*'
- 'packages/batch-submitter/**/*' - 'packages/batch-submitter/**/*'
- 'packages/contracts/**/*' - 'packages/contracts/**/*'
- 'packages/contracts-periphery/**/*'
- 'packages/data-transport-layer/**/*' - 'packages/data-transport-layer/**/*'
- 'packages/message-relayer/**/*' - 'packages/message-relayer/**/*'
- 'packages/fault-detector/**/*' - 'packages/fault-detector/**/*'
...@@ -25,6 +26,9 @@ M-batch-submitter: ...@@ -25,6 +26,9 @@ M-batch-submitter:
M-contracts: M-contracts:
- any: ['packages/contracts/**/*'] - any: ['packages/contracts/**/*']
M-contracts-periphery:
- any: ['packages/contracts-periphery/**/*']
M-core-utils: M-core-utils:
- any: ['packages/core-utils/**/*'] - any: ['packages/core-utils/**/*']
......
...@@ -6,6 +6,8 @@ temp ...@@ -6,6 +6,8 @@ temp
coverage.json coverage.json
*.tsbuildinfo *.tsbuildinfo
yarn-error.log
dist dist
artifacts artifacts
cache cache
...@@ -16,9 +18,15 @@ packages/contracts/coverage* ...@@ -16,9 +18,15 @@ packages/contracts/coverage*
packages/contracts/@ens* packages/contracts/@ens*
packages/contracts/@openzeppelin* packages/contracts/@openzeppelin*
packages/contracts/hardhat* packages/contracts/hardhat*
packages/contracts-periphery/coverage*
packages/contracts-periphery/@openzeppelin*
packages/contracts-periphery/hardhat*
packages/data-transport-layer/db packages/data-transport-layer/db
packages/integration-tests-bedrock/cache
packages/integration-tests-bedrock/artifacts
# vim # vim
*.sw* *.sw*
...@@ -26,3 +34,14 @@ packages/data-transport-layer/db ...@@ -26,3 +34,14 @@ packages/data-transport-layer/db
.env* .env*
!.env.example !.env.example
*.log *.log
.devnet
# Ignore local fuzzing results
**/testdata/fuzz/
coverage.out
# Ignore bedrock go bindings local output files
op-bindings/bin
op-exporter
[submodule "tests"] [submodule "tests"]
path = l2geth/tests/testdata path = l2geth/tests/testdata
url = https://github.com/ethereum/tests url = https://github.com/ethereum/tests
[submodule "packages/contracts-bedrock/lib/forge-std"]
path = contracts-bedrock/lib/forge-std
url = https://github.com/foundry-rs/forge-std.git
[submodule "packages/contracts-bedrock/lib/solmate"]
path = contracts-bedrock/lib/solmate
url = https://github.com/rari-capital/solmate.git
[submodule "packages/contracts-bedrock/lib/ds-test"]
path = contracts-bedrock/lib/ds-test
url = https://github.com/dapphub/ds-test.git
{
"line_length": {
"line_length": 120,
"strict": false,
"stern": true,
"code_blocks": false,
"tables": false,
},
"no-blanks-blockquote": false,
"single-title": false,
"no-emphasis-as-heading": false,
}
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
{"directory": "packages/core-utils", "changeProcessCWD": true }, {"directory": "packages/core-utils", "changeProcessCWD": true },
{"directory": "packages/common-ts", "changeProcessCWD": true }, {"directory": "packages/common-ts", "changeProcessCWD": true },
{"directory": "packages/contracts", "changeProcessCWD": true }, {"directory": "packages/contracts", "changeProcessCWD": true },
{"directory": "packages/contracts-periphery", "changeProcessCWD": true },
{"directory": "packages/data-transport-layer", "changeProcessCWD": true }, {"directory": "packages/data-transport-layer", "changeProcessCWD": true },
{"directory": "packages/batch-submitter", "changeProcessCWD": true }, {"directory": "packages/batch-submitter", "changeProcessCWD": true },
{"directory": "packages/message-relayer", "changeProcessCWD": true }, {"directory": "packages/message-relayer", "changeProcessCWD": true },
......
COMPOSEFLAGS=-d
ITESTS_L2_HOST=http://localhost:9545
build: build-go contracts integration-tests
.PHONY: build
build-go: submodules op-node op-proposer op-batcher
.PHONY: build-go
build-ts: submodules contracts integration-tests
.PHONY: build-ts
submodules:
# CI will checkout submodules on its own (and fails on these commands)
if [ -z "$$GITHUB_ENV" ]; then \
git submodule init; \
git submodule update; \
fi
.PHONY: submodules
op-bindings:
make -C ./op-bindings
.PHONY: op-bindings
op-node:
make -C ./op-node op-node
.PHONY: op-node
op-batcher:
make -C ./op-batcher op-batcher
.PHONY: op-batcher
op-proposer:
make -C ./op-proposer op-proposer
.PHONY: op-proposer
mod-tidy:
cd ./op-node && go mod tidy && cd .. && \
cd ./op-proposer && go mod tidy && cd .. && \
cd ./op-batcher && go mod tidy && cd .. && \
cd ./op-bindings && go mod tidy && cd .. && \
cd ./op-e2e && go mod tidy && cd ..
.PHONY: mod-tidy
contracts:
cd ./contracts-bedrock && yarn install && yarn build
.PHONY: contracts
integration-tests:
cd ./packages/integration-tests-bedrock && yarn install && yarn build:contracts
.PHONY: integration-tests
clean:
rm -rf ./bin
.PHONY: clean
devnet-up:
@bash ./ops-bedrock/devnet-up.sh
.PHONY: devnet-up
devnet-down:
@(cd ./ops-bedrock && GENESIS_TIMESTAMP=$(shell date +%s) docker-compose stop)
.PHONY: devnet-down
devnet-clean:
rm -rf ./contracts-bedrock/deployments/devnetL1
rm -rf ./.devnet
cd ./ops-bedrock && docker-compose down
docker volume rm ops-bedrock_l1_data
docker volume rm ops-bedrock_l2_data
docker volume rm ops-bedrock_op_log
.PHONY: devnet-clean
test-unit:
make -C ./op-node test
make -C ./op-proposer test
make -C ./op-batcher test
make -C ./op-e2e test
cd ./contracts-bedrock && yarn test
.PHONY: test-unit
test-integration:
bash ./ops-bedrock/test-integration.sh \
./contracts-bedrock/deployments/devnetL1
.PHONY: test-integration
devnet-genesis:
bash ./ops-bedrock/devnet-genesis.sh
.PHONY: devnet-genesis
<div align="center"> <div align="center">
<a href="https://community.optimism.io"><img alt="Optimism" src="https://user-images.githubusercontent.com/14298799/122151157-0b197500-ce2d-11eb-89d8-6240e3ebe130.png" width=280></a> <a href="https://community.optimism.io"><img alt="Optimism" src="https://raw.githubusercontent.com/ethereum-optimism/brand-kit/ad481978ceac19f5c1db91ca4586bf2ab238f46b/assets/svg/Profile-Logo.svg" width=280></a>
<br /> <br />
<h1> The Optimism Monorepo</h1> <h1> The Optimism Monorepo</h1>
</div> </div>
...@@ -31,21 +31,40 @@ Then check out our list of [good first issues](https://github.com/ethereum-optim ...@@ -31,21 +31,40 @@ Then check out our list of [good first issues](https://github.com/ethereum-optim
<pre> <pre>
root root
├── <a href="./packages">packages</a> ├── <a href="./packages">packages</a>
│ ├── <a href="./packages/common-ts">common-ts</a>: Common tools for building apps in TypeScript
│ ├── <a href="./packages/contracts">contracts</a>: L1 and L2 smart contracts for Optimism │ ├── <a href="./packages/contracts">contracts</a>: L1 and L2 smart contracts for Optimism
│ ├── <a href="./packages/contracts-periphery">contracts-periphery</a>: Peripheral contracts for Optimism
│ ├── <a href="./packages/core-utils">core-utils</a>: Low-level utilities that make building Optimism easier │ ├── <a href="./packages/core-utils">core-utils</a>: Low-level utilities that make building Optimism easier
│ ├── <a href="./packages/common-ts">common-ts</a>: Common tools for building apps in TypeScript
│ ├── <a href="./packages/data-transport-layer">data-transport-layer</a>: Service for indexing Optimism-related L1 data │ ├── <a href="./packages/data-transport-layer">data-transport-layer</a>: Service for indexing Optimism-related L1 data
│ ├── <a href="./packages/fault-detector">fault-detector</a>: Service for detecting faulty L2 output proposals │ ├── <a href="./packages/fault-detector">fault-detector</a>:
│ ├── <a href="./packages/integration-tests-bedrock">integration-tests-bedrock</a> (BEDROCK upgrade): Bedrock integration tests.
│ ├── <a href="./packages/message-relayer">message-relayer</a>: Tool for automatically relaying L1<>L2 messages in development │ ├── <a href="./packages/message-relayer">message-relayer</a>: Tool for automatically relaying L1<>L2 messages in development
│ └── <a href="./packages/replica-healthcheck">replica-healthcheck</a>: Service for monitoring the health of a replica node │ ├── <a href="./packages/replica-healthcheck">replica-healthcheck</a>: Service for monitoring the health of a replica node
├── <a href="./go">go</a> │ └── <a href="./packages/sdk">sdk</a>: provides a set of tools for interacting with Optimism
│ ├── <a href="./batch-submitter">batch-submitter</a>: Service for submitting batches of transactions and results to L1
│ ├── <a href="./bss-core">bss-core</a>: Core batch-submitter logic and utilities ~~ Production ~~
│ ├── <a href="./gas-oracle">gas-oracle</a>: Service for updating L1 gas prices on L2 ├── <a href="./batch-submitter">batch-submitter</a>: Service for submitting batches of transactions and results to L1
│ └── <a href="./proxyd">proxyd</a>: Configurable RPC request router and proxy ├── <a href="./bss-core">bss-core</a>: Core batch-submitter logic and utilities
├── <a href="./l2geth">l2geth</a>: Optimism client software, a fork of <a href="https://github.com/ethereum/go-ethereum/tree/v1.9.10">geth v1.9.10</a> ├── <a href="./gas-oracle">gas-oracle</a>: Service for updating L1 gas prices on L2
├── <a href="./indexer">indexer</a>: indexes and syncs transactions
├── <a href="./infra/op-replica">infra/op-replica</a>: Deployment examples and resources for running an Optimism replica
├── <a href="./integration-tests">integration-tests</a>: Various integration tests for the Optimism network ├── <a href="./integration-tests">integration-tests</a>: Various integration tests for the Optimism network
└── <a href="./ops">ops</a>: Tools for running Optimism nodes and networks ├── <a href="./l2geth">l2geth</a>: Optimism client software, a fork of <a href="https://github.com/ethereum/go-ethereum/tree/v1.9.10">geth v1.9.10</a> (deprecated for BEDROCK upgrade)
├── <a href="./l2geth-exporter">l2geth-exporter</a>: A prometheus exporter to collect/serve metrics from an L2 geth node
├── <a href="./op-exporter">op-exporter</a>: A prometheus exporter to collect/serve metrics from an Optimism node
├── <a href="./proxyd">proxyd</a>: Configurable RPC request router and proxy
├── <a href="./technical-documents">technical-documents</a>: audits and post-mortem documents
├── <a href="./teleportr">teleportr</a>: Bridge for teleporting ETH between L1 and L2 at low cost
~~ BEDROCK upgrade - Not production-ready yet, part of next major upgrade ~~
├── <a href="./contracts-bedrock">contracts-bedrock</a>: Bedrock smart contracts. To be merged with ./packages/contracts.
├── <a href="./op-bindings">op-bindings</a>: Go bindings for Bedrock smart contracts.
├── <a href="./op-batcher">op-batcher</a>: L2-Batch Submitter, submits bundles of batches to L1
├── <a href="./op-e2e">op-e2e</a>: End-to-End testing of all bedrock components in Go
├── <a href="./op-node">op-node</a>: rollup consensus-layer client.
├── <a href="./op-proposer">op-proposer</a>: L2-Output Submitter, submits proposals to L1
├── <a href="./ops-bedrock">ops-bedrock</a>: Bedrock devnet work
└── <a href="./specs">specs</a>: Specs of the rollup starting at the Bedrock upgrade
</pre> </pre>
## Branching Model and Releases ## Branching Model and Releases
......
module github.com/ethereum-optimism/optimism/batch-submitter module github.com/ethereum-optimism/optimism/batch-submitter
go 1.16 go 1.18
replace github.com/ethereum-optimism/optimism/bss-core v0.0.0 => ../bss-core replace github.com/ethereum-optimism/optimism/bss-core v0.0.0 => ../bss-core
replace github.com/ethereum-optimism/optimism/l2geth v0.0.0 => ../l2geth replace github.com/ethereum-optimism/optimism/l2geth v0.0.0 => ../l2geth
require ( require (
github.com/ethereum-optimism/optimism/bss-core v0.0.0 github.com/ethereum-optimism/optimism/bss-core v0.0.0
github.com/ethereum-optimism/optimism/l2geth v0.0.0 github.com/ethereum-optimism/optimism/l2geth v0.0.0
github.com/ethereum/go-ethereum v1.10.16 github.com/ethereum/go-ethereum v1.10.16
github.com/getsentry/sentry-go v0.11.0 github.com/getsentry/sentry-go v0.12.0
github.com/prometheus/client_golang v1.11.0 github.com/prometheus/client_golang v1.11.0
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
github.com/urfave/cli v1.22.5 github.com/urfave/cli v1.22.5
) )
require (
github.com/VictoriaMetrics/fastcache v1.9.0 // indirect
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd v0.22.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deckarep/golang-set v1.8.0 // indirect
github.com/decred/base58 v1.0.3 // indirect
github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
github.com/decred/dcrd/crypto/ripemd160 v1.0.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 // indirect
github.com/decred/dcrd/hdkeychain/v3 v3.0.0 // indirect
github.com/elastic/gosigar v0.12.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
github.com/holiman/uint256 v1.2.0 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mitchellh/pointerstructure v1.2.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.30.0 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/prometheus/tsdb v0.10.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rjeczalik/notify v0.9.2 // indirect
github.com/rs/cors v1.8.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 // indirect
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.4.0 // indirect
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70 // indirect
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -6,6 +6,8 @@ import ( ...@@ -6,6 +6,8 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/tyler-smith/go-bip39"
bsscore "github.com/ethereum-optimism/optimism/bss-core" bsscore "github.com/ethereum-optimism/optimism/bss-core"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
...@@ -100,7 +102,7 @@ func TestDerivePrivateKey(t *testing.T) { ...@@ -100,7 +102,7 @@ func TestDerivePrivateKey(t *testing.T) {
name: "invalid mnemonic", name: "invalid mnemonic",
mnemonic: invalidMnemonic, mnemonic: invalidMnemonic,
hdPath: validHDPath, hdPath: validHDPath,
expErr: errors.New("Checksum incorrect"), expErr: bip39.ErrInvalidMnemonic, // the bip39 lib spells mnemonic wrong...
}, },
{ {
name: "valid mnemonic invalid hdpath", name: "valid mnemonic invalid hdpath",
......
module github.com/ethereum-optimism/optimism/bss-core module github.com/ethereum-optimism/optimism/bss-core
go 1.16 go 1.18
require ( require (
github.com/btcsuite/btcd v0.22.0-beta // indirect
github.com/decred/dcrd/hdkeychain/v3 v3.0.0 github.com/decred/dcrd/hdkeychain/v3 v3.0.0
github.com/ethereum/go-ethereum v1.10.16 github.com/ethereum/go-ethereum v1.10.17
github.com/getsentry/sentry-go v0.11.0 github.com/getsentry/sentry-go v0.12.0
github.com/prometheus/client_golang v1.11.0 github.com/prometheus/client_golang v1.11.0
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef github.com/tyler-smith/go-bip39 v1.1.0
)
require (
github.com/VictoriaMetrics/fastcache v1.9.0 // indirect
github.com/allegro/bigcache v1.2.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd v0.22.0-beta // indirect
github.com/btcsuite/btcd/btcec/v2 v2.1.2 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deckarep/golang-set v1.8.0 // indirect
github.com/decred/base58 v1.0.3 // indirect
github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
github.com/decred/dcrd/crypto/ripemd160 v1.0.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/edsrzf/mmap-go v1.1.0 // indirect
github.com/fjl/memsize v0.0.1 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
github.com/go-kit/kit v0.10.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/go-bexpr v0.1.11 // indirect
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
github.com/holiman/uint256 v1.2.0 // indirect
github.com/huin/goupnp v1.0.3 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/ginkgo v1.16.4 // indirect
github.com/onsi/gomega v1.16.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.30.0 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/prometheus/tsdb v0.10.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rjeczalik/notify v0.9.2 // indirect
github.com/rs/cors v1.8.2 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/status-im/keycard-go v0.0.0-20211109104530-b0e0482ba91d // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.4.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70 // indirect
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
) )
This source diff could not be displayed because it is too large. You can view the blob instead.
# Deps and test files
node_modules
lib
# build output
artifacts
forge-artifacts
cache
typechain
coverage*
module.exports = {
env: {
browser: false,
es2021: true,
mocha: true,
node: true,
},
plugins: ['@typescript-eslint'],
extends: [
'standard',
'plugin:prettier/recommended',
'plugin:node/recommended',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 12,
},
rules: {
'node/no-unpublished-import': 'off',
'node/no-unsupported-features/es-syntax': [
'error',
{ ignores: ['modules'] },
],
},
}
CrossDomainHashing_Test:test_l2TransactionHash() (gas: 78639)
DeployerWhitelist_Test:test_owner() (gas: 7647)
DeployerWhitelist_Test:test_storageSlots() (gas: 33483)
GasPriceOracle_Test:test_baseFee() (gas: 8395)
GasPriceOracle_Test:test_gasPrice() (gas: 8384)
GasPriceOracle_Test:test_l1BaseFee() (gas: 10626)
GasPriceOracle_Test:test_onlyOwnerSetDecimals() (gas: 10575)
GasPriceOracle_Test:test_onlyOwnerSetOverhead() (gas: 10599)
GasPriceOracle_Test:test_onlyOwnerSetScalar() (gas: 10640)
GasPriceOracle_Test:test_owner() (gas: 9762)
GasPriceOracle_Test:test_setDecimals() (gas: 36798)
GasPriceOracle_Test:test_setGasPriceReverts() (gas: 11659)
GasPriceOracle_Test:test_setL1BaseFeeReverts() (gas: 11658)
GasPriceOracle_Test:test_setOverhead() (gas: 36767)
GasPriceOracle_Test:test_setScalar() (gas: 36840)
GasPriceOracle_Test:test_storageLayout() (gas: 86683)
L1BlockTest:test_basefee() (gas: 7575)
L1BlockTest:test_hash() (gas: 7552)
L1BlockTest:test_number() (gas: 7651)
L1BlockTest:test_sequenceNumber() (gas: 7585)
L1BlockTest:test_timestamp() (gas: 7683)
L1BlockTest:test_updateValues() (gas: 28215)
L1BlockNumberTest:test_fallback() (gas: 10755)
L1BlockNumberTest:test_getL1BlockNumber() (gas: 10589)
L1BlockNumberTest:test_receive() (gas: 17418)
L1CrossDomainMessenger_Test:testCannot_L1MessengerPause() (gas: 10909)
L1CrossDomainMessenger_Test:test_L1MessengerMessageVersion() (gas: 8366)
L1CrossDomainMessenger_Test:test_L1MessengerPause() (gas: 31882)
L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageSucceeds() (gas: 61129)
L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageToSystemContract() (gas: 44815)
L1CrossDomainMessenger_Test:test_L1MessengerRelayShouldRevertIfPaused() (gas: 41631)
L1CrossDomainMessenger_Test:test_L1MessengerSendMessage() (gas: 78105)
L1CrossDomainMessenger_Test:test_L1MessengerTwiceSendMessage() (gas: 66345)
L1CrossDomainMessenger_Test:test_L1MessengerXDomainSenderReverts() (gas: 10566)
L1CrossDomainMessenger_Test:test_L1MessengerxDomainMessageSenderResets() (gas: 58425)
L1StandardBridge_Test:test_depositERC20() (gas: 371479)
L1StandardBridge_Test:test_depositERC20To() (gas: 373256)
L1StandardBridge_Test:test_depositETH() (gas: 105061)
L1StandardBridge_Test:test_depositETHTo() (gas: 111945)
L1StandardBridge_Test:test_donateETH() (gas: 17545)
L1StandardBridge_Test:test_finalizeERC20Withdrawal() (gas: 438817)
L1StandardBridge_Test:test_finalizeETHWithdrawal() (gas: 48005)
L1StandardBridge_Test:test_initialize() (gas: 14885)
L1StandardBridge_Test:test_onlyEOADepositERC20() (gas: 12085)
L1StandardBridge_Test:test_onlyEOADepositETH() (gas: 30637)
L1StandardBridge_Test:test_onlyL2BridgeFinalizeERC20Withdrawal() (gas: 23565)
L1StandardBridge_Test:test_onlyPortalFinalizeERC20Withdrawal() (gas: 22919)
L1StandardBridge_Test:test_receive() (gas: 99476)
L2CrossDomainMessenger_Test:testCannot_L2MessengerPause() (gas: 10843)
L2CrossDomainMessenger_Test:test_L2MessengerMessageVersion() (gas: 8410)
L2CrossDomainMessenger_Test:test_L2MessengerPause() (gas: 31837)
L2CrossDomainMessenger_Test:test_L2MessengerRelayMessageSucceeds() (gas: 57474)
L2CrossDomainMessenger_Test:test_L2MessengerRelayMessageToSystemContract() (gas: 24567)
L2CrossDomainMessenger_Test:test_L2MessengerRelayShouldRevertIfPaused() (gas: 41599)
L2CrossDomainMessenger_Test:test_L2MessengerSendMessage() (gas: 119659)
L2CrossDomainMessenger_Test:test_L2MessengerTwiceSendMessage() (gas: 133096)
L2CrossDomainMessenger_Test:test_L2MessengerXDomainSenderReverts() (gas: 10588)
L2CrossDomainMessenger_Test:test_L2MessengerxDomainMessageSenderResets() (gas: 54795)
L2OutputOracleTest:testCannot_appendCurrentTimestamp() (gas: 18627)
L2OutputOracleTest:testCannot_appendEmptyOutput() (gas: 16756)
L2OutputOracleTest:testCannot_appendFutureTimestamp() (gas: 18708)
L2OutputOracleTest:testCannot_appendOutputIfNotSequencer() (gas: 16458)
L2OutputOracleTest:testCannot_appendUnexpectedTimestamp() (gas: 18893)
L2OutputOracleTest:testCannot_computePreHistoricalL2BlockNumber() (gas: 11093)
L2OutputOracleTest:testCannot_deleteL2Output_ifNotSequencer() (gas: 18793)
L2OutputOracleTest:testCannot_deleteWrongL2Output() (gas: 77352)
L2OutputOracleTest:test_appendingAnotherOutput() (gas: 68582)
L2OutputOracleTest:test_computeL2BlockNumber() (gas: 14755)
L2OutputOracleTest:test_constructor() (gas: 33752)
L2OutputOracleTest:test_deleteL2Output() (gas: 64338)
L2OutputOracleTest:test_getL2Output() (gas: 74601)
L2OutputOracleTest:test_latestBlockTimestamp() (gas: 68377)
L2OutputOracleTest:test_nextTimestamp() (gas: 9236)
L2StandardBridge_Test:test_finalizeDeposit() (gas: 93191)
L2StandardBridge_Test:test_initialize() (gas: 14834)
L2StandardBridge_Test:test_receive() (gas: 136437)
L2StandardBridge_Test:test_withdraw() (gas: 352644)
L2StandardBridge_Test:test_withdrawTo() (gas: 353495)
L2ToL1MessagePasserTest:test_burn() (gas: 112023)
L2ToL1MessagePasserTest:test_initiateWithdrawal_fromContract() (gas: 67890)
L2ToL1MessagePasserTest:test_initiateWithdrawal_fromEOA() (gas: 74851)
OVM_ETH_Test:test_approve() (gas: 10695)
OVM_ETH_Test:test_burn() (gas: 10609)
OVM_ETH_Test:test_crossDomain() (gas: 10599)
OVM_ETH_Test:test_decreaseAllowance() (gas: 10731)
OVM_ETH_Test:test_increaseAllowance() (gas: 10763)
OVM_ETH_Test:test_metadata() (gas: 15608)
OVM_ETH_Test:test_mint() (gas: 10621)
OVM_ETH_Test:test_transfer() (gas: 10726)
OVM_ETH_Test:test_transferFrom() (gas: 13008)
OptimismMintableTokenFactory_Test:test_bridge() (gas: 9850)
OptimismMintableTokenFactory_Test:test_burn() (gas: 52791)
OptimismMintableTokenFactory_Test:test_burnRevertsFromNotBridge() (gas: 13211)
OptimismMintableTokenFactory_Test:test_l1Token() (gas: 9779)
OptimismMintableTokenFactory_Test:test_l2Bridge() (gas: 9768)
OptimismMintableTokenFactory_Test:test_mint() (gas: 65732)
OptimismMintableTokenFactory_Test:test_mintRevertsFromNotBridge() (gas: 13213)
OptimismMintableTokenFactory_Test:test_remoteToken() (gas: 9762)
OptimismMintableTokenFactory_Test:test_bridge() (gas: 9707)
OptimismMintableTokenFactory_Test:test_createStandardL2Token() (gas: 1106538)
OptimismMintableTokenFactory_Test:test_createStandardL2TokenSameTwice() (gas: 2193987)
OptimismMintableTokenFactory_Test:test_createStandardL2TokenShouldRevertIfRemoteIsZero() (gas: 9374)
OptimismMintableTokenFactory_Test:test_initializeShouldRevert() (gas: 12696)
OptimismPortal_Test:test_OptimismPortalConstructor() (gas: 11302)
OptimismPortal_Test:test_OptimismPortalContractCreationReverts() (gas: 9421)
OptimismPortal_Test:test_OptimismPortalReceiveEth() (gas: 24732)
OptimismPortal_Test:test_cannotVerifyRecentWithdrawal() (gas: 19657)
OptimismPortal_Test:test_depositTransaction_NoValueContract() (gas: 24478)
OptimismPortal_Test:test_depositTransaction_NoValueEOA() (gas: 24846)
OptimismPortal_Test:test_depositTransaction_createWithZeroValueForContract() (gas: 24497)
OptimismPortal_Test:test_depositTransaction_createWithZeroValueForEOA() (gas: 24841)
OptimismPortal_Test:test_depositTransaction_withEthValueAndContractContractCreation() (gas: 31519)
OptimismPortal_Test:test_depositTransaction_withEthValueAndEOAContractCreation() (gas: 22971)
OptimismPortal_Test:test_depositTransaction_withEthValueFromContract() (gas: 31210)
OptimismPortal_Test:test_depositTransaction_withEthValueFromEOA() (gas: 31781)
OptimismPortal_Test:test_invalidWithdrawalProof() (gas: 26565)
SequencerFeeVault_Test:test_constructor() (gas: 7611)
SequencerFeeVault_Test:test_minWithdrawalAmount() (gas: 5429)
SequencerFeeVault_Test:test_receive() (gas: 17280)
SequencerFeeVault_Test:test_revertWithdraw() (gas: 9245)
SequencerFeeVault_Test:test_withdraw() (gas: 147274)
artifacts
forge-artifacts
cache
typechain
coverage.out
.deps
deployments
\ No newline at end of file
hardhat.config.ts
scripts
test
# Deps and test files
node_modules
lib
contracts/test/*.t.sol
# build output
artifacts
forge-artifacts
cache
typechain
coverage*
contracts/L2/WETH9.sol
# Other autogenerated files
gasReporterOutput.json
slither.db.json
module.exports = {
$schema: 'http://json.schemastore.org/prettierrc',
trailingComma: 'es5',
tabWidth: 2,
semi: false,
singleQuote: true,
arrowParens: 'always',
overrides: [
{
files: '*.sol',
options: {
// These options are native to Prettier.
printWidth: 100,
tabWidth: 4,
useTabs: false,
singleQuote: false,
bracketSpacing: true,
// These options are specific to the Solidity Plugin
explicitTypes: 'always',
compiler: '0.8.10',
},
},
],
}
{
"extends": "solhint:recommended",
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error",
"compiler-version": "off",
"code-complexity": ["warn", 5],
"max-line-length": ["error", 100],
"func-param-name-mixedcase": "error",
"modifier-name-mixedcase": "error",
"ordering": "warn",
"avoid-low-level-calls": "off",
"event-name-camelcase": "off",
"reason-string": "off",
"avoid-tx-origin": "off",
"func-visibility": ["warn", { "ignoreConstructors": true }]
}
}
# Deps and test files
node_modules
lib
contracts/test/*.t.sol
contracts/L2/WETH9.sol
MIT License
Copyright (c) 2022 Optimism
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# Optimism: Bedrock Edition - Contracts
## Install
The repo currently uses a mix of typescript tests (run with HardHat) and solidity tests (run with Forge). The project
uses the default hardhat directory structure, and all build/test steps should be run using the yarn scripts to ensure
the correct options are set.
Install node modules with yarn (v1), and Node.js (14+).
```shell
yarn
```
See installation instructions for forge [here](https://github.com/gakonst/foundry).
## Build
```shell
yarn build
```
## Running Tests
First get the dependencies:
`git submodule init` and `git submodule update`
Then the full test suite can be executed via `yarn`:
```shell
yarn test
```
To run only typescript tests:
```shell
yarn test:hh
```
To run only solidity tests:
```shell
yarn test:forge
```
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import {
Lib_PredeployAddresses
} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
import { OptimismPortal } from "./OptimismPortal.sol";
import { CrossDomainMessenger } from "../universal/CrossDomainMessenger.sol";
/**
* @title L1CrossDomainMessenger
* @dev The L1 Cross Domain Messenger contract sends messages from L1 to L2, and relays messages
* from L2 onto L1.
* This contract should be deployed behind an upgradable proxy
*/
contract L1CrossDomainMessenger is CrossDomainMessenger {
/*************
* Variables *
*************/
/**
* @notice Address of the OptimismPortal.
*/
OptimismPortal public portal;
/********************
* Public Functions *
********************/
/**
* @notice Initialize the L1CrossDomainMessenger
* @param _portal The OptimismPortal
*/
function initialize(OptimismPortal _portal) external {
portal = _portal;
address[] memory blockedSystemAddresses = new address[](1);
blockedSystemAddresses[0] = address(this);
_initialize(Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER, blockedSystemAddresses);
}
/**********************
* Internal Functions *
**********************/
/**
* @notice Ensure that the L1CrossDomainMessenger can only be called
* by the OptimismPortal and the L2 sender is the L2CrossDomainMessenger.
*/
function _isSystemMessageSender() internal view override returns (bool) {
return msg.sender == address(portal) && portal.l2Sender() == otherMessenger;
}
/**
* @notice Sending a message in the L1CrossDomainMessenger involves
* depositing through the OptimismPortal.
*/
function _sendMessage(
address _to,
uint64 _gasLimit,
uint256 _value,
bytes memory _data
) internal override {
portal.depositTransaction{ value: _value }(_to, _value, _gasLimit, false, _data);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import {
Lib_PredeployAddresses
} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
import { StandardBridge } from "../universal/StandardBridge.sol";
/**
* @title L1StandardBridge
* @dev The L1 ETH and ERC20 Bridge is a contract which stores deposited L1 funds and standard
* tokens that are in use on L2. It synchronizes a corresponding L2 Bridge, informing it of deposits
* and listening to it for newly finalized withdrawals.
*/
contract L1StandardBridge is StandardBridge {
/**********
* Events *
**********/
event ETHDepositInitiated(
address indexed _from,
address indexed _to,
uint256 _amount,
bytes _data
);
event ETHWithdrawalFinalized(
address indexed _from,
address indexed _to,
uint256 _amount,
bytes _data
);
event ERC20DepositInitiated(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event ERC20WithdrawalFinalized(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
/********************
* Public Functions *
********************/
/**
* @dev initialize the L1StandardBridge with the address of the
* messenger in the same domain
*/
function initialize(address payable _messenger) public {
_initialize(_messenger, payable(Lib_PredeployAddresses.L2_STANDARD_BRIDGE));
}
/**
* @dev Get the address of the corresponding L2 bridge contract.
* This is a legacy getter, provided for backwards compatibility.
* @return Address of the corresponding L2 bridge contract.
*/
function l2TokenBridge() external returns (address) {
return address(otherBridge);
}
/**
* @dev Deposit an amount of the ETH to the caller's balance on L2.
* @param _minGasLimit limit required to complete the deposit on L2.
* @param _data Optional data to forward to L2. This data is provided
* solely as a convenience for external contracts. Aside from enforcing a maximum
* length, these contracts provide no guarantees about its content.
*/
function depositETH(uint32 _minGasLimit, bytes calldata _data) external payable onlyEOA {
_initiateETHDeposit(msg.sender, msg.sender, _minGasLimit, _data);
}
/**
* @dev Deposit an amount of ETH to a recipient's balance on L2.
* @param _to L2 address to credit the withdrawal to.
* @param _minGasLimit Gas limit required to complete the deposit on L2.
* @param _data Optional data to forward to L2. This data is provided
* solely as a convenience for external contracts. Aside from enforcing a maximum
* length, these contracts provide no guarantees about its content.
*/
function depositETHTo(
address _to,
uint32 _minGasLimit,
bytes calldata _data
) external payable {
_initiateETHDeposit(msg.sender, _to, _minGasLimit, _data);
}
/**
* @dev deposit an amount of the ERC20 to the caller's balance on L2.
* @param _l1Token Address of the L1 ERC20 we are depositing
* @param _l2Token Address of the L1 respective L2 ERC20
* @param _amount Amount of the ERC20 to deposit
* @param _minGasLimit limit required to complete the deposit on L2.
* @param _data Optional data to forward to L2. This data is provided
* solely as a convenience for external contracts. Aside from enforcing a maximum
* length, these contracts provide no guarantees about its content.
*/
function depositERC20(
address _l1Token,
address _l2Token,
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _data
) external virtual onlyEOA {
_initiateERC20Deposit(
_l1Token,
_l2Token,
msg.sender,
msg.sender,
_amount,
_minGasLimit,
_data
);
}
/**
* @dev deposit an amount of ERC20 to a recipient's balance on L2.
* @param _l1Token Address of the L1 ERC20 we are depositing
* @param _l2Token Address of the L1 respective L2 ERC20
* @param _to L2 address to credit the withdrawal to.
* @param _amount Amount of the ERC20 to deposit.
* @param _minGasLimit Gas limit required to complete the deposit on L2.
* @param _data Optional data to forward to L2. This data is provided
* solely as a convenience for external contracts. Aside from enforcing a maximum
* length, these contracts provide no guarantees about its content.
*/
function depositERC20To(
address _l1Token,
address _l2Token,
address _to,
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _data
) external virtual {
_initiateERC20Deposit(_l1Token, _l2Token, msg.sender, _to, _amount, _minGasLimit, _data);
}
function finalizeETHWithdrawal(
address _from,
address _to,
uint256 _amount,
bytes calldata _data
) external payable onlyOtherBridge {
emit ETHWithdrawalFinalized(_from, _to, _amount, _data);
finalizeBridgeETH(_from, _to, _amount, _data);
}
/**
* @dev Complete a withdrawal from L2 to L1, and credit funds to the recipient's balance of the
* L1 ERC20 token.
* This call will fail if the initialized withdrawal from L2 has not been finalized.
*
* @param _l1Token Address of L1 token to finalizeWithdrawal for.
* @param _l2Token Address of L2 token where withdrawal was initiated.
* @param _from L2 address initiating the transfer.
* @param _to L1 address to credit the withdrawal to.
* @param _amount Amount of the ERC20 to deposit.
* @param _data Data provided by the sender on L2. This data is provided
* solely as a convenience for external contracts. Aside from enforcing a maximum
* length, these contracts provide no guarantees about its content.
*/
function finalizeERC20Withdrawal(
address _l1Token,
address _l2Token,
address _from,
address _to,
uint256 _amount,
bytes calldata _data
) external onlyOtherBridge {
emit ERC20WithdrawalFinalized(_l1Token, _l2Token, _from, _to, _amount, _data);
finalizeBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _data);
}
/**********************
* Internal Functions *
**********************/
function _initiateETHDeposit(
address _from,
address _to,
uint32 _minGasLimit,
bytes memory _data
) internal {
emit ETHDepositInitiated(_from, _to, msg.value, _data);
_initiateBridgeETH(_from, _to, msg.value, _minGasLimit, _data);
}
function _initiateERC20Deposit(
address _l1Token,
address _l2Token,
address _from,
address _to,
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _data
) internal {
emit ERC20DepositInitiated(_l1Token, _l2Token, _from, _to, _amount, _data);
_initiateBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _minGasLimit, _data);
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title L2OutputOracle
* @notice The L2 state is committed to in this contract
* The payable keyword is used on appendL2Output to save gas on the msg.value check.
* This contract should be deployed behind an upgradable proxy
*/
// slither-disable-next-line locked-ether
contract L2OutputOracle is Ownable {
/**********
* Events *
**********/
/// @notice Emitted when an output is appended.
event l2OutputAppended(
bytes32 indexed _l2Output,
uint256 indexed _l1Timestamp,
uint256 indexed _l2timestamp
);
/// @notice Emitted when an output is deleted.
event l2OutputDeleted(
bytes32 indexed _l2Output,
uint256 indexed _l1Timestamp,
uint256 indexed _l2timestamp
);
/**********************
* Contract Variables *
**********************/
/// @notice The interval in seconds at which checkpoints must be submitted.
uint256 public immutable SUBMISSION_INTERVAL;
/// @notice The time between blocks on L2.
uint256 public immutable L2_BLOCK_TIME;
/// @notice The number of blocks in the chain before the first block in this contract.
uint256 public immutable HISTORICAL_TOTAL_BLOCKS;
/// @notice The timestamp of the first L2 block recorded in this contract.
uint256 public immutable STARTING_BLOCK_TIMESTAMP;
/// @notice The timestamp of the most recent L2 block recorded in this contract.
uint256 public latestBlockTimestamp;
/// @notice A mapping from L2 timestamps to the output root for the block with that timestamp.
mapping(uint256 => OutputProposal) internal l2Outputs;
/// @notice OutputProposal represents a commitment to the L2 state.
/// The timestamp is the L1 timestamp that the output root is posted.
/// This timestamp is used to verify that the finalization period
/// has passed since the output root was submitted.
struct OutputProposal {
bytes32 outputRoot;
uint256 timestamp;
}
/***************
* Constructor *
***************/
/**
* @notice Initialize the L2OutputOracle contract.
* @param _submissionInterval The desired interval in seconds at which
* checkpoints must be submitted.
* @param _l2BlockTime The desired L2 inter-block time in seconds.
* @param _genesisL2Output The initial L2 output of the L2 chain.
* @param _historicalTotalBlocks The number of blocks that preceding the
* initialization of the L2 chain.
* @param _startingBlockTimestamp The timestamp to start L2 block at.
*/
constructor(
uint256 _submissionInterval,
uint256 _l2BlockTime,
bytes32 _genesisL2Output,
uint256 _historicalTotalBlocks,
uint256 _startingBlockTimestamp,
address sequencer
) {
require(
_submissionInterval % _l2BlockTime == 0,
"Submission Interval must be a multiple of L2 Block Time"
);
SUBMISSION_INTERVAL = _submissionInterval;
L2_BLOCK_TIME = _l2BlockTime;
// solhint-disable-next-line not-rely-on-time
l2Outputs[_startingBlockTimestamp] = OutputProposal(_genesisL2Output, block.timestamp);
HISTORICAL_TOTAL_BLOCKS = _historicalTotalBlocks;
// solhint-disable-next-line not-rely-on-time
latestBlockTimestamp = _startingBlockTimestamp;
// solhint-disable-next-line not-rely-on-time
STARTING_BLOCK_TIMESTAMP = _startingBlockTimestamp;
_transferOwnership(sequencer);
}
/*********************************
* External and Public Functions *
*********************************/
/**
* @notice Accepts an L2 outputRoot and the timestamp of the corresponding L2 block. The
* timestamp must be equal to the current value returned by `nextTimestamp()` in order to be
* accepted.
* This function may only be called by the Sequencer.
* @param _l2Output The L2 output of the checkpoint block.
* @param _l2timestamp The L2 block timestamp that resulted in _l2Output.
* @param _l1Blockhash A block hash which must be included in the current chain.
* @param _l1Blocknumber The block number with the specified block hash.
*/
function appendL2Output(
bytes32 _l2Output,
uint256 _l2timestamp,
bytes32 _l1Blockhash,
uint256 _l1Blocknumber
) external payable onlyOwner {
require(_l2timestamp < block.timestamp, "Cannot append L2 output in future");
require(_l2timestamp == nextTimestamp(), "Timestamp not equal to next expected timestamp");
require(_l2Output != bytes32(0), "Cannot submit empty L2 output");
if (_l1Blockhash != bytes32(0)) {
// This check allows the sequencer to append an output based on a given L1 block,
// without fear that it will be reorged out.
// It will also revert if the blockheight provided is more than 256 blocks behind the
// chain tip (as the hash will return as zero). This does open the door to a griefing
// attack in which the sequencer's submission is censored until the block is no longer
// retrievable, if the sequencer is experiencing this attack it can simply leave out the
// blockhash value, and delay submission until it is confident that the L1 block is
// finalized.
require(
blockhash(_l1Blocknumber) == _l1Blockhash,
"Blockhash does not match the hash at the expected height."
);
}
l2Outputs[_l2timestamp] = OutputProposal(_l2Output, block.timestamp);
latestBlockTimestamp = _l2timestamp;
emit l2OutputAppended(_l2Output, block.timestamp, _l2timestamp);
}
/**
* @notice Deletes the most recent output.
* @param _proposal Represents the output proposal to delete
*/
function deleteL2Output(OutputProposal memory _proposal) external onlyOwner {
OutputProposal memory outputToDelete = l2Outputs[latestBlockTimestamp];
require(
_proposal.outputRoot == outputToDelete.outputRoot,
"Can only delete the most recent output."
);
require(_proposal.timestamp == outputToDelete.timestamp, "");
emit l2OutputDeleted(
outputToDelete.outputRoot,
outputToDelete.timestamp,
latestBlockTimestamp
);
delete l2Outputs[latestBlockTimestamp];
latestBlockTimestamp = latestBlockTimestamp - SUBMISSION_INTERVAL;
}
/**
* @notice Computes the timestamp of the next L2 block that needs to be checkpointed.
*/
function nextTimestamp() public view returns (uint256) {
return latestBlockTimestamp + SUBMISSION_INTERVAL;
}
/**
* @notice Returns the L2 output proposal given a target L2 block timestamp.
* Returns a null output proposal if none is found.
* @param _l2Timestamp The L2 block timestamp of the target block.
*/
function getL2Output(uint256 _l2Timestamp) external view returns (OutputProposal memory) {
return l2Outputs[_l2Timestamp];
}
/**
* @notice Computes the L2 block number given a target L2 block timestamp.
* @param _l2timestamp The L2 block timestamp of the target block.
*/
function computeL2BlockNumber(uint256 _l2timestamp) external view returns (uint256) {
require(
_l2timestamp >= STARTING_BLOCK_TIMESTAMP,
"Timestamp prior to startingBlockTimestamp"
);
// For the first block recorded (ie. _l2timestamp = STARTING_BLOCK_TIMESTAMP), the
// L2BlockNumber should be HISTORICAL_TOTAL_BLOCKS + 1.
unchecked {
return
HISTORICAL_TOTAL_BLOCKS +
((_l2timestamp - STARTING_BLOCK_TIMESTAMP) / L2_BLOCK_TIME);
}
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { L2OutputOracle } from "./L2OutputOracle.sol";
import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol";
import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol";
import { ExcessivelySafeCall } from "../libraries/ExcessivelySafeCall.sol";
/**
* @title OptimismPortal
* This contract should be deployed behind an upgradable proxy.
*/
contract OptimismPortal {
/**********
* Errors *
**********/
/**
* @notice Error emitted when the output root proof is invalid.
*/
error InvalidOutputRootProof();
/**
* @notice Error emitted when the withdrawal inclusion proof is invalid.
*/
error InvalidWithdrawalInclusionProof();
/**
* @notice Error emitted when a withdrawal has already been finalized.
*/
error WithdrawalAlreadyFinalized();
/**
* @notice Error emitted on deposits which create a new contract with a non-zero target.
*/
error NonZeroCreationTarget();
/**********
* Events *
**********/
/**
* @notice Emitted when a Transaction is deposited from L1 to L2. The parameters of this
* event are read by the rollup node and used to derive deposit transactions on L2.
*/
event TransactionDeposited(
address indexed from,
address indexed to,
uint256 mint,
uint256 value,
uint64 gasLimit,
bool isCreation,
bytes data
);
/**
* @notice Emitted when a withdrawal is finalized
*/
event WithdrawalFinalized(bytes32 indexed, bool success);
/*************
* Constants *
*************/
/**
* @notice Value used to reset the l2Sender, this is more efficient than setting it to zero.
*/
address internal constant DEFAULT_L2_SENDER = 0x000000000000000000000000000000000000dEaD;
/*************
* Variables *
*************/
/**
* @notice Minimum time that must elapse before a withdrawal can be finalized.
*/
uint256 public immutable FINALIZATION_PERIOD;
/**
* @notice Address of the L2OutputOracle.
*/
L2OutputOracle public immutable L2_ORACLE;
/**
* @notice Public variable which can be used to read the address of the L2 account which
* initated the withdrawal. Can also be used to determine whether or not execution is occuring
* downstream of a call to finalizeWithdrawalTransaction().
*/
address public l2Sender = DEFAULT_L2_SENDER;
/**
* @notice A list of withdrawal hashes which have been successfully finalized.
* Used for replay protection.
*/
mapping(bytes32 => bool) public finalizedWithdrawals;
/***************
* Constructor *
***************/
constructor(L2OutputOracle _l2Oracle, uint256 _finalizationPeriod) {
L2_ORACLE = _l2Oracle;
FINALIZATION_PERIOD = _finalizationPeriod;
}
/********************
* Public Functions *
********************/
/**
* @notice Accepts value so that users can send ETH directly to this contract and
* have the funds be deposited to their address on L2.
* @dev This is intended as a convenience function for EOAs. Contracts should call the
* depositTransaction() function directly.
*/
receive() external payable {
depositTransaction(msg.sender, msg.value, 100000, false, bytes(""));
}
/**
* @notice Accepts deposits of ETH and data, and emits a TransactionDeposited event for use in
* deriving deposit transactions.
* @param _to The L2 destination address.
* @param _value The ETH value to send in the deposit transaction.
* @param _gasLimit The L2 gasLimit.
* @param _isCreation Whether or not the transaction should be contract creation.
* @param _data The input data.
*/
function depositTransaction(
address _to,
uint256 _value,
uint64 _gasLimit,
bool _isCreation,
bytes memory _data
) public payable {
// Differentiate between sending to address(0)
// and creating a contract
if (_isCreation && _to != address(0)) {
revert NonZeroCreationTarget();
}
address from = msg.sender;
// Transform the from-address to its alias if the caller is a contract.
if (msg.sender != tx.origin) {
from = AddressAliasHelper.applyL1ToL2Alias(msg.sender);
}
emit TransactionDeposited(from, _to, msg.value, _value, _gasLimit, _isCreation, _data);
}
/**
* @notice Finalizes a withdrawal transaction.
* @param _nonce Nonce for the provided message.
* @param _sender Message sender address on L2.
* @param _target Target address on L1.
* @param _value ETH to send to the target.
* @param _gasLimit Gas to be forwarded to the target.
* @param _data Data to send to the target.
* @param _l2Timestamp L2 timestamp of the outputRoot.
* @param _outputRootProof Inclusion proof of the withdrawer contracts storage root.
* @param _withdrawalProof Inclusion proof for the given withdrawal in the withdrawer contract.
*/
function finalizeWithdrawalTransaction(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes calldata _data,
uint256 _l2Timestamp,
WithdrawalVerifier.OutputRootProof calldata _outputRootProof,
bytes calldata _withdrawalProof
) external payable {
// Prevent reentrency
require(_target != address(this), "Cannot send message to self.");
// Get the output root.
L2OutputOracle.OutputProposal memory proposal = L2_ORACLE.getL2Output(_l2Timestamp);
// Ensure that enough time has passed since the proposal was submitted
// before allowing a withdrawal. A fault proof should be submitted
// before this check is allowed to pass.
require(
block.timestamp > proposal.timestamp + FINALIZATION_PERIOD,
"Proposal is not yet finalized."
);
// Verify that the output root can be generated with the elements in the proof.
if (proposal.outputRoot != WithdrawalVerifier._deriveOutputRoot(_outputRootProof)) {
revert InvalidOutputRootProof();
}
// Verify that the hash of the withdrawal transaction's arguments are included in the
// storage hash of the withdrawer contract.
bytes32 withdrawalHash = WithdrawalVerifier.withdrawalHash(
_nonce,
_sender,
_target,
_value,
_gasLimit,
_data
);
// Verify proof that a withdrawal on L2 was initated
if (
WithdrawalVerifier._verifyWithdrawalInclusion(
withdrawalHash,
_outputRootProof.withdrawerStorageRoot,
_withdrawalProof
) == false
) {
revert InvalidWithdrawalInclusionProof();
}
// Check that this withdrawal has not already been finalized.
if (finalizedWithdrawals[withdrawalHash] == true) {
revert WithdrawalAlreadyFinalized();
}
// Set the withdrawal as finalized
finalizedWithdrawals[withdrawalHash] = true;
// Save enough gas so that the call cannot use up all of the gas
require(gasleft() >= _gasLimit + 20000, "Insufficient gas to finalize withdrawal.");
// Set the l2Sender so that other contracts can know which account
// on L2 is making the withdrawal
l2Sender = _sender;
// Make the call and ensure that a contract cannot out of gas
// us by returning a huge amount of data
(bool success, ) = ExcessivelySafeCall.excessivelySafeCall(
_target,
_gasLimit,
_value,
0,
_data
);
// Be sure to reset the l2Sender
l2Sender = DEFAULT_L2_SENDER;
// All withdrawals are immediately finalized. Replayability can
// be achieved through contracts built on top of this contract
emit WithdrawalFinalized(withdrawalHash, success);
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
/**
* @title Burner
* @dev This contract is used to remove ETH from
* the L2 circulating supply as it is withdrawn.
*/
contract Burner {
constructor() payable {
selfdestruct(payable(address(this)));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
/**
* @title OVM_DeployerWhitelist
* @dev The Deployer Whitelist is a temporary predeploy used to provide additional safety during the
* initial phases of our mainnet roll out. It is owned by the Optimism team, and defines accounts
* which are allowed to deploy contracts on Layer2. The Execution Manager will only allow an
* ovmCREATE or ovmCREATE2 operation to proceed if the deployer's address whitelisted.
*/
contract DeployerWhitelist {
/**********
* Events *
**********/
event OwnerChanged(address oldOwner, address newOwner);
event WhitelistStatusChanged(address deployer, bool whitelisted);
event WhitelistDisabled(address oldOwner);
/**********************
* Contract Constants *
**********************/
// WARNING: When owner is set to address(0), the whitelist is disabled.
address public owner;
mapping(address => bool) public whitelist;
/**********************
* Function Modifiers *
**********************/
/**
* Blocks functions to anyone except the contract owner.
*/
modifier onlyOwner() {
require(msg.sender == owner, "Function can only be called by the owner of this contract.");
_;
}
/********************
* Public Functions *
********************/
/**
* Adds or removes an address from the deployment whitelist.
* @param _deployer Address to update permissions for.
* @param _isWhitelisted Whether or not the address is whitelisted.
*/
function setWhitelistedDeployer(address _deployer, bool _isWhitelisted) external onlyOwner {
whitelist[_deployer] = _isWhitelisted;
emit WhitelistStatusChanged(_deployer, _isWhitelisted);
}
/**
* Updates the owner of this contract.
* @param _owner Address of the new owner.
*/
// slither-disable-next-line external-function
function setOwner(address _owner) public onlyOwner {
// Prevent users from setting the whitelist owner to address(0) except via
// enableArbitraryContractDeployment. If you want to burn the whitelist owner, send it to
// any other address that doesn't have a corresponding knowable private key.
require(
_owner != address(0),
"OVM_DeployerWhitelist: can only be disabled via enableArbitraryContractDeployment"
);
emit OwnerChanged(owner, _owner);
owner = _owner;
}
/**
* Permanently enables arbitrary contract deployment and deletes the owner.
*/
function enableArbitraryContractDeployment() external onlyOwner {
emit WhitelistDisabled(owner);
owner = address(0);
}
/**
* Checks whether an address is allowed to deploy contracts.
* @param _deployer Address to check.
* @return _allowed Whether or not the address can deploy contracts.
*/
function isDeployerAllowed(address _deployer) external view returns (bool) {
return (owner == address(0) || whitelist[_deployer]);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
/* External Imports */
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { Lib_BedrockPredeployAddresses } from "../libraries/Lib_BedrockPredeployAddresses.sol";
import { L1Block } from "../L2/L1Block.sol";
/**
* @title GasPriceOracle
* @dev This contract maintains the variables responsible for computing the L1
* portion of the total fee charged on L2. The values stored in the contract
* are looked up as part of the L2 state transition function and used to compute
* the total fee paid by the user.
* The contract exposes an API that is useful for knowing how large the L1
* portion of their transaction fee will be.
* This predeploy is found at 0x420000000000000000000000000000000000000F in the
* L2 state.
* This contract should be behind an upgradable proxy such that when the gas
* prices change, the values can be updated accordingly.
*/
contract GasPriceOracle is Ownable {
/*************
* Variables *
*************/
// backwards compatibility
uint256 internal spacer0;
uint256 internal spacer1;
// Amortized cost of batch submission per transaction
uint256 public overhead;
// Value to scale the fee up by
uint256 public scalar;
// Number of decimals of the scalar
uint256 public decimals;
/***************
* Constructor *
***************/
/**
* @param _owner Address that will initially own this contract.
*/
constructor(address _owner) Ownable() {
transferOwnership(_owner);
}
/**********
* Events *
**********/
event OverheadUpdated(uint256);
event ScalarUpdated(uint256);
event DecimalsUpdated(uint256);
/********************
* Public Functions *
********************/
// legacy backwards compat
function gasPrice() public returns (uint256) {
return block.basefee;
}
function baseFee() public returns (uint256) {
return block.basefee;
}
function l1BaseFee() public view returns (uint256) {
return L1Block(Lib_BedrockPredeployAddresses.L1_BLOCK_ATTRIBUTES).basefee();
}
/**
* Allows the owner to modify the overhead.
* @param _overhead New overhead
*/
// slither-disable-next-line external-function
function setOverhead(uint256 _overhead) public onlyOwner {
overhead = _overhead;
emit OverheadUpdated(_overhead);
}
/**
* Allows the owner to modify the scalar.
* @param _scalar New scalar
*/
// slither-disable-next-line external-function
function setScalar(uint256 _scalar) public onlyOwner {
scalar = _scalar;
emit ScalarUpdated(_scalar);
}
/**
* Allows the owner to modify the decimals.
* @param _decimals New decimals
*/
// slither-disable-next-line external-function
function setDecimals(uint256 _decimals) public onlyOwner {
decimals = _decimals;
emit DecimalsUpdated(_decimals);
}
/**
* Computes the L1 portion of the fee
* based on the size of the RLP encoded tx
* and the current l1BaseFee
* @param _data Unsigned RLP encoded tx, 6 elements
* @return L1 fee that should be paid for the tx
*/
// slither-disable-next-line external-function
function getL1Fee(bytes memory _data) public view returns (uint256) {
uint256 l1GasUsed = getL1GasUsed(_data);
uint256 l1Fee = l1GasUsed * l1BaseFee();
uint256 divisor = 10**decimals;
uint256 unscaled = l1Fee * scalar;
uint256 scaled = unscaled / divisor;
return scaled;
}
// solhint-disable max-line-length
/**
* Computes the amount of L1 gas used for a transaction
* The overhead represents the per batch gas overhead of
* posting both transaction and state roots to L1 given larger
* batch sizes.
* 4 gas for 0 byte
* https://github.com/ethereum/go-ethereum/blob/9ada4a2e2c415e6b0b51c50e901336872e028872/params/protocol_params.go#L33
* 16 gas for non zero byte
* https://github.com/ethereum/go-ethereum/blob/9ada4a2e2c415e6b0b51c50e901336872e028872/params/protocol_params.go#L87
* This will need to be updated if calldata gas prices change
* Account for the transaction being unsigned
* Padding is added to account for lack of signature on transaction
* 1 byte for RLP V prefix
* 1 byte for V
* 1 byte for RLP R prefix
* 32 bytes for R
* 1 byte for RLP S prefix
* 32 bytes for S
* Total: 68 bytes of padding
* @param _data Unsigned RLP encoded tx, 6 elements
* @return Amount of L1 gas used for a transaction
*/
// solhint-enable max-line-length
function getL1GasUsed(bytes memory _data) public view returns (uint256) {
uint256 total = 0;
uint256 length = _data.length;
for (uint256 i = 0; i < length; i++) {
if (_data[i] == 0) {
total += 4;
} else {
total += 16;
}
}
uint256 unsigned = total + overhead;
return unsigned + (68 * 16);
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
/**
* @title L1Block
* @dev This is an L2 predeploy contract that holds values from the L1
* chain. It can only be updated by a special account that has no private
* key managed by the L2 system. Transactions sent to this contract can
* be thought of as "L2 system transactions".
*/
contract L1Block {
/**
* @notice Only the Depositor account may call setL1BlockValues().
*/
error OnlyDepositor();
/**
* @notice The depositor account is a special account that sends
* transactions to this contract.
*/
address public constant DEPOSITOR_ACCOUNT = 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001;
/**
* @notice The latest L1 block number known by the L2 system
*/
uint64 public number;
/**
* @notice The latest L1 timestamp known by the L2 system
*/
uint64 public timestamp;
/**
* @notice The latest L1 basefee
*/
uint256 public basefee;
/**
* @notice The latest L1 blockhash
*/
bytes32 public hash;
/**
* @notice The number of L2 blocks in the same epoch
*/
uint64 public sequenceNumber;
/**
* @notice Sets the L1 values
* @param _number L1 blocknumber
* @param _timestamp L1 timestamp
* @param _basefee L1 basefee
* @param _hash L1 blockhash
* @param _sequenceNumber Number of L2 blocks since epoch start
*/
function setL1BlockValues(
uint64 _number,
uint64 _timestamp,
uint256 _basefee,
bytes32 _hash,
uint64 _sequenceNumber
) external {
if (msg.sender != DEPOSITOR_ACCOUNT) {
revert OnlyDepositor();
}
number = _number;
timestamp = _timestamp;
basefee = _basefee;
hash = _hash;
sequenceNumber = _sequenceNumber;
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { L1Block } from "./L1Block.sol";
import { Lib_BedrockPredeployAddresses } from "../libraries/Lib_BedrockPredeployAddresses.sol";
/**
* @title L1BlockNumber
* @dev L1BlockNumber is a legacy contract that fills the roll of the OVM_L1BlockNumber contract in
* the old version of the Optimism system. Only necessary for backwards compatibility. If you want
* to access the L1 block number going forward, you should use the L1Block contract instead.
*
* ADDRESS: 0x4200000000000000000000000000000000000013
*/
contract L1BlockNumber {
receive() external payable {
uint256 l1BlockNumber = getL1BlockNumber();
assembly {
mstore(0, l1BlockNumber)
return(0, 32)
}
}
fallback() external payable {
uint256 l1BlockNumber = getL1BlockNumber();
assembly {
mstore(0, l1BlockNumber)
return(0, 32)
}
}
function getL1BlockNumber() public view returns (uint256) {
return L1Block(Lib_BedrockPredeployAddresses.L1_BLOCK_ATTRIBUTES).number();
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol";
import {
Lib_PredeployAddresses
} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
import { CrossDomainMessenger } from "../universal/CrossDomainMessenger.sol";
import { L2ToL1MessagePasser } from "./L2ToL1MessagePasser.sol";
contract L2CrossDomainMessenger is CrossDomainMessenger {
/********************
* Public Functions *
********************/
/**
* @notice initialize the L2CrossDomainMessenger by giving
* it the address of the L1CrossDomainMessenger on L1
*/
function initialize(address _l1CrossDomainMessenger) external {
address[] memory blockedSystemAddresses = new address[](2);
blockedSystemAddresses[0] = address(this);
blockedSystemAddresses[1] = Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER;
_initialize(_l1CrossDomainMessenger, blockedSystemAddresses);
}
/**
* @notice Legacy getter for the remote messenger. This is included
* to prevent any existing contracts that relay messages from breaking.
* Use `otherMessenger()` for a standard API that works on both
* the L1 and L2 cross domain messengers.
*/
function l1CrossDomainMessenger() public returns (address) {
return otherMessenger;
}
/**********************
* Internal Functions *
**********************/
/**
* @notice Only the L1CrossDomainMessenger can call the
* L2CrossDomainMessenger
*/
function _isSystemMessageSender() internal view override returns (bool) {
return AddressAliasHelper.undoL1ToL2Alias(msg.sender) == otherMessenger;
}
/**
* @notice Sending a message from L2 to L1 involves calling the L2ToL1MessagePasser
* where it stores in a storage slot a commitment to the message being
* sent to L1. A proof is then verified against that storage slot on L1.
*/
function _sendMessage(
address _to,
uint64 _gasLimit,
uint256 _value,
bytes memory _data
) internal override {
L2ToL1MessagePasser(payable(Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER))
.initiateWithdrawal{ value: _value }(_to, _gasLimit, _data);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import {
Lib_PredeployAddresses
} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
import { StandardBridge } from "../universal/StandardBridge.sol";
import { OptimismMintableERC20 } from "../universal/OptimismMintableERC20.sol";
/**
* @title L2StandardBridge
* @dev This contract is an L2 predeploy that is responsible for facilitating
* deposits of tokens from L1 to L2.
* TODO: ensure that this has 1:1 backwards compatibility
*/
contract L2StandardBridge is StandardBridge {
/**********
* Events *
**********/
event WithdrawalInitiated(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event DepositFinalized(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event DepositFailed(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
/********************
* Public Functions *
********************/
/**
* @notice Initialize the L2StandardBridge. This must only be callable
* once. `_initialize` ensures this.
*/
function initialize(address payable _otherBridge) public {
_initialize(payable(Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER), _otherBridge);
}
/**
* @notice Withdraw tokens to self on L1
* @param _l2Token The L2 token address to withdraw
* @param _amount The amount of L2 token to withdraw
* @param _minGasLimit The min gas limit in the withdrawing call
* @param _data Additional calldata to pass along
*/
function withdraw(
address _l2Token,
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _data
) external payable virtual {
_initiateWithdrawal(_l2Token, msg.sender, msg.sender, _amount, _minGasLimit, _data);
}
/**
* @notice Withdraw tokens to an address on L1
* @param _l2Token The L2 token address to withdraw
* @param _to The L1 account to withdraw to
* @param _amount The amount of L2 token to withdraw
* @param _minGasLimit The min gas limit in the withdrawing call
* @param _data Additional calldata to pass along
*/
function withdrawTo(
address _l2Token,
address _to,
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _data
) external payable virtual {
_initiateWithdrawal(_l2Token, msg.sender, _to, _amount, _minGasLimit, _data);
}
/**
* @notice Finalize the L1 to L2 deposit. This should only be callable by
* a deposit through the L1StandardBridge.
* @param _l1Token The L1 token address
* @param _l2Token The corresponding L2 token address
* @param _from The sender of the tokens
* @param _to The recipient of the tokens
* @param _amount The amount of tokens
* @param _data Additional calldata
*/
function finalizeDeposit(
address _l1Token,
address _l2Token,
address _from,
address _to,
uint256 _amount,
bytes calldata _data
) external payable virtual {
if (_l1Token == address(0) && _l2Token == Lib_PredeployAddresses.OVM_ETH) {
finalizeBridgeETH(_from, _to, _amount, _data);
} else {
finalizeBridgeERC20(_l2Token, _l1Token, _from, _to, _amount, _data);
}
emit DepositFinalized(_l1Token, _l2Token, _from, _to, _amount, _data);
}
/**********************
* Internal Functions *
**********************/
/**
* @notice Handle withdrawals, taking into account the legacy form of ETH
* when it was represented as an ERC20 at the OVM_ETH contract.
* TODO: require(msg.value == _value) for OVM_ETH case?
*/
function _initiateWithdrawal(
address _l2Token,
address _from,
address _to,
uint256 _amount,
uint32 _minGasLimit,
bytes calldata _data
) internal {
address l1Token = OptimismMintableERC20(_l2Token).l1Token();
if (_l2Token == Lib_PredeployAddresses.OVM_ETH) {
_initiateBridgeETH(_from, _to, _amount, _minGasLimit, _data);
} else {
_initiateBridgeERC20(_l2Token, l1Token, _from, _to, _amount, _minGasLimit, _data);
}
emit WithdrawalInitiated(l1Token, _l2Token, msg.sender, _to, _amount, _data);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol";
import { Burner } from "./Burner.sol";
/**
* @title L2ToL1MessagePasser
* TODO: should this be renamed to L2OptimismPortal?
*/
contract L2ToL1MessagePasser {
/**********
* Events *
**********/
/**
* @notice Emitted any time a withdrawal is initiated.
* @param nonce Unique value corresponding to each withdrawal.
* @param sender The L2 account address which initiated the withdrawal.
* @param target The L1 account address the call will be send to.
* @param value The ETH value submitted for withdrawal, to be forwarded to the target.
* @param gasLimit The minimum amount of gas that must be provided when withdrawing on L1.
* @param data The data to be forwarded to the target on L1.
*/
event WithdrawalInitiated(
uint256 indexed nonce,
address indexed sender,
address indexed target,
uint256 value,
uint256 gasLimit,
bytes data
);
/**
* @notice Emitted when the balance of this contract is burned.
*/
event WithdrawerBalanceBurnt(uint256 indexed amount);
/*************
* Variables *
*************/
/**
* @notice Includes the message hashes for all withdrawals
*/
mapping(bytes32 => bool) public sentMessages;
/**
* @notice A unique value hashed with each withdrawal.
*/
uint256 public nonce;
/********************
* Public Functions *
********************/
/**
* @notice Allow users to withdraw by sending ETH
* directly to this contract.
* TODO: maybe this should be only EOA
*/
receive() external payable {
initiateWithdrawal(msg.sender, 100000, bytes(""));
}
/**
* @notice Initiates a withdrawal to execute on L1.
* TODO: message hashes must be migrated since the legacy
* hashes are computed differently
* @param _target Address to call on L1 execution.
* @param _gasLimit GasLimit to provide on L1.
* @param _data Data to forward to L1 target.
*/
function initiateWithdrawal(
address _target,
uint256 _gasLimit,
bytes memory _data
) public payable {
bytes32 withdrawalHash = WithdrawalVerifier.withdrawalHash(
nonce,
msg.sender,
_target,
msg.value,
_gasLimit,
_data
);
sentMessages[withdrawalHash] = true;
emit WithdrawalInitiated(nonce, msg.sender, _target, msg.value, _gasLimit, _data);
unchecked {
++nonce;
}
}
/**
* @notice Removes all ETH held in this contract from the state, by deploying a contract which
* immediately self destructs.
* For simplicity, this call is not incentivized as it costs very little to run.
* Inspired by https://etherscan.io/address/0xb69fba56b2e67e7dda61c8aa057886a8d1468575#code
*/
function burn() external {
uint256 balance = address(this).balance;
new Burner{ value: balance }();
emit WithdrawerBalanceBurnt(balance);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
/* Library Imports */
import {
Lib_PredeployAddresses
} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
/* Contract Imports */
import { OptimismMintableERC20 } from "../universal/OptimismMintableERC20.sol";
/**
* @title OVM_ETH
* @dev Deprecated contract that used to hold user ETH balances
*/
contract OVM_ETH is OptimismMintableERC20 {
/***************
* Constructor *
***************/
constructor()
OptimismMintableERC20(Lib_PredeployAddresses.L2_STANDARD_BRIDGE, address(0), "Ether", "ETH")
{}
function mint(address _to, uint256 _amount) public virtual override {
revert("OVM_ETH: mint is disabled");
}
function burn(address _from, uint256 _amount) public virtual override {
revert("OVM_ETH: burn is disabled");
}
function transfer(address _recipient, uint256 _amount) public virtual override returns (bool) {
revert("OVM_ETH: transfer is disabled");
}
function approve(address _spender, uint256 _amount) public virtual override returns (bool) {
revert("OVM_ETH: approve is disabled");
}
function transferFrom(
address _sender,
address _recipient,
uint256 _amount
) public virtual override returns (bool) {
revert("OVM_ETH: transferFrom is disabled");
}
function increaseAllowance(address _spender, uint256 _addedValue)
public
virtual
override
returns (bool)
{
revert("OVM_ETH: increaseAllowance is disabled");
}
function decreaseAllowance(address _spender, uint256 _subtractedValue)
public
virtual
override
returns (bool)
{
revert("OVM_ETH: decreaseAllowance is disabled");
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
/* Library Imports */
import {
Lib_PredeployAddresses
} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
/* Contract Imports */
import { L2StandardBridge } from "./L2StandardBridge.sol";
/**
* @title SequencerFeeVault
* @dev Simple holding contract for fees paid to the Sequencer
*/
contract SequencerFeeVault {
/*************
* Constants *
*************/
// Minimum ETH balance that can be withdrawn in a single withdrawal.
uint256 public constant MIN_WITHDRAWAL_AMOUNT = 15 ether;
/*************
* Variables *
*************/
// Address on L1 that will hold the fees once withdrawn. Dynamically
// initialized in the genesis state
address public l1FeeWallet;
/************
* Fallback *
************/
// slither-disable-next-line locked-ether
receive() external payable {}
/********************
* Public Functions *
********************/
// slither-disable-next-line external-function
function withdraw() public {
require(
address(this).balance >= MIN_WITHDRAWAL_AMOUNT,
// solhint-disable-next-line max-line-length
"OVM_SequencerFeeVault: withdrawal amount must be greater than minimum withdrawal amount"
);
uint256 balance = address(this).balance;
L2StandardBridge(payable(Lib_PredeployAddresses.L2_STANDARD_BRIDGE)).withdrawTo{
value: balance
}(Lib_PredeployAddresses.OVM_ETH, l1FeeWallet, balance, 0, bytes(""));
}
}
This diff is collapsed.
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.9;
// FROM: https://github.com/nomad-xyz/ExcessivelySafeCall/blob/main/src/ExcessivelySafeCall.sol
// TODO: Just use the original once we get our PR merged.
library ExcessivelySafeCall {
/// @notice Use when you _really_ really _really_ don't trust the called
/// contract. This prevents the called contract from causing reversion of
/// the caller in as many ways as we can.
/// @dev The main difference between this and a solidity low-level call is
/// that we limit the number of bytes that the callee can cause to be
/// copied to caller memory. This prevents stupid things like malicious
/// contracts returning 10,000,000 bytes causing a local OOG when copying
/// to memory.
/// @param _target The address to call
/// @param _gas The amount of gas to forward to the remote contract
/// @param _value Ether value to send with the call
/// @param _maxCopy The maximum number of bytes of returndata to copy
/// to memory.
/// @param _calldata The data to send to the remote contract
/// @return success and returndata, as `.call()`. Returndata is capped to
/// `_maxCopy` bytes.
function excessivelySafeCall(
address _target,
uint256 _gas,
uint256 _value,
uint16 _maxCopy,
bytes memory _calldata
) internal returns (bool, bytes memory) {
// set up for assembly call
uint256 _toCopy;
bool _success;
bytes memory _returnData = new bytes(_maxCopy);
// dispatch message to recipient
// by assembly calling "handle" function
// we call via assembly to avoid memcopying a very large returndata
// returned by a malicious contract
assembly {
_success := call(
_gas, // gas
_target, // recipient
_value, // ether value
add(_calldata, 0x20), // inloc
mload(_calldata), // inlen
0, // outloc
0 // outlen
)
// limit our copy to 256 bytes
_toCopy := returndatasize()
if gt(_toCopy, _maxCopy) {
_toCopy := _maxCopy
}
// Store the length of the copied bytes
mstore(_returnData, _toCopy)
// copy the bytes from returndata[0:_toCopy]
returndatacopy(add(_returnData, 0x20), 0, _toCopy)
}
return (_success, _returnData);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
/**
* @title Lib_BedrockPredeployAddresses
* TODO: just merge this value into the monorepo
*/
library Lib_BedrockPredeployAddresses {
address internal constant L1_BLOCK_ATTRIBUTES = 0x4200000000000000000000000000000000000015;
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import {
Lib_CrossDomainUtils
} from "@eth-optimism/contracts/libraries/bridge/Lib_CrossDomainUtils.sol";
import { Lib_RLPWriter } from "@eth-optimism/contracts/libraries/rlp/Lib_RLPWriter.sol";
/**
* @title CrossDomainHashing
* This library is responsible for holding cross domain utility
* functions.
* TODO(tynes): merge with Lib_CrossDomainUtils
* TODO(tynes): fill out more devdocs
*/
library CrossDomainHashing {
/**
* @notice Compute the L2 transaction hash given
* data about an L1 deposit transaction. This is useful for
* environments that do not have access to arbitrary
* RLP encoding functionality but have access to the
* standard web3 API
* TODO: rearrange args in a sane way
* @param _l1BlockHash The L1 block hash corresponding to the block
* the deposit was included in
* @param _logIndex The log index of the event that the deposit was
* created from. This can be found on the transaction receipt
* @param _from The sender of the deposit
* @param _to The L2 contract to be called by the deposit transaction
* @param _isCreate Indicates if the deposit creates a contract
* @param _mint The amount of ETH being minted by the transaction
* @param _value The amount of ETH send in the L2 call
* @param _gas The gas limit for the L2 call
*/
function L2TransactionHash(
bytes32 _l1BlockHash,
uint256 _logIndex,
address _from,
address _to,
bool _isCreate,
uint256 _mint,
uint256 _value,
uint256 _gas,
bytes memory _data
) internal pure returns (bytes32) {
bytes memory raw = L2Transaction(
_l1BlockHash,
_logIndex,
_from,
_to,
_isCreate,
_mint,
_value,
_gas,
_data
);
return keccak256(raw);
}
/**
* @notice Compute the deposit transaction source hash.
* This value ensures that the L2 transaction hash is unique
* and deterministic based on L1 execution
* @param l1BlockHash The L1 blockhash corresponding to the block including
* the deposit
* @param logIndex The index of the log that created the deposit transaction
*/
function sourceHash(bytes32 l1BlockHash, uint256 logIndex) internal pure returns (bytes32) {
bytes32 depositId = keccak256(abi.encode(l1BlockHash, logIndex));
return keccak256(abi.encode(bytes32(0), depositId));
}
/**
* @notice RLP encode a deposit transaction
* This only works for user deposits, not system deposits
* TODO: better name + rearrange the input param ordering?
*/
function L2Transaction(
bytes32 _l1BlockHash,
uint256 _logIndex,
address _from,
address _to,
bool _isCreate,
uint256 _mint,
uint256 _value,
uint256 _gas,
bytes memory _data
) internal pure returns (bytes memory) {
bytes32 source = sourceHash(_l1BlockHash, _logIndex);
bytes[] memory raw = new bytes[](7);
raw[0] = Lib_RLPWriter.writeBytes(bytes32ToBytes(source));
raw[1] = Lib_RLPWriter.writeAddress(_from);
if (_isCreate == true) {
require(_to == address(0));
raw[2] = Lib_RLPWriter.writeBytes("");
} else {
raw[2] = Lib_RLPWriter.writeAddress(_to);
}
raw[3] = Lib_RLPWriter.writeUint(_mint);
raw[4] = Lib_RLPWriter.writeUint(_value);
raw[5] = Lib_RLPWriter.writeUint(_gas);
raw[6] = Lib_RLPWriter.writeBytes(_data);
bytes memory encoded = Lib_RLPWriter.writeList(raw);
return abi.encodePacked(uint8(0x7e), encoded);
}
/**
* @notice Helper function to turn bytes32 into bytes
*/
function bytes32ToBytes(bytes32 input) internal pure returns (bytes memory) {
bytes memory b = new bytes(32);
assembly {
mstore(add(b, 32), input) // set the bytes data
}
return b;
}
/**
* @notice Adds the version to the nonce
*/
function addVersionToNonce(uint256 _nonce, uint16 _version)
internal
pure
returns (uint256 nonce)
{
assembly {
nonce := or(shl(240, _version), _nonce)
}
}
/**
* @notice Gets the version out of the nonce
*/
function getVersionFromNonce(uint256 _nonce) internal pure returns (uint16 version) {
assembly {
version := shr(240, _nonce)
}
}
/**
* @notice Encodes the cross domain message based on the version that
* is encoded in the nonce
*/
function getVersionedEncoding(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes memory _data
) internal pure returns (bytes memory) {
uint16 version = getVersionFromNonce(_nonce);
if (version == 0) {
return getEncodingV0(_target, _sender, _data, _nonce);
} else if (version == 1) {
return getEncodingV1(_nonce, _sender, _target, _value, _gasLimit, _data);
}
revert("Unknown version.");
}
/**
* @notice Compute the cross domain hash based on the versioned nonce
*/
function getVersionedHash(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes memory _data
) internal pure returns (bytes32) {
uint16 version = getVersionFromNonce(_nonce);
if (version == 0) {
return getHashV0(_target, _sender, _data, _nonce);
} else if (version == 1) {
return getHashV1(_nonce, _sender, _target, _value, _gasLimit, _data);
}
revert("Unknown version.");
}
/**
* @notice Compute the legacy cross domain serialization
*/
function getEncodingV0(
address _target,
address _sender,
bytes memory _data,
uint256 _nonce
) internal pure returns (bytes memory) {
return Lib_CrossDomainUtils.encodeXDomainCalldata(_target, _sender, _data, _nonce);
}
/**
* @notice Compute the V1 cross domain serialization
*/
function getEncodingV1(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes memory _data
) internal pure returns (bytes memory) {
return
abi.encodeWithSignature(
"relayMessage(uint256,address,address,uint256,uint256,bytes)",
_nonce,
_sender,
_target,
_value,
_gasLimit,
_data
);
}
/**
* @notice Compute the legacy hash of a cross domain message
*/
function getHashV0(
address _target,
address _sender,
bytes memory _data,
uint256 _nonce
) internal pure returns (bytes32) {
return keccak256(getEncodingV0(_target, _sender, _data, _nonce));
}
/**
* @notice Compute the V1 hash of a cross domain message
*/
function getHashV1(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes memory _data
) internal pure returns (bytes32) {
return keccak256(getEncodingV1(_nonce, _sender, _target, _value, _gasLimit, _data));
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
/* Library Imports */
import {
Lib_SecureMerkleTrie
} from "@eth-optimism/contracts/libraries/trie/Lib_SecureMerkleTrie.sol";
import {
Lib_CrossDomainUtils
} from "@eth-optimism/contracts/libraries/bridge/Lib_CrossDomainUtils.sol";
/**
* @title WithdrawalVerifier
* @notice A library with helper functions for verifying a withdrawal on L1.
*/
library WithdrawalVerifier {
/// @notice A struct containing the elements hashed together to generate the output root.
struct OutputRootProof {
bytes32 version;
bytes32 stateRoot;
bytes32 withdrawerStorageRoot;
bytes32 latestBlockhash;
}
/**
* @notice Derives the withdrawal hash according to the encoding in the L2 Withdrawer contract
* @param _nonce Nonce for the provided message.
* @param _sender Message sender address on L2.
* @param _target Target address on L1.
* @param _value ETH to send to the target.
* @param _gasLimit Gas to be forwarded to the target.
* @param _data Data to send to the target.
*/
function withdrawalHash(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes memory _data
) internal pure returns (bytes32) {
return keccak256(abi.encode(_nonce, _sender, _target, _value, _gasLimit, _data));
}
/**
* @notice Derives the output root corresponding to the elements provided in the proof.
* @param _outputRootProof The elements which were hashed together to generate the output root.
* @return Whether or not the output root matches the hashed output of the proof.
*/
function _deriveOutputRoot(OutputRootProof memory _outputRootProof)
internal
pure
returns (bytes32)
{
return
keccak256(
abi.encode(
_outputRootProof.version,
_outputRootProof.stateRoot,
_outputRootProof.withdrawerStorageRoot,
_outputRootProof.latestBlockhash
)
);
}
/**
* @notice Verifies a proof that a given withdrawal hash is present in the Withdrawer contract's
* withdrawals mapping.
* @param _withdrawalHash Keccak256 hash of the withdrawal transaction data.
* @param _withdrawerStorageRoot Storage root of the withdrawer predeploy contract.
* @param _withdrawalProof Merkle trie inclusion proof for the desired node.
* @return Whether or not the inclusion proof was successful.
*/
function _verifyWithdrawalInclusion(
bytes32 _withdrawalHash,
bytes32 _withdrawerStorageRoot,
bytes memory _withdrawalProof
) internal pure returns (bool) {
bytes32 storageKey = keccak256(
abi.encode(
_withdrawalHash,
uint256(0) // The withdrawals mapping is at the first slot in the layout.
)
);
return
Lib_SecureMerkleTrie.verifyInclusionProof(
abi.encode(storageKey),
hex"01",
_withdrawalProof,
_withdrawerStorageRoot
);
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
/* Testing utilities */
import { Test } from "forge-std/Test.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
import { L1StandardBridge } from "../L1/L1StandardBridge.sol";
import { L2StandardBridge } from "../L2/L2StandardBridge.sol";
import { OptimismMintableTokenFactory } from "../universal/OptimismMintableTokenFactory.sol";
import { OptimismMintableERC20 } from "../universal/OptimismMintableERC20.sol";
import { OptimismPortal } from "../L1/OptimismPortal.sol";
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol";
import { L2CrossDomainMessenger } from "../L2/L2CrossDomainMessenger.sol";
import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol";
import { OVM_ETH } from "../L2/OVM_ETH.sol";
import {
Lib_PredeployAddresses
} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract CommonTest is Test {
address alice = address(128);
address bob = address(256);
address immutable ZERO_ADDRESS = address(0);
address immutable NON_ZERO_ADDRESS = address(1);
uint256 immutable NON_ZERO_VALUE = 100;
uint256 immutable ZERO_VALUE = 0;
uint64 immutable NON_ZERO_GASLIMIT = 50000;
bytes32 nonZeroHash = keccak256(abi.encode("NON_ZERO"));
bytes NON_ZERO_DATA = hex"0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff0000";
function _setUp() public {
// Give alice and bob some ETH
vm.deal(alice, 1 << 16);
vm.deal(bob, 1 << 16);
vm.label(alice, "alice");
vm.label(bob, "bob");
}
}
contract L2OutputOracle_Initializer is CommonTest {
// Test target
L2OutputOracle oracle;
// Constructor arguments
address sequencer = 0x000000000000000000000000000000000000AbBa;
uint256 submissionInterval = 1800;
uint256 l2BlockTime = 2;
bytes32 genesisL2Output = keccak256(abi.encode(0));
uint256 historicalTotalBlocks = 100;
// Cache of the initial L2 timestamp
uint256 startingBlockTimestamp;
// By default the first block has timestamp zero, which will cause underflows in the tests
uint256 initTime = 1000;
function setUp() public virtual {
_setUp();
// Move time forward so we have a non-zero starting timestamp
vm.warp(initTime);
// Deploy the L2OutputOracle and transfer owernship to the sequencer
oracle = new L2OutputOracle(
submissionInterval,
l2BlockTime,
genesisL2Output,
historicalTotalBlocks,
initTime,
sequencer
);
startingBlockTimestamp = block.timestamp;
}
}
contract Messenger_Initializer is L2OutputOracle_Initializer {
OptimismPortal op;
L1CrossDomainMessenger L1Messenger;
L2CrossDomainMessenger L2Messenger = L2CrossDomainMessenger(Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER);
L2ToL1MessagePasser messagePasser = L2ToL1MessagePasser(payable(Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER));
event SentMessage(
address indexed target,
address sender,
bytes message,
uint256 messageNonce,
uint256 gasLimit
);
event WithdrawalInitiated(
uint256 indexed nonce,
address indexed sender,
address indexed target,
uint256 value,
uint256 gasLimit,
bytes data
);
event RelayedMessage(bytes32 indexed msgHash);
event TransactionDeposited(
address indexed from,
address indexed to,
uint256 mint,
uint256 value,
uint64 gasLimit,
bool isCreation,
bytes data
);
event WithdrawalFinalized(bytes32 indexed, bool success);
function setUp() public virtual override {
super.setUp();
// Deploy the OptimismPortal
op = new OptimismPortal(oracle, 100);
vm.label(address(op), "OptimismPortal");
L1Messenger = new L1CrossDomainMessenger();
L1Messenger.initialize(op);
vm.etch(
Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER,
address(new L2CrossDomainMessenger()).code
);
L2Messenger.initialize(address(L1Messenger));
// Set the L2ToL1MessagePasser at the correct address
vm.etch(
Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER,
address(new L2ToL1MessagePasser()).code
);
vm.label(
Lib_PredeployAddresses.OVM_ETH,
"OVM_ETH"
);
vm.label(
Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER,
"L2ToL1MessagePasser"
);
vm.label(
Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER,
"L2CrossDomainMessenger"
);
vm.label(
AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger)),
"L1CrossDomainMessenger_aliased"
);
}
}
contract Bridge_Initializer is Messenger_Initializer {
L1StandardBridge L1Bridge;
L2StandardBridge L2Bridge;
OptimismMintableTokenFactory L2TokenFactory;
OptimismMintableTokenFactory L1TokenFactory;
ERC20 L1Token;
OptimismMintableERC20 L2Token;
ERC20 NativeL2Token;
OptimismMintableERC20 RemoteL1Token;
event ETHDepositInitiated(
address indexed _from,
address indexed _to,
uint256 _amount,
bytes _data
);
event ETHWithdrawalFinalized(
address indexed _from,
address indexed _to,
uint256 _amount,
bytes _data
);
event ERC20DepositInitiated(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event ERC20WithdrawalFinalized(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event WithdrawalInitiated(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event DepositFinalized(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event DepositFailed(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event ETHBridgeInitiated(
address indexed _from,
address indexed _to,
uint256 _amount,
bytes _data
);
event ETHBridgeFinalized(
address indexed _from,
address indexed _to,
uint256 _amount,
bytes _data
);
event ERC20BridgeInitiated(
address indexed _localToken,
address indexed _remoteToken,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
event ERC20BridgeFinalized(
address indexed _localToken,
address indexed _remoteToken,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);
function setUp() public virtual override {
super.setUp();
vm.label(
Lib_PredeployAddresses.L2_STANDARD_BRIDGE,
"L2StandardBridge"
);
vm.label(
Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY,
"L2StandardTokenFactory"
);
// Deploy the L1 bridge and initialize it with the address of the
// L1CrossDomainMessenger
L1Bridge = new L1StandardBridge();
L1Bridge.initialize(payable(address(L1Messenger)));
vm.label(address(L1Bridge), "L1StandardBridge");
// Deploy the L2StandardBridge, move it to the correct predeploy
// address and then initialize it
L2StandardBridge l2B = new L2StandardBridge();
vm.etch(Lib_PredeployAddresses.L2_STANDARD_BRIDGE, address(l2B).code);
L2Bridge = L2StandardBridge(payable(Lib_PredeployAddresses.L2_STANDARD_BRIDGE));
L2Bridge.initialize(payable(address(L1Bridge)));
// Set up the L2 mintable token factory
OptimismMintableTokenFactory factory = new OptimismMintableTokenFactory();
vm.etch(Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY, address(factory).code);
L2TokenFactory = OptimismMintableTokenFactory(Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY);
L2TokenFactory.initialize(Lib_PredeployAddresses.L2_STANDARD_BRIDGE);
vm.etch(
Lib_PredeployAddresses.OVM_ETH,
address(new OVM_ETH()).code
);
L1Token = new ERC20("Native L1 Token", "L1T");
// Deploy the L2 ERC20 now
L2Token = OptimismMintableERC20(L2TokenFactory.createStandardL2Token(
address(L1Token),
string(abi.encodePacked("L2-", L1Token.name())),
string(abi.encodePacked("L2-", L1Token.symbol()))
));
NativeL2Token = new ERC20("Native L2 Token", "L2T");
L1TokenFactory = new OptimismMintableTokenFactory();
L1TokenFactory.initialize(address(L1Bridge));
RemoteL1Token = OptimismMintableERC20(L1TokenFactory.createStandardL2Token(
address(NativeL2Token),
string(abi.encodePacked("L1-", NativeL2Token.name())),
string(abi.encodePacked("L1-", NativeL2Token.symbol()))
));
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { CommonTest } from "./CommonTest.t.sol";
import { CrossDomainHashing } from "../libraries/Lib_CrossDomainHashing.sol";
contract CrossDomainHashing_Test is CommonTest {
function test_nonceVersioning(uint240 _nonce, uint16 _version) external {
uint256 nonce = CrossDomainHashing.addVersionToNonce(uint256(_nonce), _version);
uint16 version = CrossDomainHashing.getVersionFromNonce(nonce);
assertEq(version, _version);
}
// TODO(tynes): turn this into differential fuzzing
// it is very easy to do so with the typescript
function test_l2TransactionHash() external {
bytes32 l1BlockHash = 0xd1a498e053451fc90bd8a597051a1039010c8e55e2659b940d3070b326e4f4c5;
uint256 logIndex = 0x0;
address from = address(0xDe3829A23DF1479438622a08a116E8Eb3f620BB5);
address to = address(0xB7e390864a90b7b923C9f9310C6F98aafE43F707);
bool isCreate = false;
uint256 mint = 0xe043da617250000;
uint256 value = 0xde0b6b3a7640000;
uint256 gas = 0x2dc6c0;
bytes memory data = hex"";
bytes32 sourceHash = CrossDomainHashing.sourceHash(
l1BlockHash,
logIndex
);
assertEq(
sourceHash,
0x77fc5994647d128a4d131d273a5e89e0306aac472494068a4f1fceab83dd0735
);
bytes memory raw = CrossDomainHashing.L2Transaction(
l1BlockHash,
logIndex,
from,
to,
isCreate,
mint,
value,
gas,
data
);
assertEq(
raw,
hex"7ef862a077fc5994647d128a4d131d273a5e89e0306aac472494068a4f1fceab83dd073594de3829a23df1479438622a08a116e8eb3f620bb594b7e390864a90b7b923c9f9310c6f98aafe43f707880e043da617250000880de0b6b3a7640000832dc6c080"
);
bytes32 digest = CrossDomainHashing.L2TransactionHash(
l1BlockHash,
logIndex,
from,
to,
isCreate,
mint,
value,
gas,
data
);
assertEq(
digest,
0xf5f97d03e8be48a4b20ed70c9d8b11f1c851bf949bf602b7580985705bb09077
);
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { CommonTest } from "./CommonTest.t.sol";
import { DeployerWhitelist } from "../L2/DeployerWhitelist.sol";
contract DeployerWhitelist_Test is CommonTest {
DeployerWhitelist list;
function setUp() external {
list = new DeployerWhitelist();
}
// The owner should be address(0)
function test_owner() external {
assertEq(list.owner(), address(0));
}
// The storage slot for the owner must be the same
function test_storageSlots() external {
vm.prank(list.owner());
list.setOwner(address(1));
assertEq(
bytes32(uint256(1)),
vm.load(address(list), bytes32(uint256(0)))
);
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { CommonTest } from "./CommonTest.t.sol";
import { GasPriceOracle } from "../L2/GasPriceOracle.sol";
import { L1Block } from "../L2/L1Block.sol";
import { Lib_BedrockPredeployAddresses } from "../libraries/Lib_BedrockPredeployAddresses.sol";
contract GasPriceOracle_Test is CommonTest {
event OverheadUpdated(uint256);
event ScalarUpdated(uint256);
event DecimalsUpdated(uint256);
GasPriceOracle gasOracle;
L1Block l1Block;
address depositor;
function setUp() external {
// place the L1Block contract at the predeploy address
vm.etch(
Lib_BedrockPredeployAddresses.L1_BLOCK_ATTRIBUTES,
address(new L1Block()).code
);
l1Block = L1Block(Lib_BedrockPredeployAddresses.L1_BLOCK_ATTRIBUTES);
depositor = l1Block.DEPOSITOR_ACCOUNT();
// We are not setting the gas oracle at its predeploy
// address for simplicity purposes. Nothing in this test
// requires it to be at a particular address
gasOracle = new GasPriceOracle(alice);
// set the initial L1 context values
uint64 number = 10;
uint64 timestamp = 11;
uint256 basefee = 100;
bytes32 hash = bytes32(uint256(64));
uint64 sequenceNumber = 0;
vm.prank(depositor);
l1Block.setL1BlockValues(
number,
timestamp,
basefee,
hash,
sequenceNumber
);
}
function test_owner() external {
// alice is passed into the constructor of the gasOracle
assertEq(gasOracle.owner(), alice);
}
function test_storageLayout() external {
// the overhead is at slot 3
vm.prank(gasOracle.owner());
gasOracle.setOverhead(456);
assertEq(
456,
uint256(vm.load(address(gasOracle), bytes32(uint256(3))))
);
// scalar is at slot 4
vm.prank(gasOracle.owner());
gasOracle.setScalar(333);
assertEq(
333,
uint256(vm.load(address(gasOracle), bytes32(uint256(4))))
);
// decimals is at slot 5
vm.prank(gasOracle.owner());
gasOracle.setDecimals(222);
assertEq(
222,
uint256(vm.load(address(gasOracle), bytes32(uint256(5))))
);
}
function test_l1BaseFee() external {
uint256 l1BaseFee = gasOracle.l1BaseFee();
assertEq(l1BaseFee, 100);
}
function test_gasPrice() external {
vm.fee(100);
uint256 gasPrice = gasOracle.gasPrice();
assertEq(gasPrice, 100);
}
function test_baseFee() external {
vm.fee(64);
uint256 gasPrice = gasOracle.baseFee();
assertEq(gasPrice, 64);
}
function test_setGasPriceReverts() external {
vm.prank(gasOracle.owner());
(bool success, bytes memory returndata) = address(gasOracle).call(
abi.encodeWithSignature(
"setGasPrice(uint256)",
1
)
);
assertEq(success, false);
assertEq(returndata, hex"");
}
function test_setL1BaseFeeReverts() external {
vm.prank(gasOracle.owner());
(bool success, bytes memory returndata) = address(gasOracle).call(
abi.encodeWithSignature(
"setL1BaseFee(uint256)",
1
)
);
assertEq(success, false);
assertEq(returndata, hex"");
}
function test_setOverhead() external {
vm.expectEmit(true, true, true, true);
emit OverheadUpdated(1234);
vm.prank(gasOracle.owner());
gasOracle.setOverhead(1234);
assertEq(gasOracle.overhead(), 1234);
}
function test_onlyOwnerSetOverhead() external {
vm.expectRevert("Ownable: caller is not the owner");
gasOracle.setOverhead(0);
}
function test_setScalar() external {
vm.expectEmit(true, true, true, true);
emit ScalarUpdated(666);
vm.prank(gasOracle.owner());
gasOracle.setScalar(666);
assertEq(gasOracle.scalar(), 666);
}
function test_onlyOwnerSetScalar() external {
vm.expectRevert("Ownable: caller is not the owner");
gasOracle.setScalar(0);
}
function test_setDecimals() external {
vm.expectEmit(true, true, true, true);
emit DecimalsUpdated(18);
vm.prank(gasOracle.owner());
gasOracle.setDecimals(18);
assertEq(gasOracle.decimals(), 18);
}
function test_onlyOwnerSetDecimals() external {
vm.expectRevert("Ownable: caller is not the owner");
gasOracle.setDecimals(0);
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { CommonTest } from "./CommonTest.t.sol";
import { L1Block } from "../L2/L1Block.sol";
contract L1BlockTest is CommonTest {
L1Block lb;
address depositor;
bytes32 immutable NON_ZERO_HASH = keccak256(abi.encode(1));
function setUp() external {
lb = new L1Block();
depositor = lb.DEPOSITOR_ACCOUNT();
vm.prank(depositor);
lb.setL1BlockValues(uint64(1), uint64(2), 3, NON_ZERO_HASH, uint64(4));
}
function test_updatesValues(uint64 n, uint64 t, uint256 b, bytes32 h, uint64 s) external {
vm.prank(depositor);
lb.setL1BlockValues(n, t, b, h, s);
assertEq(lb.number(), n);
assertEq(lb.timestamp(), t);
assertEq(lb.basefee(), b);
assertEq(lb.hash(), h);
assertEq(lb.sequenceNumber(), s);
}
function test_number() external {
assertEq(lb.number(), uint64(1));
}
function test_timestamp() external {
assertEq(lb.timestamp(), uint64(2));
}
function test_basefee() external {
assertEq(lb.basefee(), 3);
}
function test_hash() external {
assertEq(lb.hash(), NON_ZERO_HASH);
}
function test_sequenceNumber() external {
assertEq(lb.sequenceNumber(), uint64(4));
}
function test_updateValues() external {
vm.prank(depositor);
lb.setL1BlockValues(type(uint64).max, type(uint64).max, type(uint256).max, keccak256(abi.encode(1)), type(uint64).max);
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { Test } from "forge-std/Test.sol";
import { L1Block } from "../L2/L1Block.sol";
import { L1BlockNumber } from "../L2/L1BlockNumber.sol";
import { Lib_BedrockPredeployAddresses } from "../libraries/Lib_BedrockPredeployAddresses.sol";
contract L1BlockNumberTest is Test {
L1Block lb;
L1BlockNumber bn;
function setUp() external {
vm.etch(Lib_BedrockPredeployAddresses.L1_BLOCK_ATTRIBUTES, address(new L1Block()).code);
lb = L1Block(Lib_BedrockPredeployAddresses.L1_BLOCK_ATTRIBUTES);
bn = new L1BlockNumber();
vm.prank(lb.DEPOSITOR_ACCOUNT());
lb.setL1BlockValues(uint64(999), uint64(2), 3, keccak256(abi.encode(1)), uint64(4));
}
function test_getL1BlockNumber() external {
assertEq(bn.getL1BlockNumber(), 999);
}
function test_fallback() external {
(bool success, bytes memory ret) = address(bn).call(hex"");
assertEq(success, true);
assertEq(ret, abi.encode(999));
}
function test_receive() external {
(bool success, bytes memory ret) = address(bn).call{ value: 1 }(hex"");
assertEq(success, true);
assertEq(ret, abi.encode(999));
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
/* Testing utilities */
import { Messenger_Initializer } from "./CommonTest.t.sol";
import { L2OutputOracle_Initializer } from "./L2OutputOracle.t.sol";
/* Libraries */
import {
AddressAliasHelper
} from "@eth-optimism/contracts/standards/AddressAliasHelper.sol";
import {
Lib_DefaultValues
} from "@eth-optimism/contracts/libraries/constants/Lib_DefaultValues.sol";
import {
Lib_PredeployAddresses
} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
import {
Lib_CrossDomainUtils
} from "@eth-optimism/contracts/libraries/bridge/Lib_CrossDomainUtils.sol";
import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol";
/* Target contract dependencies */
import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { OptimismPortal } from "../L1/OptimismPortal.sol";
import { CrossDomainHashing } from "../libraries/Lib_CrossDomainHashing.sol";
/* Target contract */
import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol";
import {
ICrossDomainMessenger
} from "@eth-optimism/contracts/libraries/bridge/ICrossDomainMessenger.sol";
contract L1CrossDomainMessenger_Test is Messenger_Initializer {
// Receiver address for testing
address recipient = address(0xabbaacdc);
function setUp() public override {
super.setUp();
}
// pause: should pause the contract when called by the current owner
function test_L1MessengerPause() external {
L1Messenger.pause();
assert(L1Messenger.paused());
}
// pause: should not pause the contract when called by account other than the owner
function testCannot_L1MessengerPause() external {
vm.expectRevert("Ownable: caller is not the owner");
vm.prank(address(0xABBA));
L1Messenger.pause();
}
// the version is encoded in the nonce
function test_L1MessengerMessageVersion() external {
assertEq(
CrossDomainHashing.getVersionFromNonce(L1Messenger.messageNonce()),
L1Messenger.MESSAGE_VERSION()
);
}
// sendMessage: should be able to send a single message
// TODO: this same test needs to be done with the legacy message type
// by setting the message version to 0
function test_L1MessengerSendMessage() external {
// deposit transaction on the optimism portal should be called
vm.expectCall(
address(op),
abi.encodeWithSelector(
OptimismPortal.depositTransaction.selector,
Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER,
0,
100 + L1Messenger.baseGas(hex"ff"),
false,
CrossDomainHashing.getVersionedEncoding(
L1Messenger.messageNonce(),
alice,
recipient,
0,
100,
hex"ff"
)
)
);
// TransactionDeposited event
vm.expectEmit(true, true, true, true);
emit TransactionDeposited(
AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger)),
Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER,
0,
0,
100 + L1Messenger.baseGas(hex"ff"),
false,
CrossDomainHashing.getVersionedEncoding(
L1Messenger.messageNonce(),
alice,
recipient,
0,
100,
hex"ff"
)
);
// SentMessage event
vm.expectEmit(true, true, true, true);
emit SentMessage(
recipient,
alice,
hex"ff",
L1Messenger.messageNonce(),
100
);
vm.prank(alice);
L1Messenger.sendMessage(recipient, hex"ff", uint32(100));
}
// sendMessage: should be able to send the same message twice
function test_L1MessengerTwiceSendMessage() external {
uint256 nonce = L1Messenger.messageNonce();
L1Messenger.sendMessage(recipient, hex"aa", uint32(500_000));
L1Messenger.sendMessage(recipient, hex"aa", uint32(500_000));
// the nonce increments for each message sent
assertEq(
nonce + 2,
L1Messenger.messageNonce()
);
}
function test_L1MessengerXDomainSenderReverts() external {
vm.expectRevert("xDomainMessageSender is not set");
L1Messenger.xDomainMessageSender();
}
// xDomainMessageSender: should return the xDomainMsgSender address
// TODO: might need a test contract
// function test_xDomainSenderSetCorrectly() external {}
// relayMessage: should send a successful call to the target contract
function test_L1MessengerRelayMessageSucceeds() external {
address target = address(0xabcd);
address sender = Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER;
vm.expectCall(target, hex"1111");
// set the value of op.l2Sender() to be the L2 Cross Domain Messenger.
vm.store(address(op), 0, bytes32(abi.encode(sender)));
vm.prank(address(op));
vm.expectEmit(true, true, true, true);
bytes32 hash = CrossDomainHashing.getVersionedHash(
0,
sender,
target,
0,
0,
hex"1111"
);
emit RelayedMessage(hash);
L1Messenger.relayMessage(
0, // nonce
sender,
target,
0, // value
0,
hex"1111"
);
// the message hash is in the successfulMessages mapping
assert(L1Messenger.successfulMessages(hash));
// it is not in the received messages mapping
assertEq(L1Messenger.receivedMessages(hash), false);
}
// relayMessage: should revert if attempting to relay a message sent to an L1 system contract
function test_L1MessengerRelayMessageToSystemContract() external {
// set the target to be the OptimismPortal
address target = address(op);
address sender = Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER;
bytes memory message = hex"1111";
// set the value of op.l2Sender() to be the L2 Cross Domain Messenger.
vm.prank(address(op));
vm.expectRevert("Message cannot be replayed.");
L1Messenger.relayMessage(0, sender, target, 0, 0, message);
vm.store(address(op), 0, bytes32(abi.encode(sender)));
vm.expectRevert("Message cannot be replayed.");
L1Messenger.relayMessage(0, sender, target, 0, 0, message);
}
// relayMessage: the xDomainMessageSender is reset to the original value
function test_L1MessengerxDomainMessageSenderResets() external {
vm.expectRevert("xDomainMessageSender is not set");
L1Messenger.xDomainMessageSender();
address sender = Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER;
vm.store(address(op), 0, bytes32(abi.encode(sender)));
vm.prank(address(op));
L1Messenger.relayMessage(0, address(0), address(0), 0, 0, hex"");
vm.expectRevert("xDomainMessageSender is not set");
L1Messenger.xDomainMessageSender();
}
// relayMessage: should revert if paused
function test_L1MessengerRelayShouldRevertIfPaused() external {
vm.prank(L1Messenger.owner());
L1Messenger.pause();
vm.expectRevert("Pausable: paused");
L1Messenger.relayMessage(0, address(0), address(0), 0, 0, hex"");
}
}
This diff is collapsed.
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { Messenger_Initializer } from "./CommonTest.t.sol";
import {
Lib_PredeployAddresses
} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
import {
Lib_CrossDomainUtils
} from "@eth-optimism/contracts/libraries/bridge/Lib_CrossDomainUtils.sol";
import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol";
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { L2CrossDomainMessenger } from "../L2/L2CrossDomainMessenger.sol";
import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol";
import { Lib_BedrockPredeployAddresses } from "../libraries/Lib_BedrockPredeployAddresses.sol";
import { CrossDomainHashing } from "../libraries/Lib_CrossDomainHashing.sol";
import {
Lib_DefaultValues
} from "@eth-optimism/contracts/libraries/constants/Lib_DefaultValues.sol";
import { console } from "forge-std/console.sol";
contract L2CrossDomainMessenger_Test is Messenger_Initializer {
// Receiver address for testing
address recipient = address(0xabbaacdc);
function setUp() public override {
super.setUp();
}
function test_L2MessengerPause() external {
L2Messenger.pause();
assert(L2Messenger.paused());
}
function testCannot_L2MessengerPause() external {
vm.expectRevert("Ownable: caller is not the owner");
vm.prank(address(0xABBA));
L2Messenger.pause();
}
function test_L2MessengerMessageVersion() external {
assertEq(
CrossDomainHashing.getVersionFromNonce(L2Messenger.messageNonce()),
L2Messenger.MESSAGE_VERSION()
);
}
function test_L2MessengerSendMessage() external {
vm.expectCall(
address(messagePasser),
abi.encodeWithSelector(
L2ToL1MessagePasser.initiateWithdrawal.selector,
address(L1Messenger),
100 + L2Messenger.baseGas(hex"ff"),
CrossDomainHashing.getVersionedEncoding(
L2Messenger.messageNonce(),
alice,
recipient,
0,
100,
hex"ff"
)
)
);
// WithdrawalInitiated event
vm.expectEmit(true, true, true, true);
emit WithdrawalInitiated(
messagePasser.nonce(),
address(L2Messenger),
address(L1Messenger),
0,
100 + L2Messenger.baseGas(hex"ff"),
CrossDomainHashing.getVersionedEncoding(
L2Messenger.messageNonce(),
alice,
recipient,
0,
100,
hex"ff"
)
);
vm.prank(alice);
L2Messenger.sendMessage(recipient, hex"ff", uint32(100));
}
function test_L2MessengerTwiceSendMessage() external {
uint256 nonce = L2Messenger.messageNonce();
L2Messenger.sendMessage(recipient, hex"aa", uint32(500_000));
L2Messenger.sendMessage(recipient, hex"aa", uint32(500_000));
// the nonce increments for each message sent
assertEq(
nonce + 2,
L2Messenger.messageNonce()
);
}
function test_L2MessengerXDomainSenderReverts() external {
vm.expectRevert("xDomainMessageSender is not set");
L2Messenger.xDomainMessageSender();
}
function test_L2MessengerRelayMessageSucceeds() external {
address target = address(0xabcd);
address sender = address(L1Messenger);
address caller = AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger));
vm.expectCall(target, hex"1111");
vm.prank(caller);
vm.expectEmit(true, true, true, true);
bytes32 hash = CrossDomainHashing.getVersionedHash(
0,
sender,
target,
0,
0,
hex"1111"
);
emit RelayedMessage(hash);
L2Messenger.relayMessage(
0, // nonce
sender,
target,
0, // value
0,
hex"1111"
);
// the message hash is in the successfulMessages mapping
assert(L2Messenger.successfulMessages(hash));
// it is not in the received messages mapping
assertEq(L2Messenger.receivedMessages(hash), false);
}
// relayMessage: should revert if attempting to relay a message sent to an L1 system contract
function test_L2MessengerRelayMessageToSystemContract() external {
address target = address(messagePasser);
address sender = address(L1Messenger);
address caller = AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger));
bytes memory message = hex"1111";
vm.prank(caller);
vm.expectRevert("Message cannot be replayed.");
L1Messenger.relayMessage(0, sender, target, 0, 0, message);
}
// relayMessage: the xDomainMessageSender is reset to the original value
function test_L2MessengerxDomainMessageSenderResets() external {
vm.expectRevert("xDomainMessageSender is not set");
L2Messenger.xDomainMessageSender();
address caller = AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger));
vm.prank(caller);
L2Messenger.relayMessage(0, address(0), address(0), 0, 0, hex"");
vm.expectRevert("xDomainMessageSender is not set");
L2Messenger.xDomainMessageSender();
}
// relayMessage: should revert if paused
function test_L2MessengerRelayShouldRevertIfPaused() external {
vm.prank(L2Messenger.owner());
L2Messenger.pause();
vm.expectRevert("Pausable: paused");
L2Messenger.relayMessage(0, address(0), address(0), 0, 0, hex"");
}
}
This diff is collapsed.
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { Bridge_Initializer } from "./CommonTest.t.sol";
import { stdStorage, StdStorage } from "forge-std/Test.sol";
import { CrossDomainMessenger } from "../universal/CrossDomainMessenger.sol";
import { console } from "forge-std/console.sol";
contract L2StandardBridge_Test is Bridge_Initializer {
using stdStorage for StdStorage;
function setUp() public override {
super.setUp();
}
function test_initialize() external {
assertEq(
address(L2Bridge.messenger()),
address(L2Messenger)
);
assertEq(
address(L2Bridge.otherBridge()),
address(L1Bridge)
);
}
// receive
// - can accept ETH
function test_receive() external {
assertEq(address(messagePasser).balance, 0);
vm.expectEmit(true, true, true, true);
emit ETHBridgeInitiated(alice, alice, 100, hex"");
// TODO: L2Messenger should be called
// TODO: L2ToL1MessagePasser should be called
// TODO: withdrawal hash should be computed correctly
// TODO: events from each contract
vm.prank(alice, alice);
address(L2Bridge).call{ value: 100 }(hex"");
assertEq(address(messagePasser).balance, 100);
}
// withdraw
// - token is burned
// - emits WithdrawalInitiated
// - calls Withdrawer.initiateWithdrawal
function test_withdraw() external {
// Alice has 100 L2Token
deal(address(L2Token), alice, 100, true);
assertEq(L2Token.balanceOf(alice), 100);
vm.prank(alice, alice);
L2Bridge.withdraw(
address(L2Token),
100,
1000,
hex""
);
// TODO: events and calls
assertEq(L2Token.balanceOf(alice), 0);
}
// withdrawTo
// - token is burned
// - emits WithdrawalInitiated w/ correct recipient
// - calls Withdrawer.initiateWithdrawal
function test_withdrawTo() external {
deal(address(L2Token), alice, 100, true);
vm.prank(alice, alice);
L2Bridge.withdrawTo(
address(L2Token),
bob,
100,
1000,
hex""
);
// TODO: events and calls
assertEq(L2Token.balanceOf(alice), 0);
}
// finalizeDeposit
// - only callable by l1TokenBridge
// - supported token pair emits DepositFinalized
// - invalid deposit emits DepositFailed
// - invalid deposit calls Withdrawer.initiateWithdrawal
function test_finalizeDeposit() external {
// TODO: events and calls
vm.mockCall(
address(L2Bridge.messenger()),
abi.encodeWithSelector(CrossDomainMessenger.xDomainMessageSender.selector),
abi.encode(address(L2Bridge.otherBridge()))
);
vm.prank(address(L2Messenger));
L2Bridge.finalizeDeposit(
address(L1Token),
address(L2Token),
alice,
alice,
100,
hex""
);
}
}
This diff is collapsed.
// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.0;
import {Bytes32AddressLib} from "solmate/utils/Bytes32AddressLib.sol";
// prettier-ignore
library LibRLP {
using Bytes32AddressLib for bytes32;
function computeAddress(address deployer, uint256 nonce) public pure returns (address) {
// The integer zero is treated as an empty byte string, and as a result it only has a length prefix, 0x80, computed via 0x80 + 0.
// A one byte integer uses its own value as its length prefix, there is no additional "0x80 + length" prefix that comes before it.
if (nonce == 0x00) return keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, bytes1(0x80))).fromLast20Bytes();
if (nonce <= 0x7f) return keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, uint8(nonce))).fromLast20Bytes();
// Nonces greater than 1 byte all follow a consistent encoding scheme, where each value is preceded by a prefix of 0x80 + length.
if (nonce <= type(uint8).max) return keccak256(abi.encodePacked(bytes1(0xd7), bytes1(0x94), deployer, bytes1(0x81), uint8(nonce))).fromLast20Bytes();
if (nonce <= type(uint16).max) return keccak256(abi.encodePacked(bytes1(0xd8), bytes1(0x94), deployer, bytes1(0x82), uint16(nonce))).fromLast20Bytes();
if (nonce <= type(uint24).max) return keccak256(abi.encodePacked(bytes1(0xd9), bytes1(0x94), deployer, bytes1(0x83), uint24(nonce))).fromLast20Bytes();
// More details about RLP encoding can be found here: https://eth.wiki/fundamentals/rlp
// 0xda = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x84 ++ nonce)
// 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex)
// 0x84 = 0x80 + 0x04 (0x04 = the bytes length of the nonce, 4 bytes, in hex)
// We assume nobody can have a nonce large enough to require more than 32 bytes.
return keccak256(abi.encodePacked(bytes1(0xda), bytes1(0x94), deployer, bytes1(0x84), uint32(nonce))).fromLast20Bytes();
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import { CommonTest } from "./CommonTest.t.sol";
import { OVM_ETH } from "../L2/OVM_ETH.sol";
import {
Lib_PredeployAddresses
} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
contract OVM_ETH_Test is CommonTest {
OVM_ETH eth;
function setUp() external {
eth = new OVM_ETH();
}
function test_metadata() external {
assertEq(eth.name(), "Ether");
assertEq(eth.symbol(), "ETH");
assertEq(eth.decimals(), 18);
}
function test_crossDomain() external {
assertEq(eth.l2Bridge(), Lib_PredeployAddresses.L2_STANDARD_BRIDGE);
assertEq(eth.l1Token(), address(0));
}
function test_transfer() external {
vm.expectRevert("OVM_ETH: transfer is disabled");
eth.transfer(alice, 100);
}
function test_approve() external {
vm.expectRevert("OVM_ETH: approve is disabled");
eth.approve(alice, 100);
}
function test_transferFrom() external {
vm.expectRevert("OVM_ETH: transferFrom is disabled");
eth.transferFrom(bob, alice, 100);
}
function test_increaseAllowance() external {
vm.expectRevert("OVM_ETH: increaseAllowance is disabled");
eth.increaseAllowance(alice, 100);
}
function test_decreaseAllowance() external {
vm.expectRevert("OVM_ETH: decreaseAllowance is disabled");
eth.decreaseAllowance(alice, 100);
}
function test_mint() external {
vm.expectRevert("OVM_ETH: mint is disabled");
eth.mint(alice, 100);
}
function test_burn() external {
vm.expectRevert("OVM_ETH: burn is disabled");
eth.burn(alice, 100);
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/**
* Predeploys are Solidity contracts that are injected into the initial L2 state and provide
* various useful functions.
*/
export const bedrockPredeploys = {
L1_BLOCK_ATTRIBUTES: '0x4200000000000000000000000000000000000015',
WITHDRAWER: '0x4200000000000000000000000000000000000016',
}
This diff is collapsed.
export * from './utils'
export * from './generateProofs'
export * from './constants'
This diff is collapsed.
This diff is collapsed.
Subproject commit c7a36fb236f298e04edf28e2fee385b80f53945f
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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