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
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:
go-builder:
......@@ -15,29 +6,6 @@ executors:
- image: ethereumoptimism/go-builder:latest
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:
parameters:
working_directory:
......@@ -59,118 +27,6 @@ commands:
path: /test-results
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:
parameters:
working_directory:
......@@ -400,6 +256,55 @@ jobs:
command: yarn test:coverage
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:
main:
......@@ -462,166 +367,122 @@ workflows:
- geth-tests
- integration-tests
nightly-itests:
nightly:
triggers:
- schedule:
cron: "0 1 * * * "
cron: "0 10 * * *"
filters:
branches:
only:
- develop
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:
- optimism
post-steps:
- slack/notify:
channel: $SLACK_DEFAULT_CHANNEL
event: fail
custom: |
{
"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:
- docker-publish:
name: gas-oracle-release
docker_file: ops/docker/Dockerfile.gas-oracle
docker_tags: ethereumoptimism/gas-oracle:nightly
docker_context: .
context:
- optimism
- slack
<<: *slack-nightly-build-fail-post-step
- build-deployer:
- docker-publish:
name: hardhat-node-release
docker_file: ops/docker/hardhat/Dockerfile
docker_tags: ethereumoptimism/hardhat-node:nightly
docker_context: ops/docker/hardhat
context:
- optimism
- slack
<<: *slack-nightly-build-fail-post-step
- build-l2geth:
- docker-publish:
name: go-builder-release
docker_file: ops/docker/go-builder/Dockerfile
docker_tags: ethereumoptimism/go-builder:nightly
docker_context: .
context:
- optimism
- slack
<<: *slack-nightly-build-fail-post-step
- build-gas-oracle:
- docker-publish:
name: js-builder-release
docker_file: ops/docker/js-builder/Dockerfile
docker_tags: ethereumoptimism/js-builder:nightly
docker_context: .
context:
- optimism
- slack
<<: *slack-nightly-build-fail-post-step
- build-integration-tests:
- docker-publish:
name: proxyd-release
docker_file: go/proxyd/Dockerfile
docker_tags: ethereumoptimism/proxyd:nightly
docker_context: .
context:
- optimism
- slack
<<: *slack-nightly-build-fail-post-step
- build-go-batch-submitter:
- docker-publish:
name: l2geth-exporter-release
docker_file: ops/docker/Dockerfile.l2geth-exporter
docker_tags: ethereumoptimism/l2geth-exporter:nightly
docker_context: .
context:
- optimism
- slack
<<: *slack-nightly-build-fail-post-step
- build-proxyd:
- docker-publish:
name: op-exporter-release
docker_file: ops/docker/Dockerfile.op-exporter
docker_tags: ethereumoptimism/op-exporter:nightly
docker_context: .
context:
- optimism
- slack
<<: *slack-nightly-build-fail-post-step
- deploy-nightly:
- docker-publish:
name: message-relayer-release
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:
- 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:
- "#review-threads-unresolved=0"
- "#approved-reviews-by>=2"
- "#changes-requested-reviews-by=0"
- "label!=do-not-merge"
- or:
- and:
- "label!=SR-Risk"
......@@ -100,7 +101,7 @@ pull_request_rules:
- name: Nag changesets
conditions:
- and:
- 'files~=\.(ts|go|js|mod|sum)$'
- 'files~=\.((?<!\.spec\.)ts|go|js|mod|sum)$'
- '-files~=^\.changeset/(.*)\.md'
actions:
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
import (
"database/sql"
"errors"
"fmt"
"strings"
l2common "github.com/ethereum-optimism/optimism/l2geth/common"
"github.com/ethereum/go-ethereum/common"
......@@ -17,6 +19,31 @@ type Database struct {
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.
// NOTE: "It is rarely necessary to close a DB."
// See: https://pkg.go.dev/database/sql#Open
......@@ -633,27 +660,38 @@ func (d *Database) GetIndexedL1BlockByHash(hash common.Hash) (*IndexedL1Block, e
return block, nil
}
// 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
const getAirdropQuery = `
SELECT
address, voter_amount, multisig_signer_amount, gitcoin_amount,
active_bridged_amount, op_user_amount, op_repeat_user_amount,
bonus_amount, total_amount
FROM airdrops
WHERE address = $1
`
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())
}
err = db.Ping()
if err != nil {
return nil, 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 {
return nil, err
}
if err != nil {
return nil, fmt.Errorf("error scanning airdrop: %v", err)
}
return &Database{
db: db,
config: config,
}, nil
return airdrop, nil
}
......@@ -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);
`
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{
createL1BlocksTable,
createL2BlocksTable,
......@@ -118,4 +133,5 @@ var schema = []string{
createDepositsTable,
createWithdrawalsTable,
createL1L2NumberIndex,
createAirdropsTable,
}
......@@ -9,6 +9,8 @@ import (
"strconv"
"time"
"github.com/ethereum-optimism/optimism/go/indexer/services"
l2rpc "github.com/ethereum-optimism/optimism/l2geth/rpc"
"github.com/ethereum-optimism/optimism/go/indexer/metrics"
......@@ -83,8 +85,10 @@ type Indexer struct {
l1IndexingService *l1.Service
l2IndexingService *l2.Service
airdropService *services.Airdrop
router *mux.Router
router *mux.Router
metrics *metrics.Metrics
}
// NewIndexer initializes the Indexer, gathering any resources
......@@ -201,7 +205,9 @@ func NewIndexer(cfg Config, gitVersion string) (*Indexer, error) {
l2Client: l2Client,
l1IndexingService: l1IndexingService,
l2IndexingService: l2IndexingService,
airdropService: services.NewAirdrop(db, m),
router: mux.NewRouter(),
metrics: m,
}, nil
}
......@@ -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/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/airdrops/0x{address:[a-fA-F0-9]{40}}", b.airdropService.GetAirdrop)
b.router.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
_, err := w.Write([]byte("OK"))
......@@ -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)
addr := fmt.Sprintf("%s:%s", b.cfg.RESTHostname, port)
......
......@@ -3,6 +3,8 @@ package metrics
import (
"fmt"
"net/http"
"strconv"
"time"
l2common "github.com/ethereum-optimism/optimism/l2geth/common"
"github.com/ethereum/go-ethereum/common"
......@@ -32,6 +34,12 @@ type Metrics struct {
CachedTokensCount *prometheus.CounterVec
HTTPRequestsCount prometheus.Counter
HTTPResponsesCount *prometheus.CounterVec
HTTPRequestDurationSecs prometheus.Summary
tokenAddrs map[string]string
}
......@@ -110,6 +118,27 @@ func NewMetrics(monitoredTokens map[string]string) *Metrics {
"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,
}
}
......@@ -176,6 +205,15 @@ func (m *Metrics) IncL2CachedTokensCount() {
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) {
mux := http.NewServeMux()
mux.Handle("/metrics", promhttp.Handler())
......
......@@ -6,6 +6,8 @@ import (
"runtime/debug"
"time"
"github.com/ethereum-optimism/optimism/go/indexer/metrics"
"github.com/ethereum/go-ethereum/log"
)
......@@ -50,7 +52,7 @@ func (rw *responseWriter) WriteHeader(code int) {
}
// 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 {
fn := func(w http.ResponseWriter, r *http.Request) {
defer func() {
......@@ -64,16 +66,19 @@ func LoggingMiddleware(logger log.Logger) func(http.Handler) http.Handler {
}
}()
metrics.RecordHTTPRequest()
start := time.Now()
wrapped := wrapResponseWriter(w)
next.ServeHTTP(wrapped, r)
dur := time.Since(start)
logger.Info(
"served request",
"status", wrapped.status,
"method", r.Method,
"path", r.URL.EscapedPath(),
"duration", time.Since(start),
"duration", dur,
)
metrics.RecordHTTPResponse(wrapped.status, dur)
}
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 {
func (e *EthBridge) GetDepositsByBlockRange(start, end uint64) (DepositsMap, error) {
depositsByBlockhash := make(DepositsMap)
iter, err := FilterETHDepositInitiatedWithRetry(e.filterer, &bind.FilterOpts{
Start: start,
End: &end,
Context: e.ctx,
iter, err := FilterETHDepositInitiatedWithRetry(e.ctx, e.filterer, &bind.FilterOpts{
Start: start,
End: &end,
})
if err != nil {
logger.Error("Error fetching filter", "err", err)
......
......@@ -15,54 +15,48 @@ var clientRetryInterval = 5 * time.Second
// FilterStateBatchAppendedWithRetry retries the given func until it succeeds,
// 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 {
ctxt, cancel := context.WithTimeout(opts.Context, DefaultConnectionTimeout)
ctxt, cancel := context.WithTimeout(ctx, DefaultConnectionTimeout)
opts.Context = ctxt
res, err := filterer.FilterStateBatchAppended(opts, nil)
switch err {
case nil:
cancel()
return res, err
default:
logger.Error("Error fetching filter", "err", err)
cancel()
if err == nil {
return res, nil
}
logger.Error("Error fetching filter", "err", err)
time.Sleep(clientRetryInterval)
}
}
// FilterETHDepositInitiatedWithRetry retries the given func until it succeeds,
// 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 {
ctxt, cancel := context.WithTimeout(opts.Context, DefaultConnectionTimeout)
ctxt, cancel := context.WithTimeout(ctx, DefaultConnectionTimeout)
opts.Context = ctxt
res, err := filterer.FilterETHDepositInitiated(opts, nil, nil)
switch err {
case nil:
cancel()
return res, err
default:
logger.Error("Error fetching filter", "err", err)
cancel()
if err == nil {
return res, nil
}
logger.Error("Error fetching filter", "err", err)
time.Sleep(clientRetryInterval)
}
}
// FilterERC20DepositInitiatedWithRetry retries the given func until it succeeds,
// 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 {
ctxt, cancel := context.WithTimeout(opts.Context, DefaultConnectionTimeout)
ctxt, cancel := context.WithTimeout(ctx, DefaultConnectionTimeout)
opts.Context = ctxt
res, err := filterer.FilterERC20DepositInitiated(opts, nil, nil, nil)
switch err {
case nil:
cancel()
return res, err
default:
logger.Error("Error fetching filter", "err", err)
cancel()
if err == nil {
return res, nil
}
logger.Error("Error fetching filter", "err", err)
time.Sleep(clientRetryInterval)
}
}
......@@ -24,10 +24,9 @@ func (s *StandardBridge) Address() common.Address {
func (s *StandardBridge) GetDepositsByBlockRange(start, end uint64) (DepositsMap, error) {
depositsByBlockhash := make(DepositsMap)
iter, err := FilterERC20DepositInitiatedWithRetry(s.filterer, &bind.FilterOpts{
Start: start,
End: &end,
Context: s.ctx,
iter, err := FilterERC20DepositInitiatedWithRetry(s.ctx, s.filterer, &bind.FilterOpts{
Start: start,
End: &end,
})
if err != nil {
logger.Error("Error fetching filter", "err", err)
......
......@@ -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) {
batches := make(map[common.Hash][]db.StateBatch)
iter, err := bridge.FilterStateBatchAppendedWithRetry(filterer, &bind.FilterOpts{
Start: startHeight,
End: &endHeight,
Context: ctx,
iter, err := bridge.FilterStateBatchAppendedWithRetry(ctx, filterer, &bind.FilterOpts{
Start: startHeight,
End: &endHeight,
})
if err != nil {
return nil, err
......
......@@ -14,18 +14,16 @@ var clientRetryInterval = 5 * time.Second
// FilterWithdrawalInitiatedWithRetry retries the given func until it succeeds,
// 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 {
ctxt, cancel := context.WithTimeout(opts.Context, DefaultConnectionTimeout)
ctxt, cancel := context.WithTimeout(ctx, DefaultConnectionTimeout)
opts.Context = ctxt
res, err := filterer.FilterWithdrawalInitiated(opts, nil, nil, nil)
switch err {
case nil:
cancel()
return res, err
default:
logger.Error("Error fetching filter", "err", err)
cancel()
if err == nil {
return res, nil
}
logger.Error("Error fetching filter", "err", err)
time.Sleep(clientRetryInterval)
}
}
......@@ -25,10 +25,9 @@ func (s *StandardBridge) Address() common.Address {
func (s *StandardBridge) GetWithdrawalsByBlockRange(start, end uint64) (WithdrawalsMap, error) {
withdrawalsByBlockhash := make(map[common.Hash][]db.Withdrawal)
iter, err := FilterWithdrawalInitiatedWithRetry(s.filterer, &bind.FilterOpts{
Start: start,
End: &end,
Context: s.ctx,
iter, err := FilterWithdrawalInitiatedWithRetry(s.ctx, s.filterer, &bind.FilterOpts{
Start: start,
End: &end,
})
if err != nil {
logger.Error("Error fetching filter", "err", err)
......
......@@ -25,10 +25,10 @@ lint:
golangci-lint run ./...
binding:
$(eval temp := $(shell mktemp))
$(eval tempCTC := $(shell mktemp))
cat ../../packages/contracts/deployments/mainnet/CanonicalTransactionChain.json \
| jq -r .bytecode > $(temp)
| jq -r .bytecode > $(tempCTC)
cat ../../packages/contracts/deployments/mainnet/CanonicalTransactionChain.json \
| jq .abi \
......@@ -36,6 +36,21 @@ binding:
--abi - \
--out bindings/CanonicalTransactionChain.go \
--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 (
//Define the metrics we wish to expose
var (
ctcTotalElements = prometheus.NewGaugeVec(
addressTotalElements = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "l2geth_ctc_total_elements",
Help: "CTC GetTotalElements value."},
[]string{"state"},
Name: "l2geth_total_elements",
Help: "GetTotalElements value."},
[]string{"state", "address"},
)
ctcTotalElementsCallSuccess = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "l2geth_ctc_total_elements_call_success",
Help: "CTC GetTotalElements call success."},
addressTotalElementsCallStatus = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "l2geth_total_elements_call_status",
Help: "GetTotalElements call status."},
[]string{"status", "address"},
)
)
func init() {
//Register metrics with prometheus
prometheus.MustRegister(ctcTotalElements)
prometheus.MustRegister(ctcTotalElementsCallSuccess)
prometheus.MustRegister(addressTotalElements)
prometheus.MustRegister(addressTotalElementsCallStatus)
}
......@@ -10,12 +10,36 @@ import (
"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 {
Address common.Address
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) {
contract, err := bindings.NewCanonicalTransactionChainCaller(ctc.Address, ctc.Client)
......
......@@ -31,11 +31,16 @@ func main() {
log.Error("L1_URL environmental variable is required")
os.Exit(1)
}
ctcAddress := os.Getenv("CTC_ADDRESS")
ctcAddress := os.Getenv("OVM_CTC_ADDRESS")
if ctcAddress == "" {
log.Error("CTC_ADDRESS environmental variable is required")
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)
if err != nil {
log.Error("Problem connecting to L1: %s", err)
......@@ -51,7 +56,8 @@ func main() {
</body>
</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)
if err := http.ListenAndServe(listenAddress, nil); err != nil {
......@@ -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{
Address: common.HexToAddress(address),
Client: client,
......@@ -72,16 +106,16 @@ func getCTCTotalElements(address string, client *ethclient.Client) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(l1TimeoutSeconds))
totalElements, err := ctc.GetTotalElements(ctx)
if err != nil {
ctcTotalElementsCallSuccess.Set(0)
log.Error("Error calling GetTotalElements", "error", err)
addressTotalElementsCallStatus.WithLabelValues("error", addressLabel).Inc()
log.Error("Error calling GetTotalElements", "address", addressLabel, "error", err)
cancel()
continue
}
ctcTotalElementsCallSuccess.Set(1)
addressTotalElementsCallStatus.WithLabelValues("success", addressLabel).Inc()
totalElementsFloat, _ := new(big.Float).SetInt(totalElements).Float64()
ctcTotalElements.WithLabelValues(
"latest").Set(totalElementsFloat)
log.Info("ctc updated", "ctcTotalElements", totalElementsFloat)
addressTotalElements.WithLabelValues("latest", addressLabel).Set(totalElementsFloat)
log.Info(addressLabel, "TotalElements", totalElementsFloat)
cancel()
<-ticker.C
......
......@@ -19,6 +19,7 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/gorilla/websocket"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/sync/semaphore"
)
const (
......@@ -88,7 +89,7 @@ type Backend struct {
authUsername string
authPassword string
rateLimiter RateLimiter
client *http.Client
client *LimitedHTTPClient
dialer *websocket.Dialer
maxRetries int
maxResponseSize int64
......@@ -170,6 +171,7 @@ func NewBackend(
rpcURL string,
wsURL string,
rateLimiter RateLimiter,
rpcSemaphore *semaphore.Weighted,
opts ...BackendOpt,
) *Backend {
backend := &Backend{
......@@ -178,8 +180,10 @@ func NewBackend(
wsURL: wsURL,
rateLimiter: rateLimiter,
maxResponseSize: math.MaxInt64,
client: &http.Client{
Timeout: 5 * time.Second,
client: &LimitedHTTPClient{
Client: http.Client{Timeout: 5 * time.Second},
sem: rpcSemaphore,
backendName: name,
},
dialer: &websocket.Dialer{},
}
......@@ -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("X-Forwarded-For", xForwardedFor)
httpRes, err := b.client.Do(httpReq)
httpRes, err := b.client.DoLimited(httpReq)
if err != nil {
return nil, wrapErr(err, "error in backend request")
}
......@@ -693,3 +697,18 @@ func sleepContext(ctx context.Context, duration time.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)
}
......@@ -7,11 +7,12 @@ import (
)
type ServerConfig struct {
RPCHost string `toml:"rpc_host"`
RPCPort int `toml:"rpc_port"`
WSHost string `toml:"ws_host"`
WSPort int `toml:"ws_port"`
MaxBodySizeBytes int64 `toml:"max_body_size_bytes"`
RPCHost string `toml:"rpc_host"`
RPCPort int `toml:"rpc_port"`
WSHost string `toml:"ws_host"`
WSPort int `toml:"ws_port"`
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 int `toml:"timeout_seconds"`
......
......@@ -18,6 +18,7 @@ ws_host = "0.0.0.0"
ws_port = 8085
# Maximum client body size, in bytes, that the server will accept.
max_body_size_bytes = 10485760
max_concurrent_rpcs = 1000
[redis]
# URL to a Redis instance.
......
......@@ -17,5 +17,6 @@ require (
github.com/rs/cors v1.8.0
github.com/stretchr/testify v1.7.0
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
)
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 (
Help: "Histogram of Redis command durations, in milliseconds.",
Buckets: MillisecondDurationBuckets,
}, []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) {
......
......@@ -10,9 +10,11 @@ import (
"strconv"
"time"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/prometheus/client_golang/prometheus/promhttp"
"golang.org/x/sync/semaphore"
)
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)
backendsByName := make(map[string]*Backend)
for name, cfg := range config.Backends {
......@@ -111,7 +119,7 @@ func Start(config *Config) (func(), error) {
opts = append(opts, WithStrippedTrailingXFF())
}
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)
backendsByName[name] = back
log.Info("configured backend", "name", name, "rpc_url", rpcURL, "ws_url", wsURL)
......
......@@ -27,6 +27,7 @@ const (
MaxBatchRPCCalls = 100
cacheStatusHdr = "X-Proxyd-Cache-Status"
defaultServerTimeout = time.Second * 10
maxLogLength = 2000
)
type Server struct {
......@@ -150,6 +151,12 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) {
}
RecordRequestPayloadSize(ctx, len(body))
log.Info("Raw RPC request",
"body", truncate(string(body)),
"req_id", GetReqID(ctx),
"auth", GetAuthCtx(ctx),
)
if IsBatch(body) {
reqs, err := ParseBatchRPCReq(body)
if err != nil {
......@@ -457,3 +464,11 @@ func (n *NoopRPCCache) GetRPC(context.Context, *RPCReq) (*RPCRes, error) {
func (n *NoopRPCCache) PutRPC(context.Context, *RPCReq, *RPCRes) error {
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(
tx *types.Transaction,
) (*types.Transaction, error) {
gasPrice, err := d.cfg.L1Client.SuggestGasPrice(ctx)
gasPrice, err := d.cfg.L2Client.SuggestGasPrice(ctx)
if err != nil {
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
......@@ -2,4 +2,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ./data-transport-layer.yaml
- ./replica-healthcheck.yaml
\ No newline at end of file
- ./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:
- ../../bases/l2geth-replica
- ../../bases/servicemonitors
- ../../bases/replica-healthcheck
- ../../bases/replica-healthcheck-v1
- ./volumes.yaml
- ./ingress.yaml
......@@ -27,6 +28,9 @@ images:
- 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
......@@ -39,4 +43,9 @@ patches:
group: apps
version: v1
kind: StatefulSet
name: l2geth-replica
\ No newline at end of file
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 @@
import { BigNumber, Contract, ContractFactory, utils, Wallet } from 'ethers'
import { ethers } from 'hardhat'
import { futurePredeploys } from '@eth-optimism/contracts'
import { sleep } from '@eth-optimism/core-utils'
/* Imports: Internal */
import { expect } from './shared/setup'
......@@ -102,4 +103,42 @@ describe('System addresses', () => {
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 (
"github.com/ethereum-optimism/optimism/l2geth/core/types"
"github.com/ethereum-optimism/optimism/l2geth/event"
"github.com/ethereum-optimism/optimism/l2geth/log"
"github.com/ethereum-optimism/optimism/l2geth/metrics"
"github.com/ethereum-optimism/optimism/l2geth/params"
)
......@@ -85,6 +86,13 @@ var (
// l2geth, rather the actual execution error should be returned to the
// user.
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.
......@@ -771,6 +779,7 @@ func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Addres
}
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())
if err != nil {
w.current.state.RevertToSnapshot(snap)
......@@ -779,6 +788,8 @@ func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Addres
w.current.txs = append(w.current.txs, tx)
w.current.receipts = append(w.current.receipts, receipt)
updateTransactionStateMetrics(start, w.current.state)
return receipt.Logs, nil
}
......@@ -1143,3 +1154,13 @@ func (w *worker) postSideBlock(event core.ChainSideEvent) {
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 {
} else {
go func() {
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 {
log.Crit("Sequencer cannot sync queue to tip: %w", err)
log.Crit("Sequencer cannot sync queue to tip", "err", err)
}
s.setSyncStatus(false)
go s.SequencerLoop()
......
......@@ -95,6 +95,9 @@ contract L2CrossDomainMessenger is IL2CrossDomainMessenger {
bytes memory _message,
uint256 _messageNonce
) 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(
AddressAliasHelper.undoL1ToL2Alias(msg.sender) == l1CrossDomainMessenger,
"Provided message could not be verified."
......
/* External Imports */
import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract, BigNumber } from 'ethers'
import {
smock,
MockContractFactory,
FakeContract,
} from '@defi-wonderland/smock'
import {
remove0x,
toHexString,
applyL1ToL2Alias,
} from '@eth-optimism/core-utils'
import { Contract, BigNumber } from 'ethers'
import { smock, FakeContract, MockContract } from '@defi-wonderland/smock'
import { toHexString, applyL1ToL2Alias } from '@eth-optimism/core-utils'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
/* Internal Imports */
import { expect } from '../../../setup'
import {
makeAddressManager,
setProxyTarget,
NON_NULL_BYTES32,
NON_ZERO_ADDRESS,
......@@ -28,59 +18,39 @@ import {
encodeXDomainCalldata,
getEthTime,
setEthTime,
deploy,
} from '../../../helpers'
import { predeploys } from '../../../../src'
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', () => {
let signer: Signer
let signer2: Signer
let signer1: SignerWithAddress
let signer2: SignerWithAddress
before(async () => {
;[signer, signer2] = await ethers.getSigners()
})
let AddressManager: Contract
before(async () => {
AddressManager = await makeAddressManager()
;[signer1, signer2] = await ethers.getSigners()
})
let Fake__TargetContract: FakeContract
let Fake__L2CrossDomainMessenger: FakeContract
let Fake__StateCommitmentChain: FakeContract
let Factory__CanonicalTransactionChain: ContractFactory
let Factory__ChainStorageContainer: ContractFactory
let Factory__L1CrossDomainMessenger: ContractFactory
let CanonicalTransactionChain: Contract
before(async () => {
Fake__TargetContract = await smock.fake<Contract>(
await ethers.getContractFactory('Helper_SimpleProxy')
)
Fake__TargetContract = await smock.fake<Contract>('Helper_SimpleProxy')
Fake__L2CrossDomainMessenger = await smock.fake<Contract>(
await ethers.getContractFactory('L2CrossDomainMessenger'),
'L2CrossDomainMessenger',
{
address: predeploys.L2CrossDomainMessenger,
}
)
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(
'L2CrossDomainMessenger',
......@@ -93,32 +63,18 @@ describe('L1CrossDomainMessenger', () => {
Fake__StateCommitmentChain
)
Factory__CanonicalTransactionChain = await ethers.getContractFactory(
'CanonicalTransactionChain'
)
Factory__ChainStorageContainer = await ethers.getContractFactory(
'ChainStorageContainer'
)
Factory__L1CrossDomainMessenger = await ethers.getContractFactory(
'L1CrossDomainMessenger'
)
CanonicalTransactionChain = await Factory__CanonicalTransactionChain.deploy(
AddressManager.address,
MAX_GAS_LIMIT,
L2_GAS_DISCOUNT_DIVISOR,
ENQUEUE_GAS_COST
)
CanonicalTransactionChain = await deploy('CanonicalTransactionChain', {
args: [
AddressManager.address,
MAX_GAS_LIMIT,
L2_GAS_DISCOUNT_DIVISOR,
ENQUEUE_GAS_COST,
],
})
const batches = await Factory__ChainStorageContainer.deploy(
AddressManager.address,
'CanonicalTransactionChain'
)
await Factory__ChainStorageContainer.deploy(
AddressManager.address,
'CanonicalTransactionChain'
)
const batches = await deploy('ChainStorageContainer', {
args: [AddressManager.address, 'CanonicalTransactionChain'],
})
await AddressManager.setAddress(
'ChainStorageContainer-CTC-batches',
......@@ -133,12 +89,19 @@ describe('L1CrossDomainMessenger', () => {
let L1CrossDomainMessenger: Contract
beforeEach(async () => {
const xDomainMessengerImpl = await Factory__L1CrossDomainMessenger.deploy()
// We use an upgradable proxy for the XDomainMessenger--deploy & set up the proxy.
L1CrossDomainMessenger = await deployProxyXDomainMessenger(
AddressManager,
xDomainMessengerImpl
const xDomainMessengerImpl = await deploy('L1CrossDomainMessenger')
await AddressManager.setAddress(
'L1CrossDomainMessenger',
xDomainMessengerImpl.address
)
const proxy = await deploy('Lib_ResolvedDelegateProxy', {
args: [AddressManager.address, 'L1CrossDomainMessenger'],
})
L1CrossDomainMessenger = xDomainMessengerImpl.attach(proxy.address)
await L1CrossDomainMessenger.initialize(AddressManager.address)
})
......@@ -172,7 +135,7 @@ describe('L1CrossDomainMessenger', () => {
const calldata = encodeXDomainCalldata(
target,
await signer.getAddress(),
signer1.address,
message,
0
)
......@@ -209,14 +172,9 @@ describe('L1CrossDomainMessenger', () => {
const oldGasLimit = 100_000
const newGasLimit = 200_000
let sender: string
before(async () => {
sender = await signer.getAddress()
})
let queueIndex: number
beforeEach(async () => {
await L1CrossDomainMessenger.connect(signer).sendMessage(
await L1CrossDomainMessenger.connect(signer1).sendMessage(
target,
message,
oldGasLimit
......@@ -231,7 +189,7 @@ describe('L1CrossDomainMessenger', () => {
await expect(
L1CrossDomainMessenger.replayMessage(
ethers.constants.AddressZero, // Wrong target
sender,
signer1.address,
message,
queueIndex,
oldGasLimit,
......@@ -257,7 +215,7 @@ describe('L1CrossDomainMessenger', () => {
await expect(
L1CrossDomainMessenger.replayMessage(
target,
sender,
signer1.address,
'0x', // Wrong message
queueIndex,
oldGasLimit,
......@@ -270,7 +228,7 @@ describe('L1CrossDomainMessenger', () => {
await expect(
L1CrossDomainMessenger.replayMessage(
target,
sender,
signer1.address,
message,
queueIndex - 1, // Wrong queue index
oldGasLimit,
......@@ -283,7 +241,7 @@ describe('L1CrossDomainMessenger', () => {
await expect(
L1CrossDomainMessenger.replayMessage(
target,
sender,
signer1.address,
message,
queueIndex,
oldGasLimit + 1, // Wrong gas limit
......@@ -298,7 +256,7 @@ describe('L1CrossDomainMessenger', () => {
await expect(
L1CrossDomainMessenger.replayMessage(
target,
sender,
signer1.address,
message,
queueIndex,
oldGasLimit,
......@@ -315,7 +273,7 @@ describe('L1CrossDomainMessenger', () => {
await expect(
L1CrossDomainMessenger.replayMessage(
target,
sender,
signer1.address,
message,
queueIndex,
oldGasLimit,
......@@ -327,7 +285,7 @@ describe('L1CrossDomainMessenger', () => {
applyL1ToL2Alias(L1CrossDomainMessenger.address),
Fake__L2CrossDomainMessenger.address,
newGasLimit,
encodeXDomainCalldata(target, sender, message, queueIndex),
encodeXDomainCalldata(target, signer1.address, message, queueIndex),
newQueueIndex,
newTimestamp
)
......@@ -341,7 +299,7 @@ describe('L1CrossDomainMessenger', () => {
await expect(
L1CrossDomainMessenger.replayMessage(
target,
await signer.getAddress(),
signer1.address,
message,
queueLength - 1,
oldGasLimit,
......@@ -352,24 +310,22 @@ describe('L1CrossDomainMessenger', () => {
})
describe('xDomainMessageSender', () => {
let Mock__Factory__L1CrossDomainMessenger: MockContractFactory<ContractFactory>
let Mock__L1CrossDomainMessenger
let Mock__L1CrossDomainMessenger: MockContract<Contract>
before(async () => {
Mock__Factory__L1CrossDomainMessenger = await smock.mock(
'L1CrossDomainMessenger'
)
Mock__L1CrossDomainMessenger =
await Mock__Factory__L1CrossDomainMessenger.deploy()
Mock__L1CrossDomainMessenger = await (
await smock.mock('L1CrossDomainMessenger')
).deploy()
})
it('should return the xDomainMsgSender address', async () => {
await Mock__L1CrossDomainMessenger.setVariable(
'xDomainMsgSender',
'0x0000000000000000000000000000000000000000'
NON_ZERO_ADDRESS
)
expect(
await Mock__L1CrossDomainMessenger.xDomainMessageSender()
).to.equal('0x0000000000000000000000000000000000000000')
).to.equal(NON_ZERO_ADDRESS)
})
})
......@@ -390,10 +346,17 @@ describe('L1CrossDomainMessenger', () => {
)
const storageKey = ethers.utils.keccak256(
ethers.utils.keccak256(
calldata + remove0x(Fake__L2CrossDomainMessenger.address)
) + '00'.repeat(32)
ethers.utils.hexConcat([
ethers.utils.keccak256(
ethers.utils.hexConcat([
calldata,
Fake__L2CrossDomainMessenger.address,
])
),
ethers.constants.HashZero,
])
)
const storageGenerator = await TrieTestGenerator.fromNodes({
nodes: [
{
......@@ -437,7 +400,6 @@ describe('L1CrossDomainMessenger', () => {
describe('relayMessage', () => {
let target: string
let sender: string
let message: string
let proof: any
let calldata: string
......@@ -446,11 +408,10 @@ describe('L1CrossDomainMessenger', () => {
message = Fake__TargetContract.interface.encodeFunctionData('setTarget', [
NON_ZERO_ADDRESS,
])
sender = await signer.getAddress()
const mockProof = await generateMockRelayMessageProof(
target,
sender,
signer1.address,
message
)
proof = mockProof.proof
......@@ -474,21 +435,27 @@ describe('L1CrossDomainMessenger', () => {
}
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.')
})
it('should revert if attempting to relay a message sent to an L1 system contract', async () => {
const maliciousProof = await generateMockRelayMessageProof(
CanonicalTransactionChain.address,
sender,
signer1.address,
message
)
await expect(
L1CrossDomainMessenger.relayMessage(
CanonicalTransactionChain.address,
sender,
signer1.address,
message,
0,
maliciousProof.proof
......@@ -510,25 +477,43 @@ describe('L1CrossDomainMessenger', () => {
}
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.')
})
it('should revert if provided an invalid storage trie witness', async () => {
await expect(
L1CrossDomainMessenger.relayMessage(target, sender, message, 0, {
...proof,
storageTrieWitness: '0x',
})
L1CrossDomainMessenger.relayMessage(
target,
signer1.address,
message,
0,
{
...proof,
storageTrieWitness: '0x',
}
)
).to.be.reverted
})
it('should revert if provided an invalid state trie witness', async () => {
await expect(
L1CrossDomainMessenger.relayMessage(target, sender, message, 0, {
...proof,
stateTrieWitness: '0x',
})
L1CrossDomainMessenger.relayMessage(
target,
signer1.address,
message,
0,
{
...proof,
stateTrieWitness: '0x',
}
)
).to.be.reverted
})
......@@ -537,7 +522,7 @@ describe('L1CrossDomainMessenger', () => {
await L1CrossDomainMessenger.relayMessage(
target,
sender,
signer1.address,
message,
0,
proof
......@@ -552,12 +537,14 @@ describe('L1CrossDomainMessenger', () => {
expect(
await L1CrossDomainMessenger.relayedMessages(
ethers.utils.keccak256(
calldata +
remove0x(await signer.getAddress()) +
remove0x(BigNumber.from(blockNumber).toHexString()).padStart(
64,
'0'
)
ethers.utils.hexConcat([
calldata,
signer1.address,
ethers.utils.hexZeroPad(
BigNumber.from(blockNumber).toHexString(),
32
),
])
)
)
).to.equal(true)
......@@ -567,13 +554,15 @@ describe('L1CrossDomainMessenger', () => {
await expect(
L1CrossDomainMessenger.xDomainMessageSender()
).to.be.revertedWith('xDomainMessageSender is not set')
await L1CrossDomainMessenger.relayMessage(
target,
sender,
signer1.address,
message,
0,
proof
)
await expect(
L1CrossDomainMessenger.xDomainMessageSender()
).to.be.revertedWith('xDomainMessageSender is not set')
......@@ -582,14 +571,20 @@ describe('L1CrossDomainMessenger', () => {
it('should revert if trying to send the same message twice', async () => {
await L1CrossDomainMessenger.relayMessage(
target,
sender,
signer1.address,
message,
0,
proof
)
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.')
})
......@@ -597,13 +592,20 @@ describe('L1CrossDomainMessenger', () => {
await L1CrossDomainMessenger.pause()
await expect(
L1CrossDomainMessenger.relayMessage(target, sender, message, 0, proof)
L1CrossDomainMessenger.relayMessage(
target,
signer1.address,
message,
0,
proof
)
).to.be.revertedWith('Pausable: paused')
})
describe('blockMessage and allowMessage', () => {
it('should revert if called by an account other than the owner', async () => {
const L1CrossDomainMessenger2 = L1CrossDomainMessenger.connect(signer2)
await expect(
L1CrossDomainMessenger2.blockMessage(ethers.utils.keccak256(calldata))
).to.be.revertedWith('Ownable: caller is not the owner')
......@@ -619,7 +621,13 @@ describe('L1CrossDomainMessenger', () => {
)
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.')
})
......@@ -629,7 +637,13 @@ describe('L1CrossDomainMessenger', () => {
)
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.')
await L1CrossDomainMessenger.allowMessage(
......@@ -637,7 +651,13 @@ describe('L1CrossDomainMessenger', () => {
)
await expect(
L1CrossDomainMessenger.relayMessage(target, sender, message, 0, proof)
L1CrossDomainMessenger.relayMessage(
target,
signer1.address,
message,
0,
proof
)
).to.not.be.reverted
})
})
......
/* External Imports */
import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract, constants } from 'ethers'
import { Interface } from 'ethers/lib/utils'
import {
smock,
MockContractFactory,
FakeContract,
MockContract,
} from '@defi-wonderland/smock'
/* Internal Imports */
import { Signer, Contract, constants } from 'ethers'
import { smock, FakeContract, MockContract } from '@defi-wonderland/smock'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
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'
const ERR_INVALID_MESSENGER = 'OVM_XCHAIN: messenger contract unauthenticated'
const ERR_INVALID_X_DOMAIN_MSG_SENDER =
'OVM_XCHAIN: wrong sender of cross-domain message'
const ERR_ALREADY_INITIALIZED = 'Contract has already been initialized.'
const DUMMY_L2_ERC20_ADDRESS = ethers.utils.getAddress('0x' + 'abba'.repeat(10))
const DUMMY_L2_BRIDGE_ADDRESS = ethers.utils.getAddress(
'0x' + 'acdc'.repeat(10)
)
// TODO: Maybe we should consider automatically generating these and exporting them?
const ERROR_STRINGS = {
INVALID_MESSENGER: 'OVM_XCHAIN: messenger contract unauthenticated',
INVALID_X_DOMAIN_MSG_SENDER:
'OVM_XCHAIN: wrong sender of cross-domain message',
ALREADY_INITIALIZED: 'Contract has already been initialized.',
}
const DUMMY_L2_ERC20_ADDRESS = '0xaBBAABbaaBbAABbaABbAABbAABbaAbbaaBbaaBBa'
const DUMMY_L2_BRIDGE_ADDRESS = '0xACDCacDcACdCaCDcacdcacdCaCdcACdCAcDcaCdc'
const INITIAL_TOTAL_L1_SUPPLY = 5000
const FINALIZATION_GAS = 1_200_000
describe('L1StandardBridge', () => {
// init signers
let l1MessengerImpersonator: Signer
let alice: Signer
let bob: Signer
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
let alice: SignerWithAddress
let bob: SignerWithAddress
before(async () => {
;[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>
......@@ -60,24 +34,23 @@ describe('L1StandardBridge', () => {
beforeEach(async () => {
// Get a new mock L1 messenger
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
)
// Deploy the contract under test
L1StandardBridge = await (
await ethers.getContractFactory('L1StandardBridge')
).deploy()
L1StandardBridge = await deploy('L1StandardBridge')
await L1StandardBridge.initialize(
Fake__L1CrossDomainMessenger.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('_balances', {
[aliceAddress]: INITIAL_TOTAL_L1_SUPPLY,
[alice.address]: INITIAL_TOTAL_L1_SUPPLY,
})
})
......@@ -88,7 +61,7 @@ describe('L1StandardBridge', () => {
ethers.constants.AddressZero,
DUMMY_L2_BRIDGE_ADDRESS
)
).to.be.revertedWith(ERR_ALREADY_INITIALIZED)
).to.be.revertedWith(ERROR_STRINGS.ALREADY_INITIALIZED)
})
})
......@@ -107,8 +80,7 @@ describe('L1StandardBridge', () => {
const depositAmount = 1_000
it('depositETH() escrows the deposit amount and sends the correct deposit message', async () => {
const depositer = await alice.getAddress()
const initialBalance = await ethers.provider.getBalance(depositer)
const initialBalance = await alice.getBalance()
// alice calls deposit on the bridge and the L1 bridge calls transferFrom on the token
const res = await L1StandardBridge.connect(alice).depositETH(
......@@ -119,92 +91,80 @@ describe('L1StandardBridge', () => {
}
)
const depositCallToMessenger =
Fake__L1CrossDomainMessenger.sendMessage.getCall(0)
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,
alice.address,
depositAmount,
NON_NULL_BYTES32,
]
),
FINALIZATION_GAS,
])
const depositerBalance = await ethers.provider.getBalance(depositer)
const receipt = await res.wait()
const depositerFeePaid = receipt.cumulativeGasUsed.mul(
receipt.effectiveGasPrice
)
expect(depositerBalance).to.equal(
expect(await alice.getBalance()).to.equal(
initialBalance.sub(depositAmount).sub(depositerFeePaid)
)
// bridge's balance is increased
const bridgeBalance = await ethers.provider.getBalance(
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 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)
expect(
await ethers.provider.getBalance(L1StandardBridge.address)
).to.equal(depositAmount)
})
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 ethers.provider.getBalance(aliceAddress)
const initialBalance = await alice.getBalance()
const res = await L1StandardBridge.connect(alice).depositETHTo(
bobsAddress,
bob.address,
FINALIZATION_GAS,
NON_NULL_BYTES32,
{
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 depositerFeePaid = receipt.cumulativeGasUsed.mul(
receipt.effectiveGasPrice
)
expect(depositerBalance).to.equal(
expect(await alice.getBalance()).to.equal(
initialBalance.sub(depositAmount).sub(depositerFeePaid)
)
// bridge's balance is increased
const bridgeBalance = await ethers.provider.getBalance(
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 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)
expect(
await ethers.provider.getBalance(L1StandardBridge.address)
).to.equal(depositAmount)
})
it('cannot depositETH from a contract account', async () => {
......@@ -218,29 +178,17 @@ describe('L1StandardBridge', () => {
describe('ETH withdrawals', () => {
it('onlyFromCrossDomainAccount: should revert on calls from a non-crossDomainMessenger L1 account', async () => {
// Deploy new bridge, initialize with random messenger
await expect(
L1StandardBridge.connect(alice).finalizeETHWithdrawal(
constants.AddressZero,
constants.AddressZero,
1,
NON_NULL_BYTES32,
{
from: aliceAddress,
}
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 L2ETHToken)', async () => {
L1StandardBridge = await (
await ethers.getContractFactory('L1StandardBridge')
).deploy()
await L1StandardBridge.initialize(
Fake__L1CrossDomainMessenger.address,
DUMMY_L2_BRIDGE_ADDRESS
)
Fake__L1CrossDomainMessenger.xDomainMessageSender.returns(
'0x' + '22'.repeat(20)
)
......@@ -255,23 +203,21 @@ describe('L1StandardBridge', () => {
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 () => {
// make sure no balance at start of test
expect(await ethers.provider.getBalance(NON_ZERO_ADDRESS)).to.be.equal(0)
const withdrawalAmount = 100
Fake__L1CrossDomainMessenger.xDomainMessageSender.returns(
() => DUMMY_L2_BRIDGE_ADDRESS
DUMMY_L2_BRIDGE_ADDRESS
)
await expect(
L1StandardBridge.finalizeETHWithdrawal(
NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS,
withdrawalAmount,
100,
NON_NULL_BYTES32,
{
from: Fake__L1CrossDomainMessenger.address,
......@@ -283,15 +229,13 @@ describe('L1StandardBridge', () => {
})
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)
const withdrawalAmount = 100
Fake__L1CrossDomainMessenger.xDomainMessageSender.returns(
() => DUMMY_L2_BRIDGE_ADDRESS
DUMMY_L2_BRIDGE_ADDRESS
)
// thanks Alice
await L1StandardBridge.connect(alice).depositETH(
FINALIZATION_GAS,
NON_NULL_BYTES32,
......@@ -327,7 +271,6 @@ describe('L1StandardBridge', () => {
})
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(
L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS,
......@@ -336,73 +279,68 @@ describe('L1StandardBridge', () => {
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)
expect(
Fake__L1CrossDomainMessenger.sendMessage.getCall(0).args
).to.deep.equal([
DUMMY_L2_BRIDGE_ADDRESS,
getContractInterface('IL2ERC20Bridge').encodeFunctionData(
'finalizeDeposit',
[
L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS,
alice.address,
alice.address,
depositAmount,
NON_NULL_BYTES32,
]
),
FINALIZATION_GAS,
])
// 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
expect(await L1ERC20.balanceOf(alice.address)).to.equal(
INITIAL_TOTAL_L1_SUPPLY - depositAmount
)
// the L1 bridge sends the correct message to the L1 messenger
expect(depositCallToMessenger.args[1]).to.equal(
IL2ERC20Bridge.encodeFunctionData('finalizeDeposit', [
L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS,
aliceAddress,
aliceAddress,
depositAmount,
NON_NULL_BYTES32,
])
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 () => {
// depositor calls deposit on the bridge and the L1 bridge calls transferFrom on the token
await L1StandardBridge.connect(alice).depositERC20To(
L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS,
bobsAddress,
bob.address,
depositAmount,
FINALIZATION_GAS,
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)
expect(
Fake__L1CrossDomainMessenger.sendMessage.getCall(0).args
).to.deep.equal([
DUMMY_L2_BRIDGE_ADDRESS,
getContractInterface('IL2ERC20Bridge').encodeFunctionData(
'finalizeDeposit',
[
L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS,
alice.address,
bob.address,
depositAmount,
NON_NULL_BYTES32,
]
),
FINALIZATION_GAS,
])
// 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
expect(await L1ERC20.balanceOf(alice.address)).to.equal(
INITIAL_TOTAL_L1_SUPPLY - depositAmount
)
// the L1 bridge sends the correct message to the L1 messenger
expect(depositCallToMessenger.args[1]).to.equal(
IL2ERC20Bridge.encodeFunctionData('finalizeDeposit', [
L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS,
aliceAddress,
bobsAddress,
depositAmount,
NON_NULL_BYTES32,
])
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 () => {
......@@ -419,12 +357,8 @@ describe('L1StandardBridge', () => {
describe('Handling ERC20.transferFrom() failures that revert ', () => {
let Fake__L1ERC20: FakeContract
before(async () => {
// Deploy the L1 ERC20 token, Alice will receive the full initialSupply
Fake__L1ERC20 = await smock.fake<Contract>(
await Factory__L1ERC20.deploy('L1ERC20', 'ERC')
)
Fake__L1ERC20 = await smock.fake<Contract>('ERC20')
Fake__L1ERC20.transferFrom.reverts()
})
......@@ -445,7 +379,7 @@ describe('L1StandardBridge', () => {
L1StandardBridge.connect(alice).depositERC20To(
Fake__L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS,
bobsAddress,
bob.address,
depositAmount,
FINALIZATION_GAS,
NON_NULL_BYTES32
......@@ -458,7 +392,7 @@ describe('L1StandardBridge', () => {
L1StandardBridge.connect(alice).depositERC20To(
ethers.constants.AddressZero,
DUMMY_L2_ERC20_ADDRESS,
bobsAddress,
bob.address,
depositAmount,
FINALIZATION_GAS,
NON_NULL_BYTES32
......@@ -470,9 +404,7 @@ describe('L1StandardBridge', () => {
describe('Handling ERC20.transferFrom failures that return false', () => {
let Fake__L1ERC20: FakeContract
before(async () => {
Fake__L1ERC20 = await smock.fake(
await Factory__L1ERC20.deploy('L1ERC20', 'ERC')
)
Fake__L1ERC20 = await smock.fake('ERC20')
Fake__L1ERC20.transferFrom.returns(false)
})
......@@ -493,7 +425,7 @@ describe('L1StandardBridge', () => {
L1StandardBridge.depositERC20To(
Fake__L1ERC20.address,
DUMMY_L2_ERC20_ADDRESS,
bobsAddress,
bob.address,
depositAmount,
FINALIZATION_GAS,
NON_NULL_BYTES32
......@@ -514,12 +446,12 @@ describe('L1StandardBridge', () => {
1,
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 () => {
Fake__L1CrossDomainMessenger.xDomainMessageSender.returns(
() => NON_ZERO_ADDRESS
NON_ZERO_ADDRESS
)
await expect(
......@@ -534,7 +466,7 @@ describe('L1StandardBridge', () => {
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 () => {
......@@ -561,7 +493,7 @@ describe('L1StandardBridge', () => {
expect(await L1ERC20.balanceOf(NON_ZERO_ADDRESS)).to.be.equal(0)
Fake__L1CrossDomainMessenger.xDomainMessageSender.returns(
() => DUMMY_L2_BRIDGE_ADDRESS
DUMMY_L2_BRIDGE_ADDRESS
)
await L1StandardBridge.finalizeERC20Withdrawal(
......
/* External Imports */
import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers'
import { Contract } from 'ethers'
import { MockContract, smock } from '@defi-wonderland/smock'
import { expectApprox } from '@eth-optimism/core-utils'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
/* Internal Imports */
import {
makeAddressManager,
deploy,
L2_GAS_DISCOUNT_DIVISOR,
ENQUEUE_GAS_COST,
NON_ZERO_ADDRESS,
......@@ -22,44 +21,34 @@ const INITIAL_TOTAL_L1_SUPPLY = 5000
const FINALIZATION_GAS = 1_200_000
describe('[GAS BENCHMARK] Depositing via the standard bridge [ @skip-on-coverage ]', () => {
let sequencer: Signer
let alice: Signer
let sequencer: SignerWithAddress
let alice: SignerWithAddress
before(async () => {
;[sequencer, alice] = await ethers.getSigners()
})
let AddressManager: Contract
before('Deploy address manager and register sequencer', async () => {
AddressManager = await makeAddressManager()
let CanonicalTransactionChain: Contract
before(async () => {
AddressManager = await deploy('Lib_AddressManager')
CanonicalTransactionChain = await deploy('CanonicalTransactionChain', {
args: [
AddressManager.address,
MAX_GAS_LIMIT,
L2_GAS_DISCOUNT_DIVISOR,
ENQUEUE_GAS_COST,
],
})
const batches = await deploy('ChainStorageContainer', {
args: [AddressManager.address, 'CanonicalTransactionChain'],
})
await AddressManager.setAddress(
'OVM_Sequencer',
await sequencer.getAddress()
)
})
let CanonicalTransactionChain: Contract
let Factory__ChainStorageContainer: ContractFactory
before('Init CTC and Storage Container contracts.', async () => {
CanonicalTransactionChain = await (
await ethers.getContractFactory('CanonicalTransactionChain')
).deploy(
AddressManager.address,
MAX_GAS_LIMIT,
L2_GAS_DISCOUNT_DIVISOR,
ENQUEUE_GAS_COST
)
Factory__ChainStorageContainer = await ethers.getContractFactory(
'ChainStorageContainer'
)
const batches = await Factory__ChainStorageContainer.deploy(
AddressManager.address,
'CanonicalTransactionChain'
)
await Factory__ChainStorageContainer.deploy(
AddressManager.address,
'CanonicalTransactionChain'
)
await AddressManager.setAddress(
'ChainStorageContainer-CTC-batches',
......@@ -72,50 +61,43 @@ describe('[GAS BENCHMARK] Depositing via the standard bridge [ @skip-on-coverage
)
})
// 3 Messenger
let L1CrossDomainMessenger: Contract
before('Deploy Messenger proxy and implementation', async () => {
// Deploy the implementation contract first
const xDomainMessengerImpl = await (
await ethers.getContractFactory('L1CrossDomainMessenger')
).deploy()
before(async () => {
const xDomainMessengerImpl = await deploy('L1CrossDomainMessenger')
await AddressManager.setAddress(
'L1CrossDomainMessenger',
xDomainMessengerImpl.address
)
// Deploy and initialize the Proxy Messenger
const proxy = await (
await ethers.getContractFactory('Lib_ResolvedDelegateProxy')
).deploy(AddressManager.address, 'L1CrossDomainMessenger')
const proxy = await deploy('Lib_ResolvedDelegateProxy', {
args: [AddressManager.address, 'L1CrossDomainMessenger'],
})
L1CrossDomainMessenger = xDomainMessengerImpl.attach(proxy.address)
await L1CrossDomainMessenger.initialize(AddressManager.address)
})
// 4 Bridge
let L1ERC20: MockContract<Contract>
let L1StandardBridge: Contract
before('Deploy the bridge and setup the token', async () => {
// Deploy the Bridge
L1StandardBridge = await (
await ethers.getContractFactory('L1StandardBridge')
).deploy()
L1StandardBridge = await deploy('L1StandardBridge')
await L1StandardBridge.initialize(
L1CrossDomainMessenger.address,
NON_ZERO_ADDRESS
)
L1ERC20 = await (
await smock.mock('@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20')
).deploy('L1ERC20', 'ERC')
const aliceAddress = await alice.getAddress()
L1ERC20 = await (await smock.mock('ERC20')).deploy('L1ERC20', 'ERC')
await L1ERC20.setVariable('_totalSupply', INITIAL_TOTAL_L1_SUPPLY)
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 () => {
const depositAmount = 1_000
before(async () => {
// Load a transaction into the queue first to 'dirty' the buffer's length slot
await CanonicalTransactionChain.enqueue(
......@@ -124,6 +106,7 @@ describe('[GAS BENCHMARK] Depositing via the standard bridge [ @skip-on-coverage
'0x1234'
)
})
it('cost to deposit ETH', async () => {
// Alice calls deposit on the bridge and the L1 bridge calls transferFrom on the token.
const res = await L1StandardBridge.connect(alice).depositETH(
......@@ -137,12 +120,14 @@ describe('[GAS BENCHMARK] Depositing via the standard bridge [ @skip-on-coverage
const receipt = await res.wait()
const gasUsed = receipt.gasUsed.toNumber()
console.log(' - Gas used:', gasUsed)
expectApprox(gasUsed, 132_481, {
absoluteUpperDeviation: 500,
// 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!
percentLowerDeviation: 1,
})
// Sanity check that the message was enqueued.
expect(await CanonicalTransactionChain.getQueueLength()).to.equal(2)
})
......@@ -152,6 +137,7 @@ describe('[GAS BENCHMARK] Depositing via the standard bridge [ @skip-on-coverage
L1StandardBridge.address,
depositAmount
)
// Alice calls deposit on the bridge and the L1 bridge calls transferFrom on the token.
const res = await L1StandardBridge.connect(alice).depositERC20(
L1ERC20.address,
......@@ -160,9 +146,11 @@ describe('[GAS BENCHMARK] Depositing via the standard bridge [ @skip-on-coverage
FINALIZATION_GAS,
NON_NULL_BYTES32
)
const receipt = await res.wait()
const gasUsed = receipt.gasUsed.toNumber()
console.log(' - Gas used:', gasUsed)
expectApprox(gasUsed, 192_822, {
absoluteUpperDeviation: 500,
// Assert a lower bound of 1% reduction on gas cost. If your tests are breaking because your
......
/* External Imports */
import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers'
import { Signer, Contract } from 'ethers'
import { smock, FakeContract } from '@defi-wonderland/smock'
import {
AppendSequencerBatchParams,
......@@ -9,11 +9,10 @@ import {
expectApprox,
} from '@eth-optimism/core-utils'
import { TransactionResponse } from '@ethersproject/abstract-provider'
import { keccak256 } from 'ethers/lib/utils'
/* Internal Imports */
import {
makeAddressManager,
deploy,
setProxyTarget,
L2_GAS_DISCOUNT_DIVISOR,
ENQUEUE_GAS_COST,
......@@ -31,11 +30,11 @@ const appendSequencerBatch = async (
CanonicalTransactionChain: Contract,
batch: AppendSequencerBatchParams
): Promise<TransactionResponse> => {
const methodId = keccak256(Buffer.from('appendSequencerBatch()')).slice(2, 10)
const calldata = encodeAppendSequencerBatch(batch)
return CanonicalTransactionChain.signer.sendTransaction({
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 ]', () =
let AddressManager: Contract
let Fake__StateCommitmentChain: FakeContract
before(async () => {
AddressManager = await makeAddressManager()
let CanonicalTransactionChain: Contract
beforeEach(async () => {
AddressManager = await deploy('Lib_AddressManager')
await AddressManager.setAddress(
'OVM_Sequencer',
await sequencer.getAddress()
)
Fake__StateCommitmentChain = await smock.fake<Contract>(
await ethers.getContractFactory('StateCommitmentChain')
'StateCommitmentChain'
)
await setProxyTarget(
......@@ -63,37 +64,20 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () =
'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
beforeEach(async () => {
CanonicalTransactionChain = await Factory__CanonicalTransactionChain.deploy(
AddressManager.address,
MAX_GAS_LIMIT,
L2_GAS_DISCOUNT_DIVISOR,
ENQUEUE_GAS_COST
)
CanonicalTransactionChain = await deploy('CanonicalTransactionChain', {
signer: sequencer,
args: [
AddressManager.address,
MAX_GAS_LIMIT,
L2_GAS_DISCOUNT_DIVISOR,
ENQUEUE_GAS_COST,
],
})
const batches = await Factory__ChainStorageContainer.deploy(
AddressManager.address,
'CanonicalTransactionChain'
)
await Factory__ChainStorageContainer.deploy(
AddressManager.address,
'CanonicalTransactionChain'
)
const batches = await deploy('ChainStorageContainer', {
args: [AddressManager.address, 'CanonicalTransactionChain'],
})
await AddressManager.setAddress(
'ChainStorageContainer-CTC-batches',
......@@ -107,12 +91,7 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () =
})
describe('appendSequencerBatch [ @skip-on-coverage ]', () => {
beforeEach(() => {
CanonicalTransactionChain = CanonicalTransactionChain.connect(sequencer)
})
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 blockNumber = await getNextBlockNumber(ethers.provider)
......@@ -143,13 +122,12 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () =
const receipt = await res.wait()
const gasUsed = receipt.gasUsed.toNumber()
console.log('Benchmark complete.')
console.log('Fixed calldata cost:', fixedCalldataCost)
console.log(
'Non-calldata overhead gas cost per transaction:',
(gasUsed - fixedCalldataCost) / numTxs
)
expectApprox(gasUsed, 1_402_638, {
absoluteUpperDeviation: 1000,
// 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 ]', () =
}).timeout(10_000_000)
it('200 transactions in 200 contexts', async () => {
console.log(`Benchmark: 200 transactions in 200 contexts.`)
const timestamp = (await getEthTime(ethers.provider)) - 100
const blockNumber = await getNextBlockNumber(ethers.provider)
......@@ -190,13 +167,12 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () =
const receipt = await res.wait()
const gasUsed = receipt.gasUsed.toNumber()
console.log('Benchmark complete.')
console.log('Fixed calldata cost:', fixedCalldataCost)
console.log(
'Non-calldata overhead gas cost per transaction:',
(gasUsed - fixedCalldataCost) / numTxs
)
expectApprox(gasUsed, 1_619_781, {
absoluteUpperDeviation: 1000,
// 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 ]', () =
const gasUsed = receipt.gasUsed.toNumber()
console.log('Benchmark complete.')
console.log('Fixed calldata cost:', fixedCalldataCost)
console.log(
'Non-calldata overhead gas cost per transaction:',
(gasUsed - fixedCalldataCost) / numTxs
)
expectApprox(gasUsed, 891_158, {
absoluteUpperDeviation: 1000,
// 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 ]', () =
})
describe('enqueue [ @skip-on-coverage ]', () => {
let enqueueL2GasPrepaid
let data
const data = '0x' + '12'.repeat(1234)
let enqueueL2GasPrepaid: number
beforeEach(async () => {
CanonicalTransactionChain = CanonicalTransactionChain.connect(sequencer)
enqueueL2GasPrepaid =
await CanonicalTransactionChain.enqueueL2GasPrepaid()
data = '0x' + '12'.repeat(1234)
})
it('cost to enqueue a transaction above the prepaid threshold', async () => {
......@@ -281,11 +256,10 @@ describe('[GAS BENCHMARK] CanonicalTransactionChain [ @skip-on-coverage ]', () =
l2GasLimit,
data
)
const receipt = await res.wait()
const gasUsed = receipt.gasUsed.toNumber()
console.log('Benchmark complete.')
expectApprox(gasUsed, 196_687, {
absoluteUpperDeviation: 500,
// 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 ]', () =
l2GasLimit,
data
)
const receipt = await res.wait()
const gasUsed = receipt.gasUsed.toNumber()
console.log('Benchmark complete.')
expectApprox(gasUsed, 134_100, {
absoluteUpperDeviation: 500,
// Assert a lower bound of 1% reduction on gas cost. If your tests are breaking because your
......
/* External Imports */
import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers'
import { Contract } from 'ethers'
import { smock, FakeContract } from '@defi-wonderland/smock'
import {
AppendSequencerBatchParams,
encodeAppendSequencerBatch,
} from '@eth-optimism/core-utils'
import { TransactionResponse } from '@ethersproject/abstract-provider'
import { keccak256 } from 'ethers/lib/utils'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
import _ from 'lodash'
/* Internal Imports */
import { expect } from '../../../setup'
import {
makeAddressManager,
deploy,
setProxyTarget,
L2_GAS_DISCOUNT_DIVISOR,
ENQUEUE_GAS_COST,
......@@ -27,15 +25,6 @@ import { names } from '../../../../src/address-names'
const ELEMENT_TEST_SIZES = [1, 2, 4, 8, 16]
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 = (
sender: string,
target: string,
......@@ -52,33 +41,30 @@ const appendSequencerBatch = async (
CanonicalTransactionChain: Contract,
batch: AppendSequencerBatchParams
): Promise<TransactionResponse> => {
const methodId = keccak256(Buffer.from('appendSequencerBatch()')).slice(2, 10)
const calldata = encodeAppendSequencerBatch(batch)
return CanonicalTransactionChain.signer.sendTransaction({
to: CanonicalTransactionChain.address,
data: '0x' + methodId + calldata,
data:
ethers.utils.id('appendSequencerBatch()').slice(0, 10) +
encodeAppendSequencerBatch(batch),
})
}
describe('CanonicalTransactionChain', () => {
let addressManagerOwner: Signer
let sequencer: Signer
let otherSigner: Signer
let addressManagerOwner: SignerWithAddress
let sequencer: SignerWithAddress
let otherSigner: SignerWithAddress
before(async () => {
;[addressManagerOwner, sequencer, otherSigner] = await ethers.getSigners()
})
let AddressManager: Contract
let CanonicalTransactionChain: Contract
let Fake__StateCommitmentChain: FakeContract
before(async () => {
AddressManager = await makeAddressManager()
await AddressManager.setAddress(
'OVM_Sequencer',
await sequencer.getAddress()
)
beforeEach(async () => {
AddressManager = await deploy('Lib_AddressManager')
Fake__StateCommitmentChain = await smock.fake<Contract>(
await ethers.getContractFactory('StateCommitmentChain')
'StateCommitmentChain'
)
await setProxyTarget(
......@@ -86,33 +72,22 @@ describe('CanonicalTransactionChain', () => {
'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'
)
})
CanonicalTransactionChain = await deploy('CanonicalTransactionChain', {
signer: sequencer,
args: [
AddressManager.address,
MAX_GAS_LIMIT,
L2_GAS_DISCOUNT_DIVISOR,
ENQUEUE_GAS_COST,
],
})
let CanonicalTransactionChain: Contract
beforeEach(async () => {
CanonicalTransactionChain = await Factory__CanonicalTransactionChain.deploy(
AddressManager.address,
MAX_GAS_LIMIT,
L2_GAS_DISCOUNT_DIVISOR,
ENQUEUE_GAS_COST
)
const batches = await deploy('ChainStorageContainer', {
args: [AddressManager.address, 'CanonicalTransactionChain'],
})
const batches = await Factory__ChainStorageContainer.deploy(
AddressManager.address,
'CanonicalTransactionChain'
)
await AddressManager.setAddress('OVM_Sequencer', sequencer.address)
await AddressManager.setAddress(
'ChainStorageContainer-CTC-batches',
......@@ -197,19 +172,19 @@ describe('CanonicalTransactionChain', () => {
})
it('should revert if transaction gas limit does not cover rollup burn', async () => {
const _enqueueL2GasPrepaid =
const enqueueL2GasPrepaid =
await CanonicalTransactionChain.enqueueL2GasPrepaid()
const l2GasDiscountDivisor =
await CanonicalTransactionChain.l2GasDiscountDivisor()
const data = '0x' + '12'.repeat(1234)
// 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
// additional gas overhead, it will be enough trigger the gas burn, but not enough to cover
// it.
const l1GasLimit =
(l2GasLimit - _enqueueL2GasPrepaid) / l2GasDiscountDivisor
(l2GasLimit - enqueueL2GasPrepaid) / l2GasDiscountDivisor
await expect(
CanonicalTransactionChain.enqueue(target, l2GasLimit, data, {
......@@ -219,12 +194,12 @@ describe('CanonicalTransactionChain', () => {
})
it('should burn L1 gas when L2 gas limit is high', async () => {
const _enqueueL2GasPrepaid =
const enqueueL2GasPrepaid =
await CanonicalTransactionChain.enqueueL2GasPrepaid()
const data = '0x' + '12'.repeat(1234)
// Create a tx with high L2 gas limit
const l2GasLimit = 4 * _enqueueL2GasPrepaid
const l2GasLimit = 4 * enqueueL2GasPrepaid
await expect(CanonicalTransactionChain.enqueue(target, l2GasLimit, data))
.to.not.be.reverted
......@@ -286,6 +261,7 @@ describe('CanonicalTransactionChain', () => {
data
)
const receipt2 = await res2.wait()
expect(receipt1.gasUsed).to.equal(receipt2.gasUsed)
})
})
......@@ -312,21 +288,23 @@ describe('CanonicalTransactionChain', () => {
const blockNumber = await getNextBlockNumber(ethers.provider)
await setEthTime(ethers.provider, timestamp)
const transactionHash = getTransactionHash(
await addressManagerOwner.getAddress(),
target,
gasLimit,
data
const transactionHash = ethers.utils.keccak256(
encodeQueueTransaction(
addressManagerOwner.address,
target,
gasLimit,
data
)
)
await CanonicalTransactionChain.enqueue(target, gasLimit, data)
await CanonicalTransactionChain.connect(
addressManagerOwner
).enqueue(target, gasLimit, data)
for (let i = 0; i < size; i++) {
await CanonicalTransactionChain.enqueue(
target,
gasLimit,
'0x' + '12'.repeat(i + 1)
)
await CanonicalTransactionChain.connect(
addressManagerOwner
).enqueue(target, gasLimit, '0x' + '12'.repeat(i + 1))
}
expect(
......@@ -356,20 +334,22 @@ describe('CanonicalTransactionChain', () => {
blockNumber = await getNextBlockNumber(ethers.provider)
await setEthTime(ethers.provider, timestamp)
transactionHash = getTransactionHash(
await addressManagerOwner.getAddress(),
target,
gasLimit,
data
transactionHash = ethers.utils.keccak256(
encodeQueueTransaction(
addressManagerOwner.address,
target,
gasLimit,
data
)
)
await CanonicalTransactionChain.enqueue(target, gasLimit, data)
await CanonicalTransactionChain.connect(
addressManagerOwner
).enqueue(target, gasLimit, data)
} else {
await CanonicalTransactionChain.enqueue(
target,
gasLimit,
'0x' + '12'.repeat(i + 1)
)
await CanonicalTransactionChain.connect(
addressManagerOwner
).enqueue(target, gasLimit, '0x' + '12'.repeat(i + 1))
}
}
......@@ -399,20 +379,22 @@ describe('CanonicalTransactionChain', () => {
blockNumber = await getNextBlockNumber(ethers.provider)
await setEthTime(ethers.provider, timestamp)
transactionHash = getTransactionHash(
await addressManagerOwner.getAddress(),
target,
gasLimit,
data
transactionHash = ethers.utils.keccak256(
encodeQueueTransaction(
addressManagerOwner.address,
target,
gasLimit,
data
)
)
await CanonicalTransactionChain.enqueue(target, gasLimit, data)
await CanonicalTransactionChain.connect(
addressManagerOwner
).enqueue(target, gasLimit, data)
} else {
await CanonicalTransactionChain.enqueue(
target,
gasLimit,
'0x' + '12'.repeat(i + 1)
)
await CanonicalTransactionChain.connect(
addressManagerOwner
).enqueue(target, gasLimit, '0x' + '12'.repeat(i + 1))
}
}
......@@ -432,10 +414,6 @@ describe('CanonicalTransactionChain', () => {
})
describe('appendSequencerBatch', () => {
beforeEach(() => {
CanonicalTransactionChain = CanonicalTransactionChain.connect(sequencer)
})
it('should revert if expected start does not match current total batches', async () => {
await expect(
appendSequencerBatch(CanonicalTransactionChain, {
......@@ -499,9 +477,7 @@ describe('CanonicalTransactionChain', () => {
it('should emit the previous blockhash in the TransactionBatchAppended event', async () => {
const timestamp = await getEthTime(ethers.provider)
const currentBlockHash = await (
await ethers.provider.getBlock('latest')
).hash
const currentBlock = await ethers.provider.getBlock('latest')
const blockNumber = await getNextBlockNumber(ethers.provider)
const res = await appendSequencerBatch(CanonicalTransactionChain, {
transactions: ['0x1234'],
......@@ -525,7 +501,7 @@ describe('CanonicalTransactionChain', () => {
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) {
......@@ -680,7 +656,7 @@ describe('CanonicalTransactionChain', () => {
return '0x' + '12' + '34'.repeat(idx)
})
const res = await appendSequencerBatch(
await appendSequencerBatch(
CanonicalTransactionChain.connect(sequencer),
{
transactions,
......@@ -689,7 +665,6 @@ describe('CanonicalTransactionChain', () => {
totalElementsToAppend: size,
}
)
await res.wait()
expect(await CanonicalTransactionChain.getLastTimestamp()).to.equal(
timestamp
......
/* External Imports */
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 { makeAddressManager, NON_NULL_BYTES32 } from '../../../helpers'
import { deploy, NON_NULL_BYTES32 } from '../../../helpers'
describe('ChainStorageContainer', () => {
let sequencer: Signer
let otherSigner: Signer
let signer: Signer
let signerAddress: string
let AddressManager: Contract
let Factory__ChainStorageContainer: ContractFactory
let signer1: SignerWithAddress
let signer2: SignerWithAddress
before(async () => {
;[sequencer, otherSigner, signer] = await ethers.getSigners()
signerAddress = await otherSigner.getAddress()
AddressManager = await makeAddressManager()
await AddressManager.setAddress(
'OVM_Sequencer',
await sequencer.getAddress()
)
Factory__ChainStorageContainer = await ethers.getContractFactory(
'ChainStorageContainer'
)
;[signer1, signer2] = await ethers.getSigners()
})
let AddressManager: Contract
let ChainStorageContainer: Contract
beforeEach(async () => {
ChainStorageContainer = await Factory__ChainStorageContainer.connect(
otherSigner
).deploy(AddressManager.address, signerAddress)
await AddressManager.setAddress(
'ChainStorageContainer',
ChainStorageContainer.address
)
AddressManager = await deploy('Lib_AddressManager')
ChainStorageContainer = await deploy('ChainStorageContainer', {
signer: signer1,
args: [AddressManager.address, signer1.address],
})
await AddressManager.setAddress(signerAddress, signerAddress)
// ChainStorageContainer uses name resolution to check the owner address.
await AddressManager.setAddress(signer1.address, signer1.address)
})
describe('push', () => {
for (const len of [1, 2, 4, 8, 32]) {
it(`it should be able to add ${len} element(s) to the array`, async () => {
for (let i = 0; i < len; i++) {
await expect(
ChainStorageContainer.connect(otherSigner)['push(bytes32)'](
NON_NULL_BYTES32
)
).to.not.be.reverted
await expect(ChainStorageContainer['push(bytes32)'](NON_NULL_BYTES32))
.to.not.be.reverted
}
})
}
......@@ -60,9 +39,7 @@ describe('ChainStorageContainer', () => {
describe('setGlobalMetadata', () => {
it('should modify the extra data', async () => {
const globalMetaData = `0x${'11'.repeat(27)}`
await ChainStorageContainer.connect(otherSigner).setGlobalMetadata(
globalMetaData
)
await ChainStorageContainer.setGlobalMetadata(globalMetaData)
expect(await ChainStorageContainer.getGlobalMetadata()).to.equal(
globalMetaData
......@@ -73,15 +50,13 @@ describe('ChainStorageContainer', () => {
describe('deleteElementsAfterInclusive', () => {
it('should revert when the array is empty', async () => {
await expect(
ChainStorageContainer.connect(otherSigner)[
'deleteElementsAfterInclusive(uint256)'
](0)
ChainStorageContainer['deleteElementsAfterInclusive(uint256)'](0)
).to.be.reverted
})
it('should revert when called by non-owner', async () => {
await expect(
ChainStorageContainer.connect(signer)[
ChainStorageContainer.connect(signer2)[
'deleteElementsAfterInclusive(uint256)'
](0)
).to.be.revertedWith(
......@@ -96,18 +71,14 @@ describe('ChainStorageContainer', () => {
for (let i = 0; i < len; i++) {
const value = NON_NULL_BYTES32
values.push(value)
await ChainStorageContainer.connect(otherSigner)['push(bytes32)'](
value
)
await ChainStorageContainer['push(bytes32)'](value)
}
})
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 () => {
await expect(
ChainStorageContainer.connect(otherSigner)[
'deleteElementsAfterInclusive(uint256)'
](i)
ChainStorageContainer['deleteElementsAfterInclusive(uint256)'](i)
).to.not.be.reverted
expect(await ChainStorageContainer.length()).to.equal(i)
......
/* External Imports */
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'
/* Internal Imports */
import { expect } from '../../../setup'
import {
makeAddressManager,
deploy,
setProxyTarget,
NON_NULL_BYTES32,
getEthTime,
......@@ -14,22 +13,21 @@ import {
} from '../../../helpers'
describe('StateCommitmentChain', () => {
let sequencer: Signer
let user: Signer
let sequencer: SignerWithAddress
let user: SignerWithAddress
const batch = [NON_NULL_BYTES32]
before(async () => {
;[sequencer, user] = await ethers.getSigners()
})
let AddressManager: Contract
before(async () => {
AddressManager = await makeAddressManager()
})
let Fake__CanonicalTransactionChain: FakeContract
let Fake__BondManager: FakeContract
before(async () => {
AddressManager = await deploy('Lib_AddressManager')
Fake__CanonicalTransactionChain = await smock.fake<Contract>(
await ethers.getContractFactory('CanonicalTransactionChain')
'CanonicalTransactionChain'
)
await setProxyTarget(
......@@ -38,44 +36,29 @@ describe('StateCommitmentChain', () => {
Fake__CanonicalTransactionChain
)
Fake__BondManager = await smock.fake<Contract>(
await ethers.getContractFactory('BondManager')
)
Fake__BondManager = await smock.fake<Contract>('BondManager')
await setProxyTarget(AddressManager, 'BondManager', Fake__BondManager)
Fake__BondManager.isCollateralized.returns(true)
await AddressManager.setAddress(
'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'
)
await AddressManager.setAddress('OVM_Proposer', sequencer.address)
})
let StateCommitmentChain: Contract
beforeEach(async () => {
StateCommitmentChain = await Factory__StateCommitmentChain.deploy(
AddressManager.address,
60 * 60 * 24 * 7, // 1 week fraud proof window
60 * 30 // 30 minute sequencer publish window
)
StateCommitmentChain = await deploy('StateCommitmentChain', {
signer: sequencer,
args: [
AddressManager.address,
60 * 60 * 24 * 7, // 1 week fraud proof window
60 * 30, // 30 minute sequencer publish window
],
})
const batches = await Factory__ChainStorageContainer.deploy(
AddressManager.address,
'StateCommitmentChain'
)
const batches = await deploy('ChainStorageContainer', {
args: [AddressManager.address, 'StateCommitmentChain'],
})
await AddressManager.setAddress(
'ChainStorageContainer-SCC-batches',
......@@ -90,18 +73,14 @@ describe('StateCommitmentChain', () => {
describe('appendStateBatch', () => {
describe('when the provided batch is empty', () => {
const batch = []
it('should revert', async () => {
await expect(
StateCommitmentChain.appendStateBatch(batch, 0)
StateCommitmentChain.appendStateBatch([], 0)
).to.be.revertedWith('Cannot submit an empty state batch.')
})
})
describe('when the provided batch is not empty', () => {
const batch = [NON_NULL_BYTES32]
describe('when start index does not match total elements', () => {
it('should revert', async () => {
await expect(
......@@ -145,10 +124,7 @@ describe('StateCommitmentChain', () => {
batch.length * 2
)
await StateCommitmentChain.connect(sequencer).appendStateBatch(
batch,
0
)
await StateCommitmentChain.appendStateBatch(batch, 0)
})
describe('when inside sequencer publish window', () => {
......@@ -195,7 +171,6 @@ describe('StateCommitmentChain', () => {
})
describe('deleteStateBatch', () => {
const batch = [NON_NULL_BYTES32]
const batchHeader = {
batchIndex: 0,
batchRoot: NON_NULL_BYTES32,
......@@ -213,7 +188,7 @@ describe('StateCommitmentChain', () => {
await StateCommitmentChain.appendStateBatch(batch, 0)
batchHeader.extraData = ethers.utils.defaultAbiCoder.encode(
['uint256', 'address'],
[await getEthTime(ethers.provider), await sequencer.getAddress()]
[await getEthTime(ethers.provider), sequencer.address]
)
})
......@@ -236,10 +211,7 @@ describe('StateCommitmentChain', () => {
describe('when the sender is the OVM_FraudVerifier', () => {
before(async () => {
await AddressManager.setAddress(
'OVM_FraudVerifier',
await sequencer.getAddress()
)
await AddressManager.setAddress('OVM_FraudVerifier', sequencer.address)
})
describe('when the provided batch index is greater than the total submitted', () => {
......@@ -302,13 +274,14 @@ describe('StateCommitmentChain', () => {
prevTotalElements: 0,
extraData: ethers.constants.HashZero,
}
it('should revert when timestamp is zero', async () => {
await expect(
StateCommitmentChain.insideFraudProofWindow({
...batchHeader,
extraData: ethers.utils.defaultAbiCoder.encode(
['uint256', 'address'],
[0, await sequencer.getAddress()]
[0, sequencer.address]
),
})
).to.be.revertedWith('Batch header timestamp cannot be zero')
......@@ -324,7 +297,6 @@ describe('StateCommitmentChain', () => {
describe('when one batch element has been inserted', () => {
beforeEach(async () => {
const batch = [NON_NULL_BYTES32]
Fake__CanonicalTransactionChain.getTotalElements.returns(batch.length)
await StateCommitmentChain.appendStateBatch(batch, 0)
})
......@@ -336,9 +308,11 @@ describe('StateCommitmentChain', () => {
describe('when 64 batch elements have been inserted in one batch', () => {
beforeEach(async () => {
const batch = Array(64).fill(NON_NULL_BYTES32)
Fake__CanonicalTransactionChain.getTotalElements.returns(batch.length)
await StateCommitmentChain.appendStateBatch(batch, 0)
const batchArray = Array(64).fill(NON_NULL_BYTES32)
Fake__CanonicalTransactionChain.getTotalElements.returns(
batchArray.length
)
await StateCommitmentChain.appendStateBatch(batchArray, 0)
})
it('should return the number of inserted batch elements', async () => {
......@@ -348,12 +322,12 @@ describe('StateCommitmentChain', () => {
describe('when 32 batch elements have been inserted in each of two batches', () => {
beforeEach(async () => {
const batch = Array(32).fill(NON_NULL_BYTES32)
const batchArray = Array(32).fill(NON_NULL_BYTES32)
Fake__CanonicalTransactionChain.getTotalElements.returns(
batch.length * 2
batchArray.length * 2
)
await StateCommitmentChain.appendStateBatch(batch, 0)
await StateCommitmentChain.appendStateBatch(batch, 32)
await StateCommitmentChain.appendStateBatch(batchArray, 0)
await StateCommitmentChain.appendStateBatch(batchArray, 32)
})
it('should return the number of inserted batch elements', async () => {
......@@ -371,7 +345,6 @@ describe('StateCommitmentChain', () => {
describe('when one batch has been inserted', () => {
beforeEach(async () => {
const batch = [NON_NULL_BYTES32]
Fake__CanonicalTransactionChain.getTotalElements.returns(batch.length)
await StateCommitmentChain.appendStateBatch(batch, 0)
})
......@@ -383,7 +356,6 @@ describe('StateCommitmentChain', () => {
describe('when 8 batches have been inserted', () => {
beforeEach(async () => {
const batch = [NON_NULL_BYTES32]
Fake__CanonicalTransactionChain.getTotalElements.returns(
batch.length * 8
)
......@@ -411,7 +383,6 @@ describe('StateCommitmentChain', () => {
describe('when one batch element has been inserted', () => {
let timestamp
beforeEach(async () => {
const batch = [NON_NULL_BYTES32]
Fake__CanonicalTransactionChain.getTotalElements.returns(batch.length)
await StateCommitmentChain.appendStateBatch(batch, 0)
timestamp = await getEthTime(ethers.provider)
......@@ -426,7 +397,6 @@ describe('StateCommitmentChain', () => {
})
describe('verifyStateCommitment()', () => {
const batch = [NON_NULL_BYTES32]
const batchHeader = {
batchIndex: 0,
batchRoot: NON_NULL_BYTES32,
......@@ -451,7 +421,7 @@ describe('StateCommitmentChain', () => {
await StateCommitmentChain.appendStateBatch(batch, 0)
batchHeader.extraData = ethers.utils.defaultAbiCoder.encode(
['uint256', 'address'],
[await getEthTime(ethers.provider), await sequencer.getAddress()]
[await getEthTime(ethers.provider), sequencer.address]
)
})
......
/* External Imports */
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 { deploy } from '../../../helpers'
const initialMinDepositAmount = ethers.utils.parseEther('0.01')
const initialMaxDepositAmount = ethers.utils.parseEther('1')
const initialMaxBalance = ethers.utils.parseEther('2')
describe('TeleportrDeposit', async () => {
let teleportrDeposit: Contract
let signer: Signer
let signer2: Signer
let contractAddress: string
let signerAddress: string
let signer2Address: string
let signer1: SignerWithAddress
let signer2: SignerWithAddress
before(async () => {
;[signer, signer2] = await ethers.getSigners()
teleportrDeposit = await (
await ethers.getContractFactory('TeleportrDeposit')
).deploy(
initialMinDepositAmount,
initialMaxDepositAmount,
initialMaxBalance
)
contractAddress = teleportrDeposit.address
signerAddress = await signer.getAddress()
signer2Address = await signer2.getAddress()
;[signer1, signer2] = await ethers.getSigners()
})
let TeleportrDeposit: Contract
before(async () => {
TeleportrDeposit = await deploy('TeleportrDeposit', {
args: [
initialMinDepositAmount,
initialMaxDepositAmount,
initialMaxBalance,
],
})
})
describe('receive', async () => {
const oneETH = ethers.utils.parseEther('1.0')
const twoETH = ethers.utils.parseEther('2.0')
it('should revert if deposit amount is less than min amount', async () => {
await expect(
signer.sendTransaction({
to: contractAddress,
signer1.sendTransaction({
to: TeleportrDeposit.address,
value: ethers.utils.parseEther('0.001'),
})
).to.be.revertedWith('Deposit amount is too small')
})
it('should revert if deposit amount is greater than max amount', async () => {
await expect(
signer.sendTransaction({
to: contractAddress,
signer1.sendTransaction({
to: TeleportrDeposit.address,
value: ethers.utils.parseEther('1.1'),
})
).to.be.revertedWith('Deposit amount is too big')
})
it('should emit EtherReceived if called by non-owner', async () => {
await expect(
signer2.sendTransaction({
to: contractAddress,
to: TeleportrDeposit.address,
value: oneETH,
})
)
.to.emit(teleportrDeposit, 'EtherReceived')
.withArgs(BigNumber.from('0'), signer2Address, oneETH)
.to.emit(TeleportrDeposit, 'EtherReceived')
.withArgs(BigNumber.from('0'), signer2.address, oneETH)
})
it('should increase the contract balance by deposit amount', async () => {
await expect(await ethers.provider.getBalance(contractAddress)).to.equal(
oneETH
)
expect(
await ethers.provider.getBalance(TeleportrDeposit.address)
).to.equal(oneETH)
})
it('should emit EtherReceived if called by owner', async () => {
await expect(
signer.sendTransaction({
to: contractAddress,
signer1.sendTransaction({
to: TeleportrDeposit.address,
value: oneETH,
})
)
.to.emit(teleportrDeposit, 'EtherReceived')
.withArgs(BigNumber.from('1'), signerAddress, oneETH)
.to.emit(TeleportrDeposit, 'EtherReceived')
.withArgs(BigNumber.from('1'), signer1.address, oneETH)
})
it('should increase the contract balance by deposit amount', async () => {
await expect(await ethers.provider.getBalance(contractAddress)).to.equal(
twoETH
)
expect(
await ethers.provider.getBalance(TeleportrDeposit.address)
).to.equal(twoETH)
})
it('should revert if deposit will exceed max balance', async () => {
await expect(
signer.sendTransaction({
to: contractAddress,
signer1.sendTransaction({
to: TeleportrDeposit.address,
value: initialMinDepositAmount,
})
).to.be.revertedWith('Contract max balance exceeded')
})
})
describe('withdrawBalance', async () => {
let initialContractBalance: BigNumber
let initialSignerBalance: BigNumber
before(async () => {
initialContractBalance = await ethers.provider.getBalance(contractAddress)
initialSignerBalance = await ethers.provider.getBalance(signerAddress)
initialContractBalance = await ethers.provider.getBalance(
TeleportrDeposit.address
)
initialSignerBalance = await signer1.getBalance()
})
it('should revert if called by non-owner', async () => {
await expect(
teleportrDeposit.connect(signer2).withdrawBalance()
TeleportrDeposit.connect(signer2).withdrawBalance()
).to.be.revertedWith('Ownable: caller is not the owner')
})
it('should emit BalanceWithdrawn if called by owner', async () => {
await expect(teleportrDeposit.withdrawBalance())
.to.emit(teleportrDeposit, 'BalanceWithdrawn')
.withArgs(signerAddress, initialContractBalance)
await expect(TeleportrDeposit.withdrawBalance())
.to.emit(TeleportrDeposit, 'BalanceWithdrawn')
.withArgs(signer1.address, initialContractBalance)
})
it('should leave the contract with zero balance', async () => {
await expect(await ethers.provider.getBalance(contractAddress)).to.equal(
ethers.utils.parseEther('0')
)
expect(
await ethers.provider.getBalance(TeleportrDeposit.address)
).to.equal(ethers.utils.parseEther('0'))
})
it('should credit owner with contract balance - fees', async () => {
const expSignerBalance = initialSignerBalance.add(initialContractBalance)
await expect(
await ethers.provider.getBalance(signerAddress)
).to.be.closeTo(expSignerBalance, 10 ** 15)
expect(await signer1.getBalance()).to.be.closeTo(
expSignerBalance,
10 ** 15
)
})
})
describe('setMinAmount', async () => {
const newMinDepositAmount = ethers.utils.parseEther('0.02')
it('should revert if called by non-owner', async () => {
await expect(
teleportrDeposit.connect(signer2).setMinAmount(newMinDepositAmount)
TeleportrDeposit.connect(signer2).setMinAmount(newMinDepositAmount)
).to.be.revertedWith('Ownable: caller is not the owner')
})
it('should emit MinDepositAmountSet if called by owner', async () => {
await expect(teleportrDeposit.setMinAmount(newMinDepositAmount))
.to.emit(teleportrDeposit, 'MinDepositAmountSet')
await expect(TeleportrDeposit.setMinAmount(newMinDepositAmount))
.to.emit(TeleportrDeposit, 'MinDepositAmountSet')
.withArgs(initialMinDepositAmount, newMinDepositAmount)
})
it('should have updated minDepositAmount after success', async () => {
await expect(await teleportrDeposit.minDepositAmount()).to.be.eq(
expect(await TeleportrDeposit.minDepositAmount()).to.be.eq(
newMinDepositAmount
)
})
})
describe('setMaxAmount', async () => {
const newMaxDepositAmount = ethers.utils.parseEther('2')
it('should revert if called non-owner', async () => {
await expect(
teleportrDeposit.connect(signer2).setMaxAmount(newMaxDepositAmount)
TeleportrDeposit.connect(signer2).setMaxAmount(newMaxDepositAmount)
).to.be.revertedWith('Ownable: caller is not the owner')
})
it('should emit MaxDepositAmountSet if called by owner', async () => {
await expect(teleportrDeposit.setMaxAmount(newMaxDepositAmount))
.to.emit(teleportrDeposit, 'MaxDepositAmountSet')
await expect(TeleportrDeposit.setMaxAmount(newMaxDepositAmount))
.to.emit(TeleportrDeposit, 'MaxDepositAmountSet')
.withArgs(initialMaxDepositAmount, newMaxDepositAmount)
})
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
)
})
})
describe('setMaxBalance', async () => {
const newMaxBalance = ethers.utils.parseEther('2000')
it('should revert if called by non-owner', async () => {
await expect(
teleportrDeposit.connect(signer2).setMaxBalance(newMaxBalance)
TeleportrDeposit.connect(signer2).setMaxBalance(newMaxBalance)
).to.be.revertedWith('Ownable: caller is not the owner')
})
it('should emit MaxBalanceSet if called by owner', async () => {
await expect(teleportrDeposit.setMaxBalance(newMaxBalance))
.to.emit(teleportrDeposit, 'MaxBalanceSet')
await expect(TeleportrDeposit.setMaxBalance(newMaxBalance))
.to.emit(TeleportrDeposit, 'MaxBalanceSet')
.withArgs(initialMaxBalance, newMaxBalance)
})
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 { Signer, Contract } from 'ethers'
import { Contract } from 'ethers'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
/* Internal Imports */
import { expect } from '../../../setup'
import { makeAddressManager } from '../../../helpers'
import { deploy } from '../../../helpers'
describe('BondManager', () => {
let sequencer: Signer
let nonSequencer: Signer
let sequencer: SignerWithAddress
let nonSequencer: SignerWithAddress
before(async () => {
;[sequencer, nonSequencer] = await ethers.getSigners()
})
let AddressManager: Contract
before(async () => {
AddressManager = await makeAddressManager()
})
let BondManager: Contract
before(async () => {
BondManager = await (
await ethers.getContractFactory('BondManager')
).deploy(AddressManager.address)
beforeEach(async () => {
AddressManager = await deploy('Lib_AddressManager')
BondManager = await deploy('BondManager', {
args: [AddressManager.address],
})
AddressManager.setAddress('OVM_Proposer', await sequencer.getAddress())
AddressManager.setAddress('OVM_Proposer', sequencer.address)
})
describe('isCollateralized', () => {
it('should return true for OVM_Proposer', async () => {
expect(
await BondManager.isCollateralized(await sequencer.getAddress())
).to.equal(true)
expect(await BondManager.isCollateralized(sequencer.address)).to.equal(
true
)
})
it('should return false for non-sequencer', async () => {
expect(
await BondManager.isCollateralized(await nonSequencer.getAddress())
).to.equal(false)
expect(await BondManager.isCollateralized(nonSequencer.address)).to.equal(
false
)
})
})
})
/* External Imports */
import hre, { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers'
import { ethers } from 'hardhat'
import { Contract } from 'ethers'
import { applyL1ToL2Alias } from '@eth-optimism/core-utils'
import {
smock,
MockContractFactory,
FakeContract,
} 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 { predeploys } from '../../../../src'
import {
impersonate,
deploy,
NON_NULL_BYTES32,
NON_ZERO_ADDRESS,
encodeXDomainCalldata,
} from '../../../helpers'
import { predeploys } from '../../../../src'
describe('L2CrossDomainMessenger', () => {
let signer: Signer
let signer: SignerWithAddress
before(async () => {
;[signer] = await ethers.getSigners()
})
......@@ -27,61 +24,37 @@ describe('L2CrossDomainMessenger', () => {
let Fake__L1CrossDomainMessenger: FakeContract
let Fake__OVM_L2ToL1MessagePasser: FakeContract
before(async () => {
Fake__TargetContract = await smock.fake<Contract>(
await ethers.getContractFactory('Helper_SimpleProxy')
)
Fake__TargetContract = await smock.fake<Contract>('Helper_SimpleProxy')
Fake__L1CrossDomainMessenger = await smock.fake<Contract>(
await ethers.getContractFactory('L1CrossDomainMessenger')
'L1CrossDomainMessenger'
)
Fake__OVM_L2ToL1MessagePasser = await smock.fake<Contract>(
await ethers.getContractFactory('OVM_L2ToL1MessagePasser'),
'OVM_L2ToL1MessagePasser',
{ address: predeploys.OVM_L2ToL1MessagePasser }
)
})
let impersonatedL1CrossDomainMessengerSender: Signer
let impersonatedL1CrossDomainMessengerSender: SignerWithAddress
before(async () => {
const impersonatedAddress = applyL1ToL2Alias(
Fake__L1CrossDomainMessenger.address
)
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'
impersonatedL1CrossDomainMessengerSender = await impersonate(
applyL1ToL2Alias(Fake__L1CrossDomainMessenger.address),
'0xFFFFFFFFFFFFFFFFF'
)
})
let L2CrossDomainMessenger: Contract
beforeEach(async () => {
L2CrossDomainMessenger = await Factory__L2CrossDomainMessenger.deploy(
Fake__L1CrossDomainMessenger.address
)
L2CrossDomainMessenger = await deploy('L2CrossDomainMessenger', {
args: [Fake__L1CrossDomainMessenger.address],
})
})
describe('xDomainMessageSender', () => {
let Mock__Factory__L2CrossDomainMessenger: MockContractFactory<ContractFactory>
let Mock__L2CrossDomainMessenger
let Mock__L2CrossDomainMessenger: MockContract<Contract>
before(async () => {
Mock__Factory__L2CrossDomainMessenger = await smock.mock(
'L2CrossDomainMessenger'
)
Mock__L2CrossDomainMessenger =
await Mock__Factory__L2CrossDomainMessenger.deploy(
Fake__L1CrossDomainMessenger.address
)
Mock__L2CrossDomainMessenger = await (
await smock.mock('L2CrossDomainMessenger')
).deploy(Fake__L1CrossDomainMessenger.address)
})
it('should return the xDomainMsgSender address', async () => {
......@@ -89,6 +62,7 @@ describe('L2CrossDomainMessenger', () => {
'xDomainMsgSender',
'0x0000000000000000000000000000000000000000'
)
expect(
await Mock__L2CrossDomainMessenger.xDomainMessageSender()
).to.equal('0x0000000000000000000000000000000000000000')
......@@ -96,49 +70,53 @@ describe('L2CrossDomainMessenger', () => {
})
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 () => {
await expect(
L2CrossDomainMessenger.sendMessage(target, message, gasLimit)
L2CrossDomainMessenger.sendMessage(
NON_ZERO_ADDRESS,
NON_NULL_BYTES32,
100_000
)
).to.not.be.reverted
expect(
Fake__OVM_L2ToL1MessagePasser.passMessageToL1.getCall(0).args[0]
).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 () => {
await L2CrossDomainMessenger.sendMessage(target, message, gasLimit)
await L2CrossDomainMessenger.sendMessage(
NON_ZERO_ADDRESS,
NON_NULL_BYTES32,
100_000
)
await expect(
L2CrossDomainMessenger.sendMessage(target, message, gasLimit)
L2CrossDomainMessenger.sendMessage(
NON_ZERO_ADDRESS,
NON_NULL_BYTES32,
100_000
)
).to.not.be.reverted
})
})
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 () => {
await expect(
L2CrossDomainMessenger.connect(signer).relayMessage(
target,
sender,
message,
Fake__TargetContract.address,
signer.address,
Fake__TargetContract.interface.encodeFunctionData('setTarget', [
NON_ZERO_ADDRESS,
]),
0
)
).to.be.revertedWith('Provided message could not be verified.')
......@@ -147,7 +125,14 @@ describe('L2CrossDomainMessenger', () => {
it('should send a call to the target contract', async () => {
await L2CrossDomainMessenger.connect(
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(
NON_ZERO_ADDRESS
......@@ -161,7 +146,14 @@ describe('L2CrossDomainMessenger', () => {
await L2CrossDomainMessenger.connect(
impersonatedL1CrossDomainMessengerSender
).relayMessage(target, sender, message, 0)
).relayMessage(
Fake__TargetContract.address,
signer.address,
Fake__TargetContract.interface.encodeFunctionData('setTarget', [
NON_ZERO_ADDRESS,
]),
0
)
await expect(
L2CrossDomainMessenger.xDomainMessageSender()
......@@ -171,45 +163,72 @@ describe('L2CrossDomainMessenger', () => {
it('should revert if trying to send the same message twice', async () => {
await L2CrossDomainMessenger.connect(
impersonatedL1CrossDomainMessengerSender
).relayMessage(target, sender, message, 0)
).relayMessage(
Fake__TargetContract.address,
signer.address,
Fake__TargetContract.interface.encodeFunctionData('setTarget', [
NON_ZERO_ADDRESS,
]),
0
)
await expect(
L2CrossDomainMessenger.connect(
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.')
})
it('should not make a call if the target is the L2 MessagePasser', async () => {
target = predeploys.OVM_L2ToL1MessagePasser
message = Fake__OVM_L2ToL1MessagePasser.interface.encodeFunctionData(
'passMessageToL1(bytes)',
[NON_NULL_BYTES32]
)
const resProm = L2CrossDomainMessenger.connect(
impersonatedL1CrossDomainMessengerSender
).relayMessage(target, sender, message, 0)
).relayMessage(
predeploys.OVM_L2ToL1MessagePasser,
signer.address,
Fake__OVM_L2ToL1MessagePasser.interface.encodeFunctionData(
'passMessageToL1(bytes)',
[NON_NULL_BYTES32]
),
0
)
// The call to relayMessage() should succeed.
await expect(resProm).to.not.be.reverted
// There should be no 'relayedMessage' event logged in the receipt.
const logs = (
await Fake__OVM_L2ToL1MessagePasser.provider.getTransactionReceipt(
(
await resProm
).hash
)
).logs
expect(logs).to.deep.equal([])
expect(
(
await Fake__OVM_L2ToL1MessagePasser.provider.getTransactionReceipt(
(
await resProm
).hash
)
).logs
).to.deep.equal([])
// The message should be registered as successful.
expect(
await L2CrossDomainMessenger.successfulMessages(
ethers.utils.solidityKeccak256(
['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
......
/* External Imports */
import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers'
import { Contract } from 'ethers'
import { smock, FakeContract, MockContract } from '@defi-wonderland/smock'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
/* Internal Imports */
import { expect } from '../../../setup'
import { NON_NULL_BYTES32, NON_ZERO_ADDRESS } from '../../../helpers'
import { getContractInterface } from '../../../../src'
const ERR_INVALID_MESSENGER = 'OVM_XCHAIN: messenger contract unauthenticated'
const ERR_INVALID_X_DOMAIN_MSG_SENDER =
'OVM_XCHAIN: wrong sender of cross-domain message'
const DUMMY_L1BRIDGE_ADDRESS: string =
'0x1234123412341234123412341234123412341234'
const DUMMY_L1TOKEN_ADDRESS: string =
'0x2234223412342234223422342234223422342234'
const OVM_ETH_ADDRESS: string = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000'
import { deploy, NON_NULL_BYTES32, NON_ZERO_ADDRESS } from '../../../helpers'
import { getContractInterface, predeploys } from '../../../../src'
// TODO: Maybe we should consider automatically generating these and exporting them?
const ERROR_STRINGS = {
INVALID_MESSENGER: 'OVM_XCHAIN: messenger contract unauthenticated',
INVALID_X_DOMAIN_MSG_SENDER:
'OVM_XCHAIN: wrong sender of cross-domain message',
}
const DUMMY_L1_ERC20_ADDRESS = '0xaBBAABbaaBbAABbaABbAABbAABbaAbbaaBbaaBBa'
const DUMMY_L1_BRIDGE_ADDRESS = '0xACDCacDcACdCaCDcacdcacdCaCdcACdCAcDcaCdc'
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 ALICE_INITIAL_BALANCE = 50_000
let alice: SignerWithAddress
let bob: SignerWithAddress
let l2MessengerImpersonator: SignerWithAddress
before(async () => {
// Create a special signer which will enable us to send messages from the L2Messenger contract
;[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
......@@ -45,20 +35,25 @@ describe('L2StandardBridge', () => {
beforeEach(async () => {
// Get a new mock L2 messenger
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
{ address: await l2MessengerImpersonator.getAddress() }
)
// Deploy the contract under test
L2StandardBridge = await (
await ethers.getContractFactory('L2StandardBridge')
).deploy(Fake__L2CrossDomainMessenger.address, DUMMY_L1BRIDGE_ADDRESS)
L2StandardBridge = await deploy('L2StandardBridge', {
args: [Fake__L2CrossDomainMessenger.address, DUMMY_L1_BRIDGE_ADDRESS],
})
// Deploy an L2 ERC20
L2ERC20 = await (
await ethers.getContractFactory('L2StandardERC20', alice)
).deploy(L2StandardBridge.address, DUMMY_L1TOKEN_ADDRESS, 'L2Token', 'L2T')
L2ERC20 = await deploy('L2StandardERC20', {
args: [
L2StandardBridge.address,
DUMMY_L1_ERC20_ADDRESS,
'L2Token',
'L2T',
],
})
})
// test the transfer flow of moving a token from L2 to L1
......@@ -66,14 +61,14 @@ describe('L2StandardBridge', () => {
it('onlyFromCrossDomainAccount: should revert on calls from a non-crossDomainMessenger L2 account', async () => {
await expect(
L2StandardBridge.finalizeDeposit(
DUMMY_L1TOKEN_ADDRESS,
DUMMY_L1_ERC20_ADDRESS,
NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS,
0,
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 () => {
......@@ -83,7 +78,7 @@ describe('L2StandardBridge', () => {
await expect(
L2StandardBridge.connect(l2MessengerImpersonator).finalizeDeposit(
DUMMY_L1TOKEN_ADDRESS,
DUMMY_L1_ERC20_ADDRESS,
NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS,
......@@ -93,19 +88,17 @@ describe('L2StandardBridge', () => {
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 () => {
// Deploy a non compliant ERC20
const NonCompliantERC20 = await (
await ethers.getContractFactory(
'@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20'
)
).deploy('L2Token', 'L2T')
const NonCompliantERC20 = await deploy('ERC20', {
args: ['L2Token', 'L2T'],
})
L2StandardBridge.connect(l2MessengerImpersonator).finalizeDeposit(
DUMMY_L1TOKEN_ADDRESS,
DUMMY_L1_ERC20_ADDRESS,
NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS,
......@@ -117,14 +110,14 @@ describe('L2StandardBridge', () => {
)
Fake__L2CrossDomainMessenger.xDomainMessageSender.returns(
() => DUMMY_L1BRIDGE_ADDRESS
DUMMY_L1_BRIDGE_ADDRESS
)
await L2StandardBridge.connect(l2MessengerImpersonator).finalizeDeposit(
DUMMY_L1TOKEN_ADDRESS,
DUMMY_L1_ERC20_ADDRESS,
NonCompliantERC20.address,
aliceAddress,
bobsAddress,
alice.address,
bob.address,
100,
NON_NULL_BYTES32,
{
......@@ -132,37 +125,37 @@ describe('L2StandardBridge', () => {
}
)
const withdrawalCallToMessenger =
Fake__L2CrossDomainMessenger.sendMessage.getCall(1)
expect(withdrawalCallToMessenger.args[0]).to.equal(DUMMY_L1BRIDGE_ADDRESS)
expect(withdrawalCallToMessenger.args[1]).to.equal(
Factory__L1StandardBridge.interface.encodeFunctionData(
expect(
Fake__L2CrossDomainMessenger.sendMessage.getCall(1).args
).to.deep.equal([
DUMMY_L1_BRIDGE_ADDRESS,
getContractInterface('L1StandardBridge').encodeFunctionData(
'finalizeERC20Withdrawal',
[
DUMMY_L1TOKEN_ADDRESS,
DUMMY_L1_ERC20_ADDRESS,
NonCompliantERC20.address,
bobsAddress,
aliceAddress,
bob.address,
alice.address,
100,
NON_NULL_BYTES32,
]
)
)
),
0,
])
})
it('should credit funds to the depositor', async () => {
const depositAmount = 100
Fake__L2CrossDomainMessenger.xDomainMessageSender.returns(
() => DUMMY_L1BRIDGE_ADDRESS
DUMMY_L1_BRIDGE_ADDRESS
)
await L2StandardBridge.connect(l2MessengerImpersonator).finalizeDeposit(
DUMMY_L1TOKEN_ADDRESS,
DUMMY_L1_ERC20_ADDRESS,
L2ERC20.address,
aliceAddress,
bobsAddress,
alice.address,
bob.address,
depositAmount,
NON_NULL_BYTES32,
{
......@@ -170,37 +163,35 @@ describe('L2StandardBridge', () => {
}
)
const bobsBalance = await L2ERC20.balanceOf(bobsAddress)
bobsBalance.should.equal(depositAmount)
expect(await L2ERC20.balanceOf(bob.address)).to.equal(depositAmount)
})
})
describe('withdrawals', () => {
const withdrawAmount = 1_000
let Mock__L2Token: MockContract<Contract>
let Fake__OVM_ETH
let Fake__OVM_ETH: FakeContract<Contract>
before(async () => {
Fake__OVM_ETH = await smock.fake('OVM_ETH', {
address: OVM_ETH_ADDRESS,
address: predeploys.OVM_ETH,
})
})
let Mock__L2Token: MockContract<Contract>
beforeEach(async () => {
// Deploy a smodded gateway so we can give some balances to withdraw
Mock__L2Token = await (
await smock.mock('L2StandardERC20')
).deploy(
L2StandardBridge.address,
DUMMY_L1TOKEN_ADDRESS,
DUMMY_L1_ERC20_ADDRESS,
'L2Token',
'L2T'
)
await Mock__L2Token.setVariable('_totalSupply', INITIAL_TOTAL_SUPPLY)
await Mock__L2Token.setVariable('_balances', {
[aliceAddress]: ALICE_INITIAL_BALANCE,
[alice.address]: ALICE_INITIAL_BALANCE,
})
await Mock__L2Token.setVariable('l2Bridge', L2StandardBridge.address)
})
......@@ -213,25 +204,16 @@ describe('L2StandardBridge', () => {
NON_NULL_BYTES32
)
const withdrawalCallToMessenger =
Fake__L2CrossDomainMessenger.sendMessage.getCall(0)
// 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 L1StandardBridge to finalize the withdrawal
expect(withdrawalCallToMessenger.args[1]).to.equal(
Factory__L1StandardBridge.interface.encodeFunctionData(
expect(
Fake__L2CrossDomainMessenger.sendMessage.getCall(0).args
).to.deep.equal([
DUMMY_L1_BRIDGE_ADDRESS,
getContractInterface('L1StandardBridge').encodeFunctionData(
'finalizeETHWithdrawal',
[
await alice.getAddress(),
await alice.getAddress(),
0,
NON_NULL_BYTES32,
]
)
)
[alice.address, alice.address, 0, NON_NULL_BYTES32]
),
0,
])
})
it('withdraw() burns and sends the correct withdrawal message', async () => {
......@@ -241,112 +223,94 @@ describe('L2StandardBridge', () => {
0,
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
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(
expect(
Fake__L2CrossDomainMessenger.sendMessage.getCall(0).args
).to.deep.equal([
DUMMY_L1_BRIDGE_ADDRESS,
getContractInterface('L1StandardBridge').encodeFunctionData(
'finalizeERC20Withdrawal',
[
DUMMY_L1TOKEN_ADDRESS,
DUMMY_L1_ERC20_ADDRESS,
Mock__L2Token.address,
await alice.getAddress(),
await alice.getAddress(),
alice.address,
alice.address,
withdrawAmount,
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 () => {
await L2StandardBridge.withdrawTo(
Mock__L2Token.address,
await bob.getAddress(),
bob.address,
withdrawAmount,
0,
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
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)
// The message data should be a call telling the L1L1StandardBridge to finalize the withdrawal
expect(withdrawalCallToMessenger.args[1]).to.equal(
Factory__L1StandardBridge.interface.encodeFunctionData(
expect(
Fake__L2CrossDomainMessenger.sendMessage.getCall(0).args
).to.deep.equal([
DUMMY_L1_BRIDGE_ADDRESS,
getContractInterface('L1StandardBridge').encodeFunctionData(
'finalizeERC20Withdrawal',
[
DUMMY_L1TOKEN_ADDRESS,
DUMMY_L1_ERC20_ADDRESS,
Mock__L2Token.address,
await alice.getAddress(),
await bob.getAddress(),
alice.address,
bob.address,
withdrawAmount,
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', () => {
it('should not allow anyone but the L2 bridge to mint and burn', async () => {
expect(L2ERC20.connect(alice).mint(aliceAddress, 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).mint(alice.address, 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 () => {
const supportsERC165 = await L2ERC20.supportsInterface(0x01ffc9a7)
expect(supportsERC165).to.be.true
// ERC165
expect(await L2ERC20.supportsInterface(0x01ffc9a7)).to.be.true
const supportsL2TokenInterface = await L2ERC20.supportsInterface(
0x1d1d8b63
)
expect(supportsL2TokenInterface).to.be.true
// L2StandardERC20
expect(await L2ERC20.supportsInterface(0x1d1d8b63)).to.be.true
const badSupports = await L2ERC20.supportsInterface(0xffffffff)
expect(badSupports).to.be.false
expect(await L2ERC20.supportsInterface(0xffffffff)).to.be.false
})
})
})
/* External Imports */
import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers'
import { ContractFactory, Contract } from 'ethers'
import {
smock,
MockContractFactory,
MockContract,
} from '@defi-wonderland/smock'
/* Internal Imports */
import { expect } from '../../../setup'
import { predeploys, getContractInterface } from '../../../../src'
import { deploy } from '../../../helpers'
import { predeploys } from '../../../../src'
describe('L2StandardTokenFactory', () => {
let signer: Signer
let Factory__L1ERC20: MockContractFactory<ContractFactory>
let L1ERC20: MockContract<Contract>
let L2StandardTokenFactory: Contract
before(async () => {
;[signer] = await ethers.getSigners()
// deploy an ERC20 contract on L1
Factory__L1ERC20 = await smock.mock(
'@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20'
)
Factory__L1ERC20 = await smock.mock('ERC20')
L1ERC20 = await Factory__L1ERC20.deploy('L1ERC20', 'ERC')
L2StandardTokenFactory = await (
await ethers.getContractFactory('L2StandardTokenFactory')
).deploy()
L2StandardTokenFactory = await deploy('L2StandardTokenFactory')
})
describe('Standard token factory', () => {
......@@ -36,18 +27,18 @@ describe('L2StandardTokenFactory', () => {
'L2ERC20',
'ERC'
)
// Pull the token creation event from the receipt
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')
// Get the L2 token address from the emmited event and check it was created correctly
const l2TokenAddress = tokenCreatedEvent.args._l2Token
const l2Token = new Contract(
l2TokenAddress,
getContractInterface('L2StandardERC20'),
signer
// Get the L2 token address from the emitted event and check it was created correctly
const l2Token = await ethers.getContractAt(
'L2StandardERC20',
tokenCreatedEvent.args._l2Token
)
expect(await l2Token.l2Bridge()).to.equal(predeploys.L2StandardBridge)
......
/* External Imports */
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 { deploy } from '../../../helpers'
describe('OVM_ETH', () => {
let signer1: Signer
let signer2: Signer
let signer1: SignerWithAddress
let signer2: SignerWithAddress
before(async () => {
;[signer1, signer2] = await ethers.getSigners()
})
let Factory__OVM_ETH: ContractFactory
before(async () => {
Factory__OVM_ETH = await ethers.getContractFactory('OVM_ETH')
})
let OVM_ETH: Contract
beforeEach(async () => {
OVM_ETH = await Factory__OVM_ETH.deploy()
OVM_ETH = await deploy('OVM_ETH')
})
describe('transfer', () => {
it('should revert', async () => {
await expect(
OVM_ETH.transfer(await signer2.getAddress(), 100)
).to.be.revertedWith(
await expect(OVM_ETH.transfer(signer2.address, 100)).to.be.revertedWith(
'OVM_ETH: transfer is disabled pending further community discussion.'
)
})
......@@ -33,9 +28,7 @@ describe('OVM_ETH', () => {
describe('approve', () => {
it('should revert', async () => {
await expect(
OVM_ETH.approve(await signer2.getAddress(), 100)
).to.be.revertedWith(
await expect(OVM_ETH.approve(signer2.address, 100)).to.be.revertedWith(
'OVM_ETH: approve is disabled pending further community discussion.'
)
})
......@@ -44,11 +37,7 @@ describe('OVM_ETH', () => {
describe('transferFrom', () => {
it('should revert', async () => {
await expect(
OVM_ETH.transferFrom(
await signer1.getAddress(),
await signer2.getAddress(),
100
)
OVM_ETH.transferFrom(signer1.address, signer2.address, 100)
).to.be.revertedWith(
'OVM_ETH: transferFrom is disabled pending further community discussion.'
)
......@@ -58,7 +47,7 @@ describe('OVM_ETH', () => {
describe('increaseAllowance', () => {
it('should revert', async () => {
await expect(
OVM_ETH.increaseAllowance(await signer2.getAddress(), 100)
OVM_ETH.increaseAllowance(signer2.address, 100)
).to.be.revertedWith(
'OVM_ETH: increaseAllowance is disabled pending further community discussion.'
)
......@@ -68,7 +57,7 @@ describe('OVM_ETH', () => {
describe('decreaseAllowance', () => {
it('should revert', async () => {
await expect(
OVM_ETH.decreaseAllowance(await signer2.getAddress(), 100)
OVM_ETH.decreaseAllowance(signer2.address, 100)
).to.be.revertedWith(
'OVM_ETH: decreaseAllowance is disabled pending further community discussion.'
)
......
/* External Imports */
import { ethers } from 'hardhat'
import { ContractFactory, Contract, Signer } from 'ethers'
import { Contract } from 'ethers'
import { calculateL1GasUsed, calculateL1Fee } from '@eth-optimism/core-utils'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
import { expect } from '../../../setup'
import { deploy } from '../../../helpers'
describe('OVM_GasPriceOracle', () => {
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 () => {
Factory__OVM_GasPriceOracle = await ethers.getContractFactory(
'OVM_GasPriceOracle'
)
;[signer1, signer2] = await ethers.getSigners()
})
let OVM_GasPriceOracle: Contract
beforeEach(async () => {
OVM_GasPriceOracle = await Factory__OVM_GasPriceOracle.deploy(
await signer1.getAddress()
)
OVM_GasPriceOracle = await deploy('OVM_GasPriceOracle', {
signer: signer1,
args: [signer1.address],
})
await OVM_GasPriceOracle.setOverhead(2750)
await OVM_GasPriceOracle.setScalar(1500000)
......@@ -33,9 +29,7 @@ describe('OVM_GasPriceOracle', () => {
describe('owner', () => {
it('should have an owner', async () => {
expect(await OVM_GasPriceOracle.owner()).to.equal(
await signer1.getAddress()
)
expect(await OVM_GasPriceOracle.owner()).to.equal(signer1.address)
})
})
......@@ -46,12 +40,11 @@ describe('OVM_GasPriceOracle', () => {
})
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
.reverted
await expect(OVM_GasPriceOracle.setGasPrice(0)).to.not.be.reverted
})
it('should emit event', async () => {
await expect(OVM_GasPriceOracle.connect(signer1).setGasPrice(100))
await expect(OVM_GasPriceOracle.setGasPrice(100))
.to.emit(OVM_GasPriceOracle, 'GasPriceUpdated')
.withArgs(100)
})
......@@ -65,25 +58,18 @@ describe('OVM_GasPriceOracle', () => {
it('should change when setGasPrice is called', async () => {
const gasPrice = 1234
await OVM_GasPriceOracle.connect(signer1).setGasPrice(gasPrice)
await OVM_GasPriceOracle.setGasPrice(gasPrice)
expect(await OVM_GasPriceOracle.gasPrice()).to.equal(gasPrice)
})
it('is the 1st storage slot', async () => {
const gasPrice = 333433
const slot = 1
await OVM_GasPriceOracle.setGasPrice(333433)
// 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(
ethers.BigNumber.from(priceAtSlot)
ethers.BigNumber.from(
await signer1.provider.getStorageAt(OVM_GasPriceOracle.address, 1)
)
)
})
})
......@@ -95,12 +81,11 @@ describe('OVM_GasPriceOracle', () => {
})
it('should succeed if called by the owner', async () => {
await expect(OVM_GasPriceOracle.connect(signer1).setL1BaseFee(0)).to.not
.be.reverted
await expect(OVM_GasPriceOracle.setL1BaseFee(0)).to.not.be.reverted
})
it('should emit event', async () => {
await expect(OVM_GasPriceOracle.connect(signer1).setL1BaseFee(100))
await expect(OVM_GasPriceOracle.setL1BaseFee(100))
.to.emit(OVM_GasPriceOracle, 'L1BaseFeeUpdated')
.withArgs(100)
})
......@@ -113,24 +98,17 @@ describe('OVM_GasPriceOracle', () => {
it('should change when setL1BaseFee is called', async () => {
const baseFee = 1234
await OVM_GasPriceOracle.connect(signer1).setL1BaseFee(baseFee)
await OVM_GasPriceOracle.setL1BaseFee(baseFee)
expect(await OVM_GasPriceOracle.l1BaseFee()).to.equal(baseFee)
})
it('is the 2nd storage slot', async () => {
const baseFee = 12345
const slot = 2
// set the price
await OVM_GasPriceOracle.connect(signer1).setGasPrice(baseFee)
await OVM_GasPriceOracle.setGasPrice(12345)
// get the storage slot value
const priceAtSlot = await signer1.provider.getStorageAt(
OVM_GasPriceOracle.address,
slot
)
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', () => {
for (const input of inputs) {
it(`case: ${input}`, async () => {
const overhead = await OVM_GasPriceOracle.overhead()
const cost = await OVM_GasPriceOracle.getL1GasUsed(input)
const expected = calculateL1GasUsed(input, overhead)
expect(cost).to.deep.equal(expected)
expect(await OVM_GasPriceOracle.getL1GasUsed(input)).to.deep.equal(
calculateL1GasUsed(input, overhead)
)
})
}
})
......@@ -162,19 +141,16 @@ describe('OVM_GasPriceOracle', () => {
it(`case: ${input}`, async () => {
await OVM_GasPriceOracle.setGasPrice(1)
await OVM_GasPriceOracle.setL1BaseFee(1)
const decimals = await OVM_GasPriceOracle.decimals()
const overhead = await OVM_GasPriceOracle.overhead()
const scalar = await OVM_GasPriceOracle.scalar()
const l1BaseFee = await OVM_GasPriceOracle.l1BaseFee()
const l1Fee = await OVM_GasPriceOracle.getL1Fee(input)
const expected = calculateL1Fee(
input,
overhead,
l1BaseFee,
scalar,
decimals
expect(await OVM_GasPriceOracle.getL1Fee(input)).to.deep.equal(
calculateL1Fee(
input,
await OVM_GasPriceOracle.overhead(),
await OVM_GasPriceOracle.l1BaseFee(),
await OVM_GasPriceOracle.scalar(),
await OVM_GasPriceOracle.decimals()
)
)
expect(l1Fee).to.deep.equal(expected)
})
}
})
......@@ -186,12 +162,11 @@ describe('OVM_GasPriceOracle', () => {
})
it('should succeed if called by the owner', async () => {
await expect(OVM_GasPriceOracle.connect(signer1).setOverhead(0)).to.not.be
.reverted
await expect(OVM_GasPriceOracle.setOverhead(0)).to.not.be.reverted
})
it('should emit event', async () => {
await expect(OVM_GasPriceOracle.connect(signer1).setOverhead(100))
await expect(OVM_GasPriceOracle.setOverhead(100))
.to.emit(OVM_GasPriceOracle, 'OverheadUpdated')
.withArgs(100)
})
......@@ -204,24 +179,17 @@ describe('OVM_GasPriceOracle', () => {
it('should change when setOverhead is called', async () => {
const overhead = 6657
await OVM_GasPriceOracle.connect(signer1).setOverhead(overhead)
await OVM_GasPriceOracle.setOverhead(overhead)
expect(await OVM_GasPriceOracle.overhead()).to.equal(overhead)
})
it('is the 3rd storage slot', async () => {
const overhead = 119090
const slot = 3
await OVM_GasPriceOracle.setOverhead(119090)
// 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(
ethers.BigNumber.from(priceAtSlot)
ethers.BigNumber.from(
await signer1.provider.getStorageAt(OVM_GasPriceOracle.address, 3)
)
)
})
})
......@@ -233,12 +201,11 @@ describe('OVM_GasPriceOracle', () => {
})
it('should succeed if called by the owner', async () => {
await expect(OVM_GasPriceOracle.connect(signer1).setScalar(0)).to.not.be
.reverted
await expect(OVM_GasPriceOracle.setScalar(0)).to.not.be.reverted
})
it('should emit event', async () => {
await expect(OVM_GasPriceOracle.connect(signer1).setScalar(100))
await expect(OVM_GasPriceOracle.setScalar(100))
.to.emit(OVM_GasPriceOracle, 'ScalarUpdated')
.withArgs(100)
})
......@@ -251,39 +218,27 @@ describe('OVM_GasPriceOracle', () => {
it('should change when setScalar is called', async () => {
const scalar = 9999
await OVM_GasPriceOracle.connect(signer1).setScalar(scalar)
await OVM_GasPriceOracle.setScalar(scalar)
expect(await OVM_GasPriceOracle.scalar()).to.equal(scalar)
})
it('is the 4rd storage slot', async () => {
const overhead = 111111
const slot = 4
// set the price
await OVM_GasPriceOracle.connect(signer1).setScalar(overhead)
await OVM_GasPriceOracle.setScalar(111111)
// get the storage slot value
const priceAtSlot = await signer1.provider.getStorageAt(
OVM_GasPriceOracle.address,
slot
)
expect(await OVM_GasPriceOracle.scalar()).to.equal(
ethers.BigNumber.from(priceAtSlot)
ethers.BigNumber.from(
await signer1.provider.getStorageAt(OVM_GasPriceOracle.address, 4)
)
)
})
})
describe('decimals', () => {
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(
ethers.BigNumber.from(priceAtSlot)
ethers.BigNumber.from(
await signer1.provider.getStorageAt(OVM_GasPriceOracle.address, 5)
)
)
})
})
......
......@@ -28,7 +28,7 @@ describe.skip('OVM_L2ToL1MessagePasser', () => {
let Fake__OVM_ExecutionManager: FakeContract
before(async () => {
Fake__OVM_ExecutionManager = await smock.fake<Contract>(
await ethers.getContractFactory('OVM_ExecutionManager')
'OVM_ExecutionManager'
)
})
......
......@@ -39,9 +39,7 @@ describe('Lib_MerkleTree', () => {
await ethers.getContractFactory('TestLib_MerkleTree')
).deploy()
Fake__LibMerkleTree = await smock.fake(
await ethers.getContractFactory('TestLib_MerkleTree')
)
Fake__LibMerkleTree = await smock.fake('TestLib_MerkleTree')
})
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 './deploy'
export * from './impersonation'
......@@ -76,8 +76,8 @@ components:
type: string
queueOrigin:
type: string
queueIndex: number
type: string
queueIndex:
type: number
decoded:
type: object
$ref: '#/components/schemas/DecodedSequencerBatchTransaction'
......
......@@ -18,6 +18,7 @@ type HealthcheckMetrics = {
isCurrentlyDiverged: Gauge
referenceHeight: Gauge
targetHeight: Gauge
heightDifference: Gauge
targetConnectionFailures: Counter
referenceConnectionFailures: Counter
}
......@@ -66,6 +67,10 @@ export class HealthcheckService extends BaseServiceV2<
type: Gauge,
desc: 'Block height of the target client',
},
heightDifference: {
type: Gauge,
desc: 'Difference in block heights between the two clients',
},
targetConnectionFailures: {
type: Counter,
desc: 'Number of connection failures to the target client',
......@@ -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.
this.metrics.targetHeight.set(targetLatest.number)
this.metrics.referenceHeight.set(referenceLatest.number)
this.metrics.heightDifference.set(heightDiff)
this.logger.info(`latest block heights`, {
targetHeight: targetLatest.number,
referenceHeight: referenceLatest.number,
heightDifference: referenceLatest.number - targetLatest.number,
heightDifference: heightDiff,
minBlockNumber: minBlock,
})
const referenceCorresponding =
await this.options.referenceRpcProvider.getBlock(targetLatest.number)
const reference = await this.options.referenceRpcProvider.getBlock(minBlock)
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.logger.info(`reference client does not have block yet`, {
blockNumber: targetLatest.number,
this.logger.info(`target block was not found`, {
blockNumber: target.number,
})
return
}
......@@ -134,9 +152,11 @@ export class HealthcheckService extends BaseServiceV2<
// 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
// 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`, {
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
......@@ -144,7 +164,7 @@ export class HealthcheckService extends BaseServiceV2<
this.logger.info(`beginning binary search to find first mismatched block`)
let start = 0
let end = targetLatest.number
let end = target.number
while (start !== end) {
const mid = Math.floor((start + end) / 2)
this.logger.info(`checking block`, { blockNumber: mid })
......@@ -171,11 +191,11 @@ export class HealthcheckService extends BaseServiceV2<
}
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.
this.metrics.lastMatchingStateRootHeight.set(targetLatest.number)
this.metrics.lastMatchingStateRootHeight.set(target.number)
this.metrics.isCurrentlyDiverged.set(0)
}
}
......
/* 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 {
TransactionRequest,
TransactionResponse,
......@@ -350,7 +357,7 @@ export class StandardBridgeAdapter implements IBridgeAdapter {
l2Token: AddressLike,
amount: NumberLike,
opts?: {
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber> => {
return this.messenger.l1Provider.estimateGas(
......@@ -365,7 +372,7 @@ export class StandardBridgeAdapter implements IBridgeAdapter {
opts?: {
recipient?: AddressLike
l2GasLimit?: NumberLike
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber> => {
return this.messenger.l1Provider.estimateGas(
......@@ -379,7 +386,7 @@ export class StandardBridgeAdapter implements IBridgeAdapter {
amount: NumberLike,
opts?: {
recipient?: AddressLike
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber> => {
return this.messenger.l2Provider.estimateGas(
......
......@@ -7,7 +7,7 @@ import {
TransactionRequest,
} from '@ethersproject/abstract-provider'
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 { predeploys } from '@eth-optimism/contracts'
......@@ -1128,7 +1128,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
message: CrossChainMessageRequest,
opts?: {
l2GasLimit?: NumberLike
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber> => {
const tx = await this.populateTransaction.sendMessage(message, opts)
......@@ -1143,7 +1143,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
message: MessageLike,
messageGasLimit: NumberLike,
opts?: {
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber> => {
return this.l1Provider.estimateGas(
......@@ -1158,7 +1158,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
finalizeMessage: async (
message: MessageLike,
opts?: {
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber> => {
return this.l1Provider.estimateGas(
......@@ -1171,7 +1171,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
opts?: {
recipient?: AddressLike
l2GasLimit?: NumberLike
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber> => {
return this.l1Provider.estimateGas(
......@@ -1183,7 +1183,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
amount: NumberLike,
opts?: {
recipient?: AddressLike
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber> => {
return this.l2Provider.estimateGas(
......@@ -1196,7 +1196,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
l2Token: AddressLike,
amount: NumberLike,
opts?: {
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber> => {
return this.l1Provider.estimateGas(
......@@ -1216,7 +1216,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
opts?: {
recipient?: AddressLike
l2GasLimit?: NumberLike
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber> => {
return this.l1Provider.estimateGas(
......@@ -1235,7 +1235,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
amount: NumberLike,
opts?: {
recipient?: AddressLike
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber> => {
return this.l2Provider.estimateGas(
......
import { Contract, Overrides, Signer, BigNumber } from 'ethers'
import { Contract, Overrides, Signer, BigNumber, CallOverrides } from 'ethers'
import {
TransactionRequest,
TransactionResponse,
......@@ -250,7 +250,7 @@ export interface IBridgeAdapter {
l2Token: AddressLike,
amount: NumberLike,
opts?: {
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber>
......@@ -273,7 +273,7 @@ export interface IBridgeAdapter {
opts?: {
recipient?: AddressLike
l2GasLimit?: NumberLike
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber>
......@@ -294,7 +294,7 @@ export interface IBridgeAdapter {
amount: NumberLike,
opts?: {
recipient?: AddressLike
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber>
}
......
import { Event, BigNumber, Overrides } from 'ethers'
import { Event, BigNumber, Overrides, CallOverrides } from 'ethers'
import {
Provider,
BlockTag,
......@@ -697,7 +697,7 @@ export interface ICrossChainMessenger {
message: CrossChainMessageRequest,
opts?: {
l2GasLimit?: NumberLike
overrides?: Overrides
overrides?: CallOverrides
}
) => Promise<BigNumber>
......@@ -714,7 +714,7 @@ export interface ICrossChainMessenger {
message: MessageLike,
messageGasLimit: NumberLike,
opts?: {
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber>
......@@ -729,7 +729,7 @@ export interface ICrossChainMessenger {
finalizeMessage(
message: MessageLike,
opts?: {
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber>
......@@ -748,7 +748,7 @@ export interface ICrossChainMessenger {
l2Token: AddressLike,
amount: NumberLike,
opts?: {
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber>
......@@ -767,7 +767,7 @@ export interface ICrossChainMessenger {
opts?: {
recipient?: AddressLike
l2GasLimit?: NumberLike
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber>
......@@ -784,7 +784,7 @@ export interface ICrossChainMessenger {
amount: NumberLike,
opts?: {
recipient?: AddressLike
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber>
......@@ -807,7 +807,7 @@ export interface ICrossChainMessenger {
opts?: {
recipient?: AddressLike
l2GasLimit?: NumberLike
overrides?: Overrides
overrides?: CallOverrides
}
): Promise<BigNumber>
......@@ -828,7 +828,7 @@ export interface ICrossChainMessenger {
amount: NumberLike,
opts?: {
recipient?: AddressLike
overrides?: Overrides
overrides?: CallOverrides
}
): 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