Commit 1d72344f authored by Ethen Pociask's avatar Ethen Pociask

Merge branch 'develop' of https://github.com/epociask/optimism into indexer.client

parents d2a80ce0 f94019c1
......@@ -875,14 +875,14 @@ jobs:
- run:
name: generate L1 state
command: make devnet-allocs
- store_test_results:
path: /test-results
- run:
name: Test
command: |
mkdir -p /test-results
DB_USER=postgres gotestsum --format=standard-verbose --junitfile /test-results/tests.xml -- -parallel=4 ./...
DB_USER=postgres gotestsum --format=standard-verbose --junitfile=/test-results/indexer_tests.xml -- -parallel=4 ./...
working_directory: indexer
- store_test_results:
path: /test-results
- run:
name: Build
command: make indexer
......
......@@ -86,6 +86,9 @@ nuke: clean devnet-clean
.PHONY: nuke
devnet-up:
@if ! [ -x "$(command -v geth)" ]; then \
make install-geth; \
fi
@if [ ! -e op-program/bin ]; then \
make cannon-prestate; \
fi
......
# @eth-optimism/endpoint-monitor
## 1.0.3
### Patch Changes
- [#7450](https://github.com/ethereum-optimism/optimism/pull/7450) [`ac90e16a7`](https://github.com/ethereum-optimism/optimism/commit/ac90e16a7f85c4f73661ae6023135c3d00421c1e) Thanks [@roninjin10](https://github.com/roninjin10)! - Updated dev dependencies related to testing that is causing audit tooling to report failures
## 1.0.2
### Patch Changes
......
{
"name": "@eth-optimism/endpoint-monitor",
"version": "1.0.2",
"version": "1.0.3",
"private": true,
"dependencies": {}
}
......@@ -36,7 +36,7 @@ require (
github.com/onsi/gomega v1.27.10
github.com/pkg/errors v0.9.1
github.com/pkg/profile v1.7.0
github.com/prometheus/client_golang v1.14.0
github.com/prometheus/client_golang v1.17.0
github.com/stretchr/testify v1.8.4
github.com/urfave/cli/v2 v2.25.7
golang.org/x/crypto v0.13.0
......@@ -167,16 +167,16 @@ require (
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.3.3 // indirect
github.com/quic-go/quic-go v0.38.1 // indirect
github.com/quic-go/webtransport-go v0.5.3 // indirect
github.com/raulk/go-watchdog v1.3.0 // indirect
github.com/rivo/uniseg v0.4.3 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/rs/cors v1.9.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
......@@ -200,7 +200,7 @@ require (
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.13.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
......
......@@ -679,18 +679,18 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM=
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-20 v0.3.3 h1:17/glZSLI9P9fDAeyCHBFSWSqJcwx1byhLwP5eUIDCM=
......@@ -707,8 +707,9 @@ github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE=
github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
......@@ -1101,8 +1102,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
......
# @eth-optimism/indexer-api
## 0.0.4
### Patch Changes
- [#7450](https://github.com/ethereum-optimism/optimism/pull/7450) [`ac90e16a7`](https://github.com/ethereum-optimism/optimism/commit/ac90e16a7f85c4f73661ae6023135c3d00421c1e) Thanks [@roninjin10](https://github.com/roninjin10)! - Updated dev dependencies related to testing that is causing audit tooling to report failures
{
"name": "@eth-optimism/indexer-api",
"version": "0.0.3",
"version": "0.0.4",
"description": "[Optimism] typescript types for the indexer service",
"main": "indexer.cjs",
"module": "indexer.js",
......
......@@ -25,35 +25,3 @@ func Clamp(start, end *big.Int, size uint64) *big.Int {
func Matcher(num int64) func(*big.Int) bool {
return func(bi *big.Int) bool { return bi.Int64() == num }
}
type Range struct {
Start *big.Int
End *big.Int
}
// Grouped will return a slice of inclusive ranges from (start, end),
// capped to the supplied size from `(start, end)`.
func Grouped(start, end *big.Int, size uint64) []Range {
if end.Cmp(start) < 0 || size == 0 {
return nil
}
bigMaxDiff := big.NewInt(int64(size - 1))
groups := []Range{}
for start.Cmp(end) <= 0 {
diff := new(big.Int).Sub(end, start)
switch {
case diff.Uint64()+1 <= size:
// re-use allocated diff as the next start
groups = append(groups, Range{start, end})
start = diff.Add(end, One)
default:
// re-use allocated diff as the next start
end := new(big.Int).Add(start, bigMaxDiff)
groups = append(groups, Range{start, end})
start = diff.Add(end, One)
}
}
return groups
}
......@@ -27,46 +27,3 @@ func TestClamp(t *testing.T) {
require.False(t, end == result)
require.Equal(t, uint64(5), result.Uint64())
}
func TestGrouped(t *testing.T) {
// base cases
require.Nil(t, Grouped(One, Zero, 1))
require.Nil(t, Grouped(Zero, One, 0))
// Same Start/End
group := Grouped(One, One, 1)
require.Len(t, group, 1)
require.Equal(t, One, group[0].Start)
require.Equal(t, One, group[0].End)
Three, Five := big.NewInt(3), big.NewInt(5)
// One at a time
group = Grouped(One, Three, 1)
require.Equal(t, One, group[0].End)
require.Equal(t, int64(1), group[0].End.Int64())
require.Equal(t, int64(2), group[1].Start.Int64())
require.Equal(t, int64(2), group[1].End.Int64())
require.Equal(t, int64(3), group[2].Start.Int64())
require.Equal(t, int64(3), group[2].End.Int64())
// Split groups
group = Grouped(One, Five, 3)
require.Len(t, group, 2)
require.Equal(t, One, group[0].Start)
require.Equal(t, int64(3), group[0].End.Int64())
require.Equal(t, int64(4), group[1].Start.Int64())
require.Equal(t, Five, group[1].End)
// Encompasses the range
group = Grouped(One, Five, 5)
require.Len(t, group, 1)
require.Equal(t, One, group[0].Start, Zero)
require.Equal(t, Five, group[0].End)
// Size larger than the entire range
group = Grouped(One, Five, 100)
require.Len(t, group, 1)
require.Equal(t, One, group[0].Start, Zero)
require.Equal(t, Five, group[0].End)
}
......@@ -2,8 +2,10 @@ package database
import (
"errors"
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/indexer/bigint"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
......@@ -51,7 +53,7 @@ type BlocksView interface {
L2BlockHeaderWithFilter(BlockHeader) (*L2BlockHeader, error)
L2LatestBlockHeader() (*L2BlockHeader, error)
LatestEpoch() (*Epoch, error)
LatestObservedEpoch(*big.Int, uint64) (*Epoch, error)
}
type BlocksDB interface {
......@@ -155,36 +157,74 @@ type Epoch struct {
L2BlockHeader L2BlockHeader `gorm:"embedded"`
}
// LatestEpoch return the latest epoch, seen on L1 & L2. In other words
// this returns the latest indexed L1 block that has a corresponding
// indexed L2 block with a matching L1Origin (equal timestamps).
// LatestObservedEpoch return the marker for latest epoch, observed on L1 & L2, within
// the specified bounds. In other words this returns the latest indexed L1 block that has
// a corresponding indexed L2 block with a matching L1Origin (equal timestamps).
//
// If `fromL1Height` (inclusive) is not specified, the search will start from genesis and
// continue all the way to latest indexed heights if `maxL1Range == 0`.
//
// For more, see the protocol spec:
// - https://github.com/ethereum-optimism/optimism/blob/develop/specs/derivation.md
func (db *blocksDB) LatestEpoch() (*Epoch, error) {
latestL1Header, err := db.L1LatestBlockHeader()
if err != nil {
return nil, err
} else if latestL1Header == nil {
func (db *blocksDB) LatestObservedEpoch(fromL1Height *big.Int, maxL1Range uint64) (*Epoch, error) {
// We use timestamps since that translates to both L1 & L2
var fromTimestamp, toTimestamp uint64
if fromL1Height == nil {
fromL1Height = bigint.Zero
}
// Lower Bound (the default `fromTimestamp = 0` suffices genesis representation)
if fromL1Height.BitLen() > 0 {
var header L1BlockHeader
result := db.gorm.Where("number = ?", fromL1Height).Take(&header)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
fromTimestamp = header.Timestamp
}
latestL2Header, err := db.L2LatestBlockHeader()
if err != nil {
return nil, err
} else if latestL2Header == nil {
// Upper Bound (lowest timestamp indexed between L1/L2 bounded by `maxL1Range`)
{
l1QueryFilter := fmt.Sprintf("timestamp >= %d", fromTimestamp)
if maxL1Range > 0 {
maxHeight := new(big.Int).Add(fromL1Height, big.NewInt(int64(maxL1Range)))
l1QueryFilter = fmt.Sprintf("%s AND number <= %d", l1QueryFilter, maxHeight)
}
var l1Header L1BlockHeader
result := db.gorm.Where(l1QueryFilter).Order("timestamp DESC").Take(&l1Header)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
toTimestamp = l1Header.Timestamp
minTime := latestL1Header.Timestamp
if latestL2Header.Timestamp < minTime {
minTime = latestL2Header.Timestamp
var l2Header L2BlockHeader
result = db.gorm.Where("timestamp <= ?", toTimestamp).Order("timestamp DESC").Take(&l2Header)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, result.Error
}
if l2Header.Timestamp < toTimestamp {
toTimestamp = l2Header.Timestamp
}
}
// This is a faster query than doing an INNER JOIN between l1_block_headers and l2_block_headers
// which requires a full table scan to compute the resulting table.
l1Query := db.gorm.Table("l1_block_headers").Where("timestamp <= ?", minTime)
l2Query := db.gorm.Table("l2_block_headers").Where("timestamp <= ?", minTime)
// Search for the latest indexed epoch within range. This is a faster query than doing an INNER JOIN between
// l1_block_headers and l2_block_headers which requires a full table scan to compute the resulting table.
l1Query := db.gorm.Table("l1_block_headers").Where("timestamp >= ? AND timestamp <= ?", fromTimestamp, toTimestamp)
l2Query := db.gorm.Table("l2_block_headers").Where("timestamp >= ? AND timestamp <= ?", fromTimestamp, toTimestamp)
query := db.gorm.Raw(`SELECT * FROM (?) AS l1_block_headers, (?) AS l2_block_headers
WHERE l1_block_headers.timestamp = l2_block_headers.timestamp
ORDER BY l2_block_headers.number DESC LIMIT 1`, l1Query, l2Query)
......
package database
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/mock"
......@@ -51,7 +53,7 @@ func (m *MockBlocksView) L2LatestBlockHeader() (*L2BlockHeader, error) {
return args.Get(0).(*L2BlockHeader), args.Error(1)
}
func (m *MockBlocksView) LatestEpoch() (*Epoch, error) {
func (m *MockBlocksView) LatestObservedEpoch(*big.Int, uint64) (*Epoch, error) {
args := m.Called()
return args.Get(0).(*Epoch), args.Error(1)
}
......
......@@ -54,21 +54,24 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite {
dbUser := os.Getenv("DB_USER")
dbName := setupTestDatabase(t)
// Discard the Global Logger as each component
// has its own configured logger
// Rollup System Configuration. Unless specified,
// omit logs emitted by the various components. Maybe
// we can eventually dump these logs to a temp file
log.Root().SetHandler(log.DiscardHandler())
// Rollup System Configuration and Start
opCfg := op_e2e.DefaultSystemConfig(t)
opCfg.DeployConfig.FinalizationPeriodSeconds = 2
if len(os.Getenv("ENABLE_ROLLUP_LOGS")) == 0 {
t.Log("set env 'ENABLE_ROLLUP_LOGS' to show rollup logs")
for name, logger := range opCfg.Loggers {
t.Logf("discarding logs for %s", name)
logger.SetHandler(log.DiscardHandler())
}
}
// Rollup Start
opSys, err := opCfg.Start(t)
require.NoError(t, err)
t.Cleanup(func() { opSys.Close() })
// E2E tests can run on the order of magnitude of minutes. Once
// the system is running, mark this test for Parallel execution
t.Parallel()
// Indexer Configuration and Start
indexerCfg := config.Config{
DB: config.DBConfig{
......@@ -99,8 +102,14 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite {
MetricsServer: config.ServerConfig{Host: "127.0.0.1", Port: 0},
}
// Emit debug log levels
db, err := database.NewDB(testlog.Logger(t, log.LvlDebug).New("role", "db"), indexerCfg.DB)
// E2E tests can run on the order of magnitude of minutes. Once
// the system is running, mark this test for Parallel execution
t.Parallel()
// provide a DB for the unit test. disable logging
silentLog := testlog.Logger(t, log.LvlInfo)
silentLog.SetHandler(log.DiscardHandler())
db, err := database.NewDB(silentLog, indexerCfg.DB)
require.NoError(t, err)
t.Cleanup(func() { db.Close() })
......@@ -191,7 +200,6 @@ func setupTestDatabase(t *testing.T) string {
User: user,
Password: "",
}
// NewDB will create the database schema
silentLog := log.New()
silentLog.SetHandler(log.DiscardHandler())
......
......@@ -71,11 +71,11 @@ func (etl *ETL) Start(ctx context.Context) error {
etl.log.Error("error querying for headers", "err", err)
} else if len(newHeaders) == 0 {
etl.log.Warn("no new headers. processor unexpectedly at head...")
}
} else {
headers = newHeaders
etl.metrics.RecordBatchHeaders(len(newHeaders))
}
}
// only clear the reference if we were able to process this batch
err := etl.processBatch(headers)
......@@ -107,7 +107,7 @@ func (etl *ETL) processBatch(headers []types.Header) error {
headersWithLog := make(map[common.Hash]bool, len(headers))
logs, err := etl.EthClient.FilterLogs(ethereum.FilterQuery{FromBlock: firstHeader.Number, ToBlock: lastHeader.Number, Addresses: etl.contracts})
if err != nil {
batchLog.Info("unable to extract logs", "err", err)
batchLog.Info("failed to extract logs", "err", err)
return err
}
if len(logs) > 0 {
......@@ -118,7 +118,8 @@ func (etl *ETL) processBatch(headers []types.Header) error {
log := logs[i]
if _, ok := headerMap[log.BlockHash]; !ok {
// NOTE. Definitely an error state if the none of the headers were re-orged out in between
// the blocks and logs retrieval operations. However, we need to gracefully handle reorgs
// the blocks and logs retrieval operations. Unlikely as long as the confirmation depth has
// been appropriately set or when we get to natively handling reorgs.
batchLog.Error("log found with block hash not in the batch", "block_hash", logs[i].BlockHash, "log_index", logs[i].Index)
return errors.New("parsed log with a block hash not in the batch")
}
......
......@@ -16,7 +16,6 @@ type Metricer interface {
RecordInterval() (done func(err error))
// Batch Extraction
RecordBatchFailure()
RecordBatchLatestHeight(height *big.Int)
RecordBatchHeaders(size int)
RecordBatchLog(contractAddress common.Address)
......@@ -108,17 +107,13 @@ func (m *etlMetrics) RecordInterval() func(error) {
timer := prometheus.NewTimer(m.intervalDuration)
return func(err error) {
if err != nil {
m.RecordBatchFailure()
m.batchFailures.Inc()
}
timer.ObserveDuration()
}
}
func (m *etlMetrics) RecordBatchFailure() {
m.batchFailures.Inc()
}
func (m *etlMetrics) RecordBatchLatestHeight(height *big.Int) {
m.batchLatestHeight.Set(float64(height.Uint64()))
}
......
......@@ -20,6 +20,7 @@ import (
"github.com/ethereum-optimism/optimism/indexer/etl"
"github.com/ethereum-optimism/optimism/indexer/node"
"github.com/ethereum-optimism/optimism/indexer/processors"
"github.com/ethereum-optimism/optimism/indexer/processors/bridge"
"github.com/ethereum-optimism/optimism/op-service/httputil"
"github.com/ethereum-optimism/optimism/op-service/metrics"
)
......@@ -82,7 +83,7 @@ func NewIndexer(
}
// Bridge
bridgeProcessor, err := processors.NewBridgeProcessor(log, db, l1Etl, chainConfig)
bridgeProcessor, err := processors.NewBridgeProcessor(log, db, bridge.NewMetrics(metricsRegistry), l1Etl, chainConfig)
if err != nil {
return nil, err
}
......
......@@ -7,6 +7,7 @@ import (
"github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/rpc"
"github.com/prometheus/client_golang/prometheus"
)
......
......@@ -18,6 +18,8 @@ import (
type BridgeProcessor struct {
log log.Logger
db *database.DB
metrics bridge.Metricer
l1Etl *etl.L1ETL
chainConfig config.ChainConfig
......@@ -25,7 +27,7 @@ type BridgeProcessor struct {
LatestL2Header *types.Header
}
func NewBridgeProcessor(log log.Logger, db *database.DB, l1Etl *etl.L1ETL, chainConfig config.ChainConfig) (*BridgeProcessor, error) {
func NewBridgeProcessor(log log.Logger, db *database.DB, metrics bridge.Metricer, l1Etl *etl.L1ETL, chainConfig config.ChainConfig) (*BridgeProcessor, error) {
log = log.New("processor", "bridge")
latestL1Header, err := db.BridgeTransactions.L1LatestBlockHeader()
......@@ -45,28 +47,24 @@ func NewBridgeProcessor(log log.Logger, db *database.DB, l1Etl *etl.L1ETL, chain
if latestL1Header != nil {
l1Height = latestL1Header.Number
l1Header = latestL1Header.RLPHeader.Header()
metrics.RecordLatestIndexedL1Height(l1Height)
}
if latestL2Header != nil {
l2Height = latestL2Header.Number
l2Header = latestL2Header.RLPHeader.Header()
metrics.RecordLatestIndexedL2Height(l2Height)
}
log.Info("detected latest indexed bridge state", "l1_block_number", l1Height, "l2_block_number", l2Height)
}
return &BridgeProcessor{log, db, l1Etl, chainConfig, l1Header, l2Header}, nil
return &BridgeProcessor{log, db, metrics, l1Etl, chainConfig, l1Header, l2Header}, nil
}
func (b *BridgeProcessor) Start(ctx context.Context) error {
done := ctx.Done()
// In order to ensure all seen bridge finalization events correspond with seen
// bridge initiated events, we establish a shared marker between L1 and L2 when
// processing events.
//
// As L1 and L2 blocks are indexed, the highest indexed L2 block starting a new
// sequencing epoch and corresponding L1 origin that has also been indexed
// serves as this shared marker.
// Fire off independently on startup to check for
// new data or if we've indexed new L1 data.
l1EtlUpdates := b.l1Etl.Notify()
startup := make(chan interface{}, 1)
startup <- nil
......@@ -78,45 +76,66 @@ func (b *BridgeProcessor) Start(ctx context.Context) error {
b.log.Info("stopping bridge processor")
return nil
// Fire off independently on startup to check for any
// new data or if we've indexed new L1 data.
// Tickers
case <-startup:
case <-l1EtlUpdates:
}
latestEpoch, err := b.db.Blocks.LatestEpoch()
done := b.metrics.RecordInterval()
done(b.run())
}
}
// Runs the processing loop. In order to ensure all seen bridge finalization events
// can be correlated with bridge initiated events, we establish a shared marker between
// L1 and L2 when processing events. The lastest shared indexed time (epochs) between
// L1 and L2 serves as this shared marker.
func (b *BridgeProcessor) run() error {
// In the event where we have a large number of un-observed epochs, we cap the search
// of epochs by 10k. If this turns out to be a bottleneck, we can parallelize the processing
// of epochs to significantly speed up sync times.
maxEpochRange := uint64(10_000)
var lastEpoch *big.Int
if b.LatestL1Header != nil {
lastEpoch = b.LatestL1Header.Number
}
latestEpoch, err := b.db.Blocks.LatestObservedEpoch(lastEpoch, maxEpochRange)
if err != nil {
return err
} else if latestEpoch == nil {
if b.LatestL1Header != nil || b.LatestL2Header != nil {
// Once we have some indexed state `latestEpoch` can never return nil
b.log.Error("bridge events indexed, but no indexed epoch returned", "latest_bridge_l1_block_number", b.LatestL1Header.Number)
return errors.New("bridge events indexed, but no indexed epoch returned")
// Once we have some indexed state `latestEpoch != nil` as `LatestObservedEpoch` is inclusive in its search with the last provided epoch.
b.log.Error("bridge events indexed, but no observed epoch returned", "latest_bridge_l1_block_number", b.LatestL1Header.Number)
return errors.New("bridge events indexed, but no observed epoch returned")
}
b.log.Warn("no observed epochs available. waiting...")
return nil
}
b.log.Warn("no indexed epochs available. waiting...")
continue
if b.LatestL1Header != nil && latestEpoch.L1BlockHeader.Hash == b.LatestL1Header.Hash() {
b.log.Warn("all available epochs indexed", "latest_bridge_l1_block_number", b.LatestL1Header.Number)
return nil
}
// Integrity Checks
if b.LatestL1Header != nil && latestEpoch.L1BlockHeader.Hash == b.LatestL1Header.Hash() {
b.log.Warn("all available epochs indexed", "latest_bridge_l1_block_number", b.LatestL1Header.Number)
continue
genesisL1Height := big.NewInt(int64(b.chainConfig.L1StartingHeight))
if latestEpoch.L1BlockHeader.Number.Cmp(genesisL1Height) < 0 {
b.log.Error("L1 epoch less than starting L1 height observed", "l1_starting_number", genesisL1Height, "latest_epoch_number", latestEpoch.L1BlockHeader.Number)
return errors.New("L1 epoch less than starting L1 height observed")
}
if b.LatestL1Header != nil && latestEpoch.L1BlockHeader.Number.Cmp(b.LatestL1Header.Number) <= 0 {
b.log.Error("decreasing l1 block height observed", "latest_bridge_l1_block_number", b.LatestL1Header.Number, "latest_epoch_number", latestEpoch.L1BlockHeader.Number)
return errors.New("decreasing l1 block heght observed")
b.log.Error("non-increasing l1 block height observed", "latest_bridge_l1_block_number", b.LatestL1Header.Number, "latest_epoch_l1_block_number", latestEpoch.L1BlockHeader.Number)
return errors.New("non-increasing l1 block heght observed")
}
if b.LatestL2Header != nil && latestEpoch.L2BlockHeader.Number.Cmp(b.LatestL2Header.Number) <= 0 {
b.log.Error("decreasing l2 block height observed", "latest_bridge_l2_block_number", b.LatestL2Header.Number, "latest_epoch_number", latestEpoch.L2BlockHeader.Number)
return errors.New("decreasing l2 block heght observed")
b.log.Error("non-increasing l2 block height observed", "latest_bridge_l2_block_number", b.LatestL2Header.Number, "latest_epoch_l2_block_number", latestEpoch.L2BlockHeader.Number)
return errors.New("non-increasing l2 block heght observed")
}
// Process Bridge Events
toL1Height, toL2Height := latestEpoch.L1BlockHeader.Number, latestEpoch.L2BlockHeader.Number
fromL1Height, fromL2Height := big.NewInt(int64(b.chainConfig.L1StartingHeight)), bigint.Zero
fromL1Height, fromL2Height := genesisL1Height, bigint.Zero
if b.LatestL1Header != nil {
fromL1Height = new(big.Int).Add(b.LatestL1Header.Number, bigint.One)
}
......@@ -128,60 +147,41 @@ func (b *BridgeProcessor) Start(ctx context.Context) error {
l2BedrockStartingHeight := big.NewInt(int64(b.chainConfig.L2BedrockStartingHeight))
batchLog := b.log.New("epoch_start_number", fromL1Height, "epoch_end_number", toL1Height)
batchLog.Info("unobserved epochs")
err = b.db.Transaction(func(tx *database.DB) error {
// In the event where we have a large number of un-observed blocks, group the block range
// on the order of 10k blocks at a time. If this turns out to be a bottleneck, we can
// parallelize these operations
maxBlockRange := uint64(10_000)
batchLog.Info("unobserved epochs", "latest_l1_block_number", fromL1Height, "latest_l2_block_number", fromL2Height)
if err := b.db.Transaction(func(tx *database.DB) error {
l1BridgeLog := b.log.New("bridge", "l1")
l2BridgeLog := b.log.New("bridge", "l2")
// FOR OP-MAINNET, OP-GOERLI ONLY! Specially handle the existence of pre-bedrock blocks
if l1BedrockStartingHeight.Cmp(fromL1Height) > 0 {
l1BridgeLog := l1BridgeLog.New("mode", "legacy")
l2BridgeLog := l2BridgeLog.New("mode", "legacy")
legacyFromL1Height, legacyToL1Height := fromL1Height, toL1Height
legacyFromL2Height, legacyToL2Height := fromL2Height, toL2Height
if l1BedrockStartingHeight.Cmp(toL1Height) <= 0 {
legacyToL1Height = new(big.Int).Sub(l1BedrockStartingHeight, big.NewInt(1))
legacyToL2Height = new(big.Int).Sub(l2BedrockStartingHeight, big.NewInt(1))
legacyToL1Height = new(big.Int).Sub(l1BedrockStartingHeight, bigint.One)
legacyToL2Height = new(big.Int).Sub(l2BedrockStartingHeight, bigint.One)
}
l1BridgeLog = l1BridgeLog.New("mode", "legacy", "from_l1_block_number", legacyFromL1Height, "to_l1_block_number", legacyToL1Height)
l1BridgeLog.Info("scanning for bridge events")
l2BridgeLog = l2BridgeLog.New("mode", "legacy", "from_l2_block_number", legacyFromL2Height, "to_l2_block_number", legacyToL2Height)
l2BridgeLog.Info("scanning for bridge events")
// First, find all possible initiated bridge events
l1BlockGroups := bigint.Grouped(legacyFromL1Height, legacyToL1Height, maxBlockRange)
l2BlockGroups := bigint.Grouped(legacyFromL2Height, legacyToL2Height, maxBlockRange)
for _, group := range l1BlockGroups {
log := l1BridgeLog.New("from_l1_block_number", group.Start, "to_l1_block_number", group.End)
log.Info("scanning for initiated bridge events")
if err := bridge.LegacyL1ProcessInitiatedBridgeEvents(log, tx, b.chainConfig.L1Contracts, group.Start, group.End); err != nil {
if err := bridge.LegacyL1ProcessInitiatedBridgeEvents(l1BridgeLog, tx, b.metrics, b.chainConfig.L1Contracts, legacyFromL1Height, legacyToL1Height); err != nil {
return err
}
}
for _, group := range l2BlockGroups {
log := l2BridgeLog.New("from_l2_block_number", group.Start, "to_l2_block_number", group.End)
log.Info("scanning for initiated bridge events")
if err := bridge.LegacyL2ProcessInitiatedBridgeEvents(log, tx, b.chainConfig.L2Contracts, group.Start, group.End); err != nil {
if err := bridge.LegacyL2ProcessInitiatedBridgeEvents(l2BridgeLog, tx, b.metrics, b.chainConfig.L2Contracts, legacyFromL2Height, legacyToL2Height); err != nil {
return err
}
}
// Now that all initiated events have been indexed, it is ensured that all finalization can find their counterpart.
for _, group := range l1BlockGroups {
log := l1BridgeLog.New("from_l1_block_number", group.Start, "to_l1_block_number", group.End)
log.Info("scanning for finalized bridge events")
if err := bridge.LegacyL1ProcessFinalizedBridgeEvents(log, tx, b.l1Etl.EthClient, b.chainConfig.L1Contracts, group.Start, group.End); err != nil {
if err := bridge.LegacyL1ProcessFinalizedBridgeEvents(l1BridgeLog, tx, b.metrics, b.l1Etl.EthClient, b.chainConfig.L1Contracts, legacyFromL1Height, legacyToL1Height); err != nil {
return err
}
}
for _, group := range l2BlockGroups {
log := l2BridgeLog.New("from_l2_block_number", group.Start, "to_l2_block_number", group.End)
log.Info("scanning for finalized bridge events")
if err := bridge.LegacyL2ProcessFinalizedBridgeEvents(log, tx, b.chainConfig.L2Contracts, group.Start, group.End); err != nil {
if err := bridge.LegacyL2ProcessFinalizedBridgeEvents(l2BridgeLog, tx, b.metrics, b.chainConfig.L2Contracts, legacyFromL2Height, legacyToL2Height); err != nil {
return err
}
}
if legacyToL1Height.Cmp(toL1Height) == 0 {
// a-ok! entire batch was legacy blocks
......@@ -193,52 +193,37 @@ func (b *BridgeProcessor) Start(ctx context.Context) error {
fromL2Height = l2BedrockStartingHeight
}
l1BridgeLog = l1BridgeLog.New("from_l1_block_number", fromL1Height, "to_l1_block_number", toL1Height)
l1BridgeLog.Info("scanning for bridge events")
l2BridgeLog = l2BridgeLog.New("from_l2_block_number", fromL2Height, "to_l2_block_number", toL2Height)
l2BridgeLog.Info("scanning for bridge events")
// First, find all possible initiated bridge events
l1BlockGroups := bigint.Grouped(fromL1Height, toL1Height, maxBlockRange)
l2BlockGroups := bigint.Grouped(fromL2Height, toL2Height, maxBlockRange)
for _, group := range l1BlockGroups {
log := l1BridgeLog.New("from_block_number", group.Start, "to_block_number", group.End)
log.Info("scanning for initiated bridge events")
if err := bridge.L1ProcessInitiatedBridgeEvents(log, tx, b.chainConfig.L1Contracts, group.Start, group.End); err != nil {
if err := bridge.L1ProcessInitiatedBridgeEvents(l1BridgeLog, tx, b.metrics, b.chainConfig.L1Contracts, fromL1Height, toL1Height); err != nil {
return err
}
}
for _, group := range l2BlockGroups {
log := l2BridgeLog.New("from_block_number", group.Start, "to_block_number", group.End)
log.Info("scanning for initiated bridge events")
if err := bridge.L2ProcessInitiatedBridgeEvents(log, tx, b.chainConfig.L2Contracts, group.Start, group.End); err != nil {
if err := bridge.L2ProcessInitiatedBridgeEvents(l2BridgeLog, tx, b.metrics, b.chainConfig.L2Contracts, fromL2Height, toL2Height); err != nil {
return err
}
}
// Now all finalization events can find their counterpart.
for _, group := range l1BlockGroups {
log := l1BridgeLog.New("from_block_number", group.Start, "to_block_number", group.End)
log.Info("scanning for finalized bridge events")
if err := bridge.L1ProcessFinalizedBridgeEvents(log, tx, b.chainConfig.L1Contracts, group.Start, group.End); err != nil {
if err := bridge.L1ProcessFinalizedBridgeEvents(l1BridgeLog, tx, b.metrics, b.chainConfig.L1Contracts, fromL1Height, toL1Height); err != nil {
return err
}
}
for _, group := range l2BlockGroups {
log := l2BridgeLog.New("from_block_number", group.Start, "to_block_number", group.End)
log.Info("scanning for finalized bridge events")
if err := bridge.L2ProcessFinalizedBridgeEvents(log, tx, b.chainConfig.L2Contracts, group.Start, group.End); err != nil {
if err := bridge.L2ProcessFinalizedBridgeEvents(l2BridgeLog, tx, b.metrics, b.chainConfig.L2Contracts, fromL2Height, toL2Height); err != nil {
return err
}
}
// a-ok
return nil
})
if err != nil {
// Try again on a subsequent interval
}); err != nil {
batchLog.Error("failed to index bridge events", "err", err)
} else {
return err
}
batchLog.Info("indexed bridge events", "latest_l1_block_number", toL1Height, "latest_l2_block_number", toL2Height)
b.LatestL1Header = latestEpoch.L1BlockHeader.RLPHeader.Header()
b.LatestL2Header = latestEpoch.L2BlockHeader.RLPHeader.Header()
}
}
return nil
}
......@@ -8,6 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/processors/contracts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
......@@ -17,7 +18,7 @@ import (
// 1. OptimismPortal
// 2. L1CrossDomainMessenger
// 3. L1StandardBridge
func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, metrics L1Metricer, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
// (1) OptimismPortal
optimismPortalTxDeposits, err := contracts.OptimismPortalTransactionDepositEvents(l1Contracts.OptimismPortalProxy, db, fromHeight, toHeight)
if err != nil {
......@@ -44,6 +45,7 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
if err := db.BridgeTransactions.StoreL1TransactionDeposits(transactionDeposits); err != nil {
return err
}
metrics.RecordL1TransactionDeposits(len(transactionDeposits))
}
// (2) L1CrossDomainMessenger
......@@ -56,7 +58,7 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
}
sentMessages := make(map[logKey]*contracts.CrossDomainMessengerSentMessageEvent, len(crossDomainSentMessages))
l1BridgeMessages := make([]database.L1BridgeMessage, len(crossDomainSentMessages))
bridgeMessages := make([]database.L1BridgeMessage, len(crossDomainSentMessages))
for i := range crossDomainSentMessages {
sentMessage := crossDomainSentMessages[i]
sentMessages[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex}] = &sentMessage
......@@ -68,12 +70,13 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
return fmt.Errorf("expected TransactionDeposit preceding SentMessage event. tx_hash = %s", sentMessage.Event.TransactionHash.String())
}
l1BridgeMessages[i] = database.L1BridgeMessage{TransactionSourceHash: portalDeposit.DepositTx.SourceHash, BridgeMessage: sentMessage.BridgeMessage}
bridgeMessages[i] = database.L1BridgeMessage{TransactionSourceHash: portalDeposit.DepositTx.SourceHash, BridgeMessage: sentMessage.BridgeMessage}
}
if len(l1BridgeMessages) > 0 {
if err := db.BridgeMessages.StoreL1BridgeMessages(l1BridgeMessages); err != nil {
if len(bridgeMessages) > 0 {
if err := db.BridgeMessages.StoreL1BridgeMessages(bridgeMessages); err != nil {
return err
}
metrics.RecordL1CrossDomainSentMessages(len(bridgeMessages))
}
// (3) L1StandardBridge
......@@ -85,7 +88,8 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
log.Info("detected bridge deposits", "size", len(initiatedBridges))
}
l1BridgeDeposits := make([]database.L1BridgeDeposit, len(initiatedBridges))
bridgedTokens := make(map[common.Address]int)
bridgeDeposits := make([]database.L1BridgeDeposit, len(initiatedBridges))
for i := range initiatedBridges {
initiatedBridge := initiatedBridges[i]
......@@ -102,15 +106,19 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
}
initiatedBridge.BridgeTransfer.CrossDomainMessageHash = &sentMessage.BridgeMessage.MessageHash
l1BridgeDeposits[i] = database.L1BridgeDeposit{
bridgedTokens[initiatedBridge.BridgeTransfer.TokenPair.LocalTokenAddress]++
bridgeDeposits[i] = database.L1BridgeDeposit{
TransactionSourceHash: portalDeposit.DepositTx.SourceHash,
BridgeTransfer: initiatedBridge.BridgeTransfer,
}
}
if len(l1BridgeDeposits) > 0 {
if err := db.BridgeTransfers.StoreL1BridgeDeposits(l1BridgeDeposits); err != nil {
if len(bridgeDeposits) > 0 {
if err := db.BridgeTransfers.StoreL1BridgeDeposits(bridgeDeposits); err != nil {
return err
}
for tokenAddr, size := range bridgedTokens {
metrics.RecordL1InitiatedBridgeTransfers(tokenAddr, size)
}
}
return nil
......@@ -121,7 +129,7 @@ func L1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
// 1. OptimismPortal (Bedrock prove & finalize steps)
// 2. L1CrossDomainMessenger (relayMessage marker)
// 3. L1StandardBridge (no-op, since this is simply a wrapper over the L1CrossDomainMessenger)
func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, metrics L1Metricer, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
// (1) OptimismPortal (proven withdrawals)
provenWithdrawals, err := contracts.OptimismPortalWithdrawalProvenEvents(l1Contracts.OptimismPortalProxy, db, fromHeight, toHeight)
if err != nil {
......@@ -146,6 +154,9 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
return err
}
}
if len(provenWithdrawals) > 0 {
metrics.RecordL1ProvenWithdrawals(len(provenWithdrawals))
}
// (2) OptimismPortal (finalized withdrawals)
finalizedWithdrawals, err := contracts.OptimismPortalWithdrawalFinalizedEvents(l1Contracts.OptimismPortalProxy, db, fromHeight, toHeight)
......@@ -171,6 +182,9 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
return err
}
}
if len(finalizedWithdrawals) > 0 {
metrics.RecordL1FinalizedWithdrawals(len(finalizedWithdrawals))
}
// (3) L1CrossDomainMessenger
crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l1", l1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
......@@ -198,6 +212,9 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
return err
}
}
if len(crossDomainRelayedMessages) > 0 {
metrics.RecordL1CrossDomainRelayedMessages(len(crossDomainRelayedMessages))
}
// (4) L1StandardBridge
finalizedBridges, err := contracts.StandardBridgeFinalizedEvents("l1", l1Contracts.L1StandardBridgeProxy, db, fromHeight, toHeight)
......@@ -208,6 +225,7 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
log.Info("detected finalized bridge withdrawals", "size", len(finalizedBridges))
}
finalizedTokens := make(map[common.Address]int)
for i := range finalizedBridges {
// Nothing actionable on the database. However, we can treat the relayed message
// as an invariant by ensuring we can query for a deposit by the same hash
......@@ -218,8 +236,7 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
return fmt.Errorf("expected RelayedMessage following BridgeFinalized event. tx_hash = %s", finalizedBridge.Event.TransactionHash.String())
}
// Since the message hash is computed from the relayed message, this ensures the deposit fields must match. For good measure,
// we may choose to make sure `withdrawal.BridgeTransfer` matches with the finalized bridge
// Since the message hash is computed from the relayed message, this ensures the deposit fields must match
withdrawal, err := db.BridgeTransfers.L2BridgeWithdrawalWithFilter(database.BridgeTransfer{CrossDomainMessageHash: &relayedMessage.MessageHash})
if err != nil {
return err
......@@ -227,6 +244,13 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Contracts
log.Error("missing L2StandardBridge withdrawal on L1 finalization", "tx_hash", finalizedBridge.Event.TransactionHash.String())
return fmt.Errorf("missing L2StandardBridge withdrawal on L1 finalization. tx_hash: %s", finalizedBridge.Event.TransactionHash.String())
}
finalizedTokens[finalizedBridge.BridgeTransfer.TokenPair.LocalTokenAddress]++
}
if len(finalizedBridges) > 0 {
for tokenAddr, size := range finalizedTokens {
metrics.RecordL1FinalizedBridgeTransfers(tokenAddr, size)
}
}
// a-ok!
......
......@@ -9,6 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/processors/contracts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
)
......@@ -17,7 +18,7 @@ import (
// 1. OptimismPortal
// 2. L2CrossDomainMessenger
// 3. L2StandardBridge
func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error {
func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, metrics L2Metricer, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error {
// (1) L2ToL1MessagePasser
l2ToL1MPMessagesPassed, err := contracts.L2ToL1MessagePasserMessagePassedEvents(l2Contracts.L2ToL1MessagePasser, db, fromHeight, toHeight)
if err != nil {
......@@ -44,6 +45,7 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
if err := db.BridgeTransactions.StoreL2TransactionWithdrawals(transactionWithdrawals); err != nil {
return err
}
metrics.RecordL2TransactionWithdrawals(len(transactionWithdrawals))
}
// (2) L2CrossDomainMessenger
......@@ -56,7 +58,7 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
}
sentMessages := make(map[logKey]*contracts.CrossDomainMessengerSentMessageEvent, len(crossDomainSentMessages))
l2BridgeMessages := make([]database.L2BridgeMessage, len(crossDomainSentMessages))
bridgeMessages := make([]database.L2BridgeMessage, len(crossDomainSentMessages))
for i := range crossDomainSentMessages {
sentMessage := crossDomainSentMessages[i]
sentMessages[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex}] = &sentMessage
......@@ -68,13 +70,13 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
return fmt.Errorf("expected MessagePassedEvent preceding SentMessage. tx_hash = %s", sentMessage.Event.TransactionHash.String())
}
l2BridgeMessages[i] = database.L2BridgeMessage{TransactionWithdrawalHash: messagePassed.WithdrawalHash, BridgeMessage: sentMessage.BridgeMessage}
bridgeMessages[i] = database.L2BridgeMessage{TransactionWithdrawalHash: messagePassed.WithdrawalHash, BridgeMessage: sentMessage.BridgeMessage}
}
if len(l2BridgeMessages) > 0 {
if err := db.BridgeMessages.StoreL2BridgeMessages(l2BridgeMessages); err != nil {
if len(bridgeMessages) > 0 {
if err := db.BridgeMessages.StoreL2BridgeMessages(bridgeMessages); err != nil {
return err
}
metrics.RecordL2CrossDomainSentMessages(len(bridgeMessages))
}
// (3) L2StandardBridge
......@@ -86,7 +88,8 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
log.Info("detected bridge withdrawals", "size", len(initiatedBridges))
}
l2BridgeWithdrawals := make([]database.L2BridgeWithdrawal, len(initiatedBridges))
bridgedTokens := make(map[common.Address]int)
bridgeWithdrawals := make([]database.L2BridgeWithdrawal, len(initiatedBridges))
for i := range initiatedBridges {
initiatedBridge := initiatedBridges[i]
......@@ -103,13 +106,19 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
}
initiatedBridge.BridgeTransfer.CrossDomainMessageHash = &sentMessage.BridgeMessage.MessageHash
l2BridgeWithdrawals[i] = database.L2BridgeWithdrawal{TransactionWithdrawalHash: messagePassed.WithdrawalHash, BridgeTransfer: initiatedBridge.BridgeTransfer}
bridgedTokens[initiatedBridge.BridgeTransfer.TokenPair.LocalTokenAddress]++
bridgeWithdrawals[i] = database.L2BridgeWithdrawal{
TransactionWithdrawalHash: messagePassed.WithdrawalHash,
BridgeTransfer: initiatedBridge.BridgeTransfer,
}
if len(l2BridgeWithdrawals) > 0 {
if err := db.BridgeTransfers.StoreL2BridgeWithdrawals(l2BridgeWithdrawals); err != nil {
}
if len(bridgeWithdrawals) > 0 {
if err := db.BridgeTransfers.StoreL2BridgeWithdrawals(bridgeWithdrawals); err != nil {
return err
}
for tokenAddr, size := range bridgedTokens {
metrics.RecordL2InitiatedBridgeTransfers(tokenAddr, size)
}
}
// a-ok!
......@@ -122,7 +131,7 @@ func L2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
// 2. L2StandardBridge (no-op, since this is simply a wrapper over the L2CrossDomainMEssenger)
//
// NOTE: Unlike L1, there's no L2ToL1MessagePasser stage since transaction deposits are apart of the block derivation process.
func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error {
func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, metrics L2Metricer, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error {
// (1) L2CrossDomainMessenger
crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l2", l2Contracts.L2CrossDomainMessenger, db, fromHeight, toHeight)
if err != nil {
......@@ -149,6 +158,9 @@ func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
return err
}
}
if len(relayedMessages) > 0 {
metrics.RecordL2CrossDomainRelayedMessages(len(relayedMessages))
}
// (2) L2StandardBridge
finalizedBridges, err := contracts.StandardBridgeFinalizedEvents("l2", l2Contracts.L2StandardBridge, db, fromHeight, toHeight)
......@@ -159,6 +171,7 @@ func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
log.Info("detected finalized bridge deposits", "size", len(finalizedBridges))
}
finalizedTokens := make(map[common.Address]int)
for i := range finalizedBridges {
// Nothing actionable on the database. However, we can treat the relayed message
// as an invariant by ensuring we can query for a deposit by the same hash
......@@ -169,8 +182,7 @@ func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
return fmt.Errorf("expected RelayedMessage following BridgeFinalized event. tx_hash = %s", finalizedBridge.Event.TransactionHash.String())
}
// Since the message hash is computed from the relayed message, this ensures the withdrawal fields must match. For good measure,
// we may choose to make sure `deposit.BridgeTransfer` matches with the finalized bridge
// Since the message hash is computed from the relayed message, this ensures the withdrawal fields must match
deposit, err := db.BridgeTransfers.L1BridgeDepositWithFilter(database.BridgeTransfer{CrossDomainMessageHash: &relayedMessage.MessageHash})
if err != nil {
return err
......@@ -178,6 +190,13 @@ func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts
log.Error("missing L1StandardBridge deposit on L2 finalization", "tx_hash", finalizedBridge.Event.TransactionHash.String())
return errors.New("missing L1StandardBridge deposit on L2 finalization")
}
finalizedTokens[finalizedBridge.BridgeTransfer.TokenPair.LocalTokenAddress]++
}
if len(finalizedBridges) > 0 {
for tokenAddr, size := range finalizedTokens {
metrics.RecordL2FinalizedBridgeTransfers(tokenAddr, size)
}
}
// a-ok!
......
......@@ -4,6 +4,7 @@ import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
......@@ -20,7 +21,7 @@ import (
// 1. CanonicalTransactionChain
// 2. L1CrossDomainMessenger
// 3. L1StandardBridge
func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, metrics L1Metricer, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
// (1) CanonicalTransactionChain
ctcTxDepositEvents, err := contracts.LegacyCTCDepositEvents(l1Contracts.LegacyCanonicalTransactionChain, db, fromHeight, toHeight)
if err != nil {
......@@ -50,6 +51,7 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con
if err := db.BridgeTransactions.StoreL1TransactionDeposits(transactionDeposits); err != nil {
return err
}
metrics.RecordL1TransactionDeposits(len(transactionDeposits))
}
// (2) L1CrossDomainMessenger
......@@ -62,7 +64,7 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con
}
sentMessages := make(map[logKey]*contracts.CrossDomainMessengerSentMessageEvent, len(crossDomainSentMessages))
l1BridgeMessages := make([]database.L1BridgeMessage, len(crossDomainSentMessages))
bridgeMessages := make([]database.L1BridgeMessage, len(crossDomainSentMessages))
for i := range crossDomainSentMessages {
sentMessage := crossDomainSentMessages[i]
sentMessages[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex}] = &sentMessage
......@@ -74,12 +76,13 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con
return fmt.Errorf("missing preceding TransactionEnqueued for SentMessage event. tx_hash = %s", sentMessage.Event.TransactionHash.String())
}
l1BridgeMessages[i] = database.L1BridgeMessage{TransactionSourceHash: ctcTxDeposit.TxHash, BridgeMessage: sentMessage.BridgeMessage}
bridgeMessages[i] = database.L1BridgeMessage{TransactionSourceHash: ctcTxDeposit.TxHash, BridgeMessage: sentMessage.BridgeMessage}
}
if len(l1BridgeMessages) > 0 {
if err := db.BridgeMessages.StoreL1BridgeMessages(l1BridgeMessages); err != nil {
if len(bridgeMessages) > 0 {
if err := db.BridgeMessages.StoreL1BridgeMessages(bridgeMessages); err != nil {
return err
}
metrics.RecordL1CrossDomainSentMessages(len(bridgeMessages))
}
// (3) L1StandardBridge
......@@ -91,7 +94,8 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con
log.Info("detected iegacy bridge deposits", "size", len(initiatedBridges))
}
l1BridgeDeposits := make([]database.L1BridgeDeposit, len(initiatedBridges))
bridgedTokens := make(map[common.Address]int)
bridgeDeposits := make([]database.L1BridgeDeposit, len(initiatedBridges))
for i := range initiatedBridges {
initiatedBridge := initiatedBridges[i]
......@@ -110,15 +114,19 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con
}
initiatedBridge.BridgeTransfer.CrossDomainMessageHash = &sentMessage.BridgeMessage.MessageHash
l1BridgeDeposits[i] = database.L1BridgeDeposit{
bridgedTokens[initiatedBridge.BridgeTransfer.TokenPair.LocalTokenAddress]++
bridgeDeposits[i] = database.L1BridgeDeposit{
TransactionSourceHash: ctcTxDeposit.TxHash,
BridgeTransfer: initiatedBridge.BridgeTransfer,
}
}
if len(l1BridgeDeposits) > 0 {
if err := db.BridgeTransfers.StoreL1BridgeDeposits(l1BridgeDeposits); err != nil {
if len(bridgeDeposits) > 0 {
if err := db.BridgeTransfers.StoreL1BridgeDeposits(bridgeDeposits); err != nil {
return err
}
for tokenAddr, size := range bridgedTokens {
metrics.RecordL1InitiatedBridgeTransfers(tokenAddr, size)
}
}
// a-ok!
......@@ -130,20 +138,19 @@ func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l1Con
// 1. L2CrossDomainMessenger - The LegacyMessagePasser contract cannot be used as entrypoint to bridge transactions from L2. The protocol
// only allows the L2CrossDomainMessenger as the sole sender when relaying a bridged message.
// 2. L2StandardBridge
func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error {
func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, metrics L2Metricer, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error {
// (1) L2CrossDomainMessenger
crossDomainSentMessages, err := contracts.CrossDomainMessengerSentMessageEvents("l2", l2Contracts.L2CrossDomainMessenger, db, fromHeight, toHeight)
if err != nil {
return err
}
if len(crossDomainSentMessages) > 0 {
log.Info("detected legacy transaction withdrawals (via L2CrossDomainMessenger)", "size", len(crossDomainSentMessages))
}
sentMessages := make(map[logKey]*contracts.CrossDomainMessengerSentMessageEvent, len(crossDomainSentMessages))
l2BridgeMessages := make([]database.L2BridgeMessage, len(crossDomainSentMessages))
l2TransactionWithdrawals := make([]database.L2TransactionWithdrawal, len(crossDomainSentMessages))
bridgeMessages := make([]database.L2BridgeMessage, len(crossDomainSentMessages))
transactionWithdrawals := make([]database.L2TransactionWithdrawal, len(crossDomainSentMessages))
for i := range crossDomainSentMessages {
sentMessage := crossDomainSentMessages[i]
sentMessages[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex}] = &sentMessage
......@@ -151,7 +158,7 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con
// To ensure consistency in the schema, we duplicate this as the "root" transaction withdrawal. The storage key in the message
// passer contract is sha3(calldata + sender). The sender always being the L2CrossDomainMessenger pre-bedrock.
withdrawalHash := crypto.Keccak256Hash(append(sentMessage.MessageCalldata, l2Contracts.L2CrossDomainMessenger[:]...))
l2TransactionWithdrawals[i] = database.L2TransactionWithdrawal{
transactionWithdrawals[i] = database.L2TransactionWithdrawal{
WithdrawalHash: withdrawalHash,
InitiatedL2EventGUID: sentMessage.Event.GUID,
Nonce: sentMessage.BridgeMessage.Nonce,
......@@ -165,19 +172,20 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con
},
}
l2BridgeMessages[i] = database.L2BridgeMessage{
bridgeMessages[i] = database.L2BridgeMessage{
TransactionWithdrawalHash: withdrawalHash,
BridgeMessage: sentMessage.BridgeMessage,
}
}
if len(l2BridgeMessages) > 0 {
if err := db.BridgeTransactions.StoreL2TransactionWithdrawals(l2TransactionWithdrawals); err != nil {
if len(bridgeMessages) > 0 {
if err := db.BridgeTransactions.StoreL2TransactionWithdrawals(transactionWithdrawals); err != nil {
return err
}
if err := db.BridgeMessages.StoreL2BridgeMessages(l2BridgeMessages); err != nil {
if err := db.BridgeMessages.StoreL2BridgeMessages(bridgeMessages); err != nil {
return err
}
metrics.RecordL2TransactionWithdrawals(len(transactionWithdrawals))
metrics.RecordL2CrossDomainSentMessages(len(bridgeMessages))
}
// (2) L2StandardBridge
......@@ -189,6 +197,7 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con
log.Info("detected legacy bridge withdrawals", "size", len(initiatedBridges))
}
bridgedTokens := make(map[common.Address]int)
l2BridgeWithdrawals := make([]database.L2BridgeWithdrawal, len(initiatedBridges))
for i := range initiatedBridges {
initiatedBridge := initiatedBridges[i]
......@@ -201,8 +210,9 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con
log.Error("expected SentMessage preceding DepositInitiated event", "tx_hash", initiatedBridge.Event.TransactionHash.String())
return fmt.Errorf("expected SentMessage preceding DepositInitiated event. tx_hash = %s", initiatedBridge.Event.TransactionHash)
}
initiatedBridge.BridgeTransfer.CrossDomainMessageHash = &sentMessage.BridgeMessage.MessageHash
bridgedTokens[initiatedBridge.BridgeTransfer.TokenPair.LocalTokenAddress]++
l2BridgeWithdrawals[i] = database.L2BridgeWithdrawal{
TransactionWithdrawalHash: sentMessage.BridgeMessage.MessageHash,
BridgeTransfer: initiatedBridge.BridgeTransfer,
......@@ -212,6 +222,9 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con
if err := db.BridgeTransfers.StoreL2BridgeWithdrawals(l2BridgeWithdrawals); err != nil {
return err
}
for tokenAddr, size := range bridgedTokens {
metrics.RecordL2InitiatedBridgeTransfers(tokenAddr, size)
}
}
// a-ok
......@@ -224,7 +237,7 @@ func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, l2Con
// according to the pre-bedrock protocol. This follows:
// 1. L1CrossDomainMessenger
// 2. L1StandardBridge
func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Client node.EthClient, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, metrics L1Metricer, l1Client node.EthClient, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
// (1) L1CrossDomainMessenger -> This is the root-most contract from which bridge events are finalized since withdrawals must be initiated from the
// L2CrossDomainMessenger. Since there's no two-step withdrawal process, we mark the transaction as proven/finalized in the same step
crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l1", l1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
......@@ -286,15 +299,20 @@ func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Cli
return err
}
}
if len(crossDomainRelayedMessages) > 0 {
metrics.RecordL1ProvenWithdrawals(len(crossDomainRelayedMessages))
metrics.RecordL1FinalizedWithdrawals(len(crossDomainRelayedMessages))
metrics.RecordL1CrossDomainRelayedMessages(len(crossDomainRelayedMessages))
}
if skippedPreRegenesisMessages > 0 {
// Logged as a warning just for visibility
log.Warn("skipped pre-regensis relayed L2CrossDomainMessenger withdrawals", "size", skippedPreRegenesisMessages)
}
// (2) L2StandardBridge -- no-op for now as there's nothing actionable to do here besides
// santiy checks which is not important for legacy code. The message status is already tracked
// via the relayed bridge messed through the cross domain messenger.
// santiy checks which is not important for legacy code. Not worth extra code pre-bedrock.
// The message status is already tracked via the relayed bridge messed through the cross domain messenger.
// - NOTE: This means we dont increment metrics for finalized bridge transfers
// a-ok!
return nil
......@@ -304,7 +322,7 @@ func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l1Cli
// according to the pre-bedrock protocol. This follows:
// 1. L2CrossDomainMessenger
// 2. L2StandardBridge
func LegacyL2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error {
func LegacyL2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, metrics L2Metricer, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error {
// (1) L2CrossDomainMessenger
crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l2", l2Contracts.L2CrossDomainMessenger, db, fromHeight, toHeight)
if err != nil {
......@@ -329,10 +347,14 @@ func LegacyL2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, l2Con
return err
}
}
if len(crossDomainRelayedMessages) > 0 {
metrics.RecordL2CrossDomainRelayedMessages(len(crossDomainRelayedMessages))
}
// (2) L2StandardBridge -- no-op for now as there's nothing actionable to do here besides
// santiy checks which is not important for legacy code. The message status is already tracked
// via the relayed bridge messed through the cross domain messenger.
// santiy checks which is not important for legacy code. Not worth the extra code pre-bedorck.
// The message status is already tracked via the relayed bridge messed through the cross domain messenger.
// - NOTE: This means we dont increment metrics for finalized bridge transfers
// a-ok!
return nil
......
package bridge
import (
"math/big"
"github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/ethereum/go-ethereum/common"
"github.com/prometheus/client_golang/prometheus"
)
var (
MetricsNamespace string = "bridge"
)
type L1Metricer interface {
RecordLatestIndexedL1Height(height *big.Int)
RecordL1TransactionDeposits(size int)
RecordL1ProvenWithdrawals(size int)
RecordL1FinalizedWithdrawals(size int)
RecordL1CrossDomainSentMessages(size int)
RecordL1CrossDomainRelayedMessages(size int)
RecordL1InitiatedBridgeTransfers(token common.Address, size int)
RecordL1FinalizedBridgeTransfers(token common.Address, size int)
}
type L2Metricer interface {
RecordLatestIndexedL2Height(height *big.Int)
RecordL2TransactionWithdrawals(size int)
RecordL2CrossDomainSentMessages(size int)
RecordL2CrossDomainRelayedMessages(size int)
RecordL2InitiatedBridgeTransfers(token common.Address, size int)
RecordL2FinalizedBridgeTransfers(token common.Address, size int)
}
type Metricer interface {
L1Metricer
L2Metricer
RecordInterval() (done func(err error))
}
type bridgeMetrics struct {
intervalTick prometheus.Counter
intervalDuration prometheus.Histogram
intervalFailures prometheus.Counter
latestL1Height prometheus.Gauge
latestL2Height prometheus.Gauge
txDeposits prometheus.Counter
txWithdrawals prometheus.Counter
provenWithdrawals prometheus.Counter
finalizedWithdrawals prometheus.Counter
sentMessages *prometheus.CounterVec
relayedMessages *prometheus.CounterVec
initiatedBridgeTransfers *prometheus.CounterVec
finalizedBridgeTransfers *prometheus.CounterVec
}
func NewMetrics(registry *prometheus.Registry) Metricer {
factory := metrics.With(registry)
return &bridgeMetrics{
intervalTick: factory.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "intervals_total",
Help: "number of times processing loop has run",
}),
intervalDuration: factory.NewHistogram(prometheus.HistogramOpts{
Namespace: MetricsNamespace,
Name: "interval_seconds",
Help: "duration elapsed in the processing loop",
}),
intervalFailures: factory.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "failures_total",
Help: "number of failures encountered",
}),
latestL1Height: factory.NewGauge(prometheus.GaugeOpts{
Namespace: MetricsNamespace,
Subsystem: "l1",
Name: "height",
Help: "the latest processed l1 block height",
}),
latestL2Height: factory.NewGauge(prometheus.GaugeOpts{
Namespace: MetricsNamespace,
Subsystem: "l2",
Name: "height",
Help: "the latest processed l2 block height",
}),
txDeposits: factory.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "tx_deposits",
Help: "number of processed transactions deposited from l1",
}),
txWithdrawals: factory.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "tx_withdrawals",
Help: "number of processed transactions withdrawn from l2",
}),
provenWithdrawals: factory.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "proven_withdrawals",
Help: "number of proven tx withdrawals on l1",
}),
finalizedWithdrawals: factory.NewCounter(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "finalized_withdrawals",
Help: "number of finalized tx withdrawals on l1",
}),
sentMessages: factory.NewCounterVec(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "sent_messages",
Help: "number of bridged messages between l1 and l2",
}, []string{
"chain",
}),
relayedMessages: factory.NewCounterVec(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "relayed_messages",
Help: "number of relayed messages between l1 and l2",
}, []string{
"chain",
}),
initiatedBridgeTransfers: factory.NewCounterVec(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "initiated_token_transfers",
Help: "number of bridged tokens between l1 and l2",
}, []string{
"chain",
"token_address",
}),
finalizedBridgeTransfers: factory.NewCounterVec(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "finalized_token_transfers",
Help: "number of finalized token transfers between l1 and l2",
}, []string{
"chain",
"token_address",
}),
}
}
func (m *bridgeMetrics) RecordInterval() func(error) {
m.intervalTick.Inc()
timer := prometheus.NewTimer(m.intervalDuration)
return func(err error) {
timer.ObserveDuration()
if err != nil {
m.intervalFailures.Inc()
}
}
}
// L1Metricer
func (m *bridgeMetrics) RecordLatestIndexedL1Height(height *big.Int) {
m.latestL1Height.Set(float64(height.Uint64()))
}
func (m *bridgeMetrics) RecordL1TransactionDeposits(size int) {
m.txDeposits.Add(float64(size))
}
func (m *bridgeMetrics) RecordL1ProvenWithdrawals(size int) {
m.provenWithdrawals.Add(float64(size))
}
func (m *bridgeMetrics) RecordL1FinalizedWithdrawals(size int) {
m.finalizedWithdrawals.Add(float64(size))
}
func (m *bridgeMetrics) RecordL1CrossDomainSentMessages(size int) {
m.sentMessages.WithLabelValues("l1").Add(float64(size))
}
func (m *bridgeMetrics) RecordL1CrossDomainRelayedMessages(size int) {
m.relayedMessages.WithLabelValues("l1").Add(float64(size))
}
func (m *bridgeMetrics) RecordL1InitiatedBridgeTransfers(tokenAddr common.Address, size int) {
m.initiatedBridgeTransfers.WithLabelValues("l1", tokenAddr.String()).Add(float64(size))
}
func (m *bridgeMetrics) RecordL1FinalizedBridgeTransfers(tokenAddr common.Address, size int) {
m.finalizedBridgeTransfers.WithLabelValues("l1", tokenAddr.String()).Add(float64(size))
}
// L2Metricer
func (m *bridgeMetrics) RecordLatestIndexedL2Height(height *big.Int) {
m.latestL2Height.Set(float64(height.Uint64()))
}
func (m *bridgeMetrics) RecordL2TransactionWithdrawals(size int) {
m.txWithdrawals.Add(float64(size))
}
func (m *bridgeMetrics) RecordL2CrossDomainSentMessages(size int) {
m.sentMessages.WithLabelValues("l2").Add(float64(size))
}
func (m *bridgeMetrics) RecordL2CrossDomainRelayedMessages(size int) {
m.relayedMessages.WithLabelValues("l2").Add(float64(size))
}
func (m *bridgeMetrics) RecordL2InitiatedBridgeTransfers(tokenAddr common.Address, size int) {
m.initiatedBridgeTransfers.WithLabelValues("l2", tokenAddr.String()).Add(float64(size))
}
func (m *bridgeMetrics) RecordL2FinalizedBridgeTransfers(tokenAddr common.Address, size int) {
m.finalizedBridgeTransfers.WithLabelValues("l2", tokenAddr.String()).Add(float64(size))
}
......@@ -15,21 +15,23 @@ type LegacyBridgeEvent struct {
}
func L1StandardBridgeLegacyDepositInitiatedEvents(contractAddress common.Address, db *database.DB, fromHeight, toHeight *big.Int) ([]LegacyBridgeEvent, error) {
// The L1StandardBridge ABI contains the legacy events
l1StandardBridgeAbi, err := bindings.L1StandardBridgeMetaData.GetAbi()
if err != nil {
return nil, err
}
// The L1StandardBridge contains the legacy events
ethDepositEventAbi := l1StandardBridgeAbi.Events["ETHDepositInitiated"]
erc20DepositEventAbi := l1StandardBridgeAbi.Events["ERC20DepositInitiated"]
// Grab both ETH & ERC20 Events
ethDepositEvents, err := db.ContractEvents.L1ContractEventsWithFilter(database.ContractEvent{ContractAddress: contractAddress, EventSignature: ethDepositEventAbi.ID}, fromHeight, toHeight)
contractEventFilter := database.ContractEvent{ContractAddress: contractAddress, EventSignature: ethDepositEventAbi.ID}
ethDepositEvents, err := db.ContractEvents.L1ContractEventsWithFilter(contractEventFilter, fromHeight, toHeight)
if err != nil {
return nil, err
}
erc20DepositEvents, err := db.ContractEvents.L1ContractEventsWithFilter(database.ContractEvent{ContractAddress: contractAddress, EventSignature: erc20DepositEventAbi.ID}, fromHeight, toHeight)
contractEventFilter.EventSignature = erc20DepositEventAbi.ID
erc20DepositEvents, err := db.ContractEvents.L1ContractEventsWithFilter(contractEventFilter, fromHeight, toHeight)
if err != nil {
return nil, err
}
......@@ -81,13 +83,15 @@ func L1StandardBridgeLegacyDepositInitiatedEvents(contractAddress common.Address
}
func L2StandardBridgeLegacyWithdrawalInitiatedEvents(contractAddress common.Address, db *database.DB, fromHeight, toHeight *big.Int) ([]LegacyBridgeEvent, error) {
// The L2StandardBridge ABI contains the legacy events
l2StandardBridgeAbi, err := bindings.L2StandardBridgeMetaData.GetAbi()
if err != nil {
return nil, err
}
withdrawalInitiatedEventAbi := l2StandardBridgeAbi.Events["WithdrawalInitiated"]
withdrawalEvents, err := db.ContractEvents.L2ContractEventsWithFilter(database.ContractEvent{ContractAddress: contractAddress, EventSignature: withdrawalInitiatedEventAbi.ID}, fromHeight, toHeight)
contractEventFilter := database.ContractEvent{ContractAddress: contractAddress, EventSignature: withdrawalInitiatedEventAbi.ID}
withdrawalEvents, err := db.ContractEvents.L2ContractEventsWithFilter(contractEventFilter, fromHeight, toHeight)
if err != nil {
return nil, err
}
......
......@@ -99,29 +99,31 @@ func entrypoint(ctx *cli.Context) error {
batch := safe.Batch{}
for _, chainConfig := range targets {
name, err := toDeployConfigName(chainConfig)
name, _ := toDeployConfigName(chainConfig)
config, err := genesis.NewDeployConfigWithNetwork(name, deployConfig)
if err != nil {
log.Warn("Skipping unsupported network", "name", chainConfig.Name)
continue
log.Warn("Cannot find deploy config for network", "name", chainConfig.Name, "deploy-config-name", name, "path", deployConfig)
}
config, err := genesis.NewDeployConfigWithNetwork(name, deployConfig)
if err != nil {
return err
if config != nil {
log.Info("Checking deploy config validity", "name", chainConfig.Name)
if err := config.Check(); err != nil {
return fmt.Errorf("error checking deploy config: %w", err)
}
}
clients, err := clients.NewClients(ctx.String("l1-rpc-url"), chainConfig.PublicRPC)
if err != nil {
return err
return fmt.Errorf("cannot create RPC clients: %w", err)
}
l1ChainID, err := clients.L1Client.ChainID(ctx.Context)
if err != nil {
return err
return fmt.Errorf("cannot fetch L1 chain ID: %w", err)
}
l2ChainID, err := clients.L2Client.ChainID(ctx.Context)
if err != nil {
return err
return fmt.Errorf("cannot fetch L2 chain ID: %w", err)
}
log.Info(chainConfig.Name, "l1-chain-id", l1ChainID, "l2-chain-id", l2ChainID)
......@@ -164,15 +166,19 @@ func entrypoint(ctx *cli.Context) error {
log.Info("OptimismPortal", "version", list.OptimismPortal.Version, "address", list.OptimismPortal.Address)
log.Info("SystemConfig", "version", list.SystemConfig.Version, "address", list.SystemConfig.Address)
// Ensure that the superchain registry information is correct by checking the
// actual versions based on what the registry says is true.
if err := upgrades.CheckL1(ctx.Context, &list, clients.L1Client); err != nil {
return fmt.Errorf("error checking L1: %w", err)
}
if err := upgrades.L1(&batch, list, *addresses, config, chainConfig); err != nil {
// Build the batch
if err := upgrades.L1(&batch, list, *addresses, config, chainConfig, clients.L1Client); err != nil {
return err
}
}
// Write the batch to disk or stdout
if outfile := ctx.Path("outfile"); outfile != "" {
if err := writeJSON(outfile, batch); err != nil {
return err
......@@ -207,6 +213,9 @@ func toDeployConfigName(cfg *superchain.ChainConfig) (string, error) {
if cfg.Name == "OP-Mainnet" {
return "mainnet", nil
}
if cfg.Name == "Zora Goerli" {
return "zora-goerli", nil
}
return "", fmt.Errorf("unsupported chain name %s", cfg.Name)
}
......
......@@ -4,6 +4,7 @@ import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
......@@ -14,33 +15,33 @@ import (
)
// L1 will add calls for upgrading each of the L1 contracts.
func L1(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error {
if err := L1CrossDomainMessenger(batch, implementations, list, config, chainConfig); err != nil {
return err
func L1(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, backend bind.ContractBackend) error {
if err := L1CrossDomainMessenger(batch, implementations, list, config, chainConfig, backend); err != nil {
return fmt.Errorf("upgrading L1CrossDomainMessenger: %w", err)
}
if err := L1ERC721Bridge(batch, implementations, list, config, chainConfig); err != nil {
return err
if err := L1ERC721Bridge(batch, implementations, list, config, chainConfig, backend); err != nil {
return fmt.Errorf("upgrading L1ERC721Bridge: %w", err)
}
if err := L1StandardBridge(batch, implementations, list, config, chainConfig); err != nil {
return err
if err := L1StandardBridge(batch, implementations, list, config, chainConfig, backend); err != nil {
return fmt.Errorf("upgrading L1StandardBridge: %w", err)
}
if err := L2OutputOracle(batch, implementations, list, config, chainConfig); err != nil {
return err
if err := L2OutputOracle(batch, implementations, list, config, chainConfig, backend); err != nil {
return fmt.Errorf("upgrading L2OutputOracle: %w", err)
}
if err := OptimismMintableERC20Factory(batch, implementations, list, config, chainConfig); err != nil {
return err
if err := OptimismMintableERC20Factory(batch, implementations, list, config, chainConfig, backend); err != nil {
return fmt.Errorf("upgrading OptimismMintableERC20Factory: %w", err)
}
if err := OptimismPortal(batch, implementations, list, config, chainConfig); err != nil {
return err
if err := OptimismPortal(batch, implementations, list, config, chainConfig, backend); err != nil {
return fmt.Errorf("upgrading OptimismPortal: %w", err)
}
if err := SystemConfig(batch, implementations, list, config, chainConfig); err != nil {
return err
if err := SystemConfig(batch, implementations, list, config, chainConfig, backend); err != nil {
return fmt.Errorf("upgrading SystemConfig: %w", err)
}
return nil
}
// L1CrossDomainMessenger will add a call to the batch that upgrades the L1CrossDomainMessenger.
func L1CrossDomainMessenger(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error {
func L1CrossDomainMessenger(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, backend bind.ContractBackend) error {
proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi()
if err != nil {
return err
......@@ -79,7 +80,7 @@ func L1CrossDomainMessenger(batch *safe.Batch, implementations superchain.Implem
}
// L1ERC721Bridge will add a call to the batch that upgrades the L1ERC721Bridge.
func L1ERC721Bridge(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error {
func L1ERC721Bridge(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, backend bind.ContractBackend) error {
proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi()
if err != nil {
return err
......@@ -118,7 +119,7 @@ func L1ERC721Bridge(batch *safe.Batch, implementations superchain.Implementation
}
// L1StandardBridge will add a call to the batch that upgrades the L1StandardBridge.
func L1StandardBridge(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error {
func L1StandardBridge(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, backend bind.ContractBackend) error {
proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi()
if err != nil {
return err
......@@ -157,7 +158,7 @@ func L1StandardBridge(batch *safe.Batch, implementations superchain.Implementati
}
// L2OutputOracle will add a call to the batch that upgrades the L2OutputOracle.
func L2OutputOracle(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error {
func L2OutputOracle(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, backend bind.ContractBackend) error {
proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi()
if err != nil {
return err
......@@ -173,17 +174,47 @@ func L2OutputOracle(batch *safe.Batch, implementations superchain.Implementation
return fmt.Errorf("no initialize method")
}
l2OutputOracleStartingBlockNumber := new(big.Int).SetUint64(config.L2OutputOracleStartingBlockNumber)
var l2OutputOracleStartingBlockNumber, l2OutputOracleStartingTimestamp *big.Int
var l2OutputOracleProposer, l2OutputOracleChallenger common.Address
if config != nil {
l2OutputOracleStartingBlockNumber = new(big.Int).SetUint64(config.L2OutputOracleStartingBlockNumber)
if config.L2OutputOracleStartingTimestamp < 0 {
return fmt.Errorf("L2OutputOracleStartingBlockNumber must be concrete")
return fmt.Errorf("L2OutputOracleStartingTimestamp must be concrete")
}
l2OutputOracleStartingTimestamp = new(big.Int).SetInt64(int64(config.L2OutputOracleStartingTimestamp))
l2OutputOracleProposer = config.L2OutputOracleProposer
l2OutputOracleChallenger = config.L2OutputOracleChallenger
} else {
l2OutputOracle, err := bindings.NewL2OutputOracleCaller(common.HexToAddress(list.L2OutputOracleProxy.String()), backend)
if err != nil {
return err
}
l2OutputOracleStartingBlockNumber, err = l2OutputOracle.StartingBlockNumber(&bind.CallOpts{})
if err != nil {
return err
}
l2OutputOracleStartingTimestamp, err = l2OutputOracle.StartingTimestamp(&bind.CallOpts{})
if err != nil {
return err
}
l2OutputOracleProposer, err = l2OutputOracle.PROPOSER(&bind.CallOpts{})
if err != nil {
return err
}
l2OutputOracleChallenger, err = l2OutputOracle.CHALLENGER(&bind.CallOpts{})
if err != nil {
return err
}
}
l2OutputOraclesStartingTimestamp := new(big.Int).SetInt64(int64(config.L2OutputOracleStartingTimestamp))
calldata, err := initialize.Inputs.PackValues([]any{
l2OutputOracleStartingBlockNumber,
l2OutputOraclesStartingTimestamp,
config.L2OutputOracleProposer,
config.L2OutputOracleChallenger,
l2OutputOracleStartingTimestamp,
l2OutputOracleProposer,
l2OutputOracleChallenger,
})
if err != nil {
return err
......@@ -205,7 +236,7 @@ func L2OutputOracle(batch *safe.Batch, implementations superchain.Implementation
}
// OptimismMintableERC20Factory will add a call to the batch that upgrades the OptimismMintableERC20Factory.
func OptimismMintableERC20Factory(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error {
func OptimismMintableERC20Factory(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, backend bind.ContractBackend) error {
proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi()
if err != nil {
return err
......@@ -244,7 +275,7 @@ func OptimismMintableERC20Factory(batch *safe.Batch, implementations superchain.
}
// OptimismPortal will add a call to the batch that upgrades the OptimismPortal.
func OptimismPortal(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error {
func OptimismPortal(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, backend bind.ContractBackend) error {
proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi()
if err != nil {
return err
......@@ -260,9 +291,24 @@ func OptimismPortal(batch *safe.Batch, implementations superchain.Implementation
return fmt.Errorf("no initialize method")
}
var portalGuardian common.Address
if config != nil {
portalGuardian = config.PortalGuardian
} else {
optimismPortal, err := bindings.NewOptimismPortalCaller(common.HexToAddress(list.OptimismPortalProxy.String()), backend)
if err != nil {
return err
}
guardian, err := optimismPortal.GUARDIAN(&bind.CallOpts{})
if err != nil {
return err
}
portalGuardian = guardian
}
calldata, err := initialize.Inputs.PackValues([]any{
common.HexToAddress(list.L2OutputOracleProxy.String()),
config.PortalGuardian,
portalGuardian,
common.HexToAddress(chainConfig.SystemConfigAddr.String()),
false,
})
......@@ -286,7 +332,7 @@ func OptimismPortal(batch *safe.Batch, implementations superchain.Implementation
}
// SystemConfig will add a call to the batch that upgrades the SystemConfig.
func SystemConfig(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig) error {
func SystemConfig(batch *safe.Batch, implementations superchain.ImplementationList, list superchain.AddressList, config *genesis.DeployConfig, chainConfig *superchain.ChainConfig, backend bind.ContractBackend) error {
proxyAdminABI, err := bindings.ProxyAdminMetaData.GetAbi()
if err != nil {
return err
......@@ -302,11 +348,67 @@ func SystemConfig(batch *safe.Batch, implementations superchain.ImplementationLi
return fmt.Errorf("no initialize method")
}
gasPriceOracleOverhead := new(big.Int).SetUint64(config.GasPriceOracleOverhead)
gasPriceOracleScalar := new(big.Int).SetUint64(config.GasPriceOracleScalar)
batcherHash := common.BytesToHash(config.BatchSenderAddress.Bytes())
l2GenesisBlockGasLimit := uint64(config.L2GenesisBlockGasLimit)
startBlock := new(big.Int).SetUint64(config.SystemConfigStartBlock)
// If we want to be able to override these based on the values in the config,
// the logic below will need to be updated. Right now the logic prefers the
// on chain values over the offchain values. This to maintain backwards compatibility
// in the short term.
startBlock := big.NewInt(0)
batchInboxAddress := common.HexToAddress(chainConfig.BatchInboxAddr.String())
var gasPriceOracleOverhead, gasPriceOracleScalar *big.Int
var batcherHash common.Hash
var p2pSequencerAddress, finalSystemOwner common.Address
var l2GenesisBlockGasLimit uint64
if config != nil {
gasPriceOracleOverhead = new(big.Int).SetUint64(config.GasPriceOracleOverhead)
gasPriceOracleScalar = new(big.Int).SetUint64(config.GasPriceOracleScalar)
batcherHash = common.BytesToHash(config.BatchSenderAddress.Bytes())
l2GenesisBlockGasLimit = uint64(config.L2GenesisBlockGasLimit)
startBlock = new(big.Int).SetUint64(config.SystemConfigStartBlock)
batchInboxAddress = config.BatchInboxAddress
p2pSequencerAddress = config.P2PSequencerAddress
finalSystemOwner = config.FinalSystemOwner
} else {
systemConfig, err := bindings.NewSystemConfigCaller(common.HexToAddress(chainConfig.SystemConfigAddr.String()), backend)
if err != nil {
return err
}
gasPriceOracleOverhead, err = systemConfig.Overhead(&bind.CallOpts{})
if err != nil {
return err
}
gasPriceOracleScalar, err = systemConfig.Scalar(&bind.CallOpts{})
if err != nil {
return err
}
batcherHash, err = systemConfig.BatcherHash(&bind.CallOpts{})
if err != nil {
return err
}
l2GenesisBlockGasLimit, err = systemConfig.GasLimit(&bind.CallOpts{})
if err != nil {
return err
}
// StartBlock is a new property, we want to explicitly set it to 0 if there is an error fetching it
systemConfigStartBlock, err := systemConfig.StartBlock(&bind.CallOpts{})
if err != nil && systemConfigStartBlock != nil {
startBlock = systemConfigStartBlock
}
// BatchInboxAddress is a new property, we want to set it to the offchain value if there is an error fetching it
systemConfigBatchInboxAddress, err := systemConfig.BatchInbox(&bind.CallOpts{})
if err != nil && systemConfigBatchInboxAddress != (common.Address{}) {
batchInboxAddress = systemConfigBatchInboxAddress
}
p2pSequencerAddress, err = systemConfig.UnsafeBlockSigner(&bind.CallOpts{})
if err != nil {
return err
}
finalSystemOwner, err = systemConfig.Owner(&bind.CallOpts{})
if err != nil {
return err
}
}
addresses := bindings.SystemConfigAddresses{
L1CrossDomainMessenger: common.HexToAddress(list.L1CrossDomainMessengerProxy.String()),
......@@ -317,17 +419,16 @@ func SystemConfig(batch *safe.Batch, implementations superchain.ImplementationLi
OptimismMintableERC20Factory: common.HexToAddress(list.OptimismMintableERC20FactoryProxy.String()),
}
// This is more complex
calldata, err := initialize.Inputs.PackValues([]any{
config.FinalSystemOwner,
finalSystemOwner,
gasPriceOracleOverhead,
gasPriceOracleScalar,
batcherHash,
l2GenesisBlockGasLimit,
config.P2PSequencerAddress,
p2pSequencerAddress,
genesis.DefaultResourceConfig,
startBlock,
config.BatchInboxAddress,
batchInboxAddress,
addresses,
})
if err != nil {
......
......@@ -144,7 +144,7 @@ func NewConfig(
TraceType: traceType,
TxMgrConfig: txmgr.NewCLIConfig(l1EthRpc),
TxMgrConfig: txmgr.NewCLIConfig(l1EthRpc, txmgr.DefaultChallengerFlagValues),
MetricsConfig: opmetrics.DefaultCLIConfig(),
PprofConfig: oppprof.DefaultCLIConfig(),
......
......@@ -174,7 +174,7 @@ var optionalFlags = []cli.Flag{
func init() {
optionalFlags = append(optionalFlags, oplog.CLIFlags(envVarPrefix)...)
optionalFlags = append(optionalFlags, txmgr.CLIFlags(envVarPrefix)...)
optionalFlags = append(optionalFlags, txmgr.CLIFlagsWithDefaults(envVarPrefix, txmgr.DefaultChallengerFlagValues)...)
optionalFlags = append(optionalFlags, opmetrics.CLIFlags(envVarPrefix)...)
optionalFlags = append(optionalFlags, oppprof.CLIFlags(envVarPrefix)...)
......
......@@ -152,10 +152,10 @@ func (a *Agent) tryResolveClaims(ctx context.Context) error {
resolvableClaims = append(resolvableClaims, int64(claim.ContractIndex))
}
}
a.log.Info("Resolving claims", "numClaims", len(resolvableClaims))
if len(resolvableClaims) == 0 {
return errNoResolvableClaims
}
a.log.Info("Resolving claims", "numClaims", len(resolvableClaims))
var wg sync.WaitGroup
wg.Add(len(resolvableClaims))
......
......@@ -35,10 +35,6 @@ func (s *claimSolver) NextMove(ctx context.Context, claim types.Claim, game type
if claim.Depth() == s.gameDepth {
return nil, types.ErrGameDepthReached
}
agree, err := s.agreeWithClaim(ctx, claim.ClaimData)
if err != nil {
return nil, err
}
// Before challenging this claim, first check that the move wasn't warranted.
// If the parent claim is on a dishonest path, then we would have moved against it anyways. So we don't move.
......@@ -57,6 +53,10 @@ func (s *claimSolver) NextMove(ctx context.Context, claim types.Claim, game type
}
}
agree, err := s.agreeWithClaim(ctx, claim.ClaimData)
if err != nil {
return nil, err
}
if agree {
return s.defend(ctx, claim)
} else {
......
package outputs
import (
"context"
"fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
)
var (
GetStepDataErr = fmt.Errorf("GetStepData not supported")
AbsolutePreStateErr = fmt.Errorf("AbsolutePreState not supported")
)
var _ types.TraceProvider = (*OutputTraceProvider)(nil)
type OutputRollupClient interface {
OutputAtBlock(ctx context.Context, blockNum uint64) (*eth.OutputResponse, error)
}
// OutputTraceProvider is a [types.TraceProvider] implementation that uses
// output roots for given L2 Blocks as a trace.
type OutputTraceProvider struct {
logger log.Logger
rollupClient OutputRollupClient
prestateBlock uint64
poststateBlock uint64
gameDepth uint64
}
func NewTraceProvider(ctx context.Context, logger log.Logger, rollupRpc string, gameDepth, prestateBlock, poststateBlock uint64) (*OutputTraceProvider, error) {
rollupClient, err := client.DialRollupClientWithTimeout(client.DefaultDialTimeout, logger, rollupRpc)
if err != nil {
return nil, err
}
return NewTraceProviderFromInputs(logger, rollupClient, gameDepth, prestateBlock, poststateBlock), nil
}
func NewTraceProviderFromInputs(logger log.Logger, rollupClient OutputRollupClient, gameDepth, prestateBlock, poststateBlock uint64) *OutputTraceProvider {
return &OutputTraceProvider{
logger: logger,
rollupClient: rollupClient,
prestateBlock: prestateBlock,
poststateBlock: poststateBlock,
gameDepth: gameDepth,
}
}
func (o *OutputTraceProvider) Get(ctx context.Context, pos types.Position) (common.Hash, error) {
outputBlock := pos.TraceIndex(int(o.gameDepth)) + o.prestateBlock + 1
if outputBlock > o.poststateBlock {
outputBlock = o.poststateBlock
}
output, err := o.rollupClient.OutputAtBlock(ctx, outputBlock)
if err != nil {
o.logger.Error("Failed to fetch output", "blockNumber", outputBlock, "err", err)
return common.Hash{}, err
}
return common.Hash(output.OutputRoot), nil
}
// AbsolutePreStateCommitment returns the absolute prestate at the configured prestateBlock.
func (o *OutputTraceProvider) AbsolutePreStateCommitment(ctx context.Context) (hash common.Hash, err error) {
output, err := o.rollupClient.OutputAtBlock(ctx, o.prestateBlock)
if err != nil {
o.logger.Error("Failed to fetch output", "blockNumber", o.prestateBlock, "err", err)
return common.Hash{}, err
}
return common.Hash(output.OutputRoot), nil
}
// AbsolutePreState is not supported in the [OutputTraceProvider].
func (o *OutputTraceProvider) AbsolutePreState(ctx context.Context) (preimage []byte, err error) {
return nil, AbsolutePreStateErr
}
// GetStepData is not supported in the [OutputTraceProvider].
func (o *OutputTraceProvider) GetStepData(ctx context.Context, pos types.Position) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) {
return nil, nil, nil, GetStepDataErr
}
package outputs
import (
"context"
"fmt"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
var (
prestateBlock = uint64(100)
poststateBlock = uint64(200)
gameDepth = uint64(7) // 128 leaf nodes
prestateOutputRoot = common.HexToHash("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
firstOutputRoot = common.HexToHash("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
poststateOutputRoot = common.HexToHash("0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc")
)
func TestGet(t *testing.T) {
t.Run("PrePrestateErrors", func(t *testing.T) {
provider, _ := setupWithTestData(t, 0, poststateBlock)
_, err := provider.Get(context.Background(), types.NewPosition(1, 0))
require.ErrorAs(t, fmt.Errorf("no output at block %d", 1), &err)
})
t.Run("MisconfiguredPoststateErrors", func(t *testing.T) {
provider, _ := setupWithTestData(t, 0, 0)
_, err := provider.Get(context.Background(), types.NewPosition(1, 0))
require.ErrorAs(t, fmt.Errorf("no output at block %d", 0), &err)
})
t.Run("FirstBlockAfterPrestate", func(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
value, err := provider.Get(context.Background(), types.NewPositionFromGIndex(128))
require.NoError(t, err)
require.Equal(t, firstOutputRoot, value)
})
t.Run("MissingOutputAtBlock", func(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
_, err := provider.Get(context.Background(), types.NewPositionFromGIndex(129))
require.ErrorAs(t, fmt.Errorf("no output at block %d", prestateBlock+2), &err)
})
t.Run("PostStateBlock", func(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
value, err := provider.Get(context.Background(), types.NewPositionFromGIndex(228))
require.NoError(t, err)
require.Equal(t, value, poststateOutputRoot)
})
t.Run("AfterPostStateBlock", func(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
value, err := provider.Get(context.Background(), types.NewPositionFromGIndex(229))
require.NoError(t, err)
require.Equal(t, value, poststateOutputRoot)
})
}
func TestAbsolutePreStateCommitment(t *testing.T) {
t.Run("FailedToFetchOutput", func(t *testing.T) {
provider, rollupClient := setupWithTestData(t, prestateBlock, poststateBlock)
rollupClient.errorsOnPrestateFetch = true
_, err := provider.AbsolutePreStateCommitment(context.Background())
require.ErrorAs(t, fmt.Errorf("no output at block %d", prestateBlock), &err)
})
t.Run("ReturnsCorrectPrestateOutput", func(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
value, err := provider.AbsolutePreStateCommitment(context.Background())
require.NoError(t, err)
require.Equal(t, value, prestateOutputRoot)
})
}
func TestGetStepData(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
_, _, _, err := provider.GetStepData(context.Background(), types.NewPosition(1, 0))
require.ErrorIs(t, err, GetStepDataErr)
}
func TestAbsolutePreState(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
_, err := provider.AbsolutePreState(context.Background())
require.ErrorIs(t, err, AbsolutePreStateErr)
}
func setupWithTestData(t *testing.T, prestateBlock, poststateBlock uint64) (*OutputTraceProvider, *stubRollupClient) {
rollupClient := stubRollupClient{
outputs: map[uint64]*eth.OutputResponse{
prestateBlock: {
OutputRoot: eth.Bytes32(prestateOutputRoot),
},
101: {
OutputRoot: eth.Bytes32(firstOutputRoot),
},
poststateBlock: {
OutputRoot: eth.Bytes32(poststateOutputRoot),
},
},
}
return &OutputTraceProvider{
logger: testlog.Logger(t, log.LvlInfo),
rollupClient: &rollupClient,
prestateBlock: prestateBlock,
poststateBlock: poststateBlock,
gameDepth: gameDepth,
}, &rollupClient
}
type stubRollupClient struct {
errorsOnPrestateFetch bool
outputs map[uint64]*eth.OutputResponse
}
func (s *stubRollupClient) OutputAtBlock(ctx context.Context, blockNum uint64) (*eth.OutputResponse, error) {
output, ok := s.outputs[blockNum]
if !ok || s.errorsOnPrestateFetch {
return nil, fmt.Errorf("no output at block %d", blockNum)
}
return output, nil
}
......@@ -58,22 +58,27 @@ func TestMonitorGames(t *testing.T) {
go func() {
headerNotSent := true
waitErr := wait.For(context.Background(), 100*time.Millisecond, func() (bool, error) {
for {
if len(sched.scheduled) >= 1 {
return true, nil
break
}
if mockHeadSource.sub == nil {
return false, nil
continue
}
if headerNotSent {
mockHeadSource.sub.headers <- &ethtypes.Header{
select {
case mockHeadSource.sub.headers <- &ethtypes.Header{
Number: big.NewInt(1),
}
}:
headerNotSent = false
case <-ctx.Done():
break
default:
}
}
// Just to avoid a tight loop
time.Sleep(100 * time.Millisecond)
}
return false, nil
})
require.NoError(t, waitErr)
mockHeadSource.err = fmt.Errorf("eth subscribe test error")
cancel()
}()
......@@ -94,27 +99,29 @@ func TestMonitorGames(t *testing.T) {
defer cancel()
go func() {
headerNotSent := true
waitErr := wait.For(context.Background(), 100*time.Millisecond, func() (bool, error) {
return mockHeadSource.sub != nil, nil
})
require.NoError(t, waitErr)
mockHeadSource.sub.errChan <- fmt.Errorf("test error")
waitErr = wait.For(context.Background(), 100*time.Millisecond, func() (bool, error) {
for {
if len(sched.scheduled) >= 1 {
return true, nil
break
}
if mockHeadSource.sub == nil {
return false, nil
continue
}
if headerNotSent {
mockHeadSource.sub.headers <- &ethtypes.Header{
select {
case mockHeadSource.sub.headers <- &ethtypes.Header{
Number: big.NewInt(1),
}:
case <-ctx.Done():
break
default:
}
headerNotSent = false
// Just to avoid a tight loop
time.Sleep(100 * time.Millisecond)
}
return false, nil
})
require.NoError(t, waitErr)
mockHeadSource.err = fmt.Errorf("eth subscribe test error")
cancel()
......@@ -122,7 +129,7 @@ func TestMonitorGames(t *testing.T) {
err := monitor.MonitorGames(ctx)
require.NoError(t, err)
require.Len(t, sched.scheduled, 1)
require.NotEmpty(t, sched.scheduled) // We might get more than one update scheduled.
require.Equal(t, []common.Address{addr1, addr2}, sched.scheduled[0])
})
}
......
......@@ -504,7 +504,7 @@ func TestBigL2Txs(gt *testing.T) {
if miner.l1GasPool.Gas() < tx.Gas() { // fill the L1 block with batcher txs until we run out of gas
break
}
log.Info("including batcher tx", "nonce", tx)
log.Info("including batcher tx", "nonce", tx.Nonce())
miner.IncludeTx(t, tx)
txs = txs[1:]
}
......
package actions
import (
"context"
"errors"
"time"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum-optimism/optimism/op-program/client/l2/engineapi"
"github.com/stretchr/testify/require"
......@@ -176,12 +179,22 @@ func (e *L2Engine) ActL2IncludeTx(from common.Address) Action {
return
}
i := e.engineApi.PendingIndices(from)
txs, q := e.eth.TxPool().ContentFrom(from)
require.Greaterf(t, uint64(len(txs)), i,
"no pending txs from %s, and have %d unprocessable queued txs from this account", from, len(q))
var i uint64
var txs []*types.Transaction
var q []*types.Transaction
// Wait for the tx to be in the pending tx queue
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
err := wait.For(ctx, time.Second, func() (bool, error) {
i = e.engineApi.PendingIndices(from)
txs, q = e.eth.TxPool().ContentFrom(from)
return uint64(len(txs)) > i, nil
})
require.NoError(t, err,
"no pending txs from %s, and have %d unprocessable queued txs from this account: %w", from, len(q), err)
tx := txs[i]
err := e.engineApi.IncludeTx(tx, from)
err = e.engineApi.IncludeTx(tx, from)
if errors.Is(err, engineapi.ErrNotBuildingBlock) {
t.InvalidAction(err.Error())
} else if errors.Is(err, engineapi.ErrUsesTooMuchGas) {
......
......@@ -16,6 +16,7 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-e2e/external"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
)
var (
......@@ -65,6 +66,23 @@ func init() {
testing.Init() // Register test flags before parsing
flag.Parse()
// Setup global logger
lvl := log.Lvl(EthNodeVerbosity)
if lvl < log.LvlCrit {
log.Root().SetHandler(log.DiscardHandler())
} else if lvl > log.LvlTrace { // clip to trace level
lvl = log.LvlTrace
}
// We cannot attach a testlog logger,
// because the global logger is shared between different independent parallel tests.
// Tests that write to a testlogger of another finished test fail.
h := oplog.NewLogHandler(os.Stdout, oplog.CLIConfig{
Level: lvl,
Color: false, // some CI logs do not handle colors well
Format: oplog.FormatTerminal,
})
oplog.SetGlobalLogHandler(h)
if err := allExist(l1AllocsPath, l1DeploymentsPath, deployConfigPath); err != nil {
return
}
......
......@@ -197,7 +197,7 @@ func (h *Helper) WaitForGameDataDeletion(ctx context.Context, games ...GameAddr)
if err != nil {
return false, fmt.Errorf("failed to check dir %v is deleted: %w", dir, err)
}
h.t.Errorf("Game data directory %v not yet deleted", dir)
h.t.Logf("Game data directory %v not yet deleted", dir)
return false, nil
}
return true, nil
......
package wait
import (
"context"
"fmt"
"math/big"
"time"
"github.com/ethereum/go-ethereum/core/types"
)
// BlockCaller is a subset of the [ethclient.Client] interface
// encompassing methods that query for block information.
type BlockCaller interface {
BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error)
BlockNumber(ctx context.Context) (uint64, error)
}
func ForBlock(ctx context.Context, client BlockCaller, n uint64) error {
for {
if ctx.Done() != nil {
return ctx.Err()
}
height, err := client.BlockNumber(ctx)
if err != nil {
return err
}
if height < n {
time.Sleep(500 * time.Millisecond)
continue
}
break
}
return nil
}
func ForBlockWithTimestamp(ctx context.Context, client BlockCaller, target uint64) error {
_, err := AndGet(ctx, time.Second, func() (uint64, error) {
head, err := client.BlockByNumber(ctx, nil)
if err != nil {
return 0, err
}
return head.Time(), nil
}, func(actual uint64) bool {
return actual >= target
})
return err
}
func ForNextBlock(ctx context.Context, client BlockCaller) error {
current, err := client.BlockNumber(ctx)
// Long timeout so we don't have to care what the block time is. If the test passes this will complete early anyway.
ctx, cancel := context.WithTimeout(ctx, 60*time.Second)
defer cancel()
if err != nil {
return fmt.Errorf("get starting block number: %w", err)
}
return ForBlock(ctx, client, current+1)
}
......@@ -69,52 +69,12 @@ func printDebugTrace(ctx context.Context, client *ethclient.Client, txHash commo
fmt.Printf("TxTrace: %v\n", trace)
}
func ForBlock(ctx context.Context, client *ethclient.Client, n uint64) error {
for {
height, err := client.BlockNumber(ctx)
if err != nil {
return err
}
if height < n {
time.Sleep(500 * time.Millisecond)
continue
}
break
}
return nil
}
func ForBlockWithTimestamp(ctx context.Context, client *ethclient.Client, target uint64) error {
_, err := AndGet(ctx, time.Second, func() (uint64, error) {
head, err := client.BlockByNumber(ctx, nil)
if err != nil {
return 0, err
}
return head.Time(), nil
}, func(actual uint64) bool {
return actual >= target
})
return err
}
func ForNextBlock(ctx context.Context, client *ethclient.Client) error {
current, err := client.BlockNumber(ctx)
if err != nil {
return fmt.Errorf("get starting block number: %w", err)
}
return ForBlock(ctx, client, current+1)
}
func For(ctx context.Context, rate time.Duration, cb func() (bool, error)) error {
tick := time.NewTicker(rate)
defer tick.Stop()
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-tick.C:
// Perform the first check before any waiting.
done, err := cb()
if err != nil {
return err
......@@ -122,6 +82,12 @@ func For(ctx context.Context, rate time.Duration, cb func() (bool, error)) error
if done {
return nil
}
select {
case <-ctx.Done():
return ctx.Err()
case <-tick.C:
// Allow loop to continue for next retry
}
}
}
......
......@@ -3,12 +3,6 @@ package op_e2e
import (
"os"
"testing"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-e2e/config"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum-optimism/optimism/op-service/testlog"
)
var enableParallelTesting bool = os.Getenv("OP_E2E_DISABLE_PARALLEL") != "true"
......@@ -18,12 +12,4 @@ func InitParallel(t *testing.T) {
if enableParallelTesting {
t.Parallel()
}
lvl := log.Lvl(config.EthNodeVerbosity)
if lvl < log.LvlCrit {
log.Root().SetHandler(log.DiscardHandler())
} else if lvl > log.LvlTrace { // clip to trace level
lvl = log.LvlTrace
}
h := testlog.Handler(t, lvl, log.TerminalFormat(false)) // some CI logs do not handle colors well
oplog.SetGlobalLogHandler(h)
}
......@@ -5,6 +5,7 @@ import (
"testing"
"time"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/node"
"github.com/ethereum-optimism/optimism/op-node/sources"
......@@ -34,10 +35,11 @@ func TestStopStartSequencer(t *testing.T) {
require.NoError(t, err)
require.True(t, active, "sequencer should be active")
blockBefore := latestBlock(t, l2Seq)
time.Sleep(time.Duration(cfg.DeployConfig.L2BlockTime+1) * time.Second)
blockAfter := latestBlock(t, l2Seq)
require.Greaterf(t, blockAfter, blockBefore, "Chain did not advance")
require.NoError(
t,
wait.ForNextBlock(ctx, l2Seq),
"Chain did not advance after starting sequencer",
)
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
......@@ -50,9 +52,9 @@ func TestStopStartSequencer(t *testing.T) {
require.NoError(t, err)
require.False(t, active, "sequencer should be inactive")
blockBefore = latestBlock(t, l2Seq)
blockBefore := latestBlock(t, l2Seq)
time.Sleep(time.Duration(cfg.DeployConfig.L2BlockTime+1) * time.Second)
blockAfter = latestBlock(t, l2Seq)
blockAfter := latestBlock(t, l2Seq)
require.Equal(t, blockAfter, blockBefore, "Chain advanced after stopping sequencer")
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
......@@ -66,10 +68,11 @@ func TestStopStartSequencer(t *testing.T) {
require.NoError(t, err)
require.True(t, active, "sequencer should be active again")
blockBefore = latestBlock(t, l2Seq)
time.Sleep(time.Duration(cfg.DeployConfig.L2BlockTime+1) * time.Second)
blockAfter = latestBlock(t, l2Seq)
require.Greater(t, blockAfter, blockBefore, "Chain did not advance after starting sequencer")
require.NoError(
t,
wait.ForNextBlock(ctx, l2Seq),
"Chain did not advance after starting sequencer",
)
}
func TestPersistSequencerStateWhenChanged(t *testing.T) {
......
# @eth-optimism/op-exporter
## 0.5.5
### Patch Changes
- [#7450](https://github.com/ethereum-optimism/optimism/pull/7450) [`ac90e16a7`](https://github.com/ethereum-optimism/optimism/commit/ac90e16a7f85c4f73661ae6023135c3d00421c1e) Thanks [@roninjin10](https://github.com/roninjin10)! - Updated dev dependencies related to testing that is causing audit tooling to report failures
## 0.5.4
### Patch Changes
......
{
"name": "@eth-optimism/op-exporter",
"version": "0.5.4",
"version": "0.5.5",
"private": true,
"devDependencies": {}
}
......@@ -48,17 +48,42 @@ var (
}
)
type DefaultFlagValues struct {
NumConfirmations uint64
SafeAbortNonceTooLowCount uint64
ResubmissionTimeout time.Duration
NetworkTimeout time.Duration
TxSendTimeout time.Duration
TxNotInMempoolTimeout time.Duration
ReceiptQueryInterval time.Duration
}
var (
defaultNumConfirmations = uint64(10)
defaultSafeAbortNonceTooLowCount = uint64(3)
defaultResubmissionTimeout = 48 * time.Second
defaultNetworkTimeout = 10 * time.Second
defaultTxSendTimeout = 0 * time.Second
defaultTxNotInMempoolTimeout = 2 * time.Minute
defaultReceiptQueryInterval = 12 * time.Second
DefaultBatcherFlagValues = DefaultFlagValues{
NumConfirmations: uint64(10),
SafeAbortNonceTooLowCount: uint64(3),
ResubmissionTimeout: 48 * time.Second,
NetworkTimeout: 10 * time.Second,
TxSendTimeout: 0 * time.Second,
TxNotInMempoolTimeout: 2 * time.Minute,
ReceiptQueryInterval: 12 * time.Second,
}
DefaultChallengerFlagValues = DefaultFlagValues{
NumConfirmations: uint64(3),
SafeAbortNonceTooLowCount: uint64(3),
ResubmissionTimeout: 24 * time.Second,
NetworkTimeout: 10 * time.Second,
TxSendTimeout: 2 * time.Minute,
TxNotInMempoolTimeout: 1 * time.Minute,
ReceiptQueryInterval: 12 * time.Second,
}
)
func CLIFlags(envPrefix string) []cli.Flag {
return CLIFlagsWithDefaults(envPrefix, DefaultBatcherFlagValues)
}
func CLIFlagsWithDefaults(envPrefix string, defaults DefaultFlagValues) []cli.Flag {
prefixEnvVars := func(name string) []string {
return opservice.PrefixEnvVar(envPrefix, name)
}
......@@ -81,43 +106,43 @@ func CLIFlags(envPrefix string) []cli.Flag {
&cli.Uint64Flag{
Name: NumConfirmationsFlagName,
Usage: "Number of confirmations which we will wait after sending a transaction",
Value: defaultNumConfirmations,
Value: defaults.NumConfirmations,
EnvVars: prefixEnvVars("NUM_CONFIRMATIONS"),
},
&cli.Uint64Flag{
Name: SafeAbortNonceTooLowCountFlagName,
Usage: "Number of ErrNonceTooLow observations required to give up on a tx at a particular nonce without receiving confirmation",
Value: defaultSafeAbortNonceTooLowCount,
Value: defaults.SafeAbortNonceTooLowCount,
EnvVars: prefixEnvVars("SAFE_ABORT_NONCE_TOO_LOW_COUNT"),
},
&cli.DurationFlag{
Name: ResubmissionTimeoutFlagName,
Usage: "Duration we will wait before resubmitting a transaction to L1",
Value: defaultResubmissionTimeout,
Value: defaults.ResubmissionTimeout,
EnvVars: prefixEnvVars("RESUBMISSION_TIMEOUT"),
},
&cli.DurationFlag{
Name: NetworkTimeoutFlagName,
Usage: "Timeout for all network operations",
Value: defaultNetworkTimeout,
Value: defaults.NetworkTimeout,
EnvVars: prefixEnvVars("NETWORK_TIMEOUT"),
},
&cli.DurationFlag{
Name: TxSendTimeoutFlagName,
Usage: "Timeout for sending transactions. If 0 it is disabled.",
Value: defaultTxSendTimeout,
Value: defaults.TxSendTimeout,
EnvVars: prefixEnvVars("TXMGR_TX_SEND_TIMEOUT"),
},
&cli.DurationFlag{
Name: TxNotInMempoolTimeoutFlagName,
Usage: "Timeout for aborting a tx send if the tx does not make it to the mempool.",
Value: defaultTxNotInMempoolTimeout,
Value: defaults.TxNotInMempoolTimeout,
EnvVars: prefixEnvVars("TXMGR_TX_NOT_IN_MEMPOOL_TIMEOUT"),
},
&cli.DurationFlag{
Name: ReceiptQueryIntervalFlagName,
Usage: "Frequency to poll for receipts",
Value: defaultReceiptQueryInterval,
Value: defaults.ReceiptQueryInterval,
EnvVars: prefixEnvVars("TXMGR_RECEIPT_QUERY_INTERVAL"),
},
}, client.CLIFlags(envPrefix)...)
......@@ -140,16 +165,16 @@ type CLIConfig struct {
TxNotInMempoolTimeout time.Duration
}
func NewCLIConfig(l1RPCURL string) CLIConfig {
func NewCLIConfig(l1RPCURL string, defaults DefaultFlagValues) CLIConfig {
return CLIConfig{
L1RPCURL: l1RPCURL,
NumConfirmations: defaultNumConfirmations,
SafeAbortNonceTooLowCount: defaultSafeAbortNonceTooLowCount,
ResubmissionTimeout: defaultResubmissionTimeout,
NetworkTimeout: defaultNetworkTimeout,
TxSendTimeout: defaultTxSendTimeout,
TxNotInMempoolTimeout: defaultTxNotInMempoolTimeout,
ReceiptQueryInterval: defaultReceiptQueryInterval,
NumConfirmations: defaults.NumConfirmations,
SafeAbortNonceTooLowCount: defaults.SafeAbortNonceTooLowCount,
ResubmissionTimeout: defaults.ResubmissionTimeout,
NetworkTimeout: defaults.NetworkTimeout,
TxSendTimeout: defaults.TxSendTimeout,
TxNotInMempoolTimeout: defaults.TxNotInMempoolTimeout,
ReceiptQueryInterval: defaults.ReceiptQueryInterval,
SignerCLIConfig: client.NewCLIConfig(),
}
}
......
......@@ -13,12 +13,12 @@ var (
func TestDefaultCLIOptionsMatchDefaultConfig(t *testing.T) {
cfg := configForArgs()
defaultCfg := NewCLIConfig(l1EthRpcValue)
defaultCfg := NewCLIConfig(l1EthRpcValue, DefaultBatcherFlagValues)
require.Equal(t, defaultCfg, cfg)
}
func TestDefaultConfigIsValid(t *testing.T) {
cfg := NewCLIConfig(l1EthRpcValue)
cfg := NewCLIConfig(l1EthRpcValue, DefaultBatcherFlagValues)
require.NoError(t, cfg.Check())
}
......
# @eth-optimism/drippie-mon
## 0.5.1
### Patch Changes
- [#7450](https://github.com/ethereum-optimism/optimism/pull/7450) [`ac90e16a7`](https://github.com/ethereum-optimism/optimism/commit/ac90e16a7f85c4f73661ae6023135c3d00421c1e) Thanks [@roninjin10](https://github.com/roninjin10)! - Updated dev dependencies related to testing that is causing audit tooling to report failures
- Updated dependencies [[`ac90e16a7`](https://github.com/ethereum-optimism/optimism/commit/ac90e16a7f85c4f73661ae6023135c3d00421c1e)]:
- @eth-optimism/common-ts@0.8.7
- @eth-optimism/contracts-bedrock@0.16.2
- @eth-optimism/core-utils@0.13.1
- @eth-optimism/sdk@3.1.4
## 0.5.0
### Minor Changes
......
{
"private": true,
"name": "@eth-optimism/chain-mon",
"version": "0.5.0",
"version": "0.5.1",
"description": "[Optimism] Chain monitoring services",
"main": "dist/index",
"types": "dist/index",
......@@ -53,7 +53,7 @@
"@ethersproject/abstract-provider": "^5.7.0",
"@nomiclabs/hardhat-ethers": "^2.2.3",
"@nomiclabs/hardhat-waffle": "^2.0.6",
"hardhat": "^2.17.2",
"hardhat": "^2.17.4",
"ts-node": "^10.9.1",
"tsx": "^3.12.7"
}
......
# @eth-optimism/common-ts
## 0.8.7
### Patch Changes
- [#7450](https://github.com/ethereum-optimism/optimism/pull/7450) [`ac90e16a7`](https://github.com/ethereum-optimism/optimism/commit/ac90e16a7f85c4f73661ae6023135c3d00421c1e) Thanks [@roninjin10](https://github.com/roninjin10)! - Updated dev dependencies related to testing that is causing audit tooling to report failures
- Updated dependencies [[`ac90e16a7`](https://github.com/ethereum-optimism/optimism/commit/ac90e16a7f85c4f73661ae6023135c3d00421c1e)]:
- @eth-optimism/core-utils@0.13.1
## 0.8.6
### Patch Changes
......
{
"name": "@eth-optimism/common-ts",
"version": "0.8.6",
"version": "0.8.7",
"description": "[Optimism] Advanced typescript tooling used by various services",
"main": "dist/index",
"types": "dist/index",
......@@ -54,11 +54,11 @@
"devDependencies": {
"@ethersproject/abstract-provider": "^5.7.0",
"@ethersproject/abstract-signer": "^5.7.0",
"@types/express": "^4.17.17",
"@types/morgan": "^1.9.4",
"@types/express": "^4.17.18",
"@types/morgan": "^1.9.6",
"@types/pino": "^7.0.5",
"@types/pino-multi-stream": "^5.1.4",
"chai": "^4.3.9",
"chai": "^4.3.10",
"supertest": "^6.3.3"
}
}
# @eth-optimism/contracts-bedrock
## 0.16.2
### Patch Changes
- [#7450](https://github.com/ethereum-optimism/optimism/pull/7450) [`ac90e16a7`](https://github.com/ethereum-optimism/optimism/commit/ac90e16a7f85c4f73661ae6023135c3d00421c1e) Thanks [@roninjin10](https://github.com/roninjin10)! - Updated dev dependencies related to testing that is causing audit tooling to report failures
## 0.16.1
### Patch Changes
......
......@@ -22,7 +22,7 @@
"l2GenesisRegolithTimeOffset": "0x0",
"portalGuardian": "0x45eFFbD799Ab49122eeEAB75B78D9C56A187F9A7",
"l2OutputOracleSubmissionInterval": 180,
"l2OutputOracleStartingTimestamp": -1,
"l2OutputOracleStartingTimestamp": 0,
"l2OutputOracleProposer": "0xa98B585654fC03E2fEa3FAB56E1C851E4f5c2B54",
"l2GenesisBlockGasLimit": "0x1c9c380",
"fundDevAccounts": false,
......
{
"name": "@eth-optimism/contracts-bedrock",
"version": "0.16.1",
"version": "0.16.2",
"description": "Contracts for Optimism Specs",
"license": "MIT",
"files": [
......
......@@ -580,7 +580,6 @@ contract Deploy is Deployer {
/// @notice Call from the Safe contract to the Proxy Admin's upgrade and call method
function _upgradeAndCallViaSafe(address _proxy, address _implementation, bytes memory _innerCallData) internal {
Safe safe = Safe(mustGetAddress("SystemOwnerSafe"));
address proxyAdmin = mustGetAddress("ProxyAdmin");
bytes memory data =
......
# @eth-optimism/contracts-ts
## 0.16.2
### Patch Changes
- [#7450](https://github.com/ethereum-optimism/optimism/pull/7450) [`ac90e16a7`](https://github.com/ethereum-optimism/optimism/commit/ac90e16a7f85c4f73661ae6023135c3d00421c1e) Thanks [@roninjin10](https://github.com/roninjin10)! - Updated dev dependencies related to testing that is causing audit tooling to report failures
{
"name": "@eth-optimism/contracts-ts",
"version": "0.15.0",
"version": "0.16.2",
"description": "TypeScript interface for Contracts Bedrock",
"license": "MIT",
"repository": {
......@@ -55,11 +55,11 @@
"@wagmi/cli": "^1.5.2",
"@wagmi/core": "^1.3.8",
"abitype": "^0.9.3",
"glob": "^10.3.3",
"glob": "^10.3.10",
"isomorphic-fetch": "^3.0.0",
"jest-dom": "link:@types/@testing-library/jest-dom",
"jsdom": "^22.1.0",
"tsup": "^7.1.0",
"tsup": "^7.2.0",
"typescript": "^5.2.2",
"vite": "^4.4.6",
"vitest": "^0.34.2"
......
# @eth-optimism/core-utils
## 0.13.1
### Patch Changes
- [#7450](https://github.com/ethereum-optimism/optimism/pull/7450) [`ac90e16a7`](https://github.com/ethereum-optimism/optimism/commit/ac90e16a7f85c4f73661ae6023135c3d00421c1e) Thanks [@roninjin10](https://github.com/roninjin10)! - Updated dev dependencies related to testing that is causing audit tooling to report failures
## 0.13.0
### Minor Changes
......
{
"name": "@eth-optimism/core-utils",
"version": "0.13.0",
"version": "0.13.1",
"description": "[Optimism] Core typescript utilities",
"main": "dist/index",
"types": "dist/index",
......@@ -44,7 +44,7 @@
"@ethersproject/properties": "^5.7.0",
"@ethersproject/rlp": "^5.7.0",
"@ethersproject/web": "^5.7.1",
"chai": "^4.3.9",
"chai": "^4.3.10",
"ethers": "^5.7.2",
"node-fetch": "^2.6.7"
},
......
# @eth-optimism/fee-estimation
## 0.15.3
### Patch Changes
- [#7450](https://github.com/ethereum-optimism/optimism/pull/7450) [`ac90e16a7`](https://github.com/ethereum-optimism/optimism/commit/ac90e16a7f85c4f73661ae6023135c3d00421c1e) Thanks [@roninjin10](https://github.com/roninjin10)! - Updated dev dependencies related to testing that is causing audit tooling to report failures
## 0.15.2
### Patch Changes
......
{
"name": "@eth-optimism/fee-estimation",
"version": "0.15.2",
"version": "0.15.3",
"description": "Lightweight library for doing OP-Chain gas estimation",
"license": "MIT",
"repository": {
......@@ -42,7 +42,7 @@
"isomorphic-fetch": "^3.0.0",
"jest-dom": "link:@types/@testing-library/jest-dom",
"jsdom": "^22.1.0",
"tsup": "^7.1.0",
"tsup": "^7.2.0",
"typescript": "^5.2.2",
"viem": "^1.3.1",
"vite": "^4.4.6",
......
# @eth-optimism/sdk
## 3.1.4
### Patch Changes
- [#7450](https://github.com/ethereum-optimism/optimism/pull/7450) [`ac90e16a7`](https://github.com/ethereum-optimism/optimism/commit/ac90e16a7f85c4f73661ae6023135c3d00421c1e) Thanks [@roninjin10](https://github.com/roninjin10)! - Updated dev dependencies related to testing that is causing audit tooling to report failures
- Updated dependencies [[`ac90e16a7`](https://github.com/ethereum-optimism/optimism/commit/ac90e16a7f85c4f73661ae6023135c3d00421c1e)]:
- @eth-optimism/contracts-bedrock@0.16.2
- @eth-optimism/core-utils@0.13.1
## 3.1.3
### Patch Changes
......
{
"name": "@eth-optimism/sdk",
"version": "3.1.3",
"version": "3.1.4",
"description": "[Optimism] Tools for working with Optimism",
"main": "dist/index",
"types": "dist/index",
......@@ -43,12 +43,12 @@
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@types/chai": "^4.3.6",
"@types/chai-as-promised": "^7.1.5",
"@types/mocha": "^10.0.1",
"@types/mocha": "^10.0.2",
"@types/node": "^20.5.0",
"chai-as-promised": "^7.1.1",
"ethereum-waffle": "^4.0.10",
"ethers": "^5.7.2",
"hardhat": "^2.17.2",
"hardhat": "^2.17.4",
"hardhat-deploy": "^0.11.4",
"isomorphic-fetch": "^3.0.0",
"mocha": "^10.2.0",
......
# @eth-optimism/web3.js-plugin
## 0.1.3
### Patch Changes
- [#7450](https://github.com/ethereum-optimism/optimism/pull/7450) [`ac90e16a7`](https://github.com/ethereum-optimism/optimism/commit/ac90e16a7f85c4f73661ae6023135c3d00421c1e) Thanks [@roninjin10](https://github.com/roninjin10)! - Updated dev dependencies related to testing that is causing audit tooling to report failures
## 0.1.2
### Patch Changes
......
{
"name": "@eth-optimism/web3.js-plugin",
"version": "0.1.2",
"version": "0.1.3",
"description": "A Web3.js plugin for doing OP-Chain gas estimation",
"license": "MIT",
"repository": {
......@@ -33,7 +33,7 @@
},
"devDependencies": {
"@eth-optimism/contracts-ts": "workspace:^",
"@swc/core": "^1.3.76",
"@swc/core": "^1.3.90",
"@vitest/coverage-istanbul": "^0.34.1",
"tsup": "^7.2.0",
"typescript": "^5.2.2",
......
......@@ -17,7 +17,7 @@ importers:
devDependencies:
'@babel/eslint-parser':
specifier: ^7.18.2
version: 7.22.15(@babel/core@7.22.10)(eslint@8.49.0)
version: 7.22.15(@babel/core@7.22.10)(eslint@8.50.0)
'@changesets/changelog-github':
specifier: ^0.4.8
version: 0.4.8
......@@ -31,20 +31,20 @@ importers:
specifier: ^7.1.4
version: 7.1.5
'@types/mocha':
specifier: ^10.0.1
version: 10.0.1
specifier: ^10.0.2
version: 10.0.2
'@types/node':
specifier: ^20.5.3
version: 20.6.3
'@typescript-eslint/eslint-plugin':
specifier: ^6.7.0
version: 6.7.0(@typescript-eslint/parser@6.7.3)(eslint@8.49.0)(typescript@5.2.2)
version: 6.7.0(@typescript-eslint/parser@6.7.3)(eslint@8.50.0)(typescript@5.2.2)
'@typescript-eslint/parser':
specifier: ^6.7.0
version: 6.7.3(eslint@8.49.0)(typescript@5.2.2)
version: 6.7.3(eslint@8.50.0)(typescript@5.2.2)
chai:
specifier: ^4.3.9
version: 4.3.9
specifier: ^4.3.10
version: 4.3.10
depcheck:
specifier: ^1.4.3
version: 1.4.6
......@@ -52,38 +52,38 @@ importers:
specifier: ^2.2.0
version: 2.2.1
eslint:
specifier: ^8.43.0
version: 8.49.0
specifier: ^8.50.0
version: 8.50.0
eslint-config-prettier:
specifier: ^8.3.0
version: 8.3.0(eslint@8.49.0)
version: 8.3.0(eslint@8.50.0)
eslint-config-standard:
specifier: ^16.0.3
version: 16.0.3(eslint-plugin-import@2.28.1)(eslint-plugin-node@11.1.0)(eslint-plugin-promise@5.2.0)(eslint@8.49.0)
version: 16.0.3(eslint-plugin-import@2.28.1)(eslint-plugin-node@11.1.0)(eslint-plugin-promise@5.2.0)(eslint@8.50.0)
eslint-plugin-import:
specifier: ^2.26.0
version: 2.28.1(@typescript-eslint/parser@6.7.3)(eslint@8.49.0)
version: 2.28.1(@typescript-eslint/parser@6.7.3)(eslint@8.50.0)
eslint-plugin-jsdoc:
specifier: ^35.1.2
version: 35.5.1(eslint@8.49.0)
version: 35.5.1(eslint@8.50.0)
eslint-plugin-node:
specifier: ^11.1.0
version: 11.1.0(eslint@8.49.0)
version: 11.1.0(eslint@8.50.0)
eslint-plugin-prefer-arrow:
specifier: ^1.2.3
version: 1.2.3(eslint@8.49.0)
version: 1.2.3(eslint@8.50.0)
eslint-plugin-prettier:
specifier: ^4.0.0
version: 4.2.1(eslint-config-prettier@8.3.0)(eslint@8.49.0)(prettier@2.8.8)
version: 4.2.1(eslint-config-prettier@8.3.0)(eslint@8.50.0)(prettier@2.8.8)
eslint-plugin-promise:
specifier: ^5.1.0
version: 5.2.0(eslint@8.49.0)
version: 5.2.0(eslint@8.50.0)
eslint-plugin-react:
specifier: ^7.24.0
version: 7.33.2(eslint@8.49.0)
version: 7.33.2(eslint@8.50.0)
eslint-plugin-unicorn:
specifier: ^48.0.1
version: 48.0.1(eslint@8.49.0)
version: 48.0.1(eslint@8.50.0)
husky:
specifier: ^8.0.3
version: 8.0.3
......@@ -115,8 +115,8 @@ importers:
specifier: ^1.0.0-beta.13
version: 1.0.0-beta.18(prettier@2.8.8)
rimraf:
specifier: ^5.0.1
version: 5.0.4
specifier: ^5.0.5
version: 5.0.5
ts-mocha:
specifier: ^10.0.0
version: 10.0.0(mocha@10.2.0)
......@@ -130,7 +130,7 @@ importers:
devDependencies:
tsup:
specifier: ^7.2.0
version: 7.2.0(@swc/core@1.3.76)(typescript@5.2.2)
version: 7.2.0(@swc/core@1.3.90)(typescript@5.2.2)
vitest:
specifier: ^0.34.4
version: 0.34.4
......@@ -159,7 +159,7 @@ importers:
version: 5.0.0
chai-as-promised:
specifier: ^7.1.1
version: 7.1.1(chai@4.3.9)
version: 7.1.1(chai@4.3.10)
dateformat:
specifier: ^4.5.1
version: 4.5.1
......@@ -175,13 +175,13 @@ importers:
version: 5.7.0
'@nomiclabs/hardhat-ethers':
specifier: ^2.2.3
version: 2.2.3(ethers@5.7.2)(hardhat@2.17.2)
version: 2.2.3(ethers@5.7.2)(hardhat@2.17.4)
'@nomiclabs/hardhat-waffle':
specifier: ^2.0.6
version: 2.0.6(@nomiclabs/hardhat-ethers@2.2.3)(@types/sinon-chai@3.2.5)(ethereum-waffle@4.0.10)(ethers@5.7.2)(hardhat@2.17.2)
version: 2.0.6(@nomiclabs/hardhat-ethers@2.2.3)(@types/sinon-chai@3.2.5)(ethereum-waffle@4.0.10)(ethers@5.7.2)(hardhat@2.17.4)
hardhat:
specifier: ^2.17.2
version: 2.17.2(ts-node@10.9.1)(typescript@5.2.2)
specifier: ^2.17.4
version: 2.17.4(ts-node@10.9.1)(typescript@5.2.2)
ts-node:
specifier: ^10.9.1
version: 10.9.1(@types/node@20.7.0)(typescript@5.2.2)
......@@ -247,11 +247,11 @@ importers:
specifier: ^5.7.0
version: 5.7.0
'@types/express':
specifier: ^4.17.17
version: 4.17.17
specifier: ^4.17.18
version: 4.17.18
'@types/morgan':
specifier: ^1.9.4
version: 1.9.4
specifier: ^1.9.6
version: 1.9.6
'@types/pino':
specifier: ^7.0.5
version: 7.0.5
......@@ -259,8 +259,8 @@ importers:
specifier: ^5.1.4
version: 5.1.4
chai:
specifier: ^4.3.9
version: 4.3.9
specifier: ^4.3.10
version: 4.3.10
supertest:
specifier: ^6.3.3
version: 6.3.3
......@@ -323,8 +323,8 @@ importers:
specifier: ^0.9.3
version: 0.9.3(typescript@5.2.2)(zod@3.22.0)
glob:
specifier: ^10.3.3
version: 10.3.3
specifier: ^10.3.10
version: 10.3.10
isomorphic-fetch:
specifier: ^3.0.0
version: 3.0.0
......@@ -335,8 +335,8 @@ importers:
specifier: ^22.1.0
version: 22.1.0
tsup:
specifier: ^7.1.0
version: 7.1.0(typescript@5.2.2)
specifier: ^7.2.0
version: 7.2.0(@swc/core@1.3.90)(typescript@5.2.2)
typescript:
specifier: ^5.2.2
version: 5.2.2
......@@ -383,8 +383,8 @@ importers:
specifier: ^5.7.1
version: 5.7.1
chai:
specifier: ^4.3.9
version: 4.3.9
specifier: ^4.3.10
version: 4.3.10
ethers:
specifier: ^5.7.2
version: 5.7.2
......@@ -426,8 +426,8 @@ importers:
specifier: ^22.1.0
version: 22.1.0
tsup:
specifier: ^7.1.0
version: 7.1.0(typescript@5.2.2)
specifier: ^7.2.0
version: 7.2.0(@swc/core@1.3.90)(typescript@5.2.2)
typescript:
specifier: ^5.2.2
version: 5.2.2
......@@ -473,10 +473,10 @@ importers:
version: 5.7.0
'@nomiclabs/hardhat-ethers':
specifier: ^2.2.3
version: 2.2.3(ethers@5.7.2)(hardhat@2.17.2)
version: 2.2.3(ethers@5.7.2)(hardhat@2.17.4)
'@nomiclabs/hardhat-waffle':
specifier: ^2.0.1
version: 2.0.1(@nomiclabs/hardhat-ethers@2.2.3)(ethereum-waffle@4.0.10)(ethers@5.7.2)(hardhat@2.17.2)
version: 2.0.1(@nomiclabs/hardhat-ethers@2.2.3)(ethereum-waffle@4.0.10)(ethers@5.7.2)(hardhat@2.17.4)
'@types/chai':
specifier: ^4.3.6
version: 4.3.6
......@@ -484,14 +484,14 @@ importers:
specifier: ^7.1.5
version: 7.1.5
'@types/mocha':
specifier: ^10.0.1
version: 10.0.1
specifier: ^10.0.2
version: 10.0.2
'@types/node':
specifier: ^20.5.0
version: 20.5.0
chai-as-promised:
specifier: ^7.1.1
version: 7.1.1(chai@4.3.9)
version: 7.1.1(chai@4.3.10)
ethereum-waffle:
specifier: ^4.0.10
version: 4.0.10(@ensdomains/ens@0.4.5)(@ensdomains/resolver@0.2.4)(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typescript@5.2.2)
......@@ -499,8 +499,8 @@ importers:
specifier: ^5.7.2
version: 5.7.2
hardhat:
specifier: ^2.17.2
version: 2.17.2(ts-node@10.9.1)(typescript@5.2.2)
specifier: ^2.17.4
version: 2.17.4(ts-node@10.9.1)(typescript@5.2.2)
hardhat-deploy:
specifier: ^0.11.4
version: 0.11.10
......@@ -551,14 +551,14 @@ importers:
specifier: workspace:^
version: link:../contracts-ts
'@swc/core':
specifier: ^1.3.76
version: 1.3.76
specifier: ^1.3.90
version: 1.3.90
'@vitest/coverage-istanbul':
specifier: ^0.34.1
version: 0.34.1(vitest@0.34.1)
tsup:
specifier: ^7.2.0
version: 7.2.0(@swc/core@1.3.76)(typescript@5.2.2)
version: 7.2.0(@swc/core@1.3.90)(typescript@5.2.2)
typescript:
specifier: ^5.2.2
version: 5.2.2
......@@ -632,7 +632,7 @@ packages:
- supports-color
dev: true
/@babel/eslint-parser@7.22.15(@babel/core@7.22.10)(eslint@8.49.0):
/@babel/eslint-parser@7.22.15(@babel/core@7.22.10)(eslint@8.50.0):
resolution: {integrity: sha512-yc8OOBIQk1EcRrpizuARSQS0TWAcOMpEJ1aafhNznaeYkeL+OhqnDObGFylB8ka8VFF/sZc+S4RzHyO+3LjQxg==}
engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0}
peerDependencies:
......@@ -641,7 +641,7 @@ packages:
dependencies:
'@babel/core': 7.22.10
'@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1
eslint: 8.49.0
eslint: 8.50.0
eslint-visitor-keys: 2.1.0
semver: 6.3.1
dev: true
......@@ -1775,16 +1775,6 @@ packages:
dev: true
optional: true
/@eslint-community/eslint-utils@4.4.0(eslint@8.49.0):
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
dependencies:
eslint: 8.49.0
eslint-visitor-keys: 3.4.3
dev: true
/@eslint-community/eslint-utils@4.4.0(eslint@8.50.0):
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
......@@ -1817,11 +1807,6 @@ packages:
- supports-color
dev: true
/@eslint/js@8.49.0:
resolution: {integrity: sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
/@eslint/js@8.50.0:
resolution: {integrity: sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
......@@ -1863,7 +1848,7 @@ packages:
'@ethersproject/transactions': 5.7.0
'@ethersproject/web': 5.7.1
bufio: 1.2.0
chai: 4.3.9
chai: 4.3.10
transitivePeerDependencies:
- bufferutil
- utf-8-validate
......@@ -2939,17 +2924,17 @@ packages:
'@nomicfoundation/solidity-analyzer-win32-x64-msvc': 0.1.1
dev: true
/@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2)(hardhat@2.17.2):
/@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2)(hardhat@2.17.4):
resolution: {integrity: sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg==}
peerDependencies:
ethers: ^5.0.0
hardhat: ^2.0.0
dependencies:
ethers: 5.7.2
hardhat: 2.17.2(ts-node@10.9.1)(typescript@5.2.2)
hardhat: 2.17.4(ts-node@10.9.1)(typescript@5.2.2)
dev: true
/@nomiclabs/hardhat-waffle@2.0.1(@nomiclabs/hardhat-ethers@2.2.3)(ethereum-waffle@4.0.10)(ethers@5.7.2)(hardhat@2.17.2):
/@nomiclabs/hardhat-waffle@2.0.1(@nomiclabs/hardhat-ethers@2.2.3)(ethereum-waffle@4.0.10)(ethers@5.7.2)(hardhat@2.17.4):
resolution: {integrity: sha512-2YR2V5zTiztSH9n8BYWgtv3Q+EL0N5Ltm1PAr5z20uAY4SkkfylJ98CIqt18XFvxTD5x4K2wKBzddjV9ViDAZQ==}
peerDependencies:
'@nomiclabs/hardhat-ethers': ^2.0.0
......@@ -2957,15 +2942,15 @@ packages:
ethers: ^5.0.0
hardhat: ^2.0.0
dependencies:
'@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.17.2)
'@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.17.4)
'@types/sinon-chai': 3.2.5
'@types/web3': 1.0.19
ethereum-waffle: 4.0.10(@ensdomains/ens@0.4.5)(@ensdomains/resolver@0.2.4)(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typescript@5.2.2)
ethers: 5.7.2
hardhat: 2.17.2(ts-node@10.9.1)(typescript@5.2.2)
hardhat: 2.17.4(ts-node@10.9.1)(typescript@5.2.2)
dev: true
/@nomiclabs/hardhat-waffle@2.0.6(@nomiclabs/hardhat-ethers@2.2.3)(@types/sinon-chai@3.2.5)(ethereum-waffle@4.0.10)(ethers@5.7.2)(hardhat@2.17.2):
/@nomiclabs/hardhat-waffle@2.0.6(@nomiclabs/hardhat-ethers@2.2.3)(@types/sinon-chai@3.2.5)(ethereum-waffle@4.0.10)(ethers@5.7.2)(hardhat@2.17.4):
resolution: {integrity: sha512-+Wz0hwmJGSI17B+BhU/qFRZ1l6/xMW82QGXE/Gi+WTmwgJrQefuBs1lIf7hzQ1hLk6hpkvb/zwcNkpVKRYTQYg==}
peerDependencies:
'@nomiclabs/hardhat-ethers': ^2.0.0
......@@ -2974,11 +2959,11 @@ packages:
ethers: ^5.0.0
hardhat: ^2.0.0
dependencies:
'@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.17.2)
'@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.17.4)
'@types/sinon-chai': 3.2.5
ethereum-waffle: 4.0.10(@ensdomains/ens@0.4.5)(@ensdomains/resolver@0.2.4)(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typescript@5.2.2)
ethers: 5.7.2
hardhat: 2.17.2(ts-node@10.9.1)(typescript@5.2.2)
hardhat: 2.17.4(ts-node@10.9.1)(typescript@5.2.2)
dev: true
/@nrwl/nx-cloud@16.4.0:
......@@ -3526,8 +3511,8 @@ packages:
'@stablelib/random': 1.0.2
'@stablelib/wipe': 1.0.1
/@swc/core-darwin-arm64@1.3.76:
resolution: {integrity: sha512-ovviEhZ/1E81Z9OGrO0ivLWk4VCa3I3ZzM+cd3gugglRRwVwtlIaoIYqY5S3KiCAupDd1+UCl5X7Vbio7a/V8g==}
/@swc/core-darwin-arm64@1.3.90:
resolution: {integrity: sha512-he0w74HvcoufE6CZrB/U/VGVbc7021IQvYrn1geMACnq/OqMBqjdczNtdNfJAy87LZ4AOUjHDKEIjsZZu7o8nQ==}
engines: {node: '>=10'}
cpu: [arm64]
os: [darwin]
......@@ -3535,8 +3520,8 @@ packages:
dev: true
optional: true
/@swc/core-darwin-x64@1.3.76:
resolution: {integrity: sha512-tcySTDqs0SHCebtW35sCdcLWsmTEo7bEwx0gNL/spetqVT9fpFi6qU8qcnt7i2KaZHbeNl9g1aadu+Yrni+GzA==}
/@swc/core-darwin-x64@1.3.90:
resolution: {integrity: sha512-hKNM0Ix0qMlAamPe0HUfaAhQVbZEL5uK6Iw8v9ew0FtVB4v7EifQ9n41wh+yCj0CjcHBPEBbQU0P6mNTxJu/RQ==}
engines: {node: '>=10'}
cpu: [x64]
os: [darwin]
......@@ -3544,8 +3529,8 @@ packages:
dev: true
optional: true
/@swc/core-linux-arm-gnueabihf@1.3.76:
resolution: {integrity: sha512-apgzpGWy1AwoMF4urAAASsAjE7rEzZFIF+p6utuxhS7cNHzE0AyEVDYJbo+pzBdlZ8orBdzzsHtFwoEgKOjebA==}
/@swc/core-linux-arm-gnueabihf@1.3.90:
resolution: {integrity: sha512-HumvtrqTWE8rlFuKt7If0ZL7145H/jVc4AeziVjcd+/ajpqub7IyfrLCYd5PmKMtfeSVDMsxjG0BJ0HLRxrTJA==}
engines: {node: '>=10'}
cpu: [arm]
os: [linux]
......@@ -3553,8 +3538,8 @@ packages:
dev: true
optional: true
/@swc/core-linux-arm64-gnu@1.3.76:
resolution: {integrity: sha512-c3c0zz6S0eludqidDpuqbadE0WT3OZczyQxe9Vw8lFFXES85mvNGtwYzyGK2o7TICpsuHrndwDIoYpmpWk879g==}
/@swc/core-linux-arm64-gnu@1.3.90:
resolution: {integrity: sha512-tA7DqCS7YCwngwXZQeqQhhMm8BbydpaABw8Z/EDQ7KPK1iZ1rNjZw+aWvSpmNmEGmH1RmQ9QDS9mGRDp0faAeg==}
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
......@@ -3562,8 +3547,8 @@ packages:
dev: true
optional: true
/@swc/core-linux-arm64-musl@1.3.76:
resolution: {integrity: sha512-Is3bpq7F2qtlnkzEeOD6HIZJPpOmu3q6c82lKww90Q0NnrlSluVMozTHJgwVoFZyizH7uLnk0LuNcEAWLnmJIw==}
/@swc/core-linux-arm64-musl@1.3.90:
resolution: {integrity: sha512-p2Vtid5BZA36fJkNUwk5HP+HJlKgTru+Ghna7pRe45ghKkkRIUk3fhkgudEvfKfhT+3AvP+GTVQ+T9k0gc9S8w==}
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
......@@ -3571,8 +3556,8 @@ packages:
dev: true
optional: true
/@swc/core-linux-x64-gnu@1.3.76:
resolution: {integrity: sha512-iwCeRzd9oSvUzqt7nU6p/ztceAWfnO9XVxBn502R5gs6QCBbE1HCKrWHDO77aKPK7ss+0NcIGHvXTd9L8/wRzw==}
/@swc/core-linux-x64-gnu@1.3.90:
resolution: {integrity: sha512-J6pDtWaulYGXuANERuvv4CqmUbZOQrRZBCRQGZQJ6a86RWpesZqckBelnYx48wYmkgvMkF95Y3xbI3WTfoSHzw==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
......@@ -3580,8 +3565,8 @@ packages:
dev: true
optional: true
/@swc/core-linux-x64-musl@1.3.76:
resolution: {integrity: sha512-a671g4tW8kyFeuICsgq4uB9ukQfiIyXJT4V6YSnmqhCTz5mazWuDxZ5wKnx/1g5nXTl+U5cWH2TZaCJatp4GKA==}
/@swc/core-linux-x64-musl@1.3.90:
resolution: {integrity: sha512-3Gh6EA3+0K+l3MqnRON7h5bZ32xLmfcVM6QiHHJ9dBttq7YOEeEoMOCdIPMaQxJmK1VfLgZCsPYRd66MhvUSkw==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
......@@ -3589,8 +3574,8 @@ packages:
dev: true
optional: true
/@swc/core-win32-arm64-msvc@1.3.76:
resolution: {integrity: sha512-+swEFtjdMezS0vKUhJC3psdSDtOJGY5pEOt4e8XOPvn7aQpKQ9LfF49XVtIwDSk5SGuWtVoLFzkSY3reWUJCyg==}
/@swc/core-win32-arm64-msvc@1.3.90:
resolution: {integrity: sha512-BNaw/iJloDyaNOFV23Sr53ULlnbmzSoerTJ10v0TjSZOEIpsS0Rw6xOK1iI0voDJnRXeZeWRSxEC9DhefNtN/g==}
engines: {node: '>=10'}
cpu: [arm64]
os: [win32]
......@@ -3598,8 +3583,8 @@ packages:
dev: true
optional: true
/@swc/core-win32-ia32-msvc@1.3.76:
resolution: {integrity: sha512-5CqwAykpGBJ3PqGLOlWGLGIPpBAG1IwWVDUfro3hhjQ7XJxV5Z1aQf5V5OJ90HJVtrEAVx2xx59UV/Dh081LOg==}
/@swc/core-win32-ia32-msvc@1.3.90:
resolution: {integrity: sha512-SiyTethWAheE/JbxXCukAAciU//PLcmVZ2ME92MRuLMLmOhrwksjbaa7ukj9WEF3LWrherhSqTXnpj3VC1l/qw==}
engines: {node: '>=10'}
cpu: [ia32]
os: [win32]
......@@ -3607,8 +3592,8 @@ packages:
dev: true
optional: true
/@swc/core-win32-x64-msvc@1.3.76:
resolution: {integrity: sha512-CiMpWLLlR3Cew9067E7XxaLBwYYJ90r9EhGSO6V1pvYSWj7ET/Ppmtj1ZhzPJMqRXAP6xflfl5R5o4ee1m4WLA==}
/@swc/core-win32-x64-msvc@1.3.90:
resolution: {integrity: sha512-OpWAW5ljKcPJ3SQ0pUuKqYfwXv7ssIhVgrH9XP9ONtdgXKWZRL9hqJQkcL55FARw/gDjKanoCM47wsTNQL+ZZA==}
engines: {node: '>=10'}
cpu: [x64]
os: [win32]
......@@ -3616,8 +3601,8 @@ packages:
dev: true
optional: true
/@swc/core@1.3.76:
resolution: {integrity: sha512-aYYTA2aVYkwJAZepQXtPnkUthhOfn8qd6rsh+lrJxonFrjmpI7RHt2tMDVTXP6XDX7fvnvrVtT1bwZfmBFPh0Q==}
/@swc/core@1.3.90:
resolution: {integrity: sha512-wptBxP4PldOnhmyDVj8qUcn++GRqyw1qc9wOTGtPNHz8cpuTfdfIgYGlhI4La0UYqecuaaIfLfokyuNePOMHPg==}
engines: {node: '>=10'}
requiresBuild: true
peerDependencies:
......@@ -3625,17 +3610,28 @@ packages:
peerDependenciesMeta:
'@swc/helpers':
optional: true
dependencies:
'@swc/counter': 0.1.1
'@swc/types': 0.1.5
optionalDependencies:
'@swc/core-darwin-arm64': 1.3.76
'@swc/core-darwin-x64': 1.3.76
'@swc/core-linux-arm-gnueabihf': 1.3.76
'@swc/core-linux-arm64-gnu': 1.3.76
'@swc/core-linux-arm64-musl': 1.3.76
'@swc/core-linux-x64-gnu': 1.3.76
'@swc/core-linux-x64-musl': 1.3.76
'@swc/core-win32-arm64-msvc': 1.3.76
'@swc/core-win32-ia32-msvc': 1.3.76
'@swc/core-win32-x64-msvc': 1.3.76
'@swc/core-darwin-arm64': 1.3.90
'@swc/core-darwin-x64': 1.3.90
'@swc/core-linux-arm-gnueabihf': 1.3.90
'@swc/core-linux-arm64-gnu': 1.3.90
'@swc/core-linux-arm64-musl': 1.3.90
'@swc/core-linux-x64-gnu': 1.3.90
'@swc/core-linux-x64-musl': 1.3.90
'@swc/core-win32-arm64-msvc': 1.3.90
'@swc/core-win32-ia32-msvc': 1.3.90
'@swc/core-win32-x64-msvc': 1.3.90
dev: true
/@swc/counter@0.1.1:
resolution: {integrity: sha512-xVRaR4u9hcYjFvcSg71Lz5Bo4//CyjAAfMxa7UsaDSYxAshflUkVJWiyVWrfxC59z2kP1IzI4/1BEpnhI9o3Mw==}
dev: true
/@swc/types@0.1.5:
resolution: {integrity: sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==}
dev: true
/@tanstack/query-core@4.29.25:
......@@ -3917,8 +3913,8 @@ packages:
'@types/send': 0.17.1
dev: true
/@types/express@4.17.17:
resolution: {integrity: sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==}
/@types/express@4.17.18:
resolution: {integrity: sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ==}
dependencies:
'@types/body-parser': 1.19.1
'@types/express-serve-static-core': 4.17.35
......@@ -3992,14 +3988,14 @@ packages:
'@types/node': 20.7.0
dev: true
/@types/mocha@10.0.1:
resolution: {integrity: sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==}
/@types/mocha@10.0.2:
resolution: {integrity: sha512-NaHL0+0lLNhX6d9rs+NSt97WH/gIlRHmszXbQ/8/MV/eVcFNdeJ/GYhrFuUc8K7WuPhRhTSdMkCp8VMzhUq85w==}
dev: true
/@types/morgan@1.9.4:
resolution: {integrity: sha512-cXoc4k+6+YAllH3ZHmx4hf7La1dzUk6keTR4bF4b4Sc0mZxU/zK4wO7l+ZzezXm/jkYj/qC+uYGZrarZdIVvyQ==}
/@types/morgan@1.9.6:
resolution: {integrity: sha512-xfKogz5WcKww2DAiVT9zxMgrqQt+Shq8tDVeLT+otoj6dJnkRkyJxMF51mHtUc3JCPKGk5x1EBU0buuGpfftlQ==}
dependencies:
'@types/node': 20.6.3
'@types/node': 20.7.0
dev: true
/@types/ms@0.7.31:
......@@ -4227,7 +4223,7 @@ packages:
- supports-color
dev: true
/@typescript-eslint/eslint-plugin@6.7.0(@typescript-eslint/parser@6.7.3)(eslint@8.49.0)(typescript@5.2.2):
/@typescript-eslint/eslint-plugin@6.7.0(@typescript-eslint/parser@6.7.3)(eslint@8.50.0)(typescript@5.2.2):
resolution: {integrity: sha512-gUqtknHm0TDs1LhY12K2NA3Rmlmp88jK9Tx8vGZMfHeNMLE3GH2e9TRub+y+SOjuYgtOmok+wt1AyDPZqxbNag==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
......@@ -4239,13 +4235,13 @@ packages:
optional: true
dependencies:
'@eslint-community/regexpp': 4.6.2
'@typescript-eslint/parser': 6.7.3(eslint@8.49.0)(typescript@5.2.2)
'@typescript-eslint/parser': 6.7.3(eslint@8.50.0)(typescript@5.2.2)
'@typescript-eslint/scope-manager': 6.7.0
'@typescript-eslint/type-utils': 6.7.0(eslint@8.49.0)(typescript@5.2.2)
'@typescript-eslint/utils': 6.7.0(eslint@8.49.0)(typescript@5.2.2)
'@typescript-eslint/type-utils': 6.7.0(eslint@8.50.0)(typescript@5.2.2)
'@typescript-eslint/utils': 6.7.0(eslint@8.50.0)(typescript@5.2.2)
'@typescript-eslint/visitor-keys': 6.7.0
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.49.0
eslint: 8.50.0
graphemer: 1.4.0
ignore: 5.2.4
natural-compare: 1.4.0
......@@ -4277,7 +4273,7 @@ packages:
- supports-color
dev: true
/@typescript-eslint/parser@6.7.3(eslint@8.49.0)(typescript@5.2.2):
/@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2):
resolution: {integrity: sha512-TlutE+iep2o7R8Lf+yoer3zU6/0EAUc8QIBB3GYBc1KGz4c4TRm83xwXUZVPlZ6YCLss4r77jbu6j3sendJoiQ==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
......@@ -4292,7 +4288,7 @@ packages:
'@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2)
'@typescript-eslint/visitor-keys': 6.7.3
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.49.0
eslint: 8.50.0
typescript: 5.2.2
transitivePeerDependencies:
- supports-color
......@@ -4322,26 +4318,6 @@ packages:
'@typescript-eslint/visitor-keys': 6.7.3
dev: true
/@typescript-eslint/type-utils@6.7.0(eslint@8.49.0)(typescript@5.2.2):
resolution: {integrity: sha512-f/QabJgDAlpSz3qduCyQT0Fw7hHpmhOzY/Rv6zO3yO+HVIdPfIWhrQoAyG+uZVtWAIS85zAyzgAFfyEr+MgBpg==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
eslint: ^7.0.0 || ^8.0.0
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@typescript-eslint/typescript-estree': 6.7.0(typescript@5.2.2)
'@typescript-eslint/utils': 6.7.0(eslint@8.49.0)(typescript@5.2.2)
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.49.0
ts-api-utils: 1.0.1(typescript@5.2.2)
typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/type-utils@6.7.0(eslint@8.50.0)(typescript@5.2.2):
resolution: {integrity: sha512-f/QabJgDAlpSz3qduCyQT0Fw7hHpmhOzY/Rv6zO3yO+HVIdPfIWhrQoAyG+uZVtWAIS85zAyzgAFfyEr+MgBpg==}
engines: {node: ^16.0.0 || >=18.0.0}
......@@ -4440,25 +4416,6 @@ packages:
- supports-color
dev: true
/@typescript-eslint/utils@6.7.0(eslint@8.49.0)(typescript@5.2.2):
resolution: {integrity: sha512-MfCq3cM0vh2slSikQYqK2Gq52gvOhe57vD2RM3V4gQRZYX4rDPnKLu5p6cm89+LJiGlwEXU8hkYxhqqEC/V3qA==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
eslint: ^7.0.0 || ^8.0.0
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0)
'@types/json-schema': 7.0.12
'@types/semver': 7.5.0
'@typescript-eslint/scope-manager': 6.7.0
'@typescript-eslint/types': 6.7.0
'@typescript-eslint/typescript-estree': 6.7.0(typescript@5.2.2)
eslint: 8.49.0
semver: 7.5.4
transitivePeerDependencies:
- supports-color
- typescript
dev: true
/@typescript-eslint/utils@6.7.0(eslint@8.50.0)(typescript@5.2.2):
resolution: {integrity: sha512-MfCq3cM0vh2slSikQYqK2Gq52gvOhe57vD2RM3V4gQRZYX4rDPnKLu5p6cm89+LJiGlwEXU8hkYxhqqEC/V3qA==}
engines: {node: ^16.0.0 || >=18.0.0}
......@@ -4539,7 +4496,7 @@ packages:
dependencies:
'@vitest/spy': 0.34.1
'@vitest/utils': 0.34.1
chai: 4.3.9
chai: 4.3.10
dev: true
/@vitest/expect@0.34.2:
......@@ -4547,7 +4504,7 @@ packages:
dependencies:
'@vitest/spy': 0.34.2
'@vitest/utils': 0.34.2
chai: 4.3.9
chai: 4.3.10
dev: true
/@vitest/expect@0.34.4:
......@@ -4555,7 +4512,7 @@ packages:
dependencies:
'@vitest/spy': 0.34.4
'@vitest/utils': 0.34.4
chai: 4.3.9
chai: 4.3.10
dev: true
/@vitest/runner@0.34.1:
......@@ -6363,26 +6320,25 @@ packages:
resolution: {integrity: sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==}
dev: true
/chai-as-promised@7.1.1(chai@4.3.9):
/chai-as-promised@7.1.1(chai@4.3.10):
resolution: {integrity: sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==}
peerDependencies:
chai: '>= 2.1.2 < 5'
dependencies:
chai: 4.3.9
chai: 4.3.10
check-error: 1.0.2
/chai@4.3.8:
resolution: {integrity: sha512-vX4YvVVtxlfSZ2VecZgFUTU5qPCYsobVI2O9FmwEXBhDigYGQA6jRXCycIs1yJnnWbZ6/+a2zNIF5DfVCcJBFQ==}
/chai@4.3.10:
resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==}
engines: {node: '>=4'}
dependencies:
assertion-error: 1.1.0
check-error: 1.0.2
check-error: 1.0.3
deep-eql: 4.1.3
get-func-name: 2.0.0
get-func-name: 2.0.2
loupe: 2.3.6
pathval: 1.1.1
type-detect: 4.0.8
dev: true
/chai@4.3.9:
resolution: {integrity: sha512-tH8vhfA1CfuYMkALXj+wmZcqiwqOfshU9Gry+NYiiLqIddrobkBhALv6XD4yDz68qapphYI4vSaqhqAdThCAAA==}
......@@ -6391,10 +6347,11 @@ packages:
assertion-error: 1.1.0
check-error: 1.0.3
deep-eql: 4.1.3
get-func-name: 2.0.0
get-func-name: 2.0.2
loupe: 2.3.6
pathval: 1.1.1
type-detect: 4.0.8
dev: true
/chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
......@@ -7603,16 +7560,16 @@ packages:
engines: {node: '>=10'}
dev: true
/eslint-config-prettier@8.3.0(eslint@8.49.0):
/eslint-config-prettier@8.3.0(eslint@8.50.0):
resolution: {integrity: sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==}
hasBin: true
peerDependencies:
eslint: '>=7.0.0'
dependencies:
eslint: 8.49.0
eslint: 8.50.0
dev: true
/eslint-config-standard@16.0.3(eslint-plugin-import@2.28.1)(eslint-plugin-node@11.1.0)(eslint-plugin-promise@5.2.0)(eslint@8.49.0):
/eslint-config-standard@16.0.3(eslint-plugin-import@2.28.1)(eslint-plugin-node@11.1.0)(eslint-plugin-promise@5.2.0)(eslint@8.50.0):
resolution: {integrity: sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==}
peerDependencies:
eslint: ^7.12.1
......@@ -7620,10 +7577,10 @@ packages:
eslint-plugin-node: ^11.1.0
eslint-plugin-promise: ^4.2.1 || ^5.0.0
dependencies:
eslint: 8.49.0
eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.3)(eslint@8.49.0)
eslint-plugin-node: 11.1.0(eslint@8.49.0)
eslint-plugin-promise: 5.2.0(eslint@8.49.0)
eslint: 8.50.0
eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.3)(eslint@8.50.0)
eslint-plugin-node: 11.1.0(eslint@8.50.0)
eslint-plugin-promise: 5.2.0(eslint@8.50.0)
dev: true
/eslint-import-resolver-node@0.3.9:
......@@ -7636,7 +7593,7 @@ packages:
- supports-color
dev: true
/eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-node@0.3.9)(eslint@8.49.0):
/eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-node@0.3.9)(eslint@8.50.0):
resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
engines: {node: '>=4'}
peerDependencies:
......@@ -7657,26 +7614,26 @@ packages:
eslint-import-resolver-webpack:
optional: true
dependencies:
'@typescript-eslint/parser': 6.7.3(eslint@8.49.0)(typescript@5.2.2)
'@typescript-eslint/parser': 6.7.3(eslint@8.50.0)(typescript@5.2.2)
debug: 3.2.7
eslint: 8.49.0
eslint: 8.50.0
eslint-import-resolver-node: 0.3.9
transitivePeerDependencies:
- supports-color
dev: true
/eslint-plugin-es@3.0.1(eslint@8.49.0):
/eslint-plugin-es@3.0.1(eslint@8.50.0):
resolution: {integrity: sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==}
engines: {node: '>=8.10.0'}
peerDependencies:
eslint: '>=4.19.1'
dependencies:
eslint: 8.49.0
eslint: 8.50.0
eslint-utils: 2.1.0
regexpp: 3.2.0
dev: true
/eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.3)(eslint@8.49.0):
/eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.3)(eslint@8.50.0):
resolution: {integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==}
engines: {node: '>=4'}
peerDependencies:
......@@ -7686,16 +7643,16 @@ packages:
'@typescript-eslint/parser':
optional: true
dependencies:
'@typescript-eslint/parser': 6.7.3(eslint@8.49.0)(typescript@5.2.2)
'@typescript-eslint/parser': 6.7.3(eslint@8.50.0)(typescript@5.2.2)
array-includes: 3.1.6
array.prototype.findlastindex: 1.2.2
array.prototype.flat: 1.3.1
array.prototype.flatmap: 1.3.1
debug: 3.2.7
doctrine: 2.1.0
eslint: 8.49.0
eslint: 8.50.0
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-node@0.3.9)(eslint@8.49.0)
eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-node@0.3.9)(eslint@8.50.0)
has: 1.0.3
is-core-module: 2.13.0
is-glob: 4.0.3
......@@ -7711,7 +7668,7 @@ packages:
- supports-color
dev: true
/eslint-plugin-jsdoc@35.5.1(eslint@8.49.0):
/eslint-plugin-jsdoc@35.5.1(eslint@8.50.0):
resolution: {integrity: sha512-pPYPWtsykwVEue1tYEyoppBj4dgF7XicF67tLLLraY6RQYBq7qMKjUHji19+hfiTtYKKBD0YfeK8hgjPAE5viw==}
engines: {node: '>=12'}
peerDependencies:
......@@ -7720,7 +7677,7 @@ packages:
'@es-joy/jsdoccomment': 0.9.0-alpha.1
comment-parser: 1.1.6-beta.0
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.49.0
eslint: 8.50.0
esquery: 1.4.0
jsdoc-type-pratt-parser: 1.1.1
lodash: 4.17.21
......@@ -7731,14 +7688,14 @@ packages:
- supports-color
dev: true
/eslint-plugin-node@11.1.0(eslint@8.49.0):
/eslint-plugin-node@11.1.0(eslint@8.50.0):
resolution: {integrity: sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==}
engines: {node: '>=8.10.0'}
peerDependencies:
eslint: '>=5.16.0'
dependencies:
eslint: 8.49.0
eslint-plugin-es: 3.0.1(eslint@8.49.0)
eslint: 8.50.0
eslint-plugin-es: 3.0.1(eslint@8.50.0)
eslint-utils: 2.1.0
ignore: 5.2.4
minimatch: 3.1.2
......@@ -7746,15 +7703,15 @@ packages:
semver: 6.3.1
dev: true
/eslint-plugin-prefer-arrow@1.2.3(eslint@8.49.0):
/eslint-plugin-prefer-arrow@1.2.3(eslint@8.50.0):
resolution: {integrity: sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==}
peerDependencies:
eslint: '>=2.0.0'
dependencies:
eslint: 8.49.0
eslint: 8.50.0
dev: true
/eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.3.0)(eslint@8.49.0)(prettier@2.8.8):
/eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.3.0)(eslint@8.50.0)(prettier@2.8.8):
resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==}
engines: {node: '>=12.0.0'}
peerDependencies:
......@@ -7765,22 +7722,22 @@ packages:
eslint-config-prettier:
optional: true
dependencies:
eslint: 8.49.0
eslint-config-prettier: 8.3.0(eslint@8.49.0)
eslint: 8.50.0
eslint-config-prettier: 8.3.0(eslint@8.50.0)
prettier: 2.8.8
prettier-linter-helpers: 1.0.0
dev: true
/eslint-plugin-promise@5.2.0(eslint@8.49.0):
/eslint-plugin-promise@5.2.0(eslint@8.50.0):
resolution: {integrity: sha512-SftLb1pUG01QYq2A/hGAWfDRXqYD82zE7j7TopDOyNdU+7SvvoXREls/+PRTY17vUXzXnZA/zfnyKgRH6x4JJw==}
engines: {node: ^10.12.0 || >=12.0.0}
peerDependencies:
eslint: ^7.0.0
dependencies:
eslint: 8.49.0
eslint: 8.50.0
dev: true
/eslint-plugin-react@7.33.2(eslint@8.49.0):
/eslint-plugin-react@7.33.2(eslint@8.50.0):
resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==}
engines: {node: '>=4'}
peerDependencies:
......@@ -7791,7 +7748,7 @@ packages:
array.prototype.tosorted: 1.1.1
doctrine: 2.1.0
es-iterator-helpers: 1.0.13
eslint: 8.49.0
eslint: 8.50.0
estraverse: 5.3.0
jsx-ast-utils: 3.2.0
minimatch: 3.1.2
......@@ -7805,17 +7762,17 @@ packages:
string.prototype.matchall: 4.0.8
dev: true
/eslint-plugin-unicorn@48.0.1(eslint@8.49.0):
/eslint-plugin-unicorn@48.0.1(eslint@8.50.0):
resolution: {integrity: sha512-FW+4r20myG/DqFcCSzoumaddKBicIPeFnTrifon2mWIzlfyvzwyqZjqVP7m4Cqr/ZYisS2aiLghkUWaPg6vtCw==}
engines: {node: '>=16'}
peerDependencies:
eslint: '>=8.44.0'
dependencies:
'@babel/helper-validator-identifier': 7.22.5
'@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0)
'@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0)
ci-info: 3.8.0
clean-regexp: 1.0.0
eslint: 8.49.0
eslint: 8.50.0
esquery: 1.5.0
indent-string: 4.0.0
is-builtin-module: 3.2.1
......@@ -7867,52 +7824,6 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
/eslint@8.49.0:
resolution: {integrity: sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
hasBin: true
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0)
'@eslint-community/regexpp': 4.6.2
'@eslint/eslintrc': 2.1.2
'@eslint/js': 8.49.0
'@humanwhocodes/config-array': 0.11.11
'@humanwhocodes/module-importer': 1.0.1
'@nodelib/fs.walk': 1.2.8
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.3
debug: 4.3.4(supports-color@8.1.1)
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.2.2
eslint-visitor-keys: 3.4.3
espree: 9.6.1
esquery: 1.5.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
file-entry-cache: 6.0.1
find-up: 5.0.0
glob-parent: 6.0.2
globals: 13.21.0
graphemer: 1.4.0
ignore: 5.2.4
imurmurhash: 0.1.4
is-glob: 4.0.3
is-path-inside: 3.0.3
js-yaml: 4.1.0
json-stable-stringify-without-jsonify: 1.0.1
levn: 0.4.1
lodash.merge: 4.6.2
minimatch: 3.1.2
natural-compare: 1.4.0
optionator: 0.9.3
strip-ansi: 6.0.1
text-table: 0.2.0
transitivePeerDependencies:
- supports-color
dev: true
/eslint@8.50.0:
resolution: {integrity: sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
......@@ -8827,9 +8738,6 @@ packages:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
/get-func-name@2.0.0:
resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==}
/get-func-name@2.0.2:
resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}
......@@ -8889,25 +8797,13 @@ packages:
is-glob: 4.0.3
dev: true
/glob@10.3.3:
resolution: {integrity: sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==}
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
dependencies:
foreground-child: 3.1.1
jackspeak: 2.2.1
minimatch: 9.0.3
minipass: 7.0.2
path-scurry: 1.10.1
dev: true
/glob@10.3.7:
resolution: {integrity: sha512-wCMbE1m9Nx5yD9LYtgsVWq5VhHlk5WzJirw594qZR6AIvQYuHrdDtIktUVjQItalD53y7dqoedu9xP0u0WaxIQ==}
/glob@10.3.10:
resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
dependencies:
foreground-child: 3.1.1
jackspeak: 2.2.1
jackspeak: 2.3.6
minimatch: 9.0.3
minipass: 7.0.3
path-scurry: 1.10.1
......@@ -9094,8 +8990,8 @@ packages:
- utf-8-validate
dev: true
/hardhat@2.17.2(ts-node@10.9.1)(typescript@5.2.2):
resolution: {integrity: sha512-oUv40jBeHw0dKpbyQ+iH9cmNMziweLoTW3MnkNxJ2Gc0KGLrQR/1n4vV4xY60zn2LdmRgnwPqy3CgtY0mfwIIA==}
/hardhat@2.17.4(ts-node@10.9.1)(typescript@5.2.2):
resolution: {integrity: sha512-YTyHjVc9s14CY/O7Dbtzcr/92fcz6AzhrMaj6lYsZpYPIPLzOrFCZHHPxfGQB6FiE6IPNE0uJaAbr7zGF79goA==}
hasBin: true
peerDependencies:
ts-node: '*'
......@@ -9914,8 +9810,8 @@ packages:
reflect.getprototypeof: 1.0.3
dev: true
/jackspeak@2.2.1:
resolution: {integrity: sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==}
/jackspeak@2.3.6:
resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
engines: {node: '>=14'}
dependencies:
'@isaacs/cliui': 8.0.2
......@@ -11127,11 +11023,6 @@ packages:
yallist: 4.0.0
dev: true
/minipass@7.0.2:
resolution: {integrity: sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA==}
engines: {node: '>=16 || 14 >=14.17'}
dev: true
/minipass@7.0.3:
resolution: {integrity: sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==}
engines: {node: '>=16 || 14 >=14.17'}
......@@ -12859,12 +12750,12 @@ packages:
glob: 7.2.3
dev: true
/rimraf@5.0.4:
resolution: {integrity: sha512-rizQI/o/YAMM1us0Zyax0uRfIK39XR52EAjjOi0fzMolpGp0onj6CWzBAXuOx6+6Xi9Rgi0d9tUZojhJerLUmQ==}
/rimraf@5.0.5:
resolution: {integrity: sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==}
engines: {node: '>=14'}
hasBin: true
dependencies:
glob: 10.3.7
glob: 10.3.10
dev: true
/ripemd160@2.0.2:
......@@ -13534,7 +13425,7 @@ packages:
dev: true
/strip-hex-prefix@1.0.0:
resolution: {integrity: sha1-DF8VX+8RUTczd96du1iNoFUA428=}
resolution: {integrity: sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==}
engines: {node: '>=6.5.0', npm: '>=3'}
dependencies:
is-hex-prefixed: 1.0.0
......@@ -14026,43 +13917,7 @@ packages:
resolution: {integrity: sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==}
dev: true
/tsup@7.1.0(typescript@5.2.2):
resolution: {integrity: sha512-mazl/GRAk70j8S43/AbSYXGgvRP54oQeX8Un4iZxzATHt0roW0t6HYDVZIXMw0ZQIpvr1nFMniIVnN5186lW7w==}
engines: {node: '>=16.14'}
hasBin: true
peerDependencies:
'@swc/core': ^1
postcss: ^8.4.12
typescript: '>=4.1.0'
peerDependenciesMeta:
'@swc/core':
optional: true
postcss:
optional: true
typescript:
optional: true
dependencies:
bundle-require: 4.0.1(esbuild@0.18.15)
cac: 6.7.14
chokidar: 3.5.3
debug: 4.3.4(supports-color@8.1.1)
esbuild: 0.18.15
execa: 5.1.1
globby: 11.1.0
joycon: 3.1.1
postcss-load-config: 4.0.1
resolve-from: 5.0.0
rollup: 3.26.3
source-map: 0.8.0-beta.0
sucrase: 3.34.0
tree-kill: 1.2.2
typescript: 5.2.2
transitivePeerDependencies:
- supports-color
- ts-node
dev: true
/tsup@7.2.0(@swc/core@1.3.76)(typescript@5.2.2):
/tsup@7.2.0(@swc/core@1.3.90)(typescript@5.2.2):
resolution: {integrity: sha512-vDHlczXbgUvY3rWvqFEbSqmC1L7woozbzngMqTtL2PGBODTtWlRwGDDawhvWzr5c1QjKe4OAKqJGfE1xeXUvtQ==}
engines: {node: '>=16.14'}
hasBin: true
......@@ -14078,7 +13933,7 @@ packages:
typescript:
optional: true
dependencies:
'@swc/core': 1.3.76
'@swc/core': 1.3.90
bundle-require: 4.0.1(esbuild@0.18.15)
cac: 6.7.14
chokidar: 3.5.3
......@@ -14796,7 +14651,7 @@ packages:
acorn: 8.10.0
acorn-walk: 8.2.0
cac: 6.7.14
chai: 4.3.9
chai: 4.3.10
debug: 4.3.4(supports-color@8.1.1)
local-pkg: 0.4.3
magic-string: 0.30.1
......@@ -14861,7 +14716,7 @@ packages:
acorn: 8.10.0
acorn-walk: 8.2.0
cac: 6.7.14
chai: 4.3.9
chai: 4.3.10
debug: 4.3.4(supports-color@8.1.1)
jsdom: 22.1.0
local-pkg: 0.4.3
......@@ -14927,7 +14782,7 @@ packages:
acorn: 8.10.0
acorn-walk: 8.2.0
cac: 6.7.14
chai: 4.3.8
chai: 4.3.9
debug: 4.3.4(supports-color@8.1.1)
local-pkg: 0.4.3
magic-string: 0.30.1
......
......@@ -18,6 +18,7 @@
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 1,
"links": [],
"liveNow": false,
"panels": [
......@@ -31,21 +32,58 @@
"color": {
"mode": "thresholds"
},
"custom": {
"axisCenteredZero": true,
"axisColorMode": "series",
"axisGridShow": false,
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "stepAfter",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"decimals": 0,
"displayName": "Number of Transactions (positive number = success, negative = failures)",
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red",
"color": "text",
"value": null
},
{
"color": "yellow",
"value": 1
"color": "red",
"value": -1
},
{
"color": "text",
"value": 0
},
{
"color": "green",
"value": 4
"value": 1
}
]
}
......@@ -53,93 +91,29 @@
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"h": 8,
"w": 24,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"orientation": "auto",
"reduceOptions": {
"legend": {
"calcs": [
"lastNotNull"
"last"
],
"fields": "",
"values": false
},
"showThresholdLabels": false,
"showThresholdMarkers": true
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"pluginVersion": "10.1.2",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"disableTextWrap": false,
"editorMode": "builder",
"expr": "metamask_tx_success",
"fullMetaSearch": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "A",
"useBackend": false
}
"timezone": [
"browser"
],
"title": "Successful Transaction Since Last Failure",
"type": "gauge"
},
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "text",
"value": null
},
{
"color": "red",
"value": 1
}
]
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 6,
"y": 0
},
"id": 2,
"options": {
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"showThresholdLabels": false,
"showThresholdMarkers": true
},
"pluginVersion": "10.1.2",
"targets": [
{
"datasource": {
......@@ -148,7 +122,7 @@
},
"disableTextWrap": false,
"editorMode": "builder",
"expr": "metamask_tx_failure",
"expr": "metamask_self_send",
"fullMetaSearch": false,
"includeNullMetadata": true,
"instant": false,
......@@ -158,8 +132,8 @@
"useBackend": false
}
],
"title": "Failed Transactions Since Last Success",
"type": "gauge"
"title": "Self Transferring on OP Goerli (positive number = success, negative = failures)",
"type": "timeseries"
}
],
"refresh": "5s",
......@@ -170,7 +144,7 @@
"list": []
},
"time": {
"from": "now-6h",
"from": "now-30m",
"to": "now"
},
"timepicker": {},
......
......@@ -15,8 +15,6 @@ export default defineConfig({
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
......
......@@ -6,8 +6,7 @@ import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts'
import { testWithSynpress } from './testWithSynpressUtil'
import {
incrementMetamaskTxCounter,
setMetamaskTxCounter,
incrementSelfSendTxGauge,
} from './prometheusUtils'
const env = z.object({
......@@ -24,18 +23,31 @@ const expectedSender =
: mnemonicToAccount(
env.METAMASK_SECRET_WORDS_OR_PRIVATEKEY as string
).address.toLowerCase()
const expectedRecipient = '0x8fcfbe8953433fd1f2e8375ee99057833e4e1e9e'
const expectedRecipient = expectedSender
let sharedPage: Page
let wasSuccessful: boolean
let handledFailure: boolean
test.describe.configure({ mode: 'serial' })
test.beforeAll(() => {
wasSuccessful = false
handledFailure = false
})
test.afterAll(async () => {
// This is handling failure scenarios such as Playwright timeouts
// where are not able to catch and respond to an error.
if (!wasSuccessful && !handledFailure) {
await incrementSelfSendTxGauge(false)
}
await sharedPage.close()
})
testWithSynpress('Setup wallet and dApp', async ({ page }) => {
console.log('Seting up wallet and dApp...')
console.log('Setting up wallet and dApp...')
sharedPage = page
await sharedPage.goto('http://localhost:9011')
console.log('Setup wallet and dApp')
......@@ -66,8 +78,8 @@ testWithSynpress('Add OP Goerli network', async () => {
try {
await expect(sharedPage.locator('#chainId')).toHaveText(expectedChainId)
} catch (error) {
await setMetamaskTxCounter(true, 0)
await incrementMetamaskTxCounter(false)
await incrementSelfSendTxGauge(false)
handledFailure = true
throw error
}
console.log('Added OP Goerli network')
......@@ -81,15 +93,15 @@ test(`Connect wallet with ${expectedSender}`, async () => {
try {
await expect(sharedPage.locator('#accounts')).toHaveText(expectedSender)
} catch (error) {
await setMetamaskTxCounter(true, 0)
await incrementMetamaskTxCounter(false)
await incrementSelfSendTxGauge(false)
handledFailure = true
throw error
}
console.log(`Connected wallet with ${expectedSender}`)
})
test('Send an EIP-1559 transaciton and verfiy success', async () => {
console.log('Sending an EIP-1559 transaciton and verfiy success...')
test('Send an EIP-1559 transaction and verify success', async () => {
console.log('Sending an EIP-1559 transaction and verify success...')
const expectedTransferAmount = '0x1'
const expectedTxType = '0x2'
......@@ -120,7 +132,7 @@ test('Send an EIP-1559 transaciton and verfiy success', async () => {
// Waiting for RPC response to be populated on the page
await sharedPage.waitForTimeout(2_000)
const transaction = JSON.parse(
const transactionReceipt = JSON.parse(
(await sharedPage.locator('body > main').innerText()).replace(
'Response: ',
''
......@@ -128,13 +140,13 @@ test('Send an EIP-1559 transaciton and verfiy success', async () => {
)
try {
expect(transaction.status).toBe('0x1')
await setMetamaskTxCounter(false, 0)
await incrementMetamaskTxCounter(true)
expect(transactionReceipt.status).toBe('0x1')
wasSuccessful = true
await incrementSelfSendTxGauge(true)
} catch (error) {
await setMetamaskTxCounter(true, 0)
await incrementMetamaskTxCounter(false)
await incrementSelfSendTxGauge(false)
handledFailure = true
throw error
}
console.log('Sent an EIP-1559 transaciton and verfied success')
console.log('Sent an EIP-1559 transaction and verified success')
})
import 'dotenv/config'
import { z } from 'zod'
import { Counter, Pushgateway } from 'prom-client'
import { Gauge, Pushgateway } from 'prom-client'
const env = z
.object({
......@@ -9,21 +9,15 @@ const env = z
})
.parse(process.env)
const txSuccessMetricName = 'metamask_tx_success'
const txFailureMetricName = 'metamask_tx_failure'
const selfSendTransactionMetricName = 'metamask_self_send'
const txSuccessCounter = new Counter({
name: txSuccessMetricName,
help: 'A counter signifying the number of successful transactions sent with Metamask since last failure',
})
const txFailureCounter = new Counter({
name: txFailureMetricName,
help: 'A counter signifying the number of failed transactions sent with Metamask since last successful transaction',
const selfSendGauge = new Gauge({
name: selfSendTransactionMetricName,
help: 'A gauge signifying the number of transactions sent with Metamask',
})
export const getMetamaskTxCounterValue = async (isSuccess: boolean) => {
const metricName = isSuccess ? txSuccessMetricName : txFailureMetricName
const prometheusMetricQuery = `${env.PROMETHEUS_SERVER_URL}/api/v1/query?query=${metricName}`
export const getSelfSendGaugeValue = async () => {
const prometheusMetricQuery = `${env.PROMETHEUS_SERVER_URL}/api/v1/query?query=${selfSendTransactionMetricName}`
const response = await fetch(prometheusMetricQuery)
if (!response.ok) {
......@@ -37,12 +31,12 @@ export const getMetamaskTxCounterValue = async (isSuccess: boolean) => {
// [
// {
// metric: {
// __name__: 'metamask_tx_success',
// exported_job: 'metamask_tx_count',
// __name__: 'metamask_self_send',
// exported_job: 'metamask_self_send_tx_count',
// instance: 'pushgateway:9091',
// job: 'pushgateway'
// },
// value: [ 1695250414.474, '0' ]
// value: [ 1695847795.646, '-1' ]
// }
// ]
try {
......@@ -66,7 +60,9 @@ export const getMetamaskTxCounterValue = async (isSuccess: boolean) => {
if (
error.message === "Cannot read properties of undefined (reading 'value')"
) {
console.warn(`No data found for metric ${metricName} in Prometheus`)
console.warn(
`No data found for metric ${selfSendTransactionMetricName} in Prometheus`
)
return undefined
}
......@@ -74,28 +70,26 @@ export const getMetamaskTxCounterValue = async (isSuccess: boolean) => {
}
}
export const setMetamaskTxCounter = async (
isSuccess: boolean,
valueToSetTo: number
) => {
const metricName = isSuccess ? txSuccessMetricName : txFailureMetricName
const txCounter = isSuccess ? txSuccessCounter : txFailureCounter
txCounter.reset()
console.log(`Setting ${metricName} to ${valueToSetTo}`)
txCounter.inc(valueToSetTo)
export const setSelfSendTxGauge = async (valueToSetTo: number) => {
console.log(`Setting ${selfSendTransactionMetricName} to ${valueToSetTo}`)
selfSendGauge.set(valueToSetTo)
const pushGateway = new Pushgateway(env.PROMETHEUS_PUSHGATEWAY_URL)
await pushGateway.pushAdd({ jobName: 'metamask_tx_count' })
await pushGateway.pushAdd({ jobName: 'metamask_self_send_tx_count' })
}
export const incrementMetamaskTxCounter = async (isSuccess: boolean) => {
const metricName = isSuccess ? txSuccessMetricName : txFailureMetricName
const currentMetricValue = (await getMetamaskTxCounterValue(true)) ?? 0
export const incrementSelfSendTxGauge = async (isSuccess: boolean) => {
const currentMetricValue = (await getSelfSendGaugeValue()) ?? 0
let newMetricValue: number
if (isSuccess) {
newMetricValue = currentMetricValue >= 0 ? currentMetricValue + 1 : 1
} else {
newMetricValue = currentMetricValue < 0 ? currentMetricValue - 1 : -1
}
console.log(
`Current value of ${metricName} is ${currentMetricValue}, incrementing to ${
currentMetricValue + 1
}`
`Current value of ${selfSendTransactionMetricName} is ${currentMetricValue}, incrementing to ${newMetricValue}`
)
await setMetamaskTxCounter(isSuccess, currentMetricValue + 1)
await setSelfSendTxGauge(newMetricValue)
}
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