Commit 9690fdd5 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

Merge pull request #2504 from ethereum-optimism/develop

Develop -> Master
parents 0b4fd1ea d9f058ce
---
'@eth-optimism/proxyd': patch
---
proxyd: Log ssanitized RPC requests
---
'@eth-optimism/indexer': minor
---
Add airdrops API
---
'@eth-optimism/proxyd': patch
---
proxyd: Reduced RPC request logging
---
'@eth-optimism/teleportr': patch
---
Use L2 gas price in driver
---
'@eth-optimism/l2geth-exporter': patch
---
Added SCC collection
---
'@eth-optimism/proxyd': patch
---
proxyd: Limit the number of concurrent RPCs to backends
---
'@eth-optimism/indexer': patch
---
fix context reuse
---
'@eth-optimism/l2geth': patch
---
rollup: fix log.Crit usage
---
'@eth-optimism/l2geth': patch
---
l2geth: Record rollup transaction metrics
---
'@eth-optimism/replica-healthcheck': patch
---
Fixes a bug that would cause the service to stop properly checking blocks when the target client consistently leads the reference client
---
'@eth-optimism/integration-tests': patch
---
Add tests for system addrs on verifiers/replicas
---
'@eth-optimism/sdk': patch
---
Fixes a bug where the wrong Overrides type was being used for gas estimation functions
version: 2.1 version: 2.1
orbs:
gcp-gke: circleci/gcp-gke@1.3.0
slack: circleci/slack@4.5.1
slack-nightly-build-fail-post-step: &slack-nightly-build-fail-post-step
post-steps:
- slack/notify:
channel: $SLACK_DEFAULT_CHANNEL
event: fail
template: basic_fail-1
executors: executors:
go-builder: go-builder:
...@@ -15,29 +6,6 @@ executors: ...@@ -15,29 +6,6 @@ executors:
- image: ethereumoptimism/go-builder:latest - image: ethereumoptimism/go-builder:latest
commands: commands:
build-dockerfile:
parameters:
image-name:
description: Image name
type: string
target:
description: Dockerfile target
type: string
default: ""
dockerfile:
description: Dockerfile to use
type: string
steps:
- checkout
- setup_remote_docker:
version: 19.03.13
- run:
name: Build
command: |
echo -n "$STACKMAN_REPO_AUTH" | docker login -u _json_key --password-stdin https://us-east4-docker.pkg.dev
docker build -t "$STACKMAN_REPO/<<parameters.image-name>>:nightly" -f <<parameters.dockerfile>> <<#parameters.target>>--target <<parameters.target>><</parameters.target>> .
docker push "$STACKMAN_REPO/<<parameters.image-name>>:nightly"
go-lint-test: go-lint-test:
parameters: parameters:
working_directory: working_directory:
...@@ -59,118 +27,6 @@ commands: ...@@ -59,118 +27,6 @@ commands:
path: /test-results path: /test-results
jobs: jobs:
build-dtl:
docker:
- image: cimg/base:2021.04
steps:
- build-dockerfile:
image-name: data-transport-layer
target: data-transport-layer
dockerfile: ./ops/docker/Dockerfile.packages
build-go-batch-submitter:
docker:
- image: cimg/base:2021.04
steps:
- build-dockerfile:
image-name: go-batch-submitter
dockerfile: ./ops/docker/Dockerfile.batch-submitter-service
build-deployer:
docker:
- image: cimg/base:2021.04
steps:
- build-dockerfile:
image-name: deployer
target: deployer
dockerfile: ./ops/docker/Dockerfile.packages
build-l2geth:
docker:
- image: cimg/base:2021.04
steps:
- build-dockerfile:
image-name: l2geth
dockerfile: ./ops/docker/Dockerfile.geth
build-gas-oracle:
docker:
- image: cimg/base:2021.04
steps:
- build-dockerfile:
image-name: gas-oracle
dockerfile: ./ops/docker/Dockerfile.gas-oracle
build-integration-tests:
docker:
- image: cimg/base:2021.04
steps:
- build-dockerfile:
image-name: integration-tests
target: integration-tests
dockerfile: ./ops/docker/Dockerfile.packages
build-proxyd:
docker:
- image: cimg/base:2021.04
steps:
- build-dockerfile:
image-name: proxyd
dockerfile: ./go/proxyd/Dockerfile
deploy-nightly:
docker:
- image: cimg/base:2021.04
steps:
- gcp-gke/install
- gcp-gke/update-kubeconfig-with-credentials:
cluster: $STACKMAN_CLUSTER
gcloud-service-key: STACKMAN_SERVICE_KEY
google-compute-region: STACKMAN_COMPUTE_REGION
google-compute-zone: STACKMAN_COMPUTE_ZONE
google-project-id: STACKMAN_PROJECT_ID
install-kubectl: yes
perform-login: yes
- run:
name: Deploy
command: |
echo "Current nightly pods:"
kubectl get pods --namespace nightly
echo "Redeploying pods:"
kubectl rollout restart statefulset nightly-sequencer --namespace nightly
kubectl rollout restart statefulset nightly-go-batch-submitter --namespace nightly
kubectl rollout restart statefulset nightly-dtl --namespace nightly
kubectl rollout restart deployment nightly-gas-oracle --namespace nightly
kubectl rollout restart deployment edge-proxyd --namespace nightly
run-itests-nightly:
docker:
- image: cimg/base:2021.04
steps:
- setup_remote_docker:
version: 19.03.13
- run:
name: Run integration tests
command: |
docker run \
--env PRIVATE_KEY=$NIGHTLY_ITESTS_PRIVKEY \
--env L1_URL=https://nightly-l1.optimism-stacks.net \
--env L2_URL=https://nightly-l2.optimism-stacks.net \
--env ADDRESS_MANAGER=0xfcA6De8Db94C4d99bD5a7f5De1bb7A039265Ac42 \
--env L2_CHAINID=69 \
--env MOCHA_BAIL=true \
--env MOCHA_TIMEOUT=300000 \
--env L1_GAS_PRICE=onchain \
--env L2_GAS_PRICE=onchain \
--env RUN_DEBUG_TRACE_TESTS=false \
--env RUN_REPLICA_TESTS=false \
--env RUN_HEALTHCHECK_TESTS=false \
--env RUN_STRESS_TESTS=false \
--env OVMCONTEXT_SPEC_NUM_TXS=1 \
--env DTL_ENQUEUE_CONFIRMATIONS=12 \
"$STACKMAN_REPO/integration-tests:nightly" \
yarn test:integration:live
notify:
docker:
- image: cimg/base:2021.04
steps:
- run:
name: Success
command: |
echo "Dummy job."
go-lint-test: go-lint-test:
parameters: parameters:
working_directory: working_directory:
...@@ -400,6 +256,55 @@ jobs: ...@@ -400,6 +256,55 @@ jobs:
command: yarn test:coverage command: yarn test:coverage
working_directory: packages/<<parameters.package_name>> working_directory: packages/<<parameters.package_name>>
docker-publish:
environment:
DOCKER_BUILDKIT: 1
parameters:
docker_tags:
description: Docker image tags as csv
type: string
docker_file:
description: Path to Dockerfile
type: string
docker_context:
description: Docker build context
type: string
target:
description: Docker build target
type: string
default: ""
docker:
- image: circleci/buildpack-deps:stretch
steps:
- checkout
- setup_remote_docker:
version: 20.10.12
- when:
condition: <<parameters.target>>
steps:
- run:
name: Build with context
command: |
docker build \
$(echo -ne "<< parameters.docker_tags >>" | sed "s/,/\n/g" | sed -e 's/^/-t /' | tr '\n' ' ') \
-f <<parameters.docker_file>> \
--target <<parameters.target>> \
<<parameters.docker_context>>
- unless:
condition: <<parameters.target>>
steps:
- run:
name: Build
command: |
docker build \
$(echo -ne "<< parameters.docker_tags >>" | sed "s/,/\n/g" | sed -e 's/^/-t /' | tr '\n' ' ') \
-f <<parameters.docker_file>> \
<<parameters.docker_context>>
- run:
name: Publish
command: |
echo "$DOCKER_PASS" | docker login -u "$DOCKER_USERNAME" --password-stdin
docker push <<parameters.docker_tags>>
workflows: workflows:
main: main:
...@@ -462,166 +367,122 @@ workflows: ...@@ -462,166 +367,122 @@ workflows:
- geth-tests - geth-tests
- integration-tests - integration-tests
nightly:
nightly-itests:
triggers: triggers:
- schedule: - schedule:
cron: "0 1 * * * " cron: "0 10 * * *"
filters: filters:
branches: branches:
only: only:
- develop - develop
jobs: jobs:
- run-itests-nightly: - yarn-monorepo
- docker-publish:
name: l2geth-release
docker_file: ops/docker/Dockerfile.geth
docker_tags: ethereumoptimism/l2geth:nightly
docker_context: .
context: context:
- optimism - optimism
post-steps: - docker-publish:
- slack/notify: name: gas-oracle-release
channel: $SLACK_DEFAULT_CHANNEL docker_file: ops/docker/Dockerfile.gas-oracle
event: fail docker_tags: ethereumoptimism/gas-oracle:nightly
custom: | docker_context: .
{
"text": "",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "🔴 Nightly integration tests failed!"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Job"
},
"url": "${CIRCLE_BUILD_URL}"
}
]
}
]
}
- slack/notify:
channel: $SLACK_DEFAULT_CHANNEL
event: pass
custom: |
{
"text": "",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "✅ Nightly integration tests passed."
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Job"
},
"url": "${CIRCLE_BUILD_URL}"
}
]
}
]
}
nightly:
triggers:
- schedule:
cron: "0 0 * * * "
filters:
branches:
only:
- develop
jobs:
- build-dtl:
context: context:
- optimism - optimism
- slack - docker-publish:
<<: *slack-nightly-build-fail-post-step name: hardhat-node-release
- build-deployer: docker_file: ops/docker/hardhat/Dockerfile
docker_tags: ethereumoptimism/hardhat-node:nightly
docker_context: ops/docker/hardhat
context: context:
- optimism - optimism
- slack - docker-publish:
<<: *slack-nightly-build-fail-post-step name: go-builder-release
- build-l2geth: docker_file: ops/docker/go-builder/Dockerfile
docker_tags: ethereumoptimism/go-builder:nightly
docker_context: .
context: context:
- optimism - optimism
- slack - docker-publish:
<<: *slack-nightly-build-fail-post-step name: js-builder-release
- build-gas-oracle: docker_file: ops/docker/js-builder/Dockerfile
docker_tags: ethereumoptimism/js-builder:nightly
docker_context: .
context: context:
- optimism - optimism
- slack - docker-publish:
<<: *slack-nightly-build-fail-post-step name: proxyd-release
- build-integration-tests: docker_file: go/proxyd/Dockerfile
docker_tags: ethereumoptimism/proxyd:nightly
docker_context: .
context: context:
- optimism - optimism
- slack - docker-publish:
<<: *slack-nightly-build-fail-post-step name: l2geth-exporter-release
- build-go-batch-submitter: docker_file: ops/docker/Dockerfile.l2geth-exporter
docker_tags: ethereumoptimism/l2geth-exporter:nightly
docker_context: .
context: context:
- optimism - optimism
- slack - docker-publish:
<<: *slack-nightly-build-fail-post-step name: op-exporter-release
- build-proxyd: docker_file: ops/docker/Dockerfile.op-exporter
docker_tags: ethereumoptimism/op-exporter:nightly
docker_context: .
context: context:
- optimism - optimism
- slack - docker-publish:
<<: *slack-nightly-build-fail-post-step name: message-relayer-release
- deploy-nightly: docker_file: ops/docker/Dockerfile.packages
docker_tags: ethereumoptimism/message-relayer:nightly
docker_context: .
target: message-relayer
context:
- optimism
- docker-publish:
name: data-transport-layer-release
docker_file: ops/docker/Dockerfile.packages
docker_tags: ethereumoptimism/data-transport-layer:nightly
docker_context: .
target: data-transport-layer
context:
- optimism
- docker-publish:
name: deployer-release
docker_file: ops/docker/Dockerfile.packages
docker_tags: ethereumoptimism/deployer:nightly
docker_context: .
target: integration-tests
context:
- optimism
- docker-publish:
name: replica-healthcheck-release
docker_file: ops/docker/Dockerfile.packages
docker_tags: ethereumoptimism/replica-healthcheck:nightly
docker_context: .
target: replica-healthcheck
context:
- optimism
- docker-publish:
name: batch-submitter-service-release
docker_file: ops/docker/Dockerfile.batch-submitter-service
docker_tags: ethereumoptimism/batch-submitter-service:nightly
docker_context: .
context:
- optimism
- docker-publish:
name: indexer-release
docker_file: ops/docker/Dockerfile.indexer
docker_tags: ethereumoptimism/indexer:nightly
docker_context: .
context:
- optimism
- docker-publish:
name: teleportr-release
docker_file: ops/docker/Dockerfile.teleportr
docker_tags: ethereumoptimism/teleportr:nightly
docker_context: .
context: context:
- optimism - optimism
- slack
<<: *slack-nightly-build-fail-post-step
requires:
- build-dtl
- build-go-batch-submitter
- build-deployer
- build-l2geth
- build-gas-oracle
- build-integration-tests
- build-proxyd
- notify:
context: slack
requires:
- deploy-nightly
post-steps:
- slack/notify:
custom: |
{
"text": "",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "✅ Nightly successfully deployed."
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Job"
},
"url": "${CIRCLE_BUILD_URL}"
}
]
}
]
}
event: always
...@@ -9,6 +9,7 @@ pull_request_rules: ...@@ -9,6 +9,7 @@ pull_request_rules:
- "#review-threads-unresolved=0" - "#review-threads-unresolved=0"
- "#approved-reviews-by>=2" - "#approved-reviews-by>=2"
- "#changes-requested-reviews-by=0" - "#changes-requested-reviews-by=0"
- "label!=do-not-merge"
- or: - or:
- and: - and:
- "label!=SR-Risk" - "label!=SR-Risk"
...@@ -100,7 +101,7 @@ pull_request_rules: ...@@ -100,7 +101,7 @@ pull_request_rules:
- name: Nag changesets - name: Nag changesets
conditions: conditions:
- and: - and:
- 'files~=\.(ts|go|js|mod|sum)$' - 'files~=\.((?<!\.spec\.)ts|go|js|mod|sum)$'
- '-files~=^\.changeset/(.*)\.md' - '-files~=^\.changeset/(.*)\.md'
actions: actions:
comment: comment:
......
package db
type Airdrop struct {
Address string `json:"address"`
VoterAmount string `json:"voterAmount"`
MultisigSignerAmount string `json:"multisigSignerAmount"`
GitcoinAmount string `json:"gitcoinAmount"`
ActiveBridgedAmount string `json:"activeBridgedAmount"`
OpUserAmount string `json:"opUserAmount"`
OpRepeatUserAmount string `json:"opRepeatUserAmount"`
BonusAmount string `json:"bonusAmount"`
TotalAmount string `json:"totalAmount"`
}
...@@ -3,6 +3,8 @@ package db ...@@ -3,6 +3,8 @@ package db
import ( import (
"database/sql" "database/sql"
"errors" "errors"
"fmt"
"strings"
l2common "github.com/ethereum-optimism/optimism/l2geth/common" l2common "github.com/ethereum-optimism/optimism/l2geth/common"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -17,6 +19,31 @@ type Database struct { ...@@ -17,6 +19,31 @@ type Database struct {
config string config string
} }
// NewDatabase returns the database for the given connection string.
func NewDatabase(config string) (*Database, error) {
db, err := sql.Open("postgres", config)
if err != nil {
return nil, err
}
err = db.Ping()
if err != nil {
return nil, err
}
for _, migration := range schema {
_, err = db.Exec(migration)
if err != nil {
return nil, err
}
}
return &Database{
db: db,
config: config,
}, nil
}
// Close closes the database. // Close closes the database.
// NOTE: "It is rarely necessary to close a DB." // NOTE: "It is rarely necessary to close a DB."
// See: https://pkg.go.dev/database/sql#Open // See: https://pkg.go.dev/database/sql#Open
...@@ -633,27 +660,38 @@ func (d *Database) GetIndexedL1BlockByHash(hash common.Hash) (*IndexedL1Block, e ...@@ -633,27 +660,38 @@ func (d *Database) GetIndexedL1BlockByHash(hash common.Hash) (*IndexedL1Block, e
return block, nil return block, nil
} }
// NewDatabase returns the database for the given connection string. const getAirdropQuery = `
func NewDatabase(config string) (*Database, error) { SELECT
db, err := sql.Open("postgres", config) address, voter_amount, multisig_signer_amount, gitcoin_amount,
if err != nil { active_bridged_amount, op_user_amount, op_repeat_user_amount,
return nil, err bonus_amount, total_amount
} FROM airdrops
WHERE address = $1
err = db.Ping() `
if err != nil {
return nil, err func (d *Database) GetAirdrop(address common.Address) (*Airdrop, error) {
row := d.db.QueryRow(getAirdropQuery, strings.ToLower(address.String()))
if row.Err() != nil {
return nil, fmt.Errorf("error getting airdrop: %v", row.Err())
}
airdrop := new(Airdrop)
err := row.Scan(
&airdrop.Address,
&airdrop.VoterAmount,
&airdrop.MultisigSignerAmount,
&airdrop.GitcoinAmount,
&airdrop.ActiveBridgedAmount,
&airdrop.OpUserAmount,
&airdrop.OpRepeatUserAmount,
&airdrop.BonusAmount,
&airdrop.TotalAmount,
)
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
} }
for _, migration := range schema {
_, err = db.Exec(migration)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("error scanning airdrop: %v", err)
} }
} return airdrop, nil
return &Database{
db: db,
config: config,
}, nil
} }
...@@ -107,6 +107,21 @@ CREATE UNIQUE INDEX IF NOT EXISTS l1_blocks_number ON l1_blocks(number); ...@@ -107,6 +107,21 @@ CREATE UNIQUE INDEX IF NOT EXISTS l1_blocks_number ON l1_blocks(number);
CREATE UNIQUE INDEX IF NOT EXISTS l2_blocks_number ON l2_blocks(number); CREATE UNIQUE INDEX IF NOT EXISTS l2_blocks_number ON l2_blocks(number);
` `
const createAirdropsTable = `
CREATE TABLE IF NOT EXISTS airdrops (
address VARCHAR(42) PRIMARY KEY,
voter_amount VARCHAR NOT NULL DEFAULT '0' CHECK(voter_amount ~ '^\d+$') ,
multisig_signer_amount VARCHAR NOT NULL DEFAULT '0' CHECK(multisig_signer_amount ~ '^\d+$'),
gitcoin_amount VARCHAR NOT NULL DEFAULT '0' CHECK(gitcoin_amount ~ '^\d+$'),
active_bridged_amount VARCHAR NOT NULL DEFAULT '0' CHECK(active_bridged_amount ~ '^\d+$'),
op_user_amount VARCHAR NOT NULL DEFAULT '0' CHECK(op_user_amount ~ '^\d+$'),
op_repeat_user_amount VARCHAR NOT NULL DEFAULT '0' CHECK(op_user_amount ~ '^\d+$'),
op_og_amount VARCHAR NOT NULL DEFAULT '0' CHECK(op_og_amount ~ '^\d+$'),
bonus_amount VARCHAR NOT NULL DEFAULT '0' CHECK(bonus_amount ~ '^\d+$'),
total_amount VARCHAR NOT NULL CHECK(voter_amount ~ '^\d+$')
)
`
var schema = []string{ var schema = []string{
createL1BlocksTable, createL1BlocksTable,
createL2BlocksTable, createL2BlocksTable,
...@@ -118,4 +133,5 @@ var schema = []string{ ...@@ -118,4 +133,5 @@ var schema = []string{
createDepositsTable, createDepositsTable,
createWithdrawalsTable, createWithdrawalsTable,
createL1L2NumberIndex, createL1L2NumberIndex,
createAirdropsTable,
} }
...@@ -9,6 +9,8 @@ import ( ...@@ -9,6 +9,8 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/ethereum-optimism/optimism/go/indexer/services"
l2rpc "github.com/ethereum-optimism/optimism/l2geth/rpc" l2rpc "github.com/ethereum-optimism/optimism/l2geth/rpc"
"github.com/ethereum-optimism/optimism/go/indexer/metrics" "github.com/ethereum-optimism/optimism/go/indexer/metrics"
...@@ -83,8 +85,10 @@ type Indexer struct { ...@@ -83,8 +85,10 @@ type Indexer struct {
l1IndexingService *l1.Service l1IndexingService *l1.Service
l2IndexingService *l2.Service l2IndexingService *l2.Service
airdropService *services.Airdrop
router *mux.Router router *mux.Router
metrics *metrics.Metrics
} }
// NewIndexer initializes the Indexer, gathering any resources // NewIndexer initializes the Indexer, gathering any resources
...@@ -201,7 +205,9 @@ func NewIndexer(cfg Config, gitVersion string) (*Indexer, error) { ...@@ -201,7 +205,9 @@ func NewIndexer(cfg Config, gitVersion string) (*Indexer, error) {
l2Client: l2Client, l2Client: l2Client,
l1IndexingService: l1IndexingService, l1IndexingService: l1IndexingService,
l2IndexingService: l2IndexingService, l2IndexingService: l2IndexingService,
airdropService: services.NewAirdrop(db, m),
router: mux.NewRouter(), router: mux.NewRouter(),
metrics: m,
}, nil }, nil
} }
...@@ -216,6 +222,7 @@ func (b *Indexer) Serve() error { ...@@ -216,6 +222,7 @@ func (b *Indexer) Serve() error {
b.router.HandleFunc("/v1/deposits/0x{address:[a-fA-F0-9]{40}}", b.l1IndexingService.GetDeposits).Methods("GET") b.router.HandleFunc("/v1/deposits/0x{address:[a-fA-F0-9]{40}}", b.l1IndexingService.GetDeposits).Methods("GET")
b.router.HandleFunc("/v1/withdrawal/0x{hash:[a-fA-F0-9]{64}}", b.l2IndexingService.GetWithdrawalBatch).Methods("GET") b.router.HandleFunc("/v1/withdrawal/0x{hash:[a-fA-F0-9]{64}}", b.l2IndexingService.GetWithdrawalBatch).Methods("GET")
b.router.HandleFunc("/v1/withdrawals/0x{address:[a-fA-F0-9]{40}}", b.l2IndexingService.GetWithdrawals).Methods("GET") b.router.HandleFunc("/v1/withdrawals/0x{address:[a-fA-F0-9]{40}}", b.l2IndexingService.GetWithdrawals).Methods("GET")
b.router.HandleFunc("/v1/airdrops/0x{address:[a-fA-F0-9]{40}}", b.airdropService.GetAirdrop)
b.router.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { b.router.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200) w.WriteHeader(200)
_, err := w.Write([]byte("OK")) _, err := w.Write([]byte("OK"))
...@@ -224,7 +231,7 @@ func (b *Indexer) Serve() error { ...@@ -224,7 +231,7 @@ func (b *Indexer) Serve() error {
} }
}) })
middleware := server.LoggingMiddleware(log.New("service", "server")) middleware := server.LoggingMiddleware(b.metrics, log.New("service", "server"))
port := strconv.FormatUint(b.cfg.RESTPort, 10) port := strconv.FormatUint(b.cfg.RESTPort, 10)
addr := fmt.Sprintf("%s:%s", b.cfg.RESTHostname, port) addr := fmt.Sprintf("%s:%s", b.cfg.RESTHostname, port)
......
...@@ -3,6 +3,8 @@ package metrics ...@@ -3,6 +3,8 @@ package metrics
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"strconv"
"time"
l2common "github.com/ethereum-optimism/optimism/l2geth/common" l2common "github.com/ethereum-optimism/optimism/l2geth/common"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -32,6 +34,12 @@ type Metrics struct { ...@@ -32,6 +34,12 @@ type Metrics struct {
CachedTokensCount *prometheus.CounterVec CachedTokensCount *prometheus.CounterVec
HTTPRequestsCount prometheus.Counter
HTTPResponsesCount *prometheus.CounterVec
HTTPRequestDurationSecs prometheus.Summary
tokenAddrs map[string]string tokenAddrs map[string]string
} }
...@@ -110,6 +118,27 @@ func NewMetrics(monitoredTokens map[string]string) *Metrics { ...@@ -110,6 +118,27 @@ func NewMetrics(monitoredTokens map[string]string) *Metrics {
"chain", "chain",
}), }),
HTTPRequestsCount: promauto.NewCounter(prometheus.CounterOpts{
Name: "http_requests_count",
Help: "How many HTTP requests this instance has seen",
Namespace: metricsNamespace,
}),
HTTPResponsesCount: promauto.NewCounterVec(prometheus.CounterOpts{
Name: "http_responses_count",
Help: "How many HTTP responses this instance has served",
Namespace: metricsNamespace,
}, []string{
"status_code",
}),
HTTPRequestDurationSecs: promauto.NewSummary(prometheus.SummaryOpts{
Name: "http_request_duration_secs",
Help: "How long each HTTP request took",
Namespace: metricsNamespace,
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.95: 0.005, 0.99: 0.001},
}),
tokenAddrs: mts, tokenAddrs: mts,
} }
} }
...@@ -176,6 +205,15 @@ func (m *Metrics) IncL2CachedTokensCount() { ...@@ -176,6 +205,15 @@ func (m *Metrics) IncL2CachedTokensCount() {
m.CachedTokensCount.WithLabelValues("l2").Inc() m.CachedTokensCount.WithLabelValues("l2").Inc()
} }
func (m *Metrics) RecordHTTPRequest() {
m.HTTPRequestsCount.Inc()
}
func (m *Metrics) RecordHTTPResponse(code int, dur time.Duration) {
m.HTTPResponsesCount.WithLabelValues(strconv.Itoa(code)).Inc()
m.HTTPRequestDurationSecs.Observe(float64(dur) / float64(time.Second))
}
func (m *Metrics) Serve(hostname string, port uint64) (*http.Server, error) { func (m *Metrics) Serve(hostname string, port uint64) (*http.Server, error) {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.Handle("/metrics", promhttp.Handler()) mux.Handle("/metrics", promhttp.Handler())
......
...@@ -6,6 +6,8 @@ import ( ...@@ -6,6 +6,8 @@ import (
"runtime/debug" "runtime/debug"
"time" "time"
"github.com/ethereum-optimism/optimism/go/indexer/metrics"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
...@@ -50,7 +52,7 @@ func (rw *responseWriter) WriteHeader(code int) { ...@@ -50,7 +52,7 @@ func (rw *responseWriter) WriteHeader(code int) {
} }
// LoggingMiddleware logs the incoming HTTP request & its duration. // LoggingMiddleware logs the incoming HTTP request & its duration.
func LoggingMiddleware(logger log.Logger) func(http.Handler) http.Handler { func LoggingMiddleware(metrics *metrics.Metrics, logger log.Logger) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) { fn := func(w http.ResponseWriter, r *http.Request) {
defer func() { defer func() {
...@@ -64,16 +66,19 @@ func LoggingMiddleware(logger log.Logger) func(http.Handler) http.Handler { ...@@ -64,16 +66,19 @@ func LoggingMiddleware(logger log.Logger) func(http.Handler) http.Handler {
} }
}() }()
metrics.RecordHTTPRequest()
start := time.Now() start := time.Now()
wrapped := wrapResponseWriter(w) wrapped := wrapResponseWriter(w)
next.ServeHTTP(wrapped, r) next.ServeHTTP(wrapped, r)
dur := time.Since(start)
logger.Info( logger.Info(
"served request", "served request",
"status", wrapped.status, "status", wrapped.status,
"method", r.Method, "method", r.Method,
"path", r.URL.EscapedPath(), "path", r.URL.EscapedPath(),
"duration", time.Since(start), "duration", dur,
) )
metrics.RecordHTTPResponse(wrapped.status, dur)
} }
return http.HandlerFunc(fn) return http.HandlerFunc(fn)
......
package services
import (
"net/http"
"github.com/ethereum-optimism/optimism/go/indexer/db"
"github.com/ethereum-optimism/optimism/go/indexer/metrics"
"github.com/ethereum-optimism/optimism/go/indexer/server"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/gorilla/mux"
)
var airdropLogger = log.New("service", "airdrop")
type Airdrop struct {
db *db.Database
metrics *metrics.Metrics
}
func NewAirdrop(db *db.Database, metrics *metrics.Metrics) *Airdrop {
return &Airdrop{
db: db,
metrics: metrics,
}
}
func (a *Airdrop) GetAirdrop(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
address := vars["address"]
airdrop, err := a.db.GetAirdrop(common.HexToAddress(address))
if err != nil {
airdropLogger.Error("db error getting airdrop", "err", err)
server.RespondWithError(w, http.StatusInternalServerError, "database error")
return
}
if airdrop == nil {
server.RespondWithError(w, http.StatusNotFound, "airdrop not found")
return
}
server.RespondWithJSON(w, http.StatusOK, airdrop)
}
...@@ -24,10 +24,9 @@ func (e *EthBridge) Address() common.Address { ...@@ -24,10 +24,9 @@ func (e *EthBridge) Address() common.Address {
func (e *EthBridge) GetDepositsByBlockRange(start, end uint64) (DepositsMap, error) { func (e *EthBridge) GetDepositsByBlockRange(start, end uint64) (DepositsMap, error) {
depositsByBlockhash := make(DepositsMap) depositsByBlockhash := make(DepositsMap)
iter, err := FilterETHDepositInitiatedWithRetry(e.filterer, &bind.FilterOpts{ iter, err := FilterETHDepositInitiatedWithRetry(e.ctx, e.filterer, &bind.FilterOpts{
Start: start, Start: start,
End: &end, End: &end,
Context: e.ctx,
}) })
if err != nil { if err != nil {
logger.Error("Error fetching filter", "err", err) logger.Error("Error fetching filter", "err", err)
......
...@@ -15,54 +15,48 @@ var clientRetryInterval = 5 * time.Second ...@@ -15,54 +15,48 @@ var clientRetryInterval = 5 * time.Second
// FilterStateBatchAppendedWithRetry retries the given func until it succeeds, // FilterStateBatchAppendedWithRetry retries the given func until it succeeds,
// waiting for clientRetryInterval duration after every call. // waiting for clientRetryInterval duration after every call.
func FilterStateBatchAppendedWithRetry(filterer *scc.StateCommitmentChainFilterer, opts *bind.FilterOpts) (*scc.StateCommitmentChainStateBatchAppendedIterator, error) { func FilterStateBatchAppendedWithRetry(ctx context.Context, filterer *scc.StateCommitmentChainFilterer, opts *bind.FilterOpts) (*scc.StateCommitmentChainStateBatchAppendedIterator, error) {
for { for {
ctxt, cancel := context.WithTimeout(opts.Context, DefaultConnectionTimeout) ctxt, cancel := context.WithTimeout(ctx, DefaultConnectionTimeout)
opts.Context = ctxt opts.Context = ctxt
res, err := filterer.FilterStateBatchAppended(opts, nil) res, err := filterer.FilterStateBatchAppended(opts, nil)
switch err {
case nil:
cancel() cancel()
return res, err if err == nil {
default: return res, nil
logger.Error("Error fetching filter", "err", err)
} }
logger.Error("Error fetching filter", "err", err)
time.Sleep(clientRetryInterval) time.Sleep(clientRetryInterval)
} }
} }
// FilterETHDepositInitiatedWithRetry retries the given func until it succeeds, // FilterETHDepositInitiatedWithRetry retries the given func until it succeeds,
// waiting for clientRetryInterval duration after every call. // waiting for clientRetryInterval duration after every call.
func FilterETHDepositInitiatedWithRetry(filterer *l1bridge.L1StandardBridgeFilterer, opts *bind.FilterOpts) (*l1bridge.L1StandardBridgeETHDepositInitiatedIterator, error) { func FilterETHDepositInitiatedWithRetry(ctx context.Context, filterer *l1bridge.L1StandardBridgeFilterer, opts *bind.FilterOpts) (*l1bridge.L1StandardBridgeETHDepositInitiatedIterator, error) {
for { for {
ctxt, cancel := context.WithTimeout(opts.Context, DefaultConnectionTimeout) ctxt, cancel := context.WithTimeout(ctx, DefaultConnectionTimeout)
opts.Context = ctxt opts.Context = ctxt
res, err := filterer.FilterETHDepositInitiated(opts, nil, nil) res, err := filterer.FilterETHDepositInitiated(opts, nil, nil)
switch err {
case nil:
cancel() cancel()
return res, err if err == nil {
default: return res, nil
logger.Error("Error fetching filter", "err", err)
} }
logger.Error("Error fetching filter", "err", err)
time.Sleep(clientRetryInterval) time.Sleep(clientRetryInterval)
} }
} }
// FilterERC20DepositInitiatedWithRetry retries the given func until it succeeds, // FilterERC20DepositInitiatedWithRetry retries the given func until it succeeds,
// waiting for clientRetryInterval duration after every call. // waiting for clientRetryInterval duration after every call.
func FilterERC20DepositInitiatedWithRetry(filterer *l1bridge.L1StandardBridgeFilterer, opts *bind.FilterOpts) (*l1bridge.L1StandardBridgeERC20DepositInitiatedIterator, error) { func FilterERC20DepositInitiatedWithRetry(ctx context.Context, filterer *l1bridge.L1StandardBridgeFilterer, opts *bind.FilterOpts) (*l1bridge.L1StandardBridgeERC20DepositInitiatedIterator, error) {
for { for {
ctxt, cancel := context.WithTimeout(opts.Context, DefaultConnectionTimeout) ctxt, cancel := context.WithTimeout(ctx, DefaultConnectionTimeout)
opts.Context = ctxt opts.Context = ctxt
res, err := filterer.FilterERC20DepositInitiated(opts, nil, nil, nil) res, err := filterer.FilterERC20DepositInitiated(opts, nil, nil, nil)
switch err {
case nil:
cancel() cancel()
return res, err if err == nil {
default: return res, nil
logger.Error("Error fetching filter", "err", err)
} }
logger.Error("Error fetching filter", "err", err)
time.Sleep(clientRetryInterval) time.Sleep(clientRetryInterval)
} }
} }
...@@ -24,10 +24,9 @@ func (s *StandardBridge) Address() common.Address { ...@@ -24,10 +24,9 @@ func (s *StandardBridge) Address() common.Address {
func (s *StandardBridge) GetDepositsByBlockRange(start, end uint64) (DepositsMap, error) { func (s *StandardBridge) GetDepositsByBlockRange(start, end uint64) (DepositsMap, error) {
depositsByBlockhash := make(DepositsMap) depositsByBlockhash := make(DepositsMap)
iter, err := FilterERC20DepositInitiatedWithRetry(s.filterer, &bind.FilterOpts{ iter, err := FilterERC20DepositInitiatedWithRetry(s.ctx, s.filterer, &bind.FilterOpts{
Start: start, Start: start,
End: &end, End: &end,
Context: s.ctx,
}) })
if err != nil { if err != nil {
logger.Error("Error fetching filter", "err", err) logger.Error("Error fetching filter", "err", err)
......
...@@ -44,10 +44,9 @@ func QueryERC20(address common.Address, client *ethclient.Client) (*db.Token, er ...@@ -44,10 +44,9 @@ func QueryERC20(address common.Address, client *ethclient.Client) (*db.Token, er
func QueryStateBatches(filterer *scc.StateCommitmentChainFilterer, startHeight, endHeight uint64, ctx context.Context) (map[common.Hash][]db.StateBatch, error) { func QueryStateBatches(filterer *scc.StateCommitmentChainFilterer, startHeight, endHeight uint64, ctx context.Context) (map[common.Hash][]db.StateBatch, error) {
batches := make(map[common.Hash][]db.StateBatch) batches := make(map[common.Hash][]db.StateBatch)
iter, err := bridge.FilterStateBatchAppendedWithRetry(filterer, &bind.FilterOpts{ iter, err := bridge.FilterStateBatchAppendedWithRetry(ctx, filterer, &bind.FilterOpts{
Start: startHeight, Start: startHeight,
End: &endHeight, End: &endHeight,
Context: ctx,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
......
...@@ -14,18 +14,16 @@ var clientRetryInterval = 5 * time.Second ...@@ -14,18 +14,16 @@ var clientRetryInterval = 5 * time.Second
// FilterWithdrawalInitiatedWithRetry retries the given func until it succeeds, // FilterWithdrawalInitiatedWithRetry retries the given func until it succeeds,
// waiting for clientRetryInterval duration after every call. // waiting for clientRetryInterval duration after every call.
func FilterWithdrawalInitiatedWithRetry(filterer *l2bridge.L2StandardBridgeFilterer, opts *bind.FilterOpts) (*l2bridge.L2StandardBridgeWithdrawalInitiatedIterator, error) { func FilterWithdrawalInitiatedWithRetry(ctx context.Context, filterer *l2bridge.L2StandardBridgeFilterer, opts *bind.FilterOpts) (*l2bridge.L2StandardBridgeWithdrawalInitiatedIterator, error) {
for { for {
ctxt, cancel := context.WithTimeout(opts.Context, DefaultConnectionTimeout) ctxt, cancel := context.WithTimeout(ctx, DefaultConnectionTimeout)
opts.Context = ctxt opts.Context = ctxt
res, err := filterer.FilterWithdrawalInitiated(opts, nil, nil, nil) res, err := filterer.FilterWithdrawalInitiated(opts, nil, nil, nil)
switch err {
case nil:
cancel() cancel()
return res, err if err == nil {
default: return res, nil
logger.Error("Error fetching filter", "err", err)
} }
logger.Error("Error fetching filter", "err", err)
time.Sleep(clientRetryInterval) time.Sleep(clientRetryInterval)
} }
} }
...@@ -25,10 +25,9 @@ func (s *StandardBridge) Address() common.Address { ...@@ -25,10 +25,9 @@ func (s *StandardBridge) Address() common.Address {
func (s *StandardBridge) GetWithdrawalsByBlockRange(start, end uint64) (WithdrawalsMap, error) { func (s *StandardBridge) GetWithdrawalsByBlockRange(start, end uint64) (WithdrawalsMap, error) {
withdrawalsByBlockhash := make(map[common.Hash][]db.Withdrawal) withdrawalsByBlockhash := make(map[common.Hash][]db.Withdrawal)
iter, err := FilterWithdrawalInitiatedWithRetry(s.filterer, &bind.FilterOpts{ iter, err := FilterWithdrawalInitiatedWithRetry(s.ctx, s.filterer, &bind.FilterOpts{
Start: start, Start: start,
End: &end, End: &end,
Context: s.ctx,
}) })
if err != nil { if err != nil {
logger.Error("Error fetching filter", "err", err) logger.Error("Error fetching filter", "err", err)
......
...@@ -25,10 +25,10 @@ lint: ...@@ -25,10 +25,10 @@ lint:
golangci-lint run ./... golangci-lint run ./...
binding: binding:
$(eval temp := $(shell mktemp)) $(eval tempCTC := $(shell mktemp))
cat ../../packages/contracts/deployments/mainnet/CanonicalTransactionChain.json \ cat ../../packages/contracts/deployments/mainnet/CanonicalTransactionChain.json \
| jq -r .bytecode > $(temp) | jq -r .bytecode > $(tempCTC)
cat ../../packages/contracts/deployments/mainnet/CanonicalTransactionChain.json \ cat ../../packages/contracts/deployments/mainnet/CanonicalTransactionChain.json \
| jq .abi \ | jq .abi \
...@@ -36,6 +36,21 @@ binding: ...@@ -36,6 +36,21 @@ binding:
--abi - \ --abi - \
--out bindings/CanonicalTransactionChain.go \ --out bindings/CanonicalTransactionChain.go \
--type CanonicalTransactionChain \ --type CanonicalTransactionChain \
--bin $(temp) --bin $(tempCTC)
rm $(temp) rm $(tempCTC)
$(eval tempSCC := $(shell mktemp))
cat ../../packages/contracts/deployments/mainnet/StateCommitmentChain.json \
| jq -r .bytecode > $(tempSCC)
cat ../../packages/contracts/deployments/mainnet/StateCommitmentChain.json \
| jq .abi \
| abigen --pkg bindings \
--abi - \
--out bindings/StateCommitmentChain.go \
--type StateCommitmentChain \
--bin $(tempSCC)
rm $(tempSCC)
\ No newline at end of file
// Code generated - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
package bindings
import (
"errors"
"math/big"
"strings"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
)
// Reference imports to suppress errors if they are not otherwise used.
var (
_ = errors.New
_ = big.NewInt
_ = strings.NewReader
_ = ethereum.NotFound
_ = bind.Bind
_ = common.Big1
_ = types.BloomLookup
_ = event.NewSubscription
)
// Lib_OVMCodecChainBatchHeader is an auto generated low-level Go binding around an user-defined struct.
type Lib_OVMCodecChainBatchHeader struct {
BatchIndex *big.Int
BatchRoot [32]byte
BatchSize *big.Int
PrevTotalElements *big.Int
ExtraData []byte
}
// Lib_OVMCodecChainInclusionProof is an auto generated low-level Go binding around an user-defined struct.
type Lib_OVMCodecChainInclusionProof struct {
Index *big.Int
Siblings [][32]byte
}
// StateCommitmentChainMetaData contains all meta data concerning the StateCommitmentChain contract.
var StateCommitmentChainMetaData = &bind.MetaData{
ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_libAddressManager\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_fraudProofWindow\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_sequencerPublishWindow\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_batchIndex\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"_batchRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_batchSize\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_prevTotalElements\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"_extraData\",\"type\":\"bytes\"}],\"name\":\"StateBatchAppended\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_batchIndex\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"_batchRoot\",\"type\":\"bytes32\"}],\"name\":\"StateBatchDeleted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"FRAUD_PROOF_WINDOW\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SEQUENCER_PUBLISH_WINDOW\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"_batch\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"_shouldStartAtElement\",\"type\":\"uint256\"}],\"name\":\"appendStateBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batches\",\"outputs\":[{\"internalType\":\"contractIChainStorageContainer\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"batchRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"batchSize\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"prevTotalElements\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"internalType\":\"structLib_OVMCodec.ChainBatchHeader\",\"name\":\"_batchHeader\",\"type\":\"tuple\"}],\"name\":\"deleteStateBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLastSequencerTimestamp\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"_lastSequencerTimestamp\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getTotalBatches\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"_totalBatches\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getTotalElements\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"_totalElements\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"batchRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"batchSize\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"prevTotalElements\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"internalType\":\"structLib_OVMCodec.ChainBatchHeader\",\"name\":\"_batchHeader\",\"type\":\"tuple\"}],\"name\":\"insideFraudProofWindow\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"_inside\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"libAddressManager\",\"outputs\":[{\"internalType\":\"contractLib_AddressManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_name\",\"type\":\"string\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_element\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"batchIndex\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"batchRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"batchSize\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"prevTotalElements\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"internalType\":\"structLib_OVMCodec.ChainBatchHeader\",\"name\":\"_batchHeader\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"bytes32[]\",\"name\":\"siblings\",\"type\":\"bytes32[]\"}],\"internalType\":\"structLib_OVMCodec.ChainInclusionProof\",\"name\":\"_proof\",\"type\":\"tuple\"}],\"name\":\"verifyStateCommitment\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]",
Bin: "0x608060405234801561001057600080fd5b506040516120bb3803806120bb83398101604081905261002f9161005b565b600080546001600160a01b0319166001600160a01b03949094169390931790925560015560025561009e565b60008060006060848603121561007057600080fd5b83516001600160a01b038116811461008757600080fd5b602085015160409095015190969495509392505050565b61200e806100ad6000396000f3fe608060405234801561001057600080fd5b50600436106100d45760003560e01c80638ca5cbb911610081578063c17b291b1161005b578063c17b291b146101bb578063cfdf677e146101c4578063e561dddc146101cc57600080fd5b80638ca5cbb9146101805780639418bddd14610195578063b8e189ac146101a857600080fd5b80637aa63a86116100b25780637aa63a86146101595780637ad168a01461016f57806381eb62ef1461017757600080fd5b8063299ca478146100d9578063461a4478146101235780634d69ee5714610136575b600080fd5b6000546100f99073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6100f9610131366004611a1b565b6101d4565b610149610144366004611b8d565b610281565b604051901515815260200161011a565b610161610350565b60405190815260200161011a565b610161610369565b61016160025481565b61019361018e366004611c4a565b610382565b005b6101496101a3366004611c8f565b61075c565b6101936101b6366004611c8f565b610804565b61016160015481565b6100f96109c0565b6101616109e8565b600080546040517fbf40fac100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063bf40fac19061022b908590600401611d2f565b60206040518083038186803b15801561024357600080fd5b505afa158015610257573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061027b9190611d64565b92915050565b600061028c83610a6f565b6102dd5760405162461bcd60e51b815260206004820152601560248201527f496e76616c6964206261746368206865616465722e000000000000000000000060448201526064015b60405180910390fd5b6102fa836020015185846000015185602001518760400151610b31565b6103465760405162461bcd60e51b815260206004820152601860248201527f496e76616c696420696e636c7573696f6e2070726f6f662e000000000000000060448201526064016102d4565b5060019392505050565b60008061035b610d9f565b5064ffffffffff1692915050565b600080610374610d9f565b64ffffffffff169392505050565b61038a610350565b81146103fe5760405162461bcd60e51b815260206004820152603d60248201527f41637475616c20626174636820737461727420696e64657820646f6573206e6f60448201527f74206d6174636820657870656374656420737461727420696e6465782e00000060648201526084016102d4565b61043c6040518060400160405280600b81526020017f426f6e644d616e616765720000000000000000000000000000000000000000008152506101d4565b6040517f02ad4d2a00000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff91909116906302ad4d2a9060240160206040518083038186803b1580156104a357600080fd5b505afa1580156104b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104db9190611d81565b61054d5760405162461bcd60e51b815260206004820152602f60248201527f50726f706f73657220646f6573206e6f74206861766520656e6f75676820636f60448201527f6c6c61746572616c20706f73746564000000000000000000000000000000000060648201526084016102d4565b60008251116105c45760405162461bcd60e51b815260206004820152602360248201527f43616e6e6f74207375626d697420616e20656d7074792073746174652062617460448201527f63682e000000000000000000000000000000000000000000000000000000000060648201526084016102d4565b6106026040518060400160405280601981526020017f43616e6f6e6963616c5472616e73616374696f6e436861696e000000000000008152506101d4565b73ffffffffffffffffffffffffffffffffffffffff16637aa63a866040518163ffffffff1660e01b815260040160206040518083038186803b15801561064757600080fd5b505afa15801561065b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061067f9190611da3565b8251610689610350565b6106939190611deb565b111561072d5760405162461bcd60e51b815260206004820152604960248201527f4e756d626572206f6620737461746520726f6f74732063616e6e6f742065786360448201527f65656420746865206e756d626572206f662063616e6f6e6963616c207472616e60648201527f73616374696f6e732e0000000000000000000000000000000000000000000000608482015260a4016102d4565b6040805142602082015233818301528151808203830181526060909101909152610758908390610e43565b5050565b60008082608001518060200190518101906107779190611e03565b509050806107ed5760405162461bcd60e51b815260206004820152602560248201527f4261746368206865616465722074696d657374616d702063616e6e6f7420626560448201527f207a65726f00000000000000000000000000000000000000000000000000000060648201526084016102d4565b42600154826107fc9190611deb565b119392505050565b6108426040518060400160405280601181526020017f4f564d5f467261756456657269666965720000000000000000000000000000008152506101d4565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146108e25760405162461bcd60e51b815260206004820152603b60248201527f537461746520626174636865732063616e206f6e6c792062652064656c65746560448201527f6420627920746865204f564d5f467261756456657269666965722e000000000060648201526084016102d4565b6108eb81610a6f565b6109375760405162461bcd60e51b815260206004820152601560248201527f496e76616c6964206261746368206865616465722e000000000000000000000060448201526064016102d4565b6109408161075c565b6109b4576040805162461bcd60e51b81526020600482015260248101919091527f537461746520626174636865732063616e206f6e6c792062652064656c65746560448201527f642077697468696e207468652066726175642070726f6f662077696e646f772e60648201526084016102d4565b6109bd816110e6565b50565b60006109e3604051806060016040528060218152602001611fb8602191396101d4565b905090565b60006109f26109c0565b73ffffffffffffffffffffffffffffffffffffffff16631f7b6d326040518163ffffffff1660e01b815260040160206040518083038186803b158015610a3757600080fd5b505afa158015610a4b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109e39190611da3565b6000610a796109c0565b82516040517f9507d39a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9290921691639507d39a91610ad19160040190815260200190565b60206040518083038186803b158015610ae957600080fd5b505afa158015610afd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b219190611da3565b610b2a83611317565b1492915050565b6000808211610ba85760405162461bcd60e51b815260206004820152603760248201527f4c69625f4d65726b6c65547265653a20546f74616c206c6561766573206d757360448201527f742062652067726561746572207468616e207a65726f2e00000000000000000060648201526084016102d4565b818410610c1c5760405162461bcd60e51b8152602060048201526024808201527f4c69625f4d65726b6c65547265653a20496e646578206f7574206f6620626f7560448201527f6e64732e0000000000000000000000000000000000000000000000000000000060648201526084016102d4565b610c258261135d565b835114610cc05760405162461bcd60e51b815260206004820152604d60248201527f4c69625f4d65726b6c65547265653a20546f74616c207369626c696e6773206460448201527f6f6573206e6f7420636f72726563746c7920636f72726573706f6e6420746f2060648201527f746f74616c206c65617665732e00000000000000000000000000000000000000608482015260a4016102d4565b8460005b8451811015610d92578560011660011415610d2b57848181518110610ceb57610ceb611e33565b602002602001015182604051602001610d0e929190918252602082015260400190565b604051602081830303815290604052805190602001209150610d79565b81858281518110610d3e57610d3e611e33565b6020026020010151604051602001610d60929190918252602082015260400190565b6040516020818303038152906040528051906020012091505b60019590951c9480610d8a81611e62565b915050610cc4565b5090951495945050505050565b6000806000610dac6109c0565b73ffffffffffffffffffffffffffffffffffffffff1663ccf8f9696040518163ffffffff1660e01b815260040160206040518083038186803b158015610df157600080fd5b505afa158015610e05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e299190611e9b565b64ffffffffff602882901c169460509190911c9350915050565b6000610e836040518060400160405280600c81526020017f4f564d5f50726f706f73657200000000000000000000000000000000000000008152506101d4565b9050600080610e90610d9f565b90925090503373ffffffffffffffffffffffffffffffffffffffff84161415610eba575042610f69565b426002548264ffffffffff16610ed09190611deb565b10610f695760405162461bcd60e51b815260206004820152604360248201527f43616e6e6f74207075626c69736820737461746520726f6f747320776974686960448201527f6e207468652073657175656e636572207075626c69636174696f6e2077696e6460648201527f6f772e0000000000000000000000000000000000000000000000000000000000608482015260a4016102d4565b60006040518060a00160405280610f7e6109e8565b8152602001610f8c88611443565b8152602001875181526020018464ffffffffff16815260200186815250905080600001517f16be4c5129a4e03cf3350262e181dc02ddfb4a6008d925368c0899fcd97ca9c58260200151836040015184606001518560800151604051610ff59493929190611edd565b60405180910390a26110056109c0565b73ffffffffffffffffffffffffffffffffffffffff16632015276c61102983611317565b61104e846040015185606001516110409190611deb565b602887811b91909117901b90565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815260048101929092527fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000166024820152604401600060405180830381600087803b1580156110c657600080fd5b505af11580156110da573d6000803e3d6000fd5b50505050505050505050565b6110ee6109c0565b73ffffffffffffffffffffffffffffffffffffffff16631f7b6d326040518163ffffffff1660e01b815260040160206040518083038186803b15801561113357600080fd5b505afa158015611147573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061116b9190611da3565b8151106111ba5760405162461bcd60e51b815260206004820152601460248201527f496e76616c696420626174636820696e6465782e00000000000000000000000060448201526064016102d4565b6111c381610a6f565b61120f5760405162461bcd60e51b815260206004820152601560248201527f496e76616c6964206261746368206865616465722e000000000000000000000060448201526064016102d4565b6112176109c0565b8151606083015173ffffffffffffffffffffffffffffffffffffffff929092169163167fd681919060281b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815260048101929092527fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000166024820152604401600060405180830381600087803b1580156112ba57600080fd5b505af11580156112ce573d6000803e3d6000fd5b5050505080600001517f8747b69ce8fdb31c3b9b0a67bd8049ad8c1a69ea417b69b12174068abd9cbd64826020015160405161130c91815260200190565b60405180910390a250565b600081602001518260400151836060015184608001516040516020016113409493929190611edd565b604051602081830303815290604052805190602001209050919050565b60008082116113d45760405162461bcd60e51b815260206004820152603060248201527f4c69625f4d65726b6c65547265653a2043616e6e6f7420636f6d70757465206360448201527f65696c286c6f675f3229206f6620302e0000000000000000000000000000000060648201526084016102d4565b81600114156113e557506000919050565b81600060805b600181106114235780611401600180831b611f0c565b901b83161561141b576114148183611deb565b92811c9291505b60011c6113eb565b506001811b841461143c57611439600182611deb565b90505b9392505050565b6000808251116114bb5760405162461bcd60e51b815260206004820152603460248201527f4c69625f4d65726b6c65547265653a204d7573742070726f766964652061742060448201527f6c65617374206f6e65206c65616620686173682e00000000000000000000000060648201526084016102d4565b8151600114156114e757816000815181106114d8576114d8611e33565b60200260200101519050919050565b60408051610200810182527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56381527f633dc4d7da7256660a892f8f1604a44b5432649cc8ec5cb3ced4c4e6ac94dd1d60208201527f890740a8eb06ce9be422cb8da5cdafc2b58c0a5e24036c578de2a433c828ff7d818301527f3b8ec09e026fdc305365dfc94e189a81b38c7597b3d941c279f042e8206e0bd86060808301919091527fecd50eee38e386bd62be9bedb990706951b65fe053bd9d8a521af753d139e2da60808301527fdefff6d330bb5403f63b14f33b578274160de3a50df4efecf0e0db73bcdd3da560a08301527f617bdd11f7c0a11f49db22f629387a12da7596f9d1704d7465177c63d88ec7d760c08301527f292c23a9aa1d8bea7e2435e555a4a60e379a5a35f3f452bae60121073fb6eead60e08301527fe1cea92ed99acdcb045a6726b2f87107e8a61620a232cf4d7d5b5766b3952e106101008301527f7ad66c0a68c72cb89e4fb4303841966e4062a76ab97451e3b9fb526a5ceb7f826101208301527fe026cc5a4aed3c22a58cbd3d2ac754c9352c5436f638042dca99034e836365166101408301527f3d04cffd8b46a874edf5cfae63077de85f849a660426697b06a829c70dd1409c6101608301527fad676aa337a485e4728a0b240d92b3ef7b3c372d06d189322bfd5f61f1e7203e6101808301527fa2fca4a49658f9fab7aa63289c91b7c7b6c832a6d0e69334ff5b0a3483d09dab6101a08301527f4ebfd9cd7bca2505f7bef59cc1c12ecc708fff26ae4af19abe852afe9e20c8626101c08301527f2def10d13dd169f550f578bda343d9717a138562e0093b380a1120789d53cf106101e083015282518381529081018352909160009190602082018180368337505085519192506000918291508180805b60018411156118fd57611798600285611f52565b91506117a5600285611f66565b600114905060005b82811015611851578a6117c1826002611f7a565b815181106117d1576117d1611e33565b602002602001015196508a8160026117e99190611f7a565b6117f4906001611deb565b8151811061180457611804611e33565b6020026020010151955086602089015285604089015287805190602001208b828151811061183457611834611e33565b60209081029190910101528061184981611e62565b9150506117ad565b5080156118cd5789611864600186611f0c565b8151811061187457611874611e33565b6020026020010151955087836010811061189057611890611e33565b602002015160001b945085602088015284604088015286805190602001208a83815181106118c0576118c0611e33565b6020026020010181815250505b806118d95760006118dc565b60015b6118e99060ff1683611deb565b9350826118f581611e62565b935050611784565b8960008151811061191057611910611e33565b602002602001015198505050505050505050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561199d5761199d611927565b604052919050565b600067ffffffffffffffff8311156119bf576119bf611927565b6119f060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f86011601611956565b9050828152838383011115611a0457600080fd5b828260208301376000602084830101529392505050565b600060208284031215611a2d57600080fd5b813567ffffffffffffffff811115611a4457600080fd5b8201601f81018413611a5557600080fd5b611a64848235602084016119a5565b949350505050565b600060a08284031215611a7e57600080fd5b60405160a0810167ffffffffffffffff8282108183111715611aa257611aa2611927565b81604052829350843583526020850135602084015260408501356040840152606085013560608401526080850135915080821115611adf57600080fd5b508301601f81018513611af157600080fd5b611b00858235602084016119a5565b6080830152505092915050565b600082601f830112611b1e57600080fd5b8135602067ffffffffffffffff821115611b3a57611b3a611927565b8160051b611b49828201611956565b9283528481018201928281019087851115611b6357600080fd5b83870192505b84831015611b8257823582529183019190830190611b69565b979650505050505050565b600080600060608486031215611ba257600080fd5b83359250602084013567ffffffffffffffff80821115611bc157600080fd5b611bcd87838801611a6c565b93506040860135915080821115611be357600080fd5b9085019060408288031215611bf757600080fd5b604051604081018181108382111715611c1257611c12611927565b60405282358152602083013582811115611c2b57600080fd5b611c3789828601611b0d565b6020830152508093505050509250925092565b60008060408385031215611c5d57600080fd5b823567ffffffffffffffff811115611c7457600080fd5b611c8085828601611b0d565b95602094909401359450505050565b600060208284031215611ca157600080fd5b813567ffffffffffffffff811115611cb857600080fd5b611a6484828501611a6c565b6000815180845260005b81811015611cea57602081850181015186830182015201611cce565b81811115611cfc576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061143c6020830184611cc4565b73ffffffffffffffffffffffffffffffffffffffff811681146109bd57600080fd5b600060208284031215611d7657600080fd5b815161143c81611d42565b600060208284031215611d9357600080fd5b8151801515811461143c57600080fd5b600060208284031215611db557600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115611dfe57611dfe611dbc565b500190565b60008060408385031215611e1657600080fd5b825191506020830151611e2881611d42565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415611e9457611e94611dbc565b5060010190565b600060208284031215611ead57600080fd5b81517fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000008116811461143c57600080fd5b848152836020820152826040820152608060608201526000611f026080830184611cc4565b9695505050505050565b600082821015611f1e57611f1e611dbc565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082611f6157611f61611f23565b500490565b600082611f7557611f75611f23565b500690565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615611fb257611fb2611dbc565b50029056fe436861696e53746f72616765436f6e7461696e65722d5343432d62617463686573a2646970667358221220b90af2e50ed0ae8720c8f74ce116bfe826a8d329baa23dec8c3f69958740528964736f6c63430008090033",
}
// StateCommitmentChainABI is the input ABI used to generate the binding from.
// Deprecated: Use StateCommitmentChainMetaData.ABI instead.
var StateCommitmentChainABI = StateCommitmentChainMetaData.ABI
// StateCommitmentChainBin is the compiled bytecode used for deploying new contracts.
// Deprecated: Use StateCommitmentChainMetaData.Bin instead.
var StateCommitmentChainBin = StateCommitmentChainMetaData.Bin
// DeployStateCommitmentChain deploys a new Ethereum contract, binding an instance of StateCommitmentChain to it.
func DeployStateCommitmentChain(auth *bind.TransactOpts, backend bind.ContractBackend, _libAddressManager common.Address, _fraudProofWindow *big.Int, _sequencerPublishWindow *big.Int) (common.Address, *types.Transaction, *StateCommitmentChain, error) {
parsed, err := StateCommitmentChainMetaData.GetAbi()
if err != nil {
return common.Address{}, nil, nil, err
}
if parsed == nil {
return common.Address{}, nil, nil, errors.New("GetABI returned nil")
}
address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(StateCommitmentChainBin), backend, _libAddressManager, _fraudProofWindow, _sequencerPublishWindow)
if err != nil {
return common.Address{}, nil, nil, err
}
return address, tx, &StateCommitmentChain{StateCommitmentChainCaller: StateCommitmentChainCaller{contract: contract}, StateCommitmentChainTransactor: StateCommitmentChainTransactor{contract: contract}, StateCommitmentChainFilterer: StateCommitmentChainFilterer{contract: contract}}, nil
}
// StateCommitmentChain is an auto generated Go binding around an Ethereum contract.
type StateCommitmentChain struct {
StateCommitmentChainCaller // Read-only binding to the contract
StateCommitmentChainTransactor // Write-only binding to the contract
StateCommitmentChainFilterer // Log filterer for contract events
}
// StateCommitmentChainCaller is an auto generated read-only Go binding around an Ethereum contract.
type StateCommitmentChainCaller struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// StateCommitmentChainTransactor is an auto generated write-only Go binding around an Ethereum contract.
type StateCommitmentChainTransactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// StateCommitmentChainFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
type StateCommitmentChainFilterer struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// StateCommitmentChainSession is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options.
type StateCommitmentChainSession struct {
Contract *StateCommitmentChain // Generic contract binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// StateCommitmentChainCallerSession is an auto generated read-only Go binding around an Ethereum contract,
// with pre-set call options.
type StateCommitmentChainCallerSession struct {
Contract *StateCommitmentChainCaller // Generic contract caller binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
}
// StateCommitmentChainTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
// with pre-set transact options.
type StateCommitmentChainTransactorSession struct {
Contract *StateCommitmentChainTransactor // Generic contract transactor binding to set the session for
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// StateCommitmentChainRaw is an auto generated low-level Go binding around an Ethereum contract.
type StateCommitmentChainRaw struct {
Contract *StateCommitmentChain // Generic contract binding to access the raw methods on
}
// StateCommitmentChainCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
type StateCommitmentChainCallerRaw struct {
Contract *StateCommitmentChainCaller // Generic read-only contract binding to access the raw methods on
}
// StateCommitmentChainTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
type StateCommitmentChainTransactorRaw struct {
Contract *StateCommitmentChainTransactor // Generic write-only contract binding to access the raw methods on
}
// NewStateCommitmentChain creates a new instance of StateCommitmentChain, bound to a specific deployed contract.
func NewStateCommitmentChain(address common.Address, backend bind.ContractBackend) (*StateCommitmentChain, error) {
contract, err := bindStateCommitmentChain(address, backend, backend, backend)
if err != nil {
return nil, err
}
return &StateCommitmentChain{StateCommitmentChainCaller: StateCommitmentChainCaller{contract: contract}, StateCommitmentChainTransactor: StateCommitmentChainTransactor{contract: contract}, StateCommitmentChainFilterer: StateCommitmentChainFilterer{contract: contract}}, nil
}
// NewStateCommitmentChainCaller creates a new read-only instance of StateCommitmentChain, bound to a specific deployed contract.
func NewStateCommitmentChainCaller(address common.Address, caller bind.ContractCaller) (*StateCommitmentChainCaller, error) {
contract, err := bindStateCommitmentChain(address, caller, nil, nil)
if err != nil {
return nil, err
}
return &StateCommitmentChainCaller{contract: contract}, nil
}
// NewStateCommitmentChainTransactor creates a new write-only instance of StateCommitmentChain, bound to a specific deployed contract.
func NewStateCommitmentChainTransactor(address common.Address, transactor bind.ContractTransactor) (*StateCommitmentChainTransactor, error) {
contract, err := bindStateCommitmentChain(address, nil, transactor, nil)
if err != nil {
return nil, err
}
return &StateCommitmentChainTransactor{contract: contract}, nil
}
// NewStateCommitmentChainFilterer creates a new log filterer instance of StateCommitmentChain, bound to a specific deployed contract.
func NewStateCommitmentChainFilterer(address common.Address, filterer bind.ContractFilterer) (*StateCommitmentChainFilterer, error) {
contract, err := bindStateCommitmentChain(address, nil, nil, filterer)
if err != nil {
return nil, err
}
return &StateCommitmentChainFilterer{contract: contract}, nil
}
// bindStateCommitmentChain binds a generic wrapper to an already deployed contract.
func bindStateCommitmentChain(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := abi.JSON(strings.NewReader(StateCommitmentChainABI))
if err != nil {
return nil, err
}
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_StateCommitmentChain *StateCommitmentChainRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _StateCommitmentChain.Contract.StateCommitmentChainCaller.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_StateCommitmentChain *StateCommitmentChainRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _StateCommitmentChain.Contract.StateCommitmentChainTransactor.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_StateCommitmentChain *StateCommitmentChainRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _StateCommitmentChain.Contract.StateCommitmentChainTransactor.contract.Transact(opts, method, params...)
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_StateCommitmentChain *StateCommitmentChainCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _StateCommitmentChain.Contract.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_StateCommitmentChain *StateCommitmentChainTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _StateCommitmentChain.Contract.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_StateCommitmentChain *StateCommitmentChainTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _StateCommitmentChain.Contract.contract.Transact(opts, method, params...)
}
// FRAUDPROOFWINDOW is a free data retrieval call binding the contract method 0xc17b291b.
//
// Solidity: function FRAUD_PROOF_WINDOW() view returns(uint256)
func (_StateCommitmentChain *StateCommitmentChainCaller) FRAUDPROOFWINDOW(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _StateCommitmentChain.contract.Call(opts, &out, "FRAUD_PROOF_WINDOW")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// FRAUDPROOFWINDOW is a free data retrieval call binding the contract method 0xc17b291b.
//
// Solidity: function FRAUD_PROOF_WINDOW() view returns(uint256)
func (_StateCommitmentChain *StateCommitmentChainSession) FRAUDPROOFWINDOW() (*big.Int, error) {
return _StateCommitmentChain.Contract.FRAUDPROOFWINDOW(&_StateCommitmentChain.CallOpts)
}
// FRAUDPROOFWINDOW is a free data retrieval call binding the contract method 0xc17b291b.
//
// Solidity: function FRAUD_PROOF_WINDOW() view returns(uint256)
func (_StateCommitmentChain *StateCommitmentChainCallerSession) FRAUDPROOFWINDOW() (*big.Int, error) {
return _StateCommitmentChain.Contract.FRAUDPROOFWINDOW(&_StateCommitmentChain.CallOpts)
}
// SEQUENCERPUBLISHWINDOW is a free data retrieval call binding the contract method 0x81eb62ef.
//
// Solidity: function SEQUENCER_PUBLISH_WINDOW() view returns(uint256)
func (_StateCommitmentChain *StateCommitmentChainCaller) SEQUENCERPUBLISHWINDOW(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _StateCommitmentChain.contract.Call(opts, &out, "SEQUENCER_PUBLISH_WINDOW")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// SEQUENCERPUBLISHWINDOW is a free data retrieval call binding the contract method 0x81eb62ef.
//
// Solidity: function SEQUENCER_PUBLISH_WINDOW() view returns(uint256)
func (_StateCommitmentChain *StateCommitmentChainSession) SEQUENCERPUBLISHWINDOW() (*big.Int, error) {
return _StateCommitmentChain.Contract.SEQUENCERPUBLISHWINDOW(&_StateCommitmentChain.CallOpts)
}
// SEQUENCERPUBLISHWINDOW is a free data retrieval call binding the contract method 0x81eb62ef.
//
// Solidity: function SEQUENCER_PUBLISH_WINDOW() view returns(uint256)
func (_StateCommitmentChain *StateCommitmentChainCallerSession) SEQUENCERPUBLISHWINDOW() (*big.Int, error) {
return _StateCommitmentChain.Contract.SEQUENCERPUBLISHWINDOW(&_StateCommitmentChain.CallOpts)
}
// Batches is a free data retrieval call binding the contract method 0xcfdf677e.
//
// Solidity: function batches() view returns(address)
func (_StateCommitmentChain *StateCommitmentChainCaller) Batches(opts *bind.CallOpts) (common.Address, error) {
var out []interface{}
err := _StateCommitmentChain.contract.Call(opts, &out, "batches")
if err != nil {
return *new(common.Address), err
}
out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
return out0, err
}
// Batches is a free data retrieval call binding the contract method 0xcfdf677e.
//
// Solidity: function batches() view returns(address)
func (_StateCommitmentChain *StateCommitmentChainSession) Batches() (common.Address, error) {
return _StateCommitmentChain.Contract.Batches(&_StateCommitmentChain.CallOpts)
}
// Batches is a free data retrieval call binding the contract method 0xcfdf677e.
//
// Solidity: function batches() view returns(address)
func (_StateCommitmentChain *StateCommitmentChainCallerSession) Batches() (common.Address, error) {
return _StateCommitmentChain.Contract.Batches(&_StateCommitmentChain.CallOpts)
}
// GetLastSequencerTimestamp is a free data retrieval call binding the contract method 0x7ad168a0.
//
// Solidity: function getLastSequencerTimestamp() view returns(uint256 _lastSequencerTimestamp)
func (_StateCommitmentChain *StateCommitmentChainCaller) GetLastSequencerTimestamp(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _StateCommitmentChain.contract.Call(opts, &out, "getLastSequencerTimestamp")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// GetLastSequencerTimestamp is a free data retrieval call binding the contract method 0x7ad168a0.
//
// Solidity: function getLastSequencerTimestamp() view returns(uint256 _lastSequencerTimestamp)
func (_StateCommitmentChain *StateCommitmentChainSession) GetLastSequencerTimestamp() (*big.Int, error) {
return _StateCommitmentChain.Contract.GetLastSequencerTimestamp(&_StateCommitmentChain.CallOpts)
}
// GetLastSequencerTimestamp is a free data retrieval call binding the contract method 0x7ad168a0.
//
// Solidity: function getLastSequencerTimestamp() view returns(uint256 _lastSequencerTimestamp)
func (_StateCommitmentChain *StateCommitmentChainCallerSession) GetLastSequencerTimestamp() (*big.Int, error) {
return _StateCommitmentChain.Contract.GetLastSequencerTimestamp(&_StateCommitmentChain.CallOpts)
}
// GetTotalBatches is a free data retrieval call binding the contract method 0xe561dddc.
//
// Solidity: function getTotalBatches() view returns(uint256 _totalBatches)
func (_StateCommitmentChain *StateCommitmentChainCaller) GetTotalBatches(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _StateCommitmentChain.contract.Call(opts, &out, "getTotalBatches")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// GetTotalBatches is a free data retrieval call binding the contract method 0xe561dddc.
//
// Solidity: function getTotalBatches() view returns(uint256 _totalBatches)
func (_StateCommitmentChain *StateCommitmentChainSession) GetTotalBatches() (*big.Int, error) {
return _StateCommitmentChain.Contract.GetTotalBatches(&_StateCommitmentChain.CallOpts)
}
// GetTotalBatches is a free data retrieval call binding the contract method 0xe561dddc.
//
// Solidity: function getTotalBatches() view returns(uint256 _totalBatches)
func (_StateCommitmentChain *StateCommitmentChainCallerSession) GetTotalBatches() (*big.Int, error) {
return _StateCommitmentChain.Contract.GetTotalBatches(&_StateCommitmentChain.CallOpts)
}
// GetTotalElements is a free data retrieval call binding the contract method 0x7aa63a86.
//
// Solidity: function getTotalElements() view returns(uint256 _totalElements)
func (_StateCommitmentChain *StateCommitmentChainCaller) GetTotalElements(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _StateCommitmentChain.contract.Call(opts, &out, "getTotalElements")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// GetTotalElements is a free data retrieval call binding the contract method 0x7aa63a86.
//
// Solidity: function getTotalElements() view returns(uint256 _totalElements)
func (_StateCommitmentChain *StateCommitmentChainSession) GetTotalElements() (*big.Int, error) {
return _StateCommitmentChain.Contract.GetTotalElements(&_StateCommitmentChain.CallOpts)
}
// GetTotalElements is a free data retrieval call binding the contract method 0x7aa63a86.
//
// Solidity: function getTotalElements() view returns(uint256 _totalElements)
func (_StateCommitmentChain *StateCommitmentChainCallerSession) GetTotalElements() (*big.Int, error) {
return _StateCommitmentChain.Contract.GetTotalElements(&_StateCommitmentChain.CallOpts)
}
// InsideFraudProofWindow is a free data retrieval call binding the contract method 0x9418bddd.
//
// Solidity: function insideFraudProofWindow((uint256,bytes32,uint256,uint256,bytes) _batchHeader) view returns(bool _inside)
func (_StateCommitmentChain *StateCommitmentChainCaller) InsideFraudProofWindow(opts *bind.CallOpts, _batchHeader Lib_OVMCodecChainBatchHeader) (bool, error) {
var out []interface{}
err := _StateCommitmentChain.contract.Call(opts, &out, "insideFraudProofWindow", _batchHeader)
if err != nil {
return *new(bool), err
}
out0 := *abi.ConvertType(out[0], new(bool)).(*bool)
return out0, err
}
// InsideFraudProofWindow is a free data retrieval call binding the contract method 0x9418bddd.
//
// Solidity: function insideFraudProofWindow((uint256,bytes32,uint256,uint256,bytes) _batchHeader) view returns(bool _inside)
func (_StateCommitmentChain *StateCommitmentChainSession) InsideFraudProofWindow(_batchHeader Lib_OVMCodecChainBatchHeader) (bool, error) {
return _StateCommitmentChain.Contract.InsideFraudProofWindow(&_StateCommitmentChain.CallOpts, _batchHeader)
}
// InsideFraudProofWindow is a free data retrieval call binding the contract method 0x9418bddd.
//
// Solidity: function insideFraudProofWindow((uint256,bytes32,uint256,uint256,bytes) _batchHeader) view returns(bool _inside)
func (_StateCommitmentChain *StateCommitmentChainCallerSession) InsideFraudProofWindow(_batchHeader Lib_OVMCodecChainBatchHeader) (bool, error) {
return _StateCommitmentChain.Contract.InsideFraudProofWindow(&_StateCommitmentChain.CallOpts, _batchHeader)
}
// LibAddressManager is a free data retrieval call binding the contract method 0x299ca478.
//
// Solidity: function libAddressManager() view returns(address)
func (_StateCommitmentChain *StateCommitmentChainCaller) LibAddressManager(opts *bind.CallOpts) (common.Address, error) {
var out []interface{}
err := _StateCommitmentChain.contract.Call(opts, &out, "libAddressManager")
if err != nil {
return *new(common.Address), err
}
out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
return out0, err
}
// LibAddressManager is a free data retrieval call binding the contract method 0x299ca478.
//
// Solidity: function libAddressManager() view returns(address)
func (_StateCommitmentChain *StateCommitmentChainSession) LibAddressManager() (common.Address, error) {
return _StateCommitmentChain.Contract.LibAddressManager(&_StateCommitmentChain.CallOpts)
}
// LibAddressManager is a free data retrieval call binding the contract method 0x299ca478.
//
// Solidity: function libAddressManager() view returns(address)
func (_StateCommitmentChain *StateCommitmentChainCallerSession) LibAddressManager() (common.Address, error) {
return _StateCommitmentChain.Contract.LibAddressManager(&_StateCommitmentChain.CallOpts)
}
// Resolve is a free data retrieval call binding the contract method 0x461a4478.
//
// Solidity: function resolve(string _name) view returns(address)
func (_StateCommitmentChain *StateCommitmentChainCaller) Resolve(opts *bind.CallOpts, _name string) (common.Address, error) {
var out []interface{}
err := _StateCommitmentChain.contract.Call(opts, &out, "resolve", _name)
if err != nil {
return *new(common.Address), err
}
out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
return out0, err
}
// Resolve is a free data retrieval call binding the contract method 0x461a4478.
//
// Solidity: function resolve(string _name) view returns(address)
func (_StateCommitmentChain *StateCommitmentChainSession) Resolve(_name string) (common.Address, error) {
return _StateCommitmentChain.Contract.Resolve(&_StateCommitmentChain.CallOpts, _name)
}
// Resolve is a free data retrieval call binding the contract method 0x461a4478.
//
// Solidity: function resolve(string _name) view returns(address)
func (_StateCommitmentChain *StateCommitmentChainCallerSession) Resolve(_name string) (common.Address, error) {
return _StateCommitmentChain.Contract.Resolve(&_StateCommitmentChain.CallOpts, _name)
}
// VerifyStateCommitment is a free data retrieval call binding the contract method 0x4d69ee57.
//
// Solidity: function verifyStateCommitment(bytes32 _element, (uint256,bytes32,uint256,uint256,bytes) _batchHeader, (uint256,bytes32[]) _proof) view returns(bool)
func (_StateCommitmentChain *StateCommitmentChainCaller) VerifyStateCommitment(opts *bind.CallOpts, _element [32]byte, _batchHeader Lib_OVMCodecChainBatchHeader, _proof Lib_OVMCodecChainInclusionProof) (bool, error) {
var out []interface{}
err := _StateCommitmentChain.contract.Call(opts, &out, "verifyStateCommitment", _element, _batchHeader, _proof)
if err != nil {
return *new(bool), err
}
out0 := *abi.ConvertType(out[0], new(bool)).(*bool)
return out0, err
}
// VerifyStateCommitment is a free data retrieval call binding the contract method 0x4d69ee57.
//
// Solidity: function verifyStateCommitment(bytes32 _element, (uint256,bytes32,uint256,uint256,bytes) _batchHeader, (uint256,bytes32[]) _proof) view returns(bool)
func (_StateCommitmentChain *StateCommitmentChainSession) VerifyStateCommitment(_element [32]byte, _batchHeader Lib_OVMCodecChainBatchHeader, _proof Lib_OVMCodecChainInclusionProof) (bool, error) {
return _StateCommitmentChain.Contract.VerifyStateCommitment(&_StateCommitmentChain.CallOpts, _element, _batchHeader, _proof)
}
// VerifyStateCommitment is a free data retrieval call binding the contract method 0x4d69ee57.
//
// Solidity: function verifyStateCommitment(bytes32 _element, (uint256,bytes32,uint256,uint256,bytes) _batchHeader, (uint256,bytes32[]) _proof) view returns(bool)
func (_StateCommitmentChain *StateCommitmentChainCallerSession) VerifyStateCommitment(_element [32]byte, _batchHeader Lib_OVMCodecChainBatchHeader, _proof Lib_OVMCodecChainInclusionProof) (bool, error) {
return _StateCommitmentChain.Contract.VerifyStateCommitment(&_StateCommitmentChain.CallOpts, _element, _batchHeader, _proof)
}
// AppendStateBatch is a paid mutator transaction binding the contract method 0x8ca5cbb9.
//
// Solidity: function appendStateBatch(bytes32[] _batch, uint256 _shouldStartAtElement) returns()
func (_StateCommitmentChain *StateCommitmentChainTransactor) AppendStateBatch(opts *bind.TransactOpts, _batch [][32]byte, _shouldStartAtElement *big.Int) (*types.Transaction, error) {
return _StateCommitmentChain.contract.Transact(opts, "appendStateBatch", _batch, _shouldStartAtElement)
}
// AppendStateBatch is a paid mutator transaction binding the contract method 0x8ca5cbb9.
//
// Solidity: function appendStateBatch(bytes32[] _batch, uint256 _shouldStartAtElement) returns()
func (_StateCommitmentChain *StateCommitmentChainSession) AppendStateBatch(_batch [][32]byte, _shouldStartAtElement *big.Int) (*types.Transaction, error) {
return _StateCommitmentChain.Contract.AppendStateBatch(&_StateCommitmentChain.TransactOpts, _batch, _shouldStartAtElement)
}
// AppendStateBatch is a paid mutator transaction binding the contract method 0x8ca5cbb9.
//
// Solidity: function appendStateBatch(bytes32[] _batch, uint256 _shouldStartAtElement) returns()
func (_StateCommitmentChain *StateCommitmentChainTransactorSession) AppendStateBatch(_batch [][32]byte, _shouldStartAtElement *big.Int) (*types.Transaction, error) {
return _StateCommitmentChain.Contract.AppendStateBatch(&_StateCommitmentChain.TransactOpts, _batch, _shouldStartAtElement)
}
// DeleteStateBatch is a paid mutator transaction binding the contract method 0xb8e189ac.
//
// Solidity: function deleteStateBatch((uint256,bytes32,uint256,uint256,bytes) _batchHeader) returns()
func (_StateCommitmentChain *StateCommitmentChainTransactor) DeleteStateBatch(opts *bind.TransactOpts, _batchHeader Lib_OVMCodecChainBatchHeader) (*types.Transaction, error) {
return _StateCommitmentChain.contract.Transact(opts, "deleteStateBatch", _batchHeader)
}
// DeleteStateBatch is a paid mutator transaction binding the contract method 0xb8e189ac.
//
// Solidity: function deleteStateBatch((uint256,bytes32,uint256,uint256,bytes) _batchHeader) returns()
func (_StateCommitmentChain *StateCommitmentChainSession) DeleteStateBatch(_batchHeader Lib_OVMCodecChainBatchHeader) (*types.Transaction, error) {
return _StateCommitmentChain.Contract.DeleteStateBatch(&_StateCommitmentChain.TransactOpts, _batchHeader)
}
// DeleteStateBatch is a paid mutator transaction binding the contract method 0xb8e189ac.
//
// Solidity: function deleteStateBatch((uint256,bytes32,uint256,uint256,bytes) _batchHeader) returns()
func (_StateCommitmentChain *StateCommitmentChainTransactorSession) DeleteStateBatch(_batchHeader Lib_OVMCodecChainBatchHeader) (*types.Transaction, error) {
return _StateCommitmentChain.Contract.DeleteStateBatch(&_StateCommitmentChain.TransactOpts, _batchHeader)
}
// StateCommitmentChainStateBatchAppendedIterator is returned from FilterStateBatchAppended and is used to iterate over the raw logs and unpacked data for StateBatchAppended events raised by the StateCommitmentChain contract.
type StateCommitmentChainStateBatchAppendedIterator struct {
Event *StateCommitmentChainStateBatchAppended // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *StateCommitmentChainStateBatchAppendedIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(StateCommitmentChainStateBatchAppended)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(StateCommitmentChainStateBatchAppended)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *StateCommitmentChainStateBatchAppendedIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *StateCommitmentChainStateBatchAppendedIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// StateCommitmentChainStateBatchAppended represents a StateBatchAppended event raised by the StateCommitmentChain contract.
type StateCommitmentChainStateBatchAppended struct {
BatchIndex *big.Int
BatchRoot [32]byte
BatchSize *big.Int
PrevTotalElements *big.Int
ExtraData []byte
Raw types.Log // Blockchain specific contextual infos
}
// FilterStateBatchAppended is a free log retrieval operation binding the contract event 0x16be4c5129a4e03cf3350262e181dc02ddfb4a6008d925368c0899fcd97ca9c5.
//
// Solidity: event StateBatchAppended(uint256 indexed _batchIndex, bytes32 _batchRoot, uint256 _batchSize, uint256 _prevTotalElements, bytes _extraData)
func (_StateCommitmentChain *StateCommitmentChainFilterer) FilterStateBatchAppended(opts *bind.FilterOpts, _batchIndex []*big.Int) (*StateCommitmentChainStateBatchAppendedIterator, error) {
var _batchIndexRule []interface{}
for _, _batchIndexItem := range _batchIndex {
_batchIndexRule = append(_batchIndexRule, _batchIndexItem)
}
logs, sub, err := _StateCommitmentChain.contract.FilterLogs(opts, "StateBatchAppended", _batchIndexRule)
if err != nil {
return nil, err
}
return &StateCommitmentChainStateBatchAppendedIterator{contract: _StateCommitmentChain.contract, event: "StateBatchAppended", logs: logs, sub: sub}, nil
}
// WatchStateBatchAppended is a free log subscription operation binding the contract event 0x16be4c5129a4e03cf3350262e181dc02ddfb4a6008d925368c0899fcd97ca9c5.
//
// Solidity: event StateBatchAppended(uint256 indexed _batchIndex, bytes32 _batchRoot, uint256 _batchSize, uint256 _prevTotalElements, bytes _extraData)
func (_StateCommitmentChain *StateCommitmentChainFilterer) WatchStateBatchAppended(opts *bind.WatchOpts, sink chan<- *StateCommitmentChainStateBatchAppended, _batchIndex []*big.Int) (event.Subscription, error) {
var _batchIndexRule []interface{}
for _, _batchIndexItem := range _batchIndex {
_batchIndexRule = append(_batchIndexRule, _batchIndexItem)
}
logs, sub, err := _StateCommitmentChain.contract.WatchLogs(opts, "StateBatchAppended", _batchIndexRule)
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(StateCommitmentChainStateBatchAppended)
if err := _StateCommitmentChain.contract.UnpackLog(event, "StateBatchAppended", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ParseStateBatchAppended is a log parse operation binding the contract event 0x16be4c5129a4e03cf3350262e181dc02ddfb4a6008d925368c0899fcd97ca9c5.
//
// Solidity: event StateBatchAppended(uint256 indexed _batchIndex, bytes32 _batchRoot, uint256 _batchSize, uint256 _prevTotalElements, bytes _extraData)
func (_StateCommitmentChain *StateCommitmentChainFilterer) ParseStateBatchAppended(log types.Log) (*StateCommitmentChainStateBatchAppended, error) {
event := new(StateCommitmentChainStateBatchAppended)
if err := _StateCommitmentChain.contract.UnpackLog(event, "StateBatchAppended", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
// StateCommitmentChainStateBatchDeletedIterator is returned from FilterStateBatchDeleted and is used to iterate over the raw logs and unpacked data for StateBatchDeleted events raised by the StateCommitmentChain contract.
type StateCommitmentChainStateBatchDeletedIterator struct {
Event *StateCommitmentChainStateBatchDeleted // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *StateCommitmentChainStateBatchDeletedIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(StateCommitmentChainStateBatchDeleted)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(StateCommitmentChainStateBatchDeleted)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *StateCommitmentChainStateBatchDeletedIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *StateCommitmentChainStateBatchDeletedIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// StateCommitmentChainStateBatchDeleted represents a StateBatchDeleted event raised by the StateCommitmentChain contract.
type StateCommitmentChainStateBatchDeleted struct {
BatchIndex *big.Int
BatchRoot [32]byte
Raw types.Log // Blockchain specific contextual infos
}
// FilterStateBatchDeleted is a free log retrieval operation binding the contract event 0x8747b69ce8fdb31c3b9b0a67bd8049ad8c1a69ea417b69b12174068abd9cbd64.
//
// Solidity: event StateBatchDeleted(uint256 indexed _batchIndex, bytes32 _batchRoot)
func (_StateCommitmentChain *StateCommitmentChainFilterer) FilterStateBatchDeleted(opts *bind.FilterOpts, _batchIndex []*big.Int) (*StateCommitmentChainStateBatchDeletedIterator, error) {
var _batchIndexRule []interface{}
for _, _batchIndexItem := range _batchIndex {
_batchIndexRule = append(_batchIndexRule, _batchIndexItem)
}
logs, sub, err := _StateCommitmentChain.contract.FilterLogs(opts, "StateBatchDeleted", _batchIndexRule)
if err != nil {
return nil, err
}
return &StateCommitmentChainStateBatchDeletedIterator{contract: _StateCommitmentChain.contract, event: "StateBatchDeleted", logs: logs, sub: sub}, nil
}
// WatchStateBatchDeleted is a free log subscription operation binding the contract event 0x8747b69ce8fdb31c3b9b0a67bd8049ad8c1a69ea417b69b12174068abd9cbd64.
//
// Solidity: event StateBatchDeleted(uint256 indexed _batchIndex, bytes32 _batchRoot)
func (_StateCommitmentChain *StateCommitmentChainFilterer) WatchStateBatchDeleted(opts *bind.WatchOpts, sink chan<- *StateCommitmentChainStateBatchDeleted, _batchIndex []*big.Int) (event.Subscription, error) {
var _batchIndexRule []interface{}
for _, _batchIndexItem := range _batchIndex {
_batchIndexRule = append(_batchIndexRule, _batchIndexItem)
}
logs, sub, err := _StateCommitmentChain.contract.WatchLogs(opts, "StateBatchDeleted", _batchIndexRule)
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(StateCommitmentChainStateBatchDeleted)
if err := _StateCommitmentChain.contract.UnpackLog(event, "StateBatchDeleted", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ParseStateBatchDeleted is a log parse operation binding the contract event 0x8747b69ce8fdb31c3b9b0a67bd8049ad8c1a69ea417b69b12174068abd9cbd64.
//
// Solidity: event StateBatchDeleted(uint256 indexed _batchIndex, bytes32 _batchRoot)
func (_StateCommitmentChain *StateCommitmentChainFilterer) ParseStateBatchDeleted(log types.Log) (*StateCommitmentChainStateBatchDeleted, error) {
event := new(StateCommitmentChainStateBatchDeleted)
if err := _StateCommitmentChain.contract.UnpackLog(event, "StateBatchDeleted", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
...@@ -6,21 +6,22 @@ import ( ...@@ -6,21 +6,22 @@ import (
//Define the metrics we wish to expose //Define the metrics we wish to expose
var ( var (
ctcTotalElements = prometheus.NewGaugeVec( addressTotalElements = prometheus.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: "l2geth_ctc_total_elements", Name: "l2geth_total_elements",
Help: "CTC GetTotalElements value."}, Help: "GetTotalElements value."},
[]string{"state"}, []string{"state", "address"},
) )
ctcTotalElementsCallSuccess = prometheus.NewGauge( addressTotalElementsCallStatus = prometheus.NewCounterVec(
prometheus.GaugeOpts{ prometheus.CounterOpts{
Name: "l2geth_ctc_total_elements_call_success", Name: "l2geth_total_elements_call_status",
Help: "CTC GetTotalElements call success."}, Help: "GetTotalElements call status."},
[]string{"status", "address"},
) )
) )
func init() { func init() {
//Register metrics with prometheus //Register metrics with prometheus
prometheus.MustRegister(ctcTotalElements) prometheus.MustRegister(addressTotalElements)
prometheus.MustRegister(ctcTotalElementsCallSuccess) prometheus.MustRegister(addressTotalElementsCallStatus)
} }
...@@ -10,12 +10,36 @@ import ( ...@@ -10,12 +10,36 @@ import (
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
) )
// CTC interacts with the OVM CTC contract // CTC interacts with the OVM Canonical Transaction Chain contract
type CTC struct { type CTC struct {
Address common.Address Address common.Address
Client *ethclient.Client Client *ethclient.Client
} }
// SCC interacts with the OVM State Commitment Chain contract
type SCC struct {
Address common.Address
Client *ethclient.Client
}
func (ctc *SCC) GetTotalElements(ctx context.Context) (*big.Int, error) {
contract, err := bindings.NewCanonicalTransactionChainCaller(ctc.Address, ctc.Client)
if err != nil {
return nil, err
}
totalElements, err := contract.GetTotalElements(&bind.CallOpts{
Context: ctx,
})
if err != nil {
return nil, err
}
return totalElements, nil
}
func (ctc *CTC) GetTotalElements(ctx context.Context) (*big.Int, error) { func (ctc *CTC) GetTotalElements(ctx context.Context) (*big.Int, error) {
contract, err := bindings.NewCanonicalTransactionChainCaller(ctc.Address, ctc.Client) contract, err := bindings.NewCanonicalTransactionChainCaller(ctc.Address, ctc.Client)
......
...@@ -31,11 +31,16 @@ func main() { ...@@ -31,11 +31,16 @@ func main() {
log.Error("L1_URL environmental variable is required") log.Error("L1_URL environmental variable is required")
os.Exit(1) os.Exit(1)
} }
ctcAddress := os.Getenv("CTC_ADDRESS") ctcAddress := os.Getenv("OVM_CTC_ADDRESS")
if ctcAddress == "" { if ctcAddress == "" {
log.Error("CTC_ADDRESS environmental variable is required") log.Error("CTC_ADDRESS environmental variable is required")
os.Exit(1) os.Exit(1)
} }
sccAddress := os.Getenv("OVM_SCC_ADDRESS")
if sccAddress == "" {
log.Error("OVM_SCC_ADDRESS environmental variable is required")
os.Exit(1)
}
client, err := ethclient.Dial(l1Url) client, err := ethclient.Dial(l1Url)
if err != nil { if err != nil {
log.Error("Problem connecting to L1: %s", err) log.Error("Problem connecting to L1: %s", err)
...@@ -51,7 +56,8 @@ func main() { ...@@ -51,7 +56,8 @@ func main() {
</body> </body>
</html>`)) </html>`))
}) })
go getCTCTotalElements(ctcAddress, client) go getCTCTotalElements(ctcAddress, "ctc", client)
go getSCCTotalElements(sccAddress, "scc", client)
log.Info("Program starting", "listenAddress", listenAddress, "GETH_URL", l1Url, "CTC_ADDRESS", ctcAddress) log.Info("Program starting", "listenAddress", listenAddress, "GETH_URL", l1Url, "CTC_ADDRESS", ctcAddress)
if err := http.ListenAndServe(listenAddress, nil); err != nil { if err := http.ListenAndServe(listenAddress, nil); err != nil {
...@@ -60,7 +66,35 @@ func main() { ...@@ -60,7 +66,35 @@ func main() {
} }
func getCTCTotalElements(address string, client *ethclient.Client) { func getSCCTotalElements(address string, addressLabel string, client *ethclient.Client) {
scc := l1contracts.SCC{
Address: common.HexToAddress(address),
Client: client,
}
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(l1TimeoutSeconds))
totalElements, err := scc.GetTotalElements(ctx)
if err != nil {
addressTotalElementsCallStatus.WithLabelValues("error", addressLabel).Inc()
log.Error("Error calling GetTotalElements", "address", addressLabel, "error", err)
cancel()
continue
}
addressTotalElementsCallStatus.WithLabelValues("success", addressLabel).Inc()
totalElementsFloat, _ := new(big.Float).SetInt(totalElements).Float64()
addressTotalElements.WithLabelValues("latest", addressLabel).Set(totalElementsFloat)
log.Info(addressLabel, "TotalElements", totalElementsFloat)
cancel()
<-ticker.C
}
}
func getCTCTotalElements(address string, addressLabel string, client *ethclient.Client) {
ctc := l1contracts.CTC{ ctc := l1contracts.CTC{
Address: common.HexToAddress(address), Address: common.HexToAddress(address),
Client: client, Client: client,
...@@ -72,16 +106,16 @@ func getCTCTotalElements(address string, client *ethclient.Client) { ...@@ -72,16 +106,16 @@ func getCTCTotalElements(address string, client *ethclient.Client) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(l1TimeoutSeconds)) ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(l1TimeoutSeconds))
totalElements, err := ctc.GetTotalElements(ctx) totalElements, err := ctc.GetTotalElements(ctx)
if err != nil { if err != nil {
ctcTotalElementsCallSuccess.Set(0) addressTotalElementsCallStatus.WithLabelValues("error", addressLabel).Inc()
log.Error("Error calling GetTotalElements", "error", err) log.Error("Error calling GetTotalElements", "address", addressLabel, "error", err)
cancel() cancel()
continue continue
} }
ctcTotalElementsCallSuccess.Set(1) addressTotalElementsCallStatus.WithLabelValues("success", addressLabel).Inc()
totalElementsFloat, _ := new(big.Float).SetInt(totalElements).Float64() totalElementsFloat, _ := new(big.Float).SetInt(totalElements).Float64()
ctcTotalElements.WithLabelValues( addressTotalElements.WithLabelValues("latest", addressLabel).Set(totalElementsFloat)
"latest").Set(totalElementsFloat)
log.Info("ctc updated", "ctcTotalElements", totalElementsFloat) log.Info(addressLabel, "TotalElements", totalElementsFloat)
cancel() cancel()
<-ticker.C <-ticker.C
......
...@@ -19,6 +19,7 @@ import ( ...@@ -19,6 +19,7 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"golang.org/x/sync/semaphore"
) )
const ( const (
...@@ -88,7 +89,7 @@ type Backend struct { ...@@ -88,7 +89,7 @@ type Backend struct {
authUsername string authUsername string
authPassword string authPassword string
rateLimiter RateLimiter rateLimiter RateLimiter
client *http.Client client *LimitedHTTPClient
dialer *websocket.Dialer dialer *websocket.Dialer
maxRetries int maxRetries int
maxResponseSize int64 maxResponseSize int64
...@@ -170,6 +171,7 @@ func NewBackend( ...@@ -170,6 +171,7 @@ func NewBackend(
rpcURL string, rpcURL string,
wsURL string, wsURL string,
rateLimiter RateLimiter, rateLimiter RateLimiter,
rpcSemaphore *semaphore.Weighted,
opts ...BackendOpt, opts ...BackendOpt,
) *Backend { ) *Backend {
backend := &Backend{ backend := &Backend{
...@@ -178,8 +180,10 @@ func NewBackend( ...@@ -178,8 +180,10 @@ func NewBackend(
wsURL: wsURL, wsURL: wsURL,
rateLimiter: rateLimiter, rateLimiter: rateLimiter,
maxResponseSize: math.MaxInt64, maxResponseSize: math.MaxInt64,
client: &http.Client{ client: &LimitedHTTPClient{
Timeout: 5 * time.Second, Client: http.Client{Timeout: 5 * time.Second},
sem: rpcSemaphore,
backendName: name,
}, },
dialer: &websocket.Dialer{}, dialer: &websocket.Dialer{},
} }
...@@ -358,7 +362,7 @@ func (b *Backend) doForward(ctx context.Context, rpcReq *RPCReq) (*RPCRes, error ...@@ -358,7 +362,7 @@ func (b *Backend) doForward(ctx context.Context, rpcReq *RPCReq) (*RPCRes, error
httpReq.Header.Set("content-type", "application/json") httpReq.Header.Set("content-type", "application/json")
httpReq.Header.Set("X-Forwarded-For", xForwardedFor) httpReq.Header.Set("X-Forwarded-For", xForwardedFor)
httpRes, err := b.client.Do(httpReq) httpRes, err := b.client.DoLimited(httpReq)
if err != nil { if err != nil {
return nil, wrapErr(err, "error in backend request") return nil, wrapErr(err, "error in backend request")
} }
...@@ -693,3 +697,18 @@ func sleepContext(ctx context.Context, duration time.Duration) { ...@@ -693,3 +697,18 @@ func sleepContext(ctx context.Context, duration time.Duration) {
case <-time.After(duration): case <-time.After(duration):
} }
} }
type LimitedHTTPClient struct {
http.Client
sem *semaphore.Weighted
backendName string
}
func (c *LimitedHTTPClient) DoLimited(req *http.Request) (*http.Response, error) {
if err := c.sem.Acquire(req.Context(), 1); err != nil {
tooManyRequestErrorsTotal.WithLabelValues(c.backendName).Inc()
return nil, wrapErr(err, "too many requests")
}
defer c.sem.Release(1)
return c.Do(req)
}
...@@ -12,6 +12,7 @@ type ServerConfig struct { ...@@ -12,6 +12,7 @@ type ServerConfig struct {
WSHost string `toml:"ws_host"` WSHost string `toml:"ws_host"`
WSPort int `toml:"ws_port"` WSPort int `toml:"ws_port"`
MaxBodySizeBytes int64 `toml:"max_body_size_bytes"` MaxBodySizeBytes int64 `toml:"max_body_size_bytes"`
MaxConcurrentRPCs int64 `toml:"max_concurrent_rpcs"`
// TimeoutSeconds specifies the maximum time spent serving an HTTP request. Note that isn't used for websocket connections // TimeoutSeconds specifies the maximum time spent serving an HTTP request. Note that isn't used for websocket connections
TimeoutSeconds int `toml:"timeout_seconds"` TimeoutSeconds int `toml:"timeout_seconds"`
......
...@@ -18,6 +18,7 @@ ws_host = "0.0.0.0" ...@@ -18,6 +18,7 @@ ws_host = "0.0.0.0"
ws_port = 8085 ws_port = 8085
# Maximum client body size, in bytes, that the server will accept. # Maximum client body size, in bytes, that the server will accept.
max_body_size_bytes = 10485760 max_body_size_bytes = 10485760
max_concurrent_rpcs = 1000
[redis] [redis]
# URL to a Redis instance. # URL to a Redis instance.
......
...@@ -17,5 +17,6 @@ require ( ...@@ -17,5 +17,6 @@ require (
github.com/rs/cors v1.8.0 github.com/rs/cors v1.8.0
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 // indirect github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
) )
package integration_tests
import (
"net/http"
"net/http/httptest"
"os"
"sync"
"testing"
"time"
"github.com/ethereum-optimism/optimism/go/proxyd"
"github.com/stretchr/testify/require"
)
func TestMaxConcurrentRPCs(t *testing.T) {
var (
mu sync.Mutex
concurrentRPCs int
maxConcurrentRPCs int
)
handler := func(w http.ResponseWriter, r *http.Request) {
mu.Lock()
concurrentRPCs++
if maxConcurrentRPCs < concurrentRPCs {
maxConcurrentRPCs = concurrentRPCs
}
mu.Unlock()
time.Sleep(time.Second * 2)
SingleResponseHandler(200, goodResponse)(w, r)
mu.Lock()
concurrentRPCs--
mu.Unlock()
}
// We don't use the MockBackend because it serializes requests to the handler
slowBackend := httptest.NewServer(http.HandlerFunc(handler))
defer slowBackend.Close()
require.NoError(t, os.Setenv("GOOD_BACKEND_RPC_URL", slowBackend.URL))
config := ReadConfig("max_rpc_conns")
client := NewProxydClient("http://127.0.0.1:8545")
shutdown, err := proxyd.Start(config)
require.NoError(t, err)
defer shutdown()
type resWithCodeErr struct {
res []byte
code int
err error
}
resCh := make(chan *resWithCodeErr)
for i := 0; i < 3; i++ {
go func() {
res, code, err := client.SendRPC("eth_chainId", nil)
resCh <- &resWithCodeErr{
res: res,
code: code,
err: err,
}
}()
}
res1 := <-resCh
res2 := <-resCh
res3 := <-resCh
require.NoError(t, res1.err)
require.NoError(t, res2.err)
require.NoError(t, res3.err)
require.Equal(t, 200, res1.code)
require.Equal(t, 200, res2.code)
require.Equal(t, 200, res3.code)
RequireEqualJSON(t, []byte(goodResponse), res1.res)
RequireEqualJSON(t, []byte(goodResponse), res2.res)
RequireEqualJSON(t, []byte(goodResponse), res3.res)
require.EqualValues(t, 2, maxConcurrentRPCs)
}
[server]
rpc_port = 8545
max_concurrent_rpcs = 2
[backend]
# this should cover blocked requests due to max_concurrent_rpcs
response_timeout_seconds = 12
[backends]
[backends.good]
rpc_url = "$GOOD_BACKEND_RPC_URL"
ws_url = "$GOOD_BACKEND_RPC_URL"
[backend_groups]
[backend_groups.main]
backends = ["good"]
[rpc_method_mappings]
eth_chainId = "main"
...@@ -212,6 +212,14 @@ var ( ...@@ -212,6 +212,14 @@ var (
Help: "Histogram of Redis command durations, in milliseconds.", Help: "Histogram of Redis command durations, in milliseconds.",
Buckets: MillisecondDurationBuckets, Buckets: MillisecondDurationBuckets,
}, []string{"command"}) }, []string{"command"})
tooManyRequestErrorsTotal = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "too_many_request_errors_total",
Help: "Count of request timeouts due to too many concurrent RPCs.",
}, []string{
"backend_name",
})
) )
func RecordRedisError(source string) { func RecordRedisError(source string) {
......
...@@ -10,9 +10,11 @@ import ( ...@@ -10,9 +10,11 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"golang.org/x/sync/semaphore"
) )
func Start(config *Config) (func(), error) { func Start(config *Config) (func(), error) {
...@@ -53,6 +55,12 @@ func Start(config *Config) (func(), error) { ...@@ -53,6 +55,12 @@ func Start(config *Config) (func(), error) {
} }
} }
maxConcurrentRPCs := config.Server.MaxConcurrentRPCs
if maxConcurrentRPCs == 0 {
maxConcurrentRPCs = math.MaxInt64
}
rpcRequestSemaphore := semaphore.NewWeighted(maxConcurrentRPCs)
backendNames := make([]string, 0) backendNames := make([]string, 0)
backendsByName := make(map[string]*Backend) backendsByName := make(map[string]*Backend)
for name, cfg := range config.Backends { for name, cfg := range config.Backends {
...@@ -111,7 +119,7 @@ func Start(config *Config) (func(), error) { ...@@ -111,7 +119,7 @@ func Start(config *Config) (func(), error) {
opts = append(opts, WithStrippedTrailingXFF()) opts = append(opts, WithStrippedTrailingXFF())
} }
opts = append(opts, WithProxydIP(os.Getenv("PROXYD_IP"))) opts = append(opts, WithProxydIP(os.Getenv("PROXYD_IP")))
back := NewBackend(name, rpcURL, wsURL, lim, opts...) back := NewBackend(name, rpcURL, wsURL, lim, rpcRequestSemaphore, opts...)
backendNames = append(backendNames, name) backendNames = append(backendNames, name)
backendsByName[name] = back backendsByName[name] = back
log.Info("configured backend", "name", name, "rpc_url", rpcURL, "ws_url", wsURL) log.Info("configured backend", "name", name, "rpc_url", rpcURL, "ws_url", wsURL)
......
...@@ -27,6 +27,7 @@ const ( ...@@ -27,6 +27,7 @@ const (
MaxBatchRPCCalls = 100 MaxBatchRPCCalls = 100
cacheStatusHdr = "X-Proxyd-Cache-Status" cacheStatusHdr = "X-Proxyd-Cache-Status"
defaultServerTimeout = time.Second * 10 defaultServerTimeout = time.Second * 10
maxLogLength = 2000
) )
type Server struct { type Server struct {
...@@ -150,6 +151,12 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) { ...@@ -150,6 +151,12 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) {
} }
RecordRequestPayloadSize(ctx, len(body)) RecordRequestPayloadSize(ctx, len(body))
log.Info("Raw RPC request",
"body", truncate(string(body)),
"req_id", GetReqID(ctx),
"auth", GetAuthCtx(ctx),
)
if IsBatch(body) { if IsBatch(body) {
reqs, err := ParseBatchRPCReq(body) reqs, err := ParseBatchRPCReq(body)
if err != nil { if err != nil {
...@@ -457,3 +464,11 @@ func (n *NoopRPCCache) GetRPC(context.Context, *RPCReq) (*RPCRes, error) { ...@@ -457,3 +464,11 @@ func (n *NoopRPCCache) GetRPC(context.Context, *RPCReq) (*RPCRes, error) {
func (n *NoopRPCCache) PutRPC(context.Context, *RPCReq, *RPCRes) error { func (n *NoopRPCCache) PutRPC(context.Context, *RPCReq, *RPCRes) error {
return nil return nil
} }
func truncate(str string) string {
if len(str) > maxLogLength {
return str[:maxLogLength] + "..."
} else {
return str
}
}
...@@ -291,7 +291,7 @@ func (d *Driver) UpdateGasPrice( ...@@ -291,7 +291,7 @@ func (d *Driver) UpdateGasPrice(
tx *types.Transaction, tx *types.Transaction,
) (*types.Transaction, error) { ) (*types.Transaction, error) {
gasPrice, err := d.cfg.L1Client.SuggestGasPrice(ctx) gasPrice, err := d.cfg.L2Client.SuggestGasPrice(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
#!/bin/sh
set -exu
GETH_DATA_DIR=/geth
GETH_CHAINDATA_DIR=$GETH_DATA_DIR/geth/chaindata
GETH_KEYSTORE_DIR=$GETH_DATA_DIR/keystore
if [ ! -d "$GETH_KEYSTORE_DIR" ]; then
echo "$GETH_KEYSTORE_DIR missing, running account import"
echo -n "$BLOCK_SIGNER_PRIVATE_KEY_PASSWORD" > "$GETH_DATA_DIR"/password
echo -n "$BLOCK_SIGNER_PRIVATE_KEY" > "$GETH_DATA_DIR"/block-signer-key
geth account import \
--datadir="$GETH_DATA_DIR" \
--password="$GETH_DATA_DIR"/password \
"$GETH_DATA_DIR"/block-signer-key
echo "get account import complete"
fi
if [ ! -d "$GETH_CHAINDATA_DIR" ]; then
echo "$GETH_CHAINDATA_DIR missing, running init"
geth init --datadir="$GETH_DATA_DIR" "$L2GETH_GENESIS_URL" "$L2GETH_GENESIS_HASH"
echo "geth init complete"
else
echo "$GETH_CHAINDATA_DIR exists, checking for hardfork."
echo "Chain config:"
geth dump-chain-cfg --datadir="$GETH_DATA_DIR"
if geth dump-chain-cfg --datadir="$GETH_DATA_DIR" | grep -q "\"berlinBlock\": $L2GETH_BERLIN_ACTIVATION_HEIGHT"; then
echo "Hardfork already activated."
else
echo "Hardfork not activated, running init."
geth init --datadir="$GETH_DATA_DIR" "$L2GETH_GENESIS_URL" "$L2GETH_GENESIS_HASH"
echo "geth hardfork activation complete"
fi
fi
\ No newline at end of file
DATA_TRANSPORT_LAYER__ADDRESS_MANAGER=0x07917E3D28aa7C61baAAcf8e486a77007E1Daa89
DATA_TRANSPORT_LAYER__CONFIRMATIONS=12
DATA_TRANSPORT_LAYER__DANGEROUSLY_CATCH_ALL_ERRORS=true
DATA_TRANSPORT_LAYER__DB_PATH=/db
DATA_TRANSPORT_LAYER__DEFAULT_BACKEND=l2
DATA_TRANSPORT_LAYER__ENABLE_METRICS=true
DATA_TRANSPORT_LAYER__ETH_NETWORK_NAME=goerli
DATA_TRANSPORT_LAYER__L1_GAS_PRICE_BACKEND=l2
DATA_TRANSPORT_LAYER__L1_START_HEIGHT=6680000
DATA_TRANSPORT_LAYER__L2_CHAIN_ID=421
DATA_TRANSPORT_LAYER__LOGS_PER_POLLING_INTERVAL=2000
DATA_TRANSPORT_LAYER__NODE_ENV=production
DATA_TRANSPORT_LAYER__POLLING_INTERVAL=500
DATA_TRANSPORT_LAYER__SENTRY_TRACE_RATE=0.05
DATA_TRANSPORT_LAYER__SERVER_HOSTNAME=0.0.0.0
DATA_TRANSPORT_LAYER__SERVER_PORT=7878
DATA_TRANSPORT_LAYER__SYNC_FROM_L1=false
DATA_TRANSPORT_LAYER__SYNC_FROM_L2=true
DATA_TRANSPORT_LAYER__TRANSACTIONS_PER_POLLING_INTERVAL=1000
DEPLOYER_HTTP=
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
configMapGenerator:
- name: data-transport-layer
envs:
- ./data-transport-layer.env
- name: l2geth-replica
envs:
- ./l2geth-replica.env
- name: replica-healthcheck
envs:
- ./replica-healthcheck.env
- name: geth-scripts
files:
- ./check-for-chaindata.sh
\ No newline at end of file
CHAIN_ID=421
DATADIR=/geth
NETWORK_ID=421
NO_DISCOVER=true
NO_USB=true
GASPRICE=0
GCMODE=archive
BLOCK_SIGNER_ADDRESS=0x00000398232E2064F896018496b4b44b3D62751F
BLOCK_SIGNER_PRIVATE_KEY=6587ae678cf4fc9a33000cdbf9f35226b71dcc6a4684a31203241f9bcfd55d27
BLOCK_SIGNER_PRIVATE_KEY_PASSWORD=pwd
ETH1_CTC_DEPLOYMENT_HEIGHT=0
ETH1_SYNC_SERVICE_ENABLE=true
L2GETH_GENESIS_URL=https://storage.googleapis.com/optimism/goerli-nightly/genesis.json
L2GETH_GENESIS_HASH=0x20091f0016a82537cda17009b87fb67cb9e3398eb4f7d2a99a47ffab62460cb1
ROLLUP_BACKEND=l2
ROLLUP_CLIENT_HTTP=http://data-transport-layer:7878
ROLLUP_DISABLE_TRANSFERS=false
ROLLUP_ENABLE_L2_GAS_POLLING=false
ROLLUP_MAX_CALLDATA_SIZE=40000
ROLLUP_POLL_INTERVAL_FLAG=3s
ROLLUP_SYNC_SERVICE_ENABLE=true
ROLLUP_TIMESTAMP_REFRESH=3m
ROLLUP_VERIFIER_ENABLE=true
RPC_ADDR=0.0.0.0
RPC_API=eth,rollup,net,web3,debug
RPC_CORS_DOMAIN=*
RPC_ENABLE=true
RPC_PORT=8545
RPC_VHOSTS=*
TARGET_GAS_LIMIT=15000000
USING_OVM=true
WS_ADDR=0.0.0.0
WS_API=eth,rollup,net,web3,debug
WS_ORIGINS=*
WS=true
REPLICA_HEALTHCHECK__ETH_NETWORK=goerli-nightly
REPLICA_HEALTHCHECK__L2GETH_IMAGE_TAG=0.4.9
REPLICA_HEALTHCHECK__ETH_REPLICA_RPC_PROVIDER=http://l2geth-replica:8545
\ No newline at end of file
apiVersion: apps/v1
kind: Deployment
metadata:
name: replica-healthcheck-v1
spec:
replicas: 1
template:
spec:
containers:
- name: replica-healthcheck
image: ethereumoptimism/replica-healthcheck-v1
ports:
- containerPort: 7300
name: metrics
resources:
limits:
memory: 256Mi
cpu: 512m
requests:
memory: 128Mi
cpu: 256m
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
commonLabels:
app: replica-healthcheck-v1
\ No newline at end of file
...@@ -3,3 +3,4 @@ kind: Kustomization ...@@ -3,3 +3,4 @@ kind: Kustomization
resources: resources:
- ./data-transport-layer.yaml - ./data-transport-layer.yaml
- ./replica-healthcheck.yaml - ./replica-healthcheck.yaml
- ./replica-healthcheck-v1.yaml
\ No newline at end of file
---
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: replica-healthcheck-v1
spec:
selector:
matchLabels:
app: replica-healthcheck-v1
provider: internal
podMetricsEndpoints:
- port: metrics
podTargetLabels:
- network
- provider
- sync_source
\ No newline at end of file
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: goerli-nightly-replica
commonLabels:
network: goerli-nightly
provider: internal
bases:
- ../../../envs/goerli-nightly
- ../../../scripts
resources:
- ../../bases/data-transport-layer
- ../../bases/l2geth-replica
- ../../bases/servicemonitors
- ../../bases/replica-healthcheck-v1
- ./volumes.yaml
images:
- name: ethereumoptimism/data-transport-layer
newName: ethereumoptimism/data-transport-layer
newTag: latest
- name: ethereumoptimism/l2geth
newName: ethereumoptimism/l2geth
newTag: latest
- name: ethereumoptimism/replica-healthcheck-v1
newName: ethereumoptimism/replica-healthcheck
newTag: latest
patchesStrategicMerge:
- ./patches/dtl.yaml
- ./patches/l2geth.yaml
- ./patches/replica-healthcheck.yaml
patches:
- path: ./patches/l2geth-volume.yaml
target:
group: apps
version: v1
kind: StatefulSet
name: l2geth-replica
- path: ./patches/dtl-volume.yaml
target:
group: apps
version: v1
kind: StatefulSet
name: data-transport-layer
\ No newline at end of file
---
- op: replace
path: /spec/template/spec/volumes/0
value:
name: data-transport-layer
persistentVolumeClaim:
claimName: data-transport-layer-data
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: data-transport-layer
spec:
template:
spec:
initContainers:
- name: wait-for-l1
env:
- name: L1_NODE_WEB3_URL
value: http://failover-proxyd.default:8080
containers:
- name: data-transport-layer
resources:
limits:
cpu: "2"
memory: 4Gi
requests:
cpu: "1"
memory: 1Gi
env:
- name: DATA_TRANSPORT_LAYER__L1_RPC_ENDPOINT
value: http://failover-proxyd.default:8080
- name: DATA_TRANSPORT_LAYER__L2_RPC_ENDPOINT
value: http://sequencer.default:8545
- name: L1_NODE_WEB3_URL
value: http://failover-proxyd.default:8080
\ No newline at end of file
- op: replace
path: /spec/template/spec/volumes/0
value:
name: l2geth-replica-data
persistentVolumeClaim:
claimName: l2geth-replica-data
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: l2geth-replica
spec:
template:
spec:
containers:
- name: l2geth-replica
env:
- name: IPC_DISABLE
value: "false"
resources:
limits:
cpu: "4"
memory: 12Gi
requests:
cpu: "2"
memory: 8Gi
apiVersion: apps/v1
kind: Deployment
metadata:
name: replica-healthcheck-v1
spec:
replicas: 1
template:
spec:
containers:
- name: replica-healthcheck
env:
- name: HEALTHCHECK__REFERENCE_RPC_PROVIDER
value: http://sequencer.default:8545
- name: HEALTHCHECK__TARGET_RPC_PROVIDER
value: http://l2geth-replica:8545
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: l2geth-replica-data
spec:
storageClassName: premium-rwo
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 200Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-transport-layer-data
spec:
storageClassName: premium-rwo
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
\ No newline at end of file
...@@ -14,6 +14,7 @@ resources: ...@@ -14,6 +14,7 @@ resources:
- ../../bases/l2geth-replica - ../../bases/l2geth-replica
- ../../bases/servicemonitors - ../../bases/servicemonitors
- ../../bases/replica-healthcheck - ../../bases/replica-healthcheck
- ../../bases/replica-healthcheck-v1
- ./volumes.yaml - ./volumes.yaml
- ./ingress.yaml - ./ingress.yaml
...@@ -27,6 +28,9 @@ images: ...@@ -27,6 +28,9 @@ images:
- name: ethereumoptimism/replica-healthcheck - name: ethereumoptimism/replica-healthcheck
newName: ethereumoptimism/replica-healthcheck newName: ethereumoptimism/replica-healthcheck
newTag: 0.3.3 newTag: 0.3.3
- name: ethereumoptimism/replica-healthcheck-v1
newName: ethereumoptimism/replica-healthcheck
newTag: 1.0.4
patchesStrategicMerge: patchesStrategicMerge:
- ./patches/dtl.yaml - ./patches/dtl.yaml
...@@ -40,3 +44,8 @@ patches: ...@@ -40,3 +44,8 @@ patches:
version: v1 version: v1
kind: StatefulSet kind: StatefulSet
name: l2geth-replica name: l2geth-replica
configMapGenerator:
- name: replica-healthcheck-v1
envs:
- ./replica-healthcheck-v1.env
HEALTHCHECK__REFERENCE_RPC_PROVIDER=https://mainnet.optimism.io
HEALTHCHECK__TARGET_RPC_PROVIDER=http://l2geth-replica:8545
\ No newline at end of file
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: mainnet-replica-0-5-17
commonLabels:
network: mainnet
provider: internal
bases:
- ../../../envs/mainnet-gen5-berlin/
- ../../../scripts
resources:
- ../../bases/data-transport-layer
- ../../bases/l2geth-replica
- ../../bases/servicemonitors
- ../../bases/replica-healthcheck
- ../../bases/replica-healthcheck-v1
- ./volumes.yaml
images:
- name: ethereumoptimism/data-transport-layer
newName: ethereumoptimism/data-transport-layer
newTag: 0.5.25
- name: ethereumoptimism/l2geth
newName: ethereumoptimism/l2geth
newTag: 0.5.17
- name: ethereumoptimism/replica-healthcheck
newName: ethereumoptimism/replica-healthcheck
newTag: 0.3.3
- name: ethereumoptimism/replica-healthcheck-v1
newName: ethereumoptimism/replica-healthcheck
newTag: 1.0.4
patchesStrategicMerge:
- ./patches/dtl.yaml
- ./patches/l2geth.yaml
- ./patches/replica-healthcheck.yaml
patches:
- path: ./patches/l2geth-volume.yaml
target:
group: apps
version: v1
kind: StatefulSet
name: l2geth-replica
configMapGenerator:
- name: replica-healthcheck-v1
envs:
- ./replica-healthcheck-v1.env
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: data-transport-layer
spec:
template:
spec:
initContainers:
- name: wait-for-l1
env:
- name: L1_NODE_WEB3_URL
valueFrom:
secretKeyRef:
name: replica-secrets
key: l1-rpc-endpoint
containers:
- name: data-transport-layer
resources:
limits:
cpu: "2"
memory: 4Gi
requests:
cpu: "1"
memory: 1Gi
env:
- name: DATA_TRANSPORT_LAYER__L1_RPC_ENDPOINT
valueFrom:
secretKeyRef:
name: replica-secrets
key: l1-rpc-endpoint
- name: DATA_TRANSPORT_LAYER__L2_RPC_ENDPOINT
valueFrom:
secretKeyRef:
name: replica-secrets
key: l2-rpc-endpoint
- name: L1_NODE_WEB3_URL
valueFrom:
secretKeyRef:
name: replica-secrets
key: l1-rpc-endpoint
\ No newline at end of file
- op: replace
path: /spec/template/spec/volumes/0
value:
name: l2geth-replica-data
persistentVolumeClaim:
claimName: l2geth-replica-data
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: l2geth-replica
spec:
template:
spec:
containers:
- name: l2geth-replica
command:
- geth
- --config=/l2geth-config/l2geth.toml
- --datadir=$(DATADIR)
- --password=$(DATADIR)/password
- --allow-insecure-unlock
- --unlock=$(BLOCK_SIGNER_ADDRESS)
- --mine
- --miner.etherbase=$(BLOCK_SIGNER_ADDRESS)
- --metrics
- --metrics.influxdb
- --metrics.influxdb.endpoint=http://influxdb.monitoring:8086
- --metrics.influxdb.database=$(NAMESPACE)
resources:
limits:
cpu: "8"
memory: 24Gi
requests:
cpu: "4"
memory: 12Gi
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: data-transport-layer
spec:
template:
spec:
initContainers:
- name: wait-for-l1
env:
- name: L1_NODE_WEB3_URL
valueFrom:
secretKeyRef:
name: replica-secrets
key: l1-rpc-endpoint
containers:
- name: data-transport-layer
resources:
limits:
cpu: "2"
memory: 4Gi
requests:
cpu: "1"
memory: 1Gi
env:
- name: DATA_TRANSPORT_LAYER__L1_RPC_ENDPOINT
valueFrom:
secretKeyRef:
name: replica-secrets
key: l1-rpc-endpoint
- name: DATA_TRANSPORT_LAYER__L2_RPC_ENDPOINT
valueFrom:
secretKeyRef:
name: replica-secrets
key: l2-rpc-endpoint
- name: L1_NODE_WEB3_URL
valueFrom:
secretKeyRef:
name: replica-secrets
key: l1-rpc-endpoint
\ No newline at end of file
apiVersion: apps/v1
kind: Deployment
metadata:
name: replica-healthcheck
spec:
template:
spec:
containers:
- name: replica-healthcheck
env:
- name: REPLICA_HEALTHCHECK__ETH_NETWORK_RPC_PROVIDER
value: https://mainnet.optimism.io
HEALTHCHECK__REFERENCE_RPC_PROVIDER=https://mainnet.optimism.io
HEALTHCHECK__TARGET_RPC_PROVIDER=http://l2geth-replica:8545
\ No newline at end of file
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: l2geth-replica-data
spec:
storageClassName: premium-rwo
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-transport-layer-data
spec:
storageClassName: premium-rwo
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
\ No newline at end of file
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import { BigNumber, Contract, ContractFactory, utils, Wallet } from 'ethers' import { BigNumber, Contract, ContractFactory, utils, Wallet } from 'ethers'
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { futurePredeploys } from '@eth-optimism/contracts' import { futurePredeploys } from '@eth-optimism/contracts'
import { sleep } from '@eth-optimism/core-utils'
/* Imports: Internal */ /* Imports: Internal */
import { expect } from './shared/setup' import { expect } from './shared/setup'
...@@ -102,4 +103,42 @@ describe('System addresses', () => { ...@@ -102,4 +103,42 @@ describe('System addresses', () => {
expect(receipt.contractAddress).not.to.eq(null) expect(receipt.contractAddress).not.to.eq(null)
} }
}) })
const testReplica = async (otherProvider) => {
const seqBlock = await env.l2Provider.getBlock('latest')
while (true) {
const verHeight = await otherProvider.getBlockNumber()
if (verHeight >= seqBlock.number) {
break
}
await sleep(200)
}
const verBlock = await otherProvider.getBlock(seqBlock.number)
expect(verBlock).to.deep.eq(seqBlock)
for (const addr of SYSTEM_ADDRESSES) {
const seqCode = await env.l2Provider.getCode(addr)
const verCode = await otherProvider.getCode(addr)
expect(seqCode).to.eq(verCode)
}
}
it('should be properly handled on verifiers', async function () {
if (!envConfig.RUN_VERIFIER_TESTS) {
this.skip()
return
}
await testReplica(env.verifierProvider)
})
it('should be properly handled on replicas', async function () {
if (!envConfig.RUN_REPLICA_TESTS) {
this.skip()
return
}
await testReplica(env.replicaProvider)
})
}) })
...@@ -34,6 +34,7 @@ import ( ...@@ -34,6 +34,7 @@ import (
"github.com/ethereum-optimism/optimism/l2geth/core/types" "github.com/ethereum-optimism/optimism/l2geth/core/types"
"github.com/ethereum-optimism/optimism/l2geth/event" "github.com/ethereum-optimism/optimism/l2geth/event"
"github.com/ethereum-optimism/optimism/l2geth/log" "github.com/ethereum-optimism/optimism/l2geth/log"
"github.com/ethereum-optimism/optimism/l2geth/metrics"
"github.com/ethereum-optimism/optimism/l2geth/params" "github.com/ethereum-optimism/optimism/l2geth/params"
) )
...@@ -85,6 +86,13 @@ var ( ...@@ -85,6 +86,13 @@ var (
// l2geth, rather the actual execution error should be returned to the // l2geth, rather the actual execution error should be returned to the
// user. // user.
ErrCannotCommitTxn = errors.New("Cannot commit transaction in miner") ErrCannotCommitTxn = errors.New("Cannot commit transaction in miner")
// rollup apply transaction metrics
accountReadTimer = metrics.NewRegisteredTimer("rollup/tx/account/reads", nil)
accountUpdateTimer = metrics.NewRegisteredTimer("rollup/tx/account/updates", nil)
storageReadTimer = metrics.NewRegisteredTimer("rollup/tx/storage/reads", nil)
storageUpdateTimer = metrics.NewRegisteredTimer("rollup/tx/storage/updates", nil)
txExecutionTimer = metrics.NewRegisteredTimer("rollup/tx/execution", nil)
) )
// environment is the worker's current environment and holds all of the current state information. // environment is the worker's current environment and holds all of the current state information.
...@@ -771,6 +779,7 @@ func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Addres ...@@ -771,6 +779,7 @@ func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Addres
} }
snap := w.current.state.Snapshot() snap := w.current.state.Snapshot()
start := time.Now()
receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, *w.chain.GetVMConfig()) receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, *w.chain.GetVMConfig())
if err != nil { if err != nil {
w.current.state.RevertToSnapshot(snap) w.current.state.RevertToSnapshot(snap)
...@@ -779,6 +788,8 @@ func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Addres ...@@ -779,6 +788,8 @@ func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Addres
w.current.txs = append(w.current.txs, tx) w.current.txs = append(w.current.txs, tx)
w.current.receipts = append(w.current.receipts, receipt) w.current.receipts = append(w.current.receipts, receipt)
updateTransactionStateMetrics(start, w.current.state)
return receipt.Logs, nil return receipt.Logs, nil
} }
...@@ -1143,3 +1154,13 @@ func (w *worker) postSideBlock(event core.ChainSideEvent) { ...@@ -1143,3 +1154,13 @@ func (w *worker) postSideBlock(event core.ChainSideEvent) {
case <-w.exitCh: case <-w.exitCh:
} }
} }
func updateTransactionStateMetrics(start time.Time, state *state.StateDB) {
accountReadTimer.Update(state.AccountReads)
storageReadTimer.Update(state.StorageReads)
accountUpdateTimer.Update(state.AccountUpdates)
storageUpdateTimer.Update(state.StorageUpdates)
triehash := state.AccountHashes + state.StorageHashes
txExecutionTimer.Update(time.Since(start) - triehash)
}
...@@ -247,10 +247,10 @@ func (s *SyncService) Start() error { ...@@ -247,10 +247,10 @@ func (s *SyncService) Start() error {
} else { } else {
go func() { go func() {
if err := s.syncTransactionsToTip(); err != nil { if err := s.syncTransactionsToTip(); err != nil {
log.Crit("Sequencer cannot sync transactions to tip: %w", err) log.Crit("Sequencer cannot sync transactions to tip", "err", err)
} }
if err := s.syncQueueToTip(); err != nil { if err := s.syncQueueToTip(); err != nil {
log.Crit("Sequencer cannot sync queue to tip: %w", err) log.Crit("Sequencer cannot sync queue to tip", "err", err)
} }
s.setSyncStatus(false) s.setSyncStatus(false)
go s.SequencerLoop() go s.SequencerLoop()
......
...@@ -95,6 +95,9 @@ contract L2CrossDomainMessenger is IL2CrossDomainMessenger { ...@@ -95,6 +95,9 @@ contract L2CrossDomainMessenger is IL2CrossDomainMessenger {
bytes memory _message, bytes memory _message,
uint256 _messageNonce uint256 _messageNonce
) public { ) public {
// Since it is impossible to deploy a contract to an address on L2 which matches
// the alias of the L1CrossDomainMessenger, this check can only pass when it is called in
// the first call from of a deposit transaction. Thus reentrancy is prevented here.
require( require(
AddressAliasHelper.undoL1ToL2Alias(msg.sender) == l1CrossDomainMessenger, AddressAliasHelper.undoL1ToL2Alias(msg.sender) == l1CrossDomainMessenger,
"Provided message could not be verified." "Provided message could not be verified."
......
/* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract, BigNumber } from 'ethers' import { Contract, BigNumber } from 'ethers'
import { import { smock, FakeContract, MockContract } from '@defi-wonderland/smock'
smock, import { toHexString, applyL1ToL2Alias } from '@eth-optimism/core-utils'
MockContractFactory, import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
FakeContract,
} from '@defi-wonderland/smock'
import {
remove0x,
toHexString,
applyL1ToL2Alias,
} from '@eth-optimism/core-utils'
/* Internal Imports */
import { expect } from '../../../setup' import { expect } from '../../../setup'
import { import {
makeAddressManager,
setProxyTarget, setProxyTarget,
NON_NULL_BYTES32, NON_NULL_BYTES32,
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
...@@ -28,59 +18,39 @@ import { ...@@ -28,59 +18,39 @@ import {
encodeXDomainCalldata, encodeXDomainCalldata,
getEthTime, getEthTime,
setEthTime, setEthTime,
deploy,
} from '../../../helpers' } from '../../../helpers'
import { predeploys } from '../../../../src' import { predeploys } from '../../../../src'
const MAX_GAS_LIMIT = 8_000_000 const MAX_GAS_LIMIT = 8_000_000
const deployProxyXDomainMessenger = async (
addressManager: Contract,
l1XDomainMessenger: Contract
): Promise<Contract> => {
await addressManager.setAddress(
'L1CrossDomainMessenger',
l1XDomainMessenger.address
)
const proxy = await (
await ethers.getContractFactory('Lib_ResolvedDelegateProxy')
).deploy(addressManager.address, 'L1CrossDomainMessenger')
return l1XDomainMessenger.attach(proxy.address)
}
describe('L1CrossDomainMessenger', () => { describe('L1CrossDomainMessenger', () => {
let signer: Signer let signer1: SignerWithAddress
let signer2: Signer let signer2: SignerWithAddress
before(async () => {
;[signer, signer2] = await ethers.getSigners()
})
let AddressManager: Contract
before(async () => { before(async () => {
AddressManager = await makeAddressManager() ;[signer1, signer2] = await ethers.getSigners()
}) })
let Fake__TargetContract: FakeContract let Fake__TargetContract: FakeContract
let Fake__L2CrossDomainMessenger: FakeContract let Fake__L2CrossDomainMessenger: FakeContract
let Fake__StateCommitmentChain: FakeContract let Fake__StateCommitmentChain: FakeContract
let Factory__CanonicalTransactionChain: ContractFactory
let Factory__ChainStorageContainer: ContractFactory
let Factory__L1CrossDomainMessenger: ContractFactory
let CanonicalTransactionChain: Contract
before(async () => { before(async () => {
Fake__TargetContract = await smock.fake<Contract>( Fake__TargetContract = await smock.fake<Contract>('Helper_SimpleProxy')
await ethers.getContractFactory('Helper_SimpleProxy')
)
Fake__L2CrossDomainMessenger = await smock.fake<Contract>( Fake__L2CrossDomainMessenger = await smock.fake<Contract>(
await ethers.getContractFactory('L2CrossDomainMessenger'), 'L2CrossDomainMessenger',
{ {
address: predeploys.L2CrossDomainMessenger, address: predeploys.L2CrossDomainMessenger,
} }
) )
Fake__StateCommitmentChain = await smock.fake<Contract>( Fake__StateCommitmentChain = await smock.fake<Contract>(
await ethers.getContractFactory('StateCommitmentChain') 'StateCommitmentChain'
) )
})
let AddressManager: Contract
let CanonicalTransactionChain: Contract
before(async () => {
AddressManager = await deploy('Lib_AddressManager')
await AddressManager.setAddress( await AddressManager.setAddress(
'L2CrossDomainMessenger', 'L2CrossDomainMessenger',
...@@ -93,32 +63,18 @@ describe('L1CrossDomainMessenger', () => { ...@@ -93,32 +63,18 @@ describe('L1CrossDomainMessenger', () => {
Fake__StateCommitmentChain Fake__StateCommitmentChain
) )
Factory__CanonicalTransactionChain = await ethers.getContractFactory( CanonicalTransactionChain = await deploy('CanonicalTransactionChain', {
'CanonicalTransactionChain' args: [
)
Factory__ChainStorageContainer = await ethers.getContractFactory(
'ChainStorageContainer'
)
Factory__L1CrossDomainMessenger = await ethers.getContractFactory(
'L1CrossDomainMessenger'
)
CanonicalTransactionChain = await Factory__CanonicalTransactionChain.deploy(
AddressManager.address, AddressManager.address,
MAX_GAS_LIMIT, MAX_GAS_LIMIT,
L2_GAS_DISCOUNT_DIVISOR, L2_GAS_DISCOUNT_DIVISOR,
ENQUEUE_GAS_COST ENQUEUE_GAS_COST,
) ],
})
const batches = await Factory__ChainStorageContainer.deploy( const batches = await deploy('ChainStorageContainer', {
AddressManager.address, args: [AddressManager.address, 'CanonicalTransactionChain'],
'CanonicalTransactionChain' })
)
await Factory__ChainStorageContainer.deploy(
AddressManager.address,
'CanonicalTransactionChain'
)
await AddressManager.setAddress( await AddressManager.setAddress(
'ChainStorageContainer-CTC-batches', 'ChainStorageContainer-CTC-batches',
...@@ -133,12 +89,19 @@ describe('L1CrossDomainMessenger', () => { ...@@ -133,12 +89,19 @@ describe('L1CrossDomainMessenger', () => {
let L1CrossDomainMessenger: Contract let L1CrossDomainMessenger: Contract
beforeEach(async () => { beforeEach(async () => {
const xDomainMessengerImpl = await Factory__L1CrossDomainMessenger.deploy() const xDomainMessengerImpl = await deploy('L1CrossDomainMessenger')
// We use an upgradable proxy for the XDomainMessenger--deploy & set up the proxy.
L1CrossDomainMessenger = await deployProxyXDomainMessenger( await AddressManager.setAddress(
AddressManager, 'L1CrossDomainMessenger',
xDomainMessengerImpl xDomainMessengerImpl.address
) )
const proxy = await deploy('Lib_ResolvedDelegateProxy', {
args: [AddressManager.address, 'L1CrossDomainMessenger'],
})
L1CrossDomainMessenger = xDomainMessengerImpl.attach(proxy.address)
await L1CrossDomainMessenger.initialize(AddressManager.address) await L1CrossDomainMessenger.initialize(AddressManager.address)
}) })
...@@ -172,7 +135,7 @@ describe('L1CrossDomainMessenger', () => { ...@@ -172,7 +135,7 @@ describe('L1CrossDomainMessenger', () => {
const calldata = encodeXDomainCalldata( const calldata = encodeXDomainCalldata(
target, target,
await signer.getAddress(), signer1.address,
message, message,
0 0
) )
...@@ -209,14 +172,9 @@ describe('L1CrossDomainMessenger', () => { ...@@ -209,14 +172,9 @@ describe('L1CrossDomainMessenger', () => {
const oldGasLimit = 100_000 const oldGasLimit = 100_000
const newGasLimit = 200_000 const newGasLimit = 200_000
let sender: string
before(async () => {
sender = await signer.getAddress()
})
let queueIndex: number let queueIndex: number
beforeEach(async () => { beforeEach(async () => {
await L1CrossDomainMessenger.connect(signer).sendMessage( await L1CrossDomainMessenger.connect(signer1).sendMessage(
target, target,
message, message,
oldGasLimit oldGasLimit
...@@ -231,7 +189,7 @@ describe('L1CrossDomainMessenger', () => { ...@@ -231,7 +189,7 @@ describe('L1CrossDomainMessenger', () => {
await expect( await expect(
L1CrossDomainMessenger.replayMessage( L1CrossDomainMessenger.replayMessage(
ethers.constants.AddressZero, // Wrong target ethers.constants.AddressZero, // Wrong target
sender, signer1.address,
message, message,
queueIndex, queueIndex,
oldGasLimit, oldGasLimit,
...@@ -257,7 +215,7 @@ describe('L1CrossDomainMessenger', () => { ...@@ -257,7 +215,7 @@ describe('L1CrossDomainMessenger', () => {
await expect( await expect(
L1CrossDomainMessenger.replayMessage( L1CrossDomainMessenger.replayMessage(
target, target,
sender, signer1.address,
'0x', // Wrong message '0x', // Wrong message
queueIndex, queueIndex,
oldGasLimit, oldGasLimit,
...@@ -270,7 +228,7 @@ describe('L1CrossDomainMessenger', () => { ...@@ -270,7 +228,7 @@ describe('L1CrossDomainMessenger', () => {
await expect( await expect(
L1CrossDomainMessenger.replayMessage( L1CrossDomainMessenger.replayMessage(
target, target,
sender, signer1.address,
message, message,
queueIndex - 1, // Wrong queue index queueIndex - 1, // Wrong queue index
oldGasLimit, oldGasLimit,
...@@ -283,7 +241,7 @@ describe('L1CrossDomainMessenger', () => { ...@@ -283,7 +241,7 @@ describe('L1CrossDomainMessenger', () => {
await expect( await expect(
L1CrossDomainMessenger.replayMessage( L1CrossDomainMessenger.replayMessage(
target, target,
sender, signer1.address,
message, message,
queueIndex, queueIndex,
oldGasLimit + 1, // Wrong gas limit oldGasLimit + 1, // Wrong gas limit
...@@ -298,7 +256,7 @@ describe('L1CrossDomainMessenger', () => { ...@@ -298,7 +256,7 @@ describe('L1CrossDomainMessenger', () => {
await expect( await expect(
L1CrossDomainMessenger.replayMessage( L1CrossDomainMessenger.replayMessage(
target, target,
sender, signer1.address,
message, message,
queueIndex, queueIndex,
oldGasLimit, oldGasLimit,
...@@ -315,7 +273,7 @@ describe('L1CrossDomainMessenger', () => { ...@@ -315,7 +273,7 @@ describe('L1CrossDomainMessenger', () => {
await expect( await expect(
L1CrossDomainMessenger.replayMessage( L1CrossDomainMessenger.replayMessage(
target, target,
sender, signer1.address,
message, message,
queueIndex, queueIndex,
oldGasLimit, oldGasLimit,
...@@ -327,7 +285,7 @@ describe('L1CrossDomainMessenger', () => { ...@@ -327,7 +285,7 @@ describe('L1CrossDomainMessenger', () => {
applyL1ToL2Alias(L1CrossDomainMessenger.address), applyL1ToL2Alias(L1CrossDomainMessenger.address),
Fake__L2CrossDomainMessenger.address, Fake__L2CrossDomainMessenger.address,
newGasLimit, newGasLimit,
encodeXDomainCalldata(target, sender, message, queueIndex), encodeXDomainCalldata(target, signer1.address, message, queueIndex),
newQueueIndex, newQueueIndex,
newTimestamp newTimestamp
) )
...@@ -341,7 +299,7 @@ describe('L1CrossDomainMessenger', () => { ...@@ -341,7 +299,7 @@ describe('L1CrossDomainMessenger', () => {
await expect( await expect(
L1CrossDomainMessenger.replayMessage( L1CrossDomainMessenger.replayMessage(
target, target,
await signer.getAddress(), signer1.address,
message, message,
queueLength - 1, queueLength - 1,
oldGasLimit, oldGasLimit,
...@@ -352,24 +310,22 @@ describe('L1CrossDomainMessenger', () => { ...@@ -352,24 +310,22 @@ describe('L1CrossDomainMessenger', () => {
}) })
describe('xDomainMessageSender', () => { describe('xDomainMessageSender', () => {
let Mock__Factory__L1CrossDomainMessenger: MockContractFactory<ContractFactory> let Mock__L1CrossDomainMessenger: MockContract<Contract>
let Mock__L1CrossDomainMessenger
before(async () => { before(async () => {
Mock__Factory__L1CrossDomainMessenger = await smock.mock( Mock__L1CrossDomainMessenger = await (
'L1CrossDomainMessenger' await smock.mock('L1CrossDomainMessenger')
) ).deploy()
Mock__L1CrossDomainMessenger =
await Mock__Factory__L1CrossDomainMessenger.deploy()
}) })
it('should return the xDomainMsgSender address', async () => { it('should return the xDomainMsgSender address', async () => {
await Mock__L1CrossDomainMessenger.setVariable( await Mock__L1CrossDomainMessenger.setVariable(
'xDomainMsgSender', 'xDomainMsgSender',
'0x0000000000000000000000000000000000000000' NON_ZERO_ADDRESS
) )
expect( expect(
await Mock__L1CrossDomainMessenger.xDomainMessageSender() await Mock__L1CrossDomainMessenger.xDomainMessageSender()
).to.equal('0x0000000000000000000000000000000000000000') ).to.equal(NON_ZERO_ADDRESS)
}) })
}) })
...@@ -390,10 +346,17 @@ describe('L1CrossDomainMessenger', () => { ...@@ -390,10 +346,17 @@ describe('L1CrossDomainMessenger', () => {
) )
const storageKey = ethers.utils.keccak256( const storageKey = ethers.utils.keccak256(
ethers.utils.hexConcat([
ethers.utils.keccak256( ethers.utils.keccak256(
calldata + remove0x(Fake__L2CrossDomainMessenger.address) ethers.utils.hexConcat([
) + '00'.repeat(32) calldata,
Fake__L2CrossDomainMessenger.address,
])
),
ethers.constants.HashZero,
])
) )
const storageGenerator = await TrieTestGenerator.fromNodes({ const storageGenerator = await TrieTestGenerator.fromNodes({
nodes: [ nodes: [
{ {
...@@ -437,7 +400,6 @@ describe('L1CrossDomainMessenger', () => { ...@@ -437,7 +400,6 @@ describe('L1CrossDomainMessenger', () => {
describe('relayMessage', () => { describe('relayMessage', () => {
let target: string let target: string
let sender: string
let message: string let message: string
let proof: any let proof: any
let calldata: string let calldata: string
...@@ -446,11 +408,10 @@ describe('L1CrossDomainMessenger', () => { ...@@ -446,11 +408,10 @@ describe('L1CrossDomainMessenger', () => {
message = Fake__TargetContract.interface.encodeFunctionData('setTarget', [ message = Fake__TargetContract.interface.encodeFunctionData('setTarget', [
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
]) ])
sender = await signer.getAddress()
const mockProof = await generateMockRelayMessageProof( const mockProof = await generateMockRelayMessageProof(
target, target,
sender, signer1.address,
message message
) )
proof = mockProof.proof proof = mockProof.proof
...@@ -474,21 +435,27 @@ describe('L1CrossDomainMessenger', () => { ...@@ -474,21 +435,27 @@ describe('L1CrossDomainMessenger', () => {
} }
await expect( await expect(
L1CrossDomainMessenger.relayMessage(target, sender, message, 0, proof1) L1CrossDomainMessenger.relayMessage(
target,
signer1.address,
message,
0,
proof1
)
).to.be.revertedWith('Provided message could not be verified.') ).to.be.revertedWith('Provided message could not be verified.')
}) })
it('should revert if attempting to relay a message sent to an L1 system contract', async () => { it('should revert if attempting to relay a message sent to an L1 system contract', async () => {
const maliciousProof = await generateMockRelayMessageProof( const maliciousProof = await generateMockRelayMessageProof(
CanonicalTransactionChain.address, CanonicalTransactionChain.address,
sender, signer1.address,
message message
) )
await expect( await expect(
L1CrossDomainMessenger.relayMessage( L1CrossDomainMessenger.relayMessage(
CanonicalTransactionChain.address, CanonicalTransactionChain.address,
sender, signer1.address,
message, message,
0, 0,
maliciousProof.proof maliciousProof.proof
...@@ -510,25 +477,43 @@ describe('L1CrossDomainMessenger', () => { ...@@ -510,25 +477,43 @@ describe('L1CrossDomainMessenger', () => {
} }
await expect( await expect(
L1CrossDomainMessenger.relayMessage(target, sender, message, 0, proof1) L1CrossDomainMessenger.relayMessage(
target,
signer1.address,
message,
0,
proof1
)
).to.be.revertedWith('Provided message could not be verified.') ).to.be.revertedWith('Provided message could not be verified.')
}) })
it('should revert if provided an invalid storage trie witness', async () => { it('should revert if provided an invalid storage trie witness', async () => {
await expect( await expect(
L1CrossDomainMessenger.relayMessage(target, sender, message, 0, { L1CrossDomainMessenger.relayMessage(
target,
signer1.address,
message,
0,
{
...proof, ...proof,
storageTrieWitness: '0x', storageTrieWitness: '0x',
}) }
)
).to.be.reverted ).to.be.reverted
}) })
it('should revert if provided an invalid state trie witness', async () => { it('should revert if provided an invalid state trie witness', async () => {
await expect( await expect(
L1CrossDomainMessenger.relayMessage(target, sender, message, 0, { L1CrossDomainMessenger.relayMessage(
target,
signer1.address,
message,
0,
{
...proof, ...proof,
stateTrieWitness: '0x', stateTrieWitness: '0x',
}) }
)
).to.be.reverted ).to.be.reverted
}) })
...@@ -537,7 +522,7 @@ describe('L1CrossDomainMessenger', () => { ...@@ -537,7 +522,7 @@ describe('L1CrossDomainMessenger', () => {
await L1CrossDomainMessenger.relayMessage( await L1CrossDomainMessenger.relayMessage(
target, target,
sender, signer1.address,
message, message,
0, 0,
proof proof
...@@ -552,12 +537,14 @@ describe('L1CrossDomainMessenger', () => { ...@@ -552,12 +537,14 @@ describe('L1CrossDomainMessenger', () => {
expect( expect(
await L1CrossDomainMessenger.relayedMessages( await L1CrossDomainMessenger.relayedMessages(
ethers.utils.keccak256( ethers.utils.keccak256(
calldata + ethers.utils.hexConcat([
remove0x(await signer.getAddress()) + calldata,
remove0x(BigNumber.from(blockNumber).toHexString()).padStart( signer1.address,
64, ethers.utils.hexZeroPad(
'0' BigNumber.from(blockNumber).toHexString(),
) 32
),
])
) )
) )
).to.equal(true) ).to.equal(true)
...@@ -567,13 +554,15 @@ describe('L1CrossDomainMessenger', () => { ...@@ -567,13 +554,15 @@ describe('L1CrossDomainMessenger', () => {
await expect( await expect(
L1CrossDomainMessenger.xDomainMessageSender() L1CrossDomainMessenger.xDomainMessageSender()
).to.be.revertedWith('xDomainMessageSender is not set') ).to.be.revertedWith('xDomainMessageSender is not set')
await L1CrossDomainMessenger.relayMessage( await L1CrossDomainMessenger.relayMessage(
target, target,
sender, signer1.address,
message, message,
0, 0,
proof proof
) )
await expect( await expect(
L1CrossDomainMessenger.xDomainMessageSender() L1CrossDomainMessenger.xDomainMessageSender()
).to.be.revertedWith('xDomainMessageSender is not set') ).to.be.revertedWith('xDomainMessageSender is not set')
...@@ -582,14 +571,20 @@ describe('L1CrossDomainMessenger', () => { ...@@ -582,14 +571,20 @@ describe('L1CrossDomainMessenger', () => {
it('should revert if trying to send the same message twice', async () => { it('should revert if trying to send the same message twice', async () => {
await L1CrossDomainMessenger.relayMessage( await L1CrossDomainMessenger.relayMessage(
target, target,
sender, signer1.address,
message, message,
0, 0,
proof proof
) )
await expect( await expect(
L1CrossDomainMessenger.relayMessage(target, sender, message, 0, proof) L1CrossDomainMessenger.relayMessage(
target,
signer1.address,
message,
0,
proof
)
).to.be.revertedWith('Provided message has already been received.') ).to.be.revertedWith('Provided message has already been received.')
}) })
...@@ -597,13 +592,20 @@ describe('L1CrossDomainMessenger', () => { ...@@ -597,13 +592,20 @@ describe('L1CrossDomainMessenger', () => {
await L1CrossDomainMessenger.pause() await L1CrossDomainMessenger.pause()
await expect( await expect(
L1CrossDomainMessenger.relayMessage(target, sender, message, 0, proof) L1CrossDomainMessenger.relayMessage(
target,
signer1.address,
message,
0,
proof
)
).to.be.revertedWith('Pausable: paused') ).to.be.revertedWith('Pausable: paused')
}) })
describe('blockMessage and allowMessage', () => { describe('blockMessage and allowMessage', () => {
it('should revert if called by an account other than the owner', async () => { it('should revert if called by an account other than the owner', async () => {
const L1CrossDomainMessenger2 = L1CrossDomainMessenger.connect(signer2) const L1CrossDomainMessenger2 = L1CrossDomainMessenger.connect(signer2)
await expect( await expect(
L1CrossDomainMessenger2.blockMessage(ethers.utils.keccak256(calldata)) L1CrossDomainMessenger2.blockMessage(ethers.utils.keccak256(calldata))
).to.be.revertedWith('Ownable: caller is not the owner') ).to.be.revertedWith('Ownable: caller is not the owner')
...@@ -619,7 +621,13 @@ describe('L1CrossDomainMessenger', () => { ...@@ -619,7 +621,13 @@ describe('L1CrossDomainMessenger', () => {
) )
await expect( await expect(
L1CrossDomainMessenger.relayMessage(target, sender, message, 0, proof) L1CrossDomainMessenger.relayMessage(
target,
signer1.address,
message,
0,
proof
)
).to.be.revertedWith('Provided message has been blocked.') ).to.be.revertedWith('Provided message has been blocked.')
}) })
...@@ -629,7 +637,13 @@ describe('L1CrossDomainMessenger', () => { ...@@ -629,7 +637,13 @@ describe('L1CrossDomainMessenger', () => {
) )
await expect( await expect(
L1CrossDomainMessenger.relayMessage(target, sender, message, 0, proof) L1CrossDomainMessenger.relayMessage(
target,
signer1.address,
message,
0,
proof
)
).to.be.revertedWith('Provided message has been blocked.') ).to.be.revertedWith('Provided message has been blocked.')
await L1CrossDomainMessenger.allowMessage( await L1CrossDomainMessenger.allowMessage(
...@@ -637,7 +651,13 @@ describe('L1CrossDomainMessenger', () => { ...@@ -637,7 +651,13 @@ describe('L1CrossDomainMessenger', () => {
) )
await expect( await expect(
L1CrossDomainMessenger.relayMessage(target, sender, message, 0, proof) L1CrossDomainMessenger.relayMessage(
target,
signer1.address,
message,
0,
proof
)
).to.not.be.reverted ).to.not.be.reverted
}) })
}) })
......
/* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract, constants } from 'ethers' import { Signer, Contract, constants } from 'ethers'
import { Interface } from 'ethers/lib/utils' import { smock, FakeContract, MockContract } from '@defi-wonderland/smock'
import { import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
smock,
MockContractFactory,
FakeContract,
MockContract,
} from '@defi-wonderland/smock'
/* Internal Imports */
import { expect } from '../../../setup' import { expect } from '../../../setup'
import { NON_NULL_BYTES32, NON_ZERO_ADDRESS } from '../../../helpers' import { NON_NULL_BYTES32, NON_ZERO_ADDRESS, deploy } from '../../../helpers'
import { getContractInterface, predeploys } from '../../../../src' import { getContractInterface, predeploys } from '../../../../src'
const ERR_INVALID_MESSENGER = 'OVM_XCHAIN: messenger contract unauthenticated' // TODO: Maybe we should consider automatically generating these and exporting them?
const ERR_INVALID_X_DOMAIN_MSG_SENDER = const ERROR_STRINGS = {
'OVM_XCHAIN: wrong sender of cross-domain message' INVALID_MESSENGER: 'OVM_XCHAIN: messenger contract unauthenticated',
const ERR_ALREADY_INITIALIZED = 'Contract has already been initialized.' INVALID_X_DOMAIN_MSG_SENDER:
const DUMMY_L2_ERC20_ADDRESS = ethers.utils.getAddress('0x' + 'abba'.repeat(10)) 'OVM_XCHAIN: wrong sender of cross-domain message',
const DUMMY_L2_BRIDGE_ADDRESS = ethers.utils.getAddress( ALREADY_INITIALIZED: 'Contract has already been initialized.',
'0x' + 'acdc'.repeat(10) }
)
const DUMMY_L2_ERC20_ADDRESS = '0xaBBAABbaaBbAABbaABbAABbAABbaAbbaaBbaaBBa'
const DUMMY_L2_BRIDGE_ADDRESS = '0xACDCacDcACdCaCDcacdcacdCaCdcACdCAcDcaCdc'
const INITIAL_TOTAL_L1_SUPPLY = 5000 const INITIAL_TOTAL_L1_SUPPLY = 5000
const FINALIZATION_GAS = 1_200_000 const FINALIZATION_GAS = 1_200_000
describe('L1StandardBridge', () => { describe('L1StandardBridge', () => {
// init signers
let l1MessengerImpersonator: Signer let l1MessengerImpersonator: Signer
let alice: Signer let alice: SignerWithAddress
let bob: Signer let bob: SignerWithAddress
let bobsAddress
let aliceAddress
// we can just make up this string since it's on the "other" Layer
let Factory__L1ERC20: MockContractFactory<ContractFactory>
let IL2ERC20Bridge: Interface
before(async () => { before(async () => {
;[l1MessengerImpersonator, alice, bob] = await ethers.getSigners() ;[l1MessengerImpersonator, alice, bob] = await ethers.getSigners()
await smock.fake<Contract>(await ethers.getContractFactory('OVM_ETH'))
// deploy an ERC20 contract on L1
Factory__L1ERC20 = await smock.mock(
'@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20'
)
// get an L2ER20Bridge Interface
IL2ERC20Bridge = getContractInterface('IL2ERC20Bridge')
aliceAddress = await alice.getAddress()
bobsAddress = await bob.getAddress()
}) })
let L1ERC20: MockContract<Contract> let L1ERC20: MockContract<Contract>
...@@ -60,24 +34,23 @@ describe('L1StandardBridge', () => { ...@@ -60,24 +34,23 @@ describe('L1StandardBridge', () => {
beforeEach(async () => { beforeEach(async () => {
// Get a new mock L1 messenger // Get a new mock L1 messenger
Fake__L1CrossDomainMessenger = await smock.fake<Contract>( Fake__L1CrossDomainMessenger = await smock.fake<Contract>(
await ethers.getContractFactory('L1CrossDomainMessenger'), 'L1CrossDomainMessenger',
{ address: await l1MessengerImpersonator.getAddress() } // This allows us to use an ethers override {from: Mock__L2CrossDomainMessenger.address} to mock calls { address: await l1MessengerImpersonator.getAddress() } // This allows us to use an ethers override {from: Mock__L2CrossDomainMessenger.address} to mock calls
) )
// Deploy the contract under test // Deploy the contract under test
L1StandardBridge = await ( L1StandardBridge = await deploy('L1StandardBridge')
await ethers.getContractFactory('L1StandardBridge')
).deploy()
await L1StandardBridge.initialize( await L1StandardBridge.initialize(
Fake__L1CrossDomainMessenger.address, Fake__L1CrossDomainMessenger.address,
DUMMY_L2_BRIDGE_ADDRESS DUMMY_L2_BRIDGE_ADDRESS
) )
L1ERC20 = await Factory__L1ERC20.deploy('L1ERC20', 'ERC') L1ERC20 = await (
await smock.mock('@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20')
).deploy('L1ERC20', 'ERC')
await L1ERC20.setVariable('_totalSupply', INITIAL_TOTAL_L1_SUPPLY) await L1ERC20.setVariable('_totalSupply', INITIAL_TOTAL_L1_SUPPLY)
await L1ERC20.setVariable('_balances', { await L1ERC20.setVariable('_balances', {
[aliceAddress]: INITIAL_TOTAL_L1_SUPPLY, [alice.address]: INITIAL_TOTAL_L1_SUPPLY,
}) })
}) })
...@@ -88,7 +61,7 @@ describe('L1StandardBridge', () => { ...@@ -88,7 +61,7 @@ describe('L1StandardBridge', () => {
ethers.constants.AddressZero, ethers.constants.AddressZero,
DUMMY_L2_BRIDGE_ADDRESS DUMMY_L2_BRIDGE_ADDRESS
) )
).to.be.revertedWith(ERR_ALREADY_INITIALIZED) ).to.be.revertedWith(ERROR_STRINGS.ALREADY_INITIALIZED)
}) })
}) })
...@@ -107,8 +80,7 @@ describe('L1StandardBridge', () => { ...@@ -107,8 +80,7 @@ describe('L1StandardBridge', () => {
const depositAmount = 1_000 const depositAmount = 1_000
it('depositETH() escrows the deposit amount and sends the correct deposit message', async () => { it('depositETH() escrows the deposit amount and sends the correct deposit message', async () => {
const depositer = await alice.getAddress() const initialBalance = await alice.getBalance()
const initialBalance = await ethers.provider.getBalance(depositer)
// alice calls deposit on the bridge and the L1 bridge calls transferFrom on the token // alice calls deposit on the bridge and the L1 bridge calls transferFrom on the token
const res = await L1StandardBridge.connect(alice).depositETH( const res = await L1StandardBridge.connect(alice).depositETH(
...@@ -119,92 +91,80 @@ describe('L1StandardBridge', () => { ...@@ -119,92 +91,80 @@ describe('L1StandardBridge', () => {
} }
) )
const depositCallToMessenger = expect(
Fake__L1CrossDomainMessenger.sendMessage.getCall(0) Fake__L1CrossDomainMessenger.sendMessage.getCall(0).args
).to.deep.equal([
DUMMY_L2_BRIDGE_ADDRESS,
getContractInterface('IL2ERC20Bridge').encodeFunctionData(
'finalizeDeposit',
[
constants.AddressZero,
predeploys.OVM_ETH,
alice.address,
alice.address,
depositAmount,
NON_NULL_BYTES32,
]
),
FINALIZATION_GAS,
])
const depositerBalance = await ethers.provider.getBalance(depositer)
const receipt = await res.wait() const receipt = await res.wait()
const depositerFeePaid = receipt.cumulativeGasUsed.mul( const depositerFeePaid = receipt.cumulativeGasUsed.mul(
receipt.effectiveGasPrice receipt.effectiveGasPrice
) )
expect(depositerBalance).to.equal( expect(await alice.getBalance()).to.equal(
initialBalance.sub(depositAmount).sub(depositerFeePaid) initialBalance.sub(depositAmount).sub(depositerFeePaid)
) )
// bridge's balance is increased expect(
const bridgeBalance = await ethers.provider.getBalance( await ethers.provider.getBalance(L1StandardBridge.address)
L1StandardBridge.address ).to.equal(depositAmount)
)
expect(bridgeBalance).to.equal(depositAmount)
// Check the correct cross-chain call was sent:
// Message should be sent to the L2 bridge
expect(depositCallToMessenger.args[0]).to.equal(DUMMY_L2_BRIDGE_ADDRESS)
// Message data should be a call telling the L2ETHToken to finalize the deposit
// the L1 bridge sends the correct message to the L1 messenger
expect(depositCallToMessenger.args[1]).to.equal(
IL2ERC20Bridge.encodeFunctionData('finalizeDeposit', [
constants.AddressZero,
predeploys.OVM_ETH,
depositer,
depositer,
depositAmount,
NON_NULL_BYTES32,
])
)
expect(depositCallToMessenger.args[2]).to.equal(FINALIZATION_GAS)
}) })
it('depositETHTo() escrows the deposit amount and sends the correct deposit message', async () => { it('depositETHTo() escrows the deposit amount and sends the correct deposit message', async () => {
// depositor calls deposit on the bridge and the L1 bridge calls transferFrom on the token const initialBalance = await alice.getBalance()
const initialBalance = await ethers.provider.getBalance(aliceAddress)
const res = await L1StandardBridge.connect(alice).depositETHTo( const res = await L1StandardBridge.connect(alice).depositETHTo(
bobsAddress, bob.address,
FINALIZATION_GAS, FINALIZATION_GAS,
NON_NULL_BYTES32, NON_NULL_BYTES32,
{ {
value: depositAmount, value: depositAmount,
} }
) )
const depositCallToMessenger =
Fake__L1CrossDomainMessenger.sendMessage.getCall(0)
const depositerBalance = await ethers.provider.getBalance(aliceAddress) expect(
Fake__L1CrossDomainMessenger.sendMessage.getCall(0).args
).to.deep.equal([
DUMMY_L2_BRIDGE_ADDRESS,
getContractInterface('IL2ERC20Bridge').encodeFunctionData(
'finalizeDeposit',
[
constants.AddressZero,
predeploys.OVM_ETH,
alice.address,
bob.address,
depositAmount,
NON_NULL_BYTES32,
]
),
FINALIZATION_GAS,
])
const receipt = await res.wait() const receipt = await res.wait()
const depositerFeePaid = receipt.cumulativeGasUsed.mul( const depositerFeePaid = receipt.cumulativeGasUsed.mul(
receipt.effectiveGasPrice receipt.effectiveGasPrice
) )
expect(depositerBalance).to.equal( expect(await alice.getBalance()).to.equal(
initialBalance.sub(depositAmount).sub(depositerFeePaid) initialBalance.sub(depositAmount).sub(depositerFeePaid)
) )
// bridge's balance is increased expect(
const bridgeBalance = await ethers.provider.getBalance( await ethers.provider.getBalance(L1StandardBridge.address)
L1StandardBridge.address ).to.equal(depositAmount)
)
expect(bridgeBalance).to.equal(depositAmount)
// Check the correct cross-chain call was sent:
// Message should be sent to the L2 bridge
expect(depositCallToMessenger.args[0]).to.equal(DUMMY_L2_BRIDGE_ADDRESS)
// Message data should be a call telling the L2ETHToken to finalize the deposit
// the L1 bridge sends the correct message to the L1 messenger
expect(depositCallToMessenger.args[1]).to.equal(
IL2ERC20Bridge.encodeFunctionData('finalizeDeposit', [
constants.AddressZero,
predeploys.OVM_ETH,
aliceAddress,
bobsAddress,
depositAmount,
NON_NULL_BYTES32,
])
)
expect(depositCallToMessenger.args[2]).to.equal(FINALIZATION_GAS)
}) })
it('cannot depositETH from a contract account', async () => { it('cannot depositETH from a contract account', async () => {
...@@ -218,29 +178,17 @@ describe('L1StandardBridge', () => { ...@@ -218,29 +178,17 @@ describe('L1StandardBridge', () => {
describe('ETH withdrawals', () => { describe('ETH withdrawals', () => {
it('onlyFromCrossDomainAccount: should revert on calls from a non-crossDomainMessenger L1 account', async () => { it('onlyFromCrossDomainAccount: should revert on calls from a non-crossDomainMessenger L1 account', async () => {
// Deploy new bridge, initialize with random messenger
await expect( await expect(
L1StandardBridge.connect(alice).finalizeETHWithdrawal( L1StandardBridge.connect(alice).finalizeETHWithdrawal(
constants.AddressZero, constants.AddressZero,
constants.AddressZero, constants.AddressZero,
1, 1,
NON_NULL_BYTES32, NON_NULL_BYTES32
{
from: aliceAddress,
}
) )
).to.be.revertedWith(ERR_INVALID_MESSENGER) ).to.be.revertedWith(ERROR_STRINGS.INVALID_MESSENGER)
}) })
it('onlyFromCrossDomainAccount: should revert on calls from the right crossDomainMessenger, but wrong xDomainMessageSender (ie. not the L2ETHToken)', async () => { it('onlyFromCrossDomainAccount: should revert on calls from the right crossDomainMessenger, but wrong xDomainMessageSender (ie. not the L2ETHToken)', async () => {
L1StandardBridge = await (
await ethers.getContractFactory('L1StandardBridge')
).deploy()
await L1StandardBridge.initialize(
Fake__L1CrossDomainMessenger.address,
DUMMY_L2_BRIDGE_ADDRESS
)
Fake__L1CrossDomainMessenger.xDomainMessageSender.returns( Fake__L1CrossDomainMessenger.xDomainMessageSender.returns(
'0x' + '22'.repeat(20) '0x' + '22'.repeat(20)
) )
...@@ -255,23 +203,21 @@ describe('L1StandardBridge', () => { ...@@ -255,23 +203,21 @@ describe('L1StandardBridge', () => {
from: Fake__L1CrossDomainMessenger.address, from: Fake__L1CrossDomainMessenger.address,
} }
) )
).to.be.revertedWith(ERR_INVALID_X_DOMAIN_MSG_SENDER) ).to.be.revertedWith(ERROR_STRINGS.INVALID_X_DOMAIN_MSG_SENDER)
}) })
it('should revert in nothing to withdraw', async () => { it('should revert in nothing to withdraw', async () => {
// make sure no balance at start of test
expect(await ethers.provider.getBalance(NON_ZERO_ADDRESS)).to.be.equal(0) expect(await ethers.provider.getBalance(NON_ZERO_ADDRESS)).to.be.equal(0)
const withdrawalAmount = 100
Fake__L1CrossDomainMessenger.xDomainMessageSender.returns( Fake__L1CrossDomainMessenger.xDomainMessageSender.returns(
() => DUMMY_L2_BRIDGE_ADDRESS DUMMY_L2_BRIDGE_ADDRESS
) )
await expect( await expect(
L1StandardBridge.finalizeETHWithdrawal( L1StandardBridge.finalizeETHWithdrawal(
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
withdrawalAmount, 100,
NON_NULL_BYTES32, NON_NULL_BYTES32,
{ {
from: Fake__L1CrossDomainMessenger.address, from: Fake__L1CrossDomainMessenger.address,
...@@ -283,15 +229,13 @@ describe('L1StandardBridge', () => { ...@@ -283,15 +229,13 @@ describe('L1StandardBridge', () => {
}) })
it('should credit funds to the withdrawer and not use too much gas', async () => { it('should credit funds to the withdrawer and not use too much gas', async () => {
// make sure no balance at start of test
expect(await ethers.provider.getBalance(NON_ZERO_ADDRESS)).to.be.equal(0) expect(await ethers.provider.getBalance(NON_ZERO_ADDRESS)).to.be.equal(0)
const withdrawalAmount = 100 const withdrawalAmount = 100
Fake__L1CrossDomainMessenger.xDomainMessageSender.returns( Fake__L1CrossDomainMessenger.xDomainMessageSender.returns(
() => DUMMY_L2_BRIDGE_ADDRESS DUMMY_L2_BRIDGE_ADDRESS
) )
// thanks Alice
await L1StandardBridge.connect(alice).depositETH( await L1StandardBridge.connect(alice).depositETH(
FINALIZATION_GAS, FINALIZATION_GAS,
NON_NULL_BYTES32, NON_NULL_BYTES32,
...@@ -327,7 +271,6 @@ describe('L1StandardBridge', () => { ...@@ -327,7 +271,6 @@ describe('L1StandardBridge', () => {
}) })
it('depositERC20() escrows the deposit amount and sends the correct deposit message', async () => { it('depositERC20() escrows the deposit amount and sends the correct deposit message', async () => {
// alice calls deposit on the bridge and the L1 bridge calls transferFrom on the token
await L1StandardBridge.connect(alice).depositERC20( await L1StandardBridge.connect(alice).depositERC20(
L1ERC20.address, L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS, DUMMY_L2_ERC20_ADDRESS,
...@@ -336,73 +279,68 @@ describe('L1StandardBridge', () => { ...@@ -336,73 +279,68 @@ describe('L1StandardBridge', () => {
NON_NULL_BYTES32 NON_NULL_BYTES32
) )
const depositCallToMessenger = expect(
Fake__L1CrossDomainMessenger.sendMessage.getCall(0) Fake__L1CrossDomainMessenger.sendMessage.getCall(0).args
).to.deep.equal([
const depositerBalance = await L1ERC20.balanceOf(aliceAddress) DUMMY_L2_BRIDGE_ADDRESS,
getContractInterface('IL2ERC20Bridge').encodeFunctionData(
expect(depositerBalance).to.equal(INITIAL_TOTAL_L1_SUPPLY - depositAmount) 'finalizeDeposit',
[
// bridge's balance is increased
const bridgeBalance = await L1ERC20.balanceOf(L1StandardBridge.address)
expect(bridgeBalance).to.equal(depositAmount)
// Check the correct cross-chain call was sent:
// Message should be sent to the L2 bridge
expect(depositCallToMessenger.args[0]).to.equal(DUMMY_L2_BRIDGE_ADDRESS)
// Message data should be a call telling the L2DepositedERC20 to finalize the deposit
// the L1 bridge sends the correct message to the L1 messenger
expect(depositCallToMessenger.args[1]).to.equal(
IL2ERC20Bridge.encodeFunctionData('finalizeDeposit', [
L1ERC20.address, L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS, DUMMY_L2_ERC20_ADDRESS,
aliceAddress, alice.address,
aliceAddress, alice.address,
depositAmount, depositAmount,
NON_NULL_BYTES32, NON_NULL_BYTES32,
]
),
FINALIZATION_GAS,
]) ])
expect(await L1ERC20.balanceOf(alice.address)).to.equal(
INITIAL_TOTAL_L1_SUPPLY - depositAmount
)
expect(await L1ERC20.balanceOf(L1StandardBridge.address)).to.equal(
depositAmount
) )
expect(depositCallToMessenger.args[2]).to.equal(FINALIZATION_GAS)
}) })
it('depositERC20To() escrows the deposit amount and sends the correct deposit message', async () => { it('depositERC20To() escrows the deposit amount and sends the correct deposit message', async () => {
// depositor calls deposit on the bridge and the L1 bridge calls transferFrom on the token
await L1StandardBridge.connect(alice).depositERC20To( await L1StandardBridge.connect(alice).depositERC20To(
L1ERC20.address, L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS, DUMMY_L2_ERC20_ADDRESS,
bobsAddress, bob.address,
depositAmount, depositAmount,
FINALIZATION_GAS, FINALIZATION_GAS,
NON_NULL_BYTES32 NON_NULL_BYTES32
) )
const depositCallToMessenger =
Fake__L1CrossDomainMessenger.sendMessage.getCall(0)
const depositerBalance = await L1ERC20.balanceOf(aliceAddress)
expect(depositerBalance).to.equal(INITIAL_TOTAL_L1_SUPPLY - depositAmount)
// bridge's balance is increased
const bridgeBalance = await L1ERC20.balanceOf(L1StandardBridge.address)
expect(bridgeBalance).to.equal(depositAmount)
// Check the correct cross-chain call was sent:
// Message should be sent to the L2DepositedERC20 on L2
expect(depositCallToMessenger.args[0]).to.equal(DUMMY_L2_BRIDGE_ADDRESS)
// Message data should be a call telling the L2DepositedERC20 to finalize the deposit
// the L1 bridge sends the correct message to the L1 messenger expect(
expect(depositCallToMessenger.args[1]).to.equal( Fake__L1CrossDomainMessenger.sendMessage.getCall(0).args
IL2ERC20Bridge.encodeFunctionData('finalizeDeposit', [ ).to.deep.equal([
DUMMY_L2_BRIDGE_ADDRESS,
getContractInterface('IL2ERC20Bridge').encodeFunctionData(
'finalizeDeposit',
[
L1ERC20.address, L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS, DUMMY_L2_ERC20_ADDRESS,
aliceAddress, alice.address,
bobsAddress, bob.address,
depositAmount, depositAmount,
NON_NULL_BYTES32, NON_NULL_BYTES32,
]
),
FINALIZATION_GAS,
]) ])
expect(await L1ERC20.balanceOf(alice.address)).to.equal(
INITIAL_TOTAL_L1_SUPPLY - depositAmount
)
expect(await L1ERC20.balanceOf(L1StandardBridge.address)).to.equal(
depositAmount
) )
expect(depositCallToMessenger.args[2]).to.equal(FINALIZATION_GAS)
}) })
it('cannot depositERC20 from a contract account', async () => { it('cannot depositERC20 from a contract account', async () => {
...@@ -419,12 +357,8 @@ describe('L1StandardBridge', () => { ...@@ -419,12 +357,8 @@ describe('L1StandardBridge', () => {
describe('Handling ERC20.transferFrom() failures that revert ', () => { describe('Handling ERC20.transferFrom() failures that revert ', () => {
let Fake__L1ERC20: FakeContract let Fake__L1ERC20: FakeContract
before(async () => { before(async () => {
// Deploy the L1 ERC20 token, Alice will receive the full initialSupply Fake__L1ERC20 = await smock.fake<Contract>('ERC20')
Fake__L1ERC20 = await smock.fake<Contract>(
await Factory__L1ERC20.deploy('L1ERC20', 'ERC')
)
Fake__L1ERC20.transferFrom.reverts() Fake__L1ERC20.transferFrom.reverts()
}) })
...@@ -445,7 +379,7 @@ describe('L1StandardBridge', () => { ...@@ -445,7 +379,7 @@ describe('L1StandardBridge', () => {
L1StandardBridge.connect(alice).depositERC20To( L1StandardBridge.connect(alice).depositERC20To(
Fake__L1ERC20.address, Fake__L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS, DUMMY_L2_ERC20_ADDRESS,
bobsAddress, bob.address,
depositAmount, depositAmount,
FINALIZATION_GAS, FINALIZATION_GAS,
NON_NULL_BYTES32 NON_NULL_BYTES32
...@@ -458,7 +392,7 @@ describe('L1StandardBridge', () => { ...@@ -458,7 +392,7 @@ describe('L1StandardBridge', () => {
L1StandardBridge.connect(alice).depositERC20To( L1StandardBridge.connect(alice).depositERC20To(
ethers.constants.AddressZero, ethers.constants.AddressZero,
DUMMY_L2_ERC20_ADDRESS, DUMMY_L2_ERC20_ADDRESS,
bobsAddress, bob.address,
depositAmount, depositAmount,
FINALIZATION_GAS, FINALIZATION_GAS,
NON_NULL_BYTES32 NON_NULL_BYTES32
...@@ -470,9 +404,7 @@ describe('L1StandardBridge', () => { ...@@ -470,9 +404,7 @@ describe('L1StandardBridge', () => {
describe('Handling ERC20.transferFrom failures that return false', () => { describe('Handling ERC20.transferFrom failures that return false', () => {
let Fake__L1ERC20: FakeContract let Fake__L1ERC20: FakeContract
before(async () => { before(async () => {
Fake__L1ERC20 = await smock.fake( Fake__L1ERC20 = await smock.fake('ERC20')
await Factory__L1ERC20.deploy('L1ERC20', 'ERC')
)
Fake__L1ERC20.transferFrom.returns(false) Fake__L1ERC20.transferFrom.returns(false)
}) })
...@@ -493,7 +425,7 @@ describe('L1StandardBridge', () => { ...@@ -493,7 +425,7 @@ describe('L1StandardBridge', () => {
L1StandardBridge.depositERC20To( L1StandardBridge.depositERC20To(
Fake__L1ERC20.address, Fake__L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS, DUMMY_L2_ERC20_ADDRESS,
bobsAddress, bob.address,
depositAmount, depositAmount,
FINALIZATION_GAS, FINALIZATION_GAS,
NON_NULL_BYTES32 NON_NULL_BYTES32
...@@ -514,12 +446,12 @@ describe('L1StandardBridge', () => { ...@@ -514,12 +446,12 @@ describe('L1StandardBridge', () => {
1, 1,
NON_NULL_BYTES32 NON_NULL_BYTES32
) )
).to.be.revertedWith(ERR_INVALID_MESSENGER) ).to.be.revertedWith(ERROR_STRINGS.INVALID_MESSENGER)
}) })
it('onlyFromCrossDomainAccount: should revert on calls from the right crossDomainMessenger, but wrong xDomainMessageSender (ie. not the L2DepositedERC20)', async () => { it('onlyFromCrossDomainAccount: should revert on calls from the right crossDomainMessenger, but wrong xDomainMessageSender (ie. not the L2DepositedERC20)', async () => {
Fake__L1CrossDomainMessenger.xDomainMessageSender.returns( Fake__L1CrossDomainMessenger.xDomainMessageSender.returns(
() => NON_ZERO_ADDRESS NON_ZERO_ADDRESS
) )
await expect( await expect(
...@@ -534,7 +466,7 @@ describe('L1StandardBridge', () => { ...@@ -534,7 +466,7 @@ describe('L1StandardBridge', () => {
from: Fake__L1CrossDomainMessenger.address, from: Fake__L1CrossDomainMessenger.address,
} }
) )
).to.be.revertedWith(ERR_INVALID_X_DOMAIN_MSG_SENDER) ).to.be.revertedWith(ERROR_STRINGS.INVALID_X_DOMAIN_MSG_SENDER)
}) })
it('should credit funds to the withdrawer and not use too much gas', async () => { it('should credit funds to the withdrawer and not use too much gas', async () => {
...@@ -561,7 +493,7 @@ describe('L1StandardBridge', () => { ...@@ -561,7 +493,7 @@ describe('L1StandardBridge', () => {
expect(await L1ERC20.balanceOf(NON_ZERO_ADDRESS)).to.be.equal(0) expect(await L1ERC20.balanceOf(NON_ZERO_ADDRESS)).to.be.equal(0)
Fake__L1CrossDomainMessenger.xDomainMessageSender.returns( Fake__L1CrossDomainMessenger.xDomainMessageSender.returns(
() => DUMMY_L2_BRIDGE_ADDRESS DUMMY_L2_BRIDGE_ADDRESS
) )
await L1StandardBridge.finalizeERC20Withdrawal( await L1StandardBridge.finalizeERC20Withdrawal(
......
/* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers' import { Contract } from 'ethers'
import { MockContract, smock } from '@defi-wonderland/smock' import { MockContract, smock } from '@defi-wonderland/smock'
import { expectApprox } from '@eth-optimism/core-utils' import { expectApprox } from '@eth-optimism/core-utils'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
/* Internal Imports */
import { import {
makeAddressManager, deploy,
L2_GAS_DISCOUNT_DIVISOR, L2_GAS_DISCOUNT_DIVISOR,
ENQUEUE_GAS_COST, ENQUEUE_GAS_COST,
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
...@@ -22,43 +21,33 @@ const INITIAL_TOTAL_L1_SUPPLY = 5000 ...@@ -22,43 +21,33 @@ const INITIAL_TOTAL_L1_SUPPLY = 5000
const FINALIZATION_GAS = 1_200_000 const FINALIZATION_GAS = 1_200_000
describe('[GAS BENCHMARK] Depositing via the standard bridge [ @skip-on-coverage ]', () => { describe('[GAS BENCHMARK] Depositing via the standard bridge [ @skip-on-coverage ]', () => {
let sequencer: Signer let sequencer: SignerWithAddress
let alice: Signer let alice: SignerWithAddress
before(async () => { before(async () => {
;[sequencer, alice] = await ethers.getSigners() ;[sequencer, alice] = await ethers.getSigners()
}) })
let AddressManager: Contract let AddressManager: Contract
before('Deploy address manager and register sequencer', async () => {
AddressManager = await makeAddressManager()
await AddressManager.setAddress(
'OVM_Sequencer',
await sequencer.getAddress()
)
})
let CanonicalTransactionChain: Contract let CanonicalTransactionChain: Contract
let Factory__ChainStorageContainer: ContractFactory before(async () => {
before('Init CTC and Storage Container contracts.', async () => { AddressManager = await deploy('Lib_AddressManager')
CanonicalTransactionChain = await (
await ethers.getContractFactory('CanonicalTransactionChain') CanonicalTransactionChain = await deploy('CanonicalTransactionChain', {
).deploy( args: [
AddressManager.address, AddressManager.address,
MAX_GAS_LIMIT, MAX_GAS_LIMIT,
L2_GAS_DISCOUNT_DIVISOR, L2_GAS_DISCOUNT_DIVISOR,
ENQUEUE_GAS_COST ENQUEUE_GAS_COST,
) ],
})
Factory__ChainStorageContainer = await ethers.getContractFactory( const batches = await deploy('ChainStorageContainer', {
'ChainStorageContainer' args: [AddressManager.address, 'CanonicalTransactionChain'],
) })
const batches = await Factory__ChainStorageContainer.deploy(
AddressManager.address, await AddressManager.setAddress(
'CanonicalTransactionChain' 'OVM_Sequencer',
) await sequencer.getAddress()
await Factory__ChainStorageContainer.deploy(
AddressManager.address,
'CanonicalTransactionChain'
) )
await AddressManager.setAddress( await AddressManager.setAddress(
...@@ -72,50 +61,43 @@ describe('[GAS BENCHMARK] Depositing via the standard bridge [ @skip-on-coverage ...@@ -72,50 +61,43 @@ describe('[GAS BENCHMARK] Depositing via the standard bridge [ @skip-on-coverage
) )
}) })
// 3 Messenger
let L1CrossDomainMessenger: Contract let L1CrossDomainMessenger: Contract
before('Deploy Messenger proxy and implementation', async () => { before(async () => {
// Deploy the implementation contract first const xDomainMessengerImpl = await deploy('L1CrossDomainMessenger')
const xDomainMessengerImpl = await (
await ethers.getContractFactory('L1CrossDomainMessenger')
).deploy()
await AddressManager.setAddress( await AddressManager.setAddress(
'L1CrossDomainMessenger', 'L1CrossDomainMessenger',
xDomainMessengerImpl.address xDomainMessengerImpl.address
) )
// Deploy and initialize the Proxy Messenger
const proxy = await ( const proxy = await deploy('Lib_ResolvedDelegateProxy', {
await ethers.getContractFactory('Lib_ResolvedDelegateProxy') args: [AddressManager.address, 'L1CrossDomainMessenger'],
).deploy(AddressManager.address, 'L1CrossDomainMessenger') })
L1CrossDomainMessenger = xDomainMessengerImpl.attach(proxy.address) L1CrossDomainMessenger = xDomainMessengerImpl.attach(proxy.address)
await L1CrossDomainMessenger.initialize(AddressManager.address) await L1CrossDomainMessenger.initialize(AddressManager.address)
}) })
// 4 Bridge
let L1ERC20: MockContract<Contract> let L1ERC20: MockContract<Contract>
let L1StandardBridge: Contract let L1StandardBridge: Contract
before('Deploy the bridge and setup the token', async () => { before('Deploy the bridge and setup the token', async () => {
// Deploy the Bridge L1StandardBridge = await deploy('L1StandardBridge')
L1StandardBridge = await (
await ethers.getContractFactory('L1StandardBridge')
).deploy()
await L1StandardBridge.initialize( await L1StandardBridge.initialize(
L1CrossDomainMessenger.address, L1CrossDomainMessenger.address,
NON_ZERO_ADDRESS NON_ZERO_ADDRESS
) )
L1ERC20 = await ( L1ERC20 = await (await smock.mock('ERC20')).deploy('L1ERC20', 'ERC')
await smock.mock('@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20')
).deploy('L1ERC20', 'ERC')
const aliceAddress = await alice.getAddress()
await L1ERC20.setVariable('_totalSupply', INITIAL_TOTAL_L1_SUPPLY) await L1ERC20.setVariable('_totalSupply', INITIAL_TOTAL_L1_SUPPLY)
await L1ERC20.setVariable('_balances', { await L1ERC20.setVariable('_balances', {
[aliceAddress]: INITIAL_TOTAL_L1_SUPPLY, [alice.address]: INITIAL_TOTAL_L1_SUPPLY,
}) })
}) })
describe('[GAS BENCHMARK] L1 to L2 Deposit costs [ @skip-on-coverage ]', async () => { describe('[GAS BENCHMARK] L1 to L2 Deposit costs [ @skip-on-coverage ]', async () => {
const depositAmount = 1_000 const depositAmount = 1_000
before(async () => { before(async () => {
// Load a transaction into the queue first to 'dirty' the buffer's length slot // Load a transaction into the queue first to 'dirty' the buffer's length slot
await CanonicalTransactionChain.enqueue( await CanonicalTransactionChain.enqueue(
...@@ -124,6 +106,7 @@ describe('[GAS BENCHMARK] Depositing via the standard bridge [ @skip-on-coverage ...@@ -124,6 +106,7 @@ describe('[GAS BENCHMARK] Depositing via the standard bridge [ @skip-on-coverage
'0x1234' '0x1234'
) )
}) })
it('cost to deposit ETH', async () => { it('cost to deposit ETH', async () => {
// Alice calls deposit on the bridge and the L1 bridge calls transferFrom on the token. // Alice calls deposit on the bridge and the L1 bridge calls transferFrom on the token.
const res = await L1StandardBridge.connect(alice).depositETH( const res = await L1StandardBridge.connect(alice).depositETH(
...@@ -137,12 +120,14 @@ describe('[GAS BENCHMARK] Depositing via the standard bridge [ @skip-on-coverage ...@@ -137,12 +120,14 @@ describe('[GAS BENCHMARK] Depositing via the standard bridge [ @skip-on-coverage
const receipt = await res.wait() const receipt = await res.wait()
const gasUsed = receipt.gasUsed.toNumber() const gasUsed = receipt.gasUsed.toNumber()
console.log(' - Gas used:', gasUsed) console.log(' - Gas used:', gasUsed)
expectApprox(gasUsed, 132_481, { expectApprox(gasUsed, 132_481, {
absoluteUpperDeviation: 500, absoluteUpperDeviation: 500,
// Assert a lower bound of 1% reduction on gas cost. If your tests are breaking because your // Assert a lower bound of 1% reduction on gas cost. If your tests are breaking because your
// contracts are too efficient, consider updating the target value! // contracts are too efficient, consider updating the target value!
percentLowerDeviation: 1, percentLowerDeviation: 1,
}) })
// Sanity check that the message was enqueued. // Sanity check that the message was enqueued.
expect(await CanonicalTransactionChain.getQueueLength()).to.equal(2) expect(await CanonicalTransactionChain.getQueueLength()).to.equal(2)
}) })
...@@ -152,6 +137,7 @@ describe('[GAS BENCHMARK] Depositing via the standard bridge [ @skip-on-coverage ...@@ -152,6 +137,7 @@ describe('[GAS BENCHMARK] Depositing via the standard bridge [ @skip-on-coverage
L1StandardBridge.address, L1StandardBridge.address,
depositAmount depositAmount
) )
// Alice calls deposit on the bridge and the L1 bridge calls transferFrom on the token. // Alice calls deposit on the bridge and the L1 bridge calls transferFrom on the token.
const res = await L1StandardBridge.connect(alice).depositERC20( const res = await L1StandardBridge.connect(alice).depositERC20(
L1ERC20.address, L1ERC20.address,
...@@ -160,9 +146,11 @@ describe('[GAS BENCHMARK] Depositing via the standard bridge [ @skip-on-coverage ...@@ -160,9 +146,11 @@ describe('[GAS BENCHMARK] Depositing via the standard bridge [ @skip-on-coverage
FINALIZATION_GAS, FINALIZATION_GAS,
NON_NULL_BYTES32 NON_NULL_BYTES32
) )
const receipt = await res.wait() const receipt = await res.wait()
const gasUsed = receipt.gasUsed.toNumber() const gasUsed = receipt.gasUsed.toNumber()
console.log(' - Gas used:', gasUsed) console.log(' - Gas used:', gasUsed)
expectApprox(gasUsed, 192_822, { expectApprox(gasUsed, 192_822, {
absoluteUpperDeviation: 500, absoluteUpperDeviation: 500,
// Assert a lower bound of 1% reduction on gas cost. If your tests are breaking because your // Assert a lower bound of 1% reduction on gas cost. If your tests are breaking because your
......
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers' import { Signer, Contract } from 'ethers'
import { smock, FakeContract } from '@defi-wonderland/smock' import { smock, FakeContract } from '@defi-wonderland/smock'
import { import {
AppendSequencerBatchParams, AppendSequencerBatchParams,
...@@ -9,11 +9,10 @@ import { ...@@ -9,11 +9,10 @@ import {
expectApprox, expectApprox,
} from '@eth-optimism/core-utils' } from '@eth-optimism/core-utils'
import { TransactionResponse } from '@ethersproject/abstract-provider' import { TransactionResponse } from '@ethersproject/abstract-provider'
import { keccak256 } from 'ethers/lib/utils'
/* Internal Imports */ /* Internal Imports */
import { import {
makeAddressManager, deploy,
setProxyTarget, setProxyTarget,
L2_GAS_DISCOUNT_DIVISOR, L2_GAS_DISCOUNT_DIVISOR,
ENQUEUE_GAS_COST, ENQUEUE_GAS_COST,
...@@ -31,11 +30,11 @@ const appendSequencerBatch = async ( ...@@ -31,11 +30,11 @@ const appendSequencerBatch = async (
CanonicalTransactionChain: Contract, CanonicalTransactionChain: Contract,
batch: AppendSequencerBatchParams batch: AppendSequencerBatchParams
): Promise<TransactionResponse> => { ): Promise<TransactionResponse> => {
const methodId = keccak256(Buffer.from('appendSequencerBatch()')).slice(2, 10)
const calldata = encodeAppendSequencerBatch(batch)
return CanonicalTransactionChain.signer.sendTransaction({ return CanonicalTransactionChain.signer.sendTransaction({
to: CanonicalTransactionChain.address, to: CanonicalTransactionChain.address,
data: '0x' + methodId + calldata, data:
ethers.utils.id('appendSequencerBatch()').slice(0, 10) +
encodeAppendSequencerBatch(batch),
}) })
} }
...@@ -47,15 +46,17 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () = ...@@ -47,15 +46,17 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () =
let AddressManager: Contract let AddressManager: Contract
let Fake__StateCommitmentChain: FakeContract let Fake__StateCommitmentChain: FakeContract
before(async () => { let CanonicalTransactionChain: Contract
AddressManager = await makeAddressManager() beforeEach(async () => {
AddressManager = await deploy('Lib_AddressManager')
await AddressManager.setAddress( await AddressManager.setAddress(
'OVM_Sequencer', 'OVM_Sequencer',
await sequencer.getAddress() await sequencer.getAddress()
) )
Fake__StateCommitmentChain = await smock.fake<Contract>( Fake__StateCommitmentChain = await smock.fake<Contract>(
await ethers.getContractFactory('StateCommitmentChain') 'StateCommitmentChain'
) )
await setProxyTarget( await setProxyTarget(
...@@ -63,37 +64,20 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () = ...@@ -63,37 +64,20 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () =
'StateCommitmentChain', 'StateCommitmentChain',
Fake__StateCommitmentChain Fake__StateCommitmentChain
) )
})
let Factory__CanonicalTransactionChain: ContractFactory
let Factory__ChainStorageContainer: ContractFactory
before(async () => {
Factory__CanonicalTransactionChain = await ethers.getContractFactory(
'CanonicalTransactionChain'
)
Factory__ChainStorageContainer = await ethers.getContractFactory(
'ChainStorageContainer'
)
})
let CanonicalTransactionChain: Contract CanonicalTransactionChain = await deploy('CanonicalTransactionChain', {
beforeEach(async () => { signer: sequencer,
CanonicalTransactionChain = await Factory__CanonicalTransactionChain.deploy( args: [
AddressManager.address, AddressManager.address,
MAX_GAS_LIMIT, MAX_GAS_LIMIT,
L2_GAS_DISCOUNT_DIVISOR, L2_GAS_DISCOUNT_DIVISOR,
ENQUEUE_GAS_COST ENQUEUE_GAS_COST,
) ],
})
const batches = await Factory__ChainStorageContainer.deploy( const batches = await deploy('ChainStorageContainer', {
AddressManager.address, args: [AddressManager.address, 'CanonicalTransactionChain'],
'CanonicalTransactionChain' })
)
await Factory__ChainStorageContainer.deploy(
AddressManager.address,
'CanonicalTransactionChain'
)
await AddressManager.setAddress( await AddressManager.setAddress(
'ChainStorageContainer-CTC-batches', 'ChainStorageContainer-CTC-batches',
...@@ -107,12 +91,7 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () = ...@@ -107,12 +91,7 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () =
}) })
describe('appendSequencerBatch [ @skip-on-coverage ]', () => { describe('appendSequencerBatch [ @skip-on-coverage ]', () => {
beforeEach(() => {
CanonicalTransactionChain = CanonicalTransactionChain.connect(sequencer)
})
it('200 transactions in a single context', async () => { it('200 transactions in a single context', async () => {
console.log(`Benchmark: 200 transactions in a single context.`)
const timestamp = (await getEthTime(ethers.provider)) - 100 const timestamp = (await getEthTime(ethers.provider)) - 100
const blockNumber = await getNextBlockNumber(ethers.provider) const blockNumber = await getNextBlockNumber(ethers.provider)
...@@ -143,13 +122,12 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () = ...@@ -143,13 +122,12 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () =
const receipt = await res.wait() const receipt = await res.wait()
const gasUsed = receipt.gasUsed.toNumber() const gasUsed = receipt.gasUsed.toNumber()
console.log('Benchmark complete.')
console.log('Fixed calldata cost:', fixedCalldataCost) console.log('Fixed calldata cost:', fixedCalldataCost)
console.log( console.log(
'Non-calldata overhead gas cost per transaction:', 'Non-calldata overhead gas cost per transaction:',
(gasUsed - fixedCalldataCost) / numTxs (gasUsed - fixedCalldataCost) / numTxs
) )
expectApprox(gasUsed, 1_402_638, { expectApprox(gasUsed, 1_402_638, {
absoluteUpperDeviation: 1000, absoluteUpperDeviation: 1000,
// Assert a lower bound of 1% reduction on gas cost. If your tests are breaking because your // Assert a lower bound of 1% reduction on gas cost. If your tests are breaking because your
...@@ -159,7 +137,6 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () = ...@@ -159,7 +137,6 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () =
}).timeout(10_000_000) }).timeout(10_000_000)
it('200 transactions in 200 contexts', async () => { it('200 transactions in 200 contexts', async () => {
console.log(`Benchmark: 200 transactions in 200 contexts.`)
const timestamp = (await getEthTime(ethers.provider)) - 100 const timestamp = (await getEthTime(ethers.provider)) - 100
const blockNumber = await getNextBlockNumber(ethers.provider) const blockNumber = await getNextBlockNumber(ethers.provider)
...@@ -190,13 +167,12 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () = ...@@ -190,13 +167,12 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () =
const receipt = await res.wait() const receipt = await res.wait()
const gasUsed = receipt.gasUsed.toNumber() const gasUsed = receipt.gasUsed.toNumber()
console.log('Benchmark complete.')
console.log('Fixed calldata cost:', fixedCalldataCost) console.log('Fixed calldata cost:', fixedCalldataCost)
console.log( console.log(
'Non-calldata overhead gas cost per transaction:', 'Non-calldata overhead gas cost per transaction:',
(gasUsed - fixedCalldataCost) / numTxs (gasUsed - fixedCalldataCost) / numTxs
) )
expectApprox(gasUsed, 1_619_781, { expectApprox(gasUsed, 1_619_781, {
absoluteUpperDeviation: 1000, absoluteUpperDeviation: 1000,
// Assert a lower bound of 1% reduction on gas cost. If your tests are breaking because your // Assert a lower bound of 1% reduction on gas cost. If your tests are breaking because your
...@@ -248,12 +224,12 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () = ...@@ -248,12 +224,12 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () =
const gasUsed = receipt.gasUsed.toNumber() const gasUsed = receipt.gasUsed.toNumber()
console.log('Benchmark complete.') console.log('Benchmark complete.')
console.log('Fixed calldata cost:', fixedCalldataCost) console.log('Fixed calldata cost:', fixedCalldataCost)
console.log( console.log(
'Non-calldata overhead gas cost per transaction:', 'Non-calldata overhead gas cost per transaction:',
(gasUsed - fixedCalldataCost) / numTxs (gasUsed - fixedCalldataCost) / numTxs
) )
expectApprox(gasUsed, 891_158, { expectApprox(gasUsed, 891_158, {
absoluteUpperDeviation: 1000, absoluteUpperDeviation: 1000,
// Assert a lower bound of 1% reduction on gas cost. If your tests are breaking because your // Assert a lower bound of 1% reduction on gas cost. If your tests are breaking because your
...@@ -264,13 +240,12 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () = ...@@ -264,13 +240,12 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () =
}) })
describe('enqueue [ @skip-on-coverage ]', () => { describe('enqueue [ @skip-on-coverage ]', () => {
let enqueueL2GasPrepaid const data = '0x' + '12'.repeat(1234)
let data
let enqueueL2GasPrepaid: number
beforeEach(async () => { beforeEach(async () => {
CanonicalTransactionChain = CanonicalTransactionChain.connect(sequencer)
enqueueL2GasPrepaid = enqueueL2GasPrepaid =
await CanonicalTransactionChain.enqueueL2GasPrepaid() await CanonicalTransactionChain.enqueueL2GasPrepaid()
data = '0x' + '12'.repeat(1234)
}) })
it('cost to enqueue a transaction above the prepaid threshold', async () => { it('cost to enqueue a transaction above the prepaid threshold', async () => {
...@@ -281,11 +256,10 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () = ...@@ -281,11 +256,10 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () =
l2GasLimit, l2GasLimit,
data data
) )
const receipt = await res.wait() const receipt = await res.wait()
const gasUsed = receipt.gasUsed.toNumber() const gasUsed = receipt.gasUsed.toNumber()
console.log('Benchmark complete.')
expectApprox(gasUsed, 196_687, { expectApprox(gasUsed, 196_687, {
absoluteUpperDeviation: 500, absoluteUpperDeviation: 500,
// Assert a lower bound of 1% reduction on gas cost. If your tests are breaking because your // Assert a lower bound of 1% reduction on gas cost. If your tests are breaking because your
...@@ -302,11 +276,10 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () = ...@@ -302,11 +276,10 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () =
l2GasLimit, l2GasLimit,
data data
) )
const receipt = await res.wait() const receipt = await res.wait()
const gasUsed = receipt.gasUsed.toNumber() const gasUsed = receipt.gasUsed.toNumber()
console.log('Benchmark complete.')
expectApprox(gasUsed, 134_100, { expectApprox(gasUsed, 134_100, {
absoluteUpperDeviation: 500, absoluteUpperDeviation: 500,
// Assert a lower bound of 1% reduction on gas cost. If your tests are breaking because your // Assert a lower bound of 1% reduction on gas cost. If your tests are breaking because your
......
/* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers' import { Contract } from 'ethers'
import { smock, FakeContract } from '@defi-wonderland/smock' import { smock, FakeContract } from '@defi-wonderland/smock'
import { import {
AppendSequencerBatchParams, AppendSequencerBatchParams,
encodeAppendSequencerBatch, encodeAppendSequencerBatch,
} from '@eth-optimism/core-utils' } from '@eth-optimism/core-utils'
import { TransactionResponse } from '@ethersproject/abstract-provider' import { TransactionResponse } from '@ethersproject/abstract-provider'
import { keccak256 } from 'ethers/lib/utils' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
import _ from 'lodash' import _ from 'lodash'
/* Internal Imports */
import { expect } from '../../../setup' import { expect } from '../../../setup'
import { import {
makeAddressManager, deploy,
setProxyTarget, setProxyTarget,
L2_GAS_DISCOUNT_DIVISOR, L2_GAS_DISCOUNT_DIVISOR,
ENQUEUE_GAS_COST, ENQUEUE_GAS_COST,
...@@ -27,15 +25,6 @@ import { names } from '../../../../src/address-names' ...@@ -27,15 +25,6 @@ import { names } from '../../../../src/address-names'
const ELEMENT_TEST_SIZES = [1, 2, 4, 8, 16] const ELEMENT_TEST_SIZES = [1, 2, 4, 8, 16]
const MAX_GAS_LIMIT = 8_000_000 const MAX_GAS_LIMIT = 8_000_000
const getTransactionHash = (
sender: string,
target: string,
gasLimit: number,
data: string
): string => {
return keccak256(encodeQueueTransaction(sender, target, gasLimit, data))
}
const encodeQueueTransaction = ( const encodeQueueTransaction = (
sender: string, sender: string,
target: string, target: string,
...@@ -52,33 +41,30 @@ const appendSequencerBatch = async ( ...@@ -52,33 +41,30 @@ const appendSequencerBatch = async (
CanonicalTransactionChain: Contract, CanonicalTransactionChain: Contract,
batch: AppendSequencerBatchParams batch: AppendSequencerBatchParams
): Promise<TransactionResponse> => { ): Promise<TransactionResponse> => {
const methodId = keccak256(Buffer.from('appendSequencerBatch()')).slice(2, 10)
const calldata = encodeAppendSequencerBatch(batch)
return CanonicalTransactionChain.signer.sendTransaction({ return CanonicalTransactionChain.signer.sendTransaction({
to: CanonicalTransactionChain.address, to: CanonicalTransactionChain.address,
data: '0x' + methodId + calldata, data:
ethers.utils.id('appendSequencerBatch()').slice(0, 10) +
encodeAppendSequencerBatch(batch),
}) })
} }
describe('CanonicalTransactionChain', () => { describe('CanonicalTransactionChain', () => {
let addressManagerOwner: Signer let addressManagerOwner: SignerWithAddress
let sequencer: Signer let sequencer: SignerWithAddress
let otherSigner: Signer let otherSigner: SignerWithAddress
before(async () => { before(async () => {
;[addressManagerOwner, sequencer, otherSigner] = await ethers.getSigners() ;[addressManagerOwner, sequencer, otherSigner] = await ethers.getSigners()
}) })
let AddressManager: Contract let AddressManager: Contract
let CanonicalTransactionChain: Contract
let Fake__StateCommitmentChain: FakeContract let Fake__StateCommitmentChain: FakeContract
before(async () => { beforeEach(async () => {
AddressManager = await makeAddressManager() AddressManager = await deploy('Lib_AddressManager')
await AddressManager.setAddress(
'OVM_Sequencer',
await sequencer.getAddress()
)
Fake__StateCommitmentChain = await smock.fake<Contract>( Fake__StateCommitmentChain = await smock.fake<Contract>(
await ethers.getContractFactory('StateCommitmentChain') 'StateCommitmentChain'
) )
await setProxyTarget( await setProxyTarget(
...@@ -86,33 +72,22 @@ describe('CanonicalTransactionChain', () => { ...@@ -86,33 +72,22 @@ describe('CanonicalTransactionChain', () => {
'StateCommitmentChain', 'StateCommitmentChain',
Fake__StateCommitmentChain Fake__StateCommitmentChain
) )
})
let Factory__CanonicalTransactionChain: ContractFactory
let Factory__ChainStorageContainer: ContractFactory
before(async () => {
Factory__CanonicalTransactionChain = await ethers.getContractFactory(
'CanonicalTransactionChain'
)
Factory__ChainStorageContainer = await ethers.getContractFactory( CanonicalTransactionChain = await deploy('CanonicalTransactionChain', {
'ChainStorageContainer' signer: sequencer,
) args: [
})
let CanonicalTransactionChain: Contract
beforeEach(async () => {
CanonicalTransactionChain = await Factory__CanonicalTransactionChain.deploy(
AddressManager.address, AddressManager.address,
MAX_GAS_LIMIT, MAX_GAS_LIMIT,
L2_GAS_DISCOUNT_DIVISOR, L2_GAS_DISCOUNT_DIVISOR,
ENQUEUE_GAS_COST ENQUEUE_GAS_COST,
) ],
})
const batches = await Factory__ChainStorageContainer.deploy( const batches = await deploy('ChainStorageContainer', {
AddressManager.address, args: [AddressManager.address, 'CanonicalTransactionChain'],
'CanonicalTransactionChain' })
)
await AddressManager.setAddress('OVM_Sequencer', sequencer.address)
await AddressManager.setAddress( await AddressManager.setAddress(
'ChainStorageContainer-CTC-batches', 'ChainStorageContainer-CTC-batches',
...@@ -197,19 +172,19 @@ describe('CanonicalTransactionChain', () => { ...@@ -197,19 +172,19 @@ describe('CanonicalTransactionChain', () => {
}) })
it('should revert if transaction gas limit does not cover rollup burn', async () => { it('should revert if transaction gas limit does not cover rollup burn', async () => {
const _enqueueL2GasPrepaid = const enqueueL2GasPrepaid =
await CanonicalTransactionChain.enqueueL2GasPrepaid() await CanonicalTransactionChain.enqueueL2GasPrepaid()
const l2GasDiscountDivisor = const l2GasDiscountDivisor =
await CanonicalTransactionChain.l2GasDiscountDivisor() await CanonicalTransactionChain.l2GasDiscountDivisor()
const data = '0x' + '12'.repeat(1234) const data = '0x' + '12'.repeat(1234)
// Create a tx with high L2 gas limit, but insufficient L1 gas limit to cover burn. // Create a tx with high L2 gas limit, but insufficient L1 gas limit to cover burn.
const l2GasLimit = 2 * _enqueueL2GasPrepaid const l2GasLimit = 2 * enqueueL2GasPrepaid
// This l1GasLimit is equivalent to the gasToConsume amount calculated in the CTC. After // This l1GasLimit is equivalent to the gasToConsume amount calculated in the CTC. After
// additional gas overhead, it will be enough trigger the gas burn, but not enough to cover // additional gas overhead, it will be enough trigger the gas burn, but not enough to cover
// it. // it.
const l1GasLimit = const l1GasLimit =
(l2GasLimit - _enqueueL2GasPrepaid) / l2GasDiscountDivisor (l2GasLimit - enqueueL2GasPrepaid) / l2GasDiscountDivisor
await expect( await expect(
CanonicalTransactionChain.enqueue(target, l2GasLimit, data, { CanonicalTransactionChain.enqueue(target, l2GasLimit, data, {
...@@ -219,12 +194,12 @@ describe('CanonicalTransactionChain', () => { ...@@ -219,12 +194,12 @@ describe('CanonicalTransactionChain', () => {
}) })
it('should burn L1 gas when L2 gas limit is high', async () => { it('should burn L1 gas when L2 gas limit is high', async () => {
const _enqueueL2GasPrepaid = const enqueueL2GasPrepaid =
await CanonicalTransactionChain.enqueueL2GasPrepaid() await CanonicalTransactionChain.enqueueL2GasPrepaid()
const data = '0x' + '12'.repeat(1234) const data = '0x' + '12'.repeat(1234)
// Create a tx with high L2 gas limit // Create a tx with high L2 gas limit
const l2GasLimit = 4 * _enqueueL2GasPrepaid const l2GasLimit = 4 * enqueueL2GasPrepaid
await expect(CanonicalTransactionChain.enqueue(target, l2GasLimit, data)) await expect(CanonicalTransactionChain.enqueue(target, l2GasLimit, data))
.to.not.be.reverted .to.not.be.reverted
...@@ -286,6 +261,7 @@ describe('CanonicalTransactionChain', () => { ...@@ -286,6 +261,7 @@ describe('CanonicalTransactionChain', () => {
data data
) )
const receipt2 = await res2.wait() const receipt2 = await res2.wait()
expect(receipt1.gasUsed).to.equal(receipt2.gasUsed) expect(receipt1.gasUsed).to.equal(receipt2.gasUsed)
}) })
}) })
...@@ -312,21 +288,23 @@ describe('CanonicalTransactionChain', () => { ...@@ -312,21 +288,23 @@ describe('CanonicalTransactionChain', () => {
const blockNumber = await getNextBlockNumber(ethers.provider) const blockNumber = await getNextBlockNumber(ethers.provider)
await setEthTime(ethers.provider, timestamp) await setEthTime(ethers.provider, timestamp)
const transactionHash = getTransactionHash( const transactionHash = ethers.utils.keccak256(
await addressManagerOwner.getAddress(), encodeQueueTransaction(
addressManagerOwner.address,
target, target,
gasLimit, gasLimit,
data data
) )
)
await CanonicalTransactionChain.enqueue(target, gasLimit, data) await CanonicalTransactionChain.connect(
addressManagerOwner
).enqueue(target, gasLimit, data)
for (let i = 0; i < size; i++) { for (let i = 0; i < size; i++) {
await CanonicalTransactionChain.enqueue( await CanonicalTransactionChain.connect(
target, addressManagerOwner
gasLimit, ).enqueue(target, gasLimit, '0x' + '12'.repeat(i + 1))
'0x' + '12'.repeat(i + 1)
)
} }
expect( expect(
...@@ -356,20 +334,22 @@ describe('CanonicalTransactionChain', () => { ...@@ -356,20 +334,22 @@ describe('CanonicalTransactionChain', () => {
blockNumber = await getNextBlockNumber(ethers.provider) blockNumber = await getNextBlockNumber(ethers.provider)
await setEthTime(ethers.provider, timestamp) await setEthTime(ethers.provider, timestamp)
transactionHash = getTransactionHash( transactionHash = ethers.utils.keccak256(
await addressManagerOwner.getAddress(), encodeQueueTransaction(
addressManagerOwner.address,
target, target,
gasLimit, gasLimit,
data data
) )
)
await CanonicalTransactionChain.enqueue(target, gasLimit, data) await CanonicalTransactionChain.connect(
addressManagerOwner
).enqueue(target, gasLimit, data)
} else { } else {
await CanonicalTransactionChain.enqueue( await CanonicalTransactionChain.connect(
target, addressManagerOwner
gasLimit, ).enqueue(target, gasLimit, '0x' + '12'.repeat(i + 1))
'0x' + '12'.repeat(i + 1)
)
} }
} }
...@@ -399,20 +379,22 @@ describe('CanonicalTransactionChain', () => { ...@@ -399,20 +379,22 @@ describe('CanonicalTransactionChain', () => {
blockNumber = await getNextBlockNumber(ethers.provider) blockNumber = await getNextBlockNumber(ethers.provider)
await setEthTime(ethers.provider, timestamp) await setEthTime(ethers.provider, timestamp)
transactionHash = getTransactionHash( transactionHash = ethers.utils.keccak256(
await addressManagerOwner.getAddress(), encodeQueueTransaction(
addressManagerOwner.address,
target, target,
gasLimit, gasLimit,
data data
) )
)
await CanonicalTransactionChain.enqueue(target, gasLimit, data) await CanonicalTransactionChain.connect(
addressManagerOwner
).enqueue(target, gasLimit, data)
} else { } else {
await CanonicalTransactionChain.enqueue( await CanonicalTransactionChain.connect(
target, addressManagerOwner
gasLimit, ).enqueue(target, gasLimit, '0x' + '12'.repeat(i + 1))
'0x' + '12'.repeat(i + 1)
)
} }
} }
...@@ -432,10 +414,6 @@ describe('CanonicalTransactionChain', () => { ...@@ -432,10 +414,6 @@ describe('CanonicalTransactionChain', () => {
}) })
describe('appendSequencerBatch', () => { describe('appendSequencerBatch', () => {
beforeEach(() => {
CanonicalTransactionChain = CanonicalTransactionChain.connect(sequencer)
})
it('should revert if expected start does not match current total batches', async () => { it('should revert if expected start does not match current total batches', async () => {
await expect( await expect(
appendSequencerBatch(CanonicalTransactionChain, { appendSequencerBatch(CanonicalTransactionChain, {
...@@ -499,9 +477,7 @@ describe('CanonicalTransactionChain', () => { ...@@ -499,9 +477,7 @@ describe('CanonicalTransactionChain', () => {
it('should emit the previous blockhash in the TransactionBatchAppended event', async () => { it('should emit the previous blockhash in the TransactionBatchAppended event', async () => {
const timestamp = await getEthTime(ethers.provider) const timestamp = await getEthTime(ethers.provider)
const currentBlockHash = await ( const currentBlock = await ethers.provider.getBlock('latest')
await ethers.provider.getBlock('latest')
).hash
const blockNumber = await getNextBlockNumber(ethers.provider) const blockNumber = await getNextBlockNumber(ethers.provider)
const res = await appendSequencerBatch(CanonicalTransactionChain, { const res = await appendSequencerBatch(CanonicalTransactionChain, {
transactions: ['0x1234'], transactions: ['0x1234'],
...@@ -525,7 +501,7 @@ describe('CanonicalTransactionChain', () => { ...@@ -525,7 +501,7 @@ describe('CanonicalTransactionChain', () => {
receipt.logs[0].data receipt.logs[0].data
) )
await expect(eventArgs[0]).to.eq(currentBlockHash) expect(eventArgs[0]).to.eq(currentBlock.hash)
}) })
for (const size of ELEMENT_TEST_SIZES) { for (const size of ELEMENT_TEST_SIZES) {
...@@ -680,7 +656,7 @@ describe('CanonicalTransactionChain', () => { ...@@ -680,7 +656,7 @@ describe('CanonicalTransactionChain', () => {
return '0x' + '12' + '34'.repeat(idx) return '0x' + '12' + '34'.repeat(idx)
}) })
const res = await appendSequencerBatch( await appendSequencerBatch(
CanonicalTransactionChain.connect(sequencer), CanonicalTransactionChain.connect(sequencer),
{ {
transactions, transactions,
...@@ -689,7 +665,6 @@ describe('CanonicalTransactionChain', () => { ...@@ -689,7 +665,6 @@ describe('CanonicalTransactionChain', () => {
totalElementsToAppend: size, totalElementsToAppend: size,
} }
) )
await res.wait()
expect(await CanonicalTransactionChain.getLastTimestamp()).to.equal( expect(await CanonicalTransactionChain.getLastTimestamp()).to.equal(
timestamp timestamp
......
/* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Contract, Signer, ContractFactory } from 'ethers' import { Contract } from 'ethers'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
/* Internal Imports */
import { expect } from '../../../setup' import { expect } from '../../../setup'
import { makeAddressManager, NON_NULL_BYTES32 } from '../../../helpers' import { deploy, NON_NULL_BYTES32 } from '../../../helpers'
describe('ChainStorageContainer', () => { describe('ChainStorageContainer', () => {
let sequencer: Signer let signer1: SignerWithAddress
let otherSigner: Signer let signer2: SignerWithAddress
let signer: Signer
let signerAddress: string
let AddressManager: Contract
let Factory__ChainStorageContainer: ContractFactory
before(async () => { before(async () => {
;[sequencer, otherSigner, signer] = await ethers.getSigners() ;[signer1, signer2] = await ethers.getSigners()
signerAddress = await otherSigner.getAddress()
AddressManager = await makeAddressManager()
await AddressManager.setAddress(
'OVM_Sequencer',
await sequencer.getAddress()
)
Factory__ChainStorageContainer = await ethers.getContractFactory(
'ChainStorageContainer'
)
}) })
let AddressManager: Contract
let ChainStorageContainer: Contract let ChainStorageContainer: Contract
beforeEach(async () => { beforeEach(async () => {
ChainStorageContainer = await Factory__ChainStorageContainer.connect( AddressManager = await deploy('Lib_AddressManager')
otherSigner ChainStorageContainer = await deploy('ChainStorageContainer', {
).deploy(AddressManager.address, signerAddress) signer: signer1,
args: [AddressManager.address, signer1.address],
await AddressManager.setAddress( })
'ChainStorageContainer',
ChainStorageContainer.address
)
await AddressManager.setAddress(signerAddress, signerAddress) // ChainStorageContainer uses name resolution to check the owner address.
await AddressManager.setAddress(signer1.address, signer1.address)
}) })
describe('push', () => { describe('push', () => {
for (const len of [1, 2, 4, 8, 32]) { for (const len of [1, 2, 4, 8, 32]) {
it(`it should be able to add ${len} element(s) to the array`, async () => { it(`it should be able to add ${len} element(s) to the array`, async () => {
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
await expect( await expect(ChainStorageContainer['push(bytes32)'](NON_NULL_BYTES32))
ChainStorageContainer.connect(otherSigner)['push(bytes32)']( .to.not.be.reverted
NON_NULL_BYTES32
)
).to.not.be.reverted
} }
}) })
} }
...@@ -60,9 +39,7 @@ describe('ChainStorageContainer', () => { ...@@ -60,9 +39,7 @@ describe('ChainStorageContainer', () => {
describe('setGlobalMetadata', () => { describe('setGlobalMetadata', () => {
it('should modify the extra data', async () => { it('should modify the extra data', async () => {
const globalMetaData = `0x${'11'.repeat(27)}` const globalMetaData = `0x${'11'.repeat(27)}`
await ChainStorageContainer.connect(otherSigner).setGlobalMetadata( await ChainStorageContainer.setGlobalMetadata(globalMetaData)
globalMetaData
)
expect(await ChainStorageContainer.getGlobalMetadata()).to.equal( expect(await ChainStorageContainer.getGlobalMetadata()).to.equal(
globalMetaData globalMetaData
...@@ -73,15 +50,13 @@ describe('ChainStorageContainer', () => { ...@@ -73,15 +50,13 @@ describe('ChainStorageContainer', () => {
describe('deleteElementsAfterInclusive', () => { describe('deleteElementsAfterInclusive', () => {
it('should revert when the array is empty', async () => { it('should revert when the array is empty', async () => {
await expect( await expect(
ChainStorageContainer.connect(otherSigner)[ ChainStorageContainer['deleteElementsAfterInclusive(uint256)'](0)
'deleteElementsAfterInclusive(uint256)'
](0)
).to.be.reverted ).to.be.reverted
}) })
it('should revert when called by non-owner', async () => { it('should revert when called by non-owner', async () => {
await expect( await expect(
ChainStorageContainer.connect(signer)[ ChainStorageContainer.connect(signer2)[
'deleteElementsAfterInclusive(uint256)' 'deleteElementsAfterInclusive(uint256)'
](0) ](0)
).to.be.revertedWith( ).to.be.revertedWith(
...@@ -96,18 +71,14 @@ describe('ChainStorageContainer', () => { ...@@ -96,18 +71,14 @@ describe('ChainStorageContainer', () => {
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
const value = NON_NULL_BYTES32 const value = NON_NULL_BYTES32
values.push(value) values.push(value)
await ChainStorageContainer.connect(otherSigner)['push(bytes32)']( await ChainStorageContainer['push(bytes32)'](value)
value
)
} }
}) })
for (let i = len - 1; i > 0; i -= Math.max(1, len / 4)) { for (let i = len - 1; i > 0; i -= Math.max(1, len / 4)) {
it(`should be able to delete everything after and including the ${i}th/st/rd/whatever element`, async () => { it(`should be able to delete everything after and including the ${i}th/st/rd/whatever element`, async () => {
await expect( await expect(
ChainStorageContainer.connect(otherSigner)[ ChainStorageContainer['deleteElementsAfterInclusive(uint256)'](i)
'deleteElementsAfterInclusive(uint256)'
](i)
).to.not.be.reverted ).to.not.be.reverted
expect(await ChainStorageContainer.length()).to.equal(i) expect(await ChainStorageContainer.length()).to.equal(i)
......
/* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract, constants } from 'ethers' import { Contract, constants } from 'ethers'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
import { smock, FakeContract } from '@defi-wonderland/smock' import { smock, FakeContract } from '@defi-wonderland/smock'
/* Internal Imports */
import { expect } from '../../../setup' import { expect } from '../../../setup'
import { import {
makeAddressManager, deploy,
setProxyTarget, setProxyTarget,
NON_NULL_BYTES32, NON_NULL_BYTES32,
getEthTime, getEthTime,
...@@ -14,22 +13,21 @@ import { ...@@ -14,22 +13,21 @@ import {
} from '../../../helpers' } from '../../../helpers'
describe('StateCommitmentChain', () => { describe('StateCommitmentChain', () => {
let sequencer: Signer let sequencer: SignerWithAddress
let user: Signer let user: SignerWithAddress
const batch = [NON_NULL_BYTES32]
before(async () => { before(async () => {
;[sequencer, user] = await ethers.getSigners() ;[sequencer, user] = await ethers.getSigners()
}) })
let AddressManager: Contract let AddressManager: Contract
before(async () => {
AddressManager = await makeAddressManager()
})
let Fake__CanonicalTransactionChain: FakeContract let Fake__CanonicalTransactionChain: FakeContract
let Fake__BondManager: FakeContract let Fake__BondManager: FakeContract
before(async () => { before(async () => {
AddressManager = await deploy('Lib_AddressManager')
Fake__CanonicalTransactionChain = await smock.fake<Contract>( Fake__CanonicalTransactionChain = await smock.fake<Contract>(
await ethers.getContractFactory('CanonicalTransactionChain') 'CanonicalTransactionChain'
) )
await setProxyTarget( await setProxyTarget(
...@@ -38,44 +36,29 @@ describe('StateCommitmentChain', () => { ...@@ -38,44 +36,29 @@ describe('StateCommitmentChain', () => {
Fake__CanonicalTransactionChain Fake__CanonicalTransactionChain
) )
Fake__BondManager = await smock.fake<Contract>( Fake__BondManager = await smock.fake<Contract>('BondManager')
await ethers.getContractFactory('BondManager')
)
await setProxyTarget(AddressManager, 'BondManager', Fake__BondManager) await setProxyTarget(AddressManager, 'BondManager', Fake__BondManager)
Fake__BondManager.isCollateralized.returns(true) Fake__BondManager.isCollateralized.returns(true)
await AddressManager.setAddress( await AddressManager.setAddress('OVM_Proposer', sequencer.address)
'OVM_Proposer',
await sequencer.getAddress()
)
})
let Factory__StateCommitmentChain: ContractFactory
let Factory__ChainStorageContainer: ContractFactory
before(async () => {
Factory__StateCommitmentChain = await ethers.getContractFactory(
'StateCommitmentChain'
)
Factory__ChainStorageContainer = await ethers.getContractFactory(
'ChainStorageContainer'
)
}) })
let StateCommitmentChain: Contract let StateCommitmentChain: Contract
beforeEach(async () => { beforeEach(async () => {
StateCommitmentChain = await Factory__StateCommitmentChain.deploy( StateCommitmentChain = await deploy('StateCommitmentChain', {
signer: sequencer,
args: [
AddressManager.address, AddressManager.address,
60 * 60 * 24 * 7, // 1 week fraud proof window 60 * 60 * 24 * 7, // 1 week fraud proof window
60 * 30 // 30 minute sequencer publish window 60 * 30, // 30 minute sequencer publish window
) ],
})
const batches = await Factory__ChainStorageContainer.deploy( const batches = await deploy('ChainStorageContainer', {
AddressManager.address, args: [AddressManager.address, 'StateCommitmentChain'],
'StateCommitmentChain' })
)
await AddressManager.setAddress( await AddressManager.setAddress(
'ChainStorageContainer-SCC-batches', 'ChainStorageContainer-SCC-batches',
...@@ -90,18 +73,14 @@ describe('StateCommitmentChain', () => { ...@@ -90,18 +73,14 @@ describe('StateCommitmentChain', () => {
describe('appendStateBatch', () => { describe('appendStateBatch', () => {
describe('when the provided batch is empty', () => { describe('when the provided batch is empty', () => {
const batch = []
it('should revert', async () => { it('should revert', async () => {
await expect( await expect(
StateCommitmentChain.appendStateBatch(batch, 0) StateCommitmentChain.appendStateBatch([], 0)
).to.be.revertedWith('Cannot submit an empty state batch.') ).to.be.revertedWith('Cannot submit an empty state batch.')
}) })
}) })
describe('when the provided batch is not empty', () => { describe('when the provided batch is not empty', () => {
const batch = [NON_NULL_BYTES32]
describe('when start index does not match total elements', () => { describe('when start index does not match total elements', () => {
it('should revert', async () => { it('should revert', async () => {
await expect( await expect(
...@@ -145,10 +124,7 @@ describe('StateCommitmentChain', () => { ...@@ -145,10 +124,7 @@ describe('StateCommitmentChain', () => {
batch.length * 2 batch.length * 2
) )
await StateCommitmentChain.connect(sequencer).appendStateBatch( await StateCommitmentChain.appendStateBatch(batch, 0)
batch,
0
)
}) })
describe('when inside sequencer publish window', () => { describe('when inside sequencer publish window', () => {
...@@ -195,7 +171,6 @@ describe('StateCommitmentChain', () => { ...@@ -195,7 +171,6 @@ describe('StateCommitmentChain', () => {
}) })
describe('deleteStateBatch', () => { describe('deleteStateBatch', () => {
const batch = [NON_NULL_BYTES32]
const batchHeader = { const batchHeader = {
batchIndex: 0, batchIndex: 0,
batchRoot: NON_NULL_BYTES32, batchRoot: NON_NULL_BYTES32,
...@@ -213,7 +188,7 @@ describe('StateCommitmentChain', () => { ...@@ -213,7 +188,7 @@ describe('StateCommitmentChain', () => {
await StateCommitmentChain.appendStateBatch(batch, 0) await StateCommitmentChain.appendStateBatch(batch, 0)
batchHeader.extraData = ethers.utils.defaultAbiCoder.encode( batchHeader.extraData = ethers.utils.defaultAbiCoder.encode(
['uint256', 'address'], ['uint256', 'address'],
[await getEthTime(ethers.provider), await sequencer.getAddress()] [await getEthTime(ethers.provider), sequencer.address]
) )
}) })
...@@ -236,10 +211,7 @@ describe('StateCommitmentChain', () => { ...@@ -236,10 +211,7 @@ describe('StateCommitmentChain', () => {
describe('when the sender is the OVM_FraudVerifier', () => { describe('when the sender is the OVM_FraudVerifier', () => {
before(async () => { before(async () => {
await AddressManager.setAddress( await AddressManager.setAddress('OVM_FraudVerifier', sequencer.address)
'OVM_FraudVerifier',
await sequencer.getAddress()
)
}) })
describe('when the provided batch index is greater than the total submitted', () => { describe('when the provided batch index is greater than the total submitted', () => {
...@@ -302,13 +274,14 @@ describe('StateCommitmentChain', () => { ...@@ -302,13 +274,14 @@ describe('StateCommitmentChain', () => {
prevTotalElements: 0, prevTotalElements: 0,
extraData: ethers.constants.HashZero, extraData: ethers.constants.HashZero,
} }
it('should revert when timestamp is zero', async () => { it('should revert when timestamp is zero', async () => {
await expect( await expect(
StateCommitmentChain.insideFraudProofWindow({ StateCommitmentChain.insideFraudProofWindow({
...batchHeader, ...batchHeader,
extraData: ethers.utils.defaultAbiCoder.encode( extraData: ethers.utils.defaultAbiCoder.encode(
['uint256', 'address'], ['uint256', 'address'],
[0, await sequencer.getAddress()] [0, sequencer.address]
), ),
}) })
).to.be.revertedWith('Batch header timestamp cannot be zero') ).to.be.revertedWith('Batch header timestamp cannot be zero')
...@@ -324,7 +297,6 @@ describe('StateCommitmentChain', () => { ...@@ -324,7 +297,6 @@ describe('StateCommitmentChain', () => {
describe('when one batch element has been inserted', () => { describe('when one batch element has been inserted', () => {
beforeEach(async () => { beforeEach(async () => {
const batch = [NON_NULL_BYTES32]
Fake__CanonicalTransactionChain.getTotalElements.returns(batch.length) Fake__CanonicalTransactionChain.getTotalElements.returns(batch.length)
await StateCommitmentChain.appendStateBatch(batch, 0) await StateCommitmentChain.appendStateBatch(batch, 0)
}) })
...@@ -336,9 +308,11 @@ describe('StateCommitmentChain', () => { ...@@ -336,9 +308,11 @@ describe('StateCommitmentChain', () => {
describe('when 64 batch elements have been inserted in one batch', () => { describe('when 64 batch elements have been inserted in one batch', () => {
beforeEach(async () => { beforeEach(async () => {
const batch = Array(64).fill(NON_NULL_BYTES32) const batchArray = Array(64).fill(NON_NULL_BYTES32)
Fake__CanonicalTransactionChain.getTotalElements.returns(batch.length) Fake__CanonicalTransactionChain.getTotalElements.returns(
await StateCommitmentChain.appendStateBatch(batch, 0) batchArray.length
)
await StateCommitmentChain.appendStateBatch(batchArray, 0)
}) })
it('should return the number of inserted batch elements', async () => { it('should return the number of inserted batch elements', async () => {
...@@ -348,12 +322,12 @@ describe('StateCommitmentChain', () => { ...@@ -348,12 +322,12 @@ describe('StateCommitmentChain', () => {
describe('when 32 batch elements have been inserted in each of two batches', () => { describe('when 32 batch elements have been inserted in each of two batches', () => {
beforeEach(async () => { beforeEach(async () => {
const batch = Array(32).fill(NON_NULL_BYTES32) const batchArray = Array(32).fill(NON_NULL_BYTES32)
Fake__CanonicalTransactionChain.getTotalElements.returns( Fake__CanonicalTransactionChain.getTotalElements.returns(
batch.length * 2 batchArray.length * 2
) )
await StateCommitmentChain.appendStateBatch(batch, 0) await StateCommitmentChain.appendStateBatch(batchArray, 0)
await StateCommitmentChain.appendStateBatch(batch, 32) await StateCommitmentChain.appendStateBatch(batchArray, 32)
}) })
it('should return the number of inserted batch elements', async () => { it('should return the number of inserted batch elements', async () => {
...@@ -371,7 +345,6 @@ describe('StateCommitmentChain', () => { ...@@ -371,7 +345,6 @@ describe('StateCommitmentChain', () => {
describe('when one batch has been inserted', () => { describe('when one batch has been inserted', () => {
beforeEach(async () => { beforeEach(async () => {
const batch = [NON_NULL_BYTES32]
Fake__CanonicalTransactionChain.getTotalElements.returns(batch.length) Fake__CanonicalTransactionChain.getTotalElements.returns(batch.length)
await StateCommitmentChain.appendStateBatch(batch, 0) await StateCommitmentChain.appendStateBatch(batch, 0)
}) })
...@@ -383,7 +356,6 @@ describe('StateCommitmentChain', () => { ...@@ -383,7 +356,6 @@ describe('StateCommitmentChain', () => {
describe('when 8 batches have been inserted', () => { describe('when 8 batches have been inserted', () => {
beforeEach(async () => { beforeEach(async () => {
const batch = [NON_NULL_BYTES32]
Fake__CanonicalTransactionChain.getTotalElements.returns( Fake__CanonicalTransactionChain.getTotalElements.returns(
batch.length * 8 batch.length * 8
) )
...@@ -411,7 +383,6 @@ describe('StateCommitmentChain', () => { ...@@ -411,7 +383,6 @@ describe('StateCommitmentChain', () => {
describe('when one batch element has been inserted', () => { describe('when one batch element has been inserted', () => {
let timestamp let timestamp
beforeEach(async () => { beforeEach(async () => {
const batch = [NON_NULL_BYTES32]
Fake__CanonicalTransactionChain.getTotalElements.returns(batch.length) Fake__CanonicalTransactionChain.getTotalElements.returns(batch.length)
await StateCommitmentChain.appendStateBatch(batch, 0) await StateCommitmentChain.appendStateBatch(batch, 0)
timestamp = await getEthTime(ethers.provider) timestamp = await getEthTime(ethers.provider)
...@@ -426,7 +397,6 @@ describe('StateCommitmentChain', () => { ...@@ -426,7 +397,6 @@ describe('StateCommitmentChain', () => {
}) })
describe('verifyStateCommitment()', () => { describe('verifyStateCommitment()', () => {
const batch = [NON_NULL_BYTES32]
const batchHeader = { const batchHeader = {
batchIndex: 0, batchIndex: 0,
batchRoot: NON_NULL_BYTES32, batchRoot: NON_NULL_BYTES32,
...@@ -451,7 +421,7 @@ describe('StateCommitmentChain', () => { ...@@ -451,7 +421,7 @@ describe('StateCommitmentChain', () => {
await StateCommitmentChain.appendStateBatch(batch, 0) await StateCommitmentChain.appendStateBatch(batch, 0)
batchHeader.extraData = ethers.utils.defaultAbiCoder.encode( batchHeader.extraData = ethers.utils.defaultAbiCoder.encode(
['uint256', 'address'], ['uint256', 'address'],
[await getEthTime(ethers.provider), await sequencer.getAddress()] [await getEthTime(ethers.provider), sequencer.address]
) )
}) })
......
/* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Signer, Contract, BigNumber } from 'ethers' import { Contract, BigNumber } from 'ethers'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
/* Internal Imports */
import { expect } from '../../../setup' import { expect } from '../../../setup'
import { deploy } from '../../../helpers'
const initialMinDepositAmount = ethers.utils.parseEther('0.01') const initialMinDepositAmount = ethers.utils.parseEther('0.01')
const initialMaxDepositAmount = ethers.utils.parseEther('1') const initialMaxDepositAmount = ethers.utils.parseEther('1')
const initialMaxBalance = ethers.utils.parseEther('2') const initialMaxBalance = ethers.utils.parseEther('2')
describe('TeleportrDeposit', async () => { describe('TeleportrDeposit', async () => {
let teleportrDeposit: Contract let signer1: SignerWithAddress
let signer: Signer let signer2: SignerWithAddress
let signer2: Signer
let contractAddress: string
let signerAddress: string
let signer2Address: string
before(async () => { before(async () => {
;[signer, signer2] = await ethers.getSigners() ;[signer1, signer2] = await ethers.getSigners()
teleportrDeposit = await ( })
await ethers.getContractFactory('TeleportrDeposit')
).deploy( let TeleportrDeposit: Contract
before(async () => {
TeleportrDeposit = await deploy('TeleportrDeposit', {
args: [
initialMinDepositAmount, initialMinDepositAmount,
initialMaxDepositAmount, initialMaxDepositAmount,
initialMaxBalance initialMaxBalance,
) ],
contractAddress = teleportrDeposit.address })
signerAddress = await signer.getAddress()
signer2Address = await signer2.getAddress()
}) })
describe('receive', async () => { describe('receive', async () => {
const oneETH = ethers.utils.parseEther('1.0') const oneETH = ethers.utils.parseEther('1.0')
const twoETH = ethers.utils.parseEther('2.0') const twoETH = ethers.utils.parseEther('2.0')
it('should revert if deposit amount is less than min amount', async () => { it('should revert if deposit amount is less than min amount', async () => {
await expect( await expect(
signer.sendTransaction({ signer1.sendTransaction({
to: contractAddress, to: TeleportrDeposit.address,
value: ethers.utils.parseEther('0.001'), value: ethers.utils.parseEther('0.001'),
}) })
).to.be.revertedWith('Deposit amount is too small') ).to.be.revertedWith('Deposit amount is too small')
}) })
it('should revert if deposit amount is greater than max amount', async () => { it('should revert if deposit amount is greater than max amount', async () => {
await expect( await expect(
signer.sendTransaction({ signer1.sendTransaction({
to: contractAddress, to: TeleportrDeposit.address,
value: ethers.utils.parseEther('1.1'), value: ethers.utils.parseEther('1.1'),
}) })
).to.be.revertedWith('Deposit amount is too big') ).to.be.revertedWith('Deposit amount is too big')
}) })
it('should emit EtherReceived if called by non-owner', async () => { it('should emit EtherReceived if called by non-owner', async () => {
await expect( await expect(
signer2.sendTransaction({ signer2.sendTransaction({
to: contractAddress, to: TeleportrDeposit.address,
value: oneETH, value: oneETH,
}) })
) )
.to.emit(teleportrDeposit, 'EtherReceived') .to.emit(TeleportrDeposit, 'EtherReceived')
.withArgs(BigNumber.from('0'), signer2Address, oneETH) .withArgs(BigNumber.from('0'), signer2.address, oneETH)
}) })
it('should increase the contract balance by deposit amount', async () => { it('should increase the contract balance by deposit amount', async () => {
await expect(await ethers.provider.getBalance(contractAddress)).to.equal( expect(
oneETH await ethers.provider.getBalance(TeleportrDeposit.address)
) ).to.equal(oneETH)
}) })
it('should emit EtherReceived if called by owner', async () => { it('should emit EtherReceived if called by owner', async () => {
await expect( await expect(
signer.sendTransaction({ signer1.sendTransaction({
to: contractAddress, to: TeleportrDeposit.address,
value: oneETH, value: oneETH,
}) })
) )
.to.emit(teleportrDeposit, 'EtherReceived') .to.emit(TeleportrDeposit, 'EtherReceived')
.withArgs(BigNumber.from('1'), signerAddress, oneETH) .withArgs(BigNumber.from('1'), signer1.address, oneETH)
}) })
it('should increase the contract balance by deposit amount', async () => { it('should increase the contract balance by deposit amount', async () => {
await expect(await ethers.provider.getBalance(contractAddress)).to.equal( expect(
twoETH await ethers.provider.getBalance(TeleportrDeposit.address)
) ).to.equal(twoETH)
}) })
it('should revert if deposit will exceed max balance', async () => { it('should revert if deposit will exceed max balance', async () => {
await expect( await expect(
signer.sendTransaction({ signer1.sendTransaction({
to: contractAddress, to: TeleportrDeposit.address,
value: initialMinDepositAmount, value: initialMinDepositAmount,
}) })
).to.be.revertedWith('Contract max balance exceeded') ).to.be.revertedWith('Contract max balance exceeded')
}) })
}) })
describe('withdrawBalance', async () => { describe('withdrawBalance', async () => {
let initialContractBalance: BigNumber let initialContractBalance: BigNumber
let initialSignerBalance: BigNumber let initialSignerBalance: BigNumber
before(async () => { before(async () => {
initialContractBalance = await ethers.provider.getBalance(contractAddress) initialContractBalance = await ethers.provider.getBalance(
initialSignerBalance = await ethers.provider.getBalance(signerAddress) TeleportrDeposit.address
)
initialSignerBalance = await signer1.getBalance()
}) })
it('should revert if called by non-owner', async () => { it('should revert if called by non-owner', async () => {
await expect( await expect(
teleportrDeposit.connect(signer2).withdrawBalance() TeleportrDeposit.connect(signer2).withdrawBalance()
).to.be.revertedWith('Ownable: caller is not the owner') ).to.be.revertedWith('Ownable: caller is not the owner')
}) })
it('should emit BalanceWithdrawn if called by owner', async () => { it('should emit BalanceWithdrawn if called by owner', async () => {
await expect(teleportrDeposit.withdrawBalance()) await expect(TeleportrDeposit.withdrawBalance())
.to.emit(teleportrDeposit, 'BalanceWithdrawn') .to.emit(TeleportrDeposit, 'BalanceWithdrawn')
.withArgs(signerAddress, initialContractBalance) .withArgs(signer1.address, initialContractBalance)
}) })
it('should leave the contract with zero balance', async () => { it('should leave the contract with zero balance', async () => {
await expect(await ethers.provider.getBalance(contractAddress)).to.equal( expect(
ethers.utils.parseEther('0') await ethers.provider.getBalance(TeleportrDeposit.address)
) ).to.equal(ethers.utils.parseEther('0'))
}) })
it('should credit owner with contract balance - fees', async () => { it('should credit owner with contract balance - fees', async () => {
const expSignerBalance = initialSignerBalance.add(initialContractBalance) const expSignerBalance = initialSignerBalance.add(initialContractBalance)
await expect( expect(await signer1.getBalance()).to.be.closeTo(
await ethers.provider.getBalance(signerAddress) expSignerBalance,
).to.be.closeTo(expSignerBalance, 10 ** 15) 10 ** 15
)
}) })
}) })
describe('setMinAmount', async () => { describe('setMinAmount', async () => {
const newMinDepositAmount = ethers.utils.parseEther('0.02') const newMinDepositAmount = ethers.utils.parseEther('0.02')
it('should revert if called by non-owner', async () => { it('should revert if called by non-owner', async () => {
await expect( await expect(
teleportrDeposit.connect(signer2).setMinAmount(newMinDepositAmount) TeleportrDeposit.connect(signer2).setMinAmount(newMinDepositAmount)
).to.be.revertedWith('Ownable: caller is not the owner') ).to.be.revertedWith('Ownable: caller is not the owner')
}) })
it('should emit MinDepositAmountSet if called by owner', async () => { it('should emit MinDepositAmountSet if called by owner', async () => {
await expect(teleportrDeposit.setMinAmount(newMinDepositAmount)) await expect(TeleportrDeposit.setMinAmount(newMinDepositAmount))
.to.emit(teleportrDeposit, 'MinDepositAmountSet') .to.emit(TeleportrDeposit, 'MinDepositAmountSet')
.withArgs(initialMinDepositAmount, newMinDepositAmount) .withArgs(initialMinDepositAmount, newMinDepositAmount)
}) })
it('should have updated minDepositAmount after success', async () => { it('should have updated minDepositAmount after success', async () => {
await expect(await teleportrDeposit.minDepositAmount()).to.be.eq( expect(await TeleportrDeposit.minDepositAmount()).to.be.eq(
newMinDepositAmount newMinDepositAmount
) )
}) })
}) })
describe('setMaxAmount', async () => { describe('setMaxAmount', async () => {
const newMaxDepositAmount = ethers.utils.parseEther('2') const newMaxDepositAmount = ethers.utils.parseEther('2')
it('should revert if called non-owner', async () => { it('should revert if called non-owner', async () => {
await expect( await expect(
teleportrDeposit.connect(signer2).setMaxAmount(newMaxDepositAmount) TeleportrDeposit.connect(signer2).setMaxAmount(newMaxDepositAmount)
).to.be.revertedWith('Ownable: caller is not the owner') ).to.be.revertedWith('Ownable: caller is not the owner')
}) })
it('should emit MaxDepositAmountSet if called by owner', async () => { it('should emit MaxDepositAmountSet if called by owner', async () => {
await expect(teleportrDeposit.setMaxAmount(newMaxDepositAmount)) await expect(TeleportrDeposit.setMaxAmount(newMaxDepositAmount))
.to.emit(teleportrDeposit, 'MaxDepositAmountSet') .to.emit(TeleportrDeposit, 'MaxDepositAmountSet')
.withArgs(initialMaxDepositAmount, newMaxDepositAmount) .withArgs(initialMaxDepositAmount, newMaxDepositAmount)
}) })
it('should have an updated maxDepositAmount after success', async () => { it('should have an updated maxDepositAmount after success', async () => {
await expect(await teleportrDeposit.maxDepositAmount()).to.be.eq( expect(await TeleportrDeposit.maxDepositAmount()).to.be.eq(
newMaxDepositAmount newMaxDepositAmount
) )
}) })
}) })
describe('setMaxBalance', async () => { describe('setMaxBalance', async () => {
const newMaxBalance = ethers.utils.parseEther('2000') const newMaxBalance = ethers.utils.parseEther('2000')
it('should revert if called by non-owner', async () => { it('should revert if called by non-owner', async () => {
await expect( await expect(
teleportrDeposit.connect(signer2).setMaxBalance(newMaxBalance) TeleportrDeposit.connect(signer2).setMaxBalance(newMaxBalance)
).to.be.revertedWith('Ownable: caller is not the owner') ).to.be.revertedWith('Ownable: caller is not the owner')
}) })
it('should emit MaxBalanceSet if called by owner', async () => { it('should emit MaxBalanceSet if called by owner', async () => {
await expect(teleportrDeposit.setMaxBalance(newMaxBalance)) await expect(TeleportrDeposit.setMaxBalance(newMaxBalance))
.to.emit(teleportrDeposit, 'MaxBalanceSet') .to.emit(TeleportrDeposit, 'MaxBalanceSet')
.withArgs(initialMaxBalance, newMaxBalance) .withArgs(initialMaxBalance, newMaxBalance)
}) })
it('should have an updated maxBalance after success', async () => { it('should have an updated maxBalance after success', async () => {
await expect(await teleportrDeposit.maxBalance()).to.be.eq(newMaxBalance) expect(await TeleportrDeposit.maxBalance()).to.be.eq(newMaxBalance)
}) })
}) })
}) })
/* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Signer, Contract } from 'ethers' import { Contract } from 'ethers'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
/* Internal Imports */
import { expect } from '../../../setup' import { expect } from '../../../setup'
import { makeAddressManager } from '../../../helpers' import { deploy } from '../../../helpers'
describe('BondManager', () => { describe('BondManager', () => {
let sequencer: Signer let sequencer: SignerWithAddress
let nonSequencer: Signer let nonSequencer: SignerWithAddress
before(async () => { before(async () => {
;[sequencer, nonSequencer] = await ethers.getSigners() ;[sequencer, nonSequencer] = await ethers.getSigners()
}) })
let AddressManager: Contract let AddressManager: Contract
before(async () => {
AddressManager = await makeAddressManager()
})
let BondManager: Contract let BondManager: Contract
before(async () => { beforeEach(async () => {
BondManager = await ( AddressManager = await deploy('Lib_AddressManager')
await ethers.getContractFactory('BondManager')
).deploy(AddressManager.address) BondManager = await deploy('BondManager', {
args: [AddressManager.address],
})
AddressManager.setAddress('OVM_Proposer', await sequencer.getAddress()) AddressManager.setAddress('OVM_Proposer', sequencer.address)
}) })
describe('isCollateralized', () => { describe('isCollateralized', () => {
it('should return true for OVM_Proposer', async () => { it('should return true for OVM_Proposer', async () => {
expect( expect(await BondManager.isCollateralized(sequencer.address)).to.equal(
await BondManager.isCollateralized(await sequencer.getAddress()) true
).to.equal(true) )
}) })
it('should return false for non-sequencer', async () => { it('should return false for non-sequencer', async () => {
expect( expect(await BondManager.isCollateralized(nonSequencer.address)).to.equal(
await BondManager.isCollateralized(await nonSequencer.getAddress()) false
).to.equal(false) )
}) })
}) })
}) })
/* External Imports */ import { ethers } from 'hardhat'
import hre, { ethers } from 'hardhat' import { Contract } from 'ethers'
import { Signer, ContractFactory, Contract } from 'ethers'
import { applyL1ToL2Alias } from '@eth-optimism/core-utils' import { applyL1ToL2Alias } from '@eth-optimism/core-utils'
import { import { smock, FakeContract, MockContract } from '@defi-wonderland/smock'
smock, import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
MockContractFactory,
FakeContract,
} from '@defi-wonderland/smock'
/* Internal Imports */
import { expect } from '../../../setup' import { expect } from '../../../setup'
import { predeploys } from '../../../../src'
import { import {
impersonate,
deploy,
NON_NULL_BYTES32, NON_NULL_BYTES32,
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
encodeXDomainCalldata, encodeXDomainCalldata,
} from '../../../helpers' } from '../../../helpers'
import { predeploys } from '../../../../src'
describe('L2CrossDomainMessenger', () => { describe('L2CrossDomainMessenger', () => {
let signer: Signer let signer: SignerWithAddress
before(async () => { before(async () => {
;[signer] = await ethers.getSigners() ;[signer] = await ethers.getSigners()
}) })
...@@ -27,61 +24,37 @@ describe('L2CrossDomainMessenger', () => { ...@@ -27,61 +24,37 @@ describe('L2CrossDomainMessenger', () => {
let Fake__L1CrossDomainMessenger: FakeContract let Fake__L1CrossDomainMessenger: FakeContract
let Fake__OVM_L2ToL1MessagePasser: FakeContract let Fake__OVM_L2ToL1MessagePasser: FakeContract
before(async () => { before(async () => {
Fake__TargetContract = await smock.fake<Contract>( Fake__TargetContract = await smock.fake<Contract>('Helper_SimpleProxy')
await ethers.getContractFactory('Helper_SimpleProxy')
)
Fake__L1CrossDomainMessenger = await smock.fake<Contract>( Fake__L1CrossDomainMessenger = await smock.fake<Contract>(
await ethers.getContractFactory('L1CrossDomainMessenger') 'L1CrossDomainMessenger'
) )
Fake__OVM_L2ToL1MessagePasser = await smock.fake<Contract>( Fake__OVM_L2ToL1MessagePasser = await smock.fake<Contract>(
await ethers.getContractFactory('OVM_L2ToL1MessagePasser'), 'OVM_L2ToL1MessagePasser',
{ address: predeploys.OVM_L2ToL1MessagePasser } { address: predeploys.OVM_L2ToL1MessagePasser }
) )
}) })
let impersonatedL1CrossDomainMessengerSender: Signer let impersonatedL1CrossDomainMessengerSender: SignerWithAddress
before(async () => { before(async () => {
const impersonatedAddress = applyL1ToL2Alias( impersonatedL1CrossDomainMessengerSender = await impersonate(
Fake__L1CrossDomainMessenger.address applyL1ToL2Alias(Fake__L1CrossDomainMessenger.address),
) '0xFFFFFFFFFFFFFFFFF'
await hre.network.provider.request({
method: 'hardhat_impersonateAccount',
params: [impersonatedAddress],
})
await hre.network.provider.request({
method: 'hardhat_setBalance',
params: [impersonatedAddress, '0xFFFFFFFFFFFFFFFFF'],
})
impersonatedL1CrossDomainMessengerSender = await ethers.getSigner(
impersonatedAddress
)
})
let Factory__L2CrossDomainMessenger: ContractFactory
before(async () => {
Factory__L2CrossDomainMessenger = await ethers.getContractFactory(
'L2CrossDomainMessenger'
) )
}) })
let L2CrossDomainMessenger: Contract let L2CrossDomainMessenger: Contract
beforeEach(async () => { beforeEach(async () => {
L2CrossDomainMessenger = await Factory__L2CrossDomainMessenger.deploy( L2CrossDomainMessenger = await deploy('L2CrossDomainMessenger', {
Fake__L1CrossDomainMessenger.address args: [Fake__L1CrossDomainMessenger.address],
) })
}) })
describe('xDomainMessageSender', () => { describe('xDomainMessageSender', () => {
let Mock__Factory__L2CrossDomainMessenger: MockContractFactory<ContractFactory> let Mock__L2CrossDomainMessenger: MockContract<Contract>
let Mock__L2CrossDomainMessenger
before(async () => { before(async () => {
Mock__Factory__L2CrossDomainMessenger = await smock.mock( Mock__L2CrossDomainMessenger = await (
'L2CrossDomainMessenger' await smock.mock('L2CrossDomainMessenger')
) ).deploy(Fake__L1CrossDomainMessenger.address)
Mock__L2CrossDomainMessenger =
await Mock__Factory__L2CrossDomainMessenger.deploy(
Fake__L1CrossDomainMessenger.address
)
}) })
it('should return the xDomainMsgSender address', async () => { it('should return the xDomainMsgSender address', async () => {
...@@ -89,6 +62,7 @@ describe('L2CrossDomainMessenger', () => { ...@@ -89,6 +62,7 @@ describe('L2CrossDomainMessenger', () => {
'xDomainMsgSender', 'xDomainMsgSender',
'0x0000000000000000000000000000000000000000' '0x0000000000000000000000000000000000000000'
) )
expect( expect(
await Mock__L2CrossDomainMessenger.xDomainMessageSender() await Mock__L2CrossDomainMessenger.xDomainMessageSender()
).to.equal('0x0000000000000000000000000000000000000000') ).to.equal('0x0000000000000000000000000000000000000000')
...@@ -96,49 +70,53 @@ describe('L2CrossDomainMessenger', () => { ...@@ -96,49 +70,53 @@ describe('L2CrossDomainMessenger', () => {
}) })
describe('sendMessage', () => { describe('sendMessage', () => {
const target = NON_ZERO_ADDRESS
const message = NON_NULL_BYTES32
const gasLimit = 100_000
it('should be able to send a single message', async () => { it('should be able to send a single message', async () => {
await expect( await expect(
L2CrossDomainMessenger.sendMessage(target, message, gasLimit) L2CrossDomainMessenger.sendMessage(
NON_ZERO_ADDRESS,
NON_NULL_BYTES32,
100_000
)
).to.not.be.reverted ).to.not.be.reverted
expect( expect(
Fake__OVM_L2ToL1MessagePasser.passMessageToL1.getCall(0).args[0] Fake__OVM_L2ToL1MessagePasser.passMessageToL1.getCall(0).args[0]
).to.deep.equal( ).to.deep.equal(
encodeXDomainCalldata(target, await signer.getAddress(), message, 0) encodeXDomainCalldata(
NON_ZERO_ADDRESS,
await signer.getAddress(),
NON_NULL_BYTES32,
0
)
) )
}) })
it('should be able to send the same message twice', async () => { it('should be able to send the same message twice', async () => {
await L2CrossDomainMessenger.sendMessage(target, message, gasLimit) await L2CrossDomainMessenger.sendMessage(
NON_ZERO_ADDRESS,
NON_NULL_BYTES32,
100_000
)
await expect( await expect(
L2CrossDomainMessenger.sendMessage(target, message, gasLimit) L2CrossDomainMessenger.sendMessage(
NON_ZERO_ADDRESS,
NON_NULL_BYTES32,
100_000
)
).to.not.be.reverted ).to.not.be.reverted
}) })
}) })
describe('relayMessage', () => { describe('relayMessage', () => {
let target: string
let message: string
let sender: string
before(async () => {
target = Fake__TargetContract.address
message = Fake__TargetContract.interface.encodeFunctionData('setTarget', [
NON_ZERO_ADDRESS,
])
sender = await signer.getAddress()
})
it('should revert if the L1 message sender is not the L1CrossDomainMessenger', async () => { it('should revert if the L1 message sender is not the L1CrossDomainMessenger', async () => {
await expect( await expect(
L2CrossDomainMessenger.connect(signer).relayMessage( L2CrossDomainMessenger.connect(signer).relayMessage(
target, Fake__TargetContract.address,
sender, signer.address,
message, Fake__TargetContract.interface.encodeFunctionData('setTarget', [
NON_ZERO_ADDRESS,
]),
0 0
) )
).to.be.revertedWith('Provided message could not be verified.') ).to.be.revertedWith('Provided message could not be verified.')
...@@ -147,7 +125,14 @@ describe('L2CrossDomainMessenger', () => { ...@@ -147,7 +125,14 @@ describe('L2CrossDomainMessenger', () => {
it('should send a call to the target contract', async () => { it('should send a call to the target contract', async () => {
await L2CrossDomainMessenger.connect( await L2CrossDomainMessenger.connect(
impersonatedL1CrossDomainMessengerSender impersonatedL1CrossDomainMessengerSender
).relayMessage(target, sender, message, 0) ).relayMessage(
Fake__TargetContract.address,
signer.address,
Fake__TargetContract.interface.encodeFunctionData('setTarget', [
NON_ZERO_ADDRESS,
]),
0
)
expect(Fake__TargetContract.setTarget.getCall(0).args[0]).to.deep.equal( expect(Fake__TargetContract.setTarget.getCall(0).args[0]).to.deep.equal(
NON_ZERO_ADDRESS NON_ZERO_ADDRESS
...@@ -161,7 +146,14 @@ describe('L2CrossDomainMessenger', () => { ...@@ -161,7 +146,14 @@ describe('L2CrossDomainMessenger', () => {
await L2CrossDomainMessenger.connect( await L2CrossDomainMessenger.connect(
impersonatedL1CrossDomainMessengerSender impersonatedL1CrossDomainMessengerSender
).relayMessage(target, sender, message, 0) ).relayMessage(
Fake__TargetContract.address,
signer.address,
Fake__TargetContract.interface.encodeFunctionData('setTarget', [
NON_ZERO_ADDRESS,
]),
0
)
await expect( await expect(
L2CrossDomainMessenger.xDomainMessageSender() L2CrossDomainMessenger.xDomainMessageSender()
...@@ -171,45 +163,72 @@ describe('L2CrossDomainMessenger', () => { ...@@ -171,45 +163,72 @@ describe('L2CrossDomainMessenger', () => {
it('should revert if trying to send the same message twice', async () => { it('should revert if trying to send the same message twice', async () => {
await L2CrossDomainMessenger.connect( await L2CrossDomainMessenger.connect(
impersonatedL1CrossDomainMessengerSender impersonatedL1CrossDomainMessengerSender
).relayMessage(target, sender, message, 0) ).relayMessage(
Fake__TargetContract.address,
signer.address,
Fake__TargetContract.interface.encodeFunctionData('setTarget', [
NON_ZERO_ADDRESS,
]),
0
)
await expect( await expect(
L2CrossDomainMessenger.connect( L2CrossDomainMessenger.connect(
impersonatedL1CrossDomainMessengerSender impersonatedL1CrossDomainMessengerSender
).relayMessage(target, sender, message, 0) ).relayMessage(
Fake__TargetContract.address,
signer.address,
Fake__TargetContract.interface.encodeFunctionData('setTarget', [
NON_ZERO_ADDRESS,
]),
0
)
).to.be.revertedWith('Provided message has already been received.') ).to.be.revertedWith('Provided message has already been received.')
}) })
it('should not make a call if the target is the L2 MessagePasser', async () => { it('should not make a call if the target is the L2 MessagePasser', async () => {
target = predeploys.OVM_L2ToL1MessagePasser const resProm = L2CrossDomainMessenger.connect(
message = Fake__OVM_L2ToL1MessagePasser.interface.encodeFunctionData( impersonatedL1CrossDomainMessengerSender
).relayMessage(
predeploys.OVM_L2ToL1MessagePasser,
signer.address,
Fake__OVM_L2ToL1MessagePasser.interface.encodeFunctionData(
'passMessageToL1(bytes)', 'passMessageToL1(bytes)',
[NON_NULL_BYTES32] [NON_NULL_BYTES32]
),
0
) )
const resProm = L2CrossDomainMessenger.connect(
impersonatedL1CrossDomainMessengerSender
).relayMessage(target, sender, message, 0)
// The call to relayMessage() should succeed. // The call to relayMessage() should succeed.
await expect(resProm).to.not.be.reverted await expect(resProm).to.not.be.reverted
// There should be no 'relayedMessage' event logged in the receipt. // There should be no 'relayedMessage' event logged in the receipt.
const logs = ( expect(
(
await Fake__OVM_L2ToL1MessagePasser.provider.getTransactionReceipt( await Fake__OVM_L2ToL1MessagePasser.provider.getTransactionReceipt(
( (
await resProm await resProm
).hash ).hash
) )
).logs ).logs
expect(logs).to.deep.equal([]) ).to.deep.equal([])
// The message should be registered as successful. // The message should be registered as successful.
expect( expect(
await L2CrossDomainMessenger.successfulMessages( await L2CrossDomainMessenger.successfulMessages(
ethers.utils.solidityKeccak256( ethers.utils.solidityKeccak256(
['bytes'], ['bytes'],
[encodeXDomainCalldata(target, sender, message, 0)] [
encodeXDomainCalldata(
predeploys.OVM_L2ToL1MessagePasser,
signer.address,
Fake__OVM_L2ToL1MessagePasser.interface.encodeFunctionData(
'passMessageToL1(bytes)',
[NON_NULL_BYTES32]
),
0
),
]
) )
) )
).to.be.true ).to.be.true
......
/* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers' import { Contract } from 'ethers'
import { smock, FakeContract, MockContract } from '@defi-wonderland/smock' import { smock, FakeContract, MockContract } from '@defi-wonderland/smock'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
/* Internal Imports */
import { expect } from '../../../setup' import { expect } from '../../../setup'
import { NON_NULL_BYTES32, NON_ZERO_ADDRESS } from '../../../helpers' import { deploy, NON_NULL_BYTES32, NON_ZERO_ADDRESS } from '../../../helpers'
import { getContractInterface } from '../../../../src' import { getContractInterface, predeploys } from '../../../../src'
const ERR_INVALID_MESSENGER = 'OVM_XCHAIN: messenger contract unauthenticated' // TODO: Maybe we should consider automatically generating these and exporting them?
const ERR_INVALID_X_DOMAIN_MSG_SENDER = const ERROR_STRINGS = {
'OVM_XCHAIN: wrong sender of cross-domain message' INVALID_MESSENGER: 'OVM_XCHAIN: messenger contract unauthenticated',
const DUMMY_L1BRIDGE_ADDRESS: string = INVALID_X_DOMAIN_MSG_SENDER:
'0x1234123412341234123412341234123412341234' 'OVM_XCHAIN: wrong sender of cross-domain message',
const DUMMY_L1TOKEN_ADDRESS: string = }
'0x2234223412342234223422342234223422342234'
const OVM_ETH_ADDRESS: string = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000' const DUMMY_L1_ERC20_ADDRESS = '0xaBBAABbaaBbAABbaABbAABbAABbaAbbaaBbaaBBa'
const DUMMY_L1_BRIDGE_ADDRESS = '0xACDCacDcACdCaCDcacdcacdCaCdcACdCAcDcaCdc'
describe('L2StandardBridge', () => { describe('L2StandardBridge', () => {
let alice: Signer
let aliceAddress: string
let bob: Signer
let bobsAddress: string
let l2MessengerImpersonator: Signer
let Factory__L1StandardBridge: ContractFactory
const INITIAL_TOTAL_SUPPLY = 100_000 const INITIAL_TOTAL_SUPPLY = 100_000
const ALICE_INITIAL_BALANCE = 50_000 const ALICE_INITIAL_BALANCE = 50_000
let alice: SignerWithAddress
let bob: SignerWithAddress
let l2MessengerImpersonator: SignerWithAddress
before(async () => { before(async () => {
// Create a special signer which will enable us to send messages from the L2Messenger contract // Create a special signer which will enable us to send messages from the L2Messenger contract
;[alice, bob, l2MessengerImpersonator] = await ethers.getSigners() ;[alice, bob, l2MessengerImpersonator] = await ethers.getSigners()
aliceAddress = await alice.getAddress()
bobsAddress = await bob.getAddress()
Factory__L1StandardBridge = await ethers.getContractFactory(
'L1StandardBridge'
)
// get an L2ER20Bridge Interface
getContractInterface('IL2ERC20Bridge')
}) })
let L2StandardBridge: Contract let L2StandardBridge: Contract
...@@ -45,20 +35,25 @@ describe('L2StandardBridge', () => { ...@@ -45,20 +35,25 @@ describe('L2StandardBridge', () => {
beforeEach(async () => { beforeEach(async () => {
// Get a new mock L2 messenger // Get a new mock L2 messenger
Fake__L2CrossDomainMessenger = await smock.fake<Contract>( Fake__L2CrossDomainMessenger = await smock.fake<Contract>(
await ethers.getContractFactory('L2CrossDomainMessenger'), 'L2CrossDomainMessenger',
// This allows us to use an ethers override {from: Mock__L2CrossDomainMessenger.address} to mock calls // This allows us to use an ethers override {from: Mock__L2CrossDomainMessenger.address} to mock calls
{ address: await l2MessengerImpersonator.getAddress() } { address: await l2MessengerImpersonator.getAddress() }
) )
// Deploy the contract under test // Deploy the contract under test
L2StandardBridge = await ( L2StandardBridge = await deploy('L2StandardBridge', {
await ethers.getContractFactory('L2StandardBridge') args: [Fake__L2CrossDomainMessenger.address, DUMMY_L1_BRIDGE_ADDRESS],
).deploy(Fake__L2CrossDomainMessenger.address, DUMMY_L1BRIDGE_ADDRESS) })
// Deploy an L2 ERC20 // Deploy an L2 ERC20
L2ERC20 = await ( L2ERC20 = await deploy('L2StandardERC20', {
await ethers.getContractFactory('L2StandardERC20', alice) args: [
).deploy(L2StandardBridge.address, DUMMY_L1TOKEN_ADDRESS, 'L2Token', 'L2T') L2StandardBridge.address,
DUMMY_L1_ERC20_ADDRESS,
'L2Token',
'L2T',
],
})
}) })
// test the transfer flow of moving a token from L2 to L1 // test the transfer flow of moving a token from L2 to L1
...@@ -66,14 +61,14 @@ describe('L2StandardBridge', () => { ...@@ -66,14 +61,14 @@ describe('L2StandardBridge', () => {
it('onlyFromCrossDomainAccount: should revert on calls from a non-crossDomainMessenger L2 account', async () => { it('onlyFromCrossDomainAccount: should revert on calls from a non-crossDomainMessenger L2 account', async () => {
await expect( await expect(
L2StandardBridge.finalizeDeposit( L2StandardBridge.finalizeDeposit(
DUMMY_L1TOKEN_ADDRESS, DUMMY_L1_ERC20_ADDRESS,
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
0, 0,
NON_NULL_BYTES32 NON_NULL_BYTES32
) )
).to.be.revertedWith(ERR_INVALID_MESSENGER) ).to.be.revertedWith(ERROR_STRINGS.INVALID_MESSENGER)
}) })
it('onlyFromCrossDomainAccount: should revert on calls from the right crossDomainMessenger, but wrong xDomainMessageSender (ie. not the L1L1StandardBridge)', async () => { it('onlyFromCrossDomainAccount: should revert on calls from the right crossDomainMessenger, but wrong xDomainMessageSender (ie. not the L1L1StandardBridge)', async () => {
...@@ -83,7 +78,7 @@ describe('L2StandardBridge', () => { ...@@ -83,7 +78,7 @@ describe('L2StandardBridge', () => {
await expect( await expect(
L2StandardBridge.connect(l2MessengerImpersonator).finalizeDeposit( L2StandardBridge.connect(l2MessengerImpersonator).finalizeDeposit(
DUMMY_L1TOKEN_ADDRESS, DUMMY_L1_ERC20_ADDRESS,
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
...@@ -93,19 +88,17 @@ describe('L2StandardBridge', () => { ...@@ -93,19 +88,17 @@ describe('L2StandardBridge', () => {
from: Fake__L2CrossDomainMessenger.address, from: Fake__L2CrossDomainMessenger.address,
} }
) )
).to.be.revertedWith(ERR_INVALID_X_DOMAIN_MSG_SENDER) ).to.be.revertedWith(ERROR_STRINGS.INVALID_X_DOMAIN_MSG_SENDER)
}) })
it('should initialize a withdrawal if the L2 token is not compliant', async () => { it('should initialize a withdrawal if the L2 token is not compliant', async () => {
// Deploy a non compliant ERC20 // Deploy a non compliant ERC20
const NonCompliantERC20 = await ( const NonCompliantERC20 = await deploy('ERC20', {
await ethers.getContractFactory( args: ['L2Token', 'L2T'],
'@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20' })
)
).deploy('L2Token', 'L2T')
L2StandardBridge.connect(l2MessengerImpersonator).finalizeDeposit( L2StandardBridge.connect(l2MessengerImpersonator).finalizeDeposit(
DUMMY_L1TOKEN_ADDRESS, DUMMY_L1_ERC20_ADDRESS,
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
...@@ -117,14 +110,14 @@ describe('L2StandardBridge', () => { ...@@ -117,14 +110,14 @@ describe('L2StandardBridge', () => {
) )
Fake__L2CrossDomainMessenger.xDomainMessageSender.returns( Fake__L2CrossDomainMessenger.xDomainMessageSender.returns(
() => DUMMY_L1BRIDGE_ADDRESS DUMMY_L1_BRIDGE_ADDRESS
) )
await L2StandardBridge.connect(l2MessengerImpersonator).finalizeDeposit( await L2StandardBridge.connect(l2MessengerImpersonator).finalizeDeposit(
DUMMY_L1TOKEN_ADDRESS, DUMMY_L1_ERC20_ADDRESS,
NonCompliantERC20.address, NonCompliantERC20.address,
aliceAddress, alice.address,
bobsAddress, bob.address,
100, 100,
NON_NULL_BYTES32, NON_NULL_BYTES32,
{ {
...@@ -132,37 +125,37 @@ describe('L2StandardBridge', () => { ...@@ -132,37 +125,37 @@ describe('L2StandardBridge', () => {
} }
) )
const withdrawalCallToMessenger = expect(
Fake__L2CrossDomainMessenger.sendMessage.getCall(1) Fake__L2CrossDomainMessenger.sendMessage.getCall(1).args
).to.deep.equal([
expect(withdrawalCallToMessenger.args[0]).to.equal(DUMMY_L1BRIDGE_ADDRESS) DUMMY_L1_BRIDGE_ADDRESS,
expect(withdrawalCallToMessenger.args[1]).to.equal( getContractInterface('L1StandardBridge').encodeFunctionData(
Factory__L1StandardBridge.interface.encodeFunctionData(
'finalizeERC20Withdrawal', 'finalizeERC20Withdrawal',
[ [
DUMMY_L1TOKEN_ADDRESS, DUMMY_L1_ERC20_ADDRESS,
NonCompliantERC20.address, NonCompliantERC20.address,
bobsAddress, bob.address,
aliceAddress, alice.address,
100, 100,
NON_NULL_BYTES32, NON_NULL_BYTES32,
] ]
) ),
) 0,
])
}) })
it('should credit funds to the depositor', async () => { it('should credit funds to the depositor', async () => {
const depositAmount = 100 const depositAmount = 100
Fake__L2CrossDomainMessenger.xDomainMessageSender.returns( Fake__L2CrossDomainMessenger.xDomainMessageSender.returns(
() => DUMMY_L1BRIDGE_ADDRESS DUMMY_L1_BRIDGE_ADDRESS
) )
await L2StandardBridge.connect(l2MessengerImpersonator).finalizeDeposit( await L2StandardBridge.connect(l2MessengerImpersonator).finalizeDeposit(
DUMMY_L1TOKEN_ADDRESS, DUMMY_L1_ERC20_ADDRESS,
L2ERC20.address, L2ERC20.address,
aliceAddress, alice.address,
bobsAddress, bob.address,
depositAmount, depositAmount,
NON_NULL_BYTES32, NON_NULL_BYTES32,
{ {
...@@ -170,37 +163,35 @@ describe('L2StandardBridge', () => { ...@@ -170,37 +163,35 @@ describe('L2StandardBridge', () => {
} }
) )
const bobsBalance = await L2ERC20.balanceOf(bobsAddress) expect(await L2ERC20.balanceOf(bob.address)).to.equal(depositAmount)
bobsBalance.should.equal(depositAmount)
}) })
}) })
describe('withdrawals', () => { describe('withdrawals', () => {
const withdrawAmount = 1_000 const withdrawAmount = 1_000
let Mock__L2Token: MockContract<Contract>
let Fake__OVM_ETH
let Fake__OVM_ETH: FakeContract<Contract>
before(async () => { before(async () => {
Fake__OVM_ETH = await smock.fake('OVM_ETH', { Fake__OVM_ETH = await smock.fake('OVM_ETH', {
address: OVM_ETH_ADDRESS, address: predeploys.OVM_ETH,
}) })
}) })
let Mock__L2Token: MockContract<Contract>
beforeEach(async () => { beforeEach(async () => {
// Deploy a smodded gateway so we can give some balances to withdraw // Deploy a smodded gateway so we can give some balances to withdraw
Mock__L2Token = await ( Mock__L2Token = await (
await smock.mock('L2StandardERC20') await smock.mock('L2StandardERC20')
).deploy( ).deploy(
L2StandardBridge.address, L2StandardBridge.address,
DUMMY_L1TOKEN_ADDRESS, DUMMY_L1_ERC20_ADDRESS,
'L2Token', 'L2Token',
'L2T' 'L2T'
) )
await Mock__L2Token.setVariable('_totalSupply', INITIAL_TOTAL_SUPPLY) await Mock__L2Token.setVariable('_totalSupply', INITIAL_TOTAL_SUPPLY)
await Mock__L2Token.setVariable('_balances', { await Mock__L2Token.setVariable('_balances', {
[aliceAddress]: ALICE_INITIAL_BALANCE, [alice.address]: ALICE_INITIAL_BALANCE,
}) })
await Mock__L2Token.setVariable('l2Bridge', L2StandardBridge.address) await Mock__L2Token.setVariable('l2Bridge', L2StandardBridge.address)
}) })
...@@ -213,25 +204,16 @@ describe('L2StandardBridge', () => { ...@@ -213,25 +204,16 @@ describe('L2StandardBridge', () => {
NON_NULL_BYTES32 NON_NULL_BYTES32
) )
const withdrawalCallToMessenger = expect(
Fake__L2CrossDomainMessenger.sendMessage.getCall(0) Fake__L2CrossDomainMessenger.sendMessage.getCall(0).args
).to.deep.equal([
// Assert the correct cross-chain call was sent: DUMMY_L1_BRIDGE_ADDRESS,
// Message should be sent to the L1L1StandardBridge on L1 getContractInterface('L1StandardBridge').encodeFunctionData(
expect(withdrawalCallToMessenger.args[0]).to.equal(DUMMY_L1BRIDGE_ADDRESS)
// Message data should be a call telling the L1StandardBridge to finalize the withdrawal
expect(withdrawalCallToMessenger.args[1]).to.equal(
Factory__L1StandardBridge.interface.encodeFunctionData(
'finalizeETHWithdrawal', 'finalizeETHWithdrawal',
[ [alice.address, alice.address, 0, NON_NULL_BYTES32]
await alice.getAddress(), ),
await alice.getAddress(),
0, 0,
NON_NULL_BYTES32, ])
]
)
)
}) })
it('withdraw() burns and sends the correct withdrawal message', async () => { it('withdraw() burns and sends the correct withdrawal message', async () => {
...@@ -241,112 +223,94 @@ describe('L2StandardBridge', () => { ...@@ -241,112 +223,94 @@ describe('L2StandardBridge', () => {
0, 0,
NON_NULL_BYTES32 NON_NULL_BYTES32
) )
const withdrawalCallToMessenger =
Fake__L2CrossDomainMessenger.sendMessage.getCall(0)
// Assert Alice's balance went down expect(
const aliceBalance = await Mock__L2Token.balanceOf( Fake__L2CrossDomainMessenger.sendMessage.getCall(0).args
await alice.getAddress() ).to.deep.equal([
) DUMMY_L1_BRIDGE_ADDRESS,
expect(aliceBalance).to.deep.equal( getContractInterface('L1StandardBridge').encodeFunctionData(
ethers.BigNumber.from(ALICE_INITIAL_BALANCE - withdrawAmount)
)
// Assert totalSupply went down
const newTotalSupply = await Mock__L2Token.totalSupply()
expect(newTotalSupply).to.deep.equal(
ethers.BigNumber.from(INITIAL_TOTAL_SUPPLY - withdrawAmount)
)
// Assert the correct cross-chain call was sent:
// Message should be sent to the L1L1StandardBridge on L1
expect(withdrawalCallToMessenger.args[0]).to.equal(DUMMY_L1BRIDGE_ADDRESS)
// Message data should be a call telling the L1L1StandardBridge to finalize the withdrawal
expect(withdrawalCallToMessenger.args[1]).to.equal(
Factory__L1StandardBridge.interface.encodeFunctionData(
'finalizeERC20Withdrawal', 'finalizeERC20Withdrawal',
[ [
DUMMY_L1TOKEN_ADDRESS, DUMMY_L1_ERC20_ADDRESS,
Mock__L2Token.address, Mock__L2Token.address,
await alice.getAddress(), alice.address,
await alice.getAddress(), alice.address,
withdrawAmount, withdrawAmount,
NON_NULL_BYTES32, NON_NULL_BYTES32,
] ]
),
0,
])
// Assert Alice's balance went down
expect(await Mock__L2Token.balanceOf(alice.address)).to.deep.equal(
ethers.BigNumber.from(ALICE_INITIAL_BALANCE - withdrawAmount)
) )
// Assert totalSupply went down
expect(await Mock__L2Token.totalSupply()).to.deep.equal(
ethers.BigNumber.from(INITIAL_TOTAL_SUPPLY - withdrawAmount)
) )
// gaslimit should be correct
expect(withdrawalCallToMessenger.args[2]).to.equal(0)
}) })
it('withdrawTo() burns and sends the correct withdrawal message', async () => { it('withdrawTo() burns and sends the correct withdrawal message', async () => {
await L2StandardBridge.withdrawTo( await L2StandardBridge.withdrawTo(
Mock__L2Token.address, Mock__L2Token.address,
await bob.getAddress(), bob.address,
withdrawAmount, withdrawAmount,
0, 0,
NON_NULL_BYTES32 NON_NULL_BYTES32
) )
const withdrawalCallToMessenger =
Fake__L2CrossDomainMessenger.sendMessage.getCall(0)
// Assert Alice's balance went down
const aliceBalance = await Mock__L2Token.balanceOf(
await alice.getAddress()
)
expect(aliceBalance).to.deep.equal(
ethers.BigNumber.from(ALICE_INITIAL_BALANCE - withdrawAmount)
)
// Assert totalSupply went down expect(
const newTotalSupply = await Mock__L2Token.totalSupply() Fake__L2CrossDomainMessenger.sendMessage.getCall(0).args
expect(newTotalSupply).to.deep.equal( ).to.deep.equal([
ethers.BigNumber.from(INITIAL_TOTAL_SUPPLY - withdrawAmount) DUMMY_L1_BRIDGE_ADDRESS,
) getContractInterface('L1StandardBridge').encodeFunctionData(
// Assert the correct cross-chain call was sent.
// Message should be sent to the L1L1StandardBridge on L1
expect(withdrawalCallToMessenger.args[0]).to.equal(DUMMY_L1BRIDGE_ADDRESS)
// The message data should be a call telling the L1L1StandardBridge to finalize the withdrawal
expect(withdrawalCallToMessenger.args[1]).to.equal(
Factory__L1StandardBridge.interface.encodeFunctionData(
'finalizeERC20Withdrawal', 'finalizeERC20Withdrawal',
[ [
DUMMY_L1TOKEN_ADDRESS, DUMMY_L1_ERC20_ADDRESS,
Mock__L2Token.address, Mock__L2Token.address,
await alice.getAddress(), alice.address,
await bob.getAddress(), bob.address,
withdrawAmount, withdrawAmount,
NON_NULL_BYTES32, NON_NULL_BYTES32,
] ]
),
0,
])
// Assert Alice's balance went down
expect(await Mock__L2Token.balanceOf(alice.address)).to.deep.equal(
ethers.BigNumber.from(ALICE_INITIAL_BALANCE - withdrawAmount)
) )
// Assert totalSupply went down
expect(await Mock__L2Token.totalSupply()).to.deep.equal(
ethers.BigNumber.from(INITIAL_TOTAL_SUPPLY - withdrawAmount)
) )
// gas value is ignored and set to 0.
expect(withdrawalCallToMessenger.args[2]).to.equal(0)
}) })
}) })
describe('standard erc20', () => { describe('standard erc20', () => {
it('should not allow anyone but the L2 bridge to mint and burn', async () => { it('should not allow anyone but the L2 bridge to mint and burn', async () => {
expect(L2ERC20.connect(alice).mint(aliceAddress, 100)).to.be.revertedWith( expect(
'Only L2 Bridge can mint and burn' L2ERC20.connect(alice).mint(alice.address, 100)
) ).to.be.revertedWith('Only L2 Bridge can mint and burn')
expect(L2ERC20.connect(alice).burn(aliceAddress, 100)).to.be.revertedWith(
'Only L2 Bridge can mint and burn' expect(
) L2ERC20.connect(alice).burn(alice.address, 100)
).to.be.revertedWith('Only L2 Bridge can mint and burn')
}) })
it('should return the correct interface support', async () => { it('should return the correct interface support', async () => {
const supportsERC165 = await L2ERC20.supportsInterface(0x01ffc9a7) // ERC165
expect(supportsERC165).to.be.true expect(await L2ERC20.supportsInterface(0x01ffc9a7)).to.be.true
const supportsL2TokenInterface = await L2ERC20.supportsInterface( // L2StandardERC20
0x1d1d8b63 expect(await L2ERC20.supportsInterface(0x1d1d8b63)).to.be.true
)
expect(supportsL2TokenInterface).to.be.true
const badSupports = await L2ERC20.supportsInterface(0xffffffff) expect(await L2ERC20.supportsInterface(0xffffffff)).to.be.false
expect(badSupports).to.be.false
}) })
}) })
}) })
/* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers' import { ContractFactory, Contract } from 'ethers'
import { import {
smock, smock,
MockContractFactory, MockContractFactory,
MockContract, MockContract,
} from '@defi-wonderland/smock' } from '@defi-wonderland/smock'
/* Internal Imports */
import { expect } from '../../../setup' import { expect } from '../../../setup'
import { predeploys, getContractInterface } from '../../../../src' import { deploy } from '../../../helpers'
import { predeploys } from '../../../../src'
describe('L2StandardTokenFactory', () => { describe('L2StandardTokenFactory', () => {
let signer: Signer
let Factory__L1ERC20: MockContractFactory<ContractFactory> let Factory__L1ERC20: MockContractFactory<ContractFactory>
let L1ERC20: MockContract<Contract> let L1ERC20: MockContract<Contract>
let L2StandardTokenFactory: Contract let L2StandardTokenFactory: Contract
before(async () => { before(async () => {
;[signer] = await ethers.getSigners() Factory__L1ERC20 = await smock.mock('ERC20')
// deploy an ERC20 contract on L1
Factory__L1ERC20 = await smock.mock(
'@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20'
)
L1ERC20 = await Factory__L1ERC20.deploy('L1ERC20', 'ERC') L1ERC20 = await Factory__L1ERC20.deploy('L1ERC20', 'ERC')
L2StandardTokenFactory = await deploy('L2StandardTokenFactory')
L2StandardTokenFactory = await (
await ethers.getContractFactory('L2StandardTokenFactory')
).deploy()
}) })
describe('Standard token factory', () => { describe('Standard token factory', () => {
...@@ -36,18 +27,18 @@ describe('L2StandardTokenFactory', () => { ...@@ -36,18 +27,18 @@ describe('L2StandardTokenFactory', () => {
'L2ERC20', 'L2ERC20',
'ERC' 'ERC'
) )
// Pull the token creation event from the receipt
const receipt = await tx.wait() const receipt = await tx.wait()
const [tokenCreatedEvent] = receipt.events const tokenCreatedEvent = receipt.events[0]
// Expect there to be an event emmited for the standard token creation // Expect there to be an event emitted for the standard token creation
expect(tokenCreatedEvent.event).to.be.eq('StandardL2TokenCreated') expect(tokenCreatedEvent.event).to.be.eq('StandardL2TokenCreated')
// Get the L2 token address from the emmited event and check it was created correctly // Get the L2 token address from the emitted event and check it was created correctly
const l2TokenAddress = tokenCreatedEvent.args._l2Token const l2Token = await ethers.getContractAt(
const l2Token = new Contract( 'L2StandardERC20',
l2TokenAddress, tokenCreatedEvent.args._l2Token
getContractInterface('L2StandardERC20'),
signer
) )
expect(await l2Token.l2Bridge()).to.equal(predeploys.L2StandardBridge) expect(await l2Token.l2Bridge()).to.equal(predeploys.L2StandardBridge)
......
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { ContractFactory, Contract, Signer } from 'ethers' import { Contract } from 'ethers'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
import { expect } from '../../../setup' import { expect } from '../../../setup'
import { deploy } from '../../../helpers'
describe('OVM_ETH', () => { describe('OVM_ETH', () => {
let signer1: Signer let signer1: SignerWithAddress
let signer2: Signer let signer2: SignerWithAddress
before(async () => { before(async () => {
;[signer1, signer2] = await ethers.getSigners() ;[signer1, signer2] = await ethers.getSigners()
}) })
let Factory__OVM_ETH: ContractFactory
before(async () => {
Factory__OVM_ETH = await ethers.getContractFactory('OVM_ETH')
})
let OVM_ETH: Contract let OVM_ETH: Contract
beforeEach(async () => { beforeEach(async () => {
OVM_ETH = await Factory__OVM_ETH.deploy() OVM_ETH = await deploy('OVM_ETH')
}) })
describe('transfer', () => { describe('transfer', () => {
it('should revert', async () => { it('should revert', async () => {
await expect( await expect(OVM_ETH.transfer(signer2.address, 100)).to.be.revertedWith(
OVM_ETH.transfer(await signer2.getAddress(), 100)
).to.be.revertedWith(
'OVM_ETH: transfer is disabled pending further community discussion.' 'OVM_ETH: transfer is disabled pending further community discussion.'
) )
}) })
...@@ -33,9 +28,7 @@ describe('OVM_ETH', () => { ...@@ -33,9 +28,7 @@ describe('OVM_ETH', () => {
describe('approve', () => { describe('approve', () => {
it('should revert', async () => { it('should revert', async () => {
await expect( await expect(OVM_ETH.approve(signer2.address, 100)).to.be.revertedWith(
OVM_ETH.approve(await signer2.getAddress(), 100)
).to.be.revertedWith(
'OVM_ETH: approve is disabled pending further community discussion.' 'OVM_ETH: approve is disabled pending further community discussion.'
) )
}) })
...@@ -44,11 +37,7 @@ describe('OVM_ETH', () => { ...@@ -44,11 +37,7 @@ describe('OVM_ETH', () => {
describe('transferFrom', () => { describe('transferFrom', () => {
it('should revert', async () => { it('should revert', async () => {
await expect( await expect(
OVM_ETH.transferFrom( OVM_ETH.transferFrom(signer1.address, signer2.address, 100)
await signer1.getAddress(),
await signer2.getAddress(),
100
)
).to.be.revertedWith( ).to.be.revertedWith(
'OVM_ETH: transferFrom is disabled pending further community discussion.' 'OVM_ETH: transferFrom is disabled pending further community discussion.'
) )
...@@ -58,7 +47,7 @@ describe('OVM_ETH', () => { ...@@ -58,7 +47,7 @@ describe('OVM_ETH', () => {
describe('increaseAllowance', () => { describe('increaseAllowance', () => {
it('should revert', async () => { it('should revert', async () => {
await expect( await expect(
OVM_ETH.increaseAllowance(await signer2.getAddress(), 100) OVM_ETH.increaseAllowance(signer2.address, 100)
).to.be.revertedWith( ).to.be.revertedWith(
'OVM_ETH: increaseAllowance is disabled pending further community discussion.' 'OVM_ETH: increaseAllowance is disabled pending further community discussion.'
) )
...@@ -68,7 +57,7 @@ describe('OVM_ETH', () => { ...@@ -68,7 +57,7 @@ describe('OVM_ETH', () => {
describe('decreaseAllowance', () => { describe('decreaseAllowance', () => {
it('should revert', async () => { it('should revert', async () => {
await expect( await expect(
OVM_ETH.decreaseAllowance(await signer2.getAddress(), 100) OVM_ETH.decreaseAllowance(signer2.address, 100)
).to.be.revertedWith( ).to.be.revertedWith(
'OVM_ETH: decreaseAllowance is disabled pending further community discussion.' 'OVM_ETH: decreaseAllowance is disabled pending further community discussion.'
) )
......
/* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { ContractFactory, Contract, Signer } from 'ethers' import { Contract } from 'ethers'
import { calculateL1GasUsed, calculateL1Fee } from '@eth-optimism/core-utils' import { calculateL1GasUsed, calculateL1Fee } from '@eth-optimism/core-utils'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
import { expect } from '../../../setup' import { expect } from '../../../setup'
import { deploy } from '../../../helpers'
describe('OVM_GasPriceOracle', () => { describe('OVM_GasPriceOracle', () => {
const initialGasPrice = 0 const initialGasPrice = 0
let signer1: Signer
let signer2: Signer
before(async () => {
;[signer1, signer2] = await ethers.getSigners()
})
let Factory__OVM_GasPriceOracle: ContractFactory let signer1: SignerWithAddress
let signer2: SignerWithAddress
before(async () => { before(async () => {
Factory__OVM_GasPriceOracle = await ethers.getContractFactory( ;[signer1, signer2] = await ethers.getSigners()
'OVM_GasPriceOracle'
)
}) })
let OVM_GasPriceOracle: Contract let OVM_GasPriceOracle: Contract
beforeEach(async () => { beforeEach(async () => {
OVM_GasPriceOracle = await Factory__OVM_GasPriceOracle.deploy( OVM_GasPriceOracle = await deploy('OVM_GasPriceOracle', {
await signer1.getAddress() signer: signer1,
) args: [signer1.address],
})
await OVM_GasPriceOracle.setOverhead(2750) await OVM_GasPriceOracle.setOverhead(2750)
await OVM_GasPriceOracle.setScalar(1500000) await OVM_GasPriceOracle.setScalar(1500000)
...@@ -33,9 +29,7 @@ describe('OVM_GasPriceOracle', () => { ...@@ -33,9 +29,7 @@ describe('OVM_GasPriceOracle', () => {
describe('owner', () => { describe('owner', () => {
it('should have an owner', async () => { it('should have an owner', async () => {
expect(await OVM_GasPriceOracle.owner()).to.equal( expect(await OVM_GasPriceOracle.owner()).to.equal(signer1.address)
await signer1.getAddress()
)
}) })
}) })
...@@ -46,12 +40,11 @@ describe('OVM_GasPriceOracle', () => { ...@@ -46,12 +40,11 @@ describe('OVM_GasPriceOracle', () => {
}) })
it('should succeed if called by the owner and is equal to `0`', async () => { it('should succeed if called by the owner and is equal to `0`', async () => {
await expect(OVM_GasPriceOracle.connect(signer1).setGasPrice(0)).to.not.be await expect(OVM_GasPriceOracle.setGasPrice(0)).to.not.be.reverted
.reverted
}) })
it('should emit event', async () => { it('should emit event', async () => {
await expect(OVM_GasPriceOracle.connect(signer1).setGasPrice(100)) await expect(OVM_GasPriceOracle.setGasPrice(100))
.to.emit(OVM_GasPriceOracle, 'GasPriceUpdated') .to.emit(OVM_GasPriceOracle, 'GasPriceUpdated')
.withArgs(100) .withArgs(100)
}) })
...@@ -65,25 +58,18 @@ describe('OVM_GasPriceOracle', () => { ...@@ -65,25 +58,18 @@ describe('OVM_GasPriceOracle', () => {
it('should change when setGasPrice is called', async () => { it('should change when setGasPrice is called', async () => {
const gasPrice = 1234 const gasPrice = 1234
await OVM_GasPriceOracle.connect(signer1).setGasPrice(gasPrice) await OVM_GasPriceOracle.setGasPrice(gasPrice)
expect(await OVM_GasPriceOracle.gasPrice()).to.equal(gasPrice) expect(await OVM_GasPriceOracle.gasPrice()).to.equal(gasPrice)
}) })
it('is the 1st storage slot', async () => { it('is the 1st storage slot', async () => {
const gasPrice = 333433 await OVM_GasPriceOracle.setGasPrice(333433)
const slot = 1
// set the price
await OVM_GasPriceOracle.connect(signer1).setGasPrice(gasPrice)
// get the storage slot value
const priceAtSlot = await signer1.provider.getStorageAt(
OVM_GasPriceOracle.address,
slot
)
expect(await OVM_GasPriceOracle.gasPrice()).to.equal( expect(await OVM_GasPriceOracle.gasPrice()).to.equal(
ethers.BigNumber.from(priceAtSlot) ethers.BigNumber.from(
await signer1.provider.getStorageAt(OVM_GasPriceOracle.address, 1)
)
) )
}) })
}) })
...@@ -95,12 +81,11 @@ describe('OVM_GasPriceOracle', () => { ...@@ -95,12 +81,11 @@ describe('OVM_GasPriceOracle', () => {
}) })
it('should succeed if called by the owner', async () => { it('should succeed if called by the owner', async () => {
await expect(OVM_GasPriceOracle.connect(signer1).setL1BaseFee(0)).to.not await expect(OVM_GasPriceOracle.setL1BaseFee(0)).to.not.be.reverted
.be.reverted
}) })
it('should emit event', async () => { it('should emit event', async () => {
await expect(OVM_GasPriceOracle.connect(signer1).setL1BaseFee(100)) await expect(OVM_GasPriceOracle.setL1BaseFee(100))
.to.emit(OVM_GasPriceOracle, 'L1BaseFeeUpdated') .to.emit(OVM_GasPriceOracle, 'L1BaseFeeUpdated')
.withArgs(100) .withArgs(100)
}) })
...@@ -113,24 +98,17 @@ describe('OVM_GasPriceOracle', () => { ...@@ -113,24 +98,17 @@ describe('OVM_GasPriceOracle', () => {
it('should change when setL1BaseFee is called', async () => { it('should change when setL1BaseFee is called', async () => {
const baseFee = 1234 const baseFee = 1234
await OVM_GasPriceOracle.connect(signer1).setL1BaseFee(baseFee) await OVM_GasPriceOracle.setL1BaseFee(baseFee)
expect(await OVM_GasPriceOracle.l1BaseFee()).to.equal(baseFee) expect(await OVM_GasPriceOracle.l1BaseFee()).to.equal(baseFee)
}) })
it('is the 2nd storage slot', async () => { it('is the 2nd storage slot', async () => {
const baseFee = 12345 await OVM_GasPriceOracle.setGasPrice(12345)
const slot = 2
// set the price
await OVM_GasPriceOracle.connect(signer1).setGasPrice(baseFee)
// get the storage slot value
const priceAtSlot = await signer1.provider.getStorageAt(
OVM_GasPriceOracle.address,
slot
)
expect(await OVM_GasPriceOracle.l1BaseFee()).to.equal( expect(await OVM_GasPriceOracle.l1BaseFee()).to.equal(
ethers.BigNumber.from(priceAtSlot) ethers.BigNumber.from(
await signer1.provider.getStorageAt(OVM_GasPriceOracle.address, 2)
)
) )
}) })
}) })
...@@ -150,9 +128,10 @@ describe('OVM_GasPriceOracle', () => { ...@@ -150,9 +128,10 @@ describe('OVM_GasPriceOracle', () => {
for (const input of inputs) { for (const input of inputs) {
it(`case: ${input}`, async () => { it(`case: ${input}`, async () => {
const overhead = await OVM_GasPriceOracle.overhead() const overhead = await OVM_GasPriceOracle.overhead()
const cost = await OVM_GasPriceOracle.getL1GasUsed(input)
const expected = calculateL1GasUsed(input, overhead) expect(await OVM_GasPriceOracle.getL1GasUsed(input)).to.deep.equal(
expect(cost).to.deep.equal(expected) calculateL1GasUsed(input, overhead)
)
}) })
} }
}) })
...@@ -162,19 +141,16 @@ describe('OVM_GasPriceOracle', () => { ...@@ -162,19 +141,16 @@ describe('OVM_GasPriceOracle', () => {
it(`case: ${input}`, async () => { it(`case: ${input}`, async () => {
await OVM_GasPriceOracle.setGasPrice(1) await OVM_GasPriceOracle.setGasPrice(1)
await OVM_GasPriceOracle.setL1BaseFee(1) await OVM_GasPriceOracle.setL1BaseFee(1)
const decimals = await OVM_GasPriceOracle.decimals()
const overhead = await OVM_GasPriceOracle.overhead() expect(await OVM_GasPriceOracle.getL1Fee(input)).to.deep.equal(
const scalar = await OVM_GasPriceOracle.scalar() calculateL1Fee(
const l1BaseFee = await OVM_GasPriceOracle.l1BaseFee()
const l1Fee = await OVM_GasPriceOracle.getL1Fee(input)
const expected = calculateL1Fee(
input, input,
overhead, await OVM_GasPriceOracle.overhead(),
l1BaseFee, await OVM_GasPriceOracle.l1BaseFee(),
scalar, await OVM_GasPriceOracle.scalar(),
decimals await OVM_GasPriceOracle.decimals()
)
) )
expect(l1Fee).to.deep.equal(expected)
}) })
} }
}) })
...@@ -186,12 +162,11 @@ describe('OVM_GasPriceOracle', () => { ...@@ -186,12 +162,11 @@ describe('OVM_GasPriceOracle', () => {
}) })
it('should succeed if called by the owner', async () => { it('should succeed if called by the owner', async () => {
await expect(OVM_GasPriceOracle.connect(signer1).setOverhead(0)).to.not.be await expect(OVM_GasPriceOracle.setOverhead(0)).to.not.be.reverted
.reverted
}) })
it('should emit event', async () => { it('should emit event', async () => {
await expect(OVM_GasPriceOracle.connect(signer1).setOverhead(100)) await expect(OVM_GasPriceOracle.setOverhead(100))
.to.emit(OVM_GasPriceOracle, 'OverheadUpdated') .to.emit(OVM_GasPriceOracle, 'OverheadUpdated')
.withArgs(100) .withArgs(100)
}) })
...@@ -204,24 +179,17 @@ describe('OVM_GasPriceOracle', () => { ...@@ -204,24 +179,17 @@ describe('OVM_GasPriceOracle', () => {
it('should change when setOverhead is called', async () => { it('should change when setOverhead is called', async () => {
const overhead = 6657 const overhead = 6657
await OVM_GasPriceOracle.connect(signer1).setOverhead(overhead) await OVM_GasPriceOracle.setOverhead(overhead)
expect(await OVM_GasPriceOracle.overhead()).to.equal(overhead) expect(await OVM_GasPriceOracle.overhead()).to.equal(overhead)
}) })
it('is the 3rd storage slot', async () => { it('is the 3rd storage slot', async () => {
const overhead = 119090 await OVM_GasPriceOracle.setOverhead(119090)
const slot = 3
// set the price
await OVM_GasPriceOracle.connect(signer1).setOverhead(overhead)
// get the storage slot value
const priceAtSlot = await signer1.provider.getStorageAt(
OVM_GasPriceOracle.address,
slot
)
expect(await OVM_GasPriceOracle.overhead()).to.equal( expect(await OVM_GasPriceOracle.overhead()).to.equal(
ethers.BigNumber.from(priceAtSlot) ethers.BigNumber.from(
await signer1.provider.getStorageAt(OVM_GasPriceOracle.address, 3)
)
) )
}) })
}) })
...@@ -233,12 +201,11 @@ describe('OVM_GasPriceOracle', () => { ...@@ -233,12 +201,11 @@ describe('OVM_GasPriceOracle', () => {
}) })
it('should succeed if called by the owner', async () => { it('should succeed if called by the owner', async () => {
await expect(OVM_GasPriceOracle.connect(signer1).setScalar(0)).to.not.be await expect(OVM_GasPriceOracle.setScalar(0)).to.not.be.reverted
.reverted
}) })
it('should emit event', async () => { it('should emit event', async () => {
await expect(OVM_GasPriceOracle.connect(signer1).setScalar(100)) await expect(OVM_GasPriceOracle.setScalar(100))
.to.emit(OVM_GasPriceOracle, 'ScalarUpdated') .to.emit(OVM_GasPriceOracle, 'ScalarUpdated')
.withArgs(100) .withArgs(100)
}) })
...@@ -251,39 +218,27 @@ describe('OVM_GasPriceOracle', () => { ...@@ -251,39 +218,27 @@ describe('OVM_GasPriceOracle', () => {
it('should change when setScalar is called', async () => { it('should change when setScalar is called', async () => {
const scalar = 9999 const scalar = 9999
await OVM_GasPriceOracle.connect(signer1).setScalar(scalar) await OVM_GasPriceOracle.setScalar(scalar)
expect(await OVM_GasPriceOracle.scalar()).to.equal(scalar) expect(await OVM_GasPriceOracle.scalar()).to.equal(scalar)
}) })
it('is the 4rd storage slot', async () => { it('is the 4rd storage slot', async () => {
const overhead = 111111 await OVM_GasPriceOracle.setScalar(111111)
const slot = 4
// set the price
await OVM_GasPriceOracle.connect(signer1).setScalar(overhead)
// get the storage slot value
const priceAtSlot = await signer1.provider.getStorageAt(
OVM_GasPriceOracle.address,
slot
)
expect(await OVM_GasPriceOracle.scalar()).to.equal( expect(await OVM_GasPriceOracle.scalar()).to.equal(
ethers.BigNumber.from(priceAtSlot) ethers.BigNumber.from(
await signer1.provider.getStorageAt(OVM_GasPriceOracle.address, 4)
)
) )
}) })
}) })
describe('decimals', () => { describe('decimals', () => {
it('is the 5th storage slot', async () => { it('is the 5th storage slot', async () => {
const slot = 5
// get the storage slot value
const priceAtSlot = await signer1.provider.getStorageAt(
OVM_GasPriceOracle.address,
slot
)
expect(await OVM_GasPriceOracle.decimals()).to.equal( expect(await OVM_GasPriceOracle.decimals()).to.equal(
ethers.BigNumber.from(priceAtSlot) ethers.BigNumber.from(
await signer1.provider.getStorageAt(OVM_GasPriceOracle.address, 5)
)
) )
}) })
}) })
......
...@@ -28,7 +28,7 @@ describe.skip('OVM_L2ToL1MessagePasser', () => { ...@@ -28,7 +28,7 @@ describe.skip('OVM_L2ToL1MessagePasser', () => {
let Fake__OVM_ExecutionManager: FakeContract let Fake__OVM_ExecutionManager: FakeContract
before(async () => { before(async () => {
Fake__OVM_ExecutionManager = await smock.fake<Contract>( Fake__OVM_ExecutionManager = await smock.fake<Contract>(
await ethers.getContractFactory('OVM_ExecutionManager') 'OVM_ExecutionManager'
) )
}) })
......
...@@ -39,9 +39,7 @@ describe('Lib_MerkleTree', () => { ...@@ -39,9 +39,7 @@ describe('Lib_MerkleTree', () => {
await ethers.getContractFactory('TestLib_MerkleTree') await ethers.getContractFactory('TestLib_MerkleTree')
).deploy() ).deploy()
Fake__LibMerkleTree = await smock.fake( Fake__LibMerkleTree = await smock.fake('TestLib_MerkleTree')
await ethers.getContractFactory('TestLib_MerkleTree')
)
}) })
describe('getMerkleRoot', () => { describe('getMerkleRoot', () => {
......
import { toRpcHexString } from '@eth-optimism/core-utils'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
import { BigNumber } from 'ethers'
import hre from 'hardhat'
export const impersonate = async (
address: string,
balance?: string | number | BigNumber
): Promise<SignerWithAddress> => {
await hre.network.provider.request({
method: 'hardhat_impersonateAccount',
params: [address],
})
if (balance !== undefined) {
await hre.network.provider.request({
method: 'hardhat_setBalance',
params: [address, toRpcHexString(BigNumber.from(balance))],
})
}
return hre.ethers.getSigner(address)
}
export * from './eth-time' export * from './eth-time'
export * from './deploy' export * from './deploy'
export * from './impersonation'
...@@ -76,8 +76,8 @@ components: ...@@ -76,8 +76,8 @@ components:
type: string type: string
queueOrigin: queueOrigin:
type: string type: string
queueIndex: number queueIndex:
type: string type: number
decoded: decoded:
type: object type: object
$ref: '#/components/schemas/DecodedSequencerBatchTransaction' $ref: '#/components/schemas/DecodedSequencerBatchTransaction'
......
...@@ -18,6 +18,7 @@ type HealthcheckMetrics = { ...@@ -18,6 +18,7 @@ type HealthcheckMetrics = {
isCurrentlyDiverged: Gauge isCurrentlyDiverged: Gauge
referenceHeight: Gauge referenceHeight: Gauge
targetHeight: Gauge targetHeight: Gauge
heightDifference: Gauge
targetConnectionFailures: Counter targetConnectionFailures: Counter
referenceConnectionFailures: Counter referenceConnectionFailures: Counter
} }
...@@ -66,6 +67,10 @@ export class HealthcheckService extends BaseServiceV2< ...@@ -66,6 +67,10 @@ export class HealthcheckService extends BaseServiceV2<
type: Gauge, type: Gauge,
desc: 'Block height of the target client', desc: 'Block height of the target client',
}, },
heightDifference: {
type: Gauge,
desc: 'Difference in block heights between the two clients',
},
targetConnectionFailures: { targetConnectionFailures: {
type: Counter, type: Counter,
desc: 'Number of connection failures to the target client', desc: 'Number of connection failures to the target client',
...@@ -109,23 +114,36 @@ export class HealthcheckService extends BaseServiceV2< ...@@ -109,23 +114,36 @@ export class HealthcheckService extends BaseServiceV2<
} }
} }
// Later logic will depend on the height difference.
const heightDiff = Math.abs(referenceLatest.number - targetLatest.number)
const minBlock = Math.min(targetLatest.number, referenceLatest.number)
// Update these metrics first so they'll refresh no matter what. // Update these metrics first so they'll refresh no matter what.
this.metrics.targetHeight.set(targetLatest.number) this.metrics.targetHeight.set(targetLatest.number)
this.metrics.referenceHeight.set(referenceLatest.number) this.metrics.referenceHeight.set(referenceLatest.number)
this.metrics.heightDifference.set(heightDiff)
this.logger.info(`latest block heights`, { this.logger.info(`latest block heights`, {
targetHeight: targetLatest.number, targetHeight: targetLatest.number,
referenceHeight: referenceLatest.number, referenceHeight: referenceLatest.number,
heightDifference: referenceLatest.number - targetLatest.number, heightDifference: heightDiff,
minBlockNumber: minBlock,
}) })
const referenceCorresponding = const reference = await this.options.referenceRpcProvider.getBlock(minBlock)
await this.options.referenceRpcProvider.getBlock(targetLatest.number) if (!reference) {
// This is ok, but we should log it and restart the loop.
this.logger.info(`reference block was not found`, {
blockNumber: reference.number,
})
return
}
if (!referenceCorresponding) { const target = await this.options.targetRpcProvider.getBlock(minBlock)
if (!target) {
// This is ok, but we should log it and restart the loop. // This is ok, but we should log it and restart the loop.
this.logger.info(`reference client does not have block yet`, { this.logger.info(`target block was not found`, {
blockNumber: targetLatest.number, blockNumber: target.number,
}) })
return return
} }
...@@ -134,9 +152,11 @@ export class HealthcheckService extends BaseServiceV2< ...@@ -134,9 +152,11 @@ export class HealthcheckService extends BaseServiceV2<
// catch discrepancies in blocks that may not impact the state. For example, if clients have // catch discrepancies in blocks that may not impact the state. For example, if clients have
// blocks with two different timestamps, the state root will only diverge if the timestamp is // blocks with two different timestamps, the state root will only diverge if the timestamp is
// actually used during the transaction(s) within the block. // actually used during the transaction(s) within the block.
if (referenceCorresponding.hash !== targetLatest.hash) { if (reference.hash !== target.hash) {
this.logger.error(`reference client has different hash for block`, { this.logger.error(`reference client has different hash for block`, {
blockNumber: targetLatest.number, blockNumber: target.number,
referenceHash: reference.hash,
targetHash: target.hash,
}) })
// The main loop polls for "latest" so aren't checking every block. We need to use a binary // The main loop polls for "latest" so aren't checking every block. We need to use a binary
...@@ -144,7 +164,7 @@ export class HealthcheckService extends BaseServiceV2< ...@@ -144,7 +164,7 @@ export class HealthcheckService extends BaseServiceV2<
this.logger.info(`beginning binary search to find first mismatched block`) this.logger.info(`beginning binary search to find first mismatched block`)
let start = 0 let start = 0
let end = targetLatest.number let end = target.number
while (start !== end) { while (start !== end) {
const mid = Math.floor((start + end) / 2) const mid = Math.floor((start + end) / 2)
this.logger.info(`checking block`, { blockNumber: mid }) this.logger.info(`checking block`, { blockNumber: mid })
...@@ -171,11 +191,11 @@ export class HealthcheckService extends BaseServiceV2< ...@@ -171,11 +191,11 @@ export class HealthcheckService extends BaseServiceV2<
} }
this.logger.info(`blocks are matching`, { this.logger.info(`blocks are matching`, {
blockNumber: targetLatest.number, blockNumber: target.number,
}) })
// Update latest matching state root height and reset the diverged metric in case it was set. // Update latest matching state root height and reset the diverged metric in case it was set.
this.metrics.lastMatchingStateRootHeight.set(targetLatest.number) this.metrics.lastMatchingStateRootHeight.set(target.number)
this.metrics.isCurrentlyDiverged.set(0) this.metrics.isCurrentlyDiverged.set(0)
} }
} }
......
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
import { ethers, Contract, Overrides, Signer, BigNumber } from 'ethers' import {
ethers,
Contract,
Overrides,
Signer,
BigNumber,
CallOverrides,
} from 'ethers'
import { import {
TransactionRequest, TransactionRequest,
TransactionResponse, TransactionResponse,
...@@ -350,7 +357,7 @@ export class StandardBridgeAdapter implements IBridgeAdapter { ...@@ -350,7 +357,7 @@ export class StandardBridgeAdapter implements IBridgeAdapter {
l2Token: AddressLike, l2Token: AddressLike,
amount: NumberLike, amount: NumberLike,
opts?: { opts?: {
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> => { ): Promise<BigNumber> => {
return this.messenger.l1Provider.estimateGas( return this.messenger.l1Provider.estimateGas(
...@@ -365,7 +372,7 @@ export class StandardBridgeAdapter implements IBridgeAdapter { ...@@ -365,7 +372,7 @@ export class StandardBridgeAdapter implements IBridgeAdapter {
opts?: { opts?: {
recipient?: AddressLike recipient?: AddressLike
l2GasLimit?: NumberLike l2GasLimit?: NumberLike
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> => { ): Promise<BigNumber> => {
return this.messenger.l1Provider.estimateGas( return this.messenger.l1Provider.estimateGas(
...@@ -379,7 +386,7 @@ export class StandardBridgeAdapter implements IBridgeAdapter { ...@@ -379,7 +386,7 @@ export class StandardBridgeAdapter implements IBridgeAdapter {
amount: NumberLike, amount: NumberLike,
opts?: { opts?: {
recipient?: AddressLike recipient?: AddressLike
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> => { ): Promise<BigNumber> => {
return this.messenger.l2Provider.estimateGas( return this.messenger.l2Provider.estimateGas(
......
...@@ -7,7 +7,7 @@ import { ...@@ -7,7 +7,7 @@ import {
TransactionRequest, TransactionRequest,
} from '@ethersproject/abstract-provider' } from '@ethersproject/abstract-provider'
import { Signer } from '@ethersproject/abstract-signer' import { Signer } from '@ethersproject/abstract-signer'
import { ethers, BigNumber, Overrides } from 'ethers' import { ethers, BigNumber, Overrides, CallOverrides } from 'ethers'
import { sleep, remove0x } from '@eth-optimism/core-utils' import { sleep, remove0x } from '@eth-optimism/core-utils'
import { predeploys } from '@eth-optimism/contracts' import { predeploys } from '@eth-optimism/contracts'
...@@ -1128,7 +1128,7 @@ export class CrossChainMessenger implements ICrossChainMessenger { ...@@ -1128,7 +1128,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
message: CrossChainMessageRequest, message: CrossChainMessageRequest,
opts?: { opts?: {
l2GasLimit?: NumberLike l2GasLimit?: NumberLike
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> => { ): Promise<BigNumber> => {
const tx = await this.populateTransaction.sendMessage(message, opts) const tx = await this.populateTransaction.sendMessage(message, opts)
...@@ -1143,7 +1143,7 @@ export class CrossChainMessenger implements ICrossChainMessenger { ...@@ -1143,7 +1143,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
message: MessageLike, message: MessageLike,
messageGasLimit: NumberLike, messageGasLimit: NumberLike,
opts?: { opts?: {
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> => { ): Promise<BigNumber> => {
return this.l1Provider.estimateGas( return this.l1Provider.estimateGas(
...@@ -1158,7 +1158,7 @@ export class CrossChainMessenger implements ICrossChainMessenger { ...@@ -1158,7 +1158,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
finalizeMessage: async ( finalizeMessage: async (
message: MessageLike, message: MessageLike,
opts?: { opts?: {
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> => { ): Promise<BigNumber> => {
return this.l1Provider.estimateGas( return this.l1Provider.estimateGas(
...@@ -1171,7 +1171,7 @@ export class CrossChainMessenger implements ICrossChainMessenger { ...@@ -1171,7 +1171,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
opts?: { opts?: {
recipient?: AddressLike recipient?: AddressLike
l2GasLimit?: NumberLike l2GasLimit?: NumberLike
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> => { ): Promise<BigNumber> => {
return this.l1Provider.estimateGas( return this.l1Provider.estimateGas(
...@@ -1183,7 +1183,7 @@ export class CrossChainMessenger implements ICrossChainMessenger { ...@@ -1183,7 +1183,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
amount: NumberLike, amount: NumberLike,
opts?: { opts?: {
recipient?: AddressLike recipient?: AddressLike
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> => { ): Promise<BigNumber> => {
return this.l2Provider.estimateGas( return this.l2Provider.estimateGas(
...@@ -1196,7 +1196,7 @@ export class CrossChainMessenger implements ICrossChainMessenger { ...@@ -1196,7 +1196,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
l2Token: AddressLike, l2Token: AddressLike,
amount: NumberLike, amount: NumberLike,
opts?: { opts?: {
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> => { ): Promise<BigNumber> => {
return this.l1Provider.estimateGas( return this.l1Provider.estimateGas(
...@@ -1216,7 +1216,7 @@ export class CrossChainMessenger implements ICrossChainMessenger { ...@@ -1216,7 +1216,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
opts?: { opts?: {
recipient?: AddressLike recipient?: AddressLike
l2GasLimit?: NumberLike l2GasLimit?: NumberLike
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> => { ): Promise<BigNumber> => {
return this.l1Provider.estimateGas( return this.l1Provider.estimateGas(
...@@ -1235,7 +1235,7 @@ export class CrossChainMessenger implements ICrossChainMessenger { ...@@ -1235,7 +1235,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
amount: NumberLike, amount: NumberLike,
opts?: { opts?: {
recipient?: AddressLike recipient?: AddressLike
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> => { ): Promise<BigNumber> => {
return this.l2Provider.estimateGas( return this.l2Provider.estimateGas(
......
import { Contract, Overrides, Signer, BigNumber } from 'ethers' import { Contract, Overrides, Signer, BigNumber, CallOverrides } from 'ethers'
import { import {
TransactionRequest, TransactionRequest,
TransactionResponse, TransactionResponse,
...@@ -250,7 +250,7 @@ export interface IBridgeAdapter { ...@@ -250,7 +250,7 @@ export interface IBridgeAdapter {
l2Token: AddressLike, l2Token: AddressLike,
amount: NumberLike, amount: NumberLike,
opts?: { opts?: {
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> ): Promise<BigNumber>
...@@ -273,7 +273,7 @@ export interface IBridgeAdapter { ...@@ -273,7 +273,7 @@ export interface IBridgeAdapter {
opts?: { opts?: {
recipient?: AddressLike recipient?: AddressLike
l2GasLimit?: NumberLike l2GasLimit?: NumberLike
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> ): Promise<BigNumber>
...@@ -294,7 +294,7 @@ export interface IBridgeAdapter { ...@@ -294,7 +294,7 @@ export interface IBridgeAdapter {
amount: NumberLike, amount: NumberLike,
opts?: { opts?: {
recipient?: AddressLike recipient?: AddressLike
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> ): Promise<BigNumber>
} }
......
import { Event, BigNumber, Overrides } from 'ethers' import { Event, BigNumber, Overrides, CallOverrides } from 'ethers'
import { import {
Provider, Provider,
BlockTag, BlockTag,
...@@ -697,7 +697,7 @@ export interface ICrossChainMessenger { ...@@ -697,7 +697,7 @@ export interface ICrossChainMessenger {
message: CrossChainMessageRequest, message: CrossChainMessageRequest,
opts?: { opts?: {
l2GasLimit?: NumberLike l2GasLimit?: NumberLike
overrides?: Overrides overrides?: CallOverrides
} }
) => Promise<BigNumber> ) => Promise<BigNumber>
...@@ -714,7 +714,7 @@ export interface ICrossChainMessenger { ...@@ -714,7 +714,7 @@ export interface ICrossChainMessenger {
message: MessageLike, message: MessageLike,
messageGasLimit: NumberLike, messageGasLimit: NumberLike,
opts?: { opts?: {
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> ): Promise<BigNumber>
...@@ -729,7 +729,7 @@ export interface ICrossChainMessenger { ...@@ -729,7 +729,7 @@ export interface ICrossChainMessenger {
finalizeMessage( finalizeMessage(
message: MessageLike, message: MessageLike,
opts?: { opts?: {
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> ): Promise<BigNumber>
...@@ -748,7 +748,7 @@ export interface ICrossChainMessenger { ...@@ -748,7 +748,7 @@ export interface ICrossChainMessenger {
l2Token: AddressLike, l2Token: AddressLike,
amount: NumberLike, amount: NumberLike,
opts?: { opts?: {
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> ): Promise<BigNumber>
...@@ -767,7 +767,7 @@ export interface ICrossChainMessenger { ...@@ -767,7 +767,7 @@ export interface ICrossChainMessenger {
opts?: { opts?: {
recipient?: AddressLike recipient?: AddressLike
l2GasLimit?: NumberLike l2GasLimit?: NumberLike
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> ): Promise<BigNumber>
...@@ -784,7 +784,7 @@ export interface ICrossChainMessenger { ...@@ -784,7 +784,7 @@ export interface ICrossChainMessenger {
amount: NumberLike, amount: NumberLike,
opts?: { opts?: {
recipient?: AddressLike recipient?: AddressLike
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> ): Promise<BigNumber>
...@@ -807,7 +807,7 @@ export interface ICrossChainMessenger { ...@@ -807,7 +807,7 @@ export interface ICrossChainMessenger {
opts?: { opts?: {
recipient?: AddressLike recipient?: AddressLike
l2GasLimit?: NumberLike l2GasLimit?: NumberLike
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> ): Promise<BigNumber>
...@@ -828,7 +828,7 @@ export interface ICrossChainMessenger { ...@@ -828,7 +828,7 @@ export interface ICrossChainMessenger {
amount: NumberLike, amount: NumberLike,
opts?: { opts?: {
recipient?: AddressLike recipient?: AddressLike
overrides?: Overrides overrides?: CallOverrides
} }
): Promise<BigNumber> ): Promise<BigNumber>
} }
......
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