Commit 2833f2b8 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

Merge pull request #2264 from ethereum-optimism/develop

Develop -> Master
parents 03f1cfda 82465db9
---
'@eth-optimism/integration-tests': patch
'@eth-optimism/l2geth': patch
'@eth-optimism/contracts': patch
---
Add support for system addresses
---
'@eth-optimism/replica-healthcheck': patch
---
Fix bug in replica healthcheck dockerfile
---
'@eth-optimism/contracts': patch
---
Add a fetch batches hardhat task
---
'@eth-optimism/integration-tests': patch
---
Add test coverage for zlib compressed batches
---
'@eth-optimism/data-transport-layer': patch
---
Enable typed batch support
---
'@eth-optimism/batch-submitter': patch
---
Update to allow for zlib compressed batches
---
'@eth-optimism/contracts': patch
---
Remove yargs as a contracts dependency (unused)
---
'@eth-optimism/sdk': patch
---
Add a function for waiting for a particular message status
---
'@eth-optimism/batch-submitter-service': patch
---
Move L2 dial logic out of bss-core to avoid l2geth dependency
---
'@eth-optimism/integration-tests': patch
---
Replaces contract references in integration tests with SDK CrossChainMessenger objects.
---
'@eth-optimism/core-utils': patch
---
Add toJSON methods to the batch primitives
---
'@eth-optimism/batch-submitter-service': patch
---
Enable the usage of typed batches and type 0 zlib compressed batches
---
'@eth-optimism/core-utils': patch
---
Update batch serialization with typed batches and zlib compression
......@@ -2,7 +2,7 @@ version: 2.1
orbs:
gcp-gke: circleci/gcp-gke@1.3.0
slack: circleci/slack@4.5.1
slack-fail-post-step: &slack-fail-post-step
slack-nightly-build-fail-post-step: &slack-nightly-build-fail-post-step
post-steps:
- slack/notify:
channel: $SLACK_DEFAULT_CHANNEL
......@@ -154,7 +154,7 @@ jobs:
--env PRIVATE_KEY=$NIGHTLY_ITESTS_PRIVKEY \
--env L1_URL=https://nightly-l1.optimism-stacks.net \
--env L2_URL=https://nightly-l2.optimism-stacks.net \
--env ADDRESS_MANAGER=0x22D4E211ef8704f2ca2d6dfdB32125E2530ACE3e \
--env ADDRESS_MANAGER=0xfcA6De8Db94C4d99bD5a7f5De1bb7A039265Ac42 \
--env L2_CHAINID=69 \
--env MOCHA_BAIL=true \
--env MOCHA_TIMEOUT=300000 \
......@@ -190,6 +190,66 @@ workflows:
- run-itests-nightly:
context:
- optimism
post-steps:
- slack/notify:
channel: $SLACK_DEFAULT_CHANNEL
event: fail
custom: |
{
"text": "",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "🔴 Nightly integration tests failed!"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Job"
},
"url": "${CIRCLE_BUILD_URL}"
}
]
}
]
}
- slack/notify:
channel: $SLACK_DEFAULT_CHANNEL
event: pass
custom: |
{
"text": "",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "✅ Nightly integration tests passed."
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Job"
},
"url": "${CIRCLE_BUILD_URL}"
}
]
}
]
}
nightly:
triggers:
- schedule:
......@@ -203,47 +263,47 @@ workflows:
context:
- optimism
- slack
<<: *slack-fail-post-step
<<: *slack-nightly-build-fail-post-step
- build-batch-submitter:
context:
- optimism
- slack
<<: *slack-fail-post-step
<<: *slack-nightly-build-fail-post-step
- build-deployer:
context:
- optimism
- slack
<<: *slack-fail-post-step
<<: *slack-nightly-build-fail-post-step
- build-l2geth:
context:
- optimism
- slack
<<: *slack-fail-post-step
<<: *slack-nightly-build-fail-post-step
- build-gas-oracle:
context:
- optimism
- slack
<<: *slack-fail-post-step
<<: *slack-nightly-build-fail-post-step
- build-integration-tests:
context:
- optimism
- slack
<<: *slack-fail-post-step
<<: *slack-nightly-build-fail-post-step
- build-go-batch-submitter:
context:
- optimism
- slack
<<: *slack-fail-post-step
<<: *slack-nightly-build-fail-post-step
- build-proxyd:
context:
- optimism
- slack
<<: *slack-fail-post-step
<<: *slack-nightly-build-fail-post-step
- deploy-nightly:
context:
- optimism
- slack
<<: *slack-fail-post-step
<<: *slack-nightly-build-fail-post-step
requires:
- build-dtl
- build-batch-submitter
......
......@@ -14,3 +14,4 @@ l2geth/signer/fourbyte
l2geth/cmd/puppeth
l2geth/cmd/clef
go/gas-oracle/gas-oracle
go/batch-submitter/batch-submitter
......@@ -10,8 +10,8 @@ on:
- '*rc'
- 'regenesis/*'
pull_request:
paths:
- 'go/batch-submitter/**'
branches:
- '*'
workflow_dispatch:
defaults:
......
......@@ -10,8 +10,8 @@ on:
- '*rc'
- 'regenesis/*'
pull_request:
paths:
- 'go/bss-core/**'
branches:
- '*'
workflow_dispatch:
defaults:
......
......@@ -10,8 +10,8 @@ on:
- '*rc'
- 'regenesis/*'
pull_request:
paths:
- 'go/gas-oracle/**'
branches:
- '*'
workflow_dispatch:
defaults:
......
......@@ -4,15 +4,16 @@ on:
paths:
- 'go/gas-oracle/**'
- 'go/batch-submitter/**'
- 'go/bss-core/**'
- 'go/teleportr/**'
branches:
- 'master'
- 'develop'
- '*rc'
- 'regenesis/*'
pull_request:
paths:
- 'go/gas-oracle/**'
- 'go/batch-submitter/**'
branches:
- '*'
jobs:
golangci:
name: lint
......@@ -34,3 +35,8 @@ jobs:
with:
version: v1.29
working-directory: go/bss-core
- name: golangci-lint teleportr
uses: golangci/golangci-lint-action@v2
with:
version: v1.29
working-directory: go/teleportr
......@@ -20,10 +20,12 @@ jobs:
- 5000:5000
strategy:
matrix:
batch-submitter: [ts-batch-submitter, go-batch-submitter]
batch-type:
- zlib
- legacy
env:
DOCKER_BUILDKIT: 1
COMPOSE_DOCKER_CLI_BUILD: 1
DOCKER_BUILDKIT: 1
COMPOSE_DOCKER_CLI_BUILD: 1
steps:
# Monorepo tests
- uses: actions/checkout@v2
......@@ -40,11 +42,15 @@ jobs:
restore-keys: |
${{ runner.os }}-yarn-
- name: Set conditional env vars
run: |
echo "BATCH_SUBMITTER_SEQUENCER_BATCH_TYPE=${{ matrix.batch-type }}" >> $GITHUB_ENV
- name: Bring the stack up
working-directory: ./ops
run: |
./scripts/stats.sh &
docker-compose -f docker-compose.yml -f docker-compose.${{ matrix.batch-submitter }}.yml up -d
docker-compose -f docker-compose.yml up -d
- name: Wait for the Sequencer node
working-directory: ./ops
......
......@@ -6,8 +6,8 @@ on:
- 'master'
- 'develop'
pull_request:
paths:
- 'go/proxyd/**'
branches:
- '*'
workflow_dispatch:
defaults:
......
......@@ -345,7 +345,7 @@ jobs:
build-args: BUILDER_TAG=${{ needs.builder.outputs.canary-docker-tag }}
replica-healthcheck:
name: Publish Data Transport Layer Version ${{ needs.builder.outputs.canary-docker-tag }}
name: Publish Replica Healthcheck Version ${{ needs.builder.outputs.canary-docker-tag }}
needs: builder
if: needs.builder.outputs.replica-healthcheck != ''
runs-on: ubuntu-latest
......
......@@ -10,8 +10,8 @@ on:
- '*rc'
- 'regenesis/*'
pull_request:
paths:
- 'go/teleportr/**'
branches:
- '*'
workflow_dispatch:
defaults:
......@@ -25,8 +25,8 @@ jobs:
postgres:
image: postgres
env:
POSTGRES_USER=postgres
POSTGRES_PASSWORD=password
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
ports:
- 5432:5432
steps:
......
......@@ -113,12 +113,6 @@ jobs:
fail_ci_if_error: false
verbose: true
flags: core-utils
- uses: codecov/codecov-action@v1
with:
files: ./packages/batch-submitter/coverage.json
fail_ci_if_error: false
verbose: true
flags: batch-submitter
- uses: codecov/codecov-action@v1
with:
files: ./packages/data-transport-layer/coverage.json
......@@ -170,10 +164,6 @@ jobs:
# if: steps.yarn-cache.outputs.cache-hit != 'true'
run: yarn install
- name: Check packages/batch-submitter
working-directory: ./packages/batch-submitter
run: npx depcheck
- name: Check packages/contracts
working-directory: ./packages/contracts
run: npx depcheck
......
......@@ -20,7 +20,7 @@ packages/contracts/hardhat*
packages/data-transport-layer/db
# vim
*.swp
*.sw*
.env
.env*
......
......@@ -116,7 +116,7 @@ This will build the following containers:
* [`l2geth`](https://hub.docker.com/r/ethereumoptimism/l2geth): L2 geth node running in Sequencer mode
* [`verifier`](https://hub.docker.com/r/ethereumoptimism/go-ethereum): L2 geth node running in Verifier mode
* [`relayer`](https://hub.docker.com/r/ethereumoptimism/message-relayer): helper process that relays messages between L1 and L2
* [`batch_submitter`](https://hub.docker.com/r/ethereumoptimism/batch-submitter): service that submits batches of Sequencer transactions to the L1 chain
* [`batch_submitter`](https://hub.docker.com/r/ethereumoptimism/batch-submitter-service): service that submits batches of Sequencer transactions to the L1 chain
* [`integration_tests`](https://hub.docker.com/r/ethereumoptimism/integration-tests): integration tests in a box
If you want to make a change to a container, you'll need to take it down and rebuild it.
......
......@@ -35,7 +35,6 @@ root
│ ├── <a href="./packages/core-utils">core-utils</a>: Low-level utilities that make building Optimism easier
│ ├── <a href="./packages/common-ts">common-ts</a>: Common tools for building apps in TypeScript
│ ├── <a href="./packages/data-transport-layer">data-transport-layer</a>: Service for indexing Optimism-related L1 data
│ ├── <a href="./packages/batch-submitter">batch-submitter</a>: Service for submitting batches of transactions and results to L1
│ ├── <a href="./packages/message-relayer">message-relayer</a>: Tool for automatically relaying L1<>L2 messages in development
│ └── <a href="./packages/replica-healthcheck">replica-healthcheck</a>: Service for monitoring the health of a replica node
├── <a href="./go">go</a>
......@@ -83,6 +82,12 @@ Some exceptions to this rule exist for cases in which we absolutely must deploy
If you're changing or adding a contract and you're unsure about which branch to make a PR into, default to using the latest release candidate branch.
See below for info about release candidate branches.
### Release new versions
Developers can release new versions of the software by adding changesets to their pull requests using `yarn changeset`. Changesets will persist over time on the `develop` branch without triggering new version bumps to be proposed by the Changesets bot. Once changesets are merged into `master`, the bot will create a new pull request called "Version Packages" which bumps the versions of packages. The correct flow for triggering releases is to update the base branch of these pull requests onto `develop` and merge them, and then create a new pull request to merge `develop` into `master`. Then, the `release` workflow will trigger the actual publishing to `npm` and Docker hub.
Be sure to not merge other pull requests into `develop` if partially through the release process. This can cause problems with Changesets doing releases and will require manual intervention to fix it.
### Release candidate branches
Branches marked `regenesis/X.X.X` are **release candidate branches**.
......
......@@ -93,7 +93,7 @@ func Main(gitVersion string) func(ctx *cli.Context) error {
return err
}
l2Client, err := dial.L2EthClientWithTimeout(ctx, cfg.L2EthRpc, cfg.DisableHTTP2)
l2Client, err := DialL2EthClientWithTimeout(ctx, cfg.L2EthRpc, cfg.DisableHTTP2)
if err != nil {
return err
}
......@@ -125,6 +125,7 @@ func Main(gitVersion string) func(ctx *cli.Context) error {
CTCAddr: ctcAddress,
ChainID: chainID,
PrivKey: sequencerPrivKey,
BatchType: sequencer.BatchTypeFromString(cfg.SequencerBatchType),
})
if err != nil {
return err
......
......@@ -33,6 +33,11 @@ var (
ErrSameSequencerAndProposerPrivKey = errors.New("sequencer-priv-key and " +
"proposer-priv-key must be distinct")
// ErrInvalidBatchType signals that an unsupported batch type is being
// configured. The default is "legacy" and the options are "legacy" or
// "zlib"
ErrInvalidBatchType = errors.New("invalid batch type")
// ErrSentryDSNNotSet signals that not Data Source Name was provided
// with which to configure Sentry logging.
ErrSentryDSNNotSet = errors.New("sentry-dsn must be set if use-sentry " +
......@@ -164,6 +169,9 @@ type Config struct {
// the proposer transactions.
ProposerHDPath string
// SequencerBatchType represents the type of batch the sequencer submits.
SequencerBatchType string
// MetricsServerEnable if true, will create a metrics client and log to
// Prometheus.
MetricsServerEnable bool
......@@ -212,6 +220,7 @@ func NewConfig(ctx *cli.Context) (Config, error) {
Mnemonic: ctx.GlobalString(flags.MnemonicFlag.Name),
SequencerHDPath: ctx.GlobalString(flags.SequencerHDPathFlag.Name),
ProposerHDPath: ctx.GlobalString(flags.ProposerHDPathFlag.Name),
SequencerBatchType: ctx.GlobalString(flags.SequencerBatchType.Name),
MetricsServerEnable: ctx.GlobalBool(flags.MetricsServerEnableFlag.Name),
MetricsHostname: ctx.GlobalString(flags.MetricsHostnameFlag.Name),
MetricsPort: ctx.GlobalUint64(flags.MetricsPortFlag.Name),
......@@ -265,6 +274,12 @@ func ValidateConfig(cfg *Config) error {
return ErrSameSequencerAndProposerPrivKey
}
usingTypedBatches := cfg.SequencerBatchType != ""
validBatchType := cfg.SequencerBatchType == "legacy" || cfg.SequencerBatchType == "zlib"
if usingTypedBatches && !validBatchType {
return ErrInvalidBatchType
}
// Ensure the Sentry Data Source Name is set when using Sentry.
if cfg.SentryEnable && cfg.SentryDsn == "" {
return ErrSentryDSNNotSet
......
package dial
package batchsubmitter
import (
"context"
......@@ -6,18 +6,19 @@ import (
"net/http"
"strings"
"github.com/ethereum-optimism/optimism/go/bss-core/dial"
"github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum-optimism/optimism/l2geth/log"
"github.com/ethereum-optimism/optimism/l2geth/rpc"
)
// L2EthClientWithTimeout attempts to dial the L2 provider using the
// provided URL. If the dial doesn't complete within defaultDialTimeout seconds,
// DialL2EthClientWithTimeout attempts to dial the L2 provider using the
// provided URL. If the dial doesn't complete within dial.DefaultTimeout seconds,
// this method will return an error.
func L2EthClientWithTimeout(ctx context.Context, url string, disableHTTP2 bool) (
func DialL2EthClientWithTimeout(ctx context.Context, url string, disableHTTP2 bool) (
*ethclient.Client, error) {
ctxt, cancel := context.WithTimeout(ctx, defaultDialTimeout)
ctxt, cancel := context.WithTimeout(ctx, dial.DefaultTimeout)
defer cancel()
if strings.HasPrefix(url, "http") {
......
......@@ -78,6 +78,7 @@ func GenSequencerBatchParams(
shouldStartAtElement uint64,
blockOffset uint64,
batch []BatchElement,
batchType BatchType,
) (*AppendSequencerBatchParams, error) {
var (
......@@ -188,5 +189,6 @@ func GenSequencerBatchParams(
TotalElementsToAppend: uint64(len(batch)),
Contexts: contexts,
Txs: txs,
Type: batchType,
}, nil
}
......@@ -36,6 +36,7 @@ type Config struct {
CTCAddr common.Address
ChainID *big.Int
PrivKey *ecdsa.PrivateKey
BatchType BatchType
}
type Driver struct {
......@@ -160,7 +161,7 @@ func (d *Driver) CraftBatchTx(
name := d.cfg.Name
log.Info(name+" crafting batch tx", "start", start, "end", end,
"nonce", nonce)
"nonce", nonce, "type", d.cfg.BatchType.String())
var (
batchElements []BatchElement
......@@ -195,7 +196,7 @@ func (d *Driver) CraftBatchTx(
var pruneCount int
for {
batchParams, err := GenSequencerBatchParams(
shouldStartAt, d.cfg.BlockOffset, batchElements,
shouldStartAt, d.cfg.BlockOffset, batchElements, d.cfg.BatchType,
)
if err != nil {
return nil, err
......
package sequencer
import (
"bufio"
"bytes"
"compress/zlib"
"encoding/binary"
"errors"
"fmt"
"io"
"math"
......@@ -17,7 +20,13 @@ const (
TxLenSize = 3
)
var byteOrder = binary.BigEndian
var (
// byteOrder represents the endiannes used for batch serialization
byteOrder = binary.BigEndian
// ErrMalformedBatch represents a batch that is not well formed
// according to the protocol specification
ErrMalformedBatch = errors.New("malformed batch")
)
// BatchContext denotes a range of transactions that belong the same batch. It
// is used to compress shared fields that would otherwise be repeated for each
......@@ -44,11 +53,14 @@ type BatchContext struct {
// - num_subsequent_queue_txs: 3 bytes
// - timestamp: 5 bytes
// - block_number: 5 bytes
//
// Note that writing to a bytes.Buffer cannot
// error, so errors are ignored here
func (c *BatchContext) Write(w *bytes.Buffer) {
writeUint64(w, c.NumSequencedTxs, 3)
writeUint64(w, c.NumSubsequentQueueTxs, 3)
writeUint64(w, c.Timestamp, 5)
writeUint64(w, c.BlockNumber, 5)
_ = writeUint64(w, c.NumSequencedTxs, 3)
_ = writeUint64(w, c.NumSubsequentQueueTxs, 3)
_ = writeUint64(w, c.Timestamp, 5)
_ = writeUint64(w, c.BlockNumber, 5)
}
// Read decodes the BatchContext from the passed reader. If fewer than 16-bytes
......@@ -71,6 +83,45 @@ func (c *BatchContext) Read(r io.Reader) error {
return readUint64(r, &c.BlockNumber, 5)
}
// BatchType represents the type of batch being
// submitted. When the first context in the batch
// has a timestamp of 0, the blocknumber is interpreted
// as an enum that represets the type
type BatchType int8
// Implements the Stringer interface for BatchType
func (b BatchType) String() string {
switch b {
case BatchTypeLegacy:
return "LEGACY"
case BatchTypeZlib:
return "ZLIB"
default:
return ""
}
}
// BatchTypeFromString returns the BatchType
// enum based on a human readable string
func BatchTypeFromString(s string) BatchType {
switch s {
case "zlib", "ZLIB":
return BatchTypeZlib
case "legacy", "LEGACY":
return BatchTypeLegacy
default:
return BatchTypeLegacy
}
}
const (
// BatchTypeLegacy represets the legacy batch type
BatchTypeLegacy BatchType = -1
// BatchTypeZlib represents a batch type where the
// transaction data is compressed using zlib
BatchTypeZlib BatchType = 0
)
// AppendSequencerBatchParams holds the raw data required to submit a batch of
// L2 txs to L1 CTC contract. Rather than encoding the objects using the
// standard ABI encoding, a custom encoding is and provided in the call data to
......@@ -95,6 +146,9 @@ type AppendSequencerBatchParams struct {
// Txs contains all sequencer txs that will be recorded in the L1 CTC
// contract.
Txs []*CachedTx
// The type of the batch
Type BatchType
}
// Write encodes the AppendSequencerBatchParams using the following format:
......@@ -105,20 +159,73 @@ type AppendSequencerBatchParams struct {
// - [num txs ommitted]
// - tx_len: 3 bytes
// - tx_bytes: tx_len bytes
//
// Typed batches include a dummy context as the first context
// where the timestamp is 0. The blocknumber is interpreted
// as an enum that defines the type. It is impossible to have
// a timestamp of 0 in practice, so this safely can indicate
// that the batch is typed.
// Type 0 batches have a dummy context where the blocknumber is
// set to 0. The transaction data is compressed with zlib before
// submitting the transaction to the chain. The fields should_start_at_element,
// total_elements_to_append, num_contexts and the contexts themselves
// are not altered.
//
// Note that writing to a bytes.Buffer cannot
// error, so errors are ignored here
func (p *AppendSequencerBatchParams) Write(w *bytes.Buffer) error {
writeUint64(w, p.ShouldStartAtElement, 5)
writeUint64(w, p.TotalElementsToAppend, 3)
_ = writeUint64(w, p.ShouldStartAtElement, 5)
_ = writeUint64(w, p.TotalElementsToAppend, 3)
// There must be contexts if there are transactions
if len(p.Contexts) == 0 && len(p.Txs) != 0 {
return ErrMalformedBatch
}
// There must be transactions if there are contexts
if len(p.Txs) == 0 && len(p.Contexts) != 0 {
return ErrMalformedBatch
}
// copy the contexts as to not malleate the struct
// when it is a typed batch
contexts := make([]BatchContext, 0, len(p.Contexts)+1)
if p.Type == BatchTypeZlib {
// All zero values for the single batch context
// is desired here as blocknumber 0 means it is a zlib batch
contexts = append(contexts, BatchContext{})
}
contexts = append(contexts, p.Contexts...)
// Write number of contexts followed by each fixed-size BatchContext.
writeUint64(w, uint64(len(p.Contexts)), 3)
for _, context := range p.Contexts {
_ = writeUint64(w, uint64(len(contexts)), 3)
for _, context := range contexts {
context.Write(w)
}
// Write each length-prefixed tx.
for _, tx := range p.Txs {
writeUint64(w, uint64(tx.Size()), TxLenSize)
_, _ = w.Write(tx.RawTx()) // can't fail for bytes.Buffer
switch p.Type {
case BatchTypeLegacy:
// Write each length-prefixed tx.
for _, tx := range p.Txs {
_ = writeUint64(w, uint64(tx.Size()), TxLenSize)
_, _ = w.Write(tx.RawTx()) // can't fail for bytes.Buffer
}
case BatchTypeZlib:
zw := zlib.NewWriter(w)
for _, tx := range p.Txs {
if err := writeUint64(zw, uint64(tx.Size()), TxLenSize); err != nil {
return err
}
if _, err := zw.Write(tx.RawTx()); err != nil {
return err
}
}
if err := zw.Close(); err != nil {
return err
}
default:
return fmt.Errorf("Unknown batch type: %s", p.Type)
}
return nil
......@@ -159,6 +266,8 @@ func (p *AppendSequencerBatchParams) Read(r io.Reader) error {
return err
}
// Ensure that contexts is never nil
p.Contexts = make([]BatchContext, 0)
for i := uint64(0); i < numContexts; i++ {
var batchContext BatchContext
if err := batchContext.Read(r); err != nil {
......@@ -168,14 +277,44 @@ func (p *AppendSequencerBatchParams) Read(r io.Reader) error {
p.Contexts = append(p.Contexts, batchContext)
}
// Assume that it is a legacy batch at first
p.Type = BatchTypeLegacy
// Handle backwards compatible batch types
if len(p.Contexts) > 0 && p.Contexts[0].Timestamp == 0 {
switch p.Contexts[0].BlockNumber {
case 0:
// zlib compressed transaction data
p.Type = BatchTypeZlib
// remove the first dummy context
p.Contexts = p.Contexts[1:]
numContexts--
zr, err := zlib.NewReader(r)
if err != nil {
return err
}
defer zr.Close()
r = bufio.NewReader(zr)
}
}
// Deserialize any transactions. Since the number of txs is ommitted
// from the encoding, loop until the stream is consumed.
for {
var txLen uint64
err := readUint64(r, &txLen, TxLenSize)
// Getting an EOF when reading the txLen expected for a cleanly
// encoded object. Silece the error and return success.
// encoded object. Silence the error and return success if
// the batch is well formed.
if err == io.EOF {
if len(p.Contexts) == 0 && len(p.Txs) != 0 {
return ErrMalformedBatch
}
if len(p.Txs) == 0 && len(p.Contexts) != 0 {
return ErrMalformedBatch
}
return nil
} else if err != nil {
return err
......@@ -188,10 +327,11 @@ func (p *AppendSequencerBatchParams) Read(r io.Reader) error {
p.Txs = append(p.Txs, NewCachedTx(tx))
}
}
// writeUint64 writes a the bottom `n` bytes of `val` to `w`.
func writeUint64(w *bytes.Buffer, val uint64, n uint) {
func writeUint64(w io.Writer, val uint64, n uint) error {
if n < 1 || n > 8 {
panic(fmt.Sprintf("invalid number of bytes %d must be 1-8", n))
}
......@@ -204,7 +344,8 @@ func writeUint64(w *bytes.Buffer, val uint64, n uint) {
var buf [8]byte
byteOrder.PutUint64(buf[:], val)
_, _ = w.Write(buf[8-n:]) // can't fail for bytes.Buffer
_, err := w.Write(buf[8-n:])
return err
}
// readUint64 reads `n` bytes from `r` and returns them in the lower `n` bytes
......
......@@ -65,167 +65,21 @@ type AppendSequencerBatchParamsTest struct {
TotalElementsToAppend uint64 `json:"total_elements_to_append"`
Contexts []sequencer.BatchContext `json:"contexts"`
Txs []string `json:"txs"`
Error bool `json:"error"`
}
var appendSequencerBatchParamTests = AppendSequencerBatchParamsTestCases{
Tests: []AppendSequencerBatchParamsTest{
{
Name: "empty batch",
HexEncoding: "0000000000000000" +
"000000",
ShouldStartAtElement: 0,
TotalElementsToAppend: 0,
Contexts: nil,
Txs: nil,
},
{
Name: "single tx",
HexEncoding: "0000000001000001" +
"000000" +
"00000ac9808080808080808080",
ShouldStartAtElement: 1,
TotalElementsToAppend: 1,
Contexts: nil,
Txs: []string{
"c9808080808080808080",
},
},
{
Name: "multiple txs",
HexEncoding: "0000000001000004" +
"000000" +
"00000ac9808080808080808080" +
"00000ac9808080808080808080" +
"00000ac9808080808080808080" +
"00000ac9808080808080808080",
ShouldStartAtElement: 1,
TotalElementsToAppend: 4,
Contexts: nil,
Txs: []string{
"c9808080808080808080",
"c9808080808080808080",
"c9808080808080808080",
"c9808080808080808080",
},
},
{
Name: "single context",
HexEncoding: "0000000001000000" +
"000001" +
"000102030405060708090a0b0c0d0e0f",
ShouldStartAtElement: 1,
TotalElementsToAppend: 0,
Contexts: []sequencer.BatchContext{
{
NumSequencedTxs: 0x000102,
NumSubsequentQueueTxs: 0x030405,
Timestamp: 0x060708090a,
BlockNumber: 0x0b0c0d0e0f,
},
},
Txs: nil,
},
{
Name: "multiple contexts",
HexEncoding: "0000000001000000" +
"000004" +
"000102030405060708090a0b0c0d0e0f" +
"000102030405060708090a0b0c0d0e0f" +
"000102030405060708090a0b0c0d0e0f" +
"000102030405060708090a0b0c0d0e0f",
ShouldStartAtElement: 1,
TotalElementsToAppend: 0,
Contexts: []sequencer.BatchContext{
{
NumSequencedTxs: 0x000102,
NumSubsequentQueueTxs: 0x030405,
Timestamp: 0x060708090a,
BlockNumber: 0x0b0c0d0e0f,
},
{
NumSequencedTxs: 0x000102,
NumSubsequentQueueTxs: 0x030405,
Timestamp: 0x060708090a,
BlockNumber: 0x0b0c0d0e0f,
},
{
NumSequencedTxs: 0x000102,
NumSubsequentQueueTxs: 0x030405,
Timestamp: 0x060708090a,
BlockNumber: 0x0b0c0d0e0f,
},
{
NumSequencedTxs: 0x000102,
NumSubsequentQueueTxs: 0x030405,
Timestamp: 0x060708090a,
BlockNumber: 0x0b0c0d0e0f,
},
},
Txs: nil,
},
{
Name: "complex",
HexEncoding: "0102030405060708" +
"000004" +
"000102030405060708090a0b0c0d0e0f" +
"000102030405060708090a0b0c0d0e0f" +
"000102030405060708090a0b0c0d0e0f" +
"000102030405060708090a0b0c0d0e0f" +
"00000ac9808080808080808080" +
"00000ac9808080808080808080" +
"00000ac9808080808080808080" +
"00000ac9808080808080808080",
ShouldStartAtElement: 0x0102030405,
TotalElementsToAppend: 0x060708,
Contexts: []sequencer.BatchContext{
{
NumSequencedTxs: 0x000102,
NumSubsequentQueueTxs: 0x030405,
Timestamp: 0x060708090a,
BlockNumber: 0x0b0c0d0e0f,
},
{
NumSequencedTxs: 0x000102,
NumSubsequentQueueTxs: 0x030405,
Timestamp: 0x060708090a,
BlockNumber: 0x0b0c0d0e0f,
},
{
NumSequencedTxs: 0x000102,
NumSubsequentQueueTxs: 0x030405,
Timestamp: 0x060708090a,
BlockNumber: 0x0b0c0d0e0f,
},
{
NumSequencedTxs: 0x000102,
NumSubsequentQueueTxs: 0x030405,
Timestamp: 0x060708090a,
BlockNumber: 0x0b0c0d0e0f,
},
},
Txs: []string{
"c9808080808080808080",
"c9808080808080808080",
"c9808080808080808080",
"c9808080808080808080",
},
},
},
}
// TestAppendSequencerBatchParamsEncodeDecodeMatchesJSON ensures that the
// in-memory test vectors for valid encode/decode stay in sync with the JSON
// version.
func TestAppendSequencerBatchParamsEncodeDecodeMatchesJSON(t *testing.T) {
t.Parallel()
jsonBytes, err := json.MarshalIndent(appendSequencerBatchParamTests, "", "\t")
require.Nil(t, err)
var appendSequencerBatchParamTests = AppendSequencerBatchParamsTestCases{}
func init() {
data, err := os.ReadFile("./testdata/valid_append_sequencer_batch_params.json")
require.Nil(t, err)
if err != nil {
panic(err)
}
require.Equal(t, jsonBytes, data)
err = json.Unmarshal(data, &appendSequencerBatchParamTests)
if err != nil {
panic(err)
}
}
// TestAppendSequencerBatchParamsEncodeDecode asserts the proper encoding and
......@@ -265,6 +119,7 @@ func testAppendSequencerBatchParamsEncodeDecode(
TotalElementsToAppend: test.TotalElementsToAppend,
Contexts: test.Contexts,
Txs: nil,
Type: sequencer.BatchTypeLegacy,
}
// Decode the batch from the test string.
......@@ -273,7 +128,12 @@ func testAppendSequencerBatchParamsEncodeDecode(
var params sequencer.AppendSequencerBatchParams
err = params.Read(bytes.NewReader(rawBytes))
require.Nil(t, err)
if test.Error {
require.ErrorIs(t, err, sequencer.ErrMalformedBatch)
} else {
require.Nil(t, err)
}
require.Equal(t, params.Type, sequencer.BatchTypeLegacy)
// Assert that the decoded params match the expected params. The
// transactions are compared serparetly (via hash), since the internal
......@@ -290,8 +150,34 @@ func testAppendSequencerBatchParamsEncodeDecode(
// Finally, encode the decoded object and assert it matches the original
// hex string.
paramsBytes, err := params.Serialize()
// Return early when testing error cases, no need to reserialize again
if test.Error {
require.ErrorIs(t, err, sequencer.ErrMalformedBatch)
return
}
require.Nil(t, err)
require.Equal(t, test.HexEncoding, hex.EncodeToString(paramsBytes))
// Serialize the batches in compressed form
params.Type = sequencer.BatchTypeZlib
compressedParamsBytes, err := params.Serialize()
require.Nil(t, err)
// Deserialize the compressed batch
var paramsCompressed sequencer.AppendSequencerBatchParams
err = paramsCompressed.Read(bytes.NewReader(compressedParamsBytes))
require.Nil(t, err)
require.Equal(t, paramsCompressed.Type, sequencer.BatchTypeZlib)
expParams.Type = sequencer.BatchTypeZlib
decompressedTxs := paramsCompressed.Txs
paramsCompressed.Txs = nil
require.Equal(t, expParams, paramsCompressed)
compareTxs(t, expTxs, decompressedTxs)
paramsCompressed.Txs = decompressedTxs
}
// compareTxs compares a list of two transactions, testing each pair by tx hash.
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -194,6 +194,12 @@ var (
"mnemonic. The mnemonic flag must also be set.",
EnvVar: prefixEnvVar("PROPOSER_HD_PATH"),
}
SequencerBatchType = cli.StringFlag{
Name: "sequencer-batch-type",
Usage: "The type of sequencer batch to be submitted. Valid arguments are legacy or zlib.",
Value: "legacy",
EnvVar: prefixEnvVar("SEQUENCER_BATCH_TYPE"),
}
MetricsServerEnableFlag = cli.BoolFlag{
Name: "metrics-server-enable",
Usage: "Whether or not to run the embedded metrics server",
......@@ -245,6 +251,7 @@ var optionalFlags = []cli.Flag{
SentryDsnFlag,
SentryTraceRateFlag,
BlockOffsetFlag,
SequencerBatchType,
SequencerPrivateKeyFlag,
ProposerPrivateKeyFlag,
MnemonicFlag,
......
......@@ -3,7 +3,7 @@ package dial
import "time"
const (
// defaultDialTimeout is default duration the service will wait on
// startup to make a connection to either the L1 or L2 backends.
defaultDialTimeout = 5 * time.Second
// DefaultTimeout is default duration the service will wait on startup to
// make a connection to either the L1 or L2 backends.
DefaultTimeout = 5 * time.Second
)
......@@ -12,12 +12,12 @@ import (
)
// L1EthClientWithTimeout attempts to dial the L1 provider using the
// provided URL. If the dial doesn't complete within defaultDialTimeout seconds,
// provided URL. If the dial doesn't complete within DefaultTimeout seconds,
// this method will return an error.
func L1EthClientWithTimeout(ctx context.Context, url string, disableHTTP2 bool) (
*ethclient.Client, error) {
ctxt, cancel := context.WithTimeout(ctx, defaultDialTimeout)
ctxt, cancel := context.WithTimeout(ctx, DefaultTimeout)
defer cancel()
if strings.HasPrefix(url, "http") {
......
......@@ -3,13 +3,11 @@ module github.com/ethereum-optimism/optimism/go/bss-core
go 1.16
require (
github.com/btcsuite/btcd v0.22.0-beta // indirect
github.com/decred/dcrd/hdkeychain/v3 v3.0.0
github.com/ethereum-optimism/optimism/l2geth v1.0.0
github.com/ethereum/go-ethereum v1.10.12
github.com/getsentry/sentry-go v0.11.0
github.com/prometheus/client_golang v1.11.0
github.com/stretchr/testify v1.7.0
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef
)
replace github.com/ethereum-optimism/optimism/l2geth => ../../l2geth
......@@ -58,10 +58,7 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0=
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847 h1:rtI0fD4oG/8eVokGVPYJEW1F88p1ZNgXiEIs9thEE4A=
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aws/aws-sdk-go v1.42.6/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo=
github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y=
github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo=
......@@ -152,7 +149,6 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUn
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.10+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
......@@ -160,8 +156,6 @@ github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7j
github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa h1:XKAhUk/dtp+CV0VO6mhG2V7jA9vbcGcnYF/Ay9NjZrY=
github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
......@@ -207,7 +201,6 @@ github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-resty/resty/v2 v2.4.0/go.mod h1:B88+xCTEwvfD94NOuE6GS1wMlnoKNY8eEiNizfNwOwA=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
......@@ -309,7 +302,6 @@ github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0Gqw
github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 h1:6OvNmYgJyexcZ3pYbTI9jWx5tHo1Dee/tWbLMfPe2TA=
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
......@@ -429,7 +421,6 @@ github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt
github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE=
github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
......@@ -470,7 +461,6 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc=
github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE=
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
......@@ -499,10 +489,6 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg=
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE=
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw=
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639dk2kqnCPPv+wNjq7Xb6EfUxe/oX0/NM=
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
......@@ -533,7 +519,6 @@ github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
......@@ -619,11 +604,9 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
......@@ -719,7 +702,6 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
......@@ -797,7 +779,6 @@ gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3M
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
......@@ -815,7 +796,6 @@ gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
......
GITCOMMIT := $(shell git rev-parse HEAD)
GITDATE := $(shell git show -s --format='%ct')
GITVERSION := $(shell cat package.json | jq .version)
LDFLAGSSTRING +=-X main.GitCommit=$(GITCOMMIT)
LDFLAGSSTRING +=-X main.GitDate=$(GITDATE)
LDFLAGSSTRING +=-X main.GitVersion=$(GITVERSION)
LDFLAGS := -ldflags "$(LDFLAGSSTRING)"
DEPOSIT_ARTIFACT := ../../packages/contracts/artifacts/contracts/L1/teleportr/TeleportrDeposit.sol/TeleportrDeposit.json
DISBURSER_ARTIFACT := ../../packages/contracts/artifacts/contracts/L2/teleportr/TeleportrDisburser.sol/TeleportrDisburser.json
teleportr:
env GO111MODULE=on go build -v $(LDFLAGS) ./cmd/teleportr
clean:
rm teleportr
test:
go test -v ./...
lint:
golangci-lint run ./...
bindings: bindings-deposit bindings-disburser
bindings-deposit:
$(eval temp := $(shell mktemp))
cat $(DEPOSIT_ARTIFACT) | jq -r .bytecode > $(temp)
cat $(DEPOSIT_ARTIFACT) | jq .abi | \
abigen \
--pkg deposit \
--abi - \
--out bindings/deposit/teleportr_deposit.go \
--type TeleportrDeposit \
--bin $(temp)
bindings-disburser:
$(eval temp := $(shell mktemp))
cat $(DISBURSER_ARTIFACT) | jq -r .bytecode > $(temp)
cat $(DISBURSER_ARTIFACT) | jq .abi | \
abigen \
--pkg disburse \
--abi - \
--out bindings/disburse/teleportr_disburser.go \
--type TeleportrDisburser \
--bin $(temp)
.PHONY: \
teleportr \
bindings \
bindings-deposit \
bindings-disburser \
clean \
test \
lint
// Code generated - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
package deposit
import (
"errors"
"math/big"
"strings"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
)
// Reference imports to suppress errors if they are not otherwise used.
var (
_ = errors.New
_ = big.NewInt
_ = strings.NewReader
_ = ethereum.NotFound
_ = bind.Bind
_ = common.Big1
_ = types.BloomLookup
_ = event.NewSubscription
)
// TeleportrDepositMetaData contains all meta data concerning the TeleportrDeposit contract.
var TeleportrDepositMetaData = &bind.MetaData{
ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_minDepositAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_maxDepositAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_maxBalance\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"name\":\"BalanceWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"depositId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"emitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"EtherReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newBalance\",\"type\":\"uint256\"}],\"name\":\"MaxBalanceSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newAmount\",\"type\":\"uint256\"}],\"name\":\"MaxDepositAmountSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newAmount\",\"type\":\"uint256\"}],\"name\":\"MinDepositAmountSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"maxBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"maxDepositAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"minDepositAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_maxDepositAmount\",\"type\":\"uint256\"}],\"name\":\"setMaxAmount\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_maxBalance\",\"type\":\"uint256\"}],\"name\":\"setMaxBalance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_minDepositAmount\",\"type\":\"uint256\"}],\"name\":\"setMinAmount\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalDeposits\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawBalance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]",
Bin: "0x608060405234801561001057600080fd5b50604051610b4a380380610b4a83398101604081905261002f91610153565b61003833610103565b6001839055600282905560038190556000600481905560408051918252602082018590527f65779d3ca560e9bdec52d08ed75431a84df87cb7796f0e51965f6efc0f556c0f910160405180910390a16040805160008152602081018490527fb1e6cc560df1786578fd4d1fe6e046f089a0c3be401e999b51a5112437911797910160405180910390a16040805160008152602081018390527f185c6391e7218e85de8a9346fc72024a0f88e1f04c186e6351230b93976ad50b910160405180910390a1505050610181565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60008060006060848603121561016857600080fd5b8351925060208401519150604084015190509250925092565b6109ba806101906000396000f3fe6080604052600436106100c05760003560e01c80637d882097116100745780638ed832711161004e5780638ed83271146103445780639d51d9b71461035a578063f2fde38b1461037a57600080fd5b80637d882097146102d9578063897b0637146102ef5780638da5cb5b1461030f57600080fd5b8063645006ca116100a5578063645006ca14610285578063715018a6146102ae57806373ad468a146102c357600080fd5b80634fe47f701461024e5780635fd8c7101461027057600080fd5b3661024957600154341015610136576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4465706f73697420616d6f756e7420697320746f6f20736d616c6c000000000060448201526064015b60405180910390fd5b6002543411156101a2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4465706f73697420616d6f756e7420697320746f6f2062696700000000000000604482015260640161012d565b60035447111561020e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f436f6e7472616374206d61782062616c616e6365206578636565646564000000604482015260640161012d565b600454604051349133917f2d27851832fcac28a0d4af1344f01fed7ffcfd15171c14c564a0c42aa57ae5c090600090a4600480546001019055005b600080fd5b34801561025a57600080fd5b5061026e61026936600461092e565b61039a565b005b34801561027c57600080fd5b5061026e61045c565b34801561029157600080fd5b5061029b60015481565b6040519081526020015b60405180910390f35b3480156102ba57600080fd5b5061026e610578565b3480156102cf57600080fd5b5061029b60035481565b3480156102e557600080fd5b5061029b60045481565b3480156102fb57600080fd5b5061026e61030a36600461092e565b610605565b34801561031b57600080fd5b5060005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102a5565b34801561035057600080fd5b5061029b60025481565b34801561036657600080fd5b5061026e61037536600461092e565b6106c7565b34801561038657600080fd5b5061026e610395366004610947565b610789565b60005473ffffffffffffffffffffffffffffffffffffffff16331461041b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161012d565b60025460408051918252602082018390527fb1e6cc560df1786578fd4d1fe6e046f089a0c3be401e999b51a5112437911797910160405180910390a1600255565b60005473ffffffffffffffffffffffffffffffffffffffff1633146104dd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161012d565b6000546040805147808252915173ffffffffffffffffffffffffffffffffffffffff9093169283917fddc398b321237a8d40ac914388309c2f52a08c134e4dc4ce61e32f57cb7d80f1919081900360200190a260405173ffffffffffffffffffffffffffffffffffffffff83169082156108fc029083906000818181858888f19350505050158015610573573d6000803e3d6000fd5b505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146105f9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161012d565b61060360006108b9565b565b60005473ffffffffffffffffffffffffffffffffffffffff163314610686576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161012d565b60015460408051918252602082018390527f65779d3ca560e9bdec52d08ed75431a84df87cb7796f0e51965f6efc0f556c0f910160405180910390a1600155565b60005473ffffffffffffffffffffffffffffffffffffffff163314610748576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161012d565b60035460408051918252602082018390527f185c6391e7218e85de8a9346fc72024a0f88e1f04c186e6351230b93976ad50b910160405180910390a1600355565b60005473ffffffffffffffffffffffffffffffffffffffff16331461080a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161012d565b73ffffffffffffffffffffffffffffffffffffffff81166108ad576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161012d565b6108b6816108b9565b50565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561094057600080fd5b5035919050565b60006020828403121561095957600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461097d57600080fd5b939250505056fea2646970667358221220b610d8106720e33f96db31f8c5c4f9cecb50144a2cf1e4049ac0216cd47d0eff64736f6c63430008090033",
}
// TeleportrDepositABI is the input ABI used to generate the binding from.
// Deprecated: Use TeleportrDepositMetaData.ABI instead.
var TeleportrDepositABI = TeleportrDepositMetaData.ABI
// TeleportrDepositBin is the compiled bytecode used for deploying new contracts.
// Deprecated: Use TeleportrDepositMetaData.Bin instead.
var TeleportrDepositBin = TeleportrDepositMetaData.Bin
// DeployTeleportrDeposit deploys a new Ethereum contract, binding an instance of TeleportrDeposit to it.
func DeployTeleportrDeposit(auth *bind.TransactOpts, backend bind.ContractBackend, _minDepositAmount *big.Int, _maxDepositAmount *big.Int, _maxBalance *big.Int) (common.Address, *types.Transaction, *TeleportrDeposit, error) {
parsed, err := TeleportrDepositMetaData.GetAbi()
if err != nil {
return common.Address{}, nil, nil, err
}
if parsed == nil {
return common.Address{}, nil, nil, errors.New("GetABI returned nil")
}
address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(TeleportrDepositBin), backend, _minDepositAmount, _maxDepositAmount, _maxBalance)
if err != nil {
return common.Address{}, nil, nil, err
}
return address, tx, &TeleportrDeposit{TeleportrDepositCaller: TeleportrDepositCaller{contract: contract}, TeleportrDepositTransactor: TeleportrDepositTransactor{contract: contract}, TeleportrDepositFilterer: TeleportrDepositFilterer{contract: contract}}, nil
}
// TeleportrDeposit is an auto generated Go binding around an Ethereum contract.
type TeleportrDeposit struct {
TeleportrDepositCaller // Read-only binding to the contract
TeleportrDepositTransactor // Write-only binding to the contract
TeleportrDepositFilterer // Log filterer for contract events
}
// TeleportrDepositCaller is an auto generated read-only Go binding around an Ethereum contract.
type TeleportrDepositCaller struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// TeleportrDepositTransactor is an auto generated write-only Go binding around an Ethereum contract.
type TeleportrDepositTransactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// TeleportrDepositFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
type TeleportrDepositFilterer struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// TeleportrDepositSession is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options.
type TeleportrDepositSession struct {
Contract *TeleportrDeposit // Generic contract binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// TeleportrDepositCallerSession is an auto generated read-only Go binding around an Ethereum contract,
// with pre-set call options.
type TeleportrDepositCallerSession struct {
Contract *TeleportrDepositCaller // Generic contract caller binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
}
// TeleportrDepositTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
// with pre-set transact options.
type TeleportrDepositTransactorSession struct {
Contract *TeleportrDepositTransactor // Generic contract transactor binding to set the session for
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// TeleportrDepositRaw is an auto generated low-level Go binding around an Ethereum contract.
type TeleportrDepositRaw struct {
Contract *TeleportrDeposit // Generic contract binding to access the raw methods on
}
// TeleportrDepositCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
type TeleportrDepositCallerRaw struct {
Contract *TeleportrDepositCaller // Generic read-only contract binding to access the raw methods on
}
// TeleportrDepositTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
type TeleportrDepositTransactorRaw struct {
Contract *TeleportrDepositTransactor // Generic write-only contract binding to access the raw methods on
}
// NewTeleportrDeposit creates a new instance of TeleportrDeposit, bound to a specific deployed contract.
func NewTeleportrDeposit(address common.Address, backend bind.ContractBackend) (*TeleportrDeposit, error) {
contract, err := bindTeleportrDeposit(address, backend, backend, backend)
if err != nil {
return nil, err
}
return &TeleportrDeposit{TeleportrDepositCaller: TeleportrDepositCaller{contract: contract}, TeleportrDepositTransactor: TeleportrDepositTransactor{contract: contract}, TeleportrDepositFilterer: TeleportrDepositFilterer{contract: contract}}, nil
}
// NewTeleportrDepositCaller creates a new read-only instance of TeleportrDeposit, bound to a specific deployed contract.
func NewTeleportrDepositCaller(address common.Address, caller bind.ContractCaller) (*TeleportrDepositCaller, error) {
contract, err := bindTeleportrDeposit(address, caller, nil, nil)
if err != nil {
return nil, err
}
return &TeleportrDepositCaller{contract: contract}, nil
}
// NewTeleportrDepositTransactor creates a new write-only instance of TeleportrDeposit, bound to a specific deployed contract.
func NewTeleportrDepositTransactor(address common.Address, transactor bind.ContractTransactor) (*TeleportrDepositTransactor, error) {
contract, err := bindTeleportrDeposit(address, nil, transactor, nil)
if err != nil {
return nil, err
}
return &TeleportrDepositTransactor{contract: contract}, nil
}
// NewTeleportrDepositFilterer creates a new log filterer instance of TeleportrDeposit, bound to a specific deployed contract.
func NewTeleportrDepositFilterer(address common.Address, filterer bind.ContractFilterer) (*TeleportrDepositFilterer, error) {
contract, err := bindTeleportrDeposit(address, nil, nil, filterer)
if err != nil {
return nil, err
}
return &TeleportrDepositFilterer{contract: contract}, nil
}
// bindTeleportrDeposit binds a generic wrapper to an already deployed contract.
func bindTeleportrDeposit(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := abi.JSON(strings.NewReader(TeleportrDepositABI))
if err != nil {
return nil, err
}
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_TeleportrDeposit *TeleportrDepositRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _TeleportrDeposit.Contract.TeleportrDepositCaller.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_TeleportrDeposit *TeleportrDepositRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _TeleportrDeposit.Contract.TeleportrDepositTransactor.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_TeleportrDeposit *TeleportrDepositRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _TeleportrDeposit.Contract.TeleportrDepositTransactor.contract.Transact(opts, method, params...)
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_TeleportrDeposit *TeleportrDepositCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _TeleportrDeposit.Contract.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_TeleportrDeposit *TeleportrDepositTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _TeleportrDeposit.Contract.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_TeleportrDeposit *TeleportrDepositTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _TeleportrDeposit.Contract.contract.Transact(opts, method, params...)
}
// MaxBalance is a free data retrieval call binding the contract method 0x73ad468a.
//
// Solidity: function maxBalance() view returns(uint256)
func (_TeleportrDeposit *TeleportrDepositCaller) MaxBalance(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _TeleportrDeposit.contract.Call(opts, &out, "maxBalance")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// MaxBalance is a free data retrieval call binding the contract method 0x73ad468a.
//
// Solidity: function maxBalance() view returns(uint256)
func (_TeleportrDeposit *TeleportrDepositSession) MaxBalance() (*big.Int, error) {
return _TeleportrDeposit.Contract.MaxBalance(&_TeleportrDeposit.CallOpts)
}
// MaxBalance is a free data retrieval call binding the contract method 0x73ad468a.
//
// Solidity: function maxBalance() view returns(uint256)
func (_TeleportrDeposit *TeleportrDepositCallerSession) MaxBalance() (*big.Int, error) {
return _TeleportrDeposit.Contract.MaxBalance(&_TeleportrDeposit.CallOpts)
}
// MaxDepositAmount is a free data retrieval call binding the contract method 0x8ed83271.
//
// Solidity: function maxDepositAmount() view returns(uint256)
func (_TeleportrDeposit *TeleportrDepositCaller) MaxDepositAmount(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _TeleportrDeposit.contract.Call(opts, &out, "maxDepositAmount")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// MaxDepositAmount is a free data retrieval call binding the contract method 0x8ed83271.
//
// Solidity: function maxDepositAmount() view returns(uint256)
func (_TeleportrDeposit *TeleportrDepositSession) MaxDepositAmount() (*big.Int, error) {
return _TeleportrDeposit.Contract.MaxDepositAmount(&_TeleportrDeposit.CallOpts)
}
// MaxDepositAmount is a free data retrieval call binding the contract method 0x8ed83271.
//
// Solidity: function maxDepositAmount() view returns(uint256)
func (_TeleportrDeposit *TeleportrDepositCallerSession) MaxDepositAmount() (*big.Int, error) {
return _TeleportrDeposit.Contract.MaxDepositAmount(&_TeleportrDeposit.CallOpts)
}
// MinDepositAmount is a free data retrieval call binding the contract method 0x645006ca.
//
// Solidity: function minDepositAmount() view returns(uint256)
func (_TeleportrDeposit *TeleportrDepositCaller) MinDepositAmount(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _TeleportrDeposit.contract.Call(opts, &out, "minDepositAmount")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// MinDepositAmount is a free data retrieval call binding the contract method 0x645006ca.
//
// Solidity: function minDepositAmount() view returns(uint256)
func (_TeleportrDeposit *TeleportrDepositSession) MinDepositAmount() (*big.Int, error) {
return _TeleportrDeposit.Contract.MinDepositAmount(&_TeleportrDeposit.CallOpts)
}
// MinDepositAmount is a free data retrieval call binding the contract method 0x645006ca.
//
// Solidity: function minDepositAmount() view returns(uint256)
func (_TeleportrDeposit *TeleportrDepositCallerSession) MinDepositAmount() (*big.Int, error) {
return _TeleportrDeposit.Contract.MinDepositAmount(&_TeleportrDeposit.CallOpts)
}
// Owner is a free data retrieval call binding the contract method 0x8da5cb5b.
//
// Solidity: function owner() view returns(address)
func (_TeleportrDeposit *TeleportrDepositCaller) Owner(opts *bind.CallOpts) (common.Address, error) {
var out []interface{}
err := _TeleportrDeposit.contract.Call(opts, &out, "owner")
if err != nil {
return *new(common.Address), err
}
out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
return out0, err
}
// Owner is a free data retrieval call binding the contract method 0x8da5cb5b.
//
// Solidity: function owner() view returns(address)
func (_TeleportrDeposit *TeleportrDepositSession) Owner() (common.Address, error) {
return _TeleportrDeposit.Contract.Owner(&_TeleportrDeposit.CallOpts)
}
// Owner is a free data retrieval call binding the contract method 0x8da5cb5b.
//
// Solidity: function owner() view returns(address)
func (_TeleportrDeposit *TeleportrDepositCallerSession) Owner() (common.Address, error) {
return _TeleportrDeposit.Contract.Owner(&_TeleportrDeposit.CallOpts)
}
// TotalDeposits is a free data retrieval call binding the contract method 0x7d882097.
//
// Solidity: function totalDeposits() view returns(uint256)
func (_TeleportrDeposit *TeleportrDepositCaller) TotalDeposits(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _TeleportrDeposit.contract.Call(opts, &out, "totalDeposits")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// TotalDeposits is a free data retrieval call binding the contract method 0x7d882097.
//
// Solidity: function totalDeposits() view returns(uint256)
func (_TeleportrDeposit *TeleportrDepositSession) TotalDeposits() (*big.Int, error) {
return _TeleportrDeposit.Contract.TotalDeposits(&_TeleportrDeposit.CallOpts)
}
// TotalDeposits is a free data retrieval call binding the contract method 0x7d882097.
//
// Solidity: function totalDeposits() view returns(uint256)
func (_TeleportrDeposit *TeleportrDepositCallerSession) TotalDeposits() (*big.Int, error) {
return _TeleportrDeposit.Contract.TotalDeposits(&_TeleportrDeposit.CallOpts)
}
// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6.
//
// Solidity: function renounceOwnership() returns()
func (_TeleportrDeposit *TeleportrDepositTransactor) RenounceOwnership(opts *bind.TransactOpts) (*types.Transaction, error) {
return _TeleportrDeposit.contract.Transact(opts, "renounceOwnership")
}
// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6.
//
// Solidity: function renounceOwnership() returns()
func (_TeleportrDeposit *TeleportrDepositSession) RenounceOwnership() (*types.Transaction, error) {
return _TeleportrDeposit.Contract.RenounceOwnership(&_TeleportrDeposit.TransactOpts)
}
// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6.
//
// Solidity: function renounceOwnership() returns()
func (_TeleportrDeposit *TeleportrDepositTransactorSession) RenounceOwnership() (*types.Transaction, error) {
return _TeleportrDeposit.Contract.RenounceOwnership(&_TeleportrDeposit.TransactOpts)
}
// SetMaxAmount is a paid mutator transaction binding the contract method 0x4fe47f70.
//
// Solidity: function setMaxAmount(uint256 _maxDepositAmount) returns()
func (_TeleportrDeposit *TeleportrDepositTransactor) SetMaxAmount(opts *bind.TransactOpts, _maxDepositAmount *big.Int) (*types.Transaction, error) {
return _TeleportrDeposit.contract.Transact(opts, "setMaxAmount", _maxDepositAmount)
}
// SetMaxAmount is a paid mutator transaction binding the contract method 0x4fe47f70.
//
// Solidity: function setMaxAmount(uint256 _maxDepositAmount) returns()
func (_TeleportrDeposit *TeleportrDepositSession) SetMaxAmount(_maxDepositAmount *big.Int) (*types.Transaction, error) {
return _TeleportrDeposit.Contract.SetMaxAmount(&_TeleportrDeposit.TransactOpts, _maxDepositAmount)
}
// SetMaxAmount is a paid mutator transaction binding the contract method 0x4fe47f70.
//
// Solidity: function setMaxAmount(uint256 _maxDepositAmount) returns()
func (_TeleportrDeposit *TeleportrDepositTransactorSession) SetMaxAmount(_maxDepositAmount *big.Int) (*types.Transaction, error) {
return _TeleportrDeposit.Contract.SetMaxAmount(&_TeleportrDeposit.TransactOpts, _maxDepositAmount)
}
// SetMaxBalance is a paid mutator transaction binding the contract method 0x9d51d9b7.
//
// Solidity: function setMaxBalance(uint256 _maxBalance) returns()
func (_TeleportrDeposit *TeleportrDepositTransactor) SetMaxBalance(opts *bind.TransactOpts, _maxBalance *big.Int) (*types.Transaction, error) {
return _TeleportrDeposit.contract.Transact(opts, "setMaxBalance", _maxBalance)
}
// SetMaxBalance is a paid mutator transaction binding the contract method 0x9d51d9b7.
//
// Solidity: function setMaxBalance(uint256 _maxBalance) returns()
func (_TeleportrDeposit *TeleportrDepositSession) SetMaxBalance(_maxBalance *big.Int) (*types.Transaction, error) {
return _TeleportrDeposit.Contract.SetMaxBalance(&_TeleportrDeposit.TransactOpts, _maxBalance)
}
// SetMaxBalance is a paid mutator transaction binding the contract method 0x9d51d9b7.
//
// Solidity: function setMaxBalance(uint256 _maxBalance) returns()
func (_TeleportrDeposit *TeleportrDepositTransactorSession) SetMaxBalance(_maxBalance *big.Int) (*types.Transaction, error) {
return _TeleportrDeposit.Contract.SetMaxBalance(&_TeleportrDeposit.TransactOpts, _maxBalance)
}
// SetMinAmount is a paid mutator transaction binding the contract method 0x897b0637.
//
// Solidity: function setMinAmount(uint256 _minDepositAmount) returns()
func (_TeleportrDeposit *TeleportrDepositTransactor) SetMinAmount(opts *bind.TransactOpts, _minDepositAmount *big.Int) (*types.Transaction, error) {
return _TeleportrDeposit.contract.Transact(opts, "setMinAmount", _minDepositAmount)
}
// SetMinAmount is a paid mutator transaction binding the contract method 0x897b0637.
//
// Solidity: function setMinAmount(uint256 _minDepositAmount) returns()
func (_TeleportrDeposit *TeleportrDepositSession) SetMinAmount(_minDepositAmount *big.Int) (*types.Transaction, error) {
return _TeleportrDeposit.Contract.SetMinAmount(&_TeleportrDeposit.TransactOpts, _minDepositAmount)
}
// SetMinAmount is a paid mutator transaction binding the contract method 0x897b0637.
//
// Solidity: function setMinAmount(uint256 _minDepositAmount) returns()
func (_TeleportrDeposit *TeleportrDepositTransactorSession) SetMinAmount(_minDepositAmount *big.Int) (*types.Transaction, error) {
return _TeleportrDeposit.Contract.SetMinAmount(&_TeleportrDeposit.TransactOpts, _minDepositAmount)
}
// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b.
//
// Solidity: function transferOwnership(address newOwner) returns()
func (_TeleportrDeposit *TeleportrDepositTransactor) TransferOwnership(opts *bind.TransactOpts, newOwner common.Address) (*types.Transaction, error) {
return _TeleportrDeposit.contract.Transact(opts, "transferOwnership", newOwner)
}
// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b.
//
// Solidity: function transferOwnership(address newOwner) returns()
func (_TeleportrDeposit *TeleportrDepositSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) {
return _TeleportrDeposit.Contract.TransferOwnership(&_TeleportrDeposit.TransactOpts, newOwner)
}
// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b.
//
// Solidity: function transferOwnership(address newOwner) returns()
func (_TeleportrDeposit *TeleportrDepositTransactorSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) {
return _TeleportrDeposit.Contract.TransferOwnership(&_TeleportrDeposit.TransactOpts, newOwner)
}
// WithdrawBalance is a paid mutator transaction binding the contract method 0x5fd8c710.
//
// Solidity: function withdrawBalance() returns()
func (_TeleportrDeposit *TeleportrDepositTransactor) WithdrawBalance(opts *bind.TransactOpts) (*types.Transaction, error) {
return _TeleportrDeposit.contract.Transact(opts, "withdrawBalance")
}
// WithdrawBalance is a paid mutator transaction binding the contract method 0x5fd8c710.
//
// Solidity: function withdrawBalance() returns()
func (_TeleportrDeposit *TeleportrDepositSession) WithdrawBalance() (*types.Transaction, error) {
return _TeleportrDeposit.Contract.WithdrawBalance(&_TeleportrDeposit.TransactOpts)
}
// WithdrawBalance is a paid mutator transaction binding the contract method 0x5fd8c710.
//
// Solidity: function withdrawBalance() returns()
func (_TeleportrDeposit *TeleportrDepositTransactorSession) WithdrawBalance() (*types.Transaction, error) {
return _TeleportrDeposit.Contract.WithdrawBalance(&_TeleportrDeposit.TransactOpts)
}
// Receive is a paid mutator transaction binding the contract receive function.
//
// Solidity: receive() payable returns()
func (_TeleportrDeposit *TeleportrDepositTransactor) Receive(opts *bind.TransactOpts) (*types.Transaction, error) {
return _TeleportrDeposit.contract.RawTransact(opts, nil) // calldata is disallowed for receive function
}
// Receive is a paid mutator transaction binding the contract receive function.
//
// Solidity: receive() payable returns()
func (_TeleportrDeposit *TeleportrDepositSession) Receive() (*types.Transaction, error) {
return _TeleportrDeposit.Contract.Receive(&_TeleportrDeposit.TransactOpts)
}
// Receive is a paid mutator transaction binding the contract receive function.
//
// Solidity: receive() payable returns()
func (_TeleportrDeposit *TeleportrDepositTransactorSession) Receive() (*types.Transaction, error) {
return _TeleportrDeposit.Contract.Receive(&_TeleportrDeposit.TransactOpts)
}
// TeleportrDepositBalanceWithdrawnIterator is returned from FilterBalanceWithdrawn and is used to iterate over the raw logs and unpacked data for BalanceWithdrawn events raised by the TeleportrDeposit contract.
type TeleportrDepositBalanceWithdrawnIterator struct {
Event *TeleportrDepositBalanceWithdrawn // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *TeleportrDepositBalanceWithdrawnIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(TeleportrDepositBalanceWithdrawn)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(TeleportrDepositBalanceWithdrawn)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *TeleportrDepositBalanceWithdrawnIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *TeleportrDepositBalanceWithdrawnIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// TeleportrDepositBalanceWithdrawn represents a BalanceWithdrawn event raised by the TeleportrDeposit contract.
type TeleportrDepositBalanceWithdrawn struct {
Owner common.Address
Balance *big.Int
Raw types.Log // Blockchain specific contextual infos
}
// FilterBalanceWithdrawn is a free log retrieval operation binding the contract event 0xddc398b321237a8d40ac914388309c2f52a08c134e4dc4ce61e32f57cb7d80f1.
//
// Solidity: event BalanceWithdrawn(address indexed owner, uint256 balance)
func (_TeleportrDeposit *TeleportrDepositFilterer) FilterBalanceWithdrawn(opts *bind.FilterOpts, owner []common.Address) (*TeleportrDepositBalanceWithdrawnIterator, error) {
var ownerRule []interface{}
for _, ownerItem := range owner {
ownerRule = append(ownerRule, ownerItem)
}
logs, sub, err := _TeleportrDeposit.contract.FilterLogs(opts, "BalanceWithdrawn", ownerRule)
if err != nil {
return nil, err
}
return &TeleportrDepositBalanceWithdrawnIterator{contract: _TeleportrDeposit.contract, event: "BalanceWithdrawn", logs: logs, sub: sub}, nil
}
// WatchBalanceWithdrawn is a free log subscription operation binding the contract event 0xddc398b321237a8d40ac914388309c2f52a08c134e4dc4ce61e32f57cb7d80f1.
//
// Solidity: event BalanceWithdrawn(address indexed owner, uint256 balance)
func (_TeleportrDeposit *TeleportrDepositFilterer) WatchBalanceWithdrawn(opts *bind.WatchOpts, sink chan<- *TeleportrDepositBalanceWithdrawn, owner []common.Address) (event.Subscription, error) {
var ownerRule []interface{}
for _, ownerItem := range owner {
ownerRule = append(ownerRule, ownerItem)
}
logs, sub, err := _TeleportrDeposit.contract.WatchLogs(opts, "BalanceWithdrawn", ownerRule)
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(TeleportrDepositBalanceWithdrawn)
if err := _TeleportrDeposit.contract.UnpackLog(event, "BalanceWithdrawn", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ParseBalanceWithdrawn is a log parse operation binding the contract event 0xddc398b321237a8d40ac914388309c2f52a08c134e4dc4ce61e32f57cb7d80f1.
//
// Solidity: event BalanceWithdrawn(address indexed owner, uint256 balance)
func (_TeleportrDeposit *TeleportrDepositFilterer) ParseBalanceWithdrawn(log types.Log) (*TeleportrDepositBalanceWithdrawn, error) {
event := new(TeleportrDepositBalanceWithdrawn)
if err := _TeleportrDeposit.contract.UnpackLog(event, "BalanceWithdrawn", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
// TeleportrDepositEtherReceivedIterator is returned from FilterEtherReceived and is used to iterate over the raw logs and unpacked data for EtherReceived events raised by the TeleportrDeposit contract.
type TeleportrDepositEtherReceivedIterator struct {
Event *TeleportrDepositEtherReceived // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *TeleportrDepositEtherReceivedIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(TeleportrDepositEtherReceived)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(TeleportrDepositEtherReceived)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *TeleportrDepositEtherReceivedIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *TeleportrDepositEtherReceivedIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// TeleportrDepositEtherReceived represents a EtherReceived event raised by the TeleportrDeposit contract.
type TeleportrDepositEtherReceived struct {
DepositId *big.Int
Emitter common.Address
Amount *big.Int
Raw types.Log // Blockchain specific contextual infos
}
// FilterEtherReceived is a free log retrieval operation binding the contract event 0x2d27851832fcac28a0d4af1344f01fed7ffcfd15171c14c564a0c42aa57ae5c0.
//
// Solidity: event EtherReceived(uint256 indexed depositId, address indexed emitter, uint256 indexed amount)
func (_TeleportrDeposit *TeleportrDepositFilterer) FilterEtherReceived(opts *bind.FilterOpts, depositId []*big.Int, emitter []common.Address, amount []*big.Int) (*TeleportrDepositEtherReceivedIterator, error) {
var depositIdRule []interface{}
for _, depositIdItem := range depositId {
depositIdRule = append(depositIdRule, depositIdItem)
}
var emitterRule []interface{}
for _, emitterItem := range emitter {
emitterRule = append(emitterRule, emitterItem)
}
var amountRule []interface{}
for _, amountItem := range amount {
amountRule = append(amountRule, amountItem)
}
logs, sub, err := _TeleportrDeposit.contract.FilterLogs(opts, "EtherReceived", depositIdRule, emitterRule, amountRule)
if err != nil {
return nil, err
}
return &TeleportrDepositEtherReceivedIterator{contract: _TeleportrDeposit.contract, event: "EtherReceived", logs: logs, sub: sub}, nil
}
// WatchEtherReceived is a free log subscription operation binding the contract event 0x2d27851832fcac28a0d4af1344f01fed7ffcfd15171c14c564a0c42aa57ae5c0.
//
// Solidity: event EtherReceived(uint256 indexed depositId, address indexed emitter, uint256 indexed amount)
func (_TeleportrDeposit *TeleportrDepositFilterer) WatchEtherReceived(opts *bind.WatchOpts, sink chan<- *TeleportrDepositEtherReceived, depositId []*big.Int, emitter []common.Address, amount []*big.Int) (event.Subscription, error) {
var depositIdRule []interface{}
for _, depositIdItem := range depositId {
depositIdRule = append(depositIdRule, depositIdItem)
}
var emitterRule []interface{}
for _, emitterItem := range emitter {
emitterRule = append(emitterRule, emitterItem)
}
var amountRule []interface{}
for _, amountItem := range amount {
amountRule = append(amountRule, amountItem)
}
logs, sub, err := _TeleportrDeposit.contract.WatchLogs(opts, "EtherReceived", depositIdRule, emitterRule, amountRule)
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(TeleportrDepositEtherReceived)
if err := _TeleportrDeposit.contract.UnpackLog(event, "EtherReceived", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ParseEtherReceived is a log parse operation binding the contract event 0x2d27851832fcac28a0d4af1344f01fed7ffcfd15171c14c564a0c42aa57ae5c0.
//
// Solidity: event EtherReceived(uint256 indexed depositId, address indexed emitter, uint256 indexed amount)
func (_TeleportrDeposit *TeleportrDepositFilterer) ParseEtherReceived(log types.Log) (*TeleportrDepositEtherReceived, error) {
event := new(TeleportrDepositEtherReceived)
if err := _TeleportrDeposit.contract.UnpackLog(event, "EtherReceived", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
// TeleportrDepositMaxBalanceSetIterator is returned from FilterMaxBalanceSet and is used to iterate over the raw logs and unpacked data for MaxBalanceSet events raised by the TeleportrDeposit contract.
type TeleportrDepositMaxBalanceSetIterator struct {
Event *TeleportrDepositMaxBalanceSet // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *TeleportrDepositMaxBalanceSetIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(TeleportrDepositMaxBalanceSet)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(TeleportrDepositMaxBalanceSet)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *TeleportrDepositMaxBalanceSetIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *TeleportrDepositMaxBalanceSetIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// TeleportrDepositMaxBalanceSet represents a MaxBalanceSet event raised by the TeleportrDeposit contract.
type TeleportrDepositMaxBalanceSet struct {
PreviousBalance *big.Int
NewBalance *big.Int
Raw types.Log // Blockchain specific contextual infos
}
// FilterMaxBalanceSet is a free log retrieval operation binding the contract event 0x185c6391e7218e85de8a9346fc72024a0f88e1f04c186e6351230b93976ad50b.
//
// Solidity: event MaxBalanceSet(uint256 previousBalance, uint256 newBalance)
func (_TeleportrDeposit *TeleportrDepositFilterer) FilterMaxBalanceSet(opts *bind.FilterOpts) (*TeleportrDepositMaxBalanceSetIterator, error) {
logs, sub, err := _TeleportrDeposit.contract.FilterLogs(opts, "MaxBalanceSet")
if err != nil {
return nil, err
}
return &TeleportrDepositMaxBalanceSetIterator{contract: _TeleportrDeposit.contract, event: "MaxBalanceSet", logs: logs, sub: sub}, nil
}
// WatchMaxBalanceSet is a free log subscription operation binding the contract event 0x185c6391e7218e85de8a9346fc72024a0f88e1f04c186e6351230b93976ad50b.
//
// Solidity: event MaxBalanceSet(uint256 previousBalance, uint256 newBalance)
func (_TeleportrDeposit *TeleportrDepositFilterer) WatchMaxBalanceSet(opts *bind.WatchOpts, sink chan<- *TeleportrDepositMaxBalanceSet) (event.Subscription, error) {
logs, sub, err := _TeleportrDeposit.contract.WatchLogs(opts, "MaxBalanceSet")
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(TeleportrDepositMaxBalanceSet)
if err := _TeleportrDeposit.contract.UnpackLog(event, "MaxBalanceSet", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ParseMaxBalanceSet is a log parse operation binding the contract event 0x185c6391e7218e85de8a9346fc72024a0f88e1f04c186e6351230b93976ad50b.
//
// Solidity: event MaxBalanceSet(uint256 previousBalance, uint256 newBalance)
func (_TeleportrDeposit *TeleportrDepositFilterer) ParseMaxBalanceSet(log types.Log) (*TeleportrDepositMaxBalanceSet, error) {
event := new(TeleportrDepositMaxBalanceSet)
if err := _TeleportrDeposit.contract.UnpackLog(event, "MaxBalanceSet", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
// TeleportrDepositMaxDepositAmountSetIterator is returned from FilterMaxDepositAmountSet and is used to iterate over the raw logs and unpacked data for MaxDepositAmountSet events raised by the TeleportrDeposit contract.
type TeleportrDepositMaxDepositAmountSetIterator struct {
Event *TeleportrDepositMaxDepositAmountSet // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *TeleportrDepositMaxDepositAmountSetIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(TeleportrDepositMaxDepositAmountSet)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(TeleportrDepositMaxDepositAmountSet)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *TeleportrDepositMaxDepositAmountSetIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *TeleportrDepositMaxDepositAmountSetIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// TeleportrDepositMaxDepositAmountSet represents a MaxDepositAmountSet event raised by the TeleportrDeposit contract.
type TeleportrDepositMaxDepositAmountSet struct {
PreviousAmount *big.Int
NewAmount *big.Int
Raw types.Log // Blockchain specific contextual infos
}
// FilterMaxDepositAmountSet is a free log retrieval operation binding the contract event 0xb1e6cc560df1786578fd4d1fe6e046f089a0c3be401e999b51a5112437911797.
//
// Solidity: event MaxDepositAmountSet(uint256 previousAmount, uint256 newAmount)
func (_TeleportrDeposit *TeleportrDepositFilterer) FilterMaxDepositAmountSet(opts *bind.FilterOpts) (*TeleportrDepositMaxDepositAmountSetIterator, error) {
logs, sub, err := _TeleportrDeposit.contract.FilterLogs(opts, "MaxDepositAmountSet")
if err != nil {
return nil, err
}
return &TeleportrDepositMaxDepositAmountSetIterator{contract: _TeleportrDeposit.contract, event: "MaxDepositAmountSet", logs: logs, sub: sub}, nil
}
// WatchMaxDepositAmountSet is a free log subscription operation binding the contract event 0xb1e6cc560df1786578fd4d1fe6e046f089a0c3be401e999b51a5112437911797.
//
// Solidity: event MaxDepositAmountSet(uint256 previousAmount, uint256 newAmount)
func (_TeleportrDeposit *TeleportrDepositFilterer) WatchMaxDepositAmountSet(opts *bind.WatchOpts, sink chan<- *TeleportrDepositMaxDepositAmountSet) (event.Subscription, error) {
logs, sub, err := _TeleportrDeposit.contract.WatchLogs(opts, "MaxDepositAmountSet")
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(TeleportrDepositMaxDepositAmountSet)
if err := _TeleportrDeposit.contract.UnpackLog(event, "MaxDepositAmountSet", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ParseMaxDepositAmountSet is a log parse operation binding the contract event 0xb1e6cc560df1786578fd4d1fe6e046f089a0c3be401e999b51a5112437911797.
//
// Solidity: event MaxDepositAmountSet(uint256 previousAmount, uint256 newAmount)
func (_TeleportrDeposit *TeleportrDepositFilterer) ParseMaxDepositAmountSet(log types.Log) (*TeleportrDepositMaxDepositAmountSet, error) {
event := new(TeleportrDepositMaxDepositAmountSet)
if err := _TeleportrDeposit.contract.UnpackLog(event, "MaxDepositAmountSet", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
// TeleportrDepositMinDepositAmountSetIterator is returned from FilterMinDepositAmountSet and is used to iterate over the raw logs and unpacked data for MinDepositAmountSet events raised by the TeleportrDeposit contract.
type TeleportrDepositMinDepositAmountSetIterator struct {
Event *TeleportrDepositMinDepositAmountSet // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *TeleportrDepositMinDepositAmountSetIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(TeleportrDepositMinDepositAmountSet)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(TeleportrDepositMinDepositAmountSet)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *TeleportrDepositMinDepositAmountSetIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *TeleportrDepositMinDepositAmountSetIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// TeleportrDepositMinDepositAmountSet represents a MinDepositAmountSet event raised by the TeleportrDeposit contract.
type TeleportrDepositMinDepositAmountSet struct {
PreviousAmount *big.Int
NewAmount *big.Int
Raw types.Log // Blockchain specific contextual infos
}
// FilterMinDepositAmountSet is a free log retrieval operation binding the contract event 0x65779d3ca560e9bdec52d08ed75431a84df87cb7796f0e51965f6efc0f556c0f.
//
// Solidity: event MinDepositAmountSet(uint256 previousAmount, uint256 newAmount)
func (_TeleportrDeposit *TeleportrDepositFilterer) FilterMinDepositAmountSet(opts *bind.FilterOpts) (*TeleportrDepositMinDepositAmountSetIterator, error) {
logs, sub, err := _TeleportrDeposit.contract.FilterLogs(opts, "MinDepositAmountSet")
if err != nil {
return nil, err
}
return &TeleportrDepositMinDepositAmountSetIterator{contract: _TeleportrDeposit.contract, event: "MinDepositAmountSet", logs: logs, sub: sub}, nil
}
// WatchMinDepositAmountSet is a free log subscription operation binding the contract event 0x65779d3ca560e9bdec52d08ed75431a84df87cb7796f0e51965f6efc0f556c0f.
//
// Solidity: event MinDepositAmountSet(uint256 previousAmount, uint256 newAmount)
func (_TeleportrDeposit *TeleportrDepositFilterer) WatchMinDepositAmountSet(opts *bind.WatchOpts, sink chan<- *TeleportrDepositMinDepositAmountSet) (event.Subscription, error) {
logs, sub, err := _TeleportrDeposit.contract.WatchLogs(opts, "MinDepositAmountSet")
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(TeleportrDepositMinDepositAmountSet)
if err := _TeleportrDeposit.contract.UnpackLog(event, "MinDepositAmountSet", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ParseMinDepositAmountSet is a log parse operation binding the contract event 0x65779d3ca560e9bdec52d08ed75431a84df87cb7796f0e51965f6efc0f556c0f.
//
// Solidity: event MinDepositAmountSet(uint256 previousAmount, uint256 newAmount)
func (_TeleportrDeposit *TeleportrDepositFilterer) ParseMinDepositAmountSet(log types.Log) (*TeleportrDepositMinDepositAmountSet, error) {
event := new(TeleportrDepositMinDepositAmountSet)
if err := _TeleportrDeposit.contract.UnpackLog(event, "MinDepositAmountSet", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
// TeleportrDepositOwnershipTransferredIterator is returned from FilterOwnershipTransferred and is used to iterate over the raw logs and unpacked data for OwnershipTransferred events raised by the TeleportrDeposit contract.
type TeleportrDepositOwnershipTransferredIterator struct {
Event *TeleportrDepositOwnershipTransferred // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *TeleportrDepositOwnershipTransferredIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(TeleportrDepositOwnershipTransferred)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(TeleportrDepositOwnershipTransferred)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *TeleportrDepositOwnershipTransferredIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *TeleportrDepositOwnershipTransferredIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// TeleportrDepositOwnershipTransferred represents a OwnershipTransferred event raised by the TeleportrDeposit contract.
type TeleportrDepositOwnershipTransferred struct {
PreviousOwner common.Address
NewOwner common.Address
Raw types.Log // Blockchain specific contextual infos
}
// FilterOwnershipTransferred is a free log retrieval operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0.
//
// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)
func (_TeleportrDeposit *TeleportrDepositFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*TeleportrDepositOwnershipTransferredIterator, error) {
var previousOwnerRule []interface{}
for _, previousOwnerItem := range previousOwner {
previousOwnerRule = append(previousOwnerRule, previousOwnerItem)
}
var newOwnerRule []interface{}
for _, newOwnerItem := range newOwner {
newOwnerRule = append(newOwnerRule, newOwnerItem)
}
logs, sub, err := _TeleportrDeposit.contract.FilterLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule)
if err != nil {
return nil, err
}
return &TeleportrDepositOwnershipTransferredIterator{contract: _TeleportrDeposit.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil
}
// WatchOwnershipTransferred is a free log subscription operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0.
//
// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)
func (_TeleportrDeposit *TeleportrDepositFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *TeleportrDepositOwnershipTransferred, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) {
var previousOwnerRule []interface{}
for _, previousOwnerItem := range previousOwner {
previousOwnerRule = append(previousOwnerRule, previousOwnerItem)
}
var newOwnerRule []interface{}
for _, newOwnerItem := range newOwner {
newOwnerRule = append(newOwnerRule, newOwnerItem)
}
logs, sub, err := _TeleportrDeposit.contract.WatchLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule)
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(TeleportrDepositOwnershipTransferred)
if err := _TeleportrDeposit.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ParseOwnershipTransferred is a log parse operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0.
//
// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)
func (_TeleportrDeposit *TeleportrDepositFilterer) ParseOwnershipTransferred(log types.Log) (*TeleportrDepositOwnershipTransferred, error) {
event := new(TeleportrDepositOwnershipTransferred)
if err := _TeleportrDeposit.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
// Code generated - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
package disburse
import (
"errors"
"math/big"
"strings"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
)
// Reference imports to suppress errors if they are not otherwise used.
var (
_ = errors.New
_ = big.NewInt
_ = strings.NewReader
_ = ethereum.NotFound
_ = bind.Bind
_ = common.Big1
_ = types.BloomLookup
_ = event.NewSubscription
)
// TeleportrDisburserDisbursement is an auto generated low-level Go binding around an user-defined struct.
type TeleportrDisburserDisbursement struct {
Amount *big.Int
Addr common.Address
}
// TeleportrDisburserMetaData contains all meta data concerning the TeleportrDisburser contract.
var TeleportrDisburserMetaData = &bind.MetaData{
ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"name\":\"BalanceWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"depositId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"DisbursementFailed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"depositId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"DisbursementSuccess\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_nextDepositId\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"internalType\":\"structTeleportrDisburser.Disbursement[]\",\"name\":\"_disbursements\",\"type\":\"tuple[]\"}],\"name\":\"disburse\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalDisbursements\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawBalance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
Bin: "0x608060405234801561001057600080fd5b5061001a33610024565b6000600155610074565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6109e1806100836000396000f3fe6080604052600436106100655760003560e01c80638da5cb5b116100435780638da5cb5b146100bf578063ad48144d146100f4578063f2fde38b1461010757600080fd5b806325999e7f1461006a5780635fd8c71014610093578063715018a6146100aa575b600080fd5b34801561007657600080fd5b5061008060015481565b6040519081526020015b60405180910390f35b34801561009f57600080fd5b506100a8610127565b005b3480156100b657600080fd5b506100a8610248565b3480156100cb57600080fd5b5060005460405173ffffffffffffffffffffffffffffffffffffffff909116815260200161008a565b6100a8610102366004610840565b6102d5565b34801561011357600080fd5b506100a86101223660046108bf565b61069b565b60005473ffffffffffffffffffffffffffffffffffffffff1633146101ad576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b6000546040805147808252915173ffffffffffffffffffffffffffffffffffffffff9093169283917fddc398b321237a8d40ac914388309c2f52a08c134e4dc4ce61e32f57cb7d80f1919081900360200190a260405173ffffffffffffffffffffffffffffffffffffffff83169082156108fc029083906000818181858888f19350505050158015610243573d6000803e3d6000fd5b505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146102c9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016101a4565b6102d360006107cb565b565b60005473ffffffffffffffffffffffffffffffffffffffff163314610356576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016101a4565b80806103be576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f4e6f2064697362757273656d656e74730000000000000000000000000000000060448201526064016101a4565b60015484811461042a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f556e6578706563746564206e657874206465706f73697420696400000000000060448201526064016101a4565b60018054830190556000805b8381101561047757858582818110610450576104506108fc565b610463926040909102013590508361095a565b91508061046f81610972565b915050610436565b50348114610507576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f44697362757273656d656e7420746f74616c20213d20616d6f756e742073656e60448201527f740000000000000000000000000000000000000000000000000000000000000060648201526084016101a4565b60005b83811015610692576000868683818110610526576105266108fc565b9050604002016000013590506000878784818110610546576105466108fc565b905060400201602001602081019061055e91906108bf565b905060008173ffffffffffffffffffffffffffffffffffffffff16836108fc90604051600060405180830381858888f193505050503d80600081146105bf576040519150601f19603f3d011682016040523d82523d6000602084013e6105c4565b606091505b505090508015610624578173ffffffffffffffffffffffffffffffffffffffff16867feaa22fd2d7b875476355b32cf719794faf9d91b66e73bc6375a053cace9caaee8560405161061791815260200190565b60405180910390a3610676565b8173ffffffffffffffffffffffffffffffffffffffff16867f9b478c095979d3d3a7d602ffd9ee1f0843204d853558ae0882c8fcc0a5bc78cf8560405161066d91815260200190565b60405180910390a35b600186019550505050808061068a90610972565b91505061050a565b50505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461071c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016101a4565b73ffffffffffffffffffffffffffffffffffffffff81166107bf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016101a4565b6107c8816107cb565b50565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60008060006040848603121561085557600080fd5b83359250602084013567ffffffffffffffff8082111561087457600080fd5b818601915086601f83011261088857600080fd5b81358181111561089757600080fd5b8760208260061b85010111156108ac57600080fd5b6020830194508093505050509250925092565b6000602082840312156108d157600080fd5b813573ffffffffffffffffffffffffffffffffffffffff811681146108f557600080fd5b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000821982111561096d5761096d61092b565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156109a4576109a461092b565b506001019056fea2646970667358221220955e65ceaae72ce1d31ed14064916aa5576cb375d1a20fa0959e5a0d69e361fc64736f6c63430008090033",
}
// TeleportrDisburserABI is the input ABI used to generate the binding from.
// Deprecated: Use TeleportrDisburserMetaData.ABI instead.
var TeleportrDisburserABI = TeleportrDisburserMetaData.ABI
// TeleportrDisburserBin is the compiled bytecode used for deploying new contracts.
// Deprecated: Use TeleportrDisburserMetaData.Bin instead.
var TeleportrDisburserBin = TeleportrDisburserMetaData.Bin
// DeployTeleportrDisburser deploys a new Ethereum contract, binding an instance of TeleportrDisburser to it.
func DeployTeleportrDisburser(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *TeleportrDisburser, error) {
parsed, err := TeleportrDisburserMetaData.GetAbi()
if err != nil {
return common.Address{}, nil, nil, err
}
if parsed == nil {
return common.Address{}, nil, nil, errors.New("GetABI returned nil")
}
address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(TeleportrDisburserBin), backend)
if err != nil {
return common.Address{}, nil, nil, err
}
return address, tx, &TeleportrDisburser{TeleportrDisburserCaller: TeleportrDisburserCaller{contract: contract}, TeleportrDisburserTransactor: TeleportrDisburserTransactor{contract: contract}, TeleportrDisburserFilterer: TeleportrDisburserFilterer{contract: contract}}, nil
}
// TeleportrDisburser is an auto generated Go binding around an Ethereum contract.
type TeleportrDisburser struct {
TeleportrDisburserCaller // Read-only binding to the contract
TeleportrDisburserTransactor // Write-only binding to the contract
TeleportrDisburserFilterer // Log filterer for contract events
}
// TeleportrDisburserCaller is an auto generated read-only Go binding around an Ethereum contract.
type TeleportrDisburserCaller struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// TeleportrDisburserTransactor is an auto generated write-only Go binding around an Ethereum contract.
type TeleportrDisburserTransactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// TeleportrDisburserFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
type TeleportrDisburserFilterer struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// TeleportrDisburserSession is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options.
type TeleportrDisburserSession struct {
Contract *TeleportrDisburser // Generic contract binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// TeleportrDisburserCallerSession is an auto generated read-only Go binding around an Ethereum contract,
// with pre-set call options.
type TeleportrDisburserCallerSession struct {
Contract *TeleportrDisburserCaller // Generic contract caller binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
}
// TeleportrDisburserTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
// with pre-set transact options.
type TeleportrDisburserTransactorSession struct {
Contract *TeleportrDisburserTransactor // Generic contract transactor binding to set the session for
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// TeleportrDisburserRaw is an auto generated low-level Go binding around an Ethereum contract.
type TeleportrDisburserRaw struct {
Contract *TeleportrDisburser // Generic contract binding to access the raw methods on
}
// TeleportrDisburserCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
type TeleportrDisburserCallerRaw struct {
Contract *TeleportrDisburserCaller // Generic read-only contract binding to access the raw methods on
}
// TeleportrDisburserTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
type TeleportrDisburserTransactorRaw struct {
Contract *TeleportrDisburserTransactor // Generic write-only contract binding to access the raw methods on
}
// NewTeleportrDisburser creates a new instance of TeleportrDisburser, bound to a specific deployed contract.
func NewTeleportrDisburser(address common.Address, backend bind.ContractBackend) (*TeleportrDisburser, error) {
contract, err := bindTeleportrDisburser(address, backend, backend, backend)
if err != nil {
return nil, err
}
return &TeleportrDisburser{TeleportrDisburserCaller: TeleportrDisburserCaller{contract: contract}, TeleportrDisburserTransactor: TeleportrDisburserTransactor{contract: contract}, TeleportrDisburserFilterer: TeleportrDisburserFilterer{contract: contract}}, nil
}
// NewTeleportrDisburserCaller creates a new read-only instance of TeleportrDisburser, bound to a specific deployed contract.
func NewTeleportrDisburserCaller(address common.Address, caller bind.ContractCaller) (*TeleportrDisburserCaller, error) {
contract, err := bindTeleportrDisburser(address, caller, nil, nil)
if err != nil {
return nil, err
}
return &TeleportrDisburserCaller{contract: contract}, nil
}
// NewTeleportrDisburserTransactor creates a new write-only instance of TeleportrDisburser, bound to a specific deployed contract.
func NewTeleportrDisburserTransactor(address common.Address, transactor bind.ContractTransactor) (*TeleportrDisburserTransactor, error) {
contract, err := bindTeleportrDisburser(address, nil, transactor, nil)
if err != nil {
return nil, err
}
return &TeleportrDisburserTransactor{contract: contract}, nil
}
// NewTeleportrDisburserFilterer creates a new log filterer instance of TeleportrDisburser, bound to a specific deployed contract.
func NewTeleportrDisburserFilterer(address common.Address, filterer bind.ContractFilterer) (*TeleportrDisburserFilterer, error) {
contract, err := bindTeleportrDisburser(address, nil, nil, filterer)
if err != nil {
return nil, err
}
return &TeleportrDisburserFilterer{contract: contract}, nil
}
// bindTeleportrDisburser binds a generic wrapper to an already deployed contract.
func bindTeleportrDisburser(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := abi.JSON(strings.NewReader(TeleportrDisburserABI))
if err != nil {
return nil, err
}
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_TeleportrDisburser *TeleportrDisburserRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _TeleportrDisburser.Contract.TeleportrDisburserCaller.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_TeleportrDisburser *TeleportrDisburserRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _TeleportrDisburser.Contract.TeleportrDisburserTransactor.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_TeleportrDisburser *TeleportrDisburserRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _TeleportrDisburser.Contract.TeleportrDisburserTransactor.contract.Transact(opts, method, params...)
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_TeleportrDisburser *TeleportrDisburserCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _TeleportrDisburser.Contract.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_TeleportrDisburser *TeleportrDisburserTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _TeleportrDisburser.Contract.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_TeleportrDisburser *TeleportrDisburserTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _TeleportrDisburser.Contract.contract.Transact(opts, method, params...)
}
// Owner is a free data retrieval call binding the contract method 0x8da5cb5b.
//
// Solidity: function owner() view returns(address)
func (_TeleportrDisburser *TeleportrDisburserCaller) Owner(opts *bind.CallOpts) (common.Address, error) {
var out []interface{}
err := _TeleportrDisburser.contract.Call(opts, &out, "owner")
if err != nil {
return *new(common.Address), err
}
out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
return out0, err
}
// Owner is a free data retrieval call binding the contract method 0x8da5cb5b.
//
// Solidity: function owner() view returns(address)
func (_TeleportrDisburser *TeleportrDisburserSession) Owner() (common.Address, error) {
return _TeleportrDisburser.Contract.Owner(&_TeleportrDisburser.CallOpts)
}
// Owner is a free data retrieval call binding the contract method 0x8da5cb5b.
//
// Solidity: function owner() view returns(address)
func (_TeleportrDisburser *TeleportrDisburserCallerSession) Owner() (common.Address, error) {
return _TeleportrDisburser.Contract.Owner(&_TeleportrDisburser.CallOpts)
}
// TotalDisbursements is a free data retrieval call binding the contract method 0x25999e7f.
//
// Solidity: function totalDisbursements() view returns(uint256)
func (_TeleportrDisburser *TeleportrDisburserCaller) TotalDisbursements(opts *bind.CallOpts) (*big.Int, error) {
var out []interface{}
err := _TeleportrDisburser.contract.Call(opts, &out, "totalDisbursements")
if err != nil {
return *new(*big.Int), err
}
out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
return out0, err
}
// TotalDisbursements is a free data retrieval call binding the contract method 0x25999e7f.
//
// Solidity: function totalDisbursements() view returns(uint256)
func (_TeleportrDisburser *TeleportrDisburserSession) TotalDisbursements() (*big.Int, error) {
return _TeleportrDisburser.Contract.TotalDisbursements(&_TeleportrDisburser.CallOpts)
}
// TotalDisbursements is a free data retrieval call binding the contract method 0x25999e7f.
//
// Solidity: function totalDisbursements() view returns(uint256)
func (_TeleportrDisburser *TeleportrDisburserCallerSession) TotalDisbursements() (*big.Int, error) {
return _TeleportrDisburser.Contract.TotalDisbursements(&_TeleportrDisburser.CallOpts)
}
// Disburse is a paid mutator transaction binding the contract method 0xad48144d.
//
// Solidity: function disburse(uint256 _nextDepositId, (uint256,address)[] _disbursements) payable returns()
func (_TeleportrDisburser *TeleportrDisburserTransactor) Disburse(opts *bind.TransactOpts, _nextDepositId *big.Int, _disbursements []TeleportrDisburserDisbursement) (*types.Transaction, error) {
return _TeleportrDisburser.contract.Transact(opts, "disburse", _nextDepositId, _disbursements)
}
// Disburse is a paid mutator transaction binding the contract method 0xad48144d.
//
// Solidity: function disburse(uint256 _nextDepositId, (uint256,address)[] _disbursements) payable returns()
func (_TeleportrDisburser *TeleportrDisburserSession) Disburse(_nextDepositId *big.Int, _disbursements []TeleportrDisburserDisbursement) (*types.Transaction, error) {
return _TeleportrDisburser.Contract.Disburse(&_TeleportrDisburser.TransactOpts, _nextDepositId, _disbursements)
}
// Disburse is a paid mutator transaction binding the contract method 0xad48144d.
//
// Solidity: function disburse(uint256 _nextDepositId, (uint256,address)[] _disbursements) payable returns()
func (_TeleportrDisburser *TeleportrDisburserTransactorSession) Disburse(_nextDepositId *big.Int, _disbursements []TeleportrDisburserDisbursement) (*types.Transaction, error) {
return _TeleportrDisburser.Contract.Disburse(&_TeleportrDisburser.TransactOpts, _nextDepositId, _disbursements)
}
// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6.
//
// Solidity: function renounceOwnership() returns()
func (_TeleportrDisburser *TeleportrDisburserTransactor) RenounceOwnership(opts *bind.TransactOpts) (*types.Transaction, error) {
return _TeleportrDisburser.contract.Transact(opts, "renounceOwnership")
}
// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6.
//
// Solidity: function renounceOwnership() returns()
func (_TeleportrDisburser *TeleportrDisburserSession) RenounceOwnership() (*types.Transaction, error) {
return _TeleportrDisburser.Contract.RenounceOwnership(&_TeleportrDisburser.TransactOpts)
}
// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6.
//
// Solidity: function renounceOwnership() returns()
func (_TeleportrDisburser *TeleportrDisburserTransactorSession) RenounceOwnership() (*types.Transaction, error) {
return _TeleportrDisburser.Contract.RenounceOwnership(&_TeleportrDisburser.TransactOpts)
}
// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b.
//
// Solidity: function transferOwnership(address newOwner) returns()
func (_TeleportrDisburser *TeleportrDisburserTransactor) TransferOwnership(opts *bind.TransactOpts, newOwner common.Address) (*types.Transaction, error) {
return _TeleportrDisburser.contract.Transact(opts, "transferOwnership", newOwner)
}
// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b.
//
// Solidity: function transferOwnership(address newOwner) returns()
func (_TeleportrDisburser *TeleportrDisburserSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) {
return _TeleportrDisburser.Contract.TransferOwnership(&_TeleportrDisburser.TransactOpts, newOwner)
}
// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b.
//
// Solidity: function transferOwnership(address newOwner) returns()
func (_TeleportrDisburser *TeleportrDisburserTransactorSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) {
return _TeleportrDisburser.Contract.TransferOwnership(&_TeleportrDisburser.TransactOpts, newOwner)
}
// WithdrawBalance is a paid mutator transaction binding the contract method 0x5fd8c710.
//
// Solidity: function withdrawBalance() returns()
func (_TeleportrDisburser *TeleportrDisburserTransactor) WithdrawBalance(opts *bind.TransactOpts) (*types.Transaction, error) {
return _TeleportrDisburser.contract.Transact(opts, "withdrawBalance")
}
// WithdrawBalance is a paid mutator transaction binding the contract method 0x5fd8c710.
//
// Solidity: function withdrawBalance() returns()
func (_TeleportrDisburser *TeleportrDisburserSession) WithdrawBalance() (*types.Transaction, error) {
return _TeleportrDisburser.Contract.WithdrawBalance(&_TeleportrDisburser.TransactOpts)
}
// WithdrawBalance is a paid mutator transaction binding the contract method 0x5fd8c710.
//
// Solidity: function withdrawBalance() returns()
func (_TeleportrDisburser *TeleportrDisburserTransactorSession) WithdrawBalance() (*types.Transaction, error) {
return _TeleportrDisburser.Contract.WithdrawBalance(&_TeleportrDisburser.TransactOpts)
}
// TeleportrDisburserBalanceWithdrawnIterator is returned from FilterBalanceWithdrawn and is used to iterate over the raw logs and unpacked data for BalanceWithdrawn events raised by the TeleportrDisburser contract.
type TeleportrDisburserBalanceWithdrawnIterator struct {
Event *TeleportrDisburserBalanceWithdrawn // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *TeleportrDisburserBalanceWithdrawnIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(TeleportrDisburserBalanceWithdrawn)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(TeleportrDisburserBalanceWithdrawn)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *TeleportrDisburserBalanceWithdrawnIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *TeleportrDisburserBalanceWithdrawnIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// TeleportrDisburserBalanceWithdrawn represents a BalanceWithdrawn event raised by the TeleportrDisburser contract.
type TeleportrDisburserBalanceWithdrawn struct {
Owner common.Address
Balance *big.Int
Raw types.Log // Blockchain specific contextual infos
}
// FilterBalanceWithdrawn is a free log retrieval operation binding the contract event 0xddc398b321237a8d40ac914388309c2f52a08c134e4dc4ce61e32f57cb7d80f1.
//
// Solidity: event BalanceWithdrawn(address indexed owner, uint256 balance)
func (_TeleportrDisburser *TeleportrDisburserFilterer) FilterBalanceWithdrawn(opts *bind.FilterOpts, owner []common.Address) (*TeleportrDisburserBalanceWithdrawnIterator, error) {
var ownerRule []interface{}
for _, ownerItem := range owner {
ownerRule = append(ownerRule, ownerItem)
}
logs, sub, err := _TeleportrDisburser.contract.FilterLogs(opts, "BalanceWithdrawn", ownerRule)
if err != nil {
return nil, err
}
return &TeleportrDisburserBalanceWithdrawnIterator{contract: _TeleportrDisburser.contract, event: "BalanceWithdrawn", logs: logs, sub: sub}, nil
}
// WatchBalanceWithdrawn is a free log subscription operation binding the contract event 0xddc398b321237a8d40ac914388309c2f52a08c134e4dc4ce61e32f57cb7d80f1.
//
// Solidity: event BalanceWithdrawn(address indexed owner, uint256 balance)
func (_TeleportrDisburser *TeleportrDisburserFilterer) WatchBalanceWithdrawn(opts *bind.WatchOpts, sink chan<- *TeleportrDisburserBalanceWithdrawn, owner []common.Address) (event.Subscription, error) {
var ownerRule []interface{}
for _, ownerItem := range owner {
ownerRule = append(ownerRule, ownerItem)
}
logs, sub, err := _TeleportrDisburser.contract.WatchLogs(opts, "BalanceWithdrawn", ownerRule)
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(TeleportrDisburserBalanceWithdrawn)
if err := _TeleportrDisburser.contract.UnpackLog(event, "BalanceWithdrawn", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ParseBalanceWithdrawn is a log parse operation binding the contract event 0xddc398b321237a8d40ac914388309c2f52a08c134e4dc4ce61e32f57cb7d80f1.
//
// Solidity: event BalanceWithdrawn(address indexed owner, uint256 balance)
func (_TeleportrDisburser *TeleportrDisburserFilterer) ParseBalanceWithdrawn(log types.Log) (*TeleportrDisburserBalanceWithdrawn, error) {
event := new(TeleportrDisburserBalanceWithdrawn)
if err := _TeleportrDisburser.contract.UnpackLog(event, "BalanceWithdrawn", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
// TeleportrDisburserDisbursementFailedIterator is returned from FilterDisbursementFailed and is used to iterate over the raw logs and unpacked data for DisbursementFailed events raised by the TeleportrDisburser contract.
type TeleportrDisburserDisbursementFailedIterator struct {
Event *TeleportrDisburserDisbursementFailed // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *TeleportrDisburserDisbursementFailedIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(TeleportrDisburserDisbursementFailed)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(TeleportrDisburserDisbursementFailed)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *TeleportrDisburserDisbursementFailedIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *TeleportrDisburserDisbursementFailedIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// TeleportrDisburserDisbursementFailed represents a DisbursementFailed event raised by the TeleportrDisburser contract.
type TeleportrDisburserDisbursementFailed struct {
DepositId *big.Int
To common.Address
Amount *big.Int
Raw types.Log // Blockchain specific contextual infos
}
// FilterDisbursementFailed is a free log retrieval operation binding the contract event 0x9b478c095979d3d3a7d602ffd9ee1f0843204d853558ae0882c8fcc0a5bc78cf.
//
// Solidity: event DisbursementFailed(uint256 indexed depositId, address indexed to, uint256 amount)
func (_TeleportrDisburser *TeleportrDisburserFilterer) FilterDisbursementFailed(opts *bind.FilterOpts, depositId []*big.Int, to []common.Address) (*TeleportrDisburserDisbursementFailedIterator, error) {
var depositIdRule []interface{}
for _, depositIdItem := range depositId {
depositIdRule = append(depositIdRule, depositIdItem)
}
var toRule []interface{}
for _, toItem := range to {
toRule = append(toRule, toItem)
}
logs, sub, err := _TeleportrDisburser.contract.FilterLogs(opts, "DisbursementFailed", depositIdRule, toRule)
if err != nil {
return nil, err
}
return &TeleportrDisburserDisbursementFailedIterator{contract: _TeleportrDisburser.contract, event: "DisbursementFailed", logs: logs, sub: sub}, nil
}
// WatchDisbursementFailed is a free log subscription operation binding the contract event 0x9b478c095979d3d3a7d602ffd9ee1f0843204d853558ae0882c8fcc0a5bc78cf.
//
// Solidity: event DisbursementFailed(uint256 indexed depositId, address indexed to, uint256 amount)
func (_TeleportrDisburser *TeleportrDisburserFilterer) WatchDisbursementFailed(opts *bind.WatchOpts, sink chan<- *TeleportrDisburserDisbursementFailed, depositId []*big.Int, to []common.Address) (event.Subscription, error) {
var depositIdRule []interface{}
for _, depositIdItem := range depositId {
depositIdRule = append(depositIdRule, depositIdItem)
}
var toRule []interface{}
for _, toItem := range to {
toRule = append(toRule, toItem)
}
logs, sub, err := _TeleportrDisburser.contract.WatchLogs(opts, "DisbursementFailed", depositIdRule, toRule)
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(TeleportrDisburserDisbursementFailed)
if err := _TeleportrDisburser.contract.UnpackLog(event, "DisbursementFailed", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ParseDisbursementFailed is a log parse operation binding the contract event 0x9b478c095979d3d3a7d602ffd9ee1f0843204d853558ae0882c8fcc0a5bc78cf.
//
// Solidity: event DisbursementFailed(uint256 indexed depositId, address indexed to, uint256 amount)
func (_TeleportrDisburser *TeleportrDisburserFilterer) ParseDisbursementFailed(log types.Log) (*TeleportrDisburserDisbursementFailed, error) {
event := new(TeleportrDisburserDisbursementFailed)
if err := _TeleportrDisburser.contract.UnpackLog(event, "DisbursementFailed", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
// TeleportrDisburserDisbursementSuccessIterator is returned from FilterDisbursementSuccess and is used to iterate over the raw logs and unpacked data for DisbursementSuccess events raised by the TeleportrDisburser contract.
type TeleportrDisburserDisbursementSuccessIterator struct {
Event *TeleportrDisburserDisbursementSuccess // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *TeleportrDisburserDisbursementSuccessIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(TeleportrDisburserDisbursementSuccess)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(TeleportrDisburserDisbursementSuccess)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *TeleportrDisburserDisbursementSuccessIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *TeleportrDisburserDisbursementSuccessIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// TeleportrDisburserDisbursementSuccess represents a DisbursementSuccess event raised by the TeleportrDisburser contract.
type TeleportrDisburserDisbursementSuccess struct {
DepositId *big.Int
To common.Address
Amount *big.Int
Raw types.Log // Blockchain specific contextual infos
}
// FilterDisbursementSuccess is a free log retrieval operation binding the contract event 0xeaa22fd2d7b875476355b32cf719794faf9d91b66e73bc6375a053cace9caaee.
//
// Solidity: event DisbursementSuccess(uint256 indexed depositId, address indexed to, uint256 amount)
func (_TeleportrDisburser *TeleportrDisburserFilterer) FilterDisbursementSuccess(opts *bind.FilterOpts, depositId []*big.Int, to []common.Address) (*TeleportrDisburserDisbursementSuccessIterator, error) {
var depositIdRule []interface{}
for _, depositIdItem := range depositId {
depositIdRule = append(depositIdRule, depositIdItem)
}
var toRule []interface{}
for _, toItem := range to {
toRule = append(toRule, toItem)
}
logs, sub, err := _TeleportrDisburser.contract.FilterLogs(opts, "DisbursementSuccess", depositIdRule, toRule)
if err != nil {
return nil, err
}
return &TeleportrDisburserDisbursementSuccessIterator{contract: _TeleportrDisburser.contract, event: "DisbursementSuccess", logs: logs, sub: sub}, nil
}
// WatchDisbursementSuccess is a free log subscription operation binding the contract event 0xeaa22fd2d7b875476355b32cf719794faf9d91b66e73bc6375a053cace9caaee.
//
// Solidity: event DisbursementSuccess(uint256 indexed depositId, address indexed to, uint256 amount)
func (_TeleportrDisburser *TeleportrDisburserFilterer) WatchDisbursementSuccess(opts *bind.WatchOpts, sink chan<- *TeleportrDisburserDisbursementSuccess, depositId []*big.Int, to []common.Address) (event.Subscription, error) {
var depositIdRule []interface{}
for _, depositIdItem := range depositId {
depositIdRule = append(depositIdRule, depositIdItem)
}
var toRule []interface{}
for _, toItem := range to {
toRule = append(toRule, toItem)
}
logs, sub, err := _TeleportrDisburser.contract.WatchLogs(opts, "DisbursementSuccess", depositIdRule, toRule)
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(TeleportrDisburserDisbursementSuccess)
if err := _TeleportrDisburser.contract.UnpackLog(event, "DisbursementSuccess", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ParseDisbursementSuccess is a log parse operation binding the contract event 0xeaa22fd2d7b875476355b32cf719794faf9d91b66e73bc6375a053cace9caaee.
//
// Solidity: event DisbursementSuccess(uint256 indexed depositId, address indexed to, uint256 amount)
func (_TeleportrDisburser *TeleportrDisburserFilterer) ParseDisbursementSuccess(log types.Log) (*TeleportrDisburserDisbursementSuccess, error) {
event := new(TeleportrDisburserDisbursementSuccess)
if err := _TeleportrDisburser.contract.UnpackLog(event, "DisbursementSuccess", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
// TeleportrDisburserOwnershipTransferredIterator is returned from FilterOwnershipTransferred and is used to iterate over the raw logs and unpacked data for OwnershipTransferred events raised by the TeleportrDisburser contract.
type TeleportrDisburserOwnershipTransferredIterator struct {
Event *TeleportrDisburserOwnershipTransferred // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *TeleportrDisburserOwnershipTransferredIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(TeleportrDisburserOwnershipTransferred)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(TeleportrDisburserOwnershipTransferred)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *TeleportrDisburserOwnershipTransferredIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *TeleportrDisburserOwnershipTransferredIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// TeleportrDisburserOwnershipTransferred represents a OwnershipTransferred event raised by the TeleportrDisburser contract.
type TeleportrDisburserOwnershipTransferred struct {
PreviousOwner common.Address
NewOwner common.Address
Raw types.Log // Blockchain specific contextual infos
}
// FilterOwnershipTransferred is a free log retrieval operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0.
//
// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)
func (_TeleportrDisburser *TeleportrDisburserFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*TeleportrDisburserOwnershipTransferredIterator, error) {
var previousOwnerRule []interface{}
for _, previousOwnerItem := range previousOwner {
previousOwnerRule = append(previousOwnerRule, previousOwnerItem)
}
var newOwnerRule []interface{}
for _, newOwnerItem := range newOwner {
newOwnerRule = append(newOwnerRule, newOwnerItem)
}
logs, sub, err := _TeleportrDisburser.contract.FilterLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule)
if err != nil {
return nil, err
}
return &TeleportrDisburserOwnershipTransferredIterator{contract: _TeleportrDisburser.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil
}
// WatchOwnershipTransferred is a free log subscription operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0.
//
// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)
func (_TeleportrDisburser *TeleportrDisburserFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *TeleportrDisburserOwnershipTransferred, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) {
var previousOwnerRule []interface{}
for _, previousOwnerItem := range previousOwner {
previousOwnerRule = append(previousOwnerRule, previousOwnerItem)
}
var newOwnerRule []interface{}
for _, newOwnerItem := range newOwner {
newOwnerRule = append(newOwnerRule, newOwnerItem)
}
logs, sub, err := _TeleportrDisburser.contract.WatchLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule)
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(TeleportrDisburserOwnershipTransferred)
if err := _TeleportrDisburser.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ParseOwnershipTransferred is a log parse operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0.
//
// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)
func (_TeleportrDisburser *TeleportrDisburserFilterer) ParseOwnershipTransferred(log types.Log) (*TeleportrDisburserOwnershipTransferred, error) {
event := new(TeleportrDisburserOwnershipTransferred)
if err := _TeleportrDisburser.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil {
return nil, err
}
event.Raw = log
return event, nil
}
package main
import (
"fmt"
"os"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
"github.com/ethereum-optimism/optimism/go/teleportr"
"github.com/ethereum-optimism/optimism/go/teleportr/flags"
)
var (
GitVersion = ""
GitCommit = ""
GitDate = ""
)
func main() {
// Set up logger with a default INFO level in case we fail to parse flags.
// Otherwise the final critical log won't show what the parsing error was.
log.Root().SetHandler(
log.LvlFilterHandler(
log.LvlInfo,
log.StreamHandler(os.Stdout, log.TerminalFormat(true)),
),
)
app := cli.NewApp()
app.Flags = flags.Flags
app.Version = fmt.Sprintf("%s-%s-%s", GitVersion, GitCommit, GitDate)
app.Name = "teleportr"
app.Usage = "Teleportr"
app.Description = "Teleportr bridge from L1 to L2"
app.Action = teleportr.Main(GitVersion)
err := app.Run(os.Args)
if err != nil {
log.Crit("Application failed", "message", err)
}
}
package teleportr
import (
"time"
"github.com/ethereum-optimism/optimism/go/teleportr/flags"
"github.com/urfave/cli"
)
type Config struct {
/* Required Params */
// BuildEnv identifies the environment this binary is intended for, i.e.
// production, development, etc.
BuildEnv string
// EthNetworkName identifies the intended Ethereum network.
EthNetworkName string
// L1EthRpc is the HTTP provider URL for L1.
L1EthRpc string
// L2EthRpc is the HTTP provider URL for L1.
L2EthRpc string
// DepositAddress is the TeleportrDeposit contract adddress.
DepositAddress string
// DepositDeployBlockNumber is the deployment block number of the
// TeleportrDeposit contract.
DepositDeployBlockNumber uint64
// FilterQueryMaxBlocks is the maximum range of a filter query in blocks.
FilterQueryMaxBlocks uint64
// DisburserAddress is the TeleportrDisburser contract address.
DisburserAddress string
// MaxL2TxSize is the maximum size in bytes of any L2 transactions generated
// for teleportr disbursements.
MaxL2TxSize uint64
// NumDepositConfirmations is the number of confirmations required before a
// deposit is considered confirmed.
NumDepositConfirmations uint64
// PollInterval is the delay between querying L2 for more transaction
// and creating a new batch.
PollInterval time.Duration
// SafeAbortNonceTooLowCount is the number of ErrNonceTooLowObservations
// required to give up on a tx at a particular nonce without receiving
// confirmation.
SafeAbortNonceTooLowCount uint64
// ResubmissionTimeout is time we will wait before resubmitting a
// transaction.
ResubmissionTimeout time.Duration
// PostgresHost is the host of the teleportr postgres instance.
PostgresHost string
// PostgresPort is the port of the teleportr postgres instance.
PostgresPort uint16
// PostgresUser is the username for the teleportr postgres instance.
PostgresUser string
// PostgresPassword is the password for the teleportr postgres instance.
PostgresPassword string
// PostgresDBName is the database name of the teleportr postgres instance.
PostgresDBName string
// PostgresEnableSSL determines whether or not to enable SSL on connections
// to the teleportr postgres instance.
PostgresEnableSSL bool
/* Optional Params */
// LogLevel is the lowest log level that will be output.
LogLevel string
// LogTerminal if true, prints to stdout in terminal format, otherwise
// prints using JSON. If SentryEnable is true this flag is ignored, and logs
// are printed using JSON.
LogTerminal bool
// DisburserPrivKey the private key of the wallet used to submit
// transactions to the TeleportrDisburser contract.
DisburserPrivKey string
// Mnemonic is the HD seed used to derive the wallet private key for
// submitting to the TeleportrDisburser. Must be used in conjunction with
// DisburserHDPath.
Mnemonic string
// DisburserHDPath is the derivation path used to obtain the private key for
// the disburser transactions.
DisburserHDPath string
// MetricsServerEnable if true, will create a metrics client and log to
// Prometheus.
MetricsServerEnable bool
// MetricsHostname is the hostname at which the metrics server is running.
MetricsHostname string
// MetricsPort is the port at which the metrics server is running.
MetricsPort uint64
// DisableHTTP2 disables HTTP2 support.
DisableHTTP2 bool
}
func NewConfig(ctx *cli.Context) (Config, error) {
return Config{
/* Required Flags */
BuildEnv: ctx.GlobalString(flags.BuildEnvFlag.Name),
EthNetworkName: ctx.GlobalString(flags.EthNetworkNameFlag.Name),
L1EthRpc: ctx.GlobalString(flags.L1EthRpcFlag.Name),
L2EthRpc: ctx.GlobalString(flags.L2EthRpcFlag.Name),
DepositAddress: ctx.GlobalString(flags.DepositAddressFlag.Name),
DepositDeployBlockNumber: ctx.GlobalUint64(flags.DepositDeployBlockNumberFlag.Name),
DisburserAddress: ctx.GlobalString(flags.DisburserAddressFlag.Name),
MaxL2TxSize: ctx.GlobalUint64(flags.MaxL2TxSizeFlag.Name),
NumDepositConfirmations: ctx.GlobalUint64(flags.NumDepositConfirmationsFlag.Name),
FilterQueryMaxBlocks: ctx.GlobalUint64(flags.FilterQueryMaxBlocksFlag.Name),
PollInterval: ctx.GlobalDuration(flags.PollIntervalFlag.Name),
SafeAbortNonceTooLowCount: ctx.GlobalUint64(flags.SafeAbortNonceTooLowCountFlag.Name),
ResubmissionTimeout: ctx.GlobalDuration(flags.ResubmissionTimeoutFlag.Name),
PostgresHost: ctx.GlobalString(flags.PostgresHostFlag.Name),
PostgresPort: uint16(ctx.GlobalUint64(flags.PostgresPortFlag.Name)),
PostgresUser: ctx.GlobalString(flags.PostgresUserFlag.Name),
PostgresPassword: ctx.GlobalString(flags.PostgresPasswordFlag.Name),
PostgresDBName: ctx.GlobalString(flags.PostgresDBNameFlag.Name),
PostgresEnableSSL: ctx.GlobalBool(flags.PostgresEnableSSLFlag.Name),
/* Optional flags */
LogLevel: ctx.GlobalString(flags.LogLevelFlag.Name),
LogTerminal: ctx.GlobalBool(flags.LogTerminalFlag.Name),
DisburserPrivKey: ctx.GlobalString(flags.DisburserPrivateKeyFlag.Name),
Mnemonic: ctx.GlobalString(flags.MnemonicFlag.Name),
DisburserHDPath: ctx.GlobalString(flags.DisburserHDPathFlag.Name),
MetricsServerEnable: ctx.GlobalBool(flags.MetricsServerEnableFlag.Name),
MetricsHostname: ctx.GlobalString(flags.MetricsHostnameFlag.Name),
MetricsPort: ctx.GlobalUint64(flags.MetricsPortFlag.Name),
DisableHTTP2: ctx.GlobalBool(flags.HTTP2DisableFlag.Name),
}, nil
}
......@@ -24,9 +24,9 @@ var (
// Deposit represents an event emitted from the TeleportrDeposit contract on L1,
// along with additional info about the tx that generated the event.
type Deposit struct {
ID int64
ID uint64
TxnHash common.Hash
BlockNumber int64
BlockNumber uint64
BlockTimestamp time.Time
Address common.Address
Amount *big.Int
......@@ -35,16 +35,17 @@ type Deposit struct {
// ConfirmationInfo holds metadata about a tx on either the L1 or L2 chain.
type ConfirmationInfo struct {
TxnHash common.Hash
BlockNumber int64
BlockNumber uint64
BlockTimestamp time.Time
}
// CompletedTeleport represents an L1 deposit that has been disbursed on L2. The
// struct also hold info about the L1 and L2 txns involved.
type CompletedTeleport struct {
ID int64
ID uint64
Address common.Address
Amount *big.Int
Success bool
Deposit ConfirmationInfo
Disbursement ConfirmationInfo
}
......@@ -65,13 +66,32 @@ CREATE TABLE IF NOT EXISTS disbursements (
id INT8 NOT NULL PRIMARY KEY REFERENCES deposits(id),
txn_hash VARCHAR NOT NULL,
block_number INT8 NOT NULL,
block_timestamp TIMESTAMPTZ NOT NULL
block_timestamp TIMESTAMPTZ NOT NULL,
success BOOL NOT NULL
);
`
const lastProcessedBlockTable = `
CREATE TABLE IF NOT EXISTS last_processed_block (
id BOOL PRIMARY KEY DEFAULT TRUE,
value INT8 NOT NULL,
CONSTRAINT id CHECK (id)
);
`
const pendingTxTable = `
CREATE TABLE IF NOT EXISTS pending_txs (
txn_hash VARCHAR NOT NULL PRIMARY KEY,
start_id INT8 NOT NULL,
end_id INT8 NOT NULL
);
`
var migrations = []string{
createDepositsTable,
createDisbursementsTable,
lastProcessedBlockTable,
pendingTxTable,
}
// Config houses the data required to connect to a Postgres backend.
......@@ -155,6 +175,13 @@ func (d *Database) Close() error {
return d.conn.Close()
}
const upsertLastProcessedBlock = `
INSERT INTO last_processed_block (value)
VALUES ($1)
ON CONFLICT (id) DO UPDATE
SET value = $1
`
const upsertDepositStatement = `
INSERT INTO deposits (id, txn_hash, block_number, block_timestamp, address, amount)
VALUES ($1, $2, $3, $4, $5, $6)
......@@ -164,10 +191,10 @@ SET (txn_hash, block_number, block_timestamp, address, amount) = ($2, $3, $4, $5
// UpsertDeposits inserts a list of deposits into the database, or updats an
// existing deposit in place if the same ID is found.
func (d *Database) UpsertDeposits(deposits []Deposit) error {
if len(deposits) == 0 {
return nil
}
func (d *Database) UpsertDeposits(
deposits []Deposit,
lastProcessedBlock uint64,
) error {
// Sanity check deposits.
for _, deposit := range deposits {
......@@ -180,10 +207,11 @@ func (d *Database) UpsertDeposits(deposits []Deposit) error {
if err != nil {
return err
}
defer tx.Rollback()
defer func() {
_ = tx.Rollback()
}()
for _, deposit := range deposits {
_, err = tx.Exec(
upsertDepositStatement,
deposit.ID,
......@@ -198,29 +226,30 @@ func (d *Database) UpsertDeposits(deposits []Deposit) error {
}
}
_, err = tx.Exec(upsertLastProcessedBlock, lastProcessedBlock)
if err != nil {
return err
}
return tx.Commit()
}
const latestDepositQuery = `
SELECT block_number FROM deposits
ORDER BY block_number DESC
LIMIT 1
const lastProcessedBlockQuery = `
SELECT value FROM last_processed_block
`
// LatestDeposit returns the block number of the latest deposit known to the
// database.
func (d *Database) LatestDeposit() (*int64, error) {
row := d.conn.QueryRow(latestDepositQuery)
func (d *Database) LastProcessedBlock() (*uint64, error) {
row := d.conn.QueryRow(lastProcessedBlockQuery)
var latestTransfer int64
err := row.Scan(&latestTransfer)
var lastProcessedBlock uint64
err := row.Scan(&lastProcessedBlock)
if err == sql.ErrNoRows {
return nil, nil
} else if err != nil {
return nil, err
}
return &latestTransfer, nil
return &lastProcessedBlock, nil
}
const confirmedDepositsQuery = `
......@@ -233,7 +262,7 @@ ORDER BY dep.id ASC
// ConfirmedDeposits returns the set of all deposits that have sufficient
// confirmation, but do not have a recorded disbursement.
func (d *Database) ConfirmedDeposits(blockNumber, confirmations int64) ([]Deposit, error) {
func (d *Database) ConfirmedDeposits(blockNumber, confirmations uint64) ([]Deposit, error) {
rows, err := d.conn.Query(confirmedDepositsQuery, confirmations, blockNumber)
if err != nil {
return nil, err
......@@ -275,20 +304,43 @@ func (d *Database) ConfirmedDeposits(blockNumber, confirmations int64) ([]Deposi
return deposits, nil
}
const latestDisbursementIDQuery = `
SELECT id FROM disbursements
ORDER BY id DESC
LIMIT 1
`
// LatestDisbursementID returns the latest deposit id known to the database that
// has a recorded disbursement.
func (d *Database) LatestDisbursementID() (*uint64, error) {
row := d.conn.QueryRow(latestDisbursementIDQuery)
var latestDisbursementID uint64
err := row.Scan(&latestDisbursementID)
if err == sql.ErrNoRows {
return nil, nil
} else if err != nil {
return nil, err
}
return &latestDisbursementID, nil
}
const markDisbursedStatement = `
INSERT INTO disbursements (id, txn_hash, block_number, block_timestamp)
VALUES ($1, $2, $3, $4)
INSERT INTO disbursements (id, txn_hash, block_number, block_timestamp, success)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (id) DO UPDATE
SET (txn_hash, block_number, block_timestamp) = ($2, $3, $4)
SET (txn_hash, block_number, block_timestamp, success) = ($2, $3, $4, $5)
`
// UpsertDisbursement inserts a disbursement, or updates an existing record
// in-place if the ID already exists.
func (d *Database) UpsertDisbursement(
id int64,
id uint64,
txnHash common.Hash,
blockNumber int64,
blockNumber uint64,
blockTimestamp time.Time,
success bool,
) error {
if blockTimestamp.IsZero() {
return ErrZeroTimestamp
......@@ -300,6 +352,7 @@ func (d *Database) UpsertDisbursement(
txnHash.String(),
blockNumber,
blockTimestamp,
success,
)
if err != nil {
if strings.Contains(err.Error(), "violates foreign key constraint") {
......@@ -320,7 +373,7 @@ func (d *Database) UpsertDisbursement(
const completedTeleportsQuery = `
SELECT
dep.id, dep.address, dep.amount,
dep.id, dep.address, dep.amount, dis.success,
dep.txn_hash, dep.block_number, dep.block_timestamp,
dis.txn_hash, dis.block_number, dis.block_timestamp
FROM deposits AS dep, disbursements AS dis
......@@ -348,6 +401,7 @@ func (d *Database) CompletedTeleports() ([]CompletedTeleport, error) {
&teleport.ID,
&addressStr,
&amountStr,
&teleport.Success,
&depTxnHashStr,
&teleport.Deposit.BlockNumber,
&teleport.Deposit.BlockTimestamp,
......@@ -377,3 +431,88 @@ func (d *Database) CompletedTeleports() ([]CompletedTeleport, error) {
return teleports, nil
}
// PendingTx encapsulates the metadata stored about published disbursement txs.
type PendingTx struct {
// Txhash is the tx hash of the disbursement tx.
TxHash common.Hash
// StartID is the deposit id of the first disbursement, inclusive.
StartID uint64
// EndID is the deposit id fo the last disbursement, exclusive.
EndID uint64
}
const upsertPendingTxStatement = `
INSERT INTO pending_txs (txn_hash, start_id, end_id)
VALUES ($1, $2, $3)
ON CONFLICT (txn_hash) DO UPDATE
SET (start_id, end_id) = ($2, $3)
`
// UpsertPendingTx inserts a disbursement, or updates the entry if the TxHash
// already exists.
func (d *Database) UpsertPendingTx(pendingTx PendingTx) error {
_, err := d.conn.Exec(
upsertPendingTxStatement,
pendingTx.TxHash.String(),
pendingTx.StartID,
pendingTx.EndID,
)
return err
}
const listPendingTxsQuery = `
SELECT txn_hash, start_id, end_id
FROM pending_txs
ORDER BY start_id DESC, end_id DESC, txn_hash ASC
`
// ListPendingTxs returns all pending txs stored in the database.
func (d *Database) ListPendingTxs() ([]PendingTx, error) {
rows, err := d.conn.Query(listPendingTxsQuery)
if err != nil {
return nil, err
}
defer rows.Close()
var pendingTxs []PendingTx
for rows.Next() {
var pendingTx PendingTx
var txHashStr string
err = rows.Scan(
&txHashStr,
&pendingTx.StartID,
&pendingTx.EndID,
)
if err != nil {
return nil, err
}
pendingTx.TxHash = common.HexToHash(txHashStr)
pendingTxs = append(pendingTxs, pendingTx)
}
if err := rows.Err(); err != nil {
return nil, err
}
return pendingTxs, nil
}
const deletePendingTxsStatement = `
DELETE FROM pending_txs
WHERE start_id = $1 AND end_id = $2
`
// DeletePendingTx removes any pending txs with matching start and end ids. This
// allows the caller to remove any logically-conflicting pending txs from the
// database after successfully processing the outcomes.
func (d *Database) DeletePendingTx(startID, endID uint64) error {
_, err := d.conn.Exec(
deletePendingTxsStatement,
startID,
endID,
)
return err
}
......@@ -55,18 +55,18 @@ func TestOpenClose(t *testing.T) {
require.Nil(t, err)
}
// TestUpsert empty deposits asserts that it is safe to call UpsertDeposits with
// an empty list.
// TestUpsertEmptyDeposits empty deposits asserts that it is safe to call
// UpsertDeposits with an empty list.
func TestUpsertEmptyDeposits(t *testing.T) {
t.Parallel()
d := newDatabase(t)
defer d.Close()
err := d.UpsertDeposits(nil)
err := d.UpsertDeposits(nil, 0)
require.Nil(t, err)
err = d.UpsertDeposits([]db.Deposit{})
err = d.UpsertDeposits([]db.Deposit{}, 0)
require.Nil(t, err)
}
......@@ -78,58 +78,10 @@ func TestUpsertDepositWithZeroTimestampFails(t *testing.T) {
d := newDatabase(t)
defer d.Close()
err := d.UpsertDeposits([]db.Deposit{{}})
err := d.UpsertDeposits([]db.Deposit{{}}, 0)
require.Equal(t, db.ErrZeroTimestamp, err)
}
// TestLatestDeposit asserts that the LatestDeposit method properly returns the
// highest block number in the databse, or nil if no items are present.
func TestLatestDeposit(t *testing.T) {
t.Parallel()
d := newDatabase(t)
defer d.Close()
// Query should return nil on empty databse.
latestDeposit, err := d.LatestDeposit()
require.Nil(t, err)
require.Equal(t, (*int64)(nil), latestDeposit)
// Update table to have a single element.
expLatestDeposit := int64(1)
err = d.UpsertDeposits([]db.Deposit{{
ID: 1,
TxnHash: common.HexToHash("0xf1"),
BlockNumber: expLatestDeposit,
BlockTimestamp: testTimestamp,
Address: common.HexToAddress("0xa1"),
Amount: big.NewInt(1),
}})
require.Nil(t, err)
// Query should return block number of only deposit.
latestDeposit, err = d.LatestDeposit()
require.Nil(t, err)
require.Equal(t, &expLatestDeposit, latestDeposit)
// Update table to have two distinct block numbers.
expLatestDeposit = 2
err = d.UpsertDeposits([]db.Deposit{{
ID: 2,
TxnHash: common.HexToHash("0xf2"),
BlockNumber: expLatestDeposit,
BlockTimestamp: testTimestamp,
Address: common.HexToAddress("0xa2"),
Amount: big.NewInt(2),
}})
require.Nil(t, err)
// Query should return the highest of the two block numbers.
latestDeposit, err = d.LatestDeposit()
require.Nil(t, err)
require.Equal(t, &expLatestDeposit, latestDeposit)
}
// TestUpsertDeposits asserts that UpsertDeposits properly overwrites an
// existing entry with the same ID.
func TestUpsertDeposits(t *testing.T) {
......@@ -147,7 +99,7 @@ func TestUpsertDeposits(t *testing.T) {
Amount: big.NewInt(1),
}
err := d.UpsertDeposits([]db.Deposit{deposit1})
err := d.UpsertDeposits([]db.Deposit{deposit1}, 0)
require.Nil(t, err)
deposits, err := d.ConfirmedDeposits(1, 1)
......@@ -163,7 +115,7 @@ func TestUpsertDeposits(t *testing.T) {
Amount: big.NewInt(2),
}
err = d.UpsertDeposits([]db.Deposit{deposit2})
err = d.UpsertDeposits([]db.Deposit{deposit2}, 0)
require.Nil(t, err)
deposits, err = d.ConfirmedDeposits(2, 1)
......@@ -171,6 +123,59 @@ func TestUpsertDeposits(t *testing.T) {
require.Equal(t, deposits, []db.Deposit{deposit2})
}
// TestUpsertDepositsRecordsLastProcessedBlock asserts that calling
// UpsertDeposits properly records the last processed block.
func TestUpsertDepositsRecordsLastProcessedBlock(t *testing.T) {
t.Parallel()
d := newDatabase(t)
defer d.Close()
uint64Ptr := func(x uint64) *uint64 {
return &x
}
// Should be empty initially.
lastProcessedBlock, err := d.LastProcessedBlock()
require.Nil(t, err)
require.Nil(t, lastProcessedBlock)
// Insert nil deposits through block 1.
err = d.UpsertDeposits(nil, 1)
require.Nil(t, err)
// Check that LastProcessedBlock returns 1.
lastProcessedBlock, err = d.LastProcessedBlock()
require.Nil(t, err)
require.Equal(t, uint64Ptr(1), lastProcessedBlock)
// Insert empty deposits through block 2.
err = d.UpsertDeposits([]db.Deposit{}, 2)
require.Nil(t, err)
// Check that LastProcessedBlock returns 2.
lastProcessedBlock, err = d.LastProcessedBlock()
require.Nil(t, err)
require.Equal(t, uint64Ptr(2), lastProcessedBlock)
// Insert real deposit in block 3 with last processed at 4.
deposit := db.Deposit{
ID: 0,
TxnHash: common.HexToHash("0xff03"),
BlockNumber: 3,
BlockTimestamp: testTimestamp,
Address: common.HexToAddress("0xaa03"),
Amount: big.NewInt(3),
}
err = d.UpsertDeposits([]db.Deposit{deposit}, 4)
require.Nil(t, err)
// Check that LastProcessedBlock returns 2.
lastProcessedBlock, err = d.LastProcessedBlock()
require.Nil(t, err)
require.Equal(t, uint64Ptr(4), lastProcessedBlock)
}
// TestConfirmedDeposits asserts that ConfirmedDeposits properly returns the set
// of deposits that have sufficient confirmation, but do not have a recorded
// disbursement.
......@@ -211,7 +216,7 @@ func TestConfirmedDeposits(t *testing.T) {
err = d.UpsertDeposits([]db.Deposit{
deposit1, deposit2, deposit3,
})
}, 0)
require.Nil(t, err)
// First deposit only has 1 conf, should not be found using 2 confs at block
......@@ -230,7 +235,7 @@ func TestConfirmedDeposits(t *testing.T) {
require.Nil(t, err)
require.Equal(t, []db.Deposit{deposit1, deposit2, deposit3}, deposits)
err = d.UpsertDisbursement(deposit1.ID, common.HexToHash("0xdd01"), 1, testTimestamp)
err = d.UpsertDisbursement(deposit1.ID, common.HexToHash("0xdd01"), 1, testTimestamp, true)
require.Nil(t, err)
deposits, err = d.ConfirmedDeposits(2, 1)
......@@ -249,16 +254,16 @@ func TestUpsertDisbursement(t *testing.T) {
address := common.HexToAddress("0xaa01")
amount := big.NewInt(1)
depTxnHash := common.HexToHash("0xdd01")
depBlockNumber := int64(1)
depBlockNumber := uint64(1)
disTxnHash := common.HexToHash("0xee02")
disBlockNumber := int64(2)
disBlockNumber := uint64(2)
// Calling UpsertDisbursement with the zero timestamp should fail.
err := d.UpsertDisbursement(0, common.HexToHash("0xdd00"), 0, time.Time{})
err := d.UpsertDisbursement(0, common.HexToHash("0xdd00"), 0, time.Time{}, true)
require.Equal(t, db.ErrZeroTimestamp, err)
// Calling UpsertDisbursement with an unknown id should fail.
err = d.UpsertDisbursement(0, common.HexToHash("0xdd00"), 0, testTimestamp)
err = d.UpsertDisbursement(0, common.HexToHash("0xdd00"), 0, testTimestamp, true)
require.Equal(t, db.ErrUnknownDeposit, err)
// Now, insert a real deposit that we will disburse.
......@@ -271,22 +276,52 @@ func TestUpsertDisbursement(t *testing.T) {
Address: address,
Amount: amount,
},
})
}, 0)
require.Nil(t, err)
// Mark the deposit as disbursed with some temporary info.
err = d.UpsertDisbursement(1, common.HexToHash("0xee00"), 1, testTimestamp)
tempDisTxnHash := common.HexToHash("0xee00")
tempDisBlockNumber := uint64(1)
err = d.UpsertDisbursement(
1, tempDisTxnHash, tempDisBlockNumber, testTimestamp, false,
)
require.Nil(t, err)
expTeleports := []db.CompletedTeleport{
{
ID: 1,
Address: address,
Amount: amount,
Success: false,
Deposit: db.ConfirmationInfo{
TxnHash: depTxnHash,
BlockNumber: depBlockNumber,
BlockTimestamp: testTimestamp,
},
Disbursement: db.ConfirmationInfo{
TxnHash: tempDisTxnHash,
BlockNumber: tempDisBlockNumber,
BlockTimestamp: testTimestamp,
},
},
}
// Assert that the deposit shows up in the CompletedTeleports method with
// both the L1 and temp L2 confirmation info.
teleports, err := d.CompletedTeleports()
require.Nil(t, err)
require.Equal(t, expTeleports, teleports)
// Overwrite the disbursement info with the final values.
err = d.UpsertDisbursement(1, disTxnHash, disBlockNumber, testTimestamp)
err = d.UpsertDisbursement(1, disTxnHash, disBlockNumber, testTimestamp, true)
require.Nil(t, err)
expTeleports := []db.CompletedTeleport{
expTeleports = []db.CompletedTeleport{
{
ID: 1,
Address: address,
Amount: amount,
Success: true,
Deposit: db.ConfirmationInfo{
TxnHash: depTxnHash,
BlockNumber: depBlockNumber,
......@@ -302,7 +337,139 @@ func TestUpsertDisbursement(t *testing.T) {
// Assert that the deposit now shows up in the CompletedTeleports method
// with both the L1 and L2 confirmation info.
teleports, err := d.CompletedTeleports()
teleports, err = d.CompletedTeleports()
require.Nil(t, err)
require.Equal(t, expTeleports, teleports)
}
// TestUpsertPendingTxs asserts that UpsertPendingTx properly records a pending
// tx, and that it appears in ListPendingTxs on subsequent calls.
func TestUpsertPendingTxs(t *testing.T) {
t.Parallel()
d := newDatabase(t)
defer d.Close()
// Should be empty at first.
pendingTxs, err := d.ListPendingTxs()
require.Nil(t, err)
require.Nil(t, pendingTxs)
// Add first pending tx.
pendingTx1 := db.PendingTx{
TxHash: common.HexToHash("0x11"),
StartID: 0,
EndID: 1,
}
err = d.UpsertPendingTx(pendingTx1)
require.Nil(t, err)
pendingTxs, err = d.ListPendingTxs()
require.Nil(t, err)
require.Equal(t, []db.PendingTx{pendingTx1}, pendingTxs)
// Add second pending tx.
pendingTx2 := db.PendingTx{
TxHash: common.HexToHash("0x22"),
StartID: 0,
EndID: 1,
}
err = d.UpsertPendingTx(pendingTx2)
require.Nil(t, err)
pendingTxs, err = d.ListPendingTxs()
require.Nil(t, err)
require.Equal(t, []db.PendingTx{pendingTx1, pendingTx2}, pendingTxs)
// Readd duplciate pending tx.
err = d.UpsertPendingTx(pendingTx2)
require.Nil(t, err)
pendingTxs, err = d.ListPendingTxs()
require.Nil(t, err)
require.Equal(t, []db.PendingTx{pendingTx1, pendingTx2}, pendingTxs)
// Add third pending tx.
pendingTx3 := db.PendingTx{
TxHash: common.HexToHash("0x33"),
StartID: 1,
EndID: 2,
}
err = d.UpsertPendingTx(pendingTx3)
require.Nil(t, err)
pendingTxs, err = d.ListPendingTxs()
require.Nil(t, err)
require.Equal(t, []db.PendingTx{pendingTx3, pendingTx1, pendingTx2}, pendingTxs)
}
// TestDeletePendingTx asserts that DeletePendingTx properly cleans up the
// pending_txs table when provided with various start/end ids.
func TestDeletePendingTx(t *testing.T) {
t.Parallel()
d := newDatabase(t)
defer d.Close()
pendingTx1 := db.PendingTx{
TxHash: common.HexToHash("0x11"),
StartID: 0,
EndID: 1,
}
pendingTx2 := db.PendingTx{
TxHash: common.HexToHash("0x22"),
StartID: 0,
EndID: 1,
}
pendingTx3 := db.PendingTx{
TxHash: common.HexToHash("0x33"),
StartID: 1,
EndID: 2,
}
err := d.UpsertPendingTx(pendingTx1)
require.Nil(t, err)
err = d.UpsertPendingTx(pendingTx2)
require.Nil(t, err)
err = d.UpsertPendingTx(pendingTx3)
require.Nil(t, err)
pendingTxs, err := d.ListPendingTxs()
require.Nil(t, err)
require.Equal(t, []db.PendingTx{pendingTx3, pendingTx1, pendingTx2}, pendingTxs)
// Delete with indexes that do not match any start/end, no effect.
err = d.DeletePendingTx(3, 4)
require.Nil(t, err)
pendingTxs, err = d.ListPendingTxs()
require.Nil(t, err)
require.Equal(t, []db.PendingTx{pendingTx3, pendingTx1, pendingTx2}, pendingTxs)
// Delete with indexes that matches start but no end, no effect.
err = d.DeletePendingTx(1, 3)
require.Nil(t, err)
pendingTxs, err = d.ListPendingTxs()
require.Nil(t, err)
require.Equal(t, []db.PendingTx{pendingTx3, pendingTx1, pendingTx2}, pendingTxs)
// Delete with indexes that matches end but no start, no effect.
err = d.DeletePendingTx(0, 2)
require.Nil(t, err)
pendingTxs, err = d.ListPendingTxs()
require.Nil(t, err)
require.Equal(t, []db.PendingTx{pendingTx3, pendingTx1, pendingTx2}, pendingTxs)
// Delete with indexes that matches start and end, should remove both.
err = d.DeletePendingTx(0, 1)
require.Nil(t, err)
pendingTxs, err = d.ListPendingTxs()
require.Nil(t, err)
require.Equal(t, []db.PendingTx{pendingTx3}, pendingTxs)
// Delete with indexes that matches start and end, no empty.
err = d.DeletePendingTx(1, 2)
require.Nil(t, err)
pendingTxs, err = d.ListPendingTxs()
require.Nil(t, err)
require.Nil(t, pendingTxs)
}
package disburser
import (
"context"
"crypto/ecdsa"
"errors"
"math"
"math/big"
"strings"
"time"
"github.com/ethereum-optimism/optimism/go/bss-core/metrics"
"github.com/ethereum-optimism/optimism/go/bss-core/txmgr"
"github.com/ethereum-optimism/optimism/go/teleportr/bindings/deposit"
"github.com/ethereum-optimism/optimism/go/teleportr/bindings/disburse"
"github.com/ethereum-optimism/optimism/go/teleportr/db"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
)
// DisbursementSuccessTopic is the topic hash for DisbursementSuccess events.
var DisbursementSuccessTopic = common.HexToHash(
"0xeaa22fd2d7b875476355b32cf719794faf9d91b66e73bc6375a053cace9caaee",
)
// DisbursementFailedTopic is the topic hash for DisbursementFailed events.
var DisbursementFailedTopic = common.HexToHash(
"0x9b478c095979d3d3a7d602ffd9ee1f0843204d853558ae0882c8fcc0a5bc78cf",
)
type Config struct {
Name string
L1Client *ethclient.Client
L2Client *ethclient.Client
Database *db.Database
MaxTxSize uint64
NumConfirmations uint64
DeployBlockNumber uint64
FilterQueryMaxBlocks uint64
DepositAddr common.Address
DisburserAddr common.Address
ChainID *big.Int
PrivKey *ecdsa.PrivateKey
}
type Driver struct {
cfg Config
depositContract *deposit.TeleportrDeposit
disburserContract *disburse.TeleportrDisburser
rawDisburserContract *bind.BoundContract
walletAddr common.Address
metrics *Metrics
currentDepositIDs []uint64
}
func NewDriver(cfg Config) (*Driver, error) {
if cfg.NumConfirmations == 0 {
panic("NumConfirmations cannot be zero")
}
if cfg.FilterQueryMaxBlocks == 0 {
panic("FilterQueryMaxBlocks cannot be zero")
}
depositContract, err := deposit.NewTeleportrDeposit(
cfg.DepositAddr, cfg.L1Client,
)
if err != nil {
return nil, err
}
disburserContract, err := disburse.NewTeleportrDisburser(
cfg.DisburserAddr, cfg.L2Client,
)
if err != nil {
return nil, err
}
parsed, err := abi.JSON(strings.NewReader(
disburse.TeleportrDisburserMetaData.ABI,
))
if err != nil {
return nil, err
}
rawDisburserContract := bind.NewBoundContract(
cfg.DisburserAddr, parsed, cfg.L2Client, cfg.L2Client, cfg.L2Client,
)
walletAddr := crypto.PubkeyToAddress(cfg.PrivKey.PublicKey)
return &Driver{
cfg: cfg,
depositContract: depositContract,
disburserContract: disburserContract,
rawDisburserContract: rawDisburserContract,
walletAddr: walletAddr,
metrics: NewMetrics(cfg.Name),
}, nil
}
// Name is an identifier used to prefix logs for a particular service.
func (d *Driver) Name() string {
return d.cfg.Name
}
// WalletAddr is the wallet address used to pay for batch transaction fees.
func (d *Driver) WalletAddr() common.Address {
return d.walletAddr
}
// Metrics returns the subservice telemetry object.
func (d *Driver) Metrics() metrics.Metrics {
return d.metrics
}
// ClearPendingTx a publishes a transaction at the next available nonce in order
// to clear any transactions in the mempool left over from a prior running
// instance. When publishing to L2 there is no mempool so the transaction can't
// get stuck, thus the behavior is unimplemented.
func (d *Driver) ClearPendingTx(
ctx context.Context,
txMgr txmgr.TxManager,
l1Client *ethclient.Client,
) error {
return nil
}
// GetBatchBlockRange returns the start and end L2 block heights that need to be
// processed. Note that the end value is *exclusive*, therefore if the returned
// values are identical nothing needs to be processed.
func (d *Driver) GetBatchBlockRange(
ctx context.Context) (*big.Int, *big.Int, error) {
// Clear the current deposit IDs from any prior iteration.
d.currentDepositIDs = nil
// Before proceeding, process the outcomes of any transactions we've
// published in the past. This handles both the restart case, as well as
// post processing after a txn is published.
err := d.processPendingTxs(ctx)
if err != nil {
return nil, nil, err
}
// Next, load the last disbursement ID claimed by postgres and the contract.
lastDisbursementID, err := d.latestDisbursementID()
if err != nil {
return nil, nil, err
}
if lastDisbursementID != nil {
d.metrics.PostgresLastDisbursedID.Set(float64(*lastDisbursementID))
}
startID, err := d.disburserContract.TotalDisbursements(
&bind.CallOpts{},
)
if err != nil {
return nil, nil, err
}
startID64 := startID.Uint64()
d.metrics.ContractNextDisbursementID.Set(float64(startID64))
// Do a quick sanity check that that the database and contract are in sync.
d.logDatabaseContractMismatch(lastDisbursementID, startID64)
// Now, proceed to ingest any new deposits by inspect L1 events from the
// deposit contract, using the last processed block in postgres as a lower
// bound.
blockNumber, err := d.cfg.L1Client.BlockNumber(ctx)
if err != nil {
return nil, nil, err
}
lastProcessedBlock, err := d.lastProcessedBlock()
if err != nil {
return nil, nil, err
}
err = d.ingestDeposits(ctx, blockNumber, lastProcessedBlock)
if err != nil {
return nil, nil, err
}
// After successfully ingesting deposits, check to see if there are any
// now-confirmed deposits that we can attempt to disburse.
confirmedDeposits, err := d.loadConfirmedDepositsInRange(
blockNumber, startID64, math.MaxUint64,
)
if err != nil {
return nil, nil, err
}
if len(confirmedDeposits) == 0 {
return startID, startID, nil
}
// Compute the end fo the range as the last confirmed deposit plus one.
endID64 := confirmedDeposits[len(confirmedDeposits)-1].ID + 1
endID := new(big.Int).SetUint64(endID64)
return startID, endID, nil
}
// CraftBatchTx transforms the L2 blocks between start and end into a batch
// transaction using the given nonce. A dummy gas price is used in the resulting
// transaction to use for size estimation.
//
// NOTE: This method SHOULD NOT publish the resulting transaction.
func (d *Driver) CraftBatchTx(
ctx context.Context,
start, end, nonce *big.Int,
) (*types.Transaction, error) {
name := d.cfg.Name
blockNumber, err := d.cfg.L1Client.BlockNumber(ctx)
if err != nil {
return nil, err
}
confirmedDeposits, err := d.loadConfirmedDepositsInRange(
blockNumber, start.Uint64(), end.Uint64(),
)
if err != nil {
return nil, err
}
var disbursements []disburse.TeleportrDisburserDisbursement
var depositIDs []uint64
for _, deposit := range confirmedDeposits {
disbursement := disburse.TeleportrDisburserDisbursement{
Amount: deposit.Amount,
Addr: deposit.Address,
}
disbursements = append(disbursements, disbursement)
depositIDs = append(depositIDs, deposit.ID)
}
log.Info(name+" crafting batch tx", "start", start, "end", end,
"nonce", nonce)
d.metrics.NumElementsPerBatch().Observe(float64(len(disbursements)))
log.Info(name+" batch constructed", "num_disbursements", len(disbursements))
gasPrice, err := d.cfg.L1Client.SuggestGasPrice(ctx)
if err != nil {
return nil, err
}
opts, err := bind.NewKeyedTransactorWithChainID(
d.cfg.PrivKey, d.cfg.ChainID,
)
if err != nil {
return nil, err
}
opts.Context = ctx
opts.Nonce = nonce
opts.GasPrice = gasPrice
opts.NoSend = true
tx, err := d.disburserContract.Disburse(opts, start, disbursements)
if err != nil {
return nil, err
}
d.currentDepositIDs = depositIDs
return tx, nil
}
// UpdateGasPrice signs an otherwise identical txn to the one provided but with
// updated gas prices sampled from the existing network conditions.
//
// NOTE: Thie method SHOULD NOT publish the resulting transaction.
func (d *Driver) UpdateGasPrice(
ctx context.Context,
tx *types.Transaction,
) (*types.Transaction, error) {
gasPrice, err := d.cfg.L1Client.SuggestGasPrice(ctx)
if err != nil {
return nil, err
}
opts, err := bind.NewKeyedTransactorWithChainID(
d.cfg.PrivKey, d.cfg.ChainID,
)
if err != nil {
return nil, err
}
opts.Context = ctx
opts.Nonce = new(big.Int).SetUint64(tx.Nonce())
opts.GasPrice = gasPrice
opts.NoSend = true
return d.rawDisburserContract.RawTransact(opts, tx.Data())
}
// SendTransaction injects a signed transaction into the pending pool for
// execution.
func (d *Driver) SendTransaction(
ctx context.Context,
tx *types.Transaction,
) error {
txHash := tx.Hash()
startID := d.currentDepositIDs[0]
endID := d.currentDepositIDs[len(d.currentDepositIDs)] + 1
// Record the pending transaction hash so that we can recover if we crash
// after publishing.
err := d.upsertPendingTx(db.PendingTx{
TxHash: txHash,
StartID: startID,
EndID: endID,
})
if err != nil {
return err
}
return d.cfg.L2Client.SendTransaction(ctx, tx)
}
// processPendingTxs is a helper method which updates Postgres with the effects
// of a published disbursement tx. This handles both startup recovery as well as
// normal operation after a transaction is published.
func (d *Driver) processPendingTxs(ctx context.Context) error {
pendingTxs, err := d.listPendingTxs()
if err != nil {
return err
}
// Nothing to do. This can happen on first startup, or if GetBatchBlockRange
// was called before shutdown without sending a transaction.
if len(pendingTxs) == 0 {
return nil
}
// Fetch the receipt for the pending transaction that confirmed. In practice
// there should only be one, but if there isn't we will return an error here
// to process any others on subsequent calls.
var receipt *types.Receipt
var pendingTx db.PendingTx
for _, pendingTx = range pendingTxs {
r, err := d.cfg.L2Client.TransactionReceipt(ctx, pendingTx.TxHash)
if err == ethereum.NotFound {
continue
} else if err != nil {
return err
}
// Also skip any reverted transactions.
if r.Status != 1 {
continue
}
receipt = r
break
}
// Backend is reporting not knowing any of the transactions, try again
// later.
if receipt == nil {
return errors.New("unable to find receipt for any pending tx")
}
// Useing the block number, load the header so that we can get accurate
// timestamps for the disbursements.
header, err := d.cfg.L2Client.HeaderByNumber(ctx, receipt.BlockNumber)
if err != nil {
return err
}
blockTimestamp := time.Unix(int64(header.Time), 0)
var successfulDisbursements int
var failedDisbursements int
var failedUpserts uint64
for _, event := range receipt.Logs {
// Extract the deposit ID from the second topic if this is a
// success/fail event.
var depositID uint64
var success bool
switch event.Topics[0] {
case DisbursementSuccessTopic:
depositID = new(big.Int).SetBytes(event.Topics[1][:]).Uint64()
success = true
successfulDisbursements++
case DisbursementFailedTopic:
depositID = new(big.Int).SetBytes(event.Topics[1][:]).Uint64()
success = false
failedDisbursements++
default:
continue
}
err = d.cfg.Database.UpsertDisbursement(
depositID,
receipt.TxHash,
receipt.BlockNumber.Uint64(),
blockTimestamp,
success,
)
if err != nil {
failedUpserts++
log.Warn("Unable to mark disbursement success",
"depositId", depositID,
"txHash", receipt.TxHash,
"blockNumber", receipt.BlockNumber,
"blockTimestamp", blockTimestamp,
"err", err)
continue
}
log.Info("Disbursement marked success",
"depositId", depositID,
"txHash", receipt.TxHash,
"blockNumber", receipt.BlockNumber,
"blockTimestamp", blockTimestamp)
}
d.metrics.SuccessfulDisbursements.Set(float64(successfulDisbursements))
d.metrics.FailedDisbursements.Set(float64(failedDisbursements))
d.metrics.FailedDatabaseMethods.With(DBMethodUpsertDisbursement).
Set(float64(failedUpserts))
// We have completed our post-processing once all of the disbursements are
// written without failures.
if failedUpserts > 0 {
return errors.New("failed to upsert all disbursements successfully")
}
// If we upserted all disbursements successfully, remove any pending txs
// with the same start/end id.
err = d.deletePendingTx(pendingTx.StartID, pendingTx.EndID)
if err != nil {
return err
}
// Sanity check that this leaves our pending tx table empty.
pendingTxs, err = d.listPendingTxs()
if err != nil {
return err
}
// If not, return an error so that subsequent calls to GetBatchBlockRange
// will attempt to process other pending txs before continuing.
if len(pendingTxs) != 0 {
return errors.New("pending txs remain in database")
}
return nil
}
// ingestDeposits is a preprocessing step done each time we attempt to compute a
// new block range. This method scans for any missing or recent logs from the
// contract, and upserts any new deposits into the database.
func (d *Driver) ingestDeposits(
ctx context.Context,
blockNumber uint64,
lastProcessedBlockNumber *uint64,
) error {
filterStartBlockNumber := FindFilterStartBlockNumber(
FilterStartBlockNumberParams{
BlockNumber: blockNumber,
NumConfirmations: d.cfg.NumConfirmations,
DeployBlockNumber: d.cfg.DeployBlockNumber,
LastProcessedBlockNumber: lastProcessedBlockNumber,
},
)
// Fetch deposit events in block ranges capped by the FilterQueryMaxBlocks
// parameter.
maxBlocks := d.cfg.FilterQueryMaxBlocks
for start := filterStartBlockNumber; start < blockNumber+1; start += maxBlocks {
var end = start + maxBlocks
if end > blockNumber {
end = blockNumber
}
opts := &bind.FilterOpts{
Start: start,
End: &end,
Context: ctx,
}
events, err := d.depositContract.FilterEtherReceived(opts, nil, nil, nil)
if err != nil {
return err
}
defer events.Close()
var deposits []db.Deposit
for events.Next() {
event := events.Event
header, err := d.cfg.L1Client.HeaderByNumber(
ctx, big.NewInt(int64(event.Raw.BlockNumber)),
)
if err != nil {
return err
}
deposits = append(deposits, db.Deposit{
ID: event.DepositId.Uint64(),
TxnHash: event.Raw.TxHash,
BlockNumber: event.Raw.BlockNumber,
BlockTimestamp: time.Unix(int64(header.Time), 0),
Address: event.Emitter,
Amount: event.Amount,
})
}
err = events.Error()
if err != nil {
return err
}
err = d.upsertDeposits(deposits, end)
if err != nil {
return err
}
}
return nil
}
// loadConfirmedDeposits retrieves the list of confirmed deposits with IDs
// in the range [startID, endID).
func (d *Driver) loadConfirmedDepositsInRange(
blockNumber uint64,
startID uint64,
endID uint64,
) ([]db.Deposit, error) {
confirmedDeposits, err := d.confirmedDeposits(blockNumber)
if err != nil {
return nil, err
}
// On the off chance that we failed to record a disbursement, filter out any
// which are lower than what the disbursement contract says is the next
// disbursement. Note that it is possible for the last disbursement id to
// match the contract, but still have lingering, incomplete disbursements
// before that.
var filteredDeposits = make([]db.Deposit, 0, len(confirmedDeposits))
var missingDisbursements int
for _, deposit := range confirmedDeposits {
switch {
// If the deposit ID is less than our start ID, this indicates that we
// are missing a disbursement for this deposit even though the contract
// beleves it was disbursed.
case deposit.ID < startID:
log.Warn("Filtering deposit with missing disbursement",
"deposit_id", deposit.ID)
missingDisbursements++
continue
// This is mostly a defensive measure, to ensure that we return the
// exact range that was sanity checked in GetBatchBlockRange, which can
// change as a result of the block number increasing.
case deposit.ID >= endID:
continue
}
filteredDeposits = append(filteredDeposits, deposit)
}
d.metrics.MissingDisbursements.Set(float64(missingDisbursements))
if len(filteredDeposits) == 0 {
return nil, nil
}
// Ensure the next confirmed deposit matches what the contract expects.
if startID != filteredDeposits[0].ID {
panic("confirmed deposits start is not contiguous")
}
// Ensure that the slice has contiguous deposit ids. This is done by
// checking that the final deposit id is equal to:
// start + len(confirmedDeposits).
// The id is the primary key so there cannot be duplicates, and they are
// returned in sorted order.
lastDepositID := filteredDeposits[len(filteredDeposits)-1].ID
if startID+uint64(len(filteredDeposits)) != lastDepositID+1 {
panic("confirmed deposits are not continguous")
}
return filteredDeposits, nil
}
// logDatabaseContractMismatch records any instances of our database
// desynchronizing from the disrburser contract. This method panics in
// irrecoverable cases of desynchronization.
func (d *Driver) logDatabaseContractMismatch(
lastDisbursementID *uint64,
contractNextID uint64,
) {
switch {
// Database indicates we have done a disbursement.
case lastDisbursementID != nil:
switch {
// The last recorded disbursement is behind what the contract believes.
case *lastDisbursementID+1 < contractNextID:
log.Warn("Recorded disbursements behind contract",
"last_disbursement_id", *lastDisbursementID,
"contract_next_id", contractNextID)
d.metrics.DepositIDMismatch.Set(1.0)
// The last recorded disbursement is ahead of what the contract believes.
// This should NEVER happen unless the sequencer blows up and loses
// state. Exit so that the the problem can be surfaced loudly.
case *lastDisbursementID+1 > contractNextID:
log.Error("Recorded disbursements ahead of contract",
"last_disbursement_id", *lastDisbursementID,
"contract_next_id", contractNextID)
panic("Recorded disbursements ahead contract")
// Databse and contract are in sync.
default:
d.metrics.DepositIDMismatch.Set(0.0)
}
// Database indicates we have not done a disbursement, but contract does.
case contractNextID != 0:
// The contract shows that is has disbursed, but we don't have a
// recording of it.
log.Warn("Recorded disbursements behind contract",
"last_disbursement_id", nil,
"contract_next_id", contractNextID)
d.metrics.DepositIDMismatch.Set(1.0)
// Database and contract indicate we have not done a disbursement.
default:
d.metrics.DepositIDMismatch.Set(0.0)
}
}
func (d *Driver) upsertDeposits(deposits []db.Deposit, end uint64) error {
err := d.cfg.Database.UpsertDeposits(deposits, end)
if err != nil {
d.metrics.FailedDatabaseMethods.With(DBMethodUpsertDeposits).Set(1.0)
return err
}
d.metrics.FailedDatabaseMethods.With(DBMethodUpsertDeposits).Set(0.0)
return nil
}
func (d *Driver) confirmedDeposits(blockNumber uint64) ([]db.Deposit, error) {
confirmedDeposits, err := d.cfg.Database.ConfirmedDeposits(
blockNumber, d.cfg.NumConfirmations,
)
if err != nil {
d.metrics.FailedDatabaseMethods.With(DBMethodConfirmedDeposits).Set(1.0)
return nil, err
}
d.metrics.FailedDatabaseMethods.With(DBMethodConfirmedDeposits).Set(0.0)
return confirmedDeposits, nil
}
func (d *Driver) lastProcessedBlock() (*uint64, error) {
lastProcessedBlock, err := d.cfg.Database.LastProcessedBlock()
if err != nil {
d.metrics.FailedDatabaseMethods.With(DBMethodLastProcessedBlock).Set(1.0)
return nil, err
}
d.metrics.FailedDatabaseMethods.With(DBMethodLastProcessedBlock).Set(0.0)
return lastProcessedBlock, nil
}
func (d *Driver) upsertPendingTx(pendingTx db.PendingTx) error {
err := d.cfg.Database.UpsertPendingTx(pendingTx)
if err != nil {
d.metrics.FailedDatabaseMethods.With(DBMethodUpsertPendingTx).Set(1.0)
return err
}
d.metrics.FailedDatabaseMethods.With(DBMethodUpsertPendingTx).Set(0.0)
return nil
}
func (d *Driver) listPendingTxs() ([]db.PendingTx, error) {
pendingTxs, err := d.cfg.Database.ListPendingTxs()
if err != nil {
d.metrics.FailedDatabaseMethods.With(DBMethodListPendingTxs).Set(1.0)
return nil, err
}
d.metrics.FailedDatabaseMethods.With(DBMethodListPendingTxs).Set(0.0)
return pendingTxs, nil
}
func (d *Driver) latestDisbursementID() (*uint64, error) {
lastDisbursementID, err := d.cfg.Database.LatestDisbursementID()
if err != nil {
d.metrics.FailedDatabaseMethods.With(DBMethodLatestDisbursementID).Set(1.0)
return nil, err
}
d.metrics.FailedDatabaseMethods.With(DBMethodLatestDisbursementID).Set(0.0)
return lastDisbursementID, nil
}
func (d *Driver) deletePendingTx(startID, endID uint64) error {
err := d.cfg.Database.DeletePendingTx(startID, endID)
if err != nil {
d.metrics.FailedDatabaseMethods.With(DBMethodDeletePendingTx).Set(1.0)
return err
}
d.metrics.FailedDatabaseMethods.With(DBMethodDeletePendingTx).Set(0.0)
return nil
}
package disburser
// FilterStartBlockNumberParams holds the arguments passed to
// FindFilterStartBlockNumber.
type FilterStartBlockNumberParams struct {
// BlockNumber the current block height of the chain.
BlockNumber uint64
// NumConfirmations is the number of confirmations required to consider a
// block final.
NumConfirmations uint64
// DeployBlockNumber is the deployment height of the Deposit contract.
DeployBlockNumber uint64
// LastProcessedBlockNumber is the height of the last processed block.
//
// NOTE: This will be nil on the first invocation, before blocks have been
// ingested.
LastProcessedBlockNumber *uint64
}
func (p *FilterStartBlockNumberParams) unconfirmed(blockNumber uint64) bool {
return p.BlockNumber+1 < blockNumber+p.NumConfirmations
}
// FindFilterStartBlockNumber returns the block height from which to begin
// filtering logs based on the relative heights of the chain, the contract
// deployment, and the last block that was processed.
func FindFilterStartBlockNumber(params FilterStartBlockNumberParams) uint64 {
// On initilization, always start at the deploy height.
if params.LastProcessedBlockNumber == nil {
return params.DeployBlockNumber
}
// If the deployment height has not exited the confirmation window, we can
// still begin our search from the deployment height.
if params.unconfirmed(params.DeployBlockNumber) {
return params.DeployBlockNumber
}
// Otherwise, start from the block immediately following the last processed
// block. If that height is still hasn't fully confirmed, we'll use the
// height of the last confirmed block.
var filterStartBlockNumber = *params.LastProcessedBlockNumber + 1
if params.unconfirmed(filterStartBlockNumber) {
filterStartBlockNumber = params.BlockNumber + 1 - params.NumConfirmations
}
return filterStartBlockNumber
}
package disburser_test
import (
"testing"
"github.com/ethereum-optimism/optimism/go/teleportr/drivers/disburser"
"github.com/stretchr/testify/require"
)
func uint64Ptr(x uint64) *uint64 {
return &x
}
type filterStartBlockNumberTestCase struct {
name string
params disburser.FilterStartBlockNumberParams
expStartBlockNumber uint64
}
// TestFindFilterStartBlockNumber exhaustively tests the behavior of
// FindFilterStartBlockNumber and its edge cases.
func TestFindFilterStartBlockNumber(t *testing.T) {
tests := []filterStartBlockNumberTestCase{
// Deploy number should be returned if LastProcessedBlockNumber is nil.
{
name: "init returns deploy block number",
params: disburser.FilterStartBlockNumberParams{
BlockNumber: 10,
NumConfirmations: 5,
DeployBlockNumber: 42,
LastProcessedBlockNumber: nil,
},
expStartBlockNumber: 42,
},
// Deploy number should be returned if the deploy number is still in our
// confirmation window.
{
name: "conf lookback before deploy number",
params: disburser.FilterStartBlockNumberParams{
BlockNumber: 43,
NumConfirmations: 5,
DeployBlockNumber: 42,
LastProcessedBlockNumber: uint64Ptr(43),
},
expStartBlockNumber: 42,
},
// Deploy number should be returned if the deploy number is still in our
// confirmation window.
{
name: "conf lookback before deploy number",
params: disburser.FilterStartBlockNumberParams{
BlockNumber: 43,
NumConfirmations: 44,
DeployBlockNumber: 42,
LastProcessedBlockNumber: uint64Ptr(43),
},
expStartBlockNumber: 42,
},
// If our confirmation window is ahead of the last deposit + 1, expect
// last deposit + 1.
{
name: "conf lookback gt last deposit plus one",
params: disburser.FilterStartBlockNumberParams{
BlockNumber: 100,
NumConfirmations: 5,
DeployBlockNumber: 42,
LastProcessedBlockNumber: uint64Ptr(43),
},
expStartBlockNumber: 44,
},
// If our confirmation window is equal to last deposit + 1, expect last
// deposit + 1.
{
name: "conf lookback eq last deposit plus one",
params: disburser.FilterStartBlockNumberParams{
BlockNumber: 48,
NumConfirmations: 5,
DeployBlockNumber: 42,
LastProcessedBlockNumber: uint64Ptr(43),
},
expStartBlockNumber: 44,
},
// If our confirmation window starts before last deposit + 1, expect
// block number - num confs + 1.
{
name: "conf lookback lt last deposit plus one",
params: disburser.FilterStartBlockNumberParams{
BlockNumber: 47,
NumConfirmations: 5,
DeployBlockNumber: 42,
LastProcessedBlockNumber: uint64Ptr(43),
},
expStartBlockNumber: 43,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
testFindFilterStartBlockNumber(t, test)
})
}
}
func testFindFilterStartBlockNumber(
t *testing.T,
test filterStartBlockNumberTestCase,
) {
startBlockNumber := disburser.FindFilterStartBlockNumber(test.params)
require.Equal(t, test.expStartBlockNumber, startBlockNumber)
}
package disburser
import (
"github.com/ethereum-optimism/optimism/go/bss-core/metrics"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
const methodLabel = "method"
var (
// DBMethodUpsertDeposits is a label for UpsertDeposits db method.
DBMethodUpsertDeposits = prometheus.Labels{methodLabel: "upsert_deposits"}
// DBMethodConfirmedDeposits is a label for ConfirmedDeposits db method.
DBMethodConfirmedDeposits = prometheus.Labels{methodLabel: "confirmed_deposits"}
// DBMethodLastProcessedBlock is a label for LastProcessedBlock db method.
DBMethodLastProcessedBlock = prometheus.Labels{methodLabel: "last_processed_block"}
// DBMethodUpsertPendingTx is a label for UpsertPendingTx db method.
DBMethodUpsertPendingTx = prometheus.Labels{methodLabel: "upsert_pending_tx"}
// DBMethodListPendingTxs is a label for ListPendingTxs db method.
DBMethodListPendingTxs = prometheus.Labels{methodLabel: "list_pending_txs"}
// DBMethodUpsertDisbursement is a label for UpsertDisbursement db method.
DBMethodUpsertDisbursement = prometheus.Labels{methodLabel: "upsert_disbursement"}
// DBMethodLatestDisbursementID is a label for LatestDisbursementID db method.
DBMethodLatestDisbursementID = prometheus.Labels{methodLabel: "latest_disbursement_id"}
// DBMethodDeletePendingTx is a label for DeletePendingTx db method.
DBMethodDeletePendingTx = prometheus.Labels{methodLabel: "delete_pending_tx"}
)
// Metrics extends the BSS core metrics with additional metrics tracked by the
// sequencer driver.
type Metrics struct {
*metrics.Base
// FailedDatabaseMethods tracks the number of database failures for each
// known database method.
FailedDatabaseMethods *prometheus.GaugeVec
// DepositIDMismatch tracks whether or not our database is in sync with the
// disrburser contract. 1 means in sync, 0 means out of sync.
DepositIDMismatch prometheus.Gauge
// MissingDisbursements tracks the number of deposits that are missing
// disbursement below our supposed next deposit id.
MissingDisbursements prometheus.Gauge
// SuccessfulDisbursements tracks the number of disbursements that emit a
// success event from a given tx.
SuccessfulDisbursements prometheus.Gauge
// FailedDisbursements tracks the number of disbursements that emit a failed
// event from a given tx.
FailedDisbursements prometheus.Gauge
// PostgresLastDisbursedID tracks the latest disbursement id in postgres.
PostgresLastDisbursedID prometheus.Gauge
// ContractNextDisbursementID tracks the next disbursement id expected by
// the disburser contract.
ContractNextDisbursementID prometheus.Gauge
}
// NewMetrics initializes a new, extended metrics object.
func NewMetrics(subsystem string) *Metrics {
base := metrics.NewBase(subsystem, "")
return &Metrics{
Base: base,
FailedDatabaseMethods: promauto.NewGaugeVec(prometheus.GaugeOpts{
Name: "failed_database_operations",
Help: "Tracks the number of database failures",
Subsystem: base.SubsystemName(),
}, []string{methodLabel}),
DepositIDMismatch: promauto.NewGauge(prometheus.GaugeOpts{
Name: "deposit_id_mismatch",
Help: "Set to 1 when the postgres and the disrburser contract " +
"disagree on the next deposit id, and 0 otherwise",
Subsystem: base.SubsystemName(),
}),
MissingDisbursements: promauto.NewGauge(prometheus.GaugeOpts{
Name: "missing_disbursements",
Help: "Number of deposits that are missing disbursements in " +
"postgres below our supposed next deposit id",
Subsystem: base.SubsystemName(),
}),
SuccessfulDisbursements: promauto.NewGauge(prometheus.GaugeOpts{
Name: "successful_disbursements",
Help: "Number of disbursements that emit a success event " +
"from a given tx",
Subsystem: base.SubsystemName(),
}),
FailedDisbursements: promauto.NewGauge(prometheus.GaugeOpts{
Name: "failed_disbursements",
Help: "Number of disbursements that emit a failed event " +
"from a given tx",
Subsystem: base.SubsystemName(),
}),
PostgresLastDisbursedID: promauto.NewGauge(prometheus.GaugeOpts{
Name: "postgres_last_disbursed_id",
Help: "Latest recorded disbursement id in postgres",
Subsystem: base.SubsystemName(),
}),
ContractNextDisbursementID: promauto.NewGauge(prometheus.GaugeOpts{
Name: "contract_next_disbursement_id",
Help: "Next disbursement id expected by the disburser contract",
Subsystem: base.SubsystemName(),
}),
}
}
package flags
import "github.com/urfave/cli"
const envVarPrefix = "TELEPORTR_"
func prefixEnvVar(name string) string {
return envVarPrefix + name
}
var (
/* Required Flags */
BuildEnvFlag = cli.StringFlag{
Name: "build-env",
Usage: "Build environment for which the binary is produced, " +
"e.g. production or development",
Required: true,
EnvVar: "BUILD_ENV",
}
EthNetworkNameFlag = cli.StringFlag{
Name: "eth-network-name",
Usage: "Ethereum network name",
Required: true,
EnvVar: "ETH_NETWORK_NAME",
}
L1EthRpcFlag = cli.StringFlag{
Name: "l1-eth-rpc",
Usage: "HTTP provider URL for L1",
Required: true,
EnvVar: "L1_ETH_RPC",
}
L2EthRpcFlag = cli.StringFlag{
Name: "l2-eth-rpc",
Usage: "HTTP provider URL for L2",
Required: true,
EnvVar: "L2_ETH_RPC",
}
DepositAddressFlag = cli.StringFlag{
Name: "deposit-address",
Usage: "Address of the TeleportrDeposit contract",
Required: true,
EnvVar: prefixEnvVar("DEPOSIT_ADDRESS"),
}
DepositDeployBlockNumberFlag = cli.Uint64Flag{
Name: "deposit-deploy-block-number",
Usage: "Deployment block number of the TeleportrDeposit contract",
Required: true,
EnvVar: prefixEnvVar("DEPOSIT_DEPLOY_BLOCK_NUMBER"),
}
DisburserAddressFlag = cli.StringFlag{
Name: "disburser-address",
Usage: "Address of the TeleportrDisburser contract",
Required: true,
EnvVar: prefixEnvVar("DISBURSER_ADDRESS"),
}
MaxL2TxSizeFlag = cli.Uint64Flag{
Name: "max-l2-tx-size",
Usage: "Maximum size in bytes of any L2 transaction that gets " +
"sent for disbursement",
Required: true,
EnvVar: prefixEnvVar("MAX_L2_TX_SIZE"),
}
NumDepositConfirmationsFlag = cli.Uint64Flag{
Name: "num-deposit-confirmations",
Usage: "Number of confirmations before deposits are considered " +
"confirmed",
Required: true,
EnvVar: prefixEnvVar("NUM_DEPOSIT_CONFIRMATIONS"),
}
FilterQueryMaxBlocksFlag = cli.Uint64Flag{
Name: "filter-query-max-blocks",
Usage: "Maximum range of a filter query in blocks",
Required: true,
EnvVar: prefixEnvVar("FILTER_QUERY_MAX_BLOCKS"),
}
PollIntervalFlag = cli.DurationFlag{
Name: "poll-interval",
Usage: "Delay between querying L1 for more transactions and " +
"creating a new disbursement batch",
Required: true,
EnvVar: prefixEnvVar("POLL_INTERVAL"),
}
SafeAbortNonceTooLowCountFlag = cli.Uint64Flag{
Name: "safe-abort-nonce-too-low-count",
Usage: "Number of ErrNonceTooLow observations required to " +
"give up on a tx at a particular nonce without receiving " +
"confirmation",
Required: true,
EnvVar: prefixEnvVar("SAFE_ABORT_NONCE_TOO_LOW_COUNT"),
}
ResubmissionTimeoutFlag = cli.DurationFlag{
Name: "resubmission-timeout",
Usage: "Duration we will wait before resubmitting a " +
"transaction to L2",
Required: true,
EnvVar: prefixEnvVar("RESUBMISSION_TIMEOUT"),
}
PostgresHostFlag = cli.StringFlag{
Name: "postgres-host",
Usage: "Host of the teleportr postgres instance",
Required: true,
EnvVar: prefixEnvVar("POSTGRES_HOST"),
}
PostgresPortFlag = cli.Uint64Flag{
Name: "postgres-port",
Usage: "Port of the teleportr postgres instance",
Required: true,
EnvVar: prefixEnvVar("POSTGRES_PORT"),
}
PostgresUserFlag = cli.StringFlag{
Name: "postgres-user",
Usage: "Username of the teleportr postgres instance",
Required: true,
EnvVar: prefixEnvVar("POSTGRES_USER"),
}
PostgresPasswordFlag = cli.StringFlag{
Name: "postgres-password",
Usage: "Password of the teleportr postgres instance",
Required: true,
EnvVar: prefixEnvVar("POSTGRES_PASSWORD"),
}
PostgresDBNameFlag = cli.StringFlag{
Name: "postgres-db-name",
Usage: "Database name of the teleportr postgres instance",
Required: true,
EnvVar: prefixEnvVar("POSTGRES_DB_NAME"),
}
PostgresEnableSSLFlag = cli.BoolFlag{
Name: "postgres-enable-ssl",
Usage: "Whether or not to enable SSL on connections to " +
"teleportr postgres instance",
Required: true,
EnvVar: prefixEnvVar("POSTGRES_ENABLE_SSL"),
}
/* Optional Flags */
LogLevelFlag = cli.StringFlag{
Name: "log-level",
Usage: "The lowest log level that will be output",
Value: "info",
EnvVar: prefixEnvVar("LOG_LEVEL"),
}
LogTerminalFlag = cli.BoolFlag{
Name: "log-terminal",
Usage: "If true, outputs logs in terminal format, otherwise prints " +
"in JSON format. If SENTRY_ENABLE is set to true, this flag is " +
"ignored and logs are printed using JSON",
EnvVar: prefixEnvVar("LOG_TERMINAL"),
}
DisburserPrivateKeyFlag = cli.StringFlag{
Name: "disburser-private-key",
Usage: "The private key to use for sending to the disburser contract",
EnvVar: prefixEnvVar("DISBURSER_PRIVATE_KEY"),
}
MnemonicFlag = cli.StringFlag{
Name: "mnemonic",
Usage: "The mnemonic used to derive the wallet for the disburser",
EnvVar: prefixEnvVar("MNEMONIC"),
}
DisburserHDPathFlag = cli.StringFlag{
Name: "disburser-hd-path",
Usage: "The HD path used to derive the disburser wallet from the " +
"mnemonic. The mnemonic flag must also be set.",
EnvVar: prefixEnvVar("DISBURSER_HD_PATH"),
}
MetricsServerEnableFlag = cli.BoolFlag{
Name: "metrics-server-enable",
Usage: "Whether or not to run the embedded metrics server",
EnvVar: prefixEnvVar("METRICS_SERVER_ENABLE"),
}
MetricsHostnameFlag = cli.StringFlag{
Name: "metrics-hostname",
Usage: "The hostname of the metrics server",
Value: "127.0.0.1",
EnvVar: prefixEnvVar("METRICS_HOSTNAME"),
}
MetricsPortFlag = cli.Uint64Flag{
Name: "metrics-port",
Usage: "The port of the metrics server",
Value: 7300,
EnvVar: prefixEnvVar("METRICS_PORT"),
}
HTTP2DisableFlag = cli.BoolFlag{
Name: "http2-disable",
Usage: "Whether or not to disable HTTP/2 support.",
EnvVar: prefixEnvVar("HTTP2_DISABLE"),
}
)
var requiredFlags = []cli.Flag{
BuildEnvFlag,
EthNetworkNameFlag,
L1EthRpcFlag,
L2EthRpcFlag,
DepositAddressFlag,
DepositDeployBlockNumberFlag,
DisburserAddressFlag,
MaxL2TxSizeFlag,
NumDepositConfirmationsFlag,
FilterQueryMaxBlocksFlag,
PollIntervalFlag,
SafeAbortNonceTooLowCountFlag,
ResubmissionTimeoutFlag,
PostgresHostFlag,
PostgresPortFlag,
PostgresUserFlag,
PostgresPasswordFlag,
PostgresDBNameFlag,
PostgresEnableSSLFlag,
}
var optionalFlags = []cli.Flag{
LogLevelFlag,
LogTerminalFlag,
DisburserPrivateKeyFlag,
MnemonicFlag,
DisburserHDPathFlag,
MetricsServerEnableFlag,
MetricsHostnameFlag,
MetricsPortFlag,
HTTP2DisableFlag,
}
// Flags contains the list of configuration options available to the binary.
var Flags = append(requiredFlags, optionalFlags...)
package flags
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
)
// TestRequiredFlagsSetRequired asserts that all flags deemed required properly
// have the Required field set to true.
func TestRequiredFlagsSetRequired(t *testing.T) {
for _, flag := range requiredFlags {
reqFlag, ok := flag.(cli.RequiredFlag)
require.True(t, ok)
require.True(t, reqFlag.IsRequired())
}
}
// TestOptionalFlagsDontSetRequired asserts that all flags deemed optional set
// the Required field to false.
func TestOptionalFlagsDontSetRequired(t *testing.T) {
for _, flag := range optionalFlags {
reqFlag, ok := flag.(cli.RequiredFlag)
require.True(t, ok)
require.False(t, reqFlag.IsRequired())
}
}
......@@ -3,16 +3,60 @@ module github.com/ethereum-optimism/optimism/go/teleportr
go 1.17
require (
github.com/ethereum-optimism/optimism/go/bss-core v0.0.0-20220218171106-67a0414d7606
github.com/ethereum/go-ethereum v1.10.15
github.com/google/uuid v1.3.0
github.com/lib/pq v1.10.4
github.com/stretchr/testify v1.7.0
github.com/urfave/cli v1.22.5
)
require (
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
github.com/VictoriaMetrics/fastcache v1.6.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd v0.22.0-beta // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea // indirect
github.com/decred/base58 v1.0.3 // indirect
github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
github.com/decred/dcrd/crypto/ripemd160 v1.0.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 // indirect
github.com/decred/dcrd/hdkeychain/v3 v3.0.0 // indirect
github.com/getsentry/sentry-go v0.11.0 // indirect
github.com/go-ole/go-ole v1.2.1 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/golang/protobuf v1.4.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
github.com/holiman/uint256 v1.2.0 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.11.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.26.0 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
github.com/prometheus/tsdb v0.7.1 // indirect
github.com/rjeczalik/notify v0.9.1 // indirect
github.com/russross/blackfriday/v2 v2.0.1 // indirect
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
github.com/tklauser/go-sysconf v0.3.5 // indirect
github.com/tklauser/numcpus v0.2.2 // indirect
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef // indirect
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 // indirect
google.golang.org/protobuf v1.26.0-rc.1 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
replace github.com/ethereum-optimism/optimism/go/bss-core => ../bss-core
......@@ -18,6 +18,7 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4=
......@@ -33,17 +34,31 @@ github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6L
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o=
github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI=
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo=
github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y=
github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo=
......@@ -53,30 +68,48 @@ github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7
github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0=
github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM=
github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo=
github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304=
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ=
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
......@@ -85,45 +118,100 @@ github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea h1:j4317fAZh7X6GqbFowYdYdI0L9bwxL07jyPZIdepyZ0=
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
github.com/decred/base58 v1.0.3 h1:KGZuh8d1WEMIrK0leQRM47W85KqCAdl2N+uagbctdDI=
github.com/decred/base58 v1.0.3/go.mod h1:pXP9cXCfM2sFLb2viz2FNIdeMWmZDBKG3ZBYbiSM78E=
github.com/decred/dcrd/chaincfg/chainhash v1.0.2 h1:rt5Vlq/jM3ZawwiacWjPa+smINyLRN07EO0cNBV6DGU=
github.com/decred/dcrd/chaincfg/chainhash v1.0.2/go.mod h1:BpbrGgrPTr3YJYRN3Bm+D9NuaFd+zGyNeIKgrhCXK60=
github.com/decred/dcrd/chaincfg/v3 v3.0.0 h1:+TFbu7ZmvBwM+SZz5mrj6cun9ts/6DAL5sqnsaFBHGQ=
github.com/decred/dcrd/chaincfg/v3 v3.0.0/go.mod h1:EspyubQ7D2w6tjP7rBGDIE7OTbuMgBjR2F2kZFnh31A=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/crypto/ripemd160 v1.0.1 h1:TjRL4LfftzTjXzaufov96iDAkbY2R3aTvH2YMYa1IOc=
github.com/decred/dcrd/crypto/ripemd160 v1.0.1/go.mod h1:F0H8cjIuWTRoixr/LM3REB8obcWkmYx0gbxpQWR8RPg=
github.com/decred/dcrd/dcrec v1.0.0 h1:W+z6Es+Rai3MXYVoPAxYr5U1DGis0Co33scJ6uH2J6o=
github.com/decred/dcrd/dcrec v1.0.0/go.mod h1:HIaqbEJQ+PDzQcORxnqen5/V1FR3B4VpIfmePklt8Q8=
github.com/decred/dcrd/dcrec/edwards/v2 v2.0.1 h1:V6eqU1crZzuoFT4KG2LhaU5xDSdkHuvLQsj25wd7Wb4=
github.com/decred/dcrd/dcrec/edwards/v2 v2.0.1/go.mod h1:d0H8xGMWbiIQP7gN3v2rByWUcuZPm9YsgmnfoxgbINc=
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 h1:sgNeV1VRMDzs6rzyPpxyM0jp317hnwiq58Filgag2xw=
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0/go.mod h1:J70FGZSbzsjecRTiTzER+3f1KZLNaXkuv+yeFTKoxM8=
github.com/decred/dcrd/dcrutil/v3 v3.0.0 h1:n6uQaTQynIhCY89XsoDk2WQqcUcnbD+zUM9rnZcIOZo=
github.com/decred/dcrd/dcrutil/v3 v3.0.0/go.mod h1:iVsjcqVzLmYFGCZLet2H7Nq+7imV9tYcuY+0lC2mNsY=
github.com/decred/dcrd/hdkeychain/v3 v3.0.0 h1:hOPb4c8+K6bE3a/qFtzt2Z2yzK4SpmXmxvCTFp8vMxI=
github.com/decred/dcrd/hdkeychain/v3 v3.0.0/go.mod h1:Vz7PJSlLzhqmOR2lmjGD9JqAZgmUnM8P6r8hg7U4Zho=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
github.com/decred/dcrd/wire v1.4.0 h1:KmSo6eTQIvhXS0fLBQ/l7hG7QLcSJQKSwSyzSqJYDk0=
github.com/decred/dcrd/wire v1.4.0/go.mod h1:WxC/0K+cCAnBh+SKsRjIX9YPgvrjhmE+6pZlel1G7Ro=
github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M=
github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw=
github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/ethereum/go-ethereum v1.10.12/go.mod h1:W3yfrFyL9C1pHcwY5hmRHVDaorTiQxhYBkKyu5mEDHw=
github.com/ethereum/go-ethereum v1.10.15 h1:E9o0kMbD8HXhp7g6UwIwntY05WTDheCGziMhegcBsQw=
github.com/ethereum/go-ethereum v1.10.15/go.mod h1:W3yfrFyL9C1pHcwY5hmRHVDaorTiQxhYBkKyu5mEDHw=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c=
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
github.com/getsentry/sentry-go v0.11.0 h1:qro8uttJGvNAMr5CLcFI9CHR0aDzXl0Vs3Pmw/oTPg8=
github.com/getsentry/sentry-go v0.11.0/go.mod h1:KBQIxiZAetw62Cj8Ri964vAEWVdgfaUCn30Q3bCvANo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
......@@ -144,11 +232,14 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
......@@ -158,6 +249,10 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
......@@ -171,18 +266,28 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE=
github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs=
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM=
github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huin/goupnp v1.0.2 h1:RfGLP+h3mvisuWEyybxNq5Eft3NWhHLPeUN72kpKZoI=
github.com/huin/goupnp v1.0.2/go.mod h1:0dxJBVBHqTMjIUMkESDTNgOOx/Mw5wYIfyFmdzSamkM=
github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY=
github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI=
......@@ -195,29 +300,51 @@ github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19y
github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE=
github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0=
github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po=
github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk=
github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=
github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 h1:6OvNmYgJyexcZ3pYbTI9jWx5tHo1Dee/tWbLMfPe2TA=
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8=
github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE=
github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE=
github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro=
github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
......@@ -227,90 +354,150 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc=
github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE=
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg=
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
......@@ -321,17 +508,38 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4=
github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=
github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZW24CsNJDfeh9Ex6Pm0Rcpc7qrgKBiL44vF4=
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
......@@ -341,11 +549,16 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
......@@ -379,13 +592,16 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
......@@ -393,14 +609,17 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
......@@ -415,20 +634,25 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
......@@ -437,11 +661,14 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
......@@ -452,6 +679,7 @@ golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 h1:uCLL3g5wH2xjxVREVuAbP9JM5PPKjRbXKRa6IBjkzmU=
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
......@@ -462,20 +690,24 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
......@@ -497,6 +729,7 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
......@@ -541,24 +774,37 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
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=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
......
{
"name": "@eth-optimism/teleportr",
"version": "0.0.0",
"private": true,
"devDependencies": {}
}
package teleportr
import (
"context"
"os"
"os/signal"
"syscall"
"time"
bsscore "github.com/ethereum-optimism/optimism/go/bss-core"
"github.com/ethereum-optimism/optimism/go/bss-core/dial"
"github.com/ethereum-optimism/optimism/go/bss-core/metrics"
"github.com/ethereum-optimism/optimism/go/bss-core/txmgr"
"github.com/ethereum-optimism/optimism/go/teleportr/db"
"github.com/ethereum-optimism/optimism/go/teleportr/drivers/disburser"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
)
func Main(gitVersion string) func(ctx *cli.Context) error {
return func(cliCtx *cli.Context) error {
cfg, err := NewConfig(cliCtx)
if err != nil {
return err
}
log.Info("Initializing teleportr")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var logHandler log.Handler
if cfg.LogTerminal {
logHandler = log.StreamHandler(os.Stdout, log.TerminalFormat(true))
} else {
logHandler = log.StreamHandler(os.Stdout, log.JSONFormat())
}
logLevel, err := log.LvlFromString(cfg.LogLevel)
if err != nil {
return err
}
log.Root().SetHandler(log.LvlFilterHandler(logLevel, logHandler))
disburserPrivKey, disburserAddr, err := bsscore.ParseWalletPrivKeyAndContractAddr(
"Teleportr", cfg.Mnemonic, cfg.DisburserHDPath,
cfg.DisburserPrivKey, cfg.DisburserAddress,
)
if err != nil {
return err
}
depositAddr, err := bsscore.ParseAddress(cfg.DepositAddress)
if err != nil {
return err
}
l1Client, err := dial.L1EthClientWithTimeout(ctx, cfg.L1EthRpc, cfg.DisableHTTP2)
if err != nil {
return err
}
defer l1Client.Close()
l2Client, err := dial.L1EthClientWithTimeout(ctx, cfg.L2EthRpc, cfg.DisableHTTP2)
if err != nil {
return err
}
defer l2Client.Close()
database, err := db.Open(db.Config{
Host: cfg.PostgresHost,
Port: uint16(cfg.PostgresPort),
User: cfg.PostgresUser,
Password: cfg.PostgresPassword,
DBName: cfg.PostgresDBName,
EnableSSL: cfg.PostgresEnableSSL,
})
if err != nil {
return err
}
defer database.Close()
if cfg.MetricsServerEnable {
go metrics.RunServer(cfg.MetricsHostname, cfg.MetricsPort)
}
chainID, err := l1Client.ChainID(ctx)
if err != nil {
return err
}
txManagerConfig := txmgr.Config{
ResubmissionTimeout: cfg.ResubmissionTimeout,
ReceiptQueryInterval: time.Second,
NumConfirmations: 1, // L2 insta confs
SafeAbortNonceTooLowCount: cfg.SafeAbortNonceTooLowCount,
}
teleportrDriver, err := disburser.NewDriver(disburser.Config{
Name: "Teleportr",
L1Client: l1Client,
L2Client: l2Client,
Database: database,
MaxTxSize: cfg.MaxL2TxSize,
NumConfirmations: cfg.NumDepositConfirmations,
DeployBlockNumber: cfg.DepositDeployBlockNumber,
FilterQueryMaxBlocks: cfg.FilterQueryMaxBlocks,
DepositAddr: depositAddr,
DisburserAddr: disburserAddr,
ChainID: chainID,
PrivKey: disburserPrivKey,
})
if err != nil {
return err
}
teleportrService := bsscore.NewService(bsscore.ServiceConfig{
Context: ctx,
Driver: teleportrDriver,
PollInterval: cfg.PollInterval,
ClearPendingTx: false,
L1Client: l1Client,
TxManagerConfig: txManagerConfig,
})
services := []*bsscore.Service{teleportrService}
teleportr, err := bsscore.NewBatchSubmitter(ctx, cancel, services)
if err != nil {
return err
}
log.Info("Starting teleportr")
err = teleportr.Start()
if err != nil {
return err
}
defer teleportr.Stop()
log.Info("Teleportr started")
interruptChannel := make(chan os.Signal, 1)
signal.Notify(interruptChannel, []os.Signal{
os.Interrupt,
os.Kill,
syscall.SIGTERM,
syscall.SIGQUIT,
}...)
<-interruptChannel
return nil
}
}
......@@ -74,11 +74,10 @@ describe('Basic L1<>L2 Communication', async () => {
}
)
let status: MessageStatus
while (status !== MessageStatus.READY_FOR_RELAY) {
status = await env.messenger.getMessageStatus(transaction)
await sleep(1000)
}
await env.messenger.waitForMessageStatus(
transaction,
MessageStatus.READY_FOR_RELAY
)
await env.messenger.finalizeMessage(transaction)
await env.messenger.waitForMessageReceipt(transaction)
......
/* Imports: External */
import { BigNumber, Contract, ContractFactory, utils, Wallet } from 'ethers'
import { ethers } from 'hardhat'
import { getContractFactory } from '@eth-optimism/contracts'
import { MessageStatus } from '@eth-optimism/sdk'
import { sleep } from '@eth-optimism/core-utils'
/* Imports: Internal */
import { expect } from './shared/setup'
import { OptimismEnv } from './shared/env'
import { withdrawalTest } from './shared/utils'
......@@ -61,7 +62,10 @@ describe('Bridged tokens', () => {
await L2__ERC20.deployed()
// Approve the L1 ERC20 to spend our money
const tx = await L1__ERC20.approve(env.l1Bridge.address, 1000000)
const tx = await L1__ERC20.approve(
env.messenger.contracts.l1.L1StandardBridge.address,
1000000
)
await tx.wait()
})
......@@ -103,12 +107,10 @@ describe('Bridged tokens', () => {
500
)
// TODO: Maybe this should be built into the SDK
let status: MessageStatus
while (status !== MessageStatus.READY_FOR_RELAY) {
status = await env.messenger.getMessageStatus(tx)
await sleep(1000)
}
await env.messenger.waitForMessageStatus(
tx,
MessageStatus.READY_FOR_RELAY
)
await env.messenger.finalizeMessage(tx)
await env.messenger.waitForMessageReceipt(tx)
......@@ -134,12 +136,10 @@ describe('Bridged tokens', () => {
}
)
// TODO: Maybe this should be built into the SDK
let status: MessageStatus
while (status !== MessageStatus.READY_FOR_RELAY) {
status = await env.messenger.getMessageStatus(tx)
await sleep(1000)
}
await env.messenger.waitForMessageStatus(
tx,
MessageStatus.READY_FOR_RELAY
)
await env.messenger.finalizeMessage(tx)
await env.messenger.waitForMessageReceipt(tx)
......@@ -196,12 +196,10 @@ describe('Bridged tokens', () => {
}
)
// TODO: Maybe this should be built into the SDK
let status: MessageStatus
while (status !== MessageStatus.READY_FOR_RELAY) {
status = await env.messenger.getMessageStatus(withdrawalTx)
await sleep(1000)
}
await env.messenger.waitForMessageStatus(
withdrawalTx,
MessageStatus.READY_FOR_RELAY
)
await env.messenger.finalizeMessage(withdrawalTx)
await env.messenger.waitForMessageReceipt(withdrawalTx)
......
/* Imports: External */
import { BigNumber, Contract, ContractFactory, utils, Wallet } from 'ethers'
import { ethers } from 'hardhat'
import { UniswapV3Deployer } from 'uniswap-v3-deploy-plugin/dist/deployer/UniswapV3Deployer'
......@@ -5,8 +6,9 @@ import { FeeAmount, TICK_SPACINGS } from '@uniswap/v3-sdk'
import { abi as NFTABI } from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'
import { abi as RouterABI } from '@uniswap/v3-periphery/artifacts/contracts/SwapRouter.sol/SwapRouter.json'
import { OptimismEnv } from './shared/env'
/* Imports: Internal */
import { expect } from './shared/setup'
import { OptimismEnv } from './shared/env'
// Below methods taken from the Uniswap test suite, see
// https://github.com/Uniswap/v3-periphery/blob/main/test/shared/ticks.ts
......
......@@ -5,13 +5,17 @@ import { predeploys, getContractFactory } from '@eth-optimism/contracts'
/* Imports: Internal */
import { expect } from './shared/setup'
import { hardhatTest } from './shared/utils'
import { hardhatTest, gasPriceOracleWallet } from './shared/utils'
import { OptimismEnv } from './shared/env'
const setPrices = async (env: OptimismEnv, value: number | BigNumber) => {
const gasPrice = await env.gasPriceOracle.setGasPrice(value)
const gasPrice = await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).setGasPrice(value)
await gasPrice.wait()
const baseFee = await env.gasPriceOracle.setL1BaseFee(value)
const baseFee = await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).setL1BaseFee(value)
await baseFee.wait()
}
......@@ -28,13 +32,18 @@ describe('Fee Payment Integration Tests', async () => {
async () => {
const assertGasPrice = async () => {
const gasPrice = await env.l2Wallet.getGasPrice()
const oracleGasPrice = await env.gasPriceOracle.gasPrice()
const oracleGasPrice =
await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).gasPrice()
expect(gasPrice).to.deep.equal(oracleGasPrice)
}
await assertGasPrice()
// update the gas price
const tx = await env.gasPriceOracle.setGasPrice(1000)
const tx = await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).setGasPrice(1000)
await tx.wait()
await assertGasPrice()
......@@ -47,7 +56,7 @@ describe('Fee Payment Integration Tests', async () => {
const amount = utils.parseEther('0.0000001')
const balanceBefore = await env.l2Wallet.getBalance()
const feeVaultBalanceBefore = await env.l2Wallet.provider.getBalance(
env.sequencerFeeVault.address
env.messenger.contracts.l2.OVM_SequencerFeeVault.address
)
expect(balanceBefore.gt(amount))
......@@ -66,7 +75,9 @@ describe('Fee Payment Integration Tests', async () => {
data: unsigned.data,
})
const l1Fee = await env.gasPriceOracle.getL1Fee(raw)
const l1Fee = await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).getL1Fee(raw)
const tx = await env.l2Wallet.sendTransaction(unsigned)
const receipt = await tx.wait()
......@@ -74,7 +85,7 @@ describe('Fee Payment Integration Tests', async () => {
const balanceAfter = await env.l2Wallet.getBalance()
const feeVaultBalanceAfter = await env.l2Wallet.provider.getBalance(
env.sequencerFeeVault.address
env.messenger.contracts.l2.OVM_SequencerFeeVault.address
)
const l2Fee = receipt.gasUsed.mul(tx.gasPrice)
......@@ -124,7 +135,9 @@ describe('Fee Payment Integration Tests', async () => {
data: unsigned.data,
})
const l1Fee = await OVM_GasPriceOracle.getL1Fee(raw)
const l1Fee = await OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).getL1Fee(raw)
const tx = await env.l2Wallet.sendTransaction(unsigned)
const receipt = await tx.wait()
......@@ -142,30 +155,33 @@ describe('Fee Payment Integration Tests', async () => {
})
it('should not be able to withdraw fees before the minimum is met', async () => {
await expect(env.sequencerFeeVault.withdraw()).to.be.rejected
await expect(env.messenger.contracts.l2.OVM_SequencerFeeVault.withdraw()).to
.be.rejected
})
hardhatTest(
'should be able to withdraw fees back to L1 once the minimum is met',
async () => {
const l1FeeWallet = await env.sequencerFeeVault.l1FeeWallet()
const l1FeeWallet =
await env.messenger.contracts.l2.OVM_SequencerFeeVault.l1FeeWallet()
const balanceBefore = await env.l1Wallet.provider.getBalance(l1FeeWallet)
const withdrawalAmount =
await env.sequencerFeeVault.MIN_WITHDRAWAL_AMOUNT()
await env.messenger.contracts.l2.OVM_SequencerFeeVault.MIN_WITHDRAWAL_AMOUNT()
// Transfer the minimum required to withdraw.
const tx = await env.l2Wallet.sendTransaction({
to: env.sequencerFeeVault.address,
to: env.messenger.contracts.l2.OVM_SequencerFeeVault.address,
value: withdrawalAmount,
gasLimit: 500000,
})
await tx.wait()
const vaultBalance = await env.ovmEth.balanceOf(
env.sequencerFeeVault.address
const vaultBalance = await env.messenger.contracts.l2.OVM_ETH.balanceOf(
env.messenger.contracts.l2.OVM_SequencerFeeVault.address
)
const withdrawTx = await env.sequencerFeeVault.withdraw()
const withdrawTx =
await env.messenger.contracts.l2.OVM_SequencerFeeVault.withdraw()
// Wait for the withdrawal to be relayed to L1.
await withdrawTx.wait()
......
/* Imports: External */
import { Contract, BigNumber } from 'ethers'
import { ethers } from 'hardhat'
/* Imports: Internal */
import { expect } from './shared/setup'
import { OptimismEnv } from './shared/env'
......
/* Imports: External */
import { BigNumber, Contract, ContractFactory, Wallet } from 'ethers'
import { ethers } from 'hardhat'
/* Imports: Internal */
import { expect } from './shared/setup'
import {
fundUser,
......@@ -121,8 +123,10 @@ describe('Native ETH value integration tests', () => {
'geth RPC does not match ovmSELFBALANCE'
)
// query ovmSELFBALANCE() opcode via eth_call as another check
const ovmEthBalanceOf0 = await env.ovmEth.balanceOf(ValueCalls0.address)
const ovmEthBalanceOf1 = await env.ovmEth.balanceOf(ValueCalls1.address)
const ovmEthBalanceOf0 =
await env.messenger.contracts.l2.OVM_ETH.balanceOf(ValueCalls0.address)
const ovmEthBalanceOf1 =
await env.messenger.contracts.l2.OVM_ETH.balanceOf(ValueCalls1.address)
expect(ovmEthBalanceOf0).to.deep.eq(
BigNumber.from(expectedBalances[0]),
'geth RPC does not match OVM_ETH.balanceOf'
......
......@@ -11,6 +11,7 @@ import {
DEFAULT_TEST_GAS_L2,
envConfig,
withdrawalTest,
gasPriceOracleWallet,
} from './shared/utils'
import { OptimismEnv } from './shared/env'
......@@ -30,7 +31,7 @@ describe('Native ETH Integration Tests', async () => {
const l2BobBalance = await l2Bob.getBalance()
const l1BridgeBalance = await _env.l1Wallet.provider.getBalance(
_env.l1Bridge.address
_env.messenger.contracts.l1.L1StandardBridge.address
)
return {
......@@ -51,12 +52,13 @@ describe('Native ETH Integration Tests', async () => {
describe('estimateGas', () => {
it('Should estimate gas for ETH withdraw', async () => {
const amount = utils.parseEther('0.0000001')
const gas = await env.l2Bridge.estimateGas.withdraw(
predeploys.OVM_ETH,
amount,
0,
'0xFFFF'
)
const gas =
await env.messenger.contracts.l2.L2StandardBridge.estimateGas.withdraw(
predeploys.OVM_ETH,
amount,
0,
'0xFFFF'
)
// Expect gas to be less than or equal to the target plus 1%
expectApprox(gas, 6700060, { absoluteUpperDeviation: 1000 })
})
......@@ -67,7 +69,7 @@ describe('Native ETH Integration Tests', async () => {
const preBalances = await getBalances(env)
const { tx, receipt } = await env.waitForXDomainTransaction(
env.l1Wallet.sendTransaction({
to: env.l1Bridge.address,
to: env.messenger.contracts.l1.L1StandardBridge.address,
value: depositAmount,
gasLimit: DEFAULT_TEST_GAS_L1,
})
......@@ -91,10 +93,14 @@ describe('Native ETH Integration Tests', async () => {
const depositAmount = 10
const preBalances = await getBalances(env)
const { tx, receipt } = await env.waitForXDomainTransaction(
env.l1Bridge.depositETH(DEFAULT_TEST_GAS_L2, '0xFFFF', {
value: depositAmount,
gasLimit: DEFAULT_TEST_GAS_L1,
})
env.messenger.contracts.l1.L1StandardBridge.depositETH(
DEFAULT_TEST_GAS_L2,
'0xFFFF',
{
value: depositAmount,
gasLimit: DEFAULT_TEST_GAS_L1,
}
)
)
const l1FeePaid = receipt.gasUsed.mul(tx.gasPrice)
......@@ -115,10 +121,15 @@ describe('Native ETH Integration Tests', async () => {
const depositAmount = 10
const preBalances = await getBalances(env)
const depositReceipts = await env.waitForXDomainTransaction(
env.l1Bridge.depositETHTo(l2Bob.address, DEFAULT_TEST_GAS_L2, '0xFFFF', {
value: depositAmount,
gasLimit: DEFAULT_TEST_GAS_L1,
})
env.messenger.contracts.l1.L1StandardBridge.depositETHTo(
l2Bob.address,
DEFAULT_TEST_GAS_L2,
'0xFFFF',
{
value: depositAmount,
gasLimit: DEFAULT_TEST_GAS_L1,
}
)
)
const l1FeePaid = depositReceipts.receipt.gasUsed.mul(
......@@ -145,10 +156,14 @@ describe('Native ETH Integration Tests', async () => {
// to allow for encoding and other arguments
const data = `0x` + 'ab'.repeat(MAX_ROLLUP_TX_SIZE - 500)
const { tx, receipt } = await env.waitForXDomainTransaction(
env.l1Bridge.depositETH(ASSUMED_L2_GAS_LIMIT, data, {
value: depositAmount,
gasLimit: 4_000_000,
})
env.messenger.contracts.l1.L1StandardBridge.depositETH(
ASSUMED_L2_GAS_LIMIT,
data,
{
value: depositAmount,
gasLimit: 4_000_000,
}
)
)
const l1FeePaid = receipt.gasUsed.mul(tx.gasPrice)
......@@ -169,9 +184,13 @@ describe('Native ETH Integration Tests', async () => {
const data = `0x` + 'ab'.repeat(MAX_ROLLUP_TX_SIZE + 1)
await expect(
env.l1Bridge.depositETH(DEFAULT_TEST_GAS_L2, data, {
value: depositAmount,
})
env.messenger.contracts.l1.L1StandardBridge.depositETH(
DEFAULT_TEST_GAS_L2,
data,
{
value: depositAmount,
}
)
).to.be.reverted
})
......@@ -183,12 +202,13 @@ describe('Native ETH Integration Tests', async () => {
'Cannot run withdrawal test before any deposits...'
)
const transaction = await env.l2Bridge.withdraw(
predeploys.OVM_ETH,
withdrawAmount,
DEFAULT_TEST_GAS_L2,
'0xFFFF'
)
const transaction =
await env.messenger.contracts.l2.L2StandardBridge.withdraw(
predeploys.OVM_ETH,
withdrawAmount,
DEFAULT_TEST_GAS_L2,
'0xFFFF'
)
await transaction.wait()
await env.relayXDomainMessages(transaction)
const receipts = await env.waitForXDomainTransaction(transaction)
......@@ -224,13 +244,14 @@ describe('Native ETH Integration Tests', async () => {
'Cannot run withdrawal test before any deposits...'
)
const transaction = await env.l2Bridge.withdrawTo(
predeploys.OVM_ETH,
l1Bob.address,
withdrawAmount,
DEFAULT_TEST_GAS_L2,
'0xFFFF'
)
const transaction =
await env.messenger.contracts.l2.L2StandardBridge.withdrawTo(
predeploys.OVM_ETH,
l1Bob.address,
withdrawAmount,
DEFAULT_TEST_GAS_L2,
'0xFFFF'
)
await transaction.wait()
await env.relayXDomainMessages(transaction)
......@@ -248,7 +269,9 @@ describe('Native ETH Integration Tests', async () => {
data: transaction.data,
})
const l1Fee = await env.gasPriceOracle.getL1Fee(raw)
const l1Fee = await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).getL1Fee(raw)
const fee = l2Fee.add(l1Fee)
const postBalances = await getBalances(env)
......@@ -275,10 +298,14 @@ describe('Native ETH Integration Tests', async () => {
// 1. deposit
const amount = utils.parseEther('1')
await env.waitForXDomainTransaction(
env.l1Bridge.depositETH(DEFAULT_TEST_GAS_L2, '0xFFFF', {
value: amount,
gasLimit: DEFAULT_TEST_GAS_L1,
})
env.messenger.contracts.l1.L1StandardBridge.depositETH(
DEFAULT_TEST_GAS_L2,
'0xFFFF',
{
value: amount,
gasLimit: DEFAULT_TEST_GAS_L1,
}
)
)
// 2. transfer to another address
......@@ -295,9 +322,10 @@ describe('Native ETH Integration Tests', async () => {
// 3. do withdrawal
const withdrawnAmount = utils.parseEther('0.95')
const transaction = await env.l2Bridge
.connect(other)
.withdraw(
const transaction =
await env.messenger.contracts.l2.L2StandardBridge.connect(
other
).withdraw(
predeploys.OVM_ETH,
withdrawnAmount,
DEFAULT_TEST_GAS_L1,
......@@ -308,16 +336,19 @@ describe('Native ETH Integration Tests', async () => {
const receipts = await env.waitForXDomainTransaction(transaction)
// Compute the L1 portion of the fee
const l1Fee = await env.gasPriceOracle.getL1Fee(
serialize({
nonce: transaction.nonce,
value: transaction.value,
gasPrice: transaction.gasPrice,
gasLimit: transaction.gasLimit,
to: transaction.to,
data: transaction.data,
})
)
const l1Fee =
await await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).getL1Fee(
serialize({
nonce: transaction.nonce,
value: transaction.value,
gasPrice: transaction.gasPrice,
gasLimit: transaction.gasLimit,
to: transaction.to,
data: transaction.data,
})
)
// check that correct amount was withdrawn and that fee was charged
const l2Fee = receipts.tx.gasPrice.mul(receipts.receipt.gasUsed)
......
......@@ -44,14 +44,15 @@ describe('OVM Context: Layer 2 EVM Context', () => {
for (let i = 0; i < numTxs; i++) {
// Send a transaction from L1 to L2. This will automatically update the L1 contextual
// information like the L1 block number and L1 timestamp.
const tx = await env.l1Messenger.sendMessage(
OVMContextStorage.address,
'0x',
2_000_000,
{
gasLimit: DEFAULT_TEST_GAS_L1,
}
)
const tx =
await env.messenger.contracts.l1.L1CrossDomainMessenger.sendMessage(
OVMContextStorage.address,
'0x',
2_000_000,
{
gasLimit: DEFAULT_TEST_GAS_L1,
}
)
// Wait for the transaction to be sent over to L2.
await tx.wait()
......
/* Imports: Internal */
/* Imports: External */
import { ethers } from 'ethers'
import { predeploys, getContractInterface } from '@eth-optimism/contracts'
/* Imports: External */
/* Imports: Internal */
import { expect } from './shared/setup'
import { OptimismEnv } from './shared/env'
......
......@@ -26,14 +26,15 @@ describe('Queue Ingestion', () => {
// the transaction to Layer 1
const txs = []
for (let i = 0; i < numTxs; i++) {
const tx = await env.l1Messenger.sendMessage(
`0x${`${i}`.repeat(40)}`,
`0x0${i}`,
1_000_000,
{
gasLimit: DEFAULT_TEST_GAS_L1,
}
)
const tx =
await env.messenger.contracts.l1.L1CrossDomainMessenger.sendMessage(
`0x${`${i}`.repeat(40)}`,
`0x0${i}`,
1_000_000,
{
gasLimit: DEFAULT_TEST_GAS_L1,
}
)
await tx.wait()
txs.push(tx)
}
......@@ -46,10 +47,11 @@ describe('Queue Ingestion', () => {
receipt.remoteTx.hash
)) as any
const params = env.l2Messenger.interface.decodeFunctionData(
'relayMessage',
l2Tx.data
)
const params =
env.messenger.contracts.l2.L2CrossDomainMessenger.interface.decodeFunctionData(
'relayMessage',
l2Tx.data
)
expect(params._sender.toLowerCase()).to.equal(
env.l1Wallet.address.toLowerCase()
......@@ -57,7 +59,9 @@ describe('Queue Ingestion', () => {
expect(params._target).to.equal('0x' + `${i}`.repeat(40))
expect(l2Tx.queueOrigin).to.equal('l1')
expect(l2Tx.l1TxOrigin.toLowerCase()).to.equal(
applyL1ToL2Alias(env.l1Messenger.address).toLowerCase()
applyL1ToL2Alias(
env.messenger.contracts.l1.L1CrossDomainMessenger.address
).toLowerCase()
)
expect(l2Tx.l1BlockNumber).to.equal(l1TxReceipt.blockNumber)
}
......
......@@ -9,6 +9,7 @@ import {
} from '@ethersproject/providers'
/* Imports: Internal */
import { expect } from './shared/setup'
import {
defaultTransactionFactory,
fundUser,
......@@ -17,9 +18,9 @@ import {
isHardhat,
hardhatTest,
envConfig,
gasPriceOracleWallet,
} from './shared/utils'
import { OptimismEnv } from './shared/env'
import { expect } from './shared/setup'
describe('Basic RPC tests', () => {
let env: OptimismEnv
......@@ -149,8 +150,12 @@ describe('Basic RPC tests', () => {
const isHH = await isHardhat()
let gasPrice
if (isHH) {
gasPrice = await env.gasPriceOracle.gasPrice()
await env.gasPriceOracle.setGasPrice(1000)
gasPrice = await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).gasPrice()
await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).setGasPrice(1000)
}
const tx = {
......@@ -164,12 +169,17 @@ describe('Basic RPC tests', () => {
if (isHH) {
// Reset the gas price to its original price
await env.gasPriceOracle.setGasPrice(gasPrice)
await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).setGasPrice(gasPrice)
}
})
it('should reject a transaction with too high of a fee', async () => {
const gasPrice = await env.gasPriceOracle.gasPrice()
const gasPrice =
await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).gasPrice()
const largeGasPrice = gasPrice.mul(10)
const tx = {
...defaultTransactionFactory(),
......@@ -341,11 +351,25 @@ describe('Basic RPC tests', () => {
data: tx.data,
})
const l1Fee = await env.gasPriceOracle.getL1Fee(raw)
const l1GasPrice = await env.gasPriceOracle.l1BaseFee()
const l1GasUsed = await env.gasPriceOracle.getL1GasUsed(raw)
const scalar = await env.gasPriceOracle.scalar()
const decimals = await env.gasPriceOracle.decimals()
const l1Fee = await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).getL1Fee(raw)
const l1GasPrice =
await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).l1BaseFee()
const l1GasUsed =
await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).getL1GasUsed(raw)
const scalar =
await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).scalar()
const decimals =
await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).decimals()
const scaled = scalar.toNumber() / 10 ** decimals.toNumber()
......@@ -439,7 +463,8 @@ describe('Basic RPC tests', () => {
describe('eth_getBalance', () => {
it('should get the OVM_ETH balance', async () => {
const rpcBalance = await env.l2Provider.getBalance(env.l2Wallet.address)
const contractBalance = await env.ovmEth.balanceOf(env.l2Wallet.address)
const contractBalance =
await env.messenger.contracts.l2.OVM_ETH.balanceOf(env.l2Wallet.address)
expect(rpcBalance).to.be.deep.eq(contractBalance)
})
})
......@@ -525,9 +550,14 @@ describe('Basic RPC tests', () => {
describe('rollup_gasPrices', () => {
it('should return the L1 and L2 gas prices', async () => {
const result = await env.l2Provider.send('rollup_gasPrices', [])
const l1GasPrice = await env.gasPriceOracle.l1BaseFee()
const l2GasPrice = await env.gasPriceOracle.gasPrice()
const l1GasPrice =
await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).l1BaseFee()
const l2GasPrice =
await env.messenger.contracts.l2.OVM_GasPriceOracle.connect(
gasPriceOracleWallet
).gasPrice()
expect(BigNumber.from(result.l1GasPrice)).to.deep.eq(l1GasPrice)
expect(BigNumber.from(result.l2GasPrice)).to.deep.eq(l2GasPrice)
})
......
/* Imports: External */
import { Contract, utils, Wallet, providers, Transaction } from 'ethers'
import { utils, Wallet, providers, Transaction } from 'ethers'
import {
TransactionResponse,
TransactionReceipt,
} from '@ethersproject/providers'
import { getContractFactory, predeploys } from '@eth-optimism/contracts'
import { sleep } from '@eth-optimism/core-utils'
import {
CrossChainMessenger,
......@@ -14,18 +13,13 @@ import {
/* Imports: Internal */
import {
getAddressManager,
l1Provider,
l2Provider,
replicaProvider,
verifierProvider,
l1Wallet,
l2Wallet,
gasPriceOracleWallet,
fundUser,
getOvmEth,
getL1Bridge,
getL2Bridge,
envConfig,
} from './utils'
......@@ -38,21 +32,6 @@ export interface CrossDomainMessagePair {
/// Helper class for instantiating a test environment with a funded account
export class OptimismEnv {
// L1 Contracts
addressManager: Contract
l1Bridge: Contract
l1Messenger: Contract
l1BlockNumber: Contract
ctc: Contract
scc: Contract
// L2 Contracts
ovmEth: Contract
l2Bridge: Contract
l2Messenger: Contract
gasPriceOracle: Contract
sequencerFeeVault: Contract
// The wallets
l1Wallet: Wallet
l2Wallet: Wallet
......@@ -65,15 +44,6 @@ export class OptimismEnv {
verifierProvider: providers.JsonRpcProvider
constructor(args: any) {
this.addressManager = args.addressManager
this.l1Bridge = args.l1Bridge
this.l1Messenger = args.l1Messenger
this.l1BlockNumber = args.l1BlockNumber
this.ovmEth = args.ovmEth
this.l2Bridge = args.l2Bridge
this.l2Messenger = args.l2Messenger
this.gasPriceOracle = args.gasPriceOracle
this.sequencerFeeVault = args.sequencerFeeVault
this.l1Wallet = args.l1Wallet
this.l2Wallet = args.l2Wallet
this.messenger = args.messenger
......@@ -81,68 +51,15 @@ export class OptimismEnv {
this.l2Provider = args.l2Provider
this.replicaProvider = args.replicaProvider
this.verifierProvider = args.verifierProvider
this.ctc = args.ctc
this.scc = args.scc
}
static async new(): Promise<OptimismEnv> {
const network = await l1Provider.getNetwork()
const addressManager = getAddressManager(l1Wallet)
const l1Bridge = await getL1Bridge(l1Wallet, addressManager)
const l1MessengerAddress = await addressManager.getAddress(
'Proxy__OVM_L1CrossDomainMessenger'
)
const l2MessengerAddress = await addressManager.getAddress(
'L2CrossDomainMessenger'
)
const l1Messenger = getContractFactory('L1CrossDomainMessenger')
.connect(l1Wallet)
.attach(l1MessengerAddress)
const ovmEth = getOvmEth(l2Wallet)
const l2Bridge = await getL2Bridge(l2Wallet)
const l2Messenger = getContractFactory('L2CrossDomainMessenger')
.connect(l2Wallet)
.attach(l2MessengerAddress)
const ctcAddress = await addressManager.getAddress(
'CanonicalTransactionChain'
)
const ctc = getContractFactory('CanonicalTransactionChain')
.connect(l1Wallet)
.attach(ctcAddress)
const gasPriceOracle = getContractFactory('OVM_GasPriceOracle')
.connect(gasPriceOracleWallet)
.attach(predeploys.OVM_GasPriceOracle)
const sccAddress = await addressManager.getAddress('StateCommitmentChain')
const scc = getContractFactory('StateCommitmentChain')
.connect(l1Wallet)
.attach(sccAddress)
const sequencerFeeVault = getContractFactory('OVM_SequencerFeeVault')
.connect(l2Wallet)
.attach(predeploys.OVM_SequencerFeeVault)
const l1BlockNumber = getContractFactory('iOVM_L1BlockNumber')
.connect(l2Wallet)
.attach(predeploys.OVM_L1BlockNumber)
const messenger = new CrossChainMessenger({
l1SignerOrProvider: l1Wallet,
l2SignerOrProvider: l2Wallet,
l1ChainId: network.chainId,
contracts: {
l1: {
AddressManager: envConfig.ADDRESS_MANAGER,
L1CrossDomainMessenger: l1Messenger.address,
L1StandardBridge: l1Bridge.address,
StateCommitmentChain: sccAddress,
CanonicalTransactionChain: ctcAddress,
BondManager: await addressManager.getAddress('BondManager'),
},
},
})
// fund the user if needed
......@@ -154,17 +71,6 @@ export class OptimismEnv {
}
return new OptimismEnv({
addressManager,
l1Bridge,
ctc,
scc,
l1Messenger,
l1BlockNumber,
ovmEth,
gasPriceOracle,
sequencerFeeVault,
l2Bridge,
l2Messenger,
l1Wallet,
l2Wallet,
messenger,
......@@ -223,14 +129,10 @@ export class OptimismEnv {
}
for (const message of messages) {
let status: MessageStatus
while (
status !== MessageStatus.READY_FOR_RELAY &&
status !== MessageStatus.RELAYED
) {
status = await this.messenger.getMessageStatus(message)
await sleep(1000)
}
await this.messenger.waitForMessageStatus(
message,
MessageStatus.READY_FOR_RELAY
)
let relayed = false
while (!relayed) {
......
......@@ -37,19 +37,19 @@ export const executeL1ToL2Transaction = async (
) => {
const signer = wallet.connect(env.l1Wallet.provider)
const receipt = await retryOnNonceError(async () =>
env.l1Messenger
.connect(signer)
.sendMessage(
tx.contract.address,
tx.contract.interface.encodeFunctionData(
tx.functionName,
tx.functionParams
),
MESSAGE_GAS,
{
gasPrice: await gasPriceForL1(),
}
)
env.messenger.contracts.l1.L1CrossDomainMessenger.connect(
signer
).sendMessage(
tx.contract.address,
tx.contract.interface.encodeFunctionData(
tx.functionName,
tx.functionParams
),
MESSAGE_GAS,
{
gasPrice: await gasPriceForL1(),
}
)
)
await env.waitForXDomainTransaction(receipt)
}
......@@ -61,19 +61,19 @@ export const executeL2ToL1Transaction = async (
) => {
const signer = wallet.connect(env.l2Wallet.provider)
const receipt = await retryOnNonceError(() =>
env.l2Messenger
.connect(signer)
.sendMessage(
tx.contract.address,
tx.contract.interface.encodeFunctionData(
tx.functionName,
tx.functionParams
),
MESSAGE_GAS,
{
gasPrice: gasPriceForL2(),
}
)
env.messenger.contracts.l2.L2CrossDomainMessenger.connect(
signer
).sendMessage(
tx.contract.address,
tx.contract.interface.encodeFunctionData(
tx.functionName,
tx.functionParams
),
MESSAGE_GAS,
{
gasPrice: gasPriceForL2(),
}
)
)
await env.relayXDomainMessages(receipt)
......
/* Imports: External */
import {
Contract,
Wallet,
constants,
providers,
BigNumber,
utils,
} from 'ethers'
import {
getContractFactory,
getContractInterface,
predeploys,
} from '@eth-optimism/contracts'
import { Wallet, providers, BigNumber, utils } from 'ethers'
import { predeploys } from '@eth-optimism/contracts'
import { remove0x } from '@eth-optimism/core-utils'
import {
CrossChainMessenger,
......@@ -49,7 +38,7 @@ const procEnv = cleanEnv(process.env, {
L1_URL: str({ default: 'http://localhost:9545' }),
L1_POLLING_INTERVAL: num({ default: 10 }),
L2_CHAINID: num({ default: 420 }),
L2_CHAINID: num({ default: 987 }),
L2_GAS_PRICE: gasPriceValidator({
default: 'onchain',
}),
......@@ -71,9 +60,6 @@ const procEnv = cleanEnv(process.env, {
default:
'0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
}),
ADDRESS_MANAGER: str({
default: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
}),
GAS_PRICE_ORACLE_PRIVATE_KEY: str({
default:
'0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba',
......@@ -101,6 +87,9 @@ const procEnv = cleanEnv(process.env, {
RUN_VERIFIER_TESTS: bool({
default: true,
}),
RUN_SYSTEM_ADDRESS_TESTS: bool({
default: false,
}),
MOCHA_TIMEOUT: num({
default: 120_000,
......@@ -108,6 +97,9 @@ const procEnv = cleanEnv(process.env, {
MOCHA_BAIL: bool({
default: false,
}),
BATCH_SUBMITTER_SEQUENCER_BATCH_TYPE: str({
default: 'zlib',
}),
})
export const envConfig = procEnv
......@@ -149,39 +141,6 @@ export const OVM_ETH_ADDRESS = predeploys.OVM_ETH
export const L2_CHAINID = procEnv.L2_CHAINID
export const getAddressManager = (provider: any) => {
return getContractFactory('Lib_AddressManager')
.connect(provider)
.attach(procEnv.ADDRESS_MANAGER)
}
// Gets the bridge contract
export const getL1Bridge = async (wallet: Wallet, AddressManager: Contract) => {
const l1BridgeInterface = getContractInterface('L1StandardBridge')
const ProxyBridgeAddress = await AddressManager.getAddress(
'Proxy__OVM_L1StandardBridge'
)
if (
!utils.isAddress(ProxyBridgeAddress) ||
ProxyBridgeAddress === constants.AddressZero
) {
throw new Error('Proxy__OVM_L1StandardBridge not found')
}
return new Contract(ProxyBridgeAddress, l1BridgeInterface, wallet)
}
export const getL2Bridge = async (wallet: Wallet) => {
const L2BridgeInterface = getContractInterface('L2StandardBridge')
return new Contract(predeploys.L2StandardBridge, L2BridgeInterface, wallet)
}
export const getOvmEth = (wallet: Wallet) => {
return new Contract(OVM_ETH_ADDRESS, getContractInterface('OVM_ETH'), wallet)
}
export const fundUser = async (
messenger: CrossChainMessenger,
amount: NumberLike,
......
......@@ -210,17 +210,19 @@ describe('stress tests', () => {
const tip = await env.l2Provider.getBlock('latest')
const prev = {
block: await env.l2Provider.getBlock(0),
l1BlockNumber: await env.l1BlockNumber.getL1BlockNumber({
blockTag: 0,
}),
l1BlockNumber:
await env.messenger.contracts.l2.OVM_L1BlockNumber.getL1BlockNumber({
blockTag: 0,
}),
}
for (let i = 1; i < tip.number; i++) {
const block = await env.l2Provider.getBlock(i)
expect(block.timestamp).to.be.gte(prev.block.timestamp)
const l1BlockNumber = await env.l1BlockNumber.getL1BlockNumber({
blockTag: i,
})
const l1BlockNumber =
await env.messenger.contracts.l2.OVM_L1BlockNumber.getL1BlockNumber({
blockTag: i,
})
expect(l1BlockNumber.gt(prev.l1BlockNumber))
prev.block = block
......
/* Imports: External */
import { BigNumber, Contract, ContractFactory, utils, Wallet } from 'ethers'
import { ethers } from 'hardhat'
import { futurePredeploys } from '@eth-optimism/contracts'
/* Imports: Internal */
import { expect } from './shared/setup'
import { OptimismEnv } from './shared/env'
import { envConfig } from './shared/utils'
const SYSTEM_ADDRESSES = [futurePredeploys.System0, futurePredeploys.System1]
describe('System addresses', () => {
let env: OptimismEnv
let deployerWallets: Wallet[] = []
let contracts: Contract[]
let Factory__ERC20: ContractFactory
before(async function () {
if (!envConfig.RUN_SYSTEM_ADDRESS_TESTS) {
console.log('Skipping system address tests.')
this.skip()
return
}
env = await OptimismEnv.new()
deployerWallets = [
new Wallet(process.env.SYSTEM_ADDRESS_0_DEPLOYER_KEY, env.l2Provider),
new Wallet(process.env.SYSTEM_ADDRESS_1_DEPLOYER_KEY, env.l2Provider),
]
for (const deployer of deployerWallets) {
await env.l2Wallet.sendTransaction({
to: deployer.address,
value: utils.parseEther('0.1'),
})
}
contracts = []
Factory__ERC20 = await ethers.getContractFactory('ERC20', env.l2Wallet)
})
it('should have no code for the system addresses initially', async () => {
for (const addr of SYSTEM_ADDRESSES) {
const code = await env.l2Provider.getCode(addr, 'latest')
expect(code).to.eq('0x')
}
})
it('should deploy to the system address', async () => {
for (let i = 0; i < deployerWallets.length; i++) {
const contract = await Factory__ERC20.connect(deployerWallets[i]).deploy(
100000000,
'OVM Test',
8,
'OVM'
)
// have to use the receipt here since ethers calculates the
// contract address on-the-fly
const receipt = await contract.deployTransaction.wait()
expect(receipt.contractAddress).to.eq(SYSTEM_ADDRESSES[i])
const fetchedReceipt = await env.l2Provider.getTransactionReceipt(
receipt.transactionHash
)
expect(fetchedReceipt.contractAddress).to.eq(SYSTEM_ADDRESSES[i])
contracts.push(await ethers.getContractAt('ERC20', SYSTEM_ADDRESSES[i]))
}
})
it('contracts deployed at the system addresses should function', async () => {
expect(contracts.length).to.eq(2)
for (let i = 0; i < contracts.length; i++) {
const wallet = deployerWallets[i]
const contract = contracts[i].connect(wallet)
const code = await env.l2Provider.getCode(contract.address, 'latest')
expect(code).not.to.eq('0x')
const tx = await contract.transfer(env.l2Wallet.address, 1000)
await tx.wait()
const bal = await contract.balanceOf(env.l2Wallet.address)
expect(bal).to.deep.equal(BigNumber.from(1000))
}
})
it('should not deploy any additional contracts from the deployer at the system address', async () => {
for (let i = 0; i < deployerWallets.length; i++) {
const contract = await Factory__ERC20.connect(deployerWallets[i]).deploy(
100000000,
'OVM Test',
8,
'OVM'
)
await contract.deployed()
const receipt = await contract.deployTransaction.wait()
expect(receipt.contractAddress).not.to.eq(SYSTEM_ADDRESSES[i])
expect(receipt.contractAddress).not.to.eq(null)
}
})
})
import { SequencerBatch, BatchType } from '@eth-optimism/core-utils'
import { expect } from './shared/setup'
import { OptimismEnv } from './shared/env'
import { envConfig } from './shared/utils'
describe('Batch Serialization', () => {
let env: OptimismEnv
// Allow for each type to be tested. The env var here must be
// the same value that is passed to the batch submitter
const batchType = envConfig.BATCH_SUBMITTER_SEQUENCER_BATCH_TYPE.toUpperCase()
before(async () => {
env = await OptimismEnv.new()
})
it('should fetch batches', async () => {
const tip = await env.l1Provider.getBlockNumber()
const ctc = env.messenger.contracts.l1.CanonicalTransactionChain
const logs = await ctc.queryFilter(
ctc.filters.TransactionBatchAppended(),
0,
tip
)
// collect all of the batches
const batches = []
for (const log of logs) {
const tx = await env.l1Provider.getTransaction(log.transactionHash)
batches.push(tx.data)
}
expect(batches.length).to.be.gt(0, 'Submit some batches first')
let latest = 0
// decode all of the batches
for (const batch of batches) {
// Typings don't work?
const decoded = (SequencerBatch as any).fromHex(batch)
expect(decoded.type).to.eq(BatchType[batchType])
// Iterate over all of the transactions, fetch them
// by hash and make sure their blocknumbers are in
// ascending order. This lets us skip handling deposits here
for (const transaction of decoded.transactions) {
const tx = transaction.toTransaction()
const got = await env.l2Provider.getTransaction(tx.hash)
expect(got).to.not.eq(null)
expect(got.blockNumber).to.be.gt(latest)
latest = got.blockNumber
}
}
})
})
......@@ -26,6 +26,7 @@ import (
"github.com/ethereum-optimism/optimism/l2geth/crypto"
"github.com/ethereum-optimism/optimism/l2geth/params"
"github.com/ethereum-optimism/optimism/l2geth/rollup/fees"
"github.com/ethereum-optimism/optimism/l2geth/rollup/rcfg"
)
// StateProcessor is a basic Processor, which takes care of transitioning
......@@ -132,7 +133,18 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
receipt.GasUsed = gas
// if the transaction created a contract, store the creation address in the receipt.
if msg.To() == nil {
receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
if rcfg.UsingOVM {
sysAddress := rcfg.SystemAddressFor(config.ChainID, vmenv.Context.Origin)
// If nonce is zero, and the deployer is a system address deployer,
// set the provided system contract address.
if sysAddress != rcfg.ZeroSystemAddress && tx.Nonce() == 0 && tx.To() == nil {
receipt.ContractAddress = sysAddress
} else {
receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
}
} else {
receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
}
}
// Set the receipt logs and create a bloom for filtering
receipt.Logs = statedb.GetLogs(tx.Hash())
......
......@@ -24,6 +24,8 @@ import (
"math/big"
"unsafe"
"github.com/ethereum-optimism/optimism/l2geth/rollup/rcfg"
"github.com/ethereum-optimism/optimism/l2geth/common"
"github.com/ethereum-optimism/optimism/l2geth/common/hexutil"
"github.com/ethereum-optimism/optimism/l2geth/crypto"
......@@ -352,7 +354,20 @@ func (r Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, num
if txs[i].To() == nil {
// Deriving the signer is expensive, only do if it's actually needed
from, _ := Sender(signer, txs[i])
r[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce())
nonce := txs[i].Nonce()
if rcfg.UsingOVM {
sysAddress := rcfg.SystemAddressFor(config.ChainID, from)
// If nonce is zero, and the deployer is a system address deployer,
// set the provided system contract address.
if sysAddress != rcfg.ZeroSystemAddress && nonce == 0 && txs[i].To() == nil {
r[i].ContractAddress = sysAddress
} else {
r[i].ContractAddress = crypto.CreateAddress(from, nonce)
}
} else {
r[i].ContractAddress = crypto.CreateAddress(from, nonce)
}
}
// The used gas can be calculated based on previous r
if i == 0 {
......
......@@ -416,6 +416,16 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
return ret, common.Address{}, gas, errExecutionReverted
}
// Get the system address for this caller.
sysAddr := rcfg.SystemAddressFor(evm.ChainConfig().ChainID, caller.Address())
// If there is a configured system address for this caller, and the caller's nonce is zero,
// and there is no contract already deployed at this system address, then set the created
// address to the system address.
if sysAddr != rcfg.ZeroSystemAddress && evm.StateDB.GetNonce(caller.Address()) == 0 {
address = sysAddr
}
}
nonce := evm.StateDB.GetNonce(caller.Address())
evm.StateDB.SetNonce(caller.Address(), nonce+1)
......
......@@ -11,16 +11,16 @@ import (
var UsingOVM bool
var (
// l2GasPriceSlot refers to the storage slot that the L2 gas price is stored
// L2GasPriceSlot refers to the storage slot that the L2 gas price is stored
// in in the OVM_GasPriceOracle predeploy
L2GasPriceSlot = common.BigToHash(big.NewInt(1))
// l1GasPriceSlot refers to the storage slot that the L1 gas price is stored
// L1GasPriceSlot refers to the storage slot that the L1 gas price is stored
// in in the OVM_GasPriceOracle predeploy
L1GasPriceSlot = common.BigToHash(big.NewInt(2))
// l2GasPriceOracleOwnerSlot refers to the storage slot that the owner of
// L2GasPriceOracleOwnerSlot refers to the storage slot that the owner of
// the OVM_GasPriceOracle is stored in
L2GasPriceOracleOwnerSlot = common.BigToHash(big.NewInt(0))
// l2GasPriceOracleAddress is the address of the OVM_GasPriceOracle
// L2GasPriceOracleAddress is the address of the OVM_GasPriceOracle
// predeploy
L2GasPriceOracleAddress = common.HexToAddress("0x420000000000000000000000000000000000000F")
// OverheadSlot refers to the storage slot in the OVM_GasPriceOracle that
......
package rcfg
import (
"math/big"
"os"
"github.com/ethereum-optimism/optimism/l2geth/common"
)
// SystemAddress0 is the first deployable system address.
var SystemAddress0 = common.HexToAddress("0x4200000000000000000000000000000000000042")
// SystemAddress1 is the second deployable system address.
var SystemAddress1 = common.HexToAddress("0x4200000000000000000000000000000000000014")
// ZeroSystemAddress is the emprt system address.
var ZeroSystemAddress common.Address
// SystemAddressDeployer is a tuple containing the deployment
// addresses for SystemAddress0 and SystemAddress1.
type SystemAddressDeployer [2]common.Address
// SystemAddressFor returns the system address for a given deployment
// address. If no system address is configured for this deployer,
// ZeroSystemAddress is returned.
func (s SystemAddressDeployer) SystemAddressFor(addr common.Address) common.Address {
if s[0] == addr {
return SystemAddress0
}
if s[1] == addr {
return SystemAddress1
}
return ZeroSystemAddress
}
// SystemAddressFor is a convenience method that returns an environment-based
// system address if the passed-in chain ID is not hardcoded.
func SystemAddressFor(chainID *big.Int, addr common.Address) common.Address {
sysDeployer, hasHardcodedSysDeployer := SystemAddressDeployers[chainID.Uint64()]
if !hasHardcodedSysDeployer {
sysDeployer = envSystemAddressDeployer
}
return sysDeployer.SystemAddressFor(addr)
}
// SystemAddressDeployers maintains a hardcoded map of chain IDs to
// system addresses.
var SystemAddressDeployers = map[uint64]SystemAddressDeployer{
// Mainnet
10: {
common.HexToAddress("0xcDE47C1a5e2d60b9ff262b0a3b6d486048575Ad9"),
common.HexToAddress("0x53A6eecC2dD4795Fcc68940ddc6B4d53Bd88Bd9E"),
},
// Kovan
69: {
common.HexToAddress("0xd23eb5c2dd7035e6eb4a7e129249d9843123079f"),
common.HexToAddress("0xa81224490b9fa4930a2e920550cd1c9106bb6d9e"),
},
// Goerli
420: {
common.HexToAddress("0xc30276833798867c1dbc5c468bf51ca900b44e4c"),
common.HexToAddress("0x5c679a57e018f5f146838138d3e032ef4913d551"),
},
}
var envSystemAddressDeployer SystemAddressDeployer
func initEnvSystemAddressDeployer() {
deployer0Env := os.Getenv("SYSTEM_ADDRESS_0_DEPLOYER")
deployer1Env := os.Getenv("SYSTEM_ADDRESS_1_DEPLOYER")
if deployer0Env == "" && deployer1Env == "" {
return
}
if !common.IsHexAddress(deployer0Env) {
panic("SYSTEM_ADDRESS_0_DEPLOYER specified but invalid")
}
if !common.IsHexAddress(deployer1Env) {
panic("SYSTEM_ADDRESS_1_DEPLOYER specified but invalid")
}
envSystemAddressDeployer[0] = common.HexToAddress(deployer0Env)
envSystemAddressDeployer[1] = common.HexToAddress(deployer1Env)
}
func init() {
initEnvSystemAddressDeployer()
}
package rcfg
import (
"crypto/rand"
"fmt"
"math/big"
"os"
"testing"
"github.com/ethereum-optimism/optimism/l2geth/common"
)
func TestSystemAddressFor(t *testing.T) {
tests := []struct {
deployer0 common.Address
deployer1 common.Address
chainId int64
}{
{
common.HexToAddress("0xcDE47C1a5e2d60b9ff262b0a3b6d486048575Ad9"),
common.HexToAddress("0x53A6eecC2dD4795Fcc68940ddc6B4d53Bd88Bd9E"),
10,
},
{
common.HexToAddress("0xd23eb5c2dd7035e6eb4a7e129249d9843123079f"),
common.HexToAddress("0xa81224490b9fa4930a2e920550cd1c9106bb6d9e"),
69,
},
{
common.HexToAddress("0xc30276833798867c1dbc5c468bf51ca900b44e4c"),
common.HexToAddress("0x5c679a57e018f5f146838138d3e032ef4913d551"),
420,
},
}
for _, tt := range tests {
chainID := big.NewInt(tt.chainId)
sad0 := SystemAddressFor(chainID, tt.deployer0)
if sad0 != SystemAddress0 {
t.Fatalf("expected %s, got %s", SystemAddress0.String(), sad0.String())
}
sad1 := SystemAddressFor(chainID, tt.deployer1)
if sad1 != SystemAddress1 {
t.Fatalf("expected %s, got %s", SystemAddress1.String(), sad1.String())
}
if SystemAddressFor(chainID, randAddr()) != ZeroSystemAddress {
t.Fatalf("expected zero address, but got a non-zero one instead")
}
}
// test env fallback
addr0 := randAddr()
addr1 := randAddr()
chainID := big.NewInt(999)
if SystemAddressFor(chainID, addr0) != ZeroSystemAddress {
t.Fatalf("expected zero address, but got a non-zero one instead")
}
if SystemAddressFor(chainID, addr1) != ZeroSystemAddress {
t.Fatalf("expected zero address, but got a non-zero one instead")
}
if err := os.Setenv("SYSTEM_ADDRESS_0_DEPLOYER", addr0.String()); err != nil {
t.Fatalf("error setting env for deployer 0: %v", err)
}
if err := os.Setenv("SYSTEM_ADDRESS_1_DEPLOYER", addr1.String()); err != nil {
t.Fatalf("error setting env for deployer 1: %v", err)
}
initEnvSystemAddressDeployer()
sad0 := SystemAddressFor(chainID, addr0)
if sad0 != SystemAddress0 {
t.Fatalf("expected %s, got %s", SystemAddress0.String(), sad0.String())
}
sad1 := SystemAddressFor(chainID, addr1)
if sad1 != SystemAddress1 {
t.Fatalf("expected %s, got %s", SystemAddress1.String(), sad1.String())
}
// reset
if err := os.Setenv("SYSTEM_ADDRESS_0_DEPLOYER", ""); err != nil {
t.Fatalf("error setting env for deployer 0: %v", err)
}
if err := os.Setenv("SYSTEM_ADDRESS_1_DEPLOYER", ""); err != nil {
t.Fatalf("error setting env for deployer 1: %v", err)
}
initEnvSystemAddressDeployer()
}
func TestSystemAddressDeployer(t *testing.T) {
addr0 := randAddr()
addr1 := randAddr()
deployer := SystemAddressDeployer{addr0, addr1}
assertAddress(t, deployer, addr0, SystemAddress0)
assertAddress(t, deployer, addr1, SystemAddress1)
assertAddress(t, deployer, randAddr(), ZeroSystemAddress)
var zeroDeployer SystemAddressDeployer
assertAddress(t, zeroDeployer, randAddr(), ZeroSystemAddress)
}
func TestEnvSystemAddressDeployer(t *testing.T) {
addr0 := randAddr()
addr1 := randAddr()
assertAddress(t, envSystemAddressDeployer, addr0, ZeroSystemAddress)
assertAddress(t, envSystemAddressDeployer, addr1, ZeroSystemAddress)
assertAddress(t, envSystemAddressDeployer, randAddr(), ZeroSystemAddress)
if err := os.Setenv("SYSTEM_ADDRESS_0_DEPLOYER", addr0.String()); err != nil {
t.Fatalf("error setting env for deployer 0: %v", err)
}
if err := os.Setenv("SYSTEM_ADDRESS_1_DEPLOYER", addr1.String()); err != nil {
t.Fatalf("error setting env for deployer 1: %v", err)
}
initEnvSystemAddressDeployer()
assertAddress(t, envSystemAddressDeployer, addr0, SystemAddress0)
assertAddress(t, envSystemAddressDeployer, addr1, SystemAddress1)
assertAddress(t, envSystemAddressDeployer, randAddr(), ZeroSystemAddress)
tests := []struct {
deployer0 string
deployer1 string
msg string
}{
{
"not an address",
addr0.String(),
"SYSTEM_ADDRESS_0_DEPLOYER specified but invalid",
},
{
"not an address",
"not an address",
"SYSTEM_ADDRESS_0_DEPLOYER specified but invalid",
},
{
addr0.String(),
"not an address",
"SYSTEM_ADDRESS_1_DEPLOYER specified but invalid",
},
}
for _, tt := range tests {
if err := os.Setenv("SYSTEM_ADDRESS_0_DEPLOYER", tt.deployer0); err != nil {
t.Fatalf("error setting env for deployer 0: %v", err)
}
if err := os.Setenv("SYSTEM_ADDRESS_1_DEPLOYER", tt.deployer1); err != nil {
t.Fatalf("error setting env for deployer 1: %v", err)
}
assertPanic(t, tt.msg, func() {
initEnvSystemAddressDeployer()
})
}
}
func randAddr() common.Address {
buf := make([]byte, 20)
_, err := rand.Read(buf)
if err != nil {
panic(err)
}
return common.BytesToAddress(buf)
}
func assertAddress(t *testing.T, deployer SystemAddressDeployer, in common.Address, expected common.Address) {
actual := deployer.SystemAddressFor(in)
if actual != expected {
t.Fatalf("bad system address. expected %s, got %s", expected.String(), actual.String())
}
}
func assertPanic(t *testing.T, msg string, cb func()) {
defer func() {
if err := recover(); err != nil {
errMsg := fmt.Sprintf("%v", err)
if errMsg != msg {
t.Fatalf("expected error message %s, got %v", msg, errMsg)
}
}
}()
cb()
}
BATCH_SUBMITTER ?= docker-compose.ts-batch-submitter.yml
DOCKER_COMPOSE_CMD := docker-compose \
-f docker-compose.yml \
-f $(BATCH_SUBMITTER)
-f docker-compose.yml
build:
DOCKER_BUILDKIT=1 \
......
......@@ -26,7 +26,6 @@ The base stack can be started and stopped with a command like this:
```
docker-compose \
-f docker-compose.yml \
-f docker-compose.ts-batch-submitter.yml \
up --build --detach
```
......@@ -40,7 +39,6 @@ To start the stack with monitoring enabled, just add the metric composition file
```
docker-compose \
-f docker-compose.yml \
-f docker-compose.ts-batch-submitter.yml \
-f docker-compose-metrics.yml \
up --build --detach
```
......@@ -50,7 +48,6 @@ Optionally, run a verifier along the rest of the stack. Run a replica with the s
```
docker-compose
-f docker-compose.yml \
-f docker-compose.ts-batch-submitter.yml \
up --scale \
verifier=1 \
--build --detach
......@@ -71,24 +68,6 @@ Fees can be turned off at runtime by setting the environment variable
ROLLUP_ENFORCE_FEES=false docker-compose up
```
## Using the Go Batch Submitter
The existing Typescript batch submitter is in the process of being reimplemented
in Go. During this transition, the user is required to specify which batch
submitter to use with docker-compose.
The commands above all use the Typescript batch submitter, by specifying
`-f docker-compose.ts-batch-submitter.yml`. This can be swapped out for the go
batch submitter by supplying `-f docker-compose.go-batch-submitter.yml` instead.
Additionally, the `make` targets assume the use of the Typescript batch
submitter. This can be overridden by setting the `BATCH_SUBMITTER` environment
variable, e.g. `BATCH_SUBMITTER=docker-compose.go-batch-submitter.yml make up`.
Once the transition is complete, specifying the desired batch submitter will be
obsolete, and the Go batch submitter will be selected by default from the
`docker-compose.yml` file and `Makefile`.
## Cross domain communication
By default, the `message-relayer` service is turned off. This means that
......
......@@ -85,16 +85,17 @@ services:
- l1_chain
- deployer
- l2geth
image: ethereumoptimism/batch-submitter:${DOCKER_TAG:-prerelease-0.5.0-rc-7-ee217ce}
entrypoint: ./batches.sh
image: ethereumoptimism/batch-submitter-service:${DOCKER_TAG:-prerelease-0.5.0-rc-7-ee217ce}
entrypoint: ./batch-submitter.sh
env_file:
- ./envs/batches.env
- ./envs/batch-submitter.env
environment:
L1_NODE_WEB3_URL: http://l1_chain:8545
L2_NODE_WEB3_URL: http://l2geth:8545
URL: http://deployer:8081/addresses.json
SEQUENCER_PRIVATE_KEY: "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
PROPOSER_PRIVATE_KEY: "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a"
L1_ETH_RPC: http://l1_chain:8545
L2_ETH_RPC: http://l2geth:8545
URL: http://deployer:8081/addresses.json
BATCH_SUBMITTER_SEQUENCER_PRIVATE_KEY: '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'
BATCH_SUBMITTER_PROPOSER_PRIVATE_KEY: '0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a'
BATCH_SUBMITTER_SEQUENCER_BATCH_TYPE: ${BATCH_SUBMITTER_SEQUENCER_BATCH_TYPE:-zlib}
verifier:
depends_on:
......
services:
batch_submitter:
depends_on:
- l1_chain
- deployer
- l2geth
build:
context: ..
dockerfile: ./ops/docker/Dockerfile.batch-submitter-service
entrypoint: ./batch-submitter.sh
env_file:
- ./envs/batch-submitter.env
environment:
L1_ETH_RPC: http://l1_chain:8545
L2_ETH_RPC: http://l2geth:8545
URL: http://deployer:8081/addresses.json
BATCH_SUBMITTER_SEQUENCER_PRIVATE_KEY: "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
BATCH_SUBMITTER_PROPOSER_PRIVATE_KEY: "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a"
services:
batch_submitter:
depends_on:
- l1_chain
- deployer
- l2geth
build:
context: ..
dockerfile: ./ops/docker/Dockerfile.packages
target: batch-submitter
entrypoint: ./batches.sh
env_file:
- ./envs/batches.env
environment:
L1_NODE_WEB3_URL: http://l1_chain:8545
L2_NODE_WEB3_URL: http://l2geth:8545
URL: http://deployer:8081/addresses.json
SEQUENCER_PRIVATE_KEY: "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
PROPOSER_PRIVATE_KEY: "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a"
version: "3.4"
version: '3.4'
x-system-addr-env: &system-addr-env
# private key: a6aecc98b63bafb0de3b29ae9964b14acb4086057808be29f90150214ebd4a0f
# OK to publish this since it will only ever be used in itests
SYSTEM_ADDRESS_0_DEPLOYER: "0xa961b0d6dce82db098cf70a42a14add3ee3db2d5"
# private key: 3b8d2345102cce2443acb240db6e87c8edd4bb3f821b17fab8ea2c9da08ea132
# OK to publish this since it will only ever be used in itests
SYSTEM_ADDRESS_1_DEPLOYER: "0xdfc82d475833a50de90c642770f34a9db7deb725"
services:
# this is a helper service used because there's no official hardhat image
......@@ -8,9 +18,9 @@ services:
context: ./docker/hardhat
dockerfile: Dockerfile
env_file:
- ./envs/l1_chain.env
- ./envs/l1_chain.env
ports:
# expose the service to the host for integration testing
# expose the service to the host for integration testing
- ${L1CHAIN_HTTP_PORT:-9545}:8545
deployer:
......@@ -24,11 +34,11 @@ services:
environment:
# Env vars for the deployment script.
CONTRACTS_RPC_URL: http://l1_chain:8545
CONTRACTS_DEPLOYER_KEY: "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
CONTRACTS_TARGET_NETWORK: "custom"
OVM_ADDRESS_MANAGER_OWNER: "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"
OVM_PROPOSER_ADDRESS: "0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc"
OVM_SEQUENCER_ADDRESS: "0x70997970c51812dc3a010c7d01b50e0d17dc79c8"
CONTRACTS_DEPLOYER_KEY: 'ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
CONTRACTS_TARGET_NETWORK: 'custom'
OVM_ADDRESS_MANAGER_OWNER: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266'
OVM_PROPOSER_ADDRESS: '0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc'
OVM_SEQUENCER_ADDRESS: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8'
SCC_FRAUD_PROOF_WINDOW: 0
NUM_DEPLOY_CONFIRMATIONS: 0
# skip compilation when run in docker-compose, since the contracts
......@@ -37,20 +47,20 @@ services:
# Env vars for the dump script.
# Default hardhat account 5
GAS_PRICE_ORACLE_OWNER: "0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc"
GAS_PRICE_ORACLE_OWNER: '0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc'
# setting the whitelist owner to address(0) disables the whitelist
WHITELIST_OWNER: "0x0000000000000000000000000000000000000000"
L1_FEE_WALLET_ADDRESS: "0x391716d440c151c42cdf1c95c1d83a5427bca52c"
L2_CHAIN_ID: 420
WHITELIST_OWNER: '0x0000000000000000000000000000000000000000'
L1_FEE_WALLET_ADDRESS: '0x391716d440c151c42cdf1c95c1d83a5427bca52c'
L2_CHAIN_ID: 987
L2_BLOCK_GAS_LIMIT: 15000000
BLOCK_SIGNER_ADDRESS: "0x00000398232E2064F896018496b4b44b3D62751F"
BLOCK_SIGNER_ADDRESS: '0x00000398232E2064F896018496b4b44b3D62751F'
GAS_PRICE_ORACLE_OVERHEAD: 2750
GAS_PRICE_ORACLE_SCALAR: 1500000
GAS_PRICE_ORACLE_L1_BASE_FEE: 1
GAS_PRICE_ORACLE_GAS_PRICE: 1
GAS_PRICE_ORACLE_DECIMALS: 6
ports:
# expose the service to the host for getting the contract addrs
# expose the service to the host for getting the contract addrs
- ${DEPLOYER_PORT:-8080}:8081
dtl:
......@@ -65,17 +75,17 @@ services:
# override with the dtl script and the env vars required for it
entrypoint: ./dtl.sh
env_file:
- ./envs/dtl.env
- ./envs/dtl.env
# set the rest of the env vars for the network whcih do not
# depend on the docker-compose setup
environment:
# used for setting the address manager address
URL: http://deployer:8081/addresses.json
# connect to the 2 layers
DATA_TRANSPORT_LAYER__L1_RPC_ENDPOINT: http://l1_chain:8545
DATA_TRANSPORT_LAYER__L2_RPC_ENDPOINT: http://l2geth:8545
DATA_TRANSPORT_LAYER__SYNC_FROM_L2: 'true'
DATA_TRANSPORT_LAYER__L2_CHAIN_ID: 420
# used for setting the address manager address
URL: http://deployer:8081/addresses.json
# connect to the 2 layers
DATA_TRANSPORT_LAYER__L1_RPC_ENDPOINT: http://l1_chain:8545
DATA_TRANSPORT_LAYER__L2_RPC_ENDPOINT: http://l2geth:8545
DATA_TRANSPORT_LAYER__SYNC_FROM_L2: 'true'
DATA_TRANSPORT_LAYER__L2_CHAIN_ID: 987
ports:
- ${DTL_PORT:-7878}:7878
......@@ -91,20 +101,21 @@ services:
env_file:
- ./envs/geth.env
environment:
ETH1_HTTP: http://l1_chain:8545
ROLLUP_TIMESTAMP_REFRESH: 5s
ROLLUP_STATE_DUMP_PATH: http://deployer:8081/state-dump.latest.json
# connecting to the DTL
ROLLUP_CLIENT_HTTP: http://dtl:7878
ETH1_CTC_DEPLOYMENT_HEIGHT: 8
RETRIES: 60
# no need to keep this secret, only used internally to sign blocks
BLOCK_SIGNER_KEY: "6587ae678cf4fc9a33000cdbf9f35226b71dcc6a4684a31203241f9bcfd55d27"
BLOCK_SIGNER_ADDRESS: "0x00000398232E2064F896018496b4b44b3D62751F"
ROLLUP_ENFORCE_FEES: ${ROLLUP_ENFORCE_FEES:-true}
ROLLUP_FEE_THRESHOLD_DOWN: 0.9
ROLLUP_FEE_THRESHOLD_UP: 1.1
<<: *system-addr-env
ETH1_HTTP: http://l1_chain:8545
ROLLUP_TIMESTAMP_REFRESH: 5s
ROLLUP_STATE_DUMP_PATH: http://deployer:8081/state-dump.latest.json
# connecting to the DTL
ROLLUP_CLIENT_HTTP: http://dtl:7878
ETH1_CTC_DEPLOYMENT_HEIGHT: 8
RETRIES: 60
# no need to keep this secret, only used internally to sign blocks
BLOCK_SIGNER_KEY: '6587ae678cf4fc9a33000cdbf9f35226b71dcc6a4684a31203241f9bcfd55d27'
BLOCK_SIGNER_ADDRESS: '0x00000398232E2064F896018496b4b44b3D62751F'
ROLLUP_ENFORCE_FEES: ${ROLLUP_ENFORCE_FEES:-true}
ROLLUP_FEE_THRESHOLD_DOWN: 0.9
ROLLUP_FEE_THRESHOLD_UP: 1.1
ports:
- ${L2GETH_HTTP_PORT:-8545}:8545
- ${L2GETH_WS_PORT:-8546}:8546
......@@ -122,14 +133,14 @@ services:
target: relayer
entrypoint: ./relayer.sh
environment:
L1_NODE_WEB3_URL: http://l1_chain:8545
L2_NODE_WEB3_URL: http://l2geth:8545
URL: http://deployer:8081/addresses.json
# a funded hardhat account
L1_WALLET_KEY: "0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97"
RETRIES: 60
POLLING_INTERVAL: 500
GET_LOGS_INTERVAL: 500
L1_NODE_WEB3_URL: http://l1_chain:8545
L2_NODE_WEB3_URL: http://l2geth:8545
URL: http://deployer:8081/addresses.json
# a funded hardhat account
L1_WALLET_KEY: '0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97'
RETRIES: 60
POLLING_INTERVAL: 500
GET_LOGS_INTERVAL: 500
verifier:
depends_on:
......@@ -146,14 +157,15 @@ services:
env_file:
- ./envs/geth.env
environment:
ETH1_HTTP: http://l1_chain:8545
SEQUENCER_CLIENT_HTTP: http://l2geth:8545
ROLLUP_STATE_DUMP_PATH: http://deployer:8081/state-dump.latest.json
ROLLUP_CLIENT_HTTP: http://dtl:7878
ROLLUP_BACKEND: 'l1'
ETH1_CTC_DEPLOYMENT_HEIGHT: 8
RETRIES: 60
ROLLUP_VERIFIER_ENABLE: 'true'
<<: *system-addr-env
ETH1_HTTP: http://l1_chain:8545
SEQUENCER_CLIENT_HTTP: http://l2geth:8545
ROLLUP_STATE_DUMP_PATH: http://deployer:8081/state-dump.latest.json
ROLLUP_CLIENT_HTTP: http://dtl:7878
ROLLUP_BACKEND: 'l1'
ETH1_CTC_DEPLOYMENT_HEIGHT: 8
RETRIES: 60
ROLLUP_VERIFIER_ENABLE: 'true'
ports:
- ${VERIFIER_HTTP_PORT:-8547}:8545
- ${VERIFIER_WS_PORT:-8548}:8546
......@@ -171,21 +183,22 @@ services:
env_file:
- ./envs/geth.env
environment:
ETH1_HTTP: http://l1_chain:8545
SEQUENCER_CLIENT_HTTP: http://l2geth:8545
ROLLUP_STATE_DUMP_PATH: http://deployer:8081/state-dump.latest.json
ROLLUP_CLIENT_HTTP: http://dtl:7878
ROLLUP_BACKEND: 'l2'
ROLLUP_VERIFIER_ENABLE: 'true'
ETH1_CTC_DEPLOYMENT_HEIGHT: 8
RETRIES: 60
<<: *system-addr-env
ETH1_HTTP: http://l1_chain:8545
SEQUENCER_CLIENT_HTTP: http://l2geth:8545
ROLLUP_STATE_DUMP_PATH: http://deployer:8081/state-dump.latest.json
ROLLUP_CLIENT_HTTP: http://dtl:7878
ROLLUP_BACKEND: 'l2'
ROLLUP_VERIFIER_ENABLE: 'true'
ETH1_CTC_DEPLOYMENT_HEIGHT: 8
RETRIES: 60
ports:
- ${REPLICA_HTTP_PORT:-8549}:8545
- ${REPLICA_WS_PORT:-8550}:8546
integration_tests:
deploy:
replicas: 0
replicas: 0
build:
context: ..
dockerfile: ./ops/docker/Dockerfile.packages
......@@ -199,10 +212,17 @@ services:
URL: http://deployer:8081/addresses.json
ENABLE_GAS_REPORT: 1
NO_NETWORK: 1
BATCH_SUBMITTER_SEQUENCER_BATCH_TYPE: ${BATCH_SUBMITTER_SEQUENCER_BATCH_TYPE:-zlib}
RUN_SYSTEM_ADDRESS_TESTS: "true"
# must match l2geth environment, see above for why it's safe to publish these
SYSTEM_ADDRESS_0_DEPLOYER_KEY: "a6aecc98b63bafb0de3b29ae9964b14acb4086057808be29f90150214ebd4a0f"
SYSTEM_ADDRESS_1_DEPLOYER_KEY: "3b8d2345102cce2443acb240db6e87c8edd4bb3f821b17fab8ea2c9da08ea132"
gas_oracle:
deploy:
replicas: 0
replicas: 0
build:
context: ..
dockerfile: ./ops/docker/Dockerfile.gas-oracle
......@@ -210,4 +230,23 @@ services:
environment:
GAS_PRICE_ORACLE_ETHEREUM_HTTP_URL: http://l2geth:8545
# Default hardhat account 5
GAS_PRICE_ORACLE_PRIVATE_KEY: "0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba"
GAS_PRICE_ORACLE_PRIVATE_KEY: '0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba'
batch_submitter:
depends_on:
- l1_chain
- deployer
- l2geth
build:
context: ..
dockerfile: ./ops/docker/Dockerfile.batch-submitter-service
entrypoint: ./batch-submitter.sh
env_file:
- ./envs/batch-submitter.env
environment:
L1_ETH_RPC: http://l1_chain:8545
L2_ETH_RPC: http://l2geth:8545
URL: http://deployer:8081/addresses.json
BATCH_SUBMITTER_SEQUENCER_PRIVATE_KEY: '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'
BATCH_SUBMITTER_PROPOSER_PRIVATE_KEY: '0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a'
BATCH_SUBMITTER_SEQUENCER_BATCH_TYPE: ${BATCH_SUBMITTER_SEQUENCER_BATCH_TYPE:-zlib}
ARG LOCAL_REGISTRY=docker.io
ARG BUILDER_TAG=latest
FROM ${LOCAL_REGISTRY}/ethereumoptimism/builder:${BUILDER_TAG} AS builder
FROM node:16-alpine
RUN apk add --no-cache curl bash jq
WORKDIR /opt/optimism
# copy top level files
COPY --from=builder /optimism/*.json /optimism/yarn.lock ./
COPY --from=builder /optimism/node_modules ./node_modules
# copy deps (would have been nice if docker followed the symlinks required)
COPY --from=builder /optimism/packages/core-utils/package.json ./packages/core-utils/package.json
COPY --from=builder /optimism/packages/core-utils/dist ./packages/core-utils/dist
COPY --from=builder /optimism/packages/common-ts/package.json ./packages/common-ts/package.json
COPY --from=builder /optimism/packages/common-ts/dist ./packages/common-ts/dist
COPY --from=builder /optimism/packages/contracts/package.json ./packages/contracts/package.json
COPY --from=builder /optimism/packages/contracts/deployments ./packages/contracts/deployments
COPY --from=builder /optimism/packages/contracts/dist ./packages/contracts/dist
COPY --from=builder /optimism/packages/contracts/artifacts ./packages/contracts/artifacts
# copy the service
WORKDIR /opt/optimism/packages/batch-submitter
COPY --from=builder /optimism/packages/batch-submitter/package.json ./
COPY --from=builder /optimism/packages/batch-submitter/dist ./dist
COPY --from=builder /optimism/packages/batch-submitter/exec ./exec
COPY --from=builder /optimism/packages/batch-submitter/node_modules ./node_modules
# copy this over in case you want to run alongside other services
COPY ./ops/scripts/batches.sh .
ENTRYPOINT ["npm", "run", "start"]
......@@ -29,7 +29,6 @@ COPY packages/core-utils/package.json ./packages/core-utils/package.json
COPY packages/common-ts/package.json ./packages/common-ts/package.json
COPY packages/contracts/package.json ./packages/contracts/package.json
COPY packages/data-transport-layer/package.json ./packages/data-transport-layer/package.json
COPY packages/batch-submitter/package.json ./packages/batch-submitter/package.json
COPY packages/message-relayer/package.json ./packages/message-relayer/package.json
COPY packages/replica-healthcheck/package.json ./packages/replica-healthcheck/package.json
COPY integration-tests/package.json ./integration-tests/package.json
......
......@@ -18,7 +18,6 @@ COPY packages/core-utils/package.json ./packages/core-utils/package.json
COPY packages/common-ts/package.json ./packages/common-ts/package.json
COPY packages/contracts/package.json ./packages/contracts/package.json
COPY packages/data-transport-layer/package.json ./packages/data-transport-layer/package.json
COPY packages/batch-submitter/package.json ./packages/batch-submitter/package.json
COPY packages/message-relayer/package.json ./packages/message-relayer/package.json
COPY packages/replica-healthcheck/package.json ./packages/replica-healthcheck/package.json
COPY integration-tests/package.json ./integration-tests/package.json
......@@ -38,12 +37,6 @@ COPY ./ops/scripts/deployer.sh .
CMD ["yarn", "run", "deploy"]
FROM base as batch-submitter
WORKDIR /opt/optimism/packages/batch-submitter
COPY ./ops/scripts/batches.sh .
CMD ["npm", "run", "start"]
FROM base as data-transport-layer
WORKDIR /opt/optimism/packages/data-transport-layer
COPY ./ops/scripts/dtl.sh .
......
......@@ -19,6 +19,11 @@ COPY --from=builder /optimism/packages/core-utils/dist ./packages/core-utils/dis
COPY --from=builder /optimism/packages/common-ts/package.json ./packages/common-ts/package.json
COPY --from=builder /optimism/packages/common-ts/dist ./packages/common-ts/dist
COPY --from=builder /optimism/packages/contracts/package.json ./packages/contracts/package.json
COPY --from=builder /optimism/packages/contracts/deployments ./packages/contracts/deployments
COPY --from=builder /optimism/packages/contracts/dist ./packages/contracts/dist
COPY --from=builder /optimism/packages/contracts/artifacts ./packages/contracts/artifacts
# copy the service
WORKDIR /opt/optimism/packages/replica-healthcheck
COPY --from=builder /optimism/packages/replica-healthcheck/dist ./dist
......
ADDRESS_MANAGER_ADDRESS=
DEBUG=info*,error*,warn*,debug*
MAX_L1_TX_SIZE=90000
MIN_L1_TX_SIZE=32
MAX_TX_BATCH_COUNT=50
MAX_STATE_BATCH_COUNT=50
POLL_INTERVAL=500
NUM_CONFIRMATIONS=0
RESUBMISSION_TIMEOUT=1
FINALITY_CONFIRMATIONS=0
RUN_TX_BATCH_SUBMITTER=true
RUN_STATE_BATCH_SUBMITTER=true
MAX_BATCH_SUBMISSION_TIME=0
SAFE_MINIMUM_ETHER_BALANCE=0
CLEAR_PENDING_TXS=false
RETRIES=80
......@@ -20,12 +20,12 @@ WS_PORT=8546
WS_API=eth,net,rollup,web3
WS_ORIGINS=*
CHAIN_ID=420
CHAIN_ID=987
DATADIR=/root/.ethereum
GASPRICE=0
GCMODE=archive
IPC_DISABLE=true
NETWORK_ID=420
NETWORK_ID=987
NO_USB=true
NO_DISCOVER=true
TARGET_GAS_LIMIT=15000000
......
#!/bin/bash
set -e
RETRIES=${RETRIES:-40}
if [[ ! -z "$URL" ]]; then
# get the addrs from the URL provided
ADDRESSES=$(curl --fail --show-error --silent --retry-connrefused --retry $RETRIES --retry-delay 5 $URL)
# set the env
export ADDRESS_MANAGER_ADDRESS=$(echo $ADDRESSES | jq -r '.AddressManager')
fi
# waits for l2geth to be up
curl --fail \
--show-error \
--silent \
--retry-connrefused \
--retry $RETRIES \
--retry-delay 1 \
--output /dev/null \
$L2_NODE_WEB3_URL
# go
exec node ./exec/run-batch-submitter.js
......@@ -43,7 +43,6 @@ docker tag localhost:5000/ethereumoptimism/builder:latest ethereumoptimism/build
build deployer "ethereumoptimism/deployer:latest" "./ops/docker/Dockerfile.deployer" .
build dtl "ethereumoptimism/data-transport-layer:latest" "./ops/docker/Dockerfile.data-transport-layer" .
build batch_submitter "ethereumoptimism/batch-submitter:latest" "./ops/docker/Dockerfile.batch-submitter" .
build relayer "ethereumoptimism/message-relayer:latest" "./ops/docker/Dockerfile.message-relayer" .
build integration-tests "ethereumoptimism/integration-tests:latest" "./ops/docker/Dockerfile.integration-tests" .
......
# Environment
NODE_ENV=development
# Leave blank during local development
ETH_NETWORK_NAME=
# Logging & monitoring
DEBUG=info*,error*,warn*,debug*
RUN_METRICS_SERVER=
METRICS_PORT=
METRICS_HOSTNAME=
# Leave the SENTRY_DSN variable unset during local development
SENTRY_DSN=
SENTRY_TRACE_RATE=
USE_SENTRY=
L1_NODE_WEB3_URL=http://localhost:9545
L2_NODE_WEB3_URL=http://localhost:8545
MAX_L1_TX_SIZE=90000
MIN_L1_TX_SIZE=32
MAX_TX_BATCH_SIZE=50
MAX_STATE_BATCH_COUNT=2000
MAX_TX_BATCH_COUNT=250
MAX_BATCH_SUBMISSION_TIME=0
POLL_INTERVAL=15000
NUM_CONFIRMATIONS=0
RESUBMISSION_TIMEOUT=300 # in seconds
FINALITY_CONFIRMATIONS=0
RUN_TX_BATCH_SUBMITTER=true
RUN_STATE_BATCH_SUBMITTER=true
SAFE_MINIMUM_ETHER_BALANCE=0
CLEAR_PENDING_TXS=false
ADDRESS_MANAGER_ADDRESS=
USE_HARDHAT=
DEBUG_IMPERSONATE_SEQUENCER_ADDRESS=
DEBUG_IMPERSONATE_PROPOSER_ADDRESS=
# Optional gas settings
MAX_GAS_PRICE_IN_GWEI=200
GAS_RETRY_INCREMENT=5
GAS_THRESHOLD_IN_GWEI=100
SEQUENCER_PRIVATE_KEY=0xd2ab07f7c10ac88d5f86f1b4c1035d5195e81f27dbe62ad65e59cbf88205629b
module.exports = {
extends: '../../.eslintrc.js',
}
module.exports = {
...require('../../.prettierrc.js'),
};
\ No newline at end of file
# Changelog
## 0.4.20
### Patch Changes
- d3d70291: Use asL2Provider instead of injectL2Context in bss and healthcheck service.
- Updated dependencies [f37c283c]
- Updated dependencies [3f4d3c13]
- Updated dependencies [0b4453f7]
- Updated dependencies [78298782]
- Updated dependencies [0c54e60e]
- @eth-optimism/sdk@0.2.3
- @eth-optimism/core-utils@0.8.0
- @eth-optimism/contracts@0.5.15
## 0.4.19
### Patch Changes
- Updated dependencies [b4165299]
- Updated dependencies [3c2acd91]
- @eth-optimism/core-utils@0.7.7
- @eth-optimism/contracts@0.5.14
## 0.4.18
### Patch Changes
- Updated dependencies [438bc78a]
- @eth-optimism/contracts@0.5.13
## 0.4.17
### Patch Changes
- fcce5b67: Updates batch submitter to also include separate timestamps for deposit transactions"
- ba14c59d: Updates various ethers dependencies to their latest versions
- Updated dependencies [ba14c59d]
- @eth-optimism/contracts@0.5.12
- @eth-optimism/core-utils@0.7.6
## 0.4.16
### Patch Changes
- Updated dependencies [e631c39c]
- @eth-optimism/contracts@0.5.11
## 0.4.15
### Patch Changes
- ae4a90d9: Adds a fix for the BSS to account for new timestamp logic in L2Geth
- ca547c4e: Import performance to not couple batch submitter to version of nodejs that has performance as a builtin
- Updated dependencies [ad94b9d1]
- @eth-optimism/core-utils@0.7.5
- @eth-optimism/contracts@0.5.10
## 0.4.14
### Patch Changes
- Updated dependencies [ba96a455]
- Updated dependencies [c3e85fef]
- @eth-optimism/core-utils@0.7.4
- @eth-optimism/contracts@0.5.9
## 0.4.13
### Patch Changes
- 9fe09f70: Properly clear state root batch txs on startup
- Updated dependencies [b3efb8b7]
- Updated dependencies [279603e5]
- Updated dependencies [b6040bb3]
- @eth-optimism/contracts@0.5.8
## 0.4.12
### Patch Changes
- 07f1ad01: Fix the numTxPerBatch metric
## 0.4.11
### Patch Changes
- 3eb5590b: adds batchTxBuildTime gauge
- Updated dependencies [b6f89fad]
- @eth-optimism/contracts@0.5.7
## 0.4.10
### Patch Changes
- Updated dependencies [bbd42e03]
- Updated dependencies [453f0774]
- @eth-optimism/contracts@0.5.6
## 0.4.9
### Patch Changes
- 57d5b8f9: Build docker images with node.js version 16
## 0.4.8
### Patch Changes
- 0ab37fc9: Update to node.js version 16
## 0.4.7
### Patch Changes
- Updated dependencies [584cbc25]
- @eth-optimism/core-utils@0.7.3
- @eth-optimism/contracts@0.5.5
## 0.4.6
### Patch Changes
- 39607e7c: Trigger release of the batch submitter with yatm retry fix
## 0.4.5
### Patch Changes
- 85f68bd3: Immediately reject on nonce errors to stop falling behind
## 0.4.4
### Patch Changes
- 8e634b49: Fix package JSON issues
- Updated dependencies [8e634b49]
- @eth-optimism/core-utils@0.7.2
- @eth-optimism/contracts@0.5.4
## 0.4.3
### Patch Changes
- Updated dependencies [b9049406]
- Updated dependencies [a8b14a7d]
- @eth-optimism/contracts@0.5.3
## 0.4.2
### Patch Changes
- 526d7e51: Throw an error when sequencer and proposer have the same address.
- 243f33e5: Standardize package json file format
- Updated dependencies [243f33e5]
- @eth-optimism/common-ts@0.2.1
- @eth-optimism/contracts@0.5.2
- @eth-optimism/core-utils@0.7.1
## 0.4.1
### Patch Changes
- Updated dependencies [c0fc7fee]
- @eth-optimism/contracts@0.5.1
## 0.4.0
### Minor Changes
- 3f590e33: Remove the "OVM" Prefix from contract names
- 81ccd6e4: `regenesis/0.5.0` release
### Patch Changes
- ac63235a: Default tx batch validation to false
- bfeb7fba: Add `VALIDATE_TX_BATCH` config option that can disable batch validation
- 222a3eef: Add 'User-Agent' to the http headers for ethers providers
- 71f8de9c: Handle error case more explicity when creating invalid batch
- 970f421e: Ensure empty batches are not submitted
- b70ee70c: upgraded to solidity 0.8.9
- a98a1884: Fixes dependencies instead of using caret constraints
- Updated dependencies [e4a1129c]
- Updated dependencies [64ea3ac9]
- Updated dependencies [3ce62c81]
- Updated dependencies [299a459e]
- Updated dependencies [8c8807c0]
- Updated dependencies [d7978cfc]
- Updated dependencies [e16d41c0]
- Updated dependencies [5db50b3d]
- Updated dependencies [cee2a464]
- Updated dependencies [66bf56a6]
- Updated dependencies [2c91ca00]
- Updated dependencies [d5036826]
- Updated dependencies [222a3eef]
- Updated dependencies [dfc784e8]
- Updated dependencies [896168e2]
- Updated dependencies [436c48fd]
- Updated dependencies [7c352b1e]
- Updated dependencies [2ade9a79]
- Updated dependencies [0272a536]
- Updated dependencies [6ee7423f]
- Updated dependencies [3f590e33]
- Updated dependencies [e20deca0]
- Updated dependencies [2a731e0d]
- Updated dependencies [199e895e]
- Updated dependencies [872f5976]
- Updated dependencies [9c1443a4]
- Updated dependencies [26906518]
- Updated dependencies [c53b3587]
- Updated dependencies [1b917041]
- Updated dependencies [483f561b]
- Updated dependencies [b70ee70c]
- Updated dependencies [c38e4b57]
- Updated dependencies [a98a1884]
- Updated dependencies [b744b6ea]
- Updated dependencies [20c8969b]
- Updated dependencies [d2eb8ae0]
- Updated dependencies [1e63ffa0]
- Updated dependencies [ff266e9c]
- Updated dependencies [b56dd079]
- Updated dependencies [56fe3793]
- Updated dependencies [3e2aa16a]
- Updated dependencies [d3cb1b86]
- Updated dependencies [3e41df63]
- Updated dependencies [973589da]
- Updated dependencies [83a449c4]
- Updated dependencies [9c63e9bd]
- Updated dependencies [81ccd6e4]
- Updated dependencies [f38b8000]
- Updated dependencies [d5f012ab]
- Updated dependencies [76c84f21]
- Updated dependencies [6d32d701]
- Updated dependencies [3605b963]
- Updated dependencies [3f28385a]
- Updated dependencies [280f348c]
- Updated dependencies [a0947c3f]
- Updated dependencies [51821d8f]
- Updated dependencies [29f1c228]
- Updated dependencies [8f4cb337]
- Updated dependencies [beb6c977]
- Updated dependencies [33abe73d]
- Updated dependencies [71de86d6]
- @eth-optimism/contracts@0.5.0
- @eth-optimism/core-utils@0.7.0
- @eth-optimism/common-ts@0.2.0
## 0.3.15
### Patch Changes
- 6d3e1d7f: Update dependencies
- Updated dependencies [6d3e1d7f]
- Updated dependencies [2e929aa9]
- @eth-optimism/common-ts@0.1.6
- @eth-optimism/contracts@0.4.14
- @eth-optimism/core-utils@0.6.1
## 0.3.14
### Patch Changes
- 39cea8fd: Removes the call to `appendQueueBatch` from the batch submitter
- Updated dependencies [e0be02e1]
- Updated dependencies [7f7f35c3]
- Updated dependencies [8da04505]
- @eth-optimism/core-utils@0.6.0
- @eth-optimism/contracts@0.4.13
## 0.3.13
### Patch Changes
- 7482d09c: Fixes a bug in the batch submitted that would cause it to submit transactions with increasing nonces
## 0.3.12
### Patch Changes
- 21b17edd: Added coverage for packages
- 78ca518b: Add loglines for eip1559 related fields before sending a transaction
- Updated dependencies [888dafca]
- Updated dependencies [eb0854e7]
- Updated dependencies [21b17edd]
- Updated dependencies [dfe3598f]
- @eth-optimism/contracts@0.4.11
- @eth-optimism/core-utils@0.5.5
## 0.3.11
### Patch Changes
- 918c08ca: Bump ethers dependency to 5.4.x to support eip1559
- Updated dependencies [918c08ca]
- @eth-optimism/contracts@0.4.10
- @eth-optimism/core-utils@0.5.2
## 0.3.10
### Patch Changes
- b5b9fd89: Migrate to using `ethers.StaticJsonRpcProvider`
- Updated dependencies [ecc2f8c1]
- @eth-optimism/contracts@0.4.9
## 0.3.9
### Patch Changes
- 3b132974: Fix tx resubmission estimateGas bug in batch submitter
- Updated dependencies [7f26667d]
- Updated dependencies [77511b68]
- @eth-optimism/contracts@0.4.7
## 0.3.8
### Patch Changes
- c73c3939: Update the typescript version to `4.3.5`
- Updated dependencies [c73c3939]
- @eth-optimism/common-ts@0.1.5
- @eth-optimism/contracts@0.4.5
- @eth-optimism/core-utils@0.5.1
## 0.3.7
### Patch Changes
- 8a1e63dd: Prevent batch submitter from submitting batches if low on ETH
- Updated dependencies [0313794b]
- Updated dependencies [049200f4]
- Updated dependencies [21e47e1f]
- @eth-optimism/contracts@0.4.2
- @eth-optimism/core-utils@0.5.0
## 0.3.6
### Patch Changes
- f87a2d00: Use dashes instead of colons in contract names
- 52d02b14: Add failure metrics to batch submitter
- 31f517a2: Improved logging of batch submission timeout logs
- 5c89c45f: Move the metric prefix string to a label #1047
- Updated dependencies [25f09abd]
- Updated dependencies [dd8edc7b]
- Updated dependencies [c87e4c74]
- Updated dependencies [db0dbfb2]
- Updated dependencies [7f5936a8]
- Updated dependencies [f87a2d00]
- Updated dependencies [85da4979]
- Updated dependencies [57ca21a2]
- Updated dependencies [5fc728da]
- Updated dependencies [2e72fd90]
- Updated dependencies [c43b33ec]
- Updated dependencies [26bc63ad]
- Updated dependencies [a0d9e565]
- Updated dependencies [2bd49730]
- Updated dependencies [38355a3b]
- Updated dependencies [3c2c32e1]
- Updated dependencies [d9644c34]
- Updated dependencies [48ece14c]
- Updated dependencies [e04de624]
- Updated dependencies [014dea71]
- Updated dependencies [fa29b03e]
- Updated dependencies [6b46c8ba]
- Updated dependencies [e045f582]
- Updated dependencies [5c89c45f]
- Updated dependencies [df5ff890]
- Updated dependencies [e29fab10]
- Updated dependencies [c2a04893]
- Updated dependencies [baacda34]
- @eth-optimism/contracts@0.4.0
- @eth-optimism/core-utils@0.4.6
- @eth-optimism/common-ts@0.1.4
## 0.3.5
### Patch Changes
- 7cce55a9: Add status to generic error log to disambiguate errors
## 0.3.4
### Patch Changes
- baa3b761: Improve Sentry support, initializing as needed and ensuring ERROR logs route to Sentry
- cc742715: Fix typo in USE_HARDHAT config
- 98b7839f: Change monotonicity band-aid code to log warnings not errors
- c520100d: Fix a bug in fixMonotonicity auto healer
- 85362d44: Log additional data in monotonicity violation
- Updated dependencies [baa3b761]
- @eth-optimism/common-ts@0.1.3
## 0.3.3
### Patch Changes
- 750a5021: Remove dead imports from core-utils
- Updated dependencies [a64f8161]
- Updated dependencies [4e03f8a9]
- Updated dependencies [8e2bfd07]
- Updated dependencies [750a5021]
- Updated dependencies [c2b6e14b]
- Updated dependencies [245136f1]
- @eth-optimism/core-utils@0.4.5
- @eth-optimism/contracts@0.3.5
## 0.3.2
### Patch Changes
- 4340bb1: Fix: correctly read Batch Submitter env var defaults
## 0.3.1
### Patch Changes
- c79dc8b: Add impersonate account debug config.
- 0c16805: add metrics server to common-ts and batch submitter
- fa4898a: Explicitly log error messages so that they do not show as empty objects
- 96a586e: Updates the configuration to use bcfg in a backwards compatible way
- c79dc8b: Make BLOCK_OFFSET configurable.
- Updated dependencies [96a586e]
- Updated dependencies [0c16805]
- Updated dependencies [775118a]
- @eth-optimism/core-utils@0.4.3
- @eth-optimism/common-ts@0.1.2
- @eth-optimism/contracts@0.3.1
## 0.3.0
### Minor Changes
- b799caa: Updates to use RLP encoded transactions in batches for the `v0.3.0` release
### Patch Changes
- 751e2be: Add the support for different sequencer & proposer keys in the batch submitter.
- Updated dependencies [b799caa]
- Updated dependencies [6132e7a]
- Updated dependencies [b799caa]
- Updated dependencies [b799caa]
- Updated dependencies [b799caa]
- Updated dependencies [20747fd]
- Updated dependencies [b799caa]
- Updated dependencies [b799caa]
- @eth-optimism/contracts@0.3.0
- @eth-optimism/core-utils@0.4.2
## 0.2.5
### Patch Changes
- 1d40586: Removed various unused dependencies
- Updated dependencies [1d40586]
- Updated dependencies [ce7fa52]
- Updated dependencies [575bcf6]
- Updated dependencies [6dc1877]
- @eth-optimism/common-ts@0.1.1
- @eth-optimism/contracts@0.2.10
- @eth-optimism/core-utils@0.4.1
## 0.2.4
### Patch Changes
- 12dbd81: add key metrics to batch submitter
- 28dc442: move metrics, logger, and base-service to new common-ts package
- 79df44e: Add skipped deposit auto heal
- Updated dependencies [28dc442]
- Updated dependencies [d2091d4]
- Updated dependencies [a0a0052]
- Updated dependencies [0ef3069]
- @eth-optimism/common-ts@0.1.0
- @eth-optimism/core-utils@0.4.0
- @eth-optimism/contracts@0.2.9
## 0.2.3
### Patch Changes
- 6daa408: update hardhat versions so that solc is resolved correctly
- dee74ef: migrate batch submitter types to core-utils
- d64b66d: reformat error context for Sentry
- Updated dependencies [6daa408]
- Updated dependencies [ea4041b]
- Updated dependencies [f1f5bf2]
- Updated dependencies [dee74ef]
- Updated dependencies [9ec3ec0]
- Updated dependencies [d64b66d]
- Updated dependencies [5f376ee]
- Updated dependencies [eef1df4]
- Updated dependencies [a76cde5]
- Updated dependencies [e713cd0]
- Updated dependencies [572dcbc]
- Updated dependencies [6014ec0]
- @eth-optimism/contracts@0.2.8
- @eth-optimism/core-utils@0.3.2
## 0.2.2
### Patch Changes
- 6d31324: Update release tag for Sentry compatability
- a2f6e83: add default metrics to all batch submitters
## 0.2.1
### Patch Changes
- ab285e4: properly start the batch submitter instead of instantly exiting
## 0.2.0
### Minor Changes
- 5077441: - Use raw transaction in batch submitter -- incompatible with L2Geth v0.1.2.1
- Pass through raw transaction in l2context
### Patch Changes
- a3dc553: Adds a release version to batch-submitter and data-transport-layer usage of Sentry
- b95dc22: log errors for monotonicity violations
- c7bc0ce: Correctly formatted error object to log exceptions
- Updated dependencies [ce5d596]
- Updated dependencies [1a55f64]
- Updated dependencies [6e8fe1b]
- Updated dependencies [8d4aae4]
- Updated dependencies [c75a0fc]
- Updated dependencies [d4ee2d7]
- Updated dependencies [edb4346]
- Updated dependencies [5077441]
- @eth-optimism/contracts@0.2.6
- @eth-optimism/core-utils@0.3.1
## 0.1.12
### Patch Changes
- a0a7956: initialize Sentry and streams in Logger, remove Sentry from Batch Submitter
- Updated dependencies [91460d9]
- Updated dependencies [a0a7956]
- Updated dependencies [0497d7d]
- @eth-optimism/core-utils@0.3.0
- @eth-optimism/contracts@0.2.5
## 0.1.11
### Patch Changes
- 35b99b0: add Sentry to TypeScript services for error tracking
- Updated dependencies [35b99b0]
- @eth-optimism/core-utils@0.2.3
## 0.1.10
### Patch Changes
- 962e31b: removed unused l1 block number logic, added debug logging to batch submitter
## 0.1.9
### Patch Changes
- 3b00b7c: bump private package versions to try triggering a tag
## 0.1.8
### Patch Changes
- 6cbc54d: allow injecting L2 transaction and block context via core-utils (this removes the need to import the now deprecated @eth-optimism/provider package)
- Updated dependencies [6cbc54d]
- @eth-optimism/core-utils@0.2.0
- @eth-optimism/contracts@0.2.2
## v0.1.3
- Add tx resubmission logic
- Log when the batch submitter runs low on ETH
## v0.1.2
Adds mnemonic config parsing
## v0.1.1
Final fixes before minnet release.
- Add batch submission timeout
- Log sequencer address
- remove ssh
## v0.1.0
The inital release
(The MIT License)
Copyright 2020-2021 Optimism
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
[![codecov](https://codecov.io/gh/ethereum-optimism/optimism/branch/master/graph/badge.svg?token=0VTG7PG7YR&flag=batch-submitter)](https://codecov.io/gh/ethereum-optimism/optimism)
# Batch Submitter
Contains an executable batch submitter service which watches L1 and a local L2 node and submits batches to the
`CanonicalTransactionChain` & `StateCommitmentChain` based on its local information.
## Configuration
All configuration is done via environment variables. See all variables at [.env.example](.env.example); copy into a `.env` file before running.
## Building & Running
1. Make sure dependencies are installed just run `yarn` in the base directory
2. Build `yarn build`
3. Run `yarn start`
## Controlling log output verbosity
Before running, set the `DEBUG` environment variable to specify the verbosity level. It must be made up of comma-separated values of patterns to match in debug logs. Here's a few common options:
* `debug*` - Will match all debug statements -- very verbose
* `info*` - Will match all info statements -- less verbose, useful in most cases
* `warn*` - Will match all warnings -- recommended at a minimum
* `error*` - Will match all errors -- would not omit this
Examples:
* Everything but debug: `export DEBUG=info*,error*,warn*`
* Most verbose: `export DEBUG=info*,error*,warn*,debug*`
## Testing & linting
### Local
- Run unit tests with `yarn test`
- See lint errors with `yarn lint`; auto-fix with `yarn lint --fix`
### Submission
You may test a submission locally against a local Hardhat fork.
1. Follow the instructions [here](https://github.com/ethereum-optimism/hardhat) to run a Hardhat node.
2. Change the Batch Submitter `.env` field `L1_NODE_WEB3_URL` to the local Hardhat url. Depending on which network you are using, update `ADDRESS_MANAGER_ADDRESS` according to the [Regenesis repo](https://github.com/ethereum-optimism/regenesis).
3. Also check `L2_NODE_WEB3_URL` is correctly set and has transactions to submit.
3. Run `yarn build` to build your changes.
4. Start Batch Submitter with `yarn start`. It will automatically start submitting pending transactions from L2.
## Observability in production
When deploying Batch Submitter to production / a live ETH network, populate the environment variables `NODE_ENV` (`development`, `production`, or `test`) and `ETH_NETWORK_NAME` (`mainnet`, `kovan`, `goerli`). This enables Batch Submitter to capture more context in logs and metrics, and initializes [Sentry](https://docs.sentry.io/platforms/node/) to track errors.
#!/usr/bin/env node
const batchSubmitter = require('../dist/src/exec/run-batch-submitter')
batchSubmitter.run()
import '@nomiclabs/hardhat-waffle'
import { HardhatUserConfig } from 'hardhat/config'
import {
DEFAULT_ACCOUNTS_HARDHAT,
RUN_OVM_TEST_GAS,
} from './test/helpers/constants'
import '@nomiclabs/hardhat-ethers'
const config: HardhatUserConfig = {
networks: {
hardhat: {
accounts: DEFAULT_ACCOUNTS_HARDHAT,
blockGasLimit: RUN_OVM_TEST_GAS * 2,
},
},
mocha: {
timeout: 50000,
},
solidity: {
version: '0.7.0',
settings: {
optimizer: {
enabled: true,
runs: 200,
},
metadata: {
bytecodeHash: 'none',
},
outputSelection: {
'*': {
'*': ['metadata', 'storageLayout'],
},
},
},
},
}
export default config
const rootPath = __dirname
export { rootPath }
{
"private": true,
"name": "@eth-optimism/batch-submitter",
"version": "0.4.20",
"description": "[Optimism] Service for submitting transactions and transaction results",
"main": "dist/index",
"types": "dist/index",
"files": [
"dist/*"
],
"scripts": {
"start": "node ./exec/run-batch-submitter.js",
"build": "tsc -p ./tsconfig.build.json",
"clean": "rimraf cache/ dist/ ./tsconfig.build.tsbuildinfo",
"lint": "yarn lint:fix && yarn lint:check",
"pre-commit": "lint-staged",
"lint:fix": "yarn lint:check --fix",
"lint:check": "eslint . --max-warnings=0",
"test": "hardhat test --show-stack-traces",
"test:coverage": "nyc hardhat test && nyc merge .nyc_output coverage.json"
},
"keywords": [
"optimism",
"ethereum",
"sequencer",
"aggregator"
],
"homepage": "https://github.com/ethereum-optimism/optimism/tree/develop/packages/batch-submitter#readme",
"license": "MIT",
"author": "Optimism PBC",
"repository": {
"type": "git",
"url": "https://github.com/ethereum-optimism/optimism.git"
},
"dependencies": {
"@eth-optimism/common-ts": "0.2.1",
"@eth-optimism/contracts": "0.5.15",
"@eth-optimism/core-utils": "0.8.0",
"@eth-optimism/sdk": "^0.2.3",
"@eth-optimism/ynatm": "^0.2.2",
"@ethersproject/abstract-provider": "^5.5.1",
"@ethersproject/providers": "^5.5.3",
"@sentry/node": "^6.3.1",
"bcfg": "^0.1.6",
"bluebird": "^3.7.2",
"dotenv": "^10.0.0",
"ethers": "^5.5.4",
"old-contracts": "npm:@eth-optimism/contracts@^0.0.2-alpha.7",
"prom-client": "^13.1.0"
},
"devDependencies": {
"@eth-optimism/smock": "1.1.10",
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@types/bluebird": "^3.5.34",
"@types/chai": "^4.2.18",
"@types/mocha": "^8.2.2",
"@types/node": "^15.12.2",
"@types/sinon": "^9.0.10",
"@types/sinon-chai": "^3.2.5",
"@typescript-eslint/eslint-plugin": "^4.26.0",
"@typescript-eslint/parser": "^4.26.0",
"babel-eslint": "^10.1.0",
"chai": "^4.3.4",
"eslint": "^7.27.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsdoc": "^35.1.2",
"eslint-plugin-prefer-arrow": "^1.2.3",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-unicorn": "^32.0.1",
"ethereum-waffle": "^3.3.0",
"hardhat": "^2.3.0",
"lint-staged": "11.0.0",
"mocha": "^8.4.0",
"prettier": "^2.3.1",
"rimraf": "^3.0.2",
"sinon": "^9.2.4",
"sinon-chai": "^3.5.0",
"typescript": "^4.3.5"
},
"resolutions": {
"ganache-core": "^2.13.2",
"**/@sentry/node": "^6.2.5"
},
"publishConfig": {
"access": "public"
}
}
/* External Imports */
import {
Contract,
Signer,
utils,
providers,
PopulatedTransaction,
} from 'ethers'
import {
TransactionReceipt,
TransactionResponse,
} from '@ethersproject/abstract-provider'
import { Gauge, Histogram, Counter } from 'prom-client'
import { RollupInfo, sleep } from '@eth-optimism/core-utils'
import { Logger, Metrics } from '@eth-optimism/common-ts'
import { getContractFactory } from 'old-contracts'
/* Internal Imports */
import { TxSubmissionHooks } from '..'
export interface BlockRange {
start: number
end: number
}
interface BatchSubmitterMetrics {
batchSubmitterETHBalance: Gauge<string>
batchSizeInBytes: Histogram<string>
numTxPerBatch: Histogram<string>
submissionTimestamp: Histogram<string>
submissionGasUsed: Histogram<string>
batchesSubmitted: Counter<string>
failedSubmissions: Counter<string>
malformedBatches: Counter<string>
batchTxBuildTime: Gauge<string>
}
export abstract class BatchSubmitter {
protected rollupInfo: RollupInfo
protected chainContract: Contract
protected l2ChainId: number
protected syncing: boolean
protected lastBatchSubmissionTimestamp: number = 0
protected metrics: BatchSubmitterMetrics
constructor(
readonly signer: Signer,
readonly l2Provider: providers.StaticJsonRpcProvider,
readonly minTxSize: number,
readonly maxTxSize: number,
readonly maxBatchSize: number,
readonly maxBatchSubmissionTime: number,
readonly numConfirmations: number,
readonly resubmissionTimeout: number,
readonly finalityConfirmations: number,
readonly addressManagerAddress: string,
readonly minBalanceEther: number,
readonly blockOffset: number,
readonly logger: Logger,
readonly defaultMetrics: Metrics
) {
this.metrics = this._registerMetrics(defaultMetrics)
}
public abstract _submitBatch(
startBlock: number,
endBlock: number
): Promise<TransactionReceipt>
public abstract _onSync(): Promise<TransactionReceipt>
public abstract _getBatchStartAndEnd(): Promise<BlockRange>
public abstract _updateChainInfo(): Promise<void>
public async submitNextBatch(): Promise<TransactionReceipt> {
if (typeof this.l2ChainId === 'undefined') {
this.l2ChainId = await this._getL2ChainId()
}
await this._updateChainInfo()
if (!(await this._hasEnoughETHToCoverGasCosts())) {
await sleep(this.resubmissionTimeout)
return
}
this.logger.info('Readying to submit next batch...', {
l2ChainId: this.l2ChainId,
batchSubmitterAddress: await this.signer.getAddress(),
})
if (this.syncing === true) {
this.logger.info(
'Syncing mode enabled! Skipping batch submission and clearing queue...'
)
return this._onSync()
}
const range = await this._getBatchStartAndEnd()
if (!range) {
return
}
return this._submitBatch(range.start, range.end)
}
protected async _hasEnoughETHToCoverGasCosts(): Promise<boolean> {
const address = await this.signer.getAddress()
const balance = await this.signer.getBalance()
const ether = utils.formatEther(balance)
const num = parseFloat(ether)
this.logger.info('Checked balance', {
address,
ether,
})
this.metrics.batchSubmitterETHBalance.set(num)
if (num < this.minBalanceEther) {
this.logger.fatal('Current balance lower than min safe balance', {
current: num,
safeBalance: this.minBalanceEther,
})
return false
}
return true
}
protected async _getRollupInfo(): Promise<RollupInfo> {
return this.l2Provider.send('rollup_getInfo', [])
}
protected async _getL2ChainId(): Promise<number> {
return this.l2Provider.send('eth_chainId', [])
}
protected async _getChainAddresses(): Promise<{
ctcAddress: string
sccAddress: string
}> {
const addressManager = (
await getContractFactory('Lib_AddressManager', this.signer)
).attach(this.addressManagerAddress)
const sccAddress = await addressManager.getAddress('StateCommitmentChain')
const ctcAddress = await addressManager.getAddress(
'CanonicalTransactionChain'
)
return {
ctcAddress,
sccAddress,
}
}
protected _shouldSubmitBatch(batchSizeInBytes: number): boolean {
const currentTimestamp = Date.now()
if (batchSizeInBytes < this.minTxSize) {
const timeSinceLastSubmission =
currentTimestamp - this.lastBatchSubmissionTimestamp
if (timeSinceLastSubmission < this.maxBatchSubmissionTime) {
this.logger.info(
'Skipping batch submission. Batch too small & max submission timeout not reached.',
{
batchSizeInBytes,
timeSinceLastSubmission,
maxBatchSubmissionTime: this.maxBatchSubmissionTime,
minTxSize: this.minTxSize,
lastBatchSubmissionTimestamp: this.lastBatchSubmissionTimestamp,
currentTimestamp,
}
)
return false
}
this.logger.info('Timeout reached, proceeding with batch submission.', {
batchSizeInBytes,
timeSinceLastSubmission,
maxBatchSubmissionTime: this.maxBatchSubmissionTime,
lastBatchSubmissionTimestamp: this.lastBatchSubmissionTimestamp,
currentTimestamp,
})
this.metrics.batchSizeInBytes.observe(batchSizeInBytes)
return true
}
this.logger.info(
'Sufficient batch size, proceeding with batch submission.',
{
batchSizeInBytes,
lastBatchSubmissionTimestamp: this.lastBatchSubmissionTimestamp,
currentTimestamp,
}
)
this.metrics.batchSizeInBytes.observe(batchSizeInBytes)
return true
}
protected _makeHooks(txName: string): TxSubmissionHooks {
return {
beforeSendTransaction: (tx: PopulatedTransaction) => {
this.logger.info(`Submitting ${txName} transaction`, {
gasPrice: tx.gasPrice,
maxFeePerGas: tx.maxFeePerGas,
maxPriorityFeePerGas: tx.maxPriorityFeePerGas,
gasLimit: tx.gasLimit,
nonce: tx.nonce,
contractAddr: this.chainContract.address,
})
},
onTransactionResponse: (txResponse: TransactionResponse) => {
this.logger.info(`Submitted ${txName} transaction`, {
txHash: txResponse.hash,
from: txResponse.from,
})
this.logger.debug(`${txName} transaction data`, {
data: txResponse.data,
})
},
}
}
protected async _submitAndLogTx(
submitTransaction: () => Promise<TransactionReceipt>,
successMessage: string
): Promise<TransactionReceipt> {
this.lastBatchSubmissionTimestamp = Date.now()
this.logger.debug('Submitting transaction & waiting for receipt...')
let receipt: TransactionReceipt
try {
receipt = await submitTransaction()
} catch (err) {
this.metrics.failedSubmissions.inc()
if (err.reason) {
this.logger.error(`Transaction invalid: ${err.reason}, aborting`, {
message: err.toString(),
stack: err.stack,
code: err.code,
})
return
}
this.logger.error('Encountered error at submission, aborting', {
message: err.toString(),
stack: err.stack,
code: err.code,
})
return
}
this.logger.info('Received transaction receipt', { receipt })
this.logger.info(successMessage)
this.metrics.batchesSubmitted.inc()
this.metrics.submissionGasUsed.observe(receipt.gasUsed.toNumber())
this.metrics.submissionTimestamp.observe(Date.now())
return receipt
}
private _registerMetrics(metrics: Metrics): BatchSubmitterMetrics {
metrics.registry.clear()
return {
batchSubmitterETHBalance: new metrics.client.Gauge({
name: 'batch_submitter_eth_balance',
help: 'ETH balance of the batch submitter',
registers: [metrics.registry],
}),
batchSizeInBytes: new metrics.client.Histogram({
name: 'batch_size_in_bytes',
help: 'Size of batches in bytes',
registers: [metrics.registry],
}),
numTxPerBatch: new metrics.client.Histogram({
name: 'num_txs_per_batch',
help: 'Number of transactions in each batch',
registers: [metrics.registry],
}),
submissionTimestamp: new metrics.client.Histogram({
name: 'submission_timestamp',
help: 'Timestamp of each batch submitter submission',
registers: [metrics.registry],
}),
submissionGasUsed: new metrics.client.Histogram({
name: 'submission_gash_used',
help: 'Gas used to submit each batch',
registers: [metrics.registry],
}),
batchesSubmitted: new metrics.client.Counter({
name: 'batches_submitted',
help: 'Count of batches submitted',
registers: [metrics.registry],
}),
failedSubmissions: new metrics.client.Counter({
name: 'failed_submissions',
help: 'Count of failed batch submissions',
registers: [metrics.registry],
}),
malformedBatches: new metrics.client.Counter({
name: 'malformed_batches',
help: 'Count of malformed batches',
registers: [metrics.registry],
}),
batchTxBuildTime: new metrics.client.Gauge({
name: 'batch_tx_build_time',
help: 'Time to construct batch transaction',
registers: [metrics.registry],
}),
}
}
}
export * from './batch-submitter'
export * from './tx-batch-submitter'
export * from './state-batch-submitter'
export const TX_BATCH_SUBMITTER_LOG_TAG = 'oe:batch_submitter:tx_chain'
export const STATE_BATCH_SUBMITTER_LOG_TAG = 'oe:batch_submitter:state_chain'
/* External Imports */
import { performance } from 'perf_hooks'
import { Promise as bPromise } from 'bluebird'
import { Contract, Signer, providers } from 'ethers'
import { TransactionReceipt } from '@ethersproject/abstract-provider'
import { getContractFactory } from 'old-contracts'
import {
L2Block,
RollupInfo,
Bytes32,
remove0x,
} from '@eth-optimism/core-utils'
import { Logger, Metrics } from '@eth-optimism/common-ts'
/* Internal Imports */
import { TransactionSubmitter } from '../utils'
import { BlockRange, BatchSubmitter } from '.'
export class StateBatchSubmitter extends BatchSubmitter {
// TODO: Change this so that we calculate start = scc.totalElements() and end = ctc.totalElements()!
// Not based on the length of the L2 chain -- that is only used in the batch submitter
// Note this means we've got to change the state / end calc logic
protected l2ChainId: number
protected syncing: boolean
protected ctcContract: Contract
private fraudSubmissionAddress: string
private transactionSubmitter: TransactionSubmitter
constructor(
signer: Signer,
l2Provider: providers.StaticJsonRpcProvider,
minTxSize: number,
maxTxSize: number,
maxBatchSize: number,
maxBatchSubmissionTime: number,
numConfirmations: number,
resubmissionTimeout: number,
finalityConfirmations: number,
addressManagerAddress: string,
minBalanceEther: number,
transactionSubmitter: TransactionSubmitter,
blockOffset: number,
logger: Logger,
metrics: Metrics,
fraudSubmissionAddress: string
) {
super(
signer,
l2Provider,
minTxSize,
maxTxSize,
maxBatchSize,
maxBatchSubmissionTime,
numConfirmations,
resubmissionTimeout,
finalityConfirmations,
addressManagerAddress,
minBalanceEther,
blockOffset,
logger,
metrics
)
this.fraudSubmissionAddress = fraudSubmissionAddress
this.transactionSubmitter = transactionSubmitter
}
/*****************************
* Batch Submitter Overrides *
****************************/
public async _updateChainInfo(): Promise<void> {
const info: RollupInfo = await this._getRollupInfo()
if (info.mode === 'verifier') {
this.logger.error(
'Verifier mode enabled! Batch submitter only compatible with sequencer mode'
)
process.exit(1)
}
this.syncing = info.syncing
const addrs = await this._getChainAddresses()
const sccAddress = addrs.sccAddress
const ctcAddress = addrs.ctcAddress
if (
typeof this.chainContract !== 'undefined' &&
sccAddress === this.chainContract.address &&
ctcAddress === this.ctcContract.address
) {
this.logger.debug('Chain contract already initialized', {
sccAddress,
ctcAddress,
})
return
}
this.chainContract = (
await getContractFactory('OVM_StateCommitmentChain', this.signer)
).attach(sccAddress)
this.ctcContract = (
await getContractFactory('OVM_CanonicalTransactionChain', this.signer)
).attach(ctcAddress)
this.logger.info('Connected Optimism contracts', {
stateCommitmentChain: this.chainContract.address,
canonicalTransactionChain: this.ctcContract.address,
})
return
}
public async _onSync(): Promise<TransactionReceipt> {
this.logger.info('Syncing mode enabled! Skipping state batch submission...')
return
}
public async _getBatchStartAndEnd(): Promise<BlockRange> {
this.logger.info('Getting batch start and end for state batch submitter...')
const startBlock: number =
(await this.chainContract.getTotalElements()).toNumber() +
this.blockOffset
this.logger.info('Retrieved start block number from SCC', {
startBlock,
})
// We will submit state roots for txs which have been in the tx chain for a while.
const totalElements: number =
(await this.ctcContract.getTotalElements()).toNumber() + this.blockOffset
this.logger.info('Retrieved total elements from CTC', {
totalElements,
})
const endBlock: number = Math.min(
startBlock + this.maxBatchSize,
totalElements
)
if (startBlock >= endBlock) {
if (startBlock > endBlock) {
this.logger.error(
'State commitment chain is larger than transaction chain. This should never happen!'
)
}
this.logger.info(
'No state commitments to submit. Skipping batch submission...'
)
return
}
return {
start: startBlock,
end: endBlock,
}
}
public async _submitBatch(
startBlock: number,
endBlock: number
): Promise<TransactionReceipt> {
const batchTxBuildStart = performance.now()
const batch = await this._generateStateCommitmentBatch(startBlock, endBlock)
const calldata = this.chainContract.interface.encodeFunctionData(
'appendStateBatch',
[batch, startBlock]
)
const batchSizeInBytes = remove0x(calldata).length / 2
this.logger.debug('State batch generated', {
batchSizeInBytes,
calldata,
})
if (!this._shouldSubmitBatch(batchSizeInBytes)) {
return
}
const batchTxBuildEnd = performance.now()
this.metrics.batchTxBuildTime.set(batchTxBuildEnd - batchTxBuildStart)
const offsetStartsAtIndex = startBlock - this.blockOffset
this.logger.debug('Submitting batch.', { calldata })
// Generate the transaction we will repeatedly submit
const nonce = await this.signer.getTransactionCount()
const tx = await this.chainContract.populateTransaction.appendStateBatch(
batch,
offsetStartsAtIndex,
{ nonce }
)
const submitTransaction = (): Promise<TransactionReceipt> => {
return this.transactionSubmitter.submitTransaction(
tx,
this._makeHooks('appendStateBatch')
)
}
return this._submitAndLogTx(
submitTransaction,
'Submitted state root batch!'
)
}
/*********************
* Private Functions *
********************/
private async _generateStateCommitmentBatch(
startBlock: number,
endBlock: number
): Promise<Bytes32[]> {
const blockRange = endBlock - startBlock
const batch: Bytes32[] = await bPromise.map(
[...Array(blockRange).keys()],
async (i: number) => {
this.logger.debug('Fetching L2BatchElement', {
blockNo: startBlock + i,
})
const block = (await this.l2Provider.getBlockWithTransactions(
startBlock + i
)) as L2Block
const blockTx = block.transactions[0]
if (blockTx.from === this.fraudSubmissionAddress) {
this.logger.warn('Found transaction from fraud submission address', {
txHash: blockTx.hash,
fraudSubmissionAddress: this.fraudSubmissionAddress,
})
this.fraudSubmissionAddress = 'no fraud'
return '0xbad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1bad1'
}
return block.stateRoot
},
{ concurrency: 100 }
)
let tx = this.chainContract.interface.encodeFunctionData(
'appendStateBatch',
[batch, startBlock]
)
while (remove0x(tx).length / 2 > this.maxTxSize) {
batch.splice(Math.ceil((batch.length * 2) / 3)) // Delete 1/3rd of all of the batch elements
this.logger.debug('Splicing batch...', {
batchSizeInBytes: tx.length / 2,
})
tx = this.chainContract.interface.encodeFunctionData('appendStateBatch', [
batch,
startBlock,
])
}
this.logger.info('Generated state commitment batch', {
batch, // list of stateRoots
})
return batch
}
}
/* External Imports */
import { performance } from 'perf_hooks'
import { Promise as bPromise } from 'bluebird'
import { Signer, ethers, Contract, providers } from 'ethers'
import { TransactionReceipt } from '@ethersproject/abstract-provider'
import { getContractInterface, getContractFactory } from 'old-contracts'
import { getContractInterface as getNewContractInterface } from '@eth-optimism/contracts'
import {
L2Block,
RollupInfo,
BatchElement,
Batch,
QueueOrigin,
} from '@eth-optimism/core-utils'
import { Logger, Metrics } from '@eth-optimism/common-ts'
/* Internal Imports */
import {
CanonicalTransactionChainContract,
encodeAppendSequencerBatch,
BatchContext,
AppendSequencerBatchParams,
} from '../transaction-chain-contract'
import { TransactionSubmitter } from '../utils'
import { BlockRange, BatchSubmitter } from '.'
export interface AutoFixBatchOptions {
fixDoublePlayedDeposits: boolean
fixMonotonicity: boolean
fixSkippedDeposits: boolean
}
export class TransactionBatchSubmitter extends BatchSubmitter {
protected chainContract: CanonicalTransactionChainContract
protected l2ChainId: number
protected syncing: boolean
private autoFixBatchOptions: AutoFixBatchOptions
private validateBatch: boolean
private transactionSubmitter: TransactionSubmitter
private gasThresholdInGwei: number
constructor(
signer: Signer,
l2Provider: providers.StaticJsonRpcProvider,
minTxSize: number,
maxTxSize: number,
maxBatchSize: number,
maxBatchSubmissionTime: number,
numConfirmations: number,
resubmissionTimeout: number,
addressManagerAddress: string,
minBalanceEther: number,
gasThresholdInGwei: number,
transactionSubmitter: TransactionSubmitter,
blockOffset: number,
validateBatch: boolean,
logger: Logger,
metrics: Metrics,
autoFixBatchOptions: AutoFixBatchOptions = {
fixDoublePlayedDeposits: false,
fixMonotonicity: false,
fixSkippedDeposits: false,
} // TODO: Remove this
) {
super(
signer,
l2Provider,
minTxSize,
maxTxSize,
maxBatchSize,
maxBatchSubmissionTime,
numConfirmations,
resubmissionTimeout,
0, // Supply dummy value because it is not used.
addressManagerAddress,
minBalanceEther,
blockOffset,
logger,
metrics
)
this.validateBatch = validateBatch
this.autoFixBatchOptions = autoFixBatchOptions
this.gasThresholdInGwei = gasThresholdInGwei
this.transactionSubmitter = transactionSubmitter
this.logger.info('Batch validation options', {
autoFixBatchOptions,
validateBatch,
})
}
/*****************************
* Batch Submitter Overrides *
****************************/
public async _updateChainInfo(): Promise<void> {
const info: RollupInfo = await this._getRollupInfo()
if (info.mode === 'verifier') {
this.logger.error(
'Verifier mode enabled! Batch submitter only compatible with sequencer mode'
)
process.exit(1)
}
this.syncing = info.syncing
const addrs = await this._getChainAddresses()
const ctcAddress = addrs.ctcAddress
if (
typeof this.chainContract !== 'undefined' &&
ctcAddress === this.chainContract.address
) {
this.logger.debug('Chain contract already initialized', {
ctcAddress,
})
return
}
const unwrapped_OVM_CanonicalTransactionChain = (
await getContractFactory('OVM_CanonicalTransactionChain', this.signer)
).attach(ctcAddress)
this.chainContract = new CanonicalTransactionChainContract(
unwrapped_OVM_CanonicalTransactionChain.address,
getContractInterface('OVM_CanonicalTransactionChain'),
this.signer
)
this.logger.info('Initialized new CTC', {
address: this.chainContract.address,
})
return
}
public async _onSync(): Promise<TransactionReceipt> {
const pendingQueueElements =
await this.chainContract.getNumPendingQueueElements()
this.logger.debug('Got number of pending queue elements', {
pendingQueueElements,
})
if (pendingQueueElements !== 0) {
this.logger.info(
'Syncing mode enabled! Skipping batch submission and clearing queue elements',
{ pendingQueueElements }
)
}
this.logger.info('Syncing mode enabled but queue is empty. Skipping...')
return
}
public async _getBatchStartAndEnd(): Promise<BlockRange> {
this.logger.info(
'Getting batch start and end for transaction batch submitter...'
)
const startBlock =
(await this.chainContract.getTotalElements()).toNumber() +
this.blockOffset
this.logger.info('Retrieved start block number from CTC', {
startBlock,
})
const endBlock =
Math.min(
startBlock + this.maxBatchSize,
await this.l2Provider.getBlockNumber()
) + 1 // +1 because the `endBlock` is *exclusive*
this.logger.info('Retrieved end block number from L2 sequencer', {
endBlock,
})
if (startBlock >= endBlock) {
if (startBlock > endBlock) {
this.logger
.error(`More chain elements in L1 (${startBlock}) than in the L2 node (${endBlock}).
This shouldn't happen because we don't submit batches if the sequencer is syncing.`)
}
this.logger.info('No txs to submit. Skipping batch submission...')
return
}
return {
start: startBlock,
end: endBlock,
}
}
public async _submitBatch(
startBlock: number,
endBlock: number
): Promise<TransactionReceipt> {
// Do not submit batch if gas price above threshold
const gasPriceInGwei = parseInt(
ethers.utils.formatUnits(await this.signer.getGasPrice(), 'gwei'),
10
)
if (gasPriceInGwei > this.gasThresholdInGwei) {
this.logger.warn(
'Gas price is higher than gas price threshold; aborting batch submission',
{
gasPriceInGwei,
gasThresholdInGwei: this.gasThresholdInGwei,
}
)
return
}
const batchTxBuildStart = performance.now()
const params = await this._generateSequencerBatchParams(
startBlock,
endBlock
)
if (!params) {
throw new Error(
`Cannot create sequencer batch with params start ${startBlock} and end ${endBlock}`
)
}
const [batchParams, wasBatchTruncated] = params
const batchSizeInBytes = encodeAppendSequencerBatch(batchParams).length / 2
this.logger.debug('Sequencer batch generated', {
batchSizeInBytes,
})
// Only submit batch if one of the following is true:
// 1. it was truncated
// 2. it is large enough
// 3. enough time has passed since last submission
if (!wasBatchTruncated && !this._shouldSubmitBatch(batchSizeInBytes)) {
return
}
const batchTxBuildEnd = performance.now()
this.metrics.batchTxBuildTime.set(batchTxBuildEnd - batchTxBuildStart)
this.metrics.numTxPerBatch.observe(batchParams.totalElementsToAppend)
const l1tipHeight = await this.signer.provider.getBlockNumber()
this.logger.debug('Submitting batch.', {
calldata: batchParams,
l1tipHeight,
})
return this.submitAppendSequencerBatch(batchParams)
}
/*********************
* Private Functions *
********************/
private async submitAppendSequencerBatch(
batchParams: AppendSequencerBatchParams
): Promise<TransactionReceipt> {
const tx =
await this.chainContract.customPopulateTransaction.appendSequencerBatch(
batchParams
)
const submitTransaction = (): Promise<TransactionReceipt> => {
return this.transactionSubmitter.submitTransaction(
tx,
this._makeHooks('appendSequencerBatch')
)
}
return this._submitAndLogTx(submitTransaction, 'Submitted batch!')
}
private async _generateSequencerBatchParams(
startBlock: number,
endBlock: number
): Promise<[AppendSequencerBatchParams, boolean]> {
// Get all L2 BatchElements for the given range
const blockRange = endBlock - startBlock
let batch: Batch = await bPromise.map(
[...Array(blockRange).keys()],
(i) => {
this.logger.debug('Fetching L2BatchElement', {
blockNo: startBlock + i,
})
return this._getL2BatchElement(startBlock + i)
},
{ concurrency: 100 }
)
// Fix our batches if we are configured to. This will not
// modify the batch unless an autoFixBatchOption is set
batch = await this._fixBatch(batch)
if (this.validateBatch) {
this.logger.info('Validating batch')
if (!(await this._validateBatch(batch))) {
this.metrics.malformedBatches.inc()
return
}
}
let sequencerBatchParams = await this._getSequencerBatchParams(
startBlock,
batch
)
let wasBatchTruncated = false
let encoded = encodeAppendSequencerBatch(sequencerBatchParams)
while (encoded.length / 2 > this.maxTxSize) {
this.logger.debug('Splicing batch...', {
batchSizeInBytes: encoded.length / 2,
})
batch.splice(Math.ceil((batch.length * 2) / 3)) // Delete 1/3rd of all of the batch elements
sequencerBatchParams = await this._getSequencerBatchParams(
startBlock,
batch
)
encoded = encodeAppendSequencerBatch(sequencerBatchParams)
// This is to prevent against the case where a batch is oversized,
// but then gets truncated to the point where it is under the minimum size.
// In this case, we want to submit regardless of the batch's size.
wasBatchTruncated = true
}
this.logger.info('Generated sequencer batch params', {
contexts: sequencerBatchParams.contexts,
transactions: sequencerBatchParams.transactions,
wasBatchTruncated,
})
return [sequencerBatchParams, wasBatchTruncated]
}
/**
* Returns true if the batch is valid.
*/
protected async _validateBatch(batch: Batch): Promise<boolean> {
// Verify all of the queue elements are what we expect
let nextQueueIndex = await this.chainContract.getNextQueueIndex()
for (const ele of batch) {
this.logger.debug('Verifying batch element', { ele })
if (!ele.isSequencerTx) {
this.logger.debug('Checking queue equality against L1 queue index', {
nextQueueIndex,
})
if (!(await this._doesQueueElementMatchL1(nextQueueIndex, ele))) {
return false
}
nextQueueIndex++
}
}
// Verify all of the batch elements are monotonic
let lastTimestamp: number
let lastBlockNumber: number
for (const [idx, ele] of batch.entries()) {
if (ele.timestamp < lastTimestamp) {
this.logger.error('Timestamp monotonicity violated! Element', {
idx,
ele,
})
return false
}
if (ele.blockNumber < lastBlockNumber) {
this.logger.error('Block Number monotonicity violated! Element', {
idx,
ele,
})
return false
}
lastTimestamp = ele.timestamp
lastBlockNumber = ele.blockNumber
}
return true
}
private async _doesQueueElementMatchL1(
queueIndex: number,
queueElement: BatchElement
): Promise<boolean> {
const logEqualityError = (name, index, expected, got) => {
this.logger.error('Observed mismatched values', {
index,
expected,
got,
})
}
let isEqual = true
const [, timestamp, blockNumber] = await this.chainContract.getQueueElement(
queueIndex
)
// TODO: Verify queue element hash equality. The queue element hash can be computed with:
// keccak256( abi.encode( msg.sender, _target, _gasLimit, _data))
// Check timestamp & blockNumber equality
if (timestamp !== queueElement.timestamp) {
isEqual = false
logEqualityError(
'Timestamp',
queueIndex,
timestamp,
queueElement.timestamp
)
}
if (blockNumber !== queueElement.blockNumber) {
isEqual = false
logEqualityError(
'Block Number',
queueIndex,
blockNumber,
queueElement.blockNumber
)
}
return isEqual
}
/**
* Takes in a batch which is potentially malformed & returns corrected version.
* Current fixes that are supported:
* - Double played deposits.
*/
private async _fixBatch(batch: Batch): Promise<Batch> {
const fixDoublePlayedDeposits = async (b: Batch): Promise<Batch> => {
let nextQueueIndex = await this.chainContract.getNextQueueIndex()
const fixedBatch: Batch = []
for (const ele of b) {
if (!ele.isSequencerTx) {
if (!(await this._doesQueueElementMatchL1(nextQueueIndex, ele))) {
this.logger.warn('Fixing double played queue element.', {
nextQueueIndex,
})
fixedBatch.push(
await this._fixDoublePlayedDepositQueueElement(
nextQueueIndex,
ele
)
)
continue
}
nextQueueIndex++
}
fixedBatch.push(ele)
}
return fixedBatch
}
const fixSkippedDeposits = async (b: Batch): Promise<Batch> => {
this.logger.debug('Fixing skipped deposits...')
let nextQueueIndex = await this.chainContract.getNextQueueIndex()
const fixedBatch: Batch = []
for (const ele of b) {
// Look for skipped deposits
while (true) {
const pendingQueueElements =
await this.chainContract.getNumPendingQueueElements()
const nextRemoteQueueElements =
await this.chainContract.getNextQueueIndex()
const totalQueueElements =
pendingQueueElements + nextRemoteQueueElements
// No more queue elements so we clearly haven't skipped anything
if (nextQueueIndex >= totalQueueElements) {
break
}
const [, timestamp, blockNumber] =
await this.chainContract.getQueueElement(nextQueueIndex)
if (timestamp < ele.timestamp || blockNumber < ele.blockNumber) {
this.logger.warn('Fixing skipped deposit', {
badTimestamp: ele.timestamp,
skippedQueueTimestamp: timestamp,
badBlockNumber: ele.blockNumber,
skippedQueueBlockNumber: blockNumber,
})
// Push a dummy queue element
fixedBatch.push({
stateRoot: ele.stateRoot,
isSequencerTx: false,
rawTransaction: undefined,
timestamp,
blockNumber,
})
nextQueueIndex++
} else {
// The next queue element's timestamp is after this batch element so
// we must not have skipped anything.
break
}
}
fixedBatch.push(ele)
if (!ele.isSequencerTx) {
nextQueueIndex++
}
}
return fixedBatch
}
// TODO: Remove this super complex logic and rely on Geth to actually supply correct block data.
const fixMonotonicity = async (b: Batch): Promise<Batch> => {
this.logger.debug('Fixing monotonicity...')
// The earliest allowed timestamp/blockNumber is the last timestamp submitted on chain.
const { lastTimestamp, lastBlockNumber } =
await this._getLastTimestampAndBlockNumber()
let earliestTimestamp = lastTimestamp
let earliestBlockNumber = lastBlockNumber
this.logger.debug('Determined earliest timestamp and blockNumber', {
earliestTimestamp,
earliestBlockNumber,
})
// The latest allowed timestamp/blockNumber is the next queue element!
let nextQueueIndex = await this.chainContract.getNextQueueIndex()
let latestTimestamp: number
let latestBlockNumber: number
// updateLatestTimestampAndBlockNumber is a helper which updates
// the latest timestamp and block number based on the pending queue elements.
const updateLatestTimestampAndBlockNumber = async () => {
const pendingQueueElements =
await this.chainContract.getNumPendingQueueElements()
const nextRemoteQueueElements =
await this.chainContract.getNextQueueIndex()
const totalQueueElements =
pendingQueueElements + nextRemoteQueueElements
if (nextQueueIndex < totalQueueElements) {
const [, queueTimestamp, queueBlockNumber] =
await this.chainContract.getQueueElement(nextQueueIndex)
latestTimestamp = queueTimestamp
latestBlockNumber = queueBlockNumber
} else {
// If there are no queue elements left then just allow any timestamp/blocknumber
latestTimestamp = Number.MAX_SAFE_INTEGER
latestBlockNumber = Number.MAX_SAFE_INTEGER
}
}
// Actually update the latest timestamp and block number
await updateLatestTimestampAndBlockNumber()
this.logger.debug('Determined latest timestamp and blockNumber', {
latestTimestamp,
latestBlockNumber,
})
// Now go through our batch and fix the timestamps and block numbers
// to automatically enforce monotonicity.
const fixedBatch: Batch = []
for (const ele of b) {
if (!ele.isSequencerTx) {
// Set the earliest allowed timestamp to the old latest and set the new latest
// to the next queue element's timestamp / blockNumber
earliestTimestamp = latestTimestamp
earliestBlockNumber = latestBlockNumber
nextQueueIndex++
await updateLatestTimestampAndBlockNumber()
}
// Fix the element if its timestammp/blockNumber is too small
if (
ele.timestamp < earliestTimestamp ||
ele.blockNumber < earliestBlockNumber
) {
this.logger.warn('Fixing timestamp/blockNumber too small', {
oldTimestamp: ele.timestamp,
newTimestamp: earliestTimestamp,
oldBlockNumber: ele.blockNumber,
newBlockNumber: earliestBlockNumber,
})
ele.timestamp = earliestTimestamp
ele.blockNumber = earliestBlockNumber
}
// Fix the element if its timestammp/blockNumber is too large
if (
ele.timestamp > latestTimestamp ||
ele.blockNumber > latestBlockNumber
) {
this.logger.warn('Fixing timestamp/blockNumber too large.', {
oldTimestamp: ele.timestamp,
newTimestamp: latestTimestamp,
oldBlockNumber: ele.blockNumber,
newBlockNumber: latestBlockNumber,
})
ele.timestamp = latestTimestamp
ele.blockNumber = latestBlockNumber
}
earliestTimestamp = ele.timestamp
earliestBlockNumber = ele.blockNumber
fixedBatch.push(ele)
}
return fixedBatch
}
// NOTE: It is unsafe to combine multiple autoFix options.
// If you must combine them, manually verify the output before proceeding.
if (this.autoFixBatchOptions.fixDoublePlayedDeposits) {
this.logger.info('Fixing double played deposits')
batch = await fixDoublePlayedDeposits(batch)
}
if (this.autoFixBatchOptions.fixMonotonicity) {
this.logger.info('Fixing monotonicity')
batch = await fixMonotonicity(batch)
}
if (this.autoFixBatchOptions.fixSkippedDeposits) {
this.logger.info('Fixing skipped deposits')
batch = await fixSkippedDeposits(batch)
}
return batch
}
private async _getLastTimestampAndBlockNumber(): Promise<{
lastTimestamp: number
lastBlockNumber: number
}> {
const manager = new Contract(
this.addressManagerAddress,
getNewContractInterface('Lib_AddressManager'),
this.signer.provider
)
const addr = await manager.getAddress('ChainStorageContainer-CTC-batches')
const container = new Contract(
addr,
getNewContractInterface('IChainStorageContainer'),
this.signer.provider
)
let meta = await container.getGlobalMetadata()
// remove 0x
meta = meta.slice(2)
// convert to bytes27
meta = meta.slice(10)
const lastTimestamp = parseInt(meta.slice(-30, -20), 16)
const lastBlockNumber = parseInt(meta.slice(-40, -30), 16)
this.logger.debug('Retrieved timestamp and block number from CTC', {
lastTimestamp,
lastBlockNumber,
})
return { lastTimestamp, lastBlockNumber }
}
private async _fixDoublePlayedDepositQueueElement(
queueIndex: number,
queueElement: BatchElement
): Promise<BatchElement> {
const [, timestamp, blockNumber] = await this.chainContract.getQueueElement(
queueIndex
)
if (
timestamp > queueElement.timestamp &&
blockNumber > queueElement.blockNumber
) {
this.logger.warn(
'Double deposit detected. Fixing by skipping the deposit & replacing with a dummy tx.',
{
timestamp,
blockNumber,
queueElementTimestamp: queueElement.timestamp,
queueElementBlockNumber: queueElement.blockNumber,
}
)
const dummyTx: string = '0x1234'
return {
stateRoot: queueElement.stateRoot,
isSequencerTx: true,
rawTransaction: dummyTx,
timestamp: queueElement.timestamp,
blockNumber: queueElement.blockNumber,
}
}
if (
timestamp < queueElement.timestamp &&
blockNumber < queueElement.blockNumber
) {
this.logger.error('A deposit seems to have been skipped!')
throw new Error('Skipped deposit?!')
}
throw new Error('Unable to fix queue element!')
}
private async _getSequencerBatchParams(
shouldStartAtIndex: number,
blocks: Batch
): Promise<AppendSequencerBatchParams> {
const totalElementsToAppend = blocks.length
// Generate contexts
const contexts: BatchContext[] = []
let lastBlockIsSequencerTx = false
let lastTimestamp = 0
let lastBlockNumber = 0
const groupedBlocks: Array<{
sequenced: BatchElement[]
queued: BatchElement[]
}> = []
for (const block of blocks) {
// Create a new context in certain situations
if (
// If there are no contexts yet, create a new context.
groupedBlocks.length === 0 ||
// If the last block was an L1 to L2 transaction, but the next block is a Sequencer
// transaction, create a new context.
(lastBlockIsSequencerTx === false && block.isSequencerTx === true) ||
// If the timestamp of the last block differs from the timestamp of the current block,
// create a new context. Applies to both L1 to L2 transactions and Sequencer transactions.
block.timestamp !== lastTimestamp ||
// If the block number of the last block differs from the block number of the current block,
// create a new context. ONLY applies to Sequencer transactions.
(block.blockNumber !== lastBlockNumber && block.isSequencerTx === true)
) {
groupedBlocks.push({
sequenced: [],
queued: [],
})
}
const cur = groupedBlocks.length - 1
block.isSequencerTx
? groupedBlocks[cur].sequenced.push(block)
: groupedBlocks[cur].queued.push(block)
lastBlockIsSequencerTx = block.isSequencerTx
lastTimestamp = block.timestamp
lastBlockNumber = block.blockNumber
}
for (const groupedBlock of groupedBlocks) {
if (
groupedBlock.sequenced.length === 0 &&
groupedBlock.queued.length === 0
) {
throw new Error(
'Attempted to generate batch context with 0 queued and 0 sequenced txs!'
)
}
contexts.push({
numSequencedTransactions: groupedBlock.sequenced.length,
numSubsequentQueueTransactions: groupedBlock.queued.length,
timestamp:
groupedBlock.sequenced.length > 0
? groupedBlock.sequenced[0].timestamp
: groupedBlock.queued[0].timestamp,
blockNumber:
groupedBlock.sequenced.length > 0
? groupedBlock.sequenced[0].blockNumber
: groupedBlock.queued[0].blockNumber,
})
}
// Generate sequencer transactions
const transactions: string[] = []
for (const block of blocks) {
if (!block.isSequencerTx) {
continue
}
transactions.push(block.rawTransaction)
}
return {
shouldStartAtElement: shouldStartAtIndex - this.blockOffset,
totalElementsToAppend,
contexts,
transactions,
}
}
private async _getL2BatchElement(blockNumber: number): Promise<BatchElement> {
const block = await this._getBlock(blockNumber)
this.logger.debug('Fetched L2 block', {
block,
})
const batchElement = {
stateRoot: block.stateRoot,
timestamp: block.timestamp,
blockNumber: block.transactions[0].l1BlockNumber,
isSequencerTx: false,
rawTransaction: undefined,
}
if (this._isSequencerTx(block)) {
batchElement.isSequencerTx = true
batchElement.rawTransaction = block.transactions[0].rawTransaction
}
return batchElement
}
private async _getBlock(blockNumber: number): Promise<L2Block> {
const p = this.l2Provider.getBlockWithTransactions(blockNumber)
return p as Promise<L2Block>
}
private _isSequencerTx(block: L2Block): boolean {
return block.transactions[0].queueOrigin === QueueOrigin.Sequencer
}
}
/* External Imports */
import { exit } from 'process'
import { Bcfg } from '@eth-optimism/core-utils'
import { asL2Provider } from '@eth-optimism/sdk'
import * as Sentry from '@sentry/node'
import { Logger, Metrics, createMetricsServer } from '@eth-optimism/common-ts'
import { Signer, Wallet } from 'ethers'
import {
StaticJsonRpcProvider,
TransactionReceipt,
} from '@ethersproject/providers'
import * as dotenv from 'dotenv'
import Config from 'bcfg'
/* Internal Imports */
import {
TransactionBatchSubmitter,
AutoFixBatchOptions,
StateBatchSubmitter,
STATE_BATCH_SUBMITTER_LOG_TAG,
TX_BATCH_SUBMITTER_LOG_TAG,
} from '..'
import {
TransactionSubmitter,
YnatmTransactionSubmitter,
ResubmissionConfig,
} from '../utils'
interface RequiredEnvVars {
// The HTTP provider URL for L1.
L1_NODE_WEB3_URL: string
// The HTTP provider URL for L2.
L2_NODE_WEB3_URL: string
// The layer one address manager address
ADDRESS_MANAGER_ADDRESS: string
// The minimum size in bytes of any L1 transactions generated by the batch submitter.
MIN_L1_TX_SIZE: number
// The maximum size in bytes of any L1 transactions generated by the batch submitter.
MAX_L1_TX_SIZE: number
// The maximum number of L2 transactions that can ever be in a batch.
MAX_TX_BATCH_COUNT: number
// The maximum number of L2 state roots that can ever be in a batch.
MAX_STATE_BATCH_COUNT: number
// The maximum amount of time (seconds) that we will wait before submitting an under-sized batch.
MAX_BATCH_SUBMISSION_TIME: number
// The delay in milliseconds between querying L2 for more transactions / to create a new batch.
POLL_INTERVAL: number
// The number of confirmations which we will wait after appending new batches.
NUM_CONFIRMATIONS: number
// The number of seconds to wait before resubmitting a transaction.
RESUBMISSION_TIMEOUT: number
// The number of confirmations that we should wait before submitting state roots for CTC elements.
FINALITY_CONFIRMATIONS: number
// Whether or not to run the tx batch submitter.
RUN_TX_BATCH_SUBMITTER: boolean
// Whether or not to run the state batch submitter.
RUN_STATE_BATCH_SUBMITTER: boolean
// The safe minimum amount of ether the batch submitter key should
// hold before it starts to log errors.
SAFE_MINIMUM_ETHER_BALANCE: number
// A boolean to clear the pending transactions in the mempool
// on start up.
CLEAR_PENDING_TXS: boolean
}
/* Optional Env Vars
* FRAUD_SUBMISSION_ADDRESS
* SEQUENCER_PRIVATE_KEY
* PROPOSER_PRIVATE_KEY
* MNEMONIC
* SEQUENCER_MNEMONIC
* PROPOSER_MNEMONIC
* SEQUENCER_HD_PATH
* PROPOSER_HD_PATH
* BLOCK_OFFSET
* VALIDATE_TX_BATCH
* USE_HARDHAT
* DEBUG_IMPERSONATE_SEQUENCER_ADDRESS
* DEBUG_IMPERSONATE_PROPOSER_ADDRESS
* RUN_METRICS_SERVER
*/
export const run = async () => {
dotenv.config()
const config: Bcfg = new Config('batch-submitter')
config.load({
env: true,
argv: true,
})
// Parse config
const env = process.env
const environment = config.str('node-env', env.NODE_ENV)
const network = config.str('eth-network-name', env.ETH_NETWORK_NAME)
const service = `batch-submitter`
const release = `batch-submitter@${env.npm_package_version}`
const sentryDsn = config.str('sentry-dsn', env.SENTRY_DSN)
const sentryTraceRate = config.ufloat(
'sentry-trace-rate',
parseFloat(env.SENTRY_TRACE_RATE) || 0.05
)
// Default is 1 because Geth normally has 1 more block than L1
const BLOCK_OFFSET = config.uint(
'block-offset',
parseInt(env.BLOCK_OFFSET, 10) || 1
)
/* Logger */
const name = 'oe:batch_submitter:init'
let logger
if (config.bool('use-sentry', env.USE_SENTRY === 'true')) {
const sentryOptions = {
release,
dsn: sentryDsn,
tracesSampleRate: sentryTraceRate,
environment: network,
}
Sentry.init(sentryOptions)
// Initialize Sentry for Batch Submitter deployed to a network
logger = new Logger({
name,
sentryOptions,
})
} else {
// Skip initializing Sentry
logger = new Logger({ name })
}
const useHardhat = config.bool('use-hardhat', !!env.USE_HARDHAT)
const DEBUG_IMPERSONATE_SEQUENCER_ADDRESS = config.str(
'debug-impersonate-sequencer-address',
env.DEBUG_IMPERSONATE_SEQUENCER_ADDRESS
)
const DEBUG_IMPERSONATE_PROPOSER_ADDRESS = config.str(
'debug-impersonate-proposer-address',
env.DEBUG_IMPERSONATE_PROPOSER_ADDRESS
)
const getSequencerSigner = async (): Promise<Signer> => {
const l1Provider = new StaticJsonRpcProvider({
url: requiredEnvVars.L1_NODE_WEB3_URL,
headers: { 'User-Agent': 'batch-submitter' },
})
if (useHardhat) {
if (!DEBUG_IMPERSONATE_SEQUENCER_ADDRESS) {
throw new Error('Must pass DEBUG_IMPERSONATE_SEQUENCER_ADDRESS')
}
await l1Provider.send('hardhat_impersonateAccount', [
DEBUG_IMPERSONATE_SEQUENCER_ADDRESS,
])
return l1Provider.getSigner(DEBUG_IMPERSONATE_SEQUENCER_ADDRESS)
}
if (SEQUENCER_PRIVATE_KEY) {
return new Wallet(SEQUENCER_PRIVATE_KEY, l1Provider)
} else if (SEQUENCER_MNEMONIC) {
return Wallet.fromMnemonic(SEQUENCER_MNEMONIC, SEQUENCER_HD_PATH).connect(
l1Provider
)
}
throw new Error(
'Must pass one of SEQUENCER_PRIVATE_KEY, MNEMONIC, or SEQUENCER_MNEMONIC'
)
}
const getProposerSigner = async (): Promise<Signer> => {
const l1Provider = new StaticJsonRpcProvider({
url: requiredEnvVars.L1_NODE_WEB3_URL,
headers: { 'User-Agent': 'batch-submitter' },
})
if (useHardhat) {
if (!DEBUG_IMPERSONATE_PROPOSER_ADDRESS) {
throw new Error('Must pass DEBUG_IMPERSONATE_PROPOSER_ADDRESS')
}
await l1Provider.send('hardhat_impersonateAccount', [
DEBUG_IMPERSONATE_PROPOSER_ADDRESS,
])
return l1Provider.getSigner(DEBUG_IMPERSONATE_PROPOSER_ADDRESS)
}
if (PROPOSER_PRIVATE_KEY) {
return new Wallet(PROPOSER_PRIVATE_KEY, l1Provider)
} else if (PROPOSER_MNEMONIC) {
return Wallet.fromMnemonic(PROPOSER_MNEMONIC, PROPOSER_HD_PATH).connect(
l1Provider
)
}
throw new Error(
'Must pass one of PROPOSER_PRIVATE_KEY, MNEMONIC, or PROPOSER_MNEMONIC'
)
}
/* Metrics */
const metrics = new Metrics({
labels: { environment, release, network, service },
})
const FRAUD_SUBMISSION_ADDRESS = config.str(
'fraud-submisison-address',
env.FRAUD_SUBMISSION_ADDRESS || 'no fraud'
)
const MIN_GAS_PRICE_IN_GWEI = config.uint(
'min-gas-price-in-gwei',
parseInt(env.MIN_GAS_PRICE_IN_GWEI, 10) || 0
)
const GAS_RETRY_INCREMENT = config.uint(
'gas-retry-increment',
parseInt(env.GAS_RETRY_INCREMENT, 10) || 5
)
const GAS_THRESHOLD_IN_GWEI = config.uint(
'gas-threshold-in-gwei',
parseInt(env.GAS_THRESHOLD_IN_GWEI, 10) || 100
)
// Private keys & mnemonics
const SEQUENCER_PRIVATE_KEY = config.str(
'sequencer-private-key',
env.SEQUENCER_PRIVATE_KEY
)
// Kept for backwards compatibility
const PROPOSER_PRIVATE_KEY = config.str(
'proposer-private-key',
env.PROPOSER_PRIVATE_KEY || env.SEQUENCER_PRIVATE_KEY
)
const SEQUENCER_MNEMONIC = config.str(
'sequencer-mnemonic',
env.SEQUENCER_MNEMONIC || env.MNEMONIC
)
const PROPOSER_MNEMONIC = config.str(
'proposer-mnemonic',
env.PROPOSER_MNEMONIC || env.MNEMONIC
)
const SEQUENCER_HD_PATH = config.str(
'sequencer-hd-path',
env.SEQUENCER_HD_PATH || env.HD_PATH
)
const PROPOSER_HD_PATH = config.str(
'proposer-hd-path',
env.PROPOSER_HD_PATH || env.HD_PATH
)
const VALIDATE_TX_BATCH = config.bool(
'validate-tx-batch',
env.VALIDATE_TX_BATCH ? env.VALIDATE_TX_BATCH === 'true' : false
)
// Auto fix batch options -- TODO: Remove this very hacky config
const AUTO_FIX_BATCH_OPTIONS_CONF = config.str(
'auto-fix-batch-conf',
env.AUTO_FIX_BATCH_OPTIONS_CONF || ''
)
const autoFixBatchOptions: AutoFixBatchOptions = {
fixDoublePlayedDeposits: AUTO_FIX_BATCH_OPTIONS_CONF
? AUTO_FIX_BATCH_OPTIONS_CONF.includes('fixDoublePlayedDeposits')
: false,
fixMonotonicity: AUTO_FIX_BATCH_OPTIONS_CONF
? AUTO_FIX_BATCH_OPTIONS_CONF.includes('fixMonotonicity')
: false,
fixSkippedDeposits: AUTO_FIX_BATCH_OPTIONS_CONF
? AUTO_FIX_BATCH_OPTIONS_CONF.includes('fixSkippedDeposits')
: false,
}
logger.info('Starting batch submitter...')
const requiredEnvVars: RequiredEnvVars = {
L1_NODE_WEB3_URL: config.str('l1-node-web3-url', env.L1_NODE_WEB3_URL),
L2_NODE_WEB3_URL: config.str('l2-node-web3-url', env.L2_NODE_WEB3_URL),
ADDRESS_MANAGER_ADDRESS: config.str(
'address-manager-address',
env.ADDRESS_MANAGER_ADDRESS
),
MIN_L1_TX_SIZE: config.uint(
'min-l1-tx-size',
parseInt(env.MIN_L1_TX_SIZE, 10)
),
MAX_L1_TX_SIZE: config.uint(
'max-l1-tx-size',
parseInt(env.MAX_L1_TX_SIZE, 10)
),
MAX_TX_BATCH_COUNT: config.uint(
'max-tx-batch-count',
parseInt(env.MAX_TX_BATCH_COUNT, 10)
),
MAX_STATE_BATCH_COUNT: config.uint(
'max-state-batch-count',
parseInt(env.MAX_STATE_BATCH_COUNT, 10)
),
MAX_BATCH_SUBMISSION_TIME: config.uint(
'max-batch-submisison-time',
parseInt(env.MAX_BATCH_SUBMISSION_TIME, 10)
),
POLL_INTERVAL: config.uint(
'poll-interval',
parseInt(env.POLL_INTERVAL, 10)
),
NUM_CONFIRMATIONS: config.uint(
'num-confirmations',
parseInt(env.NUM_CONFIRMATIONS, 10)
),
RESUBMISSION_TIMEOUT: config.uint(
'resubmission-timeout',
parseInt(env.RESUBMISSION_TIMEOUT, 10)
),
FINALITY_CONFIRMATIONS: config.uint(
'finality-confirmations',
parseInt(env.FINALITY_CONFIRMATIONS, 10)
),
RUN_TX_BATCH_SUBMITTER: config.bool(
'run-tx-batch-submitter',
env.RUN_TX_BATCH_SUBMITTER === 'true'
),
RUN_STATE_BATCH_SUBMITTER: config.bool(
'run-state-batch-submitter',
env.RUN_STATE_BATCH_SUBMITTER === 'true'
),
SAFE_MINIMUM_ETHER_BALANCE: config.ufloat(
'safe-minimum-ether-balance',
parseFloat(env.SAFE_MINIMUM_ETHER_BALANCE)
),
CLEAR_PENDING_TXS: config.bool(
'clear-pending-txs',
env.CLEAR_PENDING_TXS === 'true'
),
}
for (const [key, val] of Object.entries(requiredEnvVars)) {
if (val === null || val === undefined) {
logger.warn('Missing environment variable', {
key,
value: val,
})
exit(1)
}
}
if (requiredEnvVars.MIN_L1_TX_SIZE === 0) {
logger.error('Must configure a MIN_L1_TX_SIZE greater than 0')
process.exit(1)
}
const clearPendingTxs = requiredEnvVars.CLEAR_PENDING_TXS
const l2Provider = asL2Provider(
new StaticJsonRpcProvider({
url: requiredEnvVars.L2_NODE_WEB3_URL,
headers: { 'User-Agent': 'batch-submitter' },
})
)
const sequencerSigner: Signer = await getSequencerSigner()
let proposerSigner: Signer = await getProposerSigner()
const sequencerAddress = await sequencerSigner.getAddress()
const proposerAddress = await proposerSigner.getAddress()
// If the sequencer & proposer are the same, use a single wallet
if (sequencerAddress === proposerAddress) {
proposerSigner = sequencerSigner
throw new Error(
'Sequencer and proposer have the same address, multiple transactions may be sent with the same nonce.'
)
}
logger.info('Configured batch submitter addresses', {
sequencerAddress,
proposerAddress,
addressManagerAddress: requiredEnvVars.ADDRESS_MANAGER_ADDRESS,
})
const resubmissionConfig: ResubmissionConfig = {
resubmissionTimeout: requiredEnvVars.RESUBMISSION_TIMEOUT * 1_000,
minGasPriceInGwei: MIN_GAS_PRICE_IN_GWEI,
maxGasPriceInGwei: GAS_THRESHOLD_IN_GWEI,
gasRetryIncrement: GAS_RETRY_INCREMENT,
}
const txBatchTxSubmitter: TransactionSubmitter =
new YnatmTransactionSubmitter(
sequencerSigner,
resubmissionConfig,
requiredEnvVars.NUM_CONFIRMATIONS
)
const txBatchSubmitter = new TransactionBatchSubmitter(
sequencerSigner,
l2Provider,
requiredEnvVars.MIN_L1_TX_SIZE,
requiredEnvVars.MAX_L1_TX_SIZE,
requiredEnvVars.MAX_TX_BATCH_COUNT,
requiredEnvVars.MAX_BATCH_SUBMISSION_TIME * 1_000,
requiredEnvVars.NUM_CONFIRMATIONS,
requiredEnvVars.RESUBMISSION_TIMEOUT * 1_000,
requiredEnvVars.ADDRESS_MANAGER_ADDRESS,
requiredEnvVars.SAFE_MINIMUM_ETHER_BALANCE,
GAS_THRESHOLD_IN_GWEI,
txBatchTxSubmitter,
BLOCK_OFFSET,
VALIDATE_TX_BATCH,
logger.child({ name: TX_BATCH_SUBMITTER_LOG_TAG }),
metrics,
autoFixBatchOptions
)
const stateBatchTxSubmitter: TransactionSubmitter =
new YnatmTransactionSubmitter(
proposerSigner,
resubmissionConfig,
requiredEnvVars.NUM_CONFIRMATIONS
)
const stateBatchSubmitter = new StateBatchSubmitter(
proposerSigner,
l2Provider,
requiredEnvVars.MIN_L1_TX_SIZE,
requiredEnvVars.MAX_L1_TX_SIZE,
requiredEnvVars.MAX_STATE_BATCH_COUNT,
requiredEnvVars.MAX_BATCH_SUBMISSION_TIME * 1_000,
requiredEnvVars.NUM_CONFIRMATIONS,
requiredEnvVars.RESUBMISSION_TIMEOUT * 1_000,
requiredEnvVars.FINALITY_CONFIRMATIONS,
requiredEnvVars.ADDRESS_MANAGER_ADDRESS,
requiredEnvVars.SAFE_MINIMUM_ETHER_BALANCE,
stateBatchTxSubmitter,
BLOCK_OFFSET,
logger.child({ name: STATE_BATCH_SUBMITTER_LOG_TAG }),
metrics,
FRAUD_SUBMISSION_ADDRESS
)
// Loops infinitely!
const loop = async (
func: () => Promise<TransactionReceipt>,
signer: Signer
): Promise<void> => {
// Clear all pending transactions
if (clearPendingTxs) {
try {
const pendingTxs = await signer.getTransactionCount('pending')
const latestTxs = await signer.getTransactionCount('latest')
if (pendingTxs > latestTxs) {
logger.info(
'Detected pending transactions. Clearing all transactions!'
)
for (let i = latestTxs; i < pendingTxs; i++) {
const response = await signer.sendTransaction({
to: await signer.getAddress(),
value: 0,
nonce: i,
})
logger.info('Submitted empty transaction', {
nonce: i,
txHash: response.hash,
to: response.to,
from: response.from,
})
logger.debug('empty transaction data', {
data: response.data,
})
await signer.provider.waitForTransaction(
response.hash,
requiredEnvVars.NUM_CONFIRMATIONS
)
}
}
} catch (err) {
logger.error('Cannot clear transactions', {
message: err.toString(),
stack: err.stack,
code: err.code,
})
process.exit(1)
}
}
while (true) {
try {
await func()
} catch (err) {
switch (err.code) {
case 'SERVER_ERROR':
logger.error(`Encountered server error with status ${err.status}`, {
message: err.toString(),
stack: err.stack,
code: err.code,
})
break
case 'NETWORK_ERROR':
logger.error('Could not detect network', {
message: err.toString(),
stack: err.stack,
code: err.code,
})
break
default:
logger.error('Unhandled exception during batch submission', {
message: err.toString(),
stack: err.stack,
code: err.code,
})
break
}
logger.info('Retrying...')
}
// Sleep
await new Promise((r) => setTimeout(r, requiredEnvVars.POLL_INTERVAL))
}
}
// Run batch submitters in two seperate infinite loops!
if (requiredEnvVars.RUN_TX_BATCH_SUBMITTER) {
loop(() => txBatchSubmitter.submitNextBatch(), sequencerSigner)
}
if (requiredEnvVars.RUN_STATE_BATCH_SUBMITTER) {
loop(() => stateBatchSubmitter.submitNextBatch(), proposerSigner)
}
if (config.bool('run-metrics-server', env.RUN_METRICS_SERVER === 'true')) {
// Initialize metrics server
await createMetricsServer({
logger,
registry: metrics.registry,
port: config.uint('metrics-port', parseInt(env.METRICS_PORT, 10) || 7300),
hostname: config.str(
'metrics-hostname',
env.METRICS_HOSTNAME || '127.0.0.1'
),
})
}
}
export * from './batch-submitter'
export * from './utils'
export * from './transaction-chain-contract'
/* External Imports */
import { Contract, ethers } from 'ethers'
import {
TransactionResponse,
TransactionRequest,
} from '@ethersproject/abstract-provider'
import { keccak256 } from 'ethers/lib/utils'
import {
AppendSequencerBatchParams,
BatchContext,
encodeAppendSequencerBatch,
remove0x,
} from '@eth-optimism/core-utils'
export { encodeAppendSequencerBatch, BatchContext, AppendSequencerBatchParams }
/*
* OVM_CanonicalTransactionChainContract is a wrapper around a normal Ethers contract
* where the `appendSequencerBatch(...)` function uses a specialized encoding for improved efficiency.
*/
export class CanonicalTransactionChainContract extends Contract {
public customPopulateTransaction = {
appendSequencerBatch: async (
batch: AppendSequencerBatchParams
): Promise<ethers.PopulatedTransaction> => {
const nonce = await this.signer.getTransactionCount()
const to = this.address
const data = getEncodedCalldata(batch)
const gasLimit = await this.signer.provider.estimateGas({
to,
from: await this.signer.getAddress(),
data,
})
return {
nonce,
to,
data,
gasLimit,
}
},
}
public async appendSequencerBatch(
batch: AppendSequencerBatchParams,
options?: TransactionRequest
): Promise<TransactionResponse> {
return appendSequencerBatch(this, batch, options)
}
}
/**********************
* Internal Functions *
*********************/
const APPEND_SEQUENCER_BATCH_METHOD_ID = keccak256(
Buffer.from('appendSequencerBatch()')
).slice(2, 10)
const appendSequencerBatch = async (
OVM_CanonicalTransactionChain: Contract,
batch: AppendSequencerBatchParams,
options?: TransactionRequest
): Promise<TransactionResponse> => {
return OVM_CanonicalTransactionChain.signer.sendTransaction({
to: OVM_CanonicalTransactionChain.address,
data: getEncodedCalldata(batch),
...options,
})
}
const getEncodedCalldata = (batch: AppendSequencerBatchParams): string => {
const methodId = APPEND_SEQUENCER_BATCH_METHOD_ID
const calldata = encodeAppendSequencerBatch(batch)
return '0x' + remove0x(methodId) + remove0x(calldata)
}
import { Signer, ethers, PopulatedTransaction } from 'ethers'
import {
TransactionReceipt,
TransactionResponse,
} from '@ethersproject/abstract-provider'
import * as ynatm from '@eth-optimism/ynatm'
export interface ResubmissionConfig {
resubmissionTimeout: number
minGasPriceInGwei: number
maxGasPriceInGwei: number
gasRetryIncrement: number
}
export type SubmitTransactionFn = (
tx: PopulatedTransaction
) => Promise<TransactionReceipt>
export interface TxSubmissionHooks {
beforeSendTransaction: (tx: PopulatedTransaction) => void
onTransactionResponse: (txResponse: TransactionResponse) => void
}
const getGasPriceInGwei = async (signer: Signer): Promise<number> => {
return parseInt(
ethers.utils.formatUnits(await signer.getGasPrice(), 'gwei'),
10
)
}
export const ynatmRejectOn = (e) => {
// taken almost verbatim from the readme,
// see https://github.com/ethereum-optimism/ynatm.
// immediately rejects on reverts and nonce errors
const errMsg = e.toString().toLowerCase()
const conditions = ['revert', 'nonce']
for (const cond of conditions) {
if (errMsg.includes(cond)) {
return true
}
}
return false
}
export const submitTransactionWithYNATM = async (
tx: PopulatedTransaction,
signer: Signer,
config: ResubmissionConfig,
numConfirmations: number,
hooks: TxSubmissionHooks
): Promise<TransactionReceipt> => {
const sendTxAndWaitForReceipt = async (
gasPrice
): Promise<TransactionReceipt> => {
const fullTx = {
...tx,
gasPrice,
}
hooks.beforeSendTransaction(fullTx)
const txResponse = await signer.sendTransaction(fullTx)
hooks.onTransactionResponse(txResponse)
return signer.provider.waitForTransaction(txResponse.hash, numConfirmations)
}
const minGasPrice = await getGasPriceInGwei(signer)
const receipt = await ynatm.send({
sendTransactionFunction: sendTxAndWaitForReceipt,
minGasPrice: ynatm.toGwei(minGasPrice),
maxGasPrice: ynatm.toGwei(config.maxGasPriceInGwei),
gasPriceScalingFunction: ynatm.LINEAR(config.gasRetryIncrement),
delay: config.resubmissionTimeout,
rejectImmediatelyOnCondition: ynatmRejectOn,
})
return receipt
}
export interface TransactionSubmitter {
submitTransaction(
tx: PopulatedTransaction,
hooks?: TxSubmissionHooks
): Promise<TransactionReceipt>
}
export class YnatmTransactionSubmitter implements TransactionSubmitter {
constructor(
readonly signer: Signer,
readonly ynatmConfig: ResubmissionConfig,
readonly numConfirmations: number
) {}
public async submitTransaction(
tx: PopulatedTransaction,
hooks?: TxSubmissionHooks
): Promise<TransactionReceipt> {
if (!hooks) {
hooks = {
beforeSendTransaction: () => undefined,
onTransactionResponse: () => undefined,
}
}
return submitTransactionWithYNATM(
tx,
this.signer,
this.ynatmConfig,
this.numConfirmations,
hooks
)
}
}
/* External Imports */
import { ethers } from 'hardhat'
import '@nomiclabs/hardhat-ethers'
import { Signer, ContractFactory, Contract, BigNumber } from 'ethers'
import sinon from 'sinon'
import scc from '@eth-optimism/contracts/artifacts/contracts/L1/rollup/StateCommitmentChain.sol/StateCommitmentChain.json'
import { getContractInterface } from '@eth-optimism/contracts'
import { smockit, MockContract } from '@eth-optimism/smock'
import { getContractFactory } from 'old-contracts'
import { QueueOrigin, Batch, remove0x } from '@eth-optimism/core-utils'
import { Logger, Metrics } from '@eth-optimism/common-ts'
/* Internal Imports */
import { MockchainProvider } from './mockchain-provider'
import { expect } from '../setup'
import {
CanonicalTransactionChainContract,
TransactionBatchSubmitter as RealTransactionBatchSubmitter,
StateBatchSubmitter,
TX_BATCH_SUBMITTER_LOG_TAG,
STATE_BATCH_SUBMITTER_LOG_TAG,
YnatmTransactionSubmitter,
ResubmissionConfig,
} from '../../src'
import {
makeAddressManager,
setProxyTarget,
FORCE_INCLUSION_PERIOD_SECONDS,
} from '../helpers'
const EXAMPLE_STATE_ROOT =
'0x16b7f83f409c7195b1f4fde5652f1b54a4477eacb6db7927691becafba5f8801'
const MAX_GAS_LIMIT = 8_000_000
const MAX_TX_SIZE = 100_000
const MIN_TX_SIZE = 1_000
const MIN_GAS_PRICE_IN_GWEI = 1
const GAS_RETRY_INCREMENT = 5
const GAS_THRESHOLD_IN_GWEI = 120
// Helper functions
interface QueueElement {
queueRoot: string
timestamp: number
blockNumber: number
}
const getQueueElement = async (
ctcContract: Contract,
nextQueueIndex?: number
): Promise<QueueElement> => {
if (!nextQueueIndex) {
nextQueueIndex = await ctcContract.getNextQueueIndex()
}
const nextQueueElement = await ctcContract.getQueueElement(nextQueueIndex)
return nextQueueElement
}
// A transaction batch submitter which skips the validate batch check
class TransactionBatchSubmitter extends RealTransactionBatchSubmitter {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected async _validateBatch(batch: Batch): Promise<boolean> {
return true
}
}
const testMetrics = new Metrics({ prefix: 'bs_test' })
describe('BatchSubmitter', () => {
let signer: Signer
let sequencer: Signer
before(async () => {
;[signer, sequencer] = await ethers.getSigners()
})
let AddressManager: Contract
let Mock__OVM_ExecutionManager: MockContract
let Mock__OVM_BondManager: MockContract
let Mock__OVM_StateCommitmentChain: MockContract
before(async () => {
AddressManager = await makeAddressManager()
await AddressManager.setAddress(
'OVM_Sequencer',
await sequencer.getAddress()
)
Mock__OVM_ExecutionManager = await smockit(
await getContractFactory('OVM_ExecutionManager')
)
Mock__OVM_BondManager = await smockit(
await getContractFactory('OVM_BondManager')
)
Mock__OVM_StateCommitmentChain = await smockit(
await getContractFactory('OVM_StateCommitmentChain')
)
await setProxyTarget(
AddressManager,
'OVM_ExecutionManager',
Mock__OVM_ExecutionManager
)
await setProxyTarget(
AddressManager,
'OVM_BondManager',
Mock__OVM_BondManager
)
await setProxyTarget(
AddressManager,
'OVM_StateCommitmentChain',
Mock__OVM_StateCommitmentChain
)
Mock__OVM_StateCommitmentChain.smocked.canOverwrite.will.return.with(false)
Mock__OVM_ExecutionManager.smocked.getMaxTransactionGasLimit.will.return.with(
MAX_GAS_LIMIT
)
Mock__OVM_BondManager.smocked.isCollateralized.will.return.with(true)
})
let Factory__OVM_CanonicalTransactionChain: ContractFactory
let Factory__OVM_StateCommitmentChain: ContractFactory
before(async () => {
Factory__OVM_CanonicalTransactionChain = await getContractFactory(
'OVM_CanonicalTransactionChain'
)
Factory__OVM_CanonicalTransactionChain =
Factory__OVM_CanonicalTransactionChain.connect(signer)
Factory__OVM_StateCommitmentChain = await getContractFactory(
'OVM_StateCommitmentChain'
)
Factory__OVM_StateCommitmentChain =
Factory__OVM_StateCommitmentChain.connect(signer)
})
let OVM_CanonicalTransactionChain: CanonicalTransactionChainContract
let OVM_StateCommitmentChain: Contract
let l2Provider: MockchainProvider
beforeEach(async () => {
const unwrapped_OVM_CanonicalTransactionChain =
await Factory__OVM_CanonicalTransactionChain.deploy(
AddressManager.address,
FORCE_INCLUSION_PERIOD_SECONDS
)
await unwrapped_OVM_CanonicalTransactionChain.init()
await AddressManager.setAddress(
'OVM_CanonicalTransactionChain',
unwrapped_OVM_CanonicalTransactionChain.address
)
await AddressManager.setAddress(
'CanonicalTransactionChain',
unwrapped_OVM_CanonicalTransactionChain.address
)
OVM_CanonicalTransactionChain = new CanonicalTransactionChainContract(
unwrapped_OVM_CanonicalTransactionChain.address,
getContractInterface('CanonicalTransactionChain'),
sequencer
)
const unwrapped_OVM_StateCommitmentChain =
await Factory__OVM_StateCommitmentChain.deploy(
AddressManager.address,
0, // fraudProofWindowSeconds
0 // sequencerPublishWindowSeconds
)
await unwrapped_OVM_StateCommitmentChain.init()
await AddressManager.setAddress(
'OVM_StateCommitmentChain',
unwrapped_OVM_StateCommitmentChain.address
)
await AddressManager.setAddress(
'StateCommitmentChain',
unwrapped_OVM_StateCommitmentChain.address
)
OVM_StateCommitmentChain = new Contract(
unwrapped_OVM_StateCommitmentChain.address,
getContractInterface('StateCommitmentChain'),
sequencer
)
l2Provider = new MockchainProvider(
OVM_CanonicalTransactionChain.address,
OVM_StateCommitmentChain.address
)
})
afterEach(() => {
sinon.restore()
})
const createBatchSubmitter = (timeout: number): TransactionBatchSubmitter => {
const resubmissionConfig: ResubmissionConfig = {
resubmissionTimeout: 100000,
minGasPriceInGwei: MIN_GAS_PRICE_IN_GWEI,
maxGasPriceInGwei: GAS_THRESHOLD_IN_GWEI,
gasRetryIncrement: GAS_RETRY_INCREMENT,
}
const txBatchTxSubmitter = new YnatmTransactionSubmitter(
sequencer,
resubmissionConfig,
1
)
return new TransactionBatchSubmitter(
sequencer,
l2Provider as any,
MIN_TX_SIZE,
MAX_TX_SIZE,
10,
timeout,
1,
100000,
AddressManager.address,
1,
GAS_THRESHOLD_IN_GWEI,
txBatchTxSubmitter,
1,
false,
new Logger({ name: TX_BATCH_SUBMITTER_LOG_TAG }),
testMetrics
)
}
describe('TransactionBatchSubmitter', () => {
describe('submitNextBatch', () => {
let batchSubmitter
beforeEach(async () => {
for (let i = 1; i < 15; i++) {
await OVM_CanonicalTransactionChain.enqueue(
'0x' + '01'.repeat(20),
50_000,
'0x' + i.toString().repeat(64),
{
gasLimit: 1_000_000,
}
)
}
batchSubmitter = createBatchSubmitter(0)
})
it('should submit a sequencer batch correctly', async () => {
l2Provider.setNumBlocksToReturn(5)
const nextQueueElement = await getQueueElement(
OVM_CanonicalTransactionChain
)
l2Provider.setL2BlockData(
{
rawTransaction: '0x1234',
l1BlockNumber: nextQueueElement.blockNumber - 1,
txType: 0,
queueOrigin: QueueOrigin.Sequencer,
l1TxOrigin: null,
} as any,
nextQueueElement.timestamp - 1
)
let receipt = await batchSubmitter.submitNextBatch()
let logData = remove0x(receipt.logs[1].data)
expect(parseInt(logData.slice(64 * 0, 64 * 1), 16)).to.equal(0) // _startingQueueIndex
expect(parseInt(logData.slice(64 * 1, 64 * 2), 16)).to.equal(0) // _numQueueElements
expect(parseInt(logData.slice(64 * 2, 64 * 3), 16)).to.equal(6) // _totalElements
receipt = await batchSubmitter.submitNextBatch()
logData = remove0x(receipt.logs[1].data)
expect(parseInt(logData.slice(64 * 0, 64 * 1), 16)).to.equal(0) // _startingQueueIndex
expect(parseInt(logData.slice(64 * 1, 64 * 2), 16)).to.equal(0) // _numQueueElements
expect(parseInt(logData.slice(64 * 2, 64 * 3), 16)).to.equal(11) // _totalElements
})
it('should submit a queue batch correctly', async () => {
l2Provider.setNumBlocksToReturn(5)
l2Provider.setL2BlockData({
queueOrigin: QueueOrigin.L1ToL2,
} as any)
let receipt = await batchSubmitter.submitNextBatch()
let logData = remove0x(receipt.logs[1].data)
expect(parseInt(logData.slice(64 * 0, 64 * 1), 16)).to.equal(0) // _startingQueueIndex
expect(parseInt(logData.slice(64 * 1, 64 * 2), 16)).to.equal(6) // _numQueueElements
expect(parseInt(logData.slice(64 * 2, 64 * 3), 16)).to.equal(6) // _totalElements
receipt = await batchSubmitter.submitNextBatch()
logData = remove0x(receipt.logs[1].data)
expect(parseInt(logData.slice(64 * 0, 64 * 1), 16)).to.equal(6) // _startingQueueIndex
expect(parseInt(logData.slice(64 * 1, 64 * 2), 16)).to.equal(5) // _numQueueElements
expect(parseInt(logData.slice(64 * 2, 64 * 3), 16)).to.equal(11) // _totalElements
})
it('should submit a batch with both queue and sequencer chain elements', async () => {
l2Provider.setNumBlocksToReturn(10) // For this batch we'll return 10 elements!
l2Provider.setL2BlockData({
queueOrigin: QueueOrigin.L1ToL2,
} as any)
// Turn blocks 3-5 into sequencer txs
const nextQueueElement = await getQueueElement(
OVM_CanonicalTransactionChain,
2
)
l2Provider.setL2BlockData(
{
rawTransaction: '0x1234',
l1BlockNumber: nextQueueElement.blockNumber - 1,
txType: 1,
queueOrigin: QueueOrigin.Sequencer,
l1TxOrigin: null,
} as any,
nextQueueElement.timestamp - 1,
'', // blank stateRoot
3,
6
)
const receipt = await batchSubmitter.submitNextBatch()
const logData = remove0x(receipt.logs[1].data)
expect(parseInt(logData.slice(64 * 0, 64 * 1), 16)).to.equal(0) // _startingQueueIndex
expect(parseInt(logData.slice(64 * 1, 64 * 2), 16)).to.equal(8) // _numQueueElements
expect(parseInt(logData.slice(64 * 2, 64 * 3), 16)).to.equal(11) // _totalElements
})
it('should submit a small batch only after the timeout', async () => {
l2Provider.setNumBlocksToReturn(2)
l2Provider.setL2BlockData({
queueOrigin: QueueOrigin.L1ToL2,
} as any)
// Create a batch submitter with a long timeout & make sure it doesn't submit the batches one after another
const longTimeout = 10_000
batchSubmitter = createBatchSubmitter(longTimeout)
let receipt = await batchSubmitter.submitNextBatch()
expect(receipt).to.not.be.undefined
receipt = await batchSubmitter.submitNextBatch()
// The receipt should be undefined because that means it didn't submit
expect(receipt).to.be.undefined
// This time create a batch submitter with a short timeout & it should submit batches after the timeout is reached
const shortTimeout = 5
batchSubmitter = createBatchSubmitter(shortTimeout)
receipt = await batchSubmitter.submitNextBatch()
expect(receipt).to.not.be.undefined
// Sleep for the short timeout
await new Promise((r) => setTimeout(r, shortTimeout))
receipt = await batchSubmitter.submitNextBatch()
// The receipt should NOT be undefined because that means it successfully submitted!
expect(receipt).to.not.be.undefined
})
it('should not submit if gas price is over threshold', async () => {
l2Provider.setNumBlocksToReturn(2)
l2Provider.setL2BlockData({
queueOrigin: QueueOrigin.L1ToL2,
} as any)
const highGasPriceWei = BigNumber.from(200).mul(1_000_000_000)
sinon
.stub(sequencer, 'getGasPrice')
.callsFake(async () => highGasPriceWei)
const receipt = await batchSubmitter.submitNextBatch()
expect(sequencer.getGasPrice).to.have.been.calledOnce
expect(receipt).to.be.undefined
})
it('should submit if gas price is not over threshold', async () => {
l2Provider.setNumBlocksToReturn(2)
l2Provider.setL2BlockData({
queueOrigin: QueueOrigin.L1ToL2,
} as any)
const lowGasPriceWei = BigNumber.from(2).mul(1_000_000_000)
sinon
.stub(sequencer, 'getGasPrice')
.callsFake(async () => lowGasPriceWei)
const receipt = await batchSubmitter.submitNextBatch()
expect(sequencer.getGasPrice).to.have.been.calledTwice
expect(receipt).to.not.be.undefined
})
})
})
describe('StateBatchSubmitter', () => {
let txBatchSubmitter
let stateBatchSubmitter
beforeEach(async () => {
for (let i = 1; i < 15; i++) {
await OVM_CanonicalTransactionChain.enqueue(
'0x' + '01'.repeat(20),
50_000,
'0x' + i.toString().repeat(64),
{
gasLimit: 1_000_000,
}
)
}
txBatchSubmitter = createBatchSubmitter(0)
l2Provider.setNumBlocksToReturn(5)
const nextQueueElement = await getQueueElement(
OVM_CanonicalTransactionChain
)
l2Provider.setL2BlockData(
{
rawTransaction: '0x1234',
l1BlockNumber: nextQueueElement.blockNumber - 1,
txType: 0,
queueOrigin: QueueOrigin.Sequencer,
l1TxOrigin: null,
} as any,
nextQueueElement.timestamp - 1,
EXAMPLE_STATE_ROOT // example stateRoot
)
// submit a batch of transactions to enable state batch submission
await txBatchSubmitter.submitNextBatch()
const resubmissionConfig: ResubmissionConfig = {
resubmissionTimeout: 100000,
minGasPriceInGwei: MIN_GAS_PRICE_IN_GWEI,
maxGasPriceInGwei: GAS_THRESHOLD_IN_GWEI,
gasRetryIncrement: GAS_RETRY_INCREMENT,
}
const stateBatchTxSubmitter = new YnatmTransactionSubmitter(
sequencer,
resubmissionConfig,
1
)
stateBatchSubmitter = new StateBatchSubmitter(
sequencer,
l2Provider as any,
MIN_TX_SIZE,
MAX_TX_SIZE,
10, // maxBatchSize
0,
1,
100000,
0, // finalityConfirmations
AddressManager.address,
1,
stateBatchTxSubmitter,
1,
new Logger({ name: STATE_BATCH_SUBMITTER_LOG_TAG }),
testMetrics,
'0x' + '01'.repeat(20) // placeholder for fraudSubmissionAddress
)
})
describe('submitNextBatch', () => {
it('should submit a state batch after a transaction batch', async () => {
const receipt = await stateBatchSubmitter.submitNextBatch()
expect(receipt).to.not.be.undefined
const iface = new ethers.utils.Interface(scc.abi)
const parsedLogs = iface.parseLog(receipt.logs[0])
expect(parsedLogs.eventFragment.name).to.eq('StateBatchAppended')
expect(parsedLogs.args._batchIndex.toNumber()).to.eq(0)
expect(parsedLogs.args._batchSize.toNumber()).to.eq(6)
expect(parsedLogs.args._prevTotalElements.toNumber()).to.eq(0)
})
})
})
})
/* External Imports */
import { providers, BigNumber } from 'ethers'
import {
BlockWithTransactions,
TransactionResponse,
} from '@ethersproject/abstract-provider'
import { L2Transaction, L2Block, RollupInfo } from '@eth-optimism/core-utils'
/**
* Unformatted Transaction & Blocks. This exists because Geth currently
* does not return the correct fields & so this code renames those
* poorly named fields
*/
interface UnformattedL2Transaction extends TransactionResponse {
l1BlockNumber: string
l1MessageSender: string
signatureHashType: string
queueOrigin: string
rawTransaction: string
}
interface UnformattedL2Block extends BlockWithTransactions {
stateRoot: string
transactions: [UnformattedL2Transaction]
}
export class MockchainProvider extends providers.JsonRpcProvider {
public mockBlockNumber: number = 1
public numBlocksToReturn: number = 2
public mockBlocks: L2Block[] = []
public ctcAddr: string
public sccAddr: string
constructor(ctcAddr: string, sccAddr: string) {
super('https://optimism.io')
for (const block of BLOCKS) {
if (block.number === 0) {
// No need to convert genesis to an L2Block because it has no txs
this.mockBlocks.push(block)
continue
}
this.mockBlocks.push(this._toL2Block(block))
this.ctcAddr = ctcAddr
this.sccAddr = sccAddr
}
}
public async getBlockNumber(): Promise<number> {
// Increment our mock block number every time
if (
this.mockBlockNumber + this.numBlocksToReturn <
this.mockBlocks.length
) {
this.mockBlockNumber += this.numBlocksToReturn
} else {
return this.mockBlocks.length - 1
}
return this.mockBlockNumber
}
public async send(endpoint: string, params: []): Promise<any> {
switch (endpoint) {
case 'eth_chainId':
return this.chainId()
case 'rollup_getInfo':
const info: RollupInfo = {
mode: 'sequencer',
syncing: false,
ethContext: {
timestamp: 0,
blockNumber: 0,
},
rollupContext: {
index: 0,
queueIndex: 0,
},
}
return info
case 'eth_getBlockByNumber':
if (params.length === 0) {
throw new Error(`Invalid params for ${endpoint}`)
}
const blockNumber = BigNumber.from((params as any)[0]).toNumber()
return this.mockBlocks[blockNumber]
default:
throw new Error('Unsupported endpoint!')
}
}
public setNumBlocksToReturn(numBlocks: number): void {
this.numBlocksToReturn = numBlocks
}
public setL2BlockData(
tx: L2Transaction,
timestamp?: number,
stateRoot?: string,
start: number = 1,
end: number = this.mockBlocks.length
) {
for (let i = start; i < end; i++) {
this.mockBlocks[i].timestamp = timestamp
? timestamp
: this.mockBlocks[i].timestamp
this.mockBlocks[i].transactions[0] = {
...this.mockBlocks[i].transactions[0],
...tx,
}
this.mockBlocks[i].stateRoot = stateRoot
}
}
public async getBlockWithTransactions(blockNumber: number): Promise<L2Block> {
return this.mockBlocks[blockNumber]
}
public chainId(): number {
// We know that mockBlocks will always have at least 1 value
return this.mockBlocks[1].transactions[0].chainId
}
private _toL2Block(block: UnformattedL2Block): L2Block {
const l1BlockNumber: number = parseInt(
block.transactions[0].l1BlockNumber,
10
)
const queueOrigin: string = block.transactions[0].queueOrigin
const l1TxOrigin: string = block.transactions[0].l1MessageSender
const l2Transaction: L2Transaction = {
...block.transactions[0],
// Rename the incorrectly named fields
l1TxOrigin,
queueOrigin,
l1BlockNumber,
}
// Add an interface here to fix the type casing into L2Block during Object.assign
interface PartialL2Block {
transactions: [L2Transaction]
}
const partialBlock: PartialL2Block = {
transactions: [l2Transaction],
}
const l2Block: L2Block = { ...block, ...partialBlock }
return l2Block
}
}
const BLOCKS = JSON.parse(`
[
{
"hash":"0xbc27fdbd1fee6e001438709ef57210bb7b2b1b8c23b65acb2d79161f4dc3cf05",
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"number":0,
"timestamp":1603651804,
"nonce":"0x0000000000000042",
"difficulty":1,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0x0000000000000000000000000000000000000000",
"extraData":"0x1234",
"transactions":[
]
},
{
"hash":"0x05a7f5c5fce57346f59355184daa58822f97a32e4327fe6ef4a1c37dfd36f2f0",
"parentHash":"0x64e89492b3ea72b9f9f0f4566e5198e19d7bfa583619c54c33872c7112aec9cd",
"number":1,
"timestamp":1603404102,
"nonce":"0x0000000000000042",
"difficulty":131072,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x688ddd1acc3cbccd9112018eac6c78744f43140d408128b8ed0392c7ee28966e",
"blockHash":"0x05a7f5c5fce57346f59355184daa58822f97a32e4327fe6ef4a1c37dfd36f2f0",
"blockNumber":1,
"transactionIndex":0,
"confirmations":16,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0x05bc67"
},
"to":null,
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":0,
"data":"0x608060405234801561001057600080fd5b50600080546001600160a01b03191633178082556040516001600160a01b039190911691907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a361056a806100696000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c8063715018a61461005c5780638da5cb5b146100665780639b2ea4bd1461008a578063bf40fac11461013b578063f2fde38b146101e1575b600080fd5b610064610207565b005b61006e6102b0565b604080516001600160a01b039092168252519081900360200190f35b610064600480360360408110156100a057600080fd5b8101906020810181356401000000008111156100bb57600080fd5b8201836020820111156100cd57600080fd5b803590602001918460018302840111640100000000831117156100ef57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550505090356001600160a01b031691506102bf9050565b61006e6004803603602081101561015157600080fd5b81019060208101813564010000000081111561016c57600080fd5b82018360208201111561017e57600080fd5b803590602001918460018302840111640100000000831117156101a057600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610362945050505050565b610064600480360360208110156101f757600080fd5b50356001600160a01b0316610391565b6000546001600160a01b03163314610266576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6000546001600160a01b031681565b6000546001600160a01b0316331461031e576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b806001600061032c85610490565b815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055505050565b60006001600061037184610490565b81526020810191909152604001600020546001600160a01b031692915050565b6000546001600160a01b031633146103f0576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b0381166104355760405162461bcd60e51b815260040180806020018281038252602d815260200180610508602d913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000816040516020018082805190602001908083835b602083106104c55780518252601f1990920191602091820191016104a6565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405160208183030381529060405280519060200120905091905056fe4f776e61626c653a206e6577206f776e65722063616e6e6f7420626520746865207a65726f2061646472657373a26469706673582212204367ffc2e6671623708150e2d0cff4c12cf566722a26b4748555d789953e2d2264736f6c63430007000033",
"r":"0x2babe370e2e422a38386a5a96cd3bf16772ddbbf8c9dab8aadf4416fff557756",
"s":"0x213ab994b50ed4a38e2de390f851d88cb66dd238a27d89246616b34eb8e859df",
"v":62710,
"creates":"0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA",
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
},
{
"hash":"0xb5903854b196abb9b7d1e3c2f5f8f9519b4fedefc21d72f3d92c74f128afcd46",
"parentHash":"0x05a7f5c5fce57346f59355184daa58822f97a32e4327fe6ef4a1c37dfd36f2f0",
"number":2,
"timestamp":1603404103,
"nonce":"0x0000000000000042",
"difficulty":131136,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0xeae52be001bfea0b3d8de2de5edff5c0c39d26f6e6ab0fc6623cd24913dbf150",
"blockHash":"0xb5903854b196abb9b7d1e3c2f5f8f9519b4fedefc21d72f3d92c74f128afcd46",
"blockNumber":2,
"transactionIndex":0,
"confirmations":15,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0xaeab"
},
"to":"0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":1,
"data":"0x9b2ea4bd000000000000000000000000000000000000000000000000000000000000004000000000000000000000000063fc2ad3d021a4d7e64323529a55a9442c444da0000000000000000000000000000000000000000000000000000000000000000d4f564d5f53657175656e63657200000000000000000000000000000000000000",
"r":"0x530be666add21a30fe9a0fadee8072d4d8ccb2b80a9fe07c7e0591c5a1e5f375",
"s":"0x5954b17cd0f6299ee54f20853bcfdfc870f2fc271f0433fd592b8b0fc0329aa4",
"v":62709,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
},
{
"hash":"0x7504e7104a2d80e810758c1cc2736028ffe5632a2ec988da740c93b3cfa03945",
"parentHash":"0xb5903854b196abb9b7d1e3c2f5f8f9519b4fedefc21d72f3d92c74f128afcd46",
"number":3,
"timestamp":1603404104,
"nonce":"0x0000000000000042",
"difficulty":131200,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x79f4dc191018a37a8b71b4a5aff02ce501b0341fc72708c8095845e61be15c02",
"blockHash":"0x7504e7104a2d80e810758c1cc2736028ffe5632a2ec988da740c93b3cfa03945",
"blockNumber":3,
"transactionIndex":0,
"confirmations":14,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0xafb0"
},
"to":"0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":2,
"data":"0x9b2ea4bd0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000420000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000224f564d5f4465636f6d7072657373696f6e507265636f6d70696c6541646472657373000000000000000000000000000000000000000000000000000000000000",
"r":"0x388b6b7e4fe10128e92507042971b864edd535d8f3cb70c61247daed1dc734c1",
"s":"0x6ad11366b26fa59ce73bc73fa320560f46ffb6df3142adbd16ebce0bcc3ba9f5",
"v":62710,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
},
{
"hash":"0x6587aa5da64944673741bdd3299c0d2471c2fdbfcd94713d511ab7240d127bbb",
"parentHash":"0x7504e7104a2d80e810758c1cc2736028ffe5632a2ec988da740c93b3cfa03945",
"number":4,
"timestamp":1603404105,
"nonce":"0x0000000000000042",
"difficulty":131264,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x0584fbd34096ffc0fc96dbdf1d8ee574c3eda9e07bf85aed9862542416b1a007",
"blockHash":"0x6587aa5da64944673741bdd3299c0d2471c2fdbfcd94713d511ab7240d127bbb",
"blockNumber":4,
"transactionIndex":0,
"confirmations":13,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0x02e294"
},
"to":null,
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":3,
"data":"0x608060405234801561001057600080fd5b50600080546001600160a01b03191633179055610213806100326000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063776d1a0114610077575b60015460408051602036601f8101829004820283018201909352828252610075936001600160a01b0316926000918190840183828082843760009201919091525061009d92505050565b005b6100756004803603602081101561008d57600080fd5b50356001600160a01b031661015d565b60006060836001600160a01b0316836040518082805190602001908083835b602083106100db5780518252601f1990920191602091820191016100bc565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461013d576040519150601f19603f3d011682016040523d82523d6000602084013e610142565b606091505b5091509150811561015557805160208201f35b805160208201fd5b6000546001600160a01b031633141561019057600180546001600160a01b0319166001600160a01b0383161790556101da565b60015460408051602036601f81018290048202830182019093528282526101da936001600160a01b0316926000918190840183828082843760009201919091525061009d92505050565b5056fea2646970667358221220293887d48c4c1c34de868edf3e9a6be82327946c76d71f7c2023e67f556c6ecb64736f6c63430007000033",
"r":"0x2e420851664bb81c0d5d0bd1a805661fc1f83922b92e1d9e0e57c9184eddec0e",
"s":"0x5e00184c9b50ed54e714231af904caed92cf47ee309fc3604793d7d32a9f4988",
"v":62709,
"creates":"0x94BA4d5Ebb0e05A50e977FFbF6e1a1Ee3D89299c",
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
},
{
"hash":"0x7a215669ab018e508473ef76b42d19cc4228cc5cc855089dc04cb22555fcf555",
"parentHash":"0x6587aa5da64944673741bdd3299c0d2471c2fdbfcd94713d511ab7240d127bbb",
"number":5,
"timestamp":1603404106,
"nonce":"0x0000000000000042",
"difficulty":131328,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0xc8243d8b9bf79624f8930527a983be3581cb7d969ae76d3e5962f9db2be7b71a",
"blockHash":"0x7a215669ab018e508473ef76b42d19cc4228cc5cc855089dc04cb22555fcf555",
"blockNumber":5,
"transactionIndex":0,
"confirmations":12,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0xa93c"
},
"to":"0x94BA4d5Ebb0e05A50e977FFbF6e1a1Ee3D89299c",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":4,
"data":"0x776d1a01000000000000000000000000e9a9a643588daa154de182f88a5b04e8745909c2",
"r":"0x2de73fc5aec124cc9cf0fa54fc8492692a48b8921a32d31f46f5d431fdeea7a0",
"s":"0x4e813ae50bc7705fb023d79429ed6cc92367cb5209460e6ea3904c014b06cd4d",
"v":62709,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
},
{
"hash":"0xb4c01e6e867856dce9b451ba67273f86bdc584ff398ed5b8c46f70ea37b1002f",
"parentHash":"0x7a215669ab018e508473ef76b42d19cc4228cc5cc855089dc04cb22555fcf555",
"number":6,
"timestamp":1603404107,
"nonce":"0x0000000000000042",
"difficulty":131392,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0xa5bf13c2638f1d99a26c4c7d867f1bc64a34ebf141524b35840c879536e69d69",
"blockHash":"0xb4c01e6e867856dce9b451ba67273f86bdc584ff398ed5b8c46f70ea37b1002f",
"blockNumber":6,
"transactionIndex":0,
"confirmations":11,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0xaeff"
},
"to":"0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":5,
"data":"0x9b2ea4bd000000000000000000000000000000000000000000000000000000000000004000000000000000000000000094ba4d5ebb0e05a50e977ffbf6e1a1ee3d89299c00000000000000000000000000000000000000000000000000000000000000144f564d5f457865637574696f6e4d616e61676572000000000000000000000000",
"r":"0x42a7ca8603050e58d948df4573374746fabc8542c3fabc1d3b03391c2e50ae3b",
"s":"0x99b52df93bddbd15393ec9b6649c569ec9f65b1e7dda62b7a81baf32bd951a36",
"v":62709,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
},
{
"hash":"0x9f8a3aae03d0f8f769d5af64583c1ee32458bd5b63bf6436147e624ef3297c69",
"parentHash":"0xb4c01e6e867856dce9b451ba67273f86bdc584ff398ed5b8c46f70ea37b1002f",
"number":7,
"timestamp":1603404108,
"nonce":"0x0000000000000042",
"difficulty":131456,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x967c90bfcb19090f0b73b0d0ca4d5f866160d7628d84412fada18abc50e3da69",
"blockHash":"0x9f8a3aae03d0f8f769d5af64583c1ee32458bd5b63bf6436147e624ef3297c69",
"blockNumber":7,
"transactionIndex":0,
"confirmations":10,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0x02e294"
},
"to":null,
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":6,
"data":"0x608060405234801561001057600080fd5b50600080546001600160a01b03191633179055610213806100326000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063776d1a0114610077575b60015460408051602036601f8101829004820283018201909352828252610075936001600160a01b0316926000918190840183828082843760009201919091525061009d92505050565b005b6100756004803603602081101561008d57600080fd5b50356001600160a01b031661015d565b60006060836001600160a01b0316836040518082805190602001908083835b602083106100db5780518252601f1990920191602091820191016100bc565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461013d576040519150601f19603f3d011682016040523d82523d6000602084013e610142565b606091505b5091509150811561015557805160208201f35b805160208201fd5b6000546001600160a01b031633141561019057600180546001600160a01b0319166001600160a01b0383161790556101da565b60015460408051602036601f81018290048202830182019093528282526101da936001600160a01b0316926000918190840183828082843760009201919091525061009d92505050565b5056fea2646970667358221220293887d48c4c1c34de868edf3e9a6be82327946c76d71f7c2023e67f556c6ecb64736f6c63430007000033",
"r":"0x16f49916bda30884d49df7f83c60ca49899fd21311e4cd4b464ac52bfa722b40",
"s":"0x3a2873207cbcba218ff71bb7e3916ea809c393c2407c03a70bbf4e393cbebfcb",
"v":62709,
"creates":"0x956dA338C1518a7FB213042b70c60c021aeBd554",
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
},
{
"hash":"0x808315e40a80d00bf171b8ba924b451fb7936d446177c9e3545303b2ef830801",
"parentHash":"0x9f8a3aae03d0f8f769d5af64583c1ee32458bd5b63bf6436147e624ef3297c69",
"number":8,
"timestamp":1603404109,
"nonce":"0x0000000000000042",
"difficulty":131520,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x3dabdb84873e50c3b26df28ead1c053451a340b20d945a8cf8bddf5b5ff11775",
"blockHash":"0x808315e40a80d00bf171b8ba924b451fb7936d446177c9e3545303b2ef830801",
"blockNumber":8,
"transactionIndex":0,
"confirmations":9,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0xa93c"
},
"to":"0x956dA338C1518a7FB213042b70c60c021aeBd554",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":7,
"data":"0x776d1a01000000000000000000000000048b45c16e9631d3f630106c6086ec21a30cdf60",
"r":"0x553525e3656dfb41299a9f2a0d1a0058445e017bb98992de6d9b762203cf975a",
"s":"0x74b9f344513fa2881cfcec5a4c20027bb905de5c52a8e584da6767d946cd465d",
"v":62710,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
},
{
"hash":"0xed13ac7f6bf594309a325c1558e4a3032f745e66ee0ffc2f95a701562b53dd13",
"parentHash":"0x808315e40a80d00bf171b8ba924b451fb7936d446177c9e3545303b2ef830801",
"number":9,
"timestamp":1603404110,
"nonce":"0x0000000000000042",
"difficulty":131584,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x2b9e216b390804c731f3ffc0630e522865bff35d0c539f377d204e94a346eddd",
"blockHash":"0xed13ac7f6bf594309a325c1558e4a3032f745e66ee0ffc2f95a701562b53dd13",
"blockNumber":9,
"transactionIndex":0,
"confirmations":8,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0xaf2f"
},
"to":"0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":8,
"data":"0x9b2ea4bd0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000956da338c1518a7fb213042b70c60c021aebd55400000000000000000000000000000000000000000000000000000000000000184f564d5f5374617465436f6d6d69746d656e74436861696e0000000000000000",
"r":"0x6318ce7714d7aefc9add30ebcf9d657a01b50c308c689c6f7d5567d806b6914e",
"s":"0xfe4d90d895f4b18b89309d768a383334bf5d622d367051079ac0e401b23960e0",
"v":62709,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
},
{
"hash":"0x65f3831f69d1e746f30d07a3def1eefdcc6650ee0714cbac795c2be3ce9430a2",
"parentHash":"0xed13ac7f6bf594309a325c1558e4a3032f745e66ee0ffc2f95a701562b53dd13",
"number":10,
"timestamp":1603404111,
"nonce":"0x0000000000000042",
"difficulty":131648,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0xa77aca829b5bc91a16c11efb4dee280345851fccbd83b1469f746349894a46ab",
"blockHash":"0x65f3831f69d1e746f30d07a3def1eefdcc6650ee0714cbac795c2be3ce9430a2",
"blockNumber":10,
"transactionIndex":0,
"confirmations":7,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0x24f27e"
},
"to":null,
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":9,
"data":"0x12345678",
"r":"0x06e79a060942823dc5b328a5b059b58cf42372c03617122139deb5b7844c043d",
"s":"0xfabd07fab3f36816397917ae8c048a4675d34d4ca3f7b06ca6595796a159d359",
"v":62710,
"creates":"0x6454C9d69a4721feBA60e26A367bD4D56196Ee7c",
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
},
{
"hash":"0x709bb2a24ac6120a12bf56e806cfcf3ceab71a41408b0685892800eebc4e3a45",
"parentHash":"0x65f3831f69d1e746f30d07a3def1eefdcc6650ee0714cbac795c2be3ce9430a2",
"number":11,
"timestamp":1603404112,
"nonce":"0x0000000000000042",
"difficulty":131712,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x2b5cd67036954f3c4cce09951c076adb3d0517750167e712562abba7910bd536",
"blockHash":"0x709bb2a24ac6120a12bf56e806cfcf3ceab71a41408b0685892800eebc4e3a45",
"blockNumber":11,
"transactionIndex":0,
"confirmations":6,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0x0f4240"
},
"to":"0x6454C9d69a4721feBA60e26A367bD4D56196Ee7c",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":10,
"data":"0x6fee07e00000000000000000000000000101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000c350000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000201111111111111111111111111111111111111111111111111111111111111111",
"r":"0x7538f2153c482a762f133f1438b30c8874887f6da52f04adeba5cc65632ee661",
"s":"0x3dc3a219d164b159ad61f874ccb5c75766cb1d73954196eaba142d890b299d0e",
"v":62710,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
},
{
"hash":"0x13e683738bac942ae739e45b0a0e451b20d7e986d9463a2dac0acf2015e8d09b",
"parentHash":"0x709bb2a24ac6120a12bf56e806cfcf3ceab71a41408b0685892800eebc4e3a45",
"number":12,
"timestamp":1603404113,
"nonce":"0x0000000000000042",
"difficulty":131776,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x339282191d55c5b9fc53d68f199c53ea9b946c9975ed34f15312acd8a70054f7",
"blockHash":"0x13e683738bac942ae739e45b0a0e451b20d7e986d9463a2dac0acf2015e8d09b",
"blockNumber":12,
"transactionIndex":0,
"confirmations":5,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0x0f4240"
},
"to":"0x6454C9d69a4721feBA60e26A367bD4D56196Ee7c",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":11,
"data":"0x6fee07e00000000000000000000000000101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000c350000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000202222222222222222222222222222222222222222222222222222222222222222",
"r":"0x4ab1b83146fbfa4f0cce0110149c2c52e57971ce7cbe5b97a3fd3086bf9f0935",
"s":"0x11dac6b6c1e1d66a89d833ddd230120e6ecdedf49ae9fb38496f4385cb80057d",
"v":62709,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
},
{
"hash":"0x382c468636dafe28d7b3aad0f0521c2e7b9395fc0146b16502d85fa2a49ffe7b",
"parentHash":"0x13e683738bac942ae739e45b0a0e451b20d7e986d9463a2dac0acf2015e8d09b",
"number":13,
"timestamp":1603404114,
"nonce":"0x0000000000000042",
"difficulty":131840,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x8c260f877daaaaeba12b41da6763ce2a641a55ec2e545c4dcbc211b340480e93",
"blockHash":"0x382c468636dafe28d7b3aad0f0521c2e7b9395fc0146b16502d85fa2a49ffe7b",
"blockNumber":13,
"transactionIndex":0,
"confirmations":4,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0x0f4240"
},
"to":"0x6454C9d69a4721feBA60e26A367bD4D56196Ee7c",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":12,
"data":"0x6fee07e00000000000000000000000000101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000c350000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000203333333333333333333333333333333333333333333333333333333333333333",
"r":"0x52f5316fc04aafc95110ac6be222ed656cd0a4ace50fac3e09384408c8b7e32a",
"s":"0x03de7779650526057629e842c45017cfd2dc19137d309cd99f244c0ce9b13186",
"v":62709,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
},
{
"hash":"0x5e5f592a3c96a45bc7fceea8681d066af84709dc155d305fe4e3d68f0bb7bd63",
"parentHash":"0x382c468636dafe28d7b3aad0f0521c2e7b9395fc0146b16502d85fa2a49ffe7b",
"number":14,
"timestamp":1603404115,
"nonce":"0x0000000000000042",
"difficulty":131904,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x2fbd6a3a19c6e5778762619ecfd849b4c3beb35bce0c99b1f357077f3380fa9e",
"blockHash":"0x5e5f592a3c96a45bc7fceea8681d066af84709dc155d305fe4e3d68f0bb7bd63",
"blockNumber":14,
"transactionIndex":0,
"confirmations":3,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0x0f4240"
},
"to":"0x6454C9d69a4721feBA60e26A367bD4D56196Ee7c",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":13,
"data":"0x6fee07e00000000000000000000000000101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000c350000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000204444444444444444444444444444444444444444444444444444444444444444",
"r":"0x546c5a59b1753dd9b9f1f7ff0ae10f6f7fe07bfadf9882e140a021b0ca1a8ab4",
"s":"0x8b6c62b492ea8cf8e7dc8e8236ce7283a24e18370ce7950bda5f450fb7993547",
"v":62709,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
},
{
"hash":"0x13be1ecbdbaae00332acaa341ea3168781b112e7aff368a8bab060fa102085f4",
"parentHash":"0x5e5f592a3c96a45bc7fceea8681d066af84709dc155d305fe4e3d68f0bb7bd63",
"number":15,
"timestamp":1603404116,
"nonce":"0x0000000000000042",
"difficulty":131968,
"gasLimit":{
"type":"BigNumber",
"hex":"0x02625a00"
},
"gasUsed":{
"type":"BigNumber",
"hex":"0x00"
},
"miner":"0xC014BA5EC014ba5ec014Ba5EC014ba5Ec014bA5E",
"extraData":"0x",
"transactions":[
{
"hash":"0x3a60f459be600341f831b6e9b6b75a242cd31d1e4ae6b0bbd763a6b56054ef7b",
"blockHash":"0x13be1ecbdbaae00332acaa341ea3168781b112e7aff368a8bab060fa102085f4",
"blockNumber":15,
"transactionIndex":0,
"confirmations":2,
"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff",
"gasPrice":{
"type":"BigNumber",
"hex":"0x01dcd65000"
},
"gasLimit":{
"type":"BigNumber",
"hex":"0x0f4240"
},
"to":"0x6454C9d69a4721feBA60e26A367bD4D56196Ee7c",
"value":{
"type":"BigNumber",
"hex":"0x00"
},
"nonce":14,
"data":"0x6fee07e00000000000000000000000000101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000c350000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000205555555555555555555555555555555555555555555555555555555555555555",
"r":"0x3b214ae35181aaf5542f970325ef36ca881db2fc1b145524534a4fb6885b05d7",
"s":"0xdf4fd847db5d5a246f4cedd2dee14e15e70ff469a7d88cdc86efa7c6d61d7cad",
"v":62709,
"creates":null,
"l1BlockNumber":"1",
"l1TxOrigin":"0x3333333333333333333333333333333333333333",
"rawTransaction":"0x420420",
"signatureHashType":"0",
"queueOrigin":"sequencer",
"chainId":31337
}
]
}
]
`)
/* External Imports */
import { defaultAccounts } from 'ethereum-waffle'
export const FORCE_INCLUSION_PERIOD_SECONDS = 600
export const DEFAULT_ACCOUNTS = defaultAccounts
export const DEFAULT_ACCOUNTS_HARDHAT = defaultAccounts.map((account) => {
return {
balance: account.balance,
privateKey: account.secretKey,
}
})
export const OVM_TX_GAS_LIMIT = 10_000_000
export const RUN_OVM_TEST_GAS = 20_000_000
/* External Imports */
import { keccak256 } from 'ethers/lib/utils'
export const DUMMY_BYTECODE = '0x123412341234'
export const DUMMY_BYTECODE_BYTELEN = 6
export const DUMMY_BYTECODE_HASH = keccak256(DUMMY_BYTECODE)
/* External Imports */
import { ethers } from 'ethers'
export const DUMMY_BYTES32: string[] = Array.from(
{
length: 10,
},
(_, i) => {
return ethers.utils.keccak256(`0x0${i}`)
}
)
export * from './bytes32'
export * from './bytecode'
export * from './dummy'
export * from './constants'
export * from './resolver'
/* External Imports */
import { ethers } from 'hardhat'
import { Contract } from 'ethers'
import { getContractFactory as ctFactory } from 'old-contracts'
export const getContractFactory = async (contract: string) =>
ctFactory(contract, (await ethers.getSigners())[0])
export const setProxyTarget = async (
AddressManager: Contract,
name: string,
target: Contract
): Promise<void> => {
const SimpleProxy: Contract = await (
await getContractFactory('Helper_SimpleProxy')
).deploy()
await SimpleProxy.setTarget(target.address)
await AddressManager.setAddress(name, SimpleProxy.address)
}
export const makeAddressManager = async (): Promise<Contract> => {
return (await getContractFactory('Lib_AddressManager')).deploy()
}
/* External Imports */
import chai = require('chai')
import sinonChai from 'sinon-chai'
import Mocha from 'mocha'
const should = chai.should()
const expect = chai.expect
chai.use(sinonChai)
export { should, expect, chai, Mocha }
import { ethers, BigNumber, Signer } from 'ethers'
import {
TransactionReceipt,
TransactionResponse,
} from '@ethersproject/abstract-provider'
import { expect } from '../setup'
import { submitTransactionWithYNATM } from '../../src/utils/tx-submission'
import { ResubmissionConfig } from '../../src'
const nullFunction = () => undefined
const nullHooks = {
beforeSendTransaction: nullFunction,
onTransactionResponse: nullFunction,
}
describe('submitTransactionWithYNATM', async () => {
it('calls sendTransaction, waitForTransaction, and hooks with correct inputs', async () => {
const called = {
sendTransaction: false,
waitForTransaction: false,
beforeSendTransaction: false,
onTransactionResponse: false,
}
const dummyHash = 'dummy hash'
const numConfirmations = 3
const tx = {
data: 'we here though',
} as ethers.PopulatedTransaction
const sendTransaction = async (
_tx: ethers.PopulatedTransaction
): Promise<TransactionResponse> => {
called.sendTransaction = true
expect(_tx.data).to.equal(tx.data)
return {
hash: dummyHash,
} as TransactionResponse
}
const waitForTransaction = async (
hash: string,
_numConfirmations: number
): Promise<TransactionReceipt> => {
called.waitForTransaction = true
expect(hash).to.equal(dummyHash)
expect(_numConfirmations).to.equal(numConfirmations)
return {
to: '',
from: '',
status: 1,
} as TransactionReceipt
}
const signer = {
getGasPrice: async () => ethers.BigNumber.from(0),
sendTransaction,
provider: {
waitForTransaction,
},
} as Signer
const hooks = {
beforeSendTransaction: (submittingTx: ethers.PopulatedTransaction) => {
called.beforeSendTransaction = true
expect(submittingTx.data).to.equal(tx.data)
},
onTransactionResponse: (txResponse: TransactionResponse) => {
called.onTransactionResponse = true
expect(txResponse.hash).to.equal(dummyHash)
},
}
const config: ResubmissionConfig = {
resubmissionTimeout: 1000,
minGasPriceInGwei: 0,
maxGasPriceInGwei: 0,
gasRetryIncrement: 1,
}
await submitTransactionWithYNATM(
tx,
signer,
config,
numConfirmations,
hooks
)
expect(called.sendTransaction).to.be.true
expect(called.waitForTransaction).to.be.true
expect(called.beforeSendTransaction).to.be.true
expect(called.onTransactionResponse).to.be.true
})
it('repeatedly increases the gas limit of the transaction when wait takes too long', async () => {
// Make transactions take longer to be included
// than our resubmission timeout
const resubmissionTimeout = 100
const txReceiptDelay = resubmissionTimeout * 3
let lastGasPrice = BigNumber.from(0)
// Create a transaction which has a gas price that we will watch increment
const tx = {
gasPrice: lastGasPrice.add(1),
data: 'hello world!',
} as ethers.PopulatedTransaction
const sendTransaction = async (
_tx: ethers.PopulatedTransaction
): Promise<TransactionResponse> => {
// Ensure the gas price is always increasing
expect(_tx.gasPrice > lastGasPrice).to.be.true
lastGasPrice = _tx.gasPrice
return {
hash: 'dummy hash',
} as TransactionResponse
}
const waitForTransaction = async (): Promise<TransactionReceipt> => {
await new Promise((r) => setTimeout(r, txReceiptDelay))
return {} as TransactionReceipt
}
const signer = {
getGasPrice: async () => ethers.BigNumber.from(0),
sendTransaction,
provider: {
waitForTransaction: waitForTransaction as any,
},
} as Signer
const config: ResubmissionConfig = {
resubmissionTimeout,
minGasPriceInGwei: 0,
maxGasPriceInGwei: 1000,
gasRetryIncrement: 1,
}
await submitTransactionWithYNATM(tx, signer, config, 0, nullHooks)
})
it('should immediately reject if a nonce error is encountered', async () => {
const tx = {
gasPrice: BigNumber.from(1),
data: 'hello world!',
} as ethers.PopulatedTransaction
let txCount = 0
const waitForTransaction = async (): Promise<TransactionReceipt> => {
return {} as TransactionReceipt
}
const sendTransaction = async () => {
txCount++
throw new Error('Transaction nonce is too low.')
}
const signer = {
getGasPrice: async () => BigNumber.from(1),
sendTransaction: sendTransaction as any,
provider: {
waitForTransaction: waitForTransaction as any,
},
} as Signer
const config: ResubmissionConfig = {
resubmissionTimeout: 100,
minGasPriceInGwei: 0,
maxGasPriceInGwei: 1000,
gasRetryIncrement: 1,
}
try {
await submitTransactionWithYNATM(tx, signer, config, 0, nullHooks)
} catch (e) {
expect(txCount).to.equal(1)
return
}
expect.fail('Expected an error.')
})
})
{
"extends": "../../tsconfig.build.json",
"compilerOptions": {
"outDir": "./dist"
},
"include": [
"src/**/*"
]
}
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"resolveJsonModule": true
}
}
......@@ -6,5 +6,4 @@ ignores: [
"prettier-plugin-solidity",
"solhint-plugin-prettier",
"ts-generator",
"yargs",
]
......@@ -74,13 +74,13 @@ contract L2StandardBridge is IL2ERC20Bridge, CrossDomainEnabled {
}
/**
* @dev Performs the logic for deposits by storing the token and informing the L2 token Gateway
* of the deposit.
* @param _l2Token Address of L2 token where withdrawal was initiated.
* @param _from Account to pull the deposit from on L2.
* @dev Performs the logic for withdrawals by burning the token and informing
* the L1 token Gateway of the withdrawal.
* @param _l2Token Address of L2 token where withdrawal is initiated.
* @param _from Account to pull the withdrawal from on L2.
* @param _to Account to give the withdrawal to on L1.
* @param _amount Amount of the token to withdraw.
* param _l1Gas Unused, but included for potential forward compatibility considerations.
* @param _l1Gas Unused, but included for potential forward compatibility considerations.
* @param _data Optional data to forward to L1. This data is provided
* solely as a convenience for external contracts. Aside from enforcing a maximum
* length, these contracts provide no guarantees about its content.
......
......@@ -9,6 +9,7 @@ import { Lib_Buffer } from "../../libraries/utils/Lib_Buffer.sol";
*/
contract TestLib_Buffer {
using Lib_Buffer for Lib_Buffer.Buffer;
using Lib_Buffer for Lib_Buffer.BufferContext;
Lib_Buffer.Buffer internal buf;
......@@ -16,6 +17,10 @@ contract TestLib_Buffer {
buf.push(_value, _extraData);
}
function push(bytes32 _value) public {
buf.push(_value);
}
function get(uint256 _index) public view returns (bytes32) {
return buf.get(_index);
}
......@@ -39,4 +44,16 @@ contract TestLib_Buffer {
function getExtraData() public view returns (bytes27) {
return buf.getExtraData();
}
function getContext() public view returns (Lib_Buffer.BufferContext memory) {
return buf.getContext();
}
function setContext(uint40 _index, bytes27 _extraData) public {
Lib_Buffer.BufferContext memory _ctx = Lib_Buffer.BufferContext({
length: _index,
extraData: _extraData
});
return buf.setContext(_ctx);
}
}
......@@ -20,6 +20,7 @@ import './tasks/validate-address-dictator'
import './tasks/validate-chugsplash-dictator'
import './tasks/whitelist'
import './tasks/withdraw-fees'
import './tasks/fetch-batches'
import 'hardhat-gas-reporter'
import '@primitivefi/hardhat-dodoc'
import 'hardhat-output-validator'
......
......@@ -119,8 +119,7 @@
"ts-generator": "0.0.8",
"ts-node": "^10.0.0",
"typechain": "^6.0.2",
"typescript": "^4.3.5",
"yargs": "^16.2.0"
"typescript": "^4.3.5"
},
"peerDependencies": {
"ethers": "^5"
......
......@@ -24,3 +24,9 @@ export const predeploys = {
// We're also putting WETH9 at the old OVM_ETH address.
WETH9: '0x4200000000000000000000000000000000000006',
}
export const futurePredeploys = {
// System addresses, for use later
System0: '0x4200000000000000000000000000000000000042',
System1: '0x4200000000000000000000000000000000000014',
}
import { ethers } from 'ethers'
import { task } from 'hardhat/config'
import * as types from 'hardhat/internal/core/params/argumentTypes'
import { SequencerBatch } from '@eth-optimism/core-utils'
import { names } from '../src/address-names'
import { getContractFromArtifact } from '../src/deploy-utils'
// Need to export env vars
// CONTRACTS_TARGET_NETWORK
// CONTRACTS_DEPLOYER_KEY
// CONTRACTS_RPC_URL
task('fetch-batches')
.addOptionalParam(
'contractsRpcUrl',
'Ethereum HTTP Endpoint',
process.env.CONTRACTS_RPC_URL || 'http://127.0.0.1:8545',
types.string
)
.addOptionalParam('start', 'Start block height', 0, types.int)
.addOptionalParam('end', 'End block height', undefined, types.int)
.setAction(async (args, hre) => {
const provider = new ethers.providers.StaticJsonRpcProvider(
args.contractsRpcUrl
)
let CanonicalTransactionChain = await getContractFromArtifact(
hre,
names.managed.contracts.CanonicalTransactionChain
)
CanonicalTransactionChain = CanonicalTransactionChain.connect(provider)
const start = args.start
let end = args.end
if (!end) {
end = await provider.getBlockNumber()
}
const batches = []
for (let i = start; i <= end; i += 2001) {
const tip = Math.min(i + 2000, end)
console.error(`Querying events ${i}-${tip}`)
const events = await CanonicalTransactionChain.queryFilter(
CanonicalTransactionChain.filters.SequencerBatchAppended(),
i,
tip
)
for (const event of events) {
const tx = await provider.getTransaction(event.transactionHash)
const batch = (SequencerBatch as any).fromHex(tx.data)
batches.push(batch.toJSON())
}
}
console.log(JSON.stringify(batches, null, 2))
})
......@@ -49,7 +49,7 @@ task('set-l2-gasprice')
const GasPriceOracle = new ethers.Contract(
predeploys.OVM_GasPriceOracle,
GasPriceOracleArtifact.abi,
provider
signer
)
const addr = await signer.getAddress()
......
/* External Imports */
import { ethers } from 'hardhat'
import { Contract, Signer, ContractFactory } from 'ethers'
/* Internal Imports */
import { expect } from '../../../setup'
import { NON_ZERO_ADDRESS } from '../../../helpers'
describe('AddressDictator', () => {
let signer: Signer
let otherSigner: Signer
let signerAddress: string
let Factory__AddressDictator: ContractFactory
let Factory__Lib_AddressManager: ContractFactory
before(async () => {
;[signer, otherSigner] = await ethers.getSigners()
Factory__AddressDictator = await ethers.getContractFactory(
'AddressDictator'
)
Factory__Lib_AddressManager = await ethers.getContractFactory(
'Lib_AddressManager'
)
signerAddress = await signer.getAddress()
})
let AddressDictator: Contract
let Lib_AddressManager: Contract
beforeEach(async () => {
Lib_AddressManager = await Factory__Lib_AddressManager.connect(
signer
).deploy()
AddressDictator = await Factory__AddressDictator.connect(signer).deploy(
Lib_AddressManager.address,
signerAddress,
['addr1'],
[NON_ZERO_ADDRESS]
)
Lib_AddressManager.transferOwnership(AddressDictator.address)
})
describe('initialize', () => {
it('should revert when providing wrong arguments', async () => {
await expect(
Factory__AddressDictator.connect(signer).deploy(
Lib_AddressManager.address,
signerAddress,
['addr1', 'addr2'],
[NON_ZERO_ADDRESS]
)
).to.be.revertedWith(
'AddressDictator: Must provide an equal number of names and addresses.'
)
})
})
describe('setAddresses', async () => {
it('should change the addresses associated with a name', async () => {
await AddressDictator.setAddresses()
expect(await Lib_AddressManager.getAddress('addr1')).to.be.equal(
NON_ZERO_ADDRESS
)
})
})
describe('getNamedAddresses', () => {
it('should return all the addresses and their names', async () => {
expect(await AddressDictator.getNamedAddresses()).to.be.deep.equal([
['addr1', NON_ZERO_ADDRESS],
])
})
})
describe('returnOwnership', () => {
it('should transfer contract ownership to finalOwner', async () => {
await expect(AddressDictator.connect(signer).returnOwnership()).to.not.be
.reverted
})
it('should revert when called by non-owner', async () => {
await expect(
AddressDictator.connect(otherSigner).returnOwnership()
).to.be.revertedWith('AddressDictator: only callable by finalOwner')
})
})
})
/* External Imports */
import { ethers } from 'hardhat'
import { Contract, Signer, ContractFactory } from 'ethers'
/* Internal Imports */
import { expect } from '../../../setup'
describe('ChugSplashDictator', () => {
let signer: Signer
let otherSigner: Signer
let signerAddress: string
let Factory__L1ChugSplashProxy: ContractFactory
let Factory__ChugSplashDictator: ContractFactory
before(async () => {
;[signer, otherSigner] = await ethers.getSigners()
Factory__L1ChugSplashProxy = await ethers.getContractFactory(
'L1ChugSplashProxy'
)
Factory__ChugSplashDictator = await ethers.getContractFactory(
'ChugSplashDictator'
)
signerAddress = await signer.getAddress()
})
let L1ChugSplashProxy: Contract
let ChugSplashDictator: Contract
beforeEach(async () => {
L1ChugSplashProxy = await Factory__L1ChugSplashProxy.connect(signer).deploy(
signerAddress
)
ChugSplashDictator = await Factory__ChugSplashDictator.connect(
signer
).deploy(
L1ChugSplashProxy.address,
signerAddress,
ethers.utils.keccak256('0x1111'),
ethers.utils.keccak256('0x1234'),
ethers.utils.keccak256('0x5678'),
ethers.utils.keccak256('0x1234'),
ethers.utils.keccak256('0x1234')
)
await L1ChugSplashProxy.connect(signer).setOwner(ChugSplashDictator.address)
})
describe('doActions', () => {
it('should revert when sent wrong code', async () => {
await expect(ChugSplashDictator.doActions('0x2222')).to.be.revertedWith(
'ChugSplashDictator: Incorrect code hash.'
)
})
it('should set the proxy code, storage & owner', async () => {
await expect(ChugSplashDictator.connect(signer).doActions('0x1111')).to
.not.be.reverted
})
})
describe('returnOwnership', () => {
it('should transfer contractc ownership to finalOwner', async () => {
await expect(ChugSplashDictator.connect(signer).returnOwnership()).to.not
.be.reverted
})
it('should revert when called by non-owner', async () => {
await expect(
ChugSplashDictator.connect(otherSigner).returnOwnership()
).to.be.revertedWith('ChugSplashDictator: only callable by finalOwner')
})
})
})
......@@ -2,6 +2,7 @@
import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract, BigNumber } from 'ethers'
import { smockit, MockContract } from '@eth-optimism/smock'
import { smock, MockContractFactory } from '@defi-wonderland/smock'
import {
remove0x,
toHexString,
......@@ -347,6 +348,28 @@ describe('L1CrossDomainMessenger', () => {
})
})
describe('xDomainMessageSender', () => {
let Mock__Factory__L1CrossDomainMessenger: MockContractFactory<ContractFactory>
let Mock__L1CrossDomainMessenger
before(async () => {
Mock__Factory__L1CrossDomainMessenger = await smock.mock(
'L1CrossDomainMessenger'
)
Mock__L1CrossDomainMessenger =
await Mock__Factory__L1CrossDomainMessenger.deploy()
})
it('should return the xDomainMsgSender address', async () => {
await Mock__L1CrossDomainMessenger.setVariable(
'xDomainMsgSender',
'0x0000000000000000000000000000000000000000'
)
expect(
await Mock__L1CrossDomainMessenger.xDomainMessageSender()
).to.equal('0x0000000000000000000000000000000000000000')
})
})
const generateMockRelayMessageProof = async (
target: string,
sender: string,
......
......@@ -88,6 +88,17 @@ describe('L1StandardBridge', () => {
})
})
describe('receive', () => {
it('should send an amount of ETH to the callers balance on L2', async () => {
await expect(
alice.sendTransaction({
to: L1StandardBridge.address,
data: '0x',
})
).to.not.be.reverted
})
})
describe('ETH deposits', () => {
const depositAmount = 1_000
......@@ -564,4 +575,18 @@ describe('L1StandardBridge', () => {
)
})
})
describe('donateETH', () => {
it('it should just call the function', async () => {
await expect(L1StandardBridge.donateETH()).to.not.be.reverted
})
it('should send ETH to the contract account', async () => {
await expect(
L1StandardBridge.donateETH({
value: 100,
})
).to.not.be.reverted
})
})
})
......@@ -218,6 +218,18 @@ describe('CanonicalTransactionChain', () => {
).to.be.revertedWith('Insufficient gas for L2 rate limiting burn.')
})
it('should burn L1 gas when L2 gas limit is high', async () => {
const _enqueueL2GasPrepaid =
await CanonicalTransactionChain.enqueueL2GasPrepaid()
const data = '0x' + '12'.repeat(1234)
// Create a tx with high L2 gas limit
const l2GasLimit = 4 * _enqueueL2GasPrepaid
await expect(CanonicalTransactionChain.enqueue(target, l2GasLimit, data))
.to.not.be.reverted
})
describe('with valid input parameters', () => {
it('should emit a TransactionEnqueued event', async () => {
const timestamp = (await getEthTime(ethers.provider)) + 100
......
/* External Imports */
import { ethers } from 'hardhat'
import { Contract, Signer, ContractFactory } from 'ethers'
/* Internal Imports */
import { expect } from '../../../setup'
import { makeAddressManager, NON_NULL_BYTES32 } from '../../../helpers'
describe('ChainStorageContainer', () => {
let sequencer: Signer
let otherSigner: Signer
let signer: Signer
let signerAddress: string
let AddressManager: Contract
let Factory__ChainStorageContainer: ContractFactory
before(async () => {
;[sequencer, otherSigner, signer] = await ethers.getSigners()
signerAddress = await otherSigner.getAddress()
AddressManager = await makeAddressManager()
await AddressManager.setAddress(
'OVM_Sequencer',
await sequencer.getAddress()
)
Factory__ChainStorageContainer = await ethers.getContractFactory(
'ChainStorageContainer'
)
})
let ChainStorageContainer: Contract
beforeEach(async () => {
ChainStorageContainer = await Factory__ChainStorageContainer.connect(
otherSigner
).deploy(AddressManager.address, signerAddress)
await AddressManager.setAddress(
'ChainStorageContainer',
ChainStorageContainer.address
)
await AddressManager.setAddress(signerAddress, signerAddress)
})
describe('push', () => {
for (const len of [1, 2, 4, 8, 32]) {
it(`it should be able to add ${len} element(s) to the array`, async () => {
for (let i = 0; i < len; i++) {
await expect(
ChainStorageContainer.connect(otherSigner)['push(bytes32)'](
NON_NULL_BYTES32
)
).to.not.be.reverted
}
})
}
})
describe('setGlobalMetadata', () => {
it('should modify the extra data', async () => {
const globalMetaData = `0x${'11'.repeat(27)}`
await ChainStorageContainer.connect(otherSigner).setGlobalMetadata(
globalMetaData
)
expect(await ChainStorageContainer.getGlobalMetadata()).to.equal(
globalMetaData
)
})
})
describe('deleteElementsAfterInclusive', () => {
it('should revert when the array is empty', async () => {
await expect(
ChainStorageContainer.connect(otherSigner)[
'deleteElementsAfterInclusive(uint256)'
](0)
).to.be.reverted
})
it('should revert when called by non-owner', async () => {
await expect(
ChainStorageContainer.connect(signer)[
'deleteElementsAfterInclusive(uint256)'
](0)
).to.be.revertedWith(
'ChainStorageContainer: Function can only be called by the owner.'
)
})
for (const len of [1, 2, 4, 8, 32]) {
describe(`when the array has ${len} element(s)`, () => {
const values = []
beforeEach(async () => {
for (let i = 0; i < len; i++) {
const value = NON_NULL_BYTES32
values.push(value)
await ChainStorageContainer.connect(otherSigner)['push(bytes32)'](
value
)
}
})
for (let i = len - 1; i > 0; i -= Math.max(1, len / 4)) {
it(`should be able to delete everything after and including the ${i}th/st/rd/whatever element`, async () => {
await expect(
ChainStorageContainer.connect(otherSigner)[
'deleteElementsAfterInclusive(uint256)'
](i)
).to.not.be.reverted
expect(await ChainStorageContainer.length()).to.equal(i)
await expect(ChainStorageContainer.get(i)).to.be.reverted
})
}
})
}
})
})
......@@ -409,17 +409,97 @@ describe('StateCommitmentChain', () => {
})
})
describe('verifyElement()', () => {
describe('getLastSequencerTimestamp', () => {
describe('when no batches have been inserted', () => {
it('should return zero', async () => {
expect(await StateCommitmentChain.getLastSequencerTimestamp()).to.equal(
0
)
})
})
describe('when one batch element has been inserted', () => {
let timestamp
beforeEach(async () => {
const batch = [NON_NULL_BYTES32]
Mock__CanonicalTransactionChain.smocked.getTotalElements.will.return.with(
batch.length
)
await StateCommitmentChain.appendStateBatch(batch, 0)
timestamp = await getEthTime(ethers.provider)
})
it('should return the number of inserted batch elements', async () => {
expect(await StateCommitmentChain.getLastSequencerTimestamp()).to.equal(
timestamp
)
})
})
})
describe('verifyStateCommitment()', () => {
const batch = [NON_NULL_BYTES32]
const batchHeader = {
batchIndex: 0,
batchRoot: NON_NULL_BYTES32,
batchSize: 1,
prevTotalElements: 0,
extraData: ethers.constants.HashZero,
}
const inclusionProof = {
index: 0,
siblings: [],
}
const element = NON_NULL_BYTES32
before(async () => {
Mock__BondManager.smocked.isCollateralized.will.return.with(true)
})
beforeEach(async () => {
Mock__CanonicalTransactionChain.smocked.getTotalElements.will.return.with(
batch.length
)
await StateCommitmentChain.appendStateBatch(batch, 0)
batchHeader.extraData = ethers.utils.defaultAbiCoder.encode(
['uint256', 'address'],
[await getEthTime(ethers.provider), await sequencer.getAddress()]
)
})
it('should revert when given an invalid batch header', async () => {
// TODO
await expect(
StateCommitmentChain.verifyStateCommitment(
element,
{
...batchHeader,
extraData: '0x' + '22'.repeat(32),
},
inclusionProof
)
).to.be.revertedWith('Invalid batch header.')
})
it('should revert when given an invalid inclusion proof', async () => {
// TODO
await expect(
StateCommitmentChain.verifyStateCommitment(
ethers.constants.HashZero,
batchHeader,
inclusionProof
)
).to.be.revertedWith('Invalid inclusion proof.')
})
it('should return true when given a valid proof', async () => {
// TODO
expect(
await StateCommitmentChain.verifyStateCommitment(
element,
batchHeader,
inclusionProof
)
).to.equal(true)
})
})
})
......@@ -3,6 +3,7 @@ import hre, { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers'
import { smockit, MockContract } from '@eth-optimism/smock'
import { applyL1ToL2Alias } from '@eth-optimism/core-utils'
import { smock, MockContractFactory } from '@defi-wonderland/smock'
/* Internal Imports */
import { expect } from '../../../setup'
......@@ -67,6 +68,30 @@ describe('L2CrossDomainMessenger', () => {
)
})
describe('xDomainMessageSender', () => {
let Mock__Factory__L2CrossDomainMessenger: MockContractFactory<ContractFactory>
let Mock__L2CrossDomainMessenger
before(async () => {
Mock__Factory__L2CrossDomainMessenger = await smock.mock(
'L2CrossDomainMessenger'
)
Mock__L2CrossDomainMessenger =
await Mock__Factory__L2CrossDomainMessenger.deploy(
Mock__L1CrossDomainMessenger.address
)
})
it('should return the xDomainMsgSender address', async () => {
await Mock__L2CrossDomainMessenger.setVariable(
'xDomainMsgSender',
'0x0000000000000000000000000000000000000000'
)
expect(
await Mock__L2CrossDomainMessenger.xDomainMessageSender()
).to.equal('0x0000000000000000000000000000000000000000')
})
})
describe('sendMessage', () => {
const target = NON_ZERO_ADDRESS
const message = NON_NULL_BYTES32
......
......@@ -7,6 +7,7 @@ import {
smoddit,
ModifiableContract,
} from '@eth-optimism/smock'
import { smock } from '@defi-wonderland/smock'
/* Internal Imports */
import { expect } from '../../../setup'
......@@ -20,6 +21,7 @@ const DUMMY_L1BRIDGE_ADDRESS: string =
'0x1234123412341234123412341234123412341234'
const DUMMY_L1TOKEN_ADDRESS: string =
'0x2234223412342234223422342234223422342234'
const OVM_ETH_ADDRESS: string = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000'
describe('L2StandardBridge', () => {
let alice: Signer
......@@ -182,6 +184,15 @@ describe('L2StandardBridge', () => {
describe('withdrawals', () => {
const withdrawAmount = 1_000
let SmoddedL2Token: ModifiableContract
let Fake__OVM_ETH
before(async () => {
Fake__OVM_ETH = await smock.fake('OVM_ETH', {
address: OVM_ETH_ADDRESS,
})
})
beforeEach(async () => {
// Deploy a smodded gateway so we can give some balances to withdraw
SmoddedL2Token = await (
......@@ -203,6 +214,35 @@ describe('L2StandardBridge', () => {
})
})
it('withdraw() withdraws and sends the correct withdrawal message for OVM_ETH', async () => {
await L2StandardBridge.withdraw(
Fake__OVM_ETH.address,
0,
0,
NON_NULL_BYTES32
)
const withdrawalCallToMessenger =
Mock__L2CrossDomainMessenger.smocked.sendMessage.calls[0]
// Assert the correct cross-chain call was sent:
// Message should be sent to the L1L1StandardBridge on L1
expect(withdrawalCallToMessenger._target).to.equal(DUMMY_L1BRIDGE_ADDRESS)
// Message data should be a call telling the L1StandardBridge to finalize the withdrawal
expect(withdrawalCallToMessenger._message).to.equal(
Factory__L1StandardBridge.interface.encodeFunctionData(
'finalizeETHWithdrawal',
[
await alice.getAddress(),
await alice.getAddress(),
0,
NON_NULL_BYTES32,
]
)
)
})
it('withdraw() burns and sends the correct withdrawal message', async () => {
await L2StandardBridge.withdraw(
SmoddedL2Token.address,
......
/* External Imports */
import { ethers } from 'hardhat'
import { Contract, Signer, ContractFactory } from 'ethers'
import {
smock,
MockContractFactory,
MockContract,
} from '@defi-wonderland/smock'
/* Internal Imports */
import { expect } from '../../../setup'
describe('WETH9', () => {
let signer: Signer
let otherSigner: Signer
let signerAddress: string
let otherSignerAddress: string
let Mock__Factory_WETH9: MockContractFactory<ContractFactory>
let Mock__WETH9: MockContract<Contract>
before(async () => {
;[signer, otherSigner] = await ethers.getSigners()
signerAddress = await signer.getAddress()
otherSignerAddress = await otherSigner.getAddress()
})
beforeEach(async () => {
Mock__Factory_WETH9 = await smock.mock('WETH9')
Mock__WETH9 = await Mock__Factory_WETH9.deploy()
})
describe('deposit', () => {
it('should create WETH with fallback function', async () => {
await expect(
signer.sendTransaction({
to: Mock__WETH9.address,
value: 200,
})
).to.not.be.reverted
expect(await Mock__WETH9.balanceOf(signerAddress)).to.be.equal(200)
})
it('should create WETH with deposit function', async () => {
await expect(Mock__WETH9.connect(signer).deposit({ value: 100 })).to.not
.be.reverted
expect(await Mock__WETH9.balanceOf(signerAddress)).to.be.equal(100)
})
})
describe('withdraw', () => {
it('should revert when withdraw amount is bigger than balance', async () => {
await expect(Mock__WETH9.connect(signer).withdraw(10000)).to.be.reverted
})
it('should withdraw to eth', async () => {
await Mock__WETH9.connect(signer).deposit({ value: 100 })
await expect(Mock__WETH9.connect(signer).withdraw(50)).to.not.be.reverted
expect(await Mock__WETH9.balanceOf(signerAddress)).to.be.equal(50)
})
})
describe('totalSupply', () => {
it('should return the totalSupply', async () => {
await expect(Mock__WETH9.totalSupply()).to.not.be.reverted
})
})
describe('transfer', () => {
it('should revert when sending more than deposited', async () => {
await Mock__WETH9.connect(signer).deposit({ value: 100 })
await expect(
Mock__WETH9.connect(signer).transfer(otherSignerAddress, 500)
).to.be.reverted
})
it('should transfer WETH to an other address', async () => {
await Mock__WETH9.connect(signer).deposit({ value: 100 })
await expect(Mock__WETH9.connect(signer).transfer(otherSignerAddress, 50))
.to.not.be.reverted
expect(await Mock__WETH9.balanceOf(signerAddress)).to.be.equal(50)
expect(await Mock__WETH9.balanceOf(otherSignerAddress)).to.be.equal(50)
})
})
describe('transferFrom', () => {
it('should revert when there is no allowance', async () => {
await Mock__WETH9.connect(signer).deposit({ value: 100 })
await expect(
Mock__WETH9.connect(otherSigner).transferFrom(
signerAddress,
otherSignerAddress,
50
)
).to.be.reverted
})
it('should transfer WETH to an other address when there is approvement', async () => {
await Mock__WETH9.connect(signer).deposit({ value: 100 })
await Mock__WETH9.connect(signer).approve(otherSignerAddress, 50)
await expect(
Mock__WETH9.connect(otherSigner).transferFrom(
signerAddress,
otherSignerAddress,
50
)
).to.not.be.reverted
})
})
})
/* External Imports */
import { ethers } from 'hardhat'
import { Contract } from 'ethers'
/* Internal Imports */
import '../../../setup'
import { Lib_OVMCodec_TEST_JSON } from '../../../data'
import { runJsonTest } from '../../../helpers'
import { expect } from '../../../setup'
import { NON_ZERO_ADDRESS } from '../../../helpers'
describe('Lib_OVMCodec', () => {
let Lib_OVMCodec: Contract
before(async () => {
Lib_OVMCodec = await (
await ethers.getContractFactory('TestLib_OVMCodec')
).deploy()
})
describe('hashTransaction', () => {
enum QueueOrigin {
SEQUENCER_QUEUE,
L1TOL2_QUEUE,
}
it('should return the hash of a transaction', async () => {
const tx = {
timestamp: 121212,
blockNumber: 10,
l1QueueOrigin: QueueOrigin.SEQUENCER_QUEUE,
l1TxOrigin: NON_ZERO_ADDRESS,
entrypoint: NON_ZERO_ADDRESS,
gasLimit: 100,
data: '0x1234',
}
describe.skip('Lib_OVMCodec', () => {
describe('JSON tests', () => {
runJsonTest('TestLib_OVMCodec', Lib_OVMCodec_TEST_JSON)
expect(await Lib_OVMCodec.hashTransaction(tx)).to.be.equal(
'0xf07818e2db63d0140e55c9e68cfaa030f9a2d0962f671d6b339edb2207633ebd'
)
})
})
})
......@@ -38,6 +38,16 @@ describe('Lib_RLPWriter', () => {
}
})
describe('writeBool', () => {
it(`should encode bool: true`, async () => {
expect(await Lib_RLPWriter.writeBool(true)).to.equal('0x01')
})
it(`should encode bool: false`, async () => {
expect(await Lib_RLPWriter.writeBool(false)).to.equal('0x80')
})
})
describe('Use of library with other memory-modifying operations', () => {
it('should allow creation of a contract beforehand and still work', async () => {
const randomAddress = '0x1234123412341234123412341234123412341234'
......
......@@ -115,4 +115,22 @@ describe('Lib_SecureMerkleTrie', () => {
})
}
})
describe('getSingleNodeRootHash', () => {
let generator: TrieTestGenerator
before(async () => {
generator = await TrieTestGenerator.fromRandom({
seed: `seed.get.${1}`,
nodeCount: 1,
secure: true,
})
})
it(`should get the root hash of a trie with a single node`, async () => {
const test = await generator.makeInclusionProofTest(0)
expect(
await Lib_SecureMerkleTrie.getSingleNodeRootHash(test.key, test.val)
).to.equal(test.root)
})
})
})
......@@ -12,12 +12,12 @@ describe('Lib_Buffer', () => {
Lib_Buffer = await Factory__Lib_Buffer.deploy()
})
describe('push', () => {
describe('push(bytes32,bytes27)', () => {
for (const len of [1, 2, 4, 8, 32]) {
it(`it should be able to add ${len} element(s) to the array`, async () => {
for (let i = 0; i < len; i++) {
await expect(
Lib_Buffer.push(
Lib_Buffer['push(bytes32,bytes27)'](
ethers.utils.keccak256(`0x${i.toString(16).padStart(16, '0')}`),
`0x${'00'.repeat(27)}`
)
......@@ -27,6 +27,20 @@ describe('Lib_Buffer', () => {
}
})
describe('push(bytes32)', () => {
for (const len of [1, 2, 4, 8, 32]) {
it(`it should be able to add ${len} element(s) to the array`, async () => {
for (let i = 0; i < len; i++) {
await expect(
Lib_Buffer['push(bytes32)'](
ethers.utils.keccak256(`0x${i.toString(16).padStart(16, '0')}`)
)
).to.not.be.reverted
}
})
}
})
describe('get', () => {
for (const len of [1, 2, 4, 8, 32]) {
describe(`when the array has ${len} element(s)`, () => {
......@@ -37,7 +51,10 @@ describe('Lib_Buffer', () => {
`0x${i.toString(16).padStart(16, '0')}`
)
values.push(value)
await Lib_Buffer.push(value, `0x${'00'.repeat(27)}`)
await Lib_Buffer['push(bytes32,bytes27)'](
value,
`0x${'00'.repeat(27)}`
)
}
})
......@@ -68,7 +85,10 @@ describe('Lib_Buffer', () => {
`0x${i.toString(16).padStart(16, '0')}`
)
values.push(value)
await Lib_Buffer.push(value, `0x${'00'.repeat(27)}`)
await Lib_Buffer['push(bytes32,bytes27)'](
value,
`0x${'00'.repeat(27)}`
)
}
})
......@@ -86,20 +106,26 @@ describe('Lib_Buffer', () => {
it('should change if set by a call to push()', async () => {
const extraData = `0x${'11'.repeat(27)}`
await Lib_Buffer.push(ethers.utils.keccak256('0x00'), extraData)
await Lib_Buffer['push(bytes32,bytes27)'](
ethers.utils.keccak256('0x00'),
extraData
)
expect(await Lib_Buffer.getExtraData()).to.equal(extraData)
})
it('should change if set multiple times', async () => {
await Lib_Buffer.push(
await Lib_Buffer['push(bytes32,bytes27)'](
ethers.utils.keccak256('0x00'),
`0x${'11'.repeat(27)}`
)
const extraData = `0x${'22'.repeat(27)}`
await Lib_Buffer.push(ethers.utils.keccak256('0x00'), extraData)
await Lib_Buffer['push(bytes32,bytes27)'](
ethers.utils.keccak256('0x00'),
extraData
)
expect(await Lib_Buffer.getExtraData()).to.equal(extraData)
})
......@@ -140,7 +166,10 @@ describe('Lib_Buffer', () => {
`0x${i.toString(16).padStart(16, '0')}`
)
values.push(value)
await Lib_Buffer.push(value, `0x${'00'.repeat(27)}`)
await Lib_Buffer['push(bytes32,bytes27)'](
value,
`0x${'00'.repeat(27)}`
)
}
})
......@@ -172,4 +201,26 @@ describe('Lib_Buffer', () => {
})
}
})
describe('setContext', () => {
it('should modify the context', async () => {
const length = 20
const extraData = `0x${'11'.repeat(27)}`
const cntx = [length, extraData]
await Lib_Buffer.setContext(length, extraData)
expect(await Lib_Buffer.getContext()).to.eql(cntx)
})
it('should not modify the context', async () => {
const length = 0
const extraData = `0x${'00'.repeat(27)}`
const prevContext = await Lib_Buffer.getContext()
await Lib_Buffer.setContext(length, extraData)
expect(await Lib_Buffer.getContext()).to.eql(prevContext)
})
})
})
......@@ -3,6 +3,7 @@ import { ethers } from 'hardhat'
import { Contract, BigNumber } from 'ethers'
import { MerkleTree } from 'merkletreejs'
import { fromHexString, toHexString } from '@eth-optimism/core-utils'
import { smock, FakeContract } from '@defi-wonderland/smock'
/* Internal Imports */
import { expect } from '../../../setup'
......@@ -32,10 +33,15 @@ const fillDefaultHashes = (elements: string[]): string[] => {
describe('Lib_MerkleTree', () => {
let Lib_MerkleTree: Contract
let Fake__LibMerkleTree: FakeContract<Contract>
before(async () => {
Lib_MerkleTree = await (
await ethers.getContractFactory('TestLib_MerkleTree')
).deploy()
Fake__LibMerkleTree = await smock.fake(
await ethers.getContractFactory('TestLib_MerkleTree')
)
})
describe('getMerkleRoot', () => {
......@@ -78,6 +84,19 @@ describe('Lib_MerkleTree', () => {
})
}
})
describe('when odd number of elements is provided', () => {
it(`should generate the correct root when odd number of elements are provided`, async () => {
const elements = ['0x12', '0x34', '0x56'].map((value) =>
ethers.utils.keccak256(value)
)
Fake__LibMerkleTree.getMerkleRoot.returns()
// expect(await Fake__LibMerkleTree.getMerkleRoot(elements)).to.not.be.reverted
await expect(Lib_MerkleTree.getMerkleRoot(elements)).to.not.be.reverted
})
})
})
describe('verify', () => {
......@@ -99,6 +118,24 @@ describe('Lib_MerkleTree', () => {
})
})
describe('when total elements is zero', () => {
const totalLeaves = 0
it('should revert', async () => {
await expect(
Lib_MerkleTree.verify(
ethers.constants.HashZero,
ethers.constants.HashZero,
0,
[],
totalLeaves
)
).to.be.revertedWith(
'Lib_MerkleTree: Total leaves must be greater than zero.'
)
})
})
describe('when an index is out of bounds', () => {
const totalLeaves = 1
const index = 2
......
......@@ -3,4 +3,3 @@ export { tests as Lib_RLPReader_TEST_JSON } from './json/libraries/rlp/Lib_RLPRe
export { tests as Lib_Bytes32Utils_TEST_JSON } from './json/libraries/utils/Lib_Bytes32Utils.test.json'
export { tests as Lib_BytesUtils_TEST_JSON } from './json/libraries/utils/Lib_BytesUtils.test.json'
export { tests as Lib_MerkleTrie_TEST_JSON } from './json/libraries/trie/Lib_MerkleTrie.test.json'
export { tests as Lib_OVMCodec_TEST_JSON } from './json/libraries/codec/Lib_OVMCodec.test.json'
......@@ -35,7 +35,9 @@
"@ethersproject/abstract-provider": "^5.5.1",
"@ethersproject/bytes": "^5.5.0",
"@ethersproject/providers": "^5.5.3",
"@ethersproject/transactions": "^5.5.0",
"@ethersproject/web": "^5.5.1",
"bufio": "^1.0.7",
"chai": "^4.3.4",
"ethers": "^5.5.4"
},
......
declare module 'bufio' {
class BufferWriter {
public offset: number
constructor()
render(): Buffer
getSize(): number
seek(offset: number): this
destroy(): this
writeU8(n: number): this
writeU16(n: number): this
writeU16BE(n: number): this
writeU24(n: number): this
writeU24BE(n: number): this
writeU32(n: number): this
writeU32BE(n: number): this
writeU40(n: number): this
writeU40BE(n: number): this
writeU48(n: number): this
writeU48BE(n: number): this
writeU56(n: number): this
writeU56BE(n: number): this
writeU64(n: number): this
writeU64BE(n: number): this
writeBytes(b: Buffer): this
copy(value: number, start: number, end: number): this
}
class BufferReader {
constructor(data: Buffer, copy?: boolean)
getSize(): number
check(n: number): void
left(): number
seek(offset: number): this
start(): number
end(): number
destroy(): this
readU8(): number
readU16(): number
readU16BE(): number
readU24(): number
readU24BE(): number
readU32(): number
readU32BE(): number
readU40(): number
readU40BE(): number
readU48(): number
readU48BE(): number
readU56(): number
readU56BE(): number
readU64(): number
readU64BE(): number
readBytes(size: number, copy?: boolean): Buffer
}
class Struct {
constructor()
encode(extra?: object): Buffer
decode<T extends Struct>(data: Buffer, extra?: object): T
getSize(extra?: object): number
fromHex(s: string, extra?: object): this
toHex(): string
write(bw: BufferWriter, extra?: object): BufferWriter
read(br: BufferReader, extra?: object): this
static read<T extends Struct>(br: BufferReader, extra?: object): T
static decode<T extends Struct>(data: Buffer, extra?: object): T
static fromHex<T extends Struct>(s: string, extra?: object): T
}
}
import { BigNumber, ethers } from 'ethers'
import zlib from 'zlib'
import { add0x, remove0x, encodeHex } from '../common'
import { parse, serialize } from '@ethersproject/transactions'
import { ethers } from 'ethers'
import { Struct, BufferWriter, BufferReader } from 'bufio'
import { remove0x } from '../common'
export interface BatchContext {
numSequencedTransactions: number
......@@ -9,119 +13,420 @@ export interface BatchContext {
blockNumber: number
}
export enum BatchType {
LEGACY = -1,
ZLIB = 0,
}
export interface AppendSequencerBatchParams {
shouldStartAtElement: number // 5 bytes -- starts at batch
totalElementsToAppend: number // 3 bytes -- total_elements_to_append
contexts: BatchContext[] // total_elements[fixed_size[]]
transactions: string[] // total_size_bytes[],total_size_bytes[]
type?: BatchType
}
const APPEND_SEQUENCER_BATCH_METHOD_ID = 'appendSequencerBatch()'
const FOUR_BYTE_APPEND_SEQUENCER_BATCH = Buffer.from(
ethers.utils.id(APPEND_SEQUENCER_BATCH_METHOD_ID).slice(2, 10),
'hex'
)
// Legacy support
// This function returns the serialized batch
// without the 4 byte selector and without the
// 0x prefix
export const encodeAppendSequencerBatch = (
b: AppendSequencerBatchParams
): string => {
const encodeShouldStartAtElement = encodeHex(b.shouldStartAtElement, 10)
const encodedTotalElementsToAppend = encodeHex(b.totalElementsToAppend, 6)
const encodedContextsHeader = encodeHex(b.contexts.length, 6)
const encodedContexts =
encodedContextsHeader +
b.contexts.reduce((acc, cur) => acc + encodeBatchContext(cur), '')
const encodedTransactionData = b.transactions.reduce((acc, cur) => {
if (cur.length % 2 !== 0) {
for (const tx of b.transactions) {
if (tx.length % 2 !== 0) {
throw new Error('Unexpected uneven hex string value!')
}
const encodedTxDataHeader = remove0x(
BigNumber.from(remove0x(cur).length / 2).toHexString()
).padStart(6, '0')
return acc + encodedTxDataHeader + remove0x(cur)
}, '')
return (
encodeShouldStartAtElement +
encodedTotalElementsToAppend +
encodedContexts +
encodedTransactionData
)
}
const encodeBatchContext = (context: BatchContext): string => {
return (
encodeHex(context.numSequencedTransactions, 6) +
encodeHex(context.numSubsequentQueueTransactions, 6) +
encodeHex(context.timestamp, 10) +
encodeHex(context.blockNumber, 10)
)
}
const batch = sequencerBatch.encode(b)
const fnSelector = batch.slice(2, 10)
if (fnSelector !== FOUR_BYTE_APPEND_SEQUENCER_BATCH.toString('hex')) {
throw new Error(`Incorrect function signature`)
}
return batch.slice(10)
}
// Legacy support
// This function assumes there is no 4byte selector
// as part of the input data
export const decodeAppendSequencerBatch = (
b: string
): AppendSequencerBatchParams => {
b = remove0x(b)
const shouldStartAtElement = b.slice(0, 10)
const totalElementsToAppend = b.slice(10, 16)
const contextHeader = b.slice(16, 22)
const contextCount = parseInt(contextHeader, 16)
let offset = 22
const contexts = []
for (let i = 0; i < contextCount; i++) {
const numSequencedTransactions = b.slice(offset, offset + 6)
offset += 6
const numSubsequentQueueTransactions = b.slice(offset, offset + 6)
offset += 6
const timestamp = b.slice(offset, offset + 10)
offset += 10
const blockNumber = b.slice(offset, offset + 10)
offset += 10
contexts.push({
numSequencedTransactions: parseInt(numSequencedTransactions, 16),
numSubsequentQueueTransactions: parseInt(
numSubsequentQueueTransactions,
16
const calldata =
'0x' + FOUR_BYTE_APPEND_SEQUENCER_BATCH.toString('hex') + remove0x(b)
return sequencerBatch.decode(calldata)
}
// Legacy support
export const sequencerBatch = {
encode: (params: AppendSequencerBatchParams): string => {
const batch = new SequencerBatch({
shouldStartAtElement: params.shouldStartAtElement,
totalElementsToAppend: params.totalElementsToAppend,
contexts: params.contexts.map((c) => new Context(c)),
transactions: params.transactions.map((t) =>
BatchedTx.fromTransaction(t)
),
timestamp: parseInt(timestamp, 16),
blockNumber: parseInt(blockNumber, 16),
type: params.type,
})
}
return batch.toHex()
},
decode: (b: string): AppendSequencerBatchParams => {
const buf = Buffer.from(remove0x(b), 'hex')
const fnSelector = buf.slice(0, 4)
if (Buffer.compare(fnSelector, FOUR_BYTE_APPEND_SEQUENCER_BATCH) !== 0) {
throw new Error(`Incorrect function signature`)
}
const batch = SequencerBatch.decode<SequencerBatch>(buf)
const params: AppendSequencerBatchParams = {
shouldStartAtElement: batch.shouldStartAtElement,
totalElementsToAppend: batch.totalElementsToAppend,
contexts: batch.contexts.map((c) => ({
numSequencedTransactions: c.numSequencedTransactions,
numSubsequentQueueTransactions: c.numSubsequentQueueTransactions,
timestamp: c.timestamp,
blockNumber: c.blockNumber,
})),
transactions: batch.transactions.map((t) => t.toHexTransaction()),
type: batch.type,
}
return params
},
}
const transactions = []
for (const context of contexts) {
for (let i = 0; i < context.numSequencedTransactions; i++) {
const size = b.slice(offset, offset + 6)
offset += 6
const raw = b.slice(offset, offset + parseInt(size, 16) * 2)
transactions.push(add0x(raw))
offset += raw.length
export class Context extends Struct {
// 3 bytes
public numSequencedTransactions: number = 0
// 3 bytes
public numSubsequentQueueTransactions: number = 0
// 5 bytes
public timestamp: number = 0
// 5 bytes
public blockNumber: number = 0
constructor(options: Partial<Context> = {}) {
super()
if (typeof options.numSequencedTransactions === 'number') {
this.numSequencedTransactions = options.numSequencedTransactions
}
if (typeof options.numSubsequentQueueTransactions === 'number') {
this.numSubsequentQueueTransactions =
options.numSubsequentQueueTransactions
}
if (typeof options.timestamp === 'number') {
this.timestamp = options.timestamp
}
if (typeof options.blockNumber === 'number') {
this.blockNumber = options.blockNumber
}
}
getSize(): number {
return 16
}
return {
shouldStartAtElement: parseInt(shouldStartAtElement, 16),
totalElementsToAppend: parseInt(totalElementsToAppend, 16),
contexts,
transactions,
write(bw: BufferWriter): BufferWriter {
bw.writeU24BE(this.numSequencedTransactions)
bw.writeU24BE(this.numSubsequentQueueTransactions)
bw.writeU40BE(this.timestamp)
bw.writeU40BE(this.blockNumber)
return bw
}
read(br: BufferReader): this {
this.numSequencedTransactions = br.readU24BE()
this.numSubsequentQueueTransactions = br.readU24BE()
this.timestamp = br.readU40BE()
this.blockNumber = br.readU40BE()
return this
}
toJSON() {
return {
numSequencedTransactions: this.numSequencedTransactions,
numSubsequentQueueTransactions: this.numSubsequentQueueTransactions,
timestamp: this.timestamp,
blockNumber: this.blockNumber,
}
}
}
export const sequencerBatch = {
encode: (b: AppendSequencerBatchParams) => {
return (
ethers.utils.id(APPEND_SEQUENCER_BATCH_METHOD_ID).slice(0, 10) +
encodeAppendSequencerBatch(b)
// transaction
export class BatchedTx extends Struct {
// 3 bytes
public txSize: number
// rlp encoded transaction
public raw: Buffer
public tx: ethers.Transaction
constructor(tx?: ethers.Transaction) {
super()
this.tx = tx
}
getSize(): number {
if (this.raw && this.raw.length) {
return this.raw.length + 3
}
const tx = serialize(
{
nonce: this.tx.nonce,
gasPrice: this.tx.gasPrice,
gasLimit: this.tx.gasLimit,
to: this.tx.to,
value: this.tx.value,
data: this.tx.data,
},
{
v: this.tx.v,
r: this.tx.r,
s: this.tx.s,
}
)
},
decode: (b: string): AppendSequencerBatchParams => {
b = remove0x(b)
const functionSelector = b.slice(0, 8)
if (
functionSelector !==
ethers.utils.id(APPEND_SEQUENCER_BATCH_METHOD_ID).slice(2, 10)
) {
throw new Error('Incorrect function signature')
}
return decodeAppendSequencerBatch(b.slice(8))
},
// remove 0x prefix
this.raw = Buffer.from(remove0x(tx), 'hex')
return this.raw.length + 3
}
write(bw: BufferWriter): BufferWriter {
bw.writeU24BE(this.txSize)
bw.writeBytes(this.raw)
return bw
}
read(br: BufferReader): this {
this.txSize = br.readU24BE()
this.raw = br.readBytes(this.txSize)
return this
}
toTransaction(): ethers.Transaction {
if (this.tx) {
return this.tx
}
return parse(this.raw)
}
toHexTransaction(): string {
if (this.raw) {
return '0x' + this.raw.toString('hex')
}
return serialize(
{
nonce: this.tx.nonce,
gasPrice: this.tx.gasPrice,
gasLimit: this.tx.gasLimit,
to: this.tx.to,
value: this.tx.value,
data: this.tx.data,
},
{
v: this.tx.v,
r: this.tx.r,
s: this.tx.s,
}
)
}
toJSON() {
if (!this.tx) {
this.tx = parse(this.raw)
}
return {
nonce: this.tx.nonce,
gasPrice: this.tx.gasPrice.toString(),
gasLimit: this.tx.gasLimit.toString(),
to: this.tx.to,
value: this.tx.value.toString(),
data: this.tx.data,
v: this.tx.v,
r: this.tx.r,
s: this.tx.s,
chainId: this.tx.chainId,
hash: this.tx.hash,
from: this.tx.from,
}
}
// TODO: inconsistent API with toTransaction
// but unnecessary right now
// this should be fromHexTransaction
fromTransaction(tx: string): this {
this.raw = Buffer.from(remove0x(tx), 'hex')
this.txSize = this.raw.length
return this
}
fromHex(s: string, extra?: object): this {
const buffer = Buffer.from(remove0x(s), 'hex')
return this.decode(buffer, extra)
}
static fromTransaction(s: string) {
return new this().fromTransaction(s)
}
}
export class SequencerBatch extends Struct {
// 5 bytes
public shouldStartAtElement: number
// 3 bytes
public totalElementsToAppend: number
// 3 byte header for count, []Context
public contexts: Context[]
// []3 byte size, rlp encoded tx
public transactions: BatchedTx[]
// The batch type that determines how
// it is serialized
public type: BatchType
constructor(options: Partial<SequencerBatch> = {}) {
super()
this.contexts = []
this.transactions = []
if (typeof options.shouldStartAtElement === 'number') {
this.shouldStartAtElement = options.shouldStartAtElement
}
if (typeof options.totalElementsToAppend === 'number') {
this.totalElementsToAppend = options.totalElementsToAppend
}
if (Array.isArray(options.contexts)) {
this.contexts = options.contexts
}
if (Array.isArray(options.transactions)) {
this.transactions = options.transactions
}
if (typeof options.type === 'number') {
this.type = options.type
}
}
write(bw: BufferWriter): BufferWriter {
bw.writeBytes(FOUR_BYTE_APPEND_SEQUENCER_BATCH)
bw.writeU40BE(this.shouldStartAtElement)
bw.writeU24BE(this.totalElementsToAppend)
const contexts = this.contexts.slice()
if (this.type === BatchType.ZLIB) {
contexts.unshift(
new Context({
blockNumber: 0,
timestamp: 0,
numSequencedTransactions: 0,
numSubsequentQueueTransactions: 0,
})
)
}
bw.writeU24BE(contexts.length)
for (const context of contexts) {
context.write(bw)
}
if (this.type === BatchType.ZLIB) {
const writer = new BufferWriter()
for (const tx of this.transactions) {
tx.write(writer)
}
const compressed = zlib.deflateSync(writer.render())
bw.writeBytes(compressed)
} else {
// Legacy
for (const tx of this.transactions) {
tx.write(bw)
}
}
return bw
}
read(br: BufferReader): this {
const selector = br.readBytes(4)
if (Buffer.compare(selector, FOUR_BYTE_APPEND_SEQUENCER_BATCH) !== 0) {
br.seek(-4)
}
this.type = BatchType.LEGACY
this.shouldStartAtElement = br.readU40BE()
this.totalElementsToAppend = br.readU24BE()
const contexts = br.readU24BE()
for (let i = 0; i < contexts; i++) {
const context = Context.read<Context>(br)
this.contexts.push(context)
}
// handle typed batches
if (this.contexts.length > 0 && this.contexts[0].timestamp === 0) {
switch (this.contexts[0].blockNumber) {
case 0: {
this.type = BatchType.ZLIB
const bytes = br.readBytes(br.left())
const inflated = zlib.inflateSync(bytes)
br = new BufferReader(inflated)
// remove the dummy context
this.contexts = this.contexts.slice(1)
break
}
}
}
for (const context of this.contexts) {
for (let i = 0; i < context.numSequencedTransactions; i++) {
const tx = BatchedTx.read<BatchedTx>(br)
this.transactions.push(tx)
}
}
return this
}
getSize(): number {
if (this.type === BatchType.ZLIB) {
return -1
}
let size = 8 + 3 + 4
for (const context of this.contexts) {
size += context.getSize()
}
for (const tx of this.transactions) {
size += tx.getSize()
}
return size
}
fromHex(s: string, extra?: object): this {
const buffer = Buffer.from(remove0x(s), 'hex')
return this.decode(buffer, extra)
}
toHex(): string {
return '0x' + this.encode().toString('hex')
}
toJSON() {
return {
shouldStartAtElement: this.shouldStartAtElement,
totalElementsToAppend: this.totalElementsToAppend,
contexts: this.contexts.map((c) => c.toJSON()),
transactions: this.transactions.map((tx) => tx.toJSON()),
}
}
}
......@@ -7,23 +7,31 @@ import {
encodeAppendSequencerBatch,
decodeAppendSequencerBatch,
sequencerBatch,
BatchType,
SequencerBatch,
} from '../src'
describe('BatchEncoder', () => {
describe('BatchEncoder', function () {
this.timeout(10_000)
// eslint-disable-next-line @typescript-eslint/no-var-requires
const data = require('./fixtures/calldata.json')
describe('appendSequencerBatch', () => {
it('should work with the simple case', () => {
it('legacy: should work with the simple case', () => {
const batch = {
shouldStartAtElement: 0,
totalElementsToAppend: 0,
contexts: [],
transactions: [],
type: BatchType.LEGACY,
}
const encoded = encodeAppendSequencerBatch(batch)
const decoded = decodeAppendSequencerBatch(encoded)
expect(decoded).to.deep.equal(batch)
})
it('should work with more complex case', () => {
it('legacy: should work with more complex case', () => {
const batch = {
shouldStartAtElement: 10,
totalElementsToAppend: 1,
......@@ -36,19 +44,57 @@ describe('BatchEncoder', () => {
},
],
transactions: ['0x45423400000011', '0x45423400000012'],
type: BatchType.LEGACY,
}
const encoded = encodeAppendSequencerBatch(batch)
const decoded = decodeAppendSequencerBatch(encoded)
expect(decoded).to.deep.equal(batch)
})
it('should work with mainnet calldata', () => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const data = require('./fixtures/appendSequencerBatch.json')
for (const calldata of data.calldata) {
const decoded = sequencerBatch.decode(calldata)
const encoded = sequencerBatch.encode(decoded)
expect(encoded).to.equal(calldata)
describe('mainnet data', () => {
for (const [hash, calldata] of Object.entries(data)) {
// Deserialize the raw calldata
const decoded = SequencerBatch.fromHex<SequencerBatch>(
calldata as string
)
it(`${hash}`, () => {
const encoded = decoded.toHex()
expect(encoded).to.deep.equal(calldata)
const batch = SequencerBatch.decode(decoded.encode())
expect(decoded).to.deep.eq(batch)
})
it(`${hash} (compressed)`, () => {
// Set the batch type to be zlib so that the batch
// is compressed
decoded.type = BatchType.ZLIB
// Encode a compressed batch
const encodedCompressed = decoded.encode()
// Decode a compressed batch
const decodedPostCompressed =
SequencerBatch.decode<SequencerBatch>(encodedCompressed)
// Expect that the batch type is detected
expect(decodedPostCompressed.type).to.eq(BatchType.ZLIB)
// Expect that the contexts match
expect(decoded.contexts).to.deep.equal(decodedPostCompressed.contexts)
for (const [i, tx] of decoded.transactions.entries()) {
const got = decodedPostCompressed.transactions[i]
expect(got).to.deep.eq(tx)
}
// Reserialize the batch as legacy
decodedPostCompressed.type = BatchType.LEGACY
// Ensure that the original data can be recovered
const encoded = decodedPostCompressed.toHex()
expect(encoded).to.deep.equal(calldata)
})
it(`${hash}: serialize txs`, () => {
for (const tx of decoded.transactions) {
tx.toTransaction()
}
})
}
})
......
{
"calldata": [
"0xd0f893440000011fcd00006500000400001900000000603e47620000b67be400000c00000200603e4aef0000b67c2d00003c00000000603e4c020000b67c4a00000200000000603e4f870000b67c9700006301d8914345750baf3939d57f2502c481154979e99b6049e5db5ae3d164376c2c543a5b75aabd10a41dd6305486b3b0a253b5cf247e0615370d7100c13f550d0ccd0189543f0000000000448700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00014300eabe2f5db7a62275de799b9aee03f55f01743563bbee17e722cd2efb0b1fbc1a246ba5723b499d41bd46f7c20c5eab5216e7b5adac96043c2d671de2b47588200089543f000000005ec2631e93a0fb06b5ec6d52c0a2d89a3f9672d6ba64bfa005ce000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000603e49750000000000000000000000000000000000000000000000000000000000000001534e58000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000013ee5cec085b3800000006301f1fc332c1562c12b6224e441f25a10d661e009adabce0a29e41896a85323bdaf6afac2438882c57aaa9e5f137e0365ecc27d27b3a73a108957c6ed4ee5bff8530189543f00000000000d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063017f8f5cdac49108109ad45484957fb00202937447785218f4674332cf42fc6f3e458acda19fa8bd5f14b3209dd235667096cadc57c2c65330b20c6f6db7806a470189543f0000000000318700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000143008d4e5a4defd67ce253e0232e10a592857b6d04b0be36abe1fe4cefa24987df124b96b5840e08d70c536c1a49e28d767289c388b6cfade572989e74a4f28b36660089543f000000005ec3631e93a0fb06b5ec6d52c0a2d89a3f9672d6ba64bfa005ce000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000603e49d40000000000000000000000000000000000000000000000000000000000000001534e580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000141371c5e0331000000006301bcd1432ea3305683f046d921354633808c8a9175fc9b248146b721f4261749d962c7bd3c3b7412d3f8714faf932cd9b44da135b645e8c1d2bb3c44ddbde358910189543f0000000000798700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630169a529e91ff78552b8289c3e42e42a4b69ed57e87e0d3a8739058b34ad1423eb68545b3c1a89683f0b110c66fe0771e0701d24ccdb31de1fef9307b9b3fb7d3401493e0000000000007a4a16a42407aa491564643e1dfc1fd50af29794efd294f093000063013172648acf5ea4cff6ea4893d30aa73983f67773a9ce7084a40e07dccd61cecd0833133b648af4df5ba168234583bc1214eca2687901e6ee71e23fbc42059ad00089543f00000000029c8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630195447e28a93f2dd4cbb2e00f71011ac7e938d280909be4a029054521bc348aae7c226ab01aaec7c2a00880e57ffb25197d05bf0be7c314a91383844824373c6c0089543f0000000000028700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063008070b45c42792670949cbae7e60278d2cd809c9347f26b976a3fc21b3757331904afa0cda74e27122ee02098e26504173be953a608fa01e02b124020e5639d54007a11ff0000000000bdd85eafa37734e4ad237c3a3443d64dc94ae998e7af086c7e000083019cedf37f33c099a4b81e0dc68c4b8d23a1150624d5cca9b938d4aa3c21aaef8c17ff865e8605486a0ecec67b685f1ae38f3c0158e19ed23a986363ccd2ccdb0f0189543f00000000002a8700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000006f05b59d3b20000000006301165d2c69ba74b1b1ee893303b60c48797f883efbcb2019762feb26a9257bea9e7019c339f05e311e79d4799f2bfde302b7c0f82b53ef5b5c9e837a6441e877e00189543f0000000000188700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301d539deb29e2e26049c9168eca08fff60ecf63daf9cedaf27a01d95f6f8b47624185f18f727d156b6ecb4cc3dc75ad29756a1f0447d5c3ab638fd734dd090c9120089543f0000000000258700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630125e4deda3a4e62f65113248e1b3b8a834e6c2d291cedfb71a21e544c3c3217d06ed5d15f56cace11db5e0120e0d5f67a09ca9084cde7728991827851e753b14d0189543f0000000000108700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000143009d2d26177c80858fdba6683fa5d095dacb9634e9deb842286ec79c17d85f0d7b70c617d7878dded6bf0a7ca7fc1371e8bced146f6e7d0da215f67e9e0f92940c0189543f000000005ec4631e93a0fb06b5ec6d52c0a2d89a3f9672d6ba64bfa005ce000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000603e4ae20000000000000000000000000000000000000000000000000000000000000001534e580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000143687087b5b40000000083018e22501ff1c962656f4bfbdab7479cc4ed8c91486c4b19150d03782d7f374467036d7cd2cdba105a4d916dfea70fb8cc2c60121f3baca85c6f2a48909c34d3780189543f00000000001a8700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000008ac7230489e8000000006301019753a47c457cd6cad98373b2092f11a1aea45e9bdd27ab1105609b4c8f87453121c425f6864b1b85910b4c0b82743287f61bc210a46de870b484c07b17cd310189543f0000000000038700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301d08818d883a43fc9f5f64623dca45128884b782fef41d513efa33d4d40139b467641ed4c17071fb9c2bd045023307ed0b23a3fcbbb2f71e81b37e3a7aba1c6560089543f0000000000058700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301c90fa6273070d9ae5af8eea1f687ae507410a258c7861e726d2cf943e00b456f2b3bf75861fe705039179eda8db6ea2781480afd89a98e133f1fe53e379e1eda0189543f00000000000b8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063018b5913d83c37ddc2564db791692072efd3dc8e5fe10584ed96451778789131a14efe012befc05c4ad479a46ea57c7e48f7f64dd7b7c175a48dd445215b2ffa170189543f0000000000418700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000083019d241891335c51ad2cc632ed85dd94ab8eee00da71cd812ea1c40fbcb9c441f56e2f5a7f83e9aa02ae614ee89a4abd10c4332be95a1335be44d5cff6356a903a0089543f0000000000148700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000013f306a2409fc0000000063018dc9bf7f0c853dc0884f0070f8a0413a83f0d9d4d8e5f6f1325deb65684f3485152071dfdeef0a933691bafc850265ccd3624ed00671f68e9cea9bb5b2575c6f01493e000000000000064a16a42407aa491564643e1dfc1fd50af29794efd294f0930000830148b78f666b6d633554897e96f80bb797908467619895cd83eaef7f10b84415d3060967fb0afd49eec5d6759aa9f92c4a5a356369b518210b95c93a0a89ca81e60189543f0000000000088700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000068155a43676e000000000630122d546e170dee47a7952b7c158aacbc38b035f7e183e90a74e7ab91746dbf2bc5aaee131243eedd4781b970b3dd7cc40f6a2bb5fa15684d91e61bf38002c7e9d0189543f0000000000358700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000083016f747cffb6ad3bfaec6d17404d49502ec4bbace678deceb0a6d288dfe353e22b794bb94612e7016f8abf6d15186a3d737536d84f7b43be2ce552a4316280154a0089543f0000000000258700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000c40eb7864285bedec00000630124b164e81e73327bab1cfa0ec9b73e6dc492b4078dcfafb66c11a01134705a9f0d0da528387e2ad59cb192ac51fa9c7b6ebee791e30b27fbf05b956d1c5bc4f20089543f0000000000158700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063011226070ed0b7b181dce226d2ac772265b9831fb462e54a040bd2644ea5092dda6e468fff3a6701d19b413086fb37919617e6bff18a0d9b0791f4bf373d1fbce10089543f0000000000108700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301db43b348b8b2008a01869382c9a91d50ece8adbc0f3a7b2e20b63d55f5db1088624cbc9eb64bcee9508a2352e8e7b03d226c7f06752bccc322bf3af7bc6b68d40189543f0000000000428700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063011a496ca06b19d3b6673c701ae9633e0a986edab74d9fb5b90c9840d129c0ab0967a424474451adb879fe40f49db3ff3505201a5136f88d93a486dffc95a8bbfb0089543f00000000000c8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00008301fbc6c4f4c92ca11aa750c91cd1ee111d69eee808eae4283931a27860599be53344f9763d439d3054e0dbf90667cb801320e802e74ac409610e25c1516caec4ae0089543f0000000000128700daec35af8ff88c16bdf0418774cb3d7599b48a29001400000000000000000000000000000000000000000000001043561a882930000000006301fdeb20b52896ceb6392d4d2f4514cbc187587990d736539a07ca5f59ee697ba84443d3b91894442e03741b67a76afe25ff709e0d318276b404abfc499071b3810189543f0000000000068700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301ebd8f5fda3ce757e953430c5d8c1ad689f2785c7c32f3b607410bd2a1955c3c1228a6bb7dced4e85680ab1d9e2a228c9b0a75114ff636aacd0dbae447e113eb90189543f00000000003f8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301837692935ae091a54c121aaf663f22e86b5c757c1c769ca2be3120f8d20f035a5d6ea340bf095f6a60c36e1e38fb975ad6d895723999abebe38b429637be73e00089543f0000000000208700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00014300bb50bf8b5c9c56a8b515110b42c4013f4e99cf9fcf6874c32e881554562265fa13a1539b789d0364d1e82e7587aad4adb894f4f3320d3517bd90bb08394d42640089543f000000005ec5631e93a0fb06b5ec6d52c0a2d89a3f9672d6ba64bfa005ce000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000603e4cfa0000000000000000000000000000000000000000000000000000000000000001534e580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000143bdb4672850000000006301f96d41806e43f65d8570be44e855870bd2d9b78b512e61264b17105a22267e6338984c7bee80abb6a93f1c4680ae79bc8306cf8598e86d1d25126ce53866c1a10089543f0000000000ac8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630131b960ebca035ef79df2a1e63acdcd5d6e27789b2eb538f9f8232169165473216c51e7febcefc39ca0ed2ab56a316676bff81e5a051651767e0c1fb23588829a00493e0000000000000f4a16a42407aa491564643e1dfc1fd50af29794efd294f093000063015643222807be2acb62072275ed4652806827ecbe9780cf68af1d79a075135c7308230547dd63b8fee254f0a48fae06c7f74b9b1b7c9efa4b6c6801c8d3e98b940089543f0000000000368700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301ee5b0c65075609b827be37d236859f440e182f6c56b3e10736e04f26fab8bf993d3dee5160eb8e6845cbd7198f2afac45c41e93476f9e5b2b401bd1fd6085ad40089543f0000000000018700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301d13c1423797c1e5541f4e730ba701bb6e46bdaa1985cefca4129cb2928baae6c24634a3d4d58d705e65cb6eba169e82ef765a396cc808c273dd4cfa74e37ea680089543f0000000000108700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063019af7bc274b54c2434832f9714315c03020e4465bd283a4b3b25236fa308b5132447f528bae32614e2526c060df7b1139b67fd461b8c0fb688d0517fdc23826ed0189543f00000000000c8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630166b80cd6e820bd1ed6cb92d5a4c0bf178d4078666c1c1a6576f7bdeca1dd3ff40fdbf763431560cf1490d900aff9a42a7b1b0c53c16f93b56039dbc4059de7980089543f0000000000148700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063019eb6f4205fb5ee8009209763b2ee373fcad19b04e9b4411ec7393403e2a815d43b5453d8ff70c0b60ef45917c4789c6afc45c8955cd59432abc2b32bc2c37b5d01493e000000000000024a16a42407aa491564643e1dfc1fd50af29794efd294f09300008301d22b7b46b935bbe378ac97d58bbe84f8b57d9b240f56e9335ed8134214a480c032b15645d64f624cd707628971ece2c06366fd2bbff50cbb40891fe1cf0de2250189543f0000000000098700daec35af8ff88c16bdf0418774cb3d7599b48a29001400000000000000000000000000000000000000000000001043561a882930000000008301f3ecf58a1113fb3387131db1ea3feeb9e2f46e3e48ee1755a1171d3ebcde8ce12d35af50f607a050a62b614074a70d3101211de5f0e40bccdbb49a7206d049ad0089543f0000000000018700daec35af8ff88c16bdf0418774cb3d7599b4295da87d0000000000000000000000000000000000000000000000008ac7230489e800000000630188270db66b963f84c9707597b0c8b7b353042bf61585be66b653a97ead1bbf6a4c373e6831c72dae73ac1a9a6a4e20133f92f57815f0d5a3673d31ae8e7aa0030089543f0000000000168700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301bf64ddfdac64b87045ee2b616ff27224598cd3f74704abdf729f11a5c196436e43b5e9e7fca7b0fa3c3fe149ebf561f14342cfc7c08781a3c85b6cdfa574e0a40089543f0000000000208700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063010beb607a64dcab788fe6c6ad52674b82d41eb785fab9c3c7de72ad96472e9b5c094b2c6b1fe1ff3a4e03a0ed1f0a1a56c9fd10776c61062aa80352c9bc524a240189543f0000000000218700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630152df9af9c07cec85267c8d281bc35087dec67370356cfb4fdcf2afeb322082ad33376cb37f77411963298ba7d127a04236a3d00fe1feb8270c3a42fb090bbe210089543f00000000001b8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00008301244318f4f681328908180b145ca3467846f5cdc40fe6e9ada54f2e7dba843ee414b22618c62cd57adbbc86c5ac079bc69f7e45e87108c53f22ff619501a773be0089543f0000000000108700daec35af8ff88c16bdf0418774cb3d7599b48a29001400000000000000000000000000000000000000000000000340aad21b3b700000000063016715ac2a06087215c0d619320bbbe1f25e9643ec2b7a4dad9813875832360b33592babf1ed961d1c20fd6c9334fc35214ccd20a168c5d53df0b3cfcd40ce46f40089543f0000000000168700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301e3f78d50a6720d493515f979f3242514d8de70a93d4d00caa62b3991fc48e1811c7000a1cec9a13cb59a3bd639a346d6a5834f8f6bfc0b4896eb9f12f52a67750089543f00000000001d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063010d7a3ee143952287c195738b1a238e88fe1cd7c721339f21a2c9fdab99352d862ad25cb00e9d28afad4de9a1c21d8bf16fbc09d47483c928343bd12d90cf0e940089543f0000000000458700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301675ad48eb9a9cb237be82eca75f0f058a53fc42e5952a1009e51523e3e7403ca2bc2086ee05a853fe839bfe3c8123c59373066885185f052c9d6835a6c20d9770189543f0000000000188700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301d104e95267f9651e269ef7ea3a5f0a99c044a0be82bf883c536102918dffed0e38e0e7635046abf595ad5fb1860dc3a13e858bbe1e053436ebf694b7d478f1c00189543f00000000000f8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630170935022f25655a71178c3439cdb3cdc7c2f9857fb65ee2c3af721997e849c55584d650cb4fb8b15f56b708828470224802f1ec9d7fd1afe2f0ad399c5e38ea40189543f00000000004e8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301c5bbd5a56cf2621d47a4acfe87b993fe625218e9b331ab25ea7b07005a40520d70ef48e060693a69c1dcb23300d407658551bcdc270da2a8cdacc91d5cfe25760089543f00000000001d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00014300ff75b419c212ab48019e879247e2aad3a917dc6000e4c0a788af865637b2d8ac422cd67f43ce27c0d0d46c32cdc80409a74029d00c41ab9579adf760671cfd370089543f000000005ec6631e93a0fb06b5ec6d52c0a2d89a3f9672d6ba64bfa005ce000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000603e4e9c0000000000000000000000000000000000000000000000000000000000000001534e58000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000013f1070b03f0180000000630194b0adafa5ed2c77b5b500b13df63ba79d971f383e6716b58df912738766e33f01c11739fd39045d7f2ca3a6ff582391d4094c8910f683a4525b087f5fc5d49a0189543f0000000000a28700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063013a0f2024f85cd3038d8f565ef3e3a7f1ed920bf2340621f1c961c3cf2551a21d234a8d69ab9127dffc3193d3a6af98e5d5f70e166121094ad51d91c93e9e938d0089543f0000000000288700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006300f322135adf6c6959bc012fe5ffb14bb8da6443a80f4e85799ca645ffc369eb09155f05bd65a64741cf089427597a1c35130428c2ecef60dad2b3f1f6da63e3c0007a11ff0000000000bed85eafa37734e4ad237c3a3443d64dc94ae998e7af086c7e00008301ff3ecc7ecf5a169c2b1c4360c63dcf9a8d786d804f400f1bd4bb0fcb9401644f618ca9780aae23bab8a688cf6bf40258e0c44b8089e6bf0989d5aafe9236d3fa0089543f0000000000158700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000001bc16d674ec800000000630142e1b6d027dd1fb9cf43dbb834aaa03547ec356a65a693502546416bd8596250570285bcce68e72ce1bbd7fd3f1b3f8713d06e2b854eb718cafd1fbe3b2ea8ba0089543f0000000000138700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301f343a551e25a7254de23e818c649c30cf974b4f1e7feef4c30eab697f62e71a809006682008e5c25b1816228ce36d77e503e6ce1bdfe3027c6d29787be3ae1330089543f0000000000138700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301bf45e708b0d48a939480d9a16994e7f9b325b55d3a1faa989abca22ec877c799269be034bb0d4dc36a2f684295057e1f1a7de8577114fc660bbe37edbf16f84c0089543f00000000001a8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00008301f4cc4215e75d412821ef74ff9c20918c421227217f7c135f9b56a5bab8411f7061a8b99a11ea37c5346f62e70521f60ed7e664fb4b0f82e4c6a1d3e62c33607c0089543f0000000000048700daec35af8ff88c16bdf0418774cb3d7599b48a290014000000000000000000000000000000000000000000000001158e460913d00000000063013bd03efc62589e24d9d53547a31e18a18e24cb4c820ddbdf2cb3c20323d19ea01ebc043698a7a80ddc632682461ff9cadb95b19ec520f7528dea5bc57017d6df0089543f00000000001a8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301ab3f7535024aaf1bbadd31d33aeedfaf3325d617e8b3b7814080802dd4fe79d55b40120b9f68ece14e45bb347841bd2bad35c0e93f43da8ccf8c0857e4eb1fee0189543f0000000000208700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630145d68cab27031deb7b55e2e6df0df2629c3f07a60a09f433bbd45cd3594855f8508c5ab1201189091b8197d082a7d62abc6e4a28570ef28cfab8bcaf42679a8b0089543f0000000000058700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063013615d80a47143ea2f0102f196e6a2feda49b3be3352c88bb2dec2a9a567e22632fb5097f949412b398ca857b2f105b0f032a69be0f8bd401174a29ae67a8da5a0189543f0000000000148700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630128b5fef41c92369c84e8e742df1253f6eb624cfe5c807de5cb49869a7eaf573416ba55a9f86034b35a11f0c158af9ef07640e71cbcb093cb03a38b75d427ca360089543f0000000000058700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301fc2fc4d368f9f3ef73d9a4114c4a61116f7fd6259c4f172599511882a874993b437960110af8633fd8727372a880194569d295862c321dd1cfd961394e6c21eb0089543f0000000000b48700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301b2c09b2cda9dca2a6c7441d40e24d2a7b481ff76b7759d2a2aebdd7b1b2b3e9d551638ba91ae1511d03be6c90e13d8a931cb46ba48edf1f3c972c2bc6c4b30a50189543f0000000000428700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301ea34d378265585e18e8bac13f67c9733a4055886ddabbd6e467396d964c3dea6361f61256ec3eb9658c88fb1333ae4e74343622e18573a1545ca38af763a4ec40089543f0000000000348700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301563eed133b104dc9d7430fe5082fa1b722b14236053ba0003bff64fb52f073ae6c0c3985fc59c43bc799868fc2222410131a5f08b01dca6bdfc33e9df01c48900089543f0000000000028700daec35af8ff88c16bdf0418774cb3d7599b49741fb2200006301f8773e89f4266cb5afc584dc3e10b4a52b7fefa672ca7f627f758810ed1577ec56cd9dc44571c53fae24ecccd19907791eee25d5e0dd058e5444b02ad91836300089543f0000000000078700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063019ac5f89805eb37c5089ceb5f22bcba2abe7e5da0085adb520d0c2ee4efdf3cab3533f854c08f23f4071c42270b9a074b14bba44a2027e382b2dc0377621fd31100493e000000000000034a16a42407aa491564643e1dfc1fd50af29794efd294f0930000630135234c82daed5f5bb355d7f3f81edc0f5f9f0d4d11365d4e948bd2c02e7031cc291ca1f5c76d8ebe5c1a930a9375acf73f2343cc860aa0633643aecf7420eeb00089543f00000000001d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0001430051884bdaf68fe6c64943a5f3160b5fcd7ef9140463937fc8d1ecdb3c916d7096354237eb3c63430116601c5b887dc44a4d4bce5639be1fa36b91243bb76e2b350189543f000000005ec7631e93a0fb06b5ec6d52c0a2d89a3f9672d6ba64bfa005ce000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000603e508c0000000000000000000000000000000000000000000000000000000000000001534e58000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000013cf0dfffc45f0000000063016e3845ed8e5ae218069cfa7ca98d3ffd86505d0383a3655f8cd46c557bc399c50c81cdfef32719125e7b84b1fd4e7c7535bf5a464a23a86fd9801b146b700d8f0189543f00000000000d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063014c626ef869f07ea4cee4520d5bd24f2c2772a42ce289dfaedaed255446b03c0d2abca85ccfe8233061169c0fcd12d740750f0b65dd707dfe665d4d9cfaf757230189543f00000000000c8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301a0d59339cea6846e08148f7d9daba52402649546b5875c594f2f81209b36794a3e310fa64c0563d53334ae6fe87762426142dfa75b31f1021dd4092f4be895e80089543f00000000002f8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063014bd68c44229ea3cd8c4ff073a9cedbb5ff4387b257b29ce0e18ace691cd97677406b528411c6cd65df52a670dccc257bf290abd1805ba551f151fbd6355bdb980189543f0000000000068700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063016e7f6fe17cb0d1ed5970c49cc0745e3cae90e8e5a2004217472db6a1a1b9ab1124797d03a271feb38e66e690453904f568e61e6623900299f3b8079027fa15570189543f00000000002e8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063015702ed8c76f782970f0579f74a02931fb3e9dca6f3ba750d97d0df7ae60d3d10221ad66f898704bc9987b54d1109da9735c8d7acf2e4a25668134d641ac2d9120089543f00000000000e8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630129bd1bc30f7bfeb956ae7725bcd7144bc42291caf0628d7c8fb75a103f57fd6d4ce006acba63dd5593603368a0b154197d0826b8f9c3d23b93d72dc830c48a0e0189543f00000000001d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000083019cbadc64fb19e3e912ffc2492bf42130d21fb67e4654c11551f3c3dd28e4ded951eae21b5b7f98a7f1e27fdf30fce409363823c97ba03643cbd7e65b964432a90089543f0000000000008700daec35af8ff88c16bdf0418774cb3d7599b48a29001400000000000000000000000000000000000000000000000b38b3bb4459dc00000000630198e2a81ec1c025dc5bcec824708cc1f264134aa6121d90e7c284c77907ee0d0845b88fd9c030bc20002dcbf5a6709af1c42ba4c8ad6e84a55e3e48a1c79974160089543f00000000005b8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063014cc92f396e2790dd5ab926c2e4c1be43000b1098fe7eb2822e0b49ea5ea076f01fb68a84640524d5c525bf989c4a9aff9a02750a81c535c031a8452aed300f420189543f0000000000218700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063019af9087760f24d93e0fbde3bd0e2f2e64fb8d8c36d024fd6cb4a58c34000b2e810e39efa69c5310be3580a4d94bbf069487d65172dc8d39d9bcb75261fd841560189543f0000000000458700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063015402786088ba83e4a68e5f05a7e9c8089c862bfd9c134b9a9ee38aed4a39a08d12d4bf29039655440937a990e14d699101d883246a29b62977aeb9a0e8252f6d0089543f0000000000178700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301bca73ae8381f33c7ec9e0f6a5b518f12854b0061e63a805b3d5941c1516128ce0775f6819e673108d4654a2018289aa951f9340dbee582b9c6b548ec6450149e0189543f0000000000828700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063011bcab33ce1cd156607d9c5a4cefa0d67b82c89462b0d3d4cfd79fa7b2945ee0c14103722c3702f1e247190a3baf4329fd0867d9ceff2bf212e9ff633a89915010089543f00000000001c8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e00006301b9ca39e85b19a0f5f03b067f2bca5be6dc27294107dd4e9ffa9c1af204b6feb235c7e21a385cc3fad8ddfa3aefe7951e2494d63f0a9f057923b39a73008e2a250189543f0000000000388700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063013656758d6e0c9219bb9de64106bb2dcaaec309bc54c0e53993cd37cd9cd03bb15b5102cbef23c0f2e60fcd16e8729059bc6f01546f8dd2c94ee4c9cdae79a0040089543f00000000003f8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630157b7af6659df3a6f211042201fefbaf98780f3b2945398494147dd0a08add01a37fb4cdbccd4df5949381357110e41daf963859999bde4cee42291dadb4fd31b0089543f00000000000d8700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000063012a0b8faa1536ed92b8c2eef51fd0e8a5a82d4a7ada7f2a46ed8e6b6ec29e9a263bd1866f30953a0ceefd68c4a931975bf487ff35fed2b6b3837cc83f0b5486510189543f0000000000218700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e000083012aa213c2eaa5a3f0887ba7ab48498bd289bc54a9e59fbff3a8a2a13e13eb9598399c58f16b07789e8c36268985d107091b01fe0afa3faa6fd71baaeef68afb340089543f00000000001f8700daec35af8ff88c16bdf0418774cb3d7599b48a2900140000000000000000000000000000000000000000000000056bc75e2d6310000000006301da0581ee658208bd90e7e1cc34873f56a3395523a5bdb7dcc1b3245b3cf2b4ad2fd8f550641f2ec4700865d040583e8ff7297dc21f06821332bd5e1aa01ba2770089543f0000000000468700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e0000630114adc64102322203225ec04c16480aa2d7b73e323f4ca68256753726fb81e4450f4318ca276d297a402371994ced771b50eb7409744c8f9182d01599e9c5c6ba0189543f0000000000158700daec35af8ff88c16bdf0418774cb3d7599b4af086c7e"
]
}
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"extends": "../../tsconfig.json"
"extends": "../../tsconfig.json",
"typeRoots": ["node_modules/@types", "src/@types"]
}
/* Imports: External */
import { LevelUp } from 'levelup'
import { BigNumber } from 'ethers'
import { BatchType } from '@eth-optimism/core-utils'
/* Imports: Internal */
import { SimpleDB } from './simple-db'
......@@ -127,7 +128,14 @@ export class TransportDB {
public async getTransactionBatchByIndex(
index: number
): Promise<TransactionBatchEntry> {
return this._getEntryByIndex(TRANSPORT_DB_KEYS.TRANSACTION_BATCH, index)
const entry = (await this._getEntryByIndex(
TRANSPORT_DB_KEYS.TRANSACTION_BATCH,
index
)) as TransactionBatchEntry
if (entry && typeof entry.type === 'undefined') {
entry.type = BatchType[BatchType.LEGACY]
}
return entry
}
public async getStateRootByIndex(index: number): Promise<StateRootEntry> {
......@@ -169,7 +177,13 @@ export class TransportDB {
}
public async getLatestTransactionBatch(): Promise<TransactionBatchEntry> {
return this._getLatestEntry(TRANSPORT_DB_KEYS.TRANSACTION_BATCH)
const entry = (await this._getLatestEntry(
TRANSPORT_DB_KEYS.TRANSACTION_BATCH
)) as TransactionBatchEntry
if (entry && typeof entry.type === 'undefined') {
entry.type = BatchType[BatchType.LEGACY]
}
return entry
}
public async getLatestStateRoot(): Promise<StateRootEntry> {
......
/* Imports: External */
import { BigNumber, ethers, constants } from 'ethers'
import { serialize, Transaction } from '@ethersproject/transactions'
import { getContractFactory } from '@eth-optimism/contracts'
import {
fromHexString,
toHexString,
toRpcHexString,
BatchType,
SequencerBatch,
} from '@eth-optimism/core-utils'
import { SequencerBatchAppendedEvent } from '@eth-optimism/contracts/dist/types/CanonicalTransactionChain'
......@@ -76,33 +78,33 @@ export const handleEventsSequencerBatchAppended: EventHandlerSet<
parseEvent: (event, extraData, l2ChainId) => {
const transactionEntries: TransactionEntry[] = []
// It's easier to deal with this data if it's a Buffer.
const calldata = fromHexString(extraData.l1TransactionData)
if (calldata.length < 12) {
// 12 * 2 + 2 = 26
if (extraData.l1TransactionData.length < 26) {
throw new Error(
`Block ${extraData.blockNumber} transaction data is invalid for decoding: ${extraData.l1TransactionData} , ` +
`converted buffer length is < 12.`
`Block ${extraData.blockNumber} transaction data is too small: ${extraData.l1TransactionData.length}`
)
}
const numContexts = BigNumber.from(calldata.slice(12, 15)).toNumber()
// TODO: typings not working?
const decoded = (SequencerBatch as any).fromHex(extraData.l1TransactionData)
// Keep track of the CTC index
let transactionIndex = 0
// Keep track of the number of deposits
let enqueuedCount = 0
let nextTxPointer = 15 + 16 * numContexts
for (let i = 0; i < numContexts; i++) {
const contextPointer = 15 + 16 * i
const context = parseSequencerBatchContext(calldata, contextPointer)
// Keep track of the tx index in the current batch
let index = 0
for (const context of decoded.contexts) {
for (let j = 0; j < context.numSequencedTransactions; j++) {
const sequencerTransaction = parseSequencerBatchTransaction(
calldata,
nextTxPointer
)
const buf = decoded.transactions[index]
if (!buf) {
throw new Error(
`Invalid batch context, tx count: ${decoded.transactions.length}, attempting to parse ${index}`
)
}
const decoded = decodeSequencerBatchTransaction(
sequencerTransaction,
l2ChainId
)
const tx = buf.toTransaction()
transactionEntries.push({
index: extraData.prevTotalElements
......@@ -114,16 +116,29 @@ export const handleEventsSequencerBatchAppended: EventHandlerSet<
gasLimit: BigNumber.from(0).toString(),
target: constants.AddressZero,
origin: null,
data: toHexString(sequencerTransaction),
data: serialize(
{
nonce: tx.nonce,
gasPrice: tx.gasPrice,
gasLimit: tx.gasLimit,
to: tx.to,
value: tx.value,
data: tx.data,
},
{
v: tx.v,
r: tx.r,
s: tx.s,
}
),
queueOrigin: 'sequencer',
value: decoded.value,
value: toRpcHexString(tx.value),
queueIndex: null,
decoded,
decoded: mapSequencerTransaction(tx, l2ChainId),
confirmed: true,
})
nextTxPointer += 3 + sequencerTransaction.length
transactionIndex++
index++
}
for (let j = 0; j < context.numSubsequentQueueTransactions; j++) {
......@@ -169,6 +184,7 @@ export const handleEventsSequencerBatchAppended: EventHandlerSet<
timestamp: BigNumber.from(extraData.timestamp).toNumber(),
submitter: extraData.submitter,
l1TransactionHash: extraData.l1TransactionHash,
type: BatchType[decoded.type],
}
return {
......@@ -206,61 +222,21 @@ export const handleEventsSequencerBatchAppended: EventHandlerSet<
},
}
interface SequencerBatchContext {
numSequencedTransactions: number
numSubsequentQueueTransactions: number
timestamp: number
blockNumber: number
}
const parseSequencerBatchContext = (
calldata: Buffer,
offset: number
): SequencerBatchContext => {
return {
numSequencedTransactions: BigNumber.from(
calldata.slice(offset, offset + 3)
).toNumber(),
numSubsequentQueueTransactions: BigNumber.from(
calldata.slice(offset + 3, offset + 6)
).toNumber(),
timestamp: BigNumber.from(
calldata.slice(offset + 6, offset + 11)
).toNumber(),
blockNumber: BigNumber.from(
calldata.slice(offset + 11, offset + 16)
).toNumber(),
}
}
const parseSequencerBatchTransaction = (
calldata: Buffer,
offset: number
): Buffer => {
const transactionLength = BigNumber.from(
calldata.slice(offset, offset + 3)
).toNumber()
return calldata.slice(offset + 3, offset + 3 + transactionLength)
}
const decodeSequencerBatchTransaction = (
transaction: Buffer,
const mapSequencerTransaction = (
tx: Transaction,
l2ChainId: number
): DecodedSequencerBatchTransaction => {
const decodedTx = ethers.utils.parseTransaction(transaction)
return {
nonce: BigNumber.from(decodedTx.nonce).toString(),
gasPrice: BigNumber.from(decodedTx.gasPrice).toString(),
gasLimit: BigNumber.from(decodedTx.gasLimit).toString(),
value: toRpcHexString(decodedTx.value),
target: decodedTx.to ? toHexString(decodedTx.to) : null,
data: toHexString(decodedTx.data),
nonce: BigNumber.from(tx.nonce).toString(),
gasPrice: BigNumber.from(tx.gasPrice).toString(),
gasLimit: BigNumber.from(tx.gasLimit).toString(),
value: toRpcHexString(tx.value),
target: tx.to ? toHexString(tx.to) : null,
data: toHexString(tx.data),
sig: {
v: parseSignatureVParam(decodedTx.v, l2ChainId),
r: toHexString(decodedTx.r),
s: toHexString(decodedTx.s),
v: parseSignatureVParam(tx.v, l2ChainId),
r: toHexString(tx.r),
s: toHexString(tx.s),
},
}
}
......@@ -60,6 +60,7 @@ export const handleEventsStateBatchAppended: EventHandlerSet<
prevTotalElements: event.args._prevTotalElements.toNumber(),
extraData: event.args._extraData,
l1TransactionHash: extraData.l1TransactionHash,
type: 'LEGACY', // There is currently only 1 state root batch type
}
return {
......
......@@ -48,6 +48,7 @@ interface BatchEntry {
prevTotalElements: number
extraData: string
l1TransactionHash: string
type: string
}
export type TransactionBatchEntry = BatchEntry
......
/* External Imports */
import fs from 'fs'
import path from 'path'
import chai = require('chai')
import Mocha from 'mocha'
import chaiAsPromised from 'chai-as-promised'
import { BigNumber } from 'ethers'
// Chai plugins go here.
chai.use(chaiAsPromised)
......@@ -9,4 +12,38 @@ chai.use(chaiAsPromised)
const should = chai.should()
const expect = chai.expect
export { should, expect, Mocha }
const readMockData = () => {
const mockDataPath = path.join(__dirname, 'unit-tests', 'examples')
const paths = fs.readdirSync(mockDataPath)
const files = []
for (const filename of paths) {
// Skip non .txt files
if (!filename.endsWith('.txt')) {
continue
}
const filePath = path.join(mockDataPath, filename)
const file = fs.readFileSync(filePath)
const obj = JSON.parse(file.toString())
// Reserialize the BigNumbers
obj.input.extraData.prevTotalElements = BigNumber.from(
obj.input.extraData.prevTotalElements
)
obj.input.extraData.batchIndex = BigNumber.from(
obj.input.extraData.batchIndex
)
if (obj.input.event.args.length !== 3) {
throw new Error(`ABI mismatch`)
}
obj.input.event.args = obj.input.event.args.map(BigNumber.from)
obj.input.event.args._startingQueueIndex = obj.input.event.args[0]
obj.input.event.args._numQueueElements = obj.input.event.args[1]
obj.input.event.args._totalElements = obj.input.event.args[2]
obj.input.extraData.batchSize = BigNumber.from(
obj.input.extraData.batchSize
)
files.push(obj)
}
return files
}
export { should, expect, Mocha, readMockData }
import { BigNumber, ethers } from 'ethers'
import { sequencerBatch, add0x, BatchType } from '@eth-optimism/core-utils'
const compressBatchWithZlib = (calldata: string): string => {
const batch = sequencerBatch.decode(calldata)
batch.type = BatchType.ZLIB
const encoded = sequencerBatch.encode(batch)
return add0x(encoded)
}
/* Imports: Internal */
import { expect } from '../../../../setup'
import { expect, readMockData } from '../../../../setup'
import { handleEventsSequencerBatchAppended } from '../../../../../src/services/l1-ingestion/handlers/sequencer-batch-appended'
import { SequencerBatchAppendedExtraData } from '../../../../../src/types'
describe('Event Handlers: CanonicalTransactionChain.SequencerBatchAppended', () => {
const mockData = readMockData()
describe('handleEventsSequencerBatchAppended.parseEvent', () => {
// This tests the behavior of parsing a real mainnet transaction,
// so it will break if the encoding scheme changes.
......@@ -46,9 +56,53 @@ describe('Event Handlers: CanonicalTransactionChain.SequencerBatchAppended', ()
expect(() => {
handleEventsSequencerBatchAppended.parseEvent(...input1)
}).to.throw(
`Block ${input1[1].blockNumber} transaction data is invalid for decoding: ${input1[1].l1TransactionData} , ` +
`converted buffer length is < 12.`
`Block ${input1[1].blockNumber} transaction data is too small: ${input1[1].l1TransactionData.length}`
)
})
describe('mainnet transactions', () => {
for (const mock of mockData) {
const { input, output } = mock
const { event, extraData, l2ChainId } = input
const hash = mock.input.extraData.l1TransactionHash
it(`uncompressed: ${hash}`, () => {
// Set the type to be legacy
output.transactionBatchEntry.type = BatchType[BatchType.LEGACY]
const res = handleEventsSequencerBatchAppended.parseEvent(
event,
extraData,
l2ChainId
)
// Check all of the transaction entries individually
for (const [i, got] of res.transactionEntries.entries()) {
const expected = output.transactionEntries[i]
expect(got).to.deep.eq(expected, `case ${i}`)
}
expect(res).to.deep.eq(output)
})
it(`compressed: ${hash}`, () => {
// Set the type to be zlib
output.transactionBatchEntry.type = BatchType[BatchType.ZLIB]
const compressed = compressBatchWithZlib(
input.extraData.l1TransactionData
)
const copy = { ...extraData }
copy.l1TransactionData = compressed
const res = handleEventsSequencerBatchAppended.parseEvent(
event,
copy,
l2ChainId
)
expect(res).to.deep.eq(output)
})
}
})
})
})
{
"extends": "../../tsconfig.json"
"extends": "../../tsconfig.json",
"typeRoots": ["node_modules/@types", "src/@types"]
}
......@@ -453,10 +453,13 @@ export class CrossChainMessenger implements ICrossChainMessenger {
timeoutMs?: number
} = {}
): Promise<MessageReceipt> {
// Resolving once up-front is slightly more efficient.
const resolved = await this.toCrossChainMessage(message)
let totalTimeMs = 0
while (totalTimeMs < (opts.timeoutMs || Infinity)) {
const tick = Date.now()
const receipt = await this.getMessageReceipt(message)
const receipt = await this.getMessageReceipt(resolved)
if (receipt !== null) {
return receipt
} else {
......@@ -468,6 +471,73 @@ export class CrossChainMessenger implements ICrossChainMessenger {
throw new Error(`timed out waiting for message receipt`)
}
public async waitForMessageStatus(
message: MessageLike,
status: MessageStatus,
opts: {
pollIntervalMs?: number
timeoutMs?: number
} = {}
): Promise<void> {
// Resolving once up-front is slightly more efficient.
const resolved = await this.toCrossChainMessage(message)
let totalTimeMs = 0
while (totalTimeMs < (opts.timeoutMs || Infinity)) {
const tick = Date.now()
const currentStatus = await this.getMessageStatus(resolved)
// Handle special cases for L1 to L2 messages.
if (resolved.direction === MessageDirection.L1_TO_L2) {
// If we're at the expected status, we're done.
if (currentStatus === status) {
return
}
if (
status === MessageStatus.UNCONFIRMED_L1_TO_L2_MESSAGE &&
currentStatus > status
) {
// Anything other than UNCONFIRMED_L1_TO_L2_MESSAGE implies that the message was at one
// point "unconfirmed", so we can stop waiting.
return
}
if (
status === MessageStatus.FAILED_L1_TO_L2_MESSAGE &&
currentStatus === MessageStatus.RELAYED
) {
throw new Error(
`incompatible message status, expected FAILED_L1_TO_L2_MESSAGE got RELAYED`
)
}
if (
status === MessageStatus.RELAYED &&
currentStatus === MessageStatus.FAILED_L1_TO_L2_MESSAGE
) {
throw new Error(
`incompatible message status, expected RELAYED got FAILED_L1_TO_L2_MESSAGE`
)
}
}
// Handle special cases for L2 to L1 messages.
if (resolved.direction === MessageDirection.L2_TO_L1) {
if (currentStatus >= status) {
// For L2 to L1 messages, anything after the expected status implies the previous status,
// so we can safely return if the current status enum is larger than the expected one.
return
}
}
await sleep(opts.pollIntervalMs || 4000)
totalTimeMs += Date.now() - tick
}
throw new Error(`timed out waiting for message status change`)
}
public async estimateL2MessageGasLimit(
message: MessageRequestLike,
opts?: {
......
......@@ -226,6 +226,27 @@ export interface ICrossChainMessenger {
}
): Promise<MessageReceipt>
/**
* Waits until the status of a given message changes to the expected status. Note that if the
* status of the given message changes to a status that implies the expected status, this will
* still return. If the status of the message changes to a status that exclues the expected
* status, this will throw an error.
*
* @param message Message to wait for.
* @param status Expected status of the message.
* @param opts Options to pass to the waiting function.
* @param opts.pollIntervalMs Number of milliseconds to wait when polling.
* @param opts.timeoutMs Milliseconds to wait before timing out.
*/
waitForMessageStatus(
message: MessageLike,
status: MessageStatus,
opts?: {
pollIntervalMs?: number
timeoutMs?: number
}
): Promise<void>
/**
* Estimates the amount of gas required to fully execute a given message on L2. Only applies to
* L1 => L2 messages. You would supply this gas limit when sending the message to L2.
......
......@@ -514,28 +514,6 @@
"@eth-optimism/core-utils" "^0.5.1"
bn.js "^5.2.0"
"@eth-optimism/solc@^0.6.12-alpha.1":
version "0.6.12-alpha.1"
resolved "https://registry.yarnpkg.com/@eth-optimism/solc/-/solc-0.6.12-alpha.1.tgz#041876f83b34c6afe2f19dfe9626568df6ed8590"
integrity sha512-Ky73mo+2iNJs/VTaT751nMeZ7hXns0TBAlffTOxIOsScjAZ/zi/KWsDUo3r89aV2JKXcYAU/bLidxF40MVJeUw==
dependencies:
command-exists "^1.2.8"
commander "3.0.2"
follow-redirects "^1.12.1"
fs-extra "^0.30.0"
js-sha3 "0.8.0"
memorystream "^0.3.1"
require-from-string "^2.0.0"
semver "^5.5.0"
tmp "0.0.33"
"@eth-optimism/ynatm@^0.2.2":
version "0.2.2"
resolved "https://registry.yarnpkg.com/@eth-optimism/ynatm/-/ynatm-0.2.2.tgz#b1f165c3149188f184b66329228746260ae18677"
integrity sha512-R/hIAFWEj2sjf3inNEGCffmGofqMFY/7PS/Hh4A/62Kg0wMM8rsyMyW8pXngMnD/EQAjR8WTtKDutq/L5vSMTQ==
dependencies:
bluebird "^3.7.2"
"@ethereum-waffle/chai@^3.4.0":
version "3.4.0"
resolved "https://registry.yarnpkg.com/@ethereum-waffle/chai/-/chai-3.4.0.tgz#2477877410a96bf370edd64df905b04fb9aba9d5"
......@@ -689,7 +667,7 @@
"@ethersproject/properties" "^5.0.3"
"@ethersproject/strings" "^5.0.4"
"@ethersproject/abi@5.4.0", "@ethersproject/abi@^5.0.0", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.0.1", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.4.0":
"@ethersproject/abi@5.4.0", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.0.1", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.4.0.tgz#a6d63bdb3672f738398846d4279fa6b6c9818242"
integrity sha512-9gU2H+/yK1j2eVMdzm6xvHSnMxk8waIHQGYCZg5uvAyH0rsAzxkModzBSpbAkAuhKFEovC2S9hM4nPuLym8IZw==
......@@ -734,7 +712,7 @@
"@ethersproject/properties" "^5.4.0"
"@ethersproject/strings" "^5.4.0"
"@ethersproject/abstract-provider@5.4.1", "@ethersproject/abstract-provider@^5.0.0", "@ethersproject/abstract-provider@^5.4.0", "@ethersproject/abstract-provider@^5.4.1":
"@ethersproject/abstract-provider@5.4.1", "@ethersproject/abstract-provider@^5.4.0", "@ethersproject/abstract-provider@^5.4.1":
version "5.4.1"
resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.4.1.tgz#e404309a29f771bd4d28dbafadcaa184668c2a6e"
integrity sha512-3EedfKI3LVpjSKgAxoUaI+gB27frKsxzm+r21w9G60Ugk+3wVLQwhi1LsEJAKNV7WoZc8CIpNrATlL1QFABjtQ==
......@@ -760,7 +738,7 @@
"@ethersproject/transactions" "^5.5.0"
"@ethersproject/web" "^5.5.0"
"@ethersproject/abstract-signer@5.4.1", "@ethersproject/abstract-signer@^5.0.0", "@ethersproject/abstract-signer@^5.4.0", "@ethersproject/abstract-signer@^5.4.1":
"@ethersproject/abstract-signer@5.4.1", "@ethersproject/abstract-signer@^5.4.0", "@ethersproject/abstract-signer@^5.4.1":
version "5.4.1"
resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.4.1.tgz#e4e9abcf4dd4f1ba0db7dff9746a5f78f355ea81"
integrity sha512-SkkFL5HVq1k4/25dM+NWP9MILgohJCgGv5xT5AcRruGz4ILpfHeBtO/y6j+Z3UN/PAjDeb4P7E51Yh8wcGNLGA==
......@@ -782,7 +760,7 @@
"@ethersproject/logger" "^5.5.0"
"@ethersproject/properties" "^5.5.0"
"@ethersproject/address@5.4.0", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.0", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.0.4", "@ethersproject/address@^5.4.0":
"@ethersproject/address@5.4.0", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.0.4", "@ethersproject/address@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.4.0.tgz#ba2d00a0f8c4c0854933b963b9a3a9f6eb4a37a3"
integrity sha512-SD0VgOEkcACEG/C6xavlU1Hy3m5DGSXW3CUHkaaEHbAPPsgi0coP5oNPsxau8eTlZOk/bpa/hKeCNoK5IzVI2Q==
......@@ -804,7 +782,7 @@
"@ethersproject/logger" "^5.5.0"
"@ethersproject/rlp" "^5.5.0"
"@ethersproject/base64@5.4.0", "@ethersproject/base64@^5.0.0", "@ethersproject/base64@^5.4.0":
"@ethersproject/base64@5.4.0", "@ethersproject/base64@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.4.0.tgz#7252bf65295954c9048c7ca5f43e5c86441b2a9a"
integrity sha512-CjQw6E17QDSSC5jiM9YpF7N1aSCHmYGMt9bWD8PWv6YPMxjsys2/Q8xLrROKI3IWJ7sFfZ8B3flKDTM5wlWuZQ==
......@@ -834,7 +812,7 @@
"@ethersproject/bytes" "^5.5.0"
"@ethersproject/properties" "^5.5.0"
"@ethersproject/bignumber@5.4.1", "@ethersproject/bignumber@>=5.0.0-beta.130", "@ethersproject/bignumber@^5.0.0", "@ethersproject/bignumber@^5.0.7", "@ethersproject/bignumber@^5.4.0":
"@ethersproject/bignumber@5.4.1", "@ethersproject/bignumber@>=5.0.0-beta.130", "@ethersproject/bignumber@^5.0.7", "@ethersproject/bignumber@^5.4.0":
version "5.4.1"
resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.4.1.tgz#64399d3b9ae80aa83d483e550ba57ea062c1042d"
integrity sha512-fJhdxqoQNuDOk6epfM7yD6J8Pol4NUCy1vkaGAkuujZm0+lNow//MKu1hLhRiYV4BsOHyBv5/lsTjF+7hWwhJg==
......@@ -861,7 +839,7 @@
"@ethersproject/logger" "^5.4.0"
bn.js "^4.11.9"
"@ethersproject/bytes@5.4.0", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.0", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.4.0":
"@ethersproject/bytes@5.4.0", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.4.0.tgz#56fa32ce3bf67153756dbaefda921d1d4774404e"
integrity sha512-H60ceqgTHbhzOj4uRc/83SCN9d+BSUnOkrr2intevqdtEMO1JFVZ1XL84OEZV+QjV36OaZYxtnt4lGmxcGsPfA==
......@@ -875,7 +853,7 @@
dependencies:
"@ethersproject/logger" "^5.5.0"
"@ethersproject/constants@5.4.0", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.0", "@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.4.0":
"@ethersproject/constants@5.4.0", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.4.0.tgz#ee0bdcb30bf1b532d2353c977bf2ef1ee117958a"
integrity sha512-tzjn6S7sj9+DIIeKTJLjK9WGN2Tj0P++Z8ONEIlZjyoTkBuODN+0VfhAyYksKi43l1Sx9tX2VlFfzjfmr5Wl3Q==
......@@ -889,7 +867,7 @@
dependencies:
"@ethersproject/bignumber" "^5.5.0"
"@ethersproject/contracts@5.4.1", "@ethersproject/contracts@^5.0.0", "@ethersproject/contracts@^5.0.5", "@ethersproject/contracts@^5.4.1":
"@ethersproject/contracts@5.4.1", "@ethersproject/contracts@^5.4.1":
version "5.4.1"
resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.4.1.tgz#3eb4f35b7fe60a962a75804ada2746494df3e470"
integrity sha512-m+z2ZgPy4pyR15Je//dUaymRUZq5MtDajF6GwFbGAVmKz/RF+DNIPwF0k5qEcL3wPGVqUjFg2/krlCRVTU4T5w==
......@@ -921,18 +899,6 @@
"@ethersproject/properties" "^5.5.0"
"@ethersproject/transactions" "^5.5.0"
"@ethersproject/hardware-wallets@^5.0.8":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/hardware-wallets/-/hardware-wallets-5.4.0.tgz#bce275b395e26b6f50481095331157614490a473"
integrity sha512-Ea4ymm4etZoSWy93OcEGZkuVqyYdl/RjMlaXY6yQIYjsGi75sm4apbTiBA8DA9uajkv1FVakJZEBBTaVGgnBLA==
dependencies:
"@ledgerhq/hw-app-eth" "5.27.2"
"@ledgerhq/hw-transport" "5.26.0"
"@ledgerhq/hw-transport-u2f" "5.26.0"
ethers "^5.4.0"
optionalDependencies:
"@ledgerhq/hw-transport-node-hid" "5.26.0"
"@ethersproject/hardware-wallets@^5.5.0":
version "5.5.0"
resolved "https://registry.yarnpkg.com/@ethersproject/hardware-wallets/-/hardware-wallets-5.5.0.tgz#b4a3bc99a843c3b78b133cdf94485a567ba17b8d"
......@@ -945,7 +911,7 @@
optionalDependencies:
"@ledgerhq/hw-transport-node-hid" "5.26.0"
"@ethersproject/hash@5.4.0", "@ethersproject/hash@>=5.0.0-beta.128", "@ethersproject/hash@^5.0.0", "@ethersproject/hash@^5.0.4", "@ethersproject/hash@^5.4.0":
"@ethersproject/hash@5.4.0", "@ethersproject/hash@>=5.0.0-beta.128", "@ethersproject/hash@^5.0.4", "@ethersproject/hash@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.4.0.tgz#d18a8e927e828e22860a011f39e429d388344ae0"
integrity sha512-xymAM9tmikKgbktOCjW60Z5sdouiIIurkZUr9oW5NOex5uwxrbsYG09kb5bMcNjlVeJD3yPivTNzViIs1GCbqA==
......@@ -973,7 +939,7 @@
"@ethersproject/properties" "^5.5.0"
"@ethersproject/strings" "^5.5.0"
"@ethersproject/hdnode@5.4.0", "@ethersproject/hdnode@^5.0.0", "@ethersproject/hdnode@^5.4.0":
"@ethersproject/hdnode@5.4.0", "@ethersproject/hdnode@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.4.0.tgz#4bc9999b9a12eb5ce80c5faa83114a57e4107cac"
integrity sha512-pKxdS0KAaeVGfZPp1KOiDLB0jba11tG6OP1u11QnYfb7pXn6IZx0xceqWRr6ygke8+Kw74IpOoSi7/DwANhy8Q==
......@@ -1009,7 +975,7 @@
"@ethersproject/transactions" "^5.5.0"
"@ethersproject/wordlists" "^5.5.0"
"@ethersproject/json-wallets@5.4.0", "@ethersproject/json-wallets@^5.0.0", "@ethersproject/json-wallets@^5.4.0":
"@ethersproject/json-wallets@5.4.0", "@ethersproject/json-wallets@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.4.0.tgz#2583341cfe313fc9856642e8ace3080154145e95"
integrity sha512-igWcu3fx4aiczrzEHwG1xJZo9l1cFfQOWzTqwRw/xcvxTk58q4f9M7cjh51EKphMHvrJtcezJ1gf1q1AUOfEQQ==
......@@ -1047,7 +1013,7 @@
aes-js "3.0.0"
scrypt-js "3.0.1"
"@ethersproject/keccak256@5.4.0", "@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.0", "@ethersproject/keccak256@^5.0.3", "@ethersproject/keccak256@^5.4.0":
"@ethersproject/keccak256@5.4.0", "@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.3", "@ethersproject/keccak256@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.4.0.tgz#7143b8eea4976080241d2bd92e3b1f1bf7025318"
integrity sha512-FBI1plWet+dPUvAzPAeHzRKiPpETQzqSUWR1wXJGHVWi4i8bOSrpC3NwpkPjgeXG7MnugVc1B42VbfnQikyC/A==
......@@ -1063,7 +1029,7 @@
"@ethersproject/bytes" "^5.5.0"
js-sha3 "0.8.0"
"@ethersproject/logger@5.4.0", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.0.0", "@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.4.0":
"@ethersproject/logger@5.4.0", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.4.0.tgz#f39adadf62ad610c420bcd156fd41270e91b3ca9"
integrity sha512-xYdWGGQ9P2cxBayt64d8LC8aPFJk6yWCawQi/4eJ4+oJdMMjEBMrIcIMZ9AxhwpPVmnBPrsB10PcXGmGAqgUEQ==
......@@ -1073,28 +1039,21 @@
resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d"
integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==
"@ethersproject/networks@5.4.2", "@ethersproject/networks@^5.0.0", "@ethersproject/networks@^5.4.0":
"@ethersproject/networks@5.4.2", "@ethersproject/networks@^5.4.0":
version "5.4.2"
resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.4.2.tgz#2247d977626e97e2c3b8ee73cd2457babde0ce35"
integrity sha512-eekOhvJyBnuibfJnhtK46b8HimBc5+4gqpvd1/H9LEl7Q7/qhsIhM81dI9Fcnjpk3jB1aTy6bj0hz3cifhNeYw==
dependencies:
"@ethersproject/logger" "^5.4.0"
"@ethersproject/networks@5.5.2":
"@ethersproject/networks@5.5.2", "@ethersproject/networks@^5.5.0":
version "5.5.2"
resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.5.2.tgz#784c8b1283cd2a931114ab428dae1bd00c07630b"
integrity sha512-NEqPxbGBfy6O3x4ZTISb90SjEDkWYDUbEeIFhJly0F7sZjoQMnj5KYzMSkMkLKZ+1fGpx00EDpHQCy6PrDupkQ==
dependencies:
"@ethersproject/logger" "^5.5.0"
"@ethersproject/networks@^5.5.0":
version "5.5.0"
resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.5.0.tgz#babec47cab892c51f8dd652ce7f2e3e14283981a"
integrity sha512-KWfP3xOnJeF89Uf/FCJdV1a2aDJe5XTN2N52p4fcQ34QhDqQFkgQKZ39VGtiqUgHcLI8DfT0l9azC3KFTunqtA==
dependencies:
"@ethersproject/logger" "^5.5.0"
"@ethersproject/pbkdf2@5.4.0", "@ethersproject/pbkdf2@^5.0.0", "@ethersproject/pbkdf2@^5.4.0":
"@ethersproject/pbkdf2@5.4.0", "@ethersproject/pbkdf2@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.4.0.tgz#ed88782a67fda1594c22d60d0ca911a9d669641c"
integrity sha512-x94aIv6tiA04g6BnazZSLoRXqyusawRyZWlUhKip2jvoLpzJuLb//KtMM6PEovE47pMbW+Qe1uw+68ameJjB7g==
......@@ -1110,7 +1069,7 @@
"@ethersproject/bytes" "^5.5.0"
"@ethersproject/sha2" "^5.5.0"
"@ethersproject/properties@5.4.0", "@ethersproject/properties@>=5.0.0-beta.131", "@ethersproject/properties@^5.0.0", "@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.4.0":
"@ethersproject/properties@5.4.0", "@ethersproject/properties@>=5.0.0-beta.131", "@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.4.0.tgz#38ba20539b44dcc5d5f80c45ad902017dcdbefe7"
integrity sha512-7jczalGVRAJ+XSRvNA6D5sAwT4gavLq3OXPuV/74o3Rd2wuzSL035IMpIMgei4CYyBdialJMrTqkOnzccLHn4A==
......@@ -1124,7 +1083,7 @@
dependencies:
"@ethersproject/logger" "^5.5.0"
"@ethersproject/providers@5.4.4", "@ethersproject/providers@^5.0.0":
"@ethersproject/providers@5.4.4":
version "5.4.4"
resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.4.4.tgz#6729120317942fc0ab0ecdb35e944ec6bbedb795"
integrity sha512-mQevyXj2X2D3l8p/JGDYFZbODhZjW6On15DnCK4Xc9y6b+P0vqorQC/j46omWSm4cyo7BQ/rgfhXNYmvAfyZoQ==
......@@ -1199,7 +1158,7 @@
bech32 "1.1.4"
ws "7.4.6"
"@ethersproject/random@5.4.0", "@ethersproject/random@^5.0.0", "@ethersproject/random@^5.4.0":
"@ethersproject/random@5.4.0", "@ethersproject/random@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.4.0.tgz#9cdde60e160d024be39cc16f8de3b9ce39191e16"
integrity sha512-pnpWNQlf0VAZDEOVp1rsYQosmv2o0ITS/PecNw+mS2/btF8eYdspkN0vIXrCMtkX09EAh9bdk8GoXmFXM1eAKw==
......@@ -1223,7 +1182,7 @@
"@ethersproject/bytes" "^5.5.0"
"@ethersproject/logger" "^5.5.0"
"@ethersproject/rlp@5.4.0", "@ethersproject/rlp@^5.0.0", "@ethersproject/rlp@^5.4.0":
"@ethersproject/rlp@5.4.0", "@ethersproject/rlp@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.4.0.tgz#de61afda5ff979454e76d3b3310a6c32ad060931"
integrity sha512-0I7MZKfi+T5+G8atId9QaQKHRvvasM/kqLyAH4XxBCBchAooH2EX5rL9kYZWwcm3awYV+XC7VF6nLhfeQFKVPg==
......@@ -1239,7 +1198,7 @@
"@ethersproject/bytes" "^5.5.0"
"@ethersproject/logger" "^5.5.0"
"@ethersproject/sha2@5.4.0", "@ethersproject/sha2@^5.0.0", "@ethersproject/sha2@^5.4.0":
"@ethersproject/sha2@5.4.0", "@ethersproject/sha2@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.4.0.tgz#c9a8db1037014cbc4e9482bd662f86c090440371"
integrity sha512-siheo36r1WD7Cy+bDdE1BJ8y0bDtqXCOxRMzPa4bV1TGt/eTUUt03BHoJNB6reWJD8A30E/pdJ8WFkq+/uz4Gg==
......@@ -1257,7 +1216,7 @@
"@ethersproject/logger" "^5.5.0"
hash.js "1.1.7"
"@ethersproject/signing-key@5.4.0", "@ethersproject/signing-key@^5.0.0", "@ethersproject/signing-key@^5.4.0":
"@ethersproject/signing-key@5.4.0", "@ethersproject/signing-key@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.4.0.tgz#2f05120984e81cf89a3d5f6dec5c68ee0894fbec"
integrity sha512-q8POUeywx6AKg2/jX9qBYZIAmKSB4ubGXdQ88l40hmATj29JnG5pp331nAWwwxPn2Qao4JpWHNZsQN+bPiSW9A==
......@@ -1281,7 +1240,7 @@
elliptic "6.5.4"
hash.js "1.1.7"
"@ethersproject/solidity@5.4.0", "@ethersproject/solidity@^5.0.0", "@ethersproject/solidity@^5.0.9", "@ethersproject/solidity@^5.4.0":
"@ethersproject/solidity@5.4.0", "@ethersproject/solidity@^5.0.9", "@ethersproject/solidity@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.4.0.tgz#1305e058ea02dc4891df18b33232b11a14ece9ec"
integrity sha512-XFQTZ7wFSHOhHcV1DpcWj7VXECEiSrBuv7JErJvB9Uo+KfCdc3QtUZV+Vjh/AAaYgezUEKbCtE6Khjm44seevQ==
......@@ -1304,7 +1263,7 @@
"@ethersproject/sha2" "^5.5.0"
"@ethersproject/strings" "^5.5.0"
"@ethersproject/strings@5.4.0", "@ethersproject/strings@>=5.0.0-beta.130", "@ethersproject/strings@^5.0.0", "@ethersproject/strings@^5.0.4", "@ethersproject/strings@^5.4.0":
"@ethersproject/strings@5.4.0", "@ethersproject/strings@>=5.0.0-beta.130", "@ethersproject/strings@^5.0.4", "@ethersproject/strings@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.4.0.tgz#fb12270132dd84b02906a8d895ae7e7fa3d07d9a"
integrity sha512-k/9DkH5UGDhv7aReXLluFG5ExurwtIpUfnDNhQA29w896Dw3i4uDTz01Quaptbks1Uj9kI8wo9tmW73wcIEaWA==
......@@ -1322,7 +1281,7 @@
"@ethersproject/constants" "^5.5.0"
"@ethersproject/logger" "^5.5.0"
"@ethersproject/transactions@5.4.0", "@ethersproject/transactions@^5.0.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.4.0":
"@ethersproject/transactions@5.4.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.4.0.tgz#a159d035179334bd92f340ce0f77e83e9e1522e0"
integrity sha512-s3EjZZt7xa4BkLknJZ98QGoIza94rVjaEed0rzZ/jB9WrIuu/1+tjvYCWzVrystXtDswy7TPBeIepyXwSYa4WQ==
......@@ -1352,7 +1311,7 @@
"@ethersproject/rlp" "^5.5.0"
"@ethersproject/signing-key" "^5.5.0"
"@ethersproject/units@5.4.0", "@ethersproject/units@^5.0.0":
"@ethersproject/units@5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.4.0.tgz#d57477a4498b14b88b10396062c8cbbaf20c79fe"
integrity sha512-Z88krX40KCp+JqPCP5oPv5p750g+uU6gopDYRTBGcDvOASh6qhiEYCRatuM/suC4S2XW9Zz90QI35MfSrTIaFg==
......@@ -1370,7 +1329,7 @@
"@ethersproject/constants" "^5.5.0"
"@ethersproject/logger" "^5.5.0"
"@ethersproject/wallet@5.4.0", "@ethersproject/wallet@^5.0.0", "@ethersproject/wallet@^5.4.0":
"@ethersproject/wallet@5.4.0", "@ethersproject/wallet@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.4.0.tgz#fa5b59830b42e9be56eadd45a16a2e0933ad9353"
integrity sha512-wU29majLjM6AjCjpat21mPPviG+EpK7wY1+jzKD0fg3ui5fgedf2zEu1RDgpfIMsfn8fJHJuzM4zXZ2+hSHaSQ==
......@@ -1412,7 +1371,7 @@
"@ethersproject/transactions" "^5.5.0"
"@ethersproject/wordlists" "^5.5.0"
"@ethersproject/web@5.4.0", "@ethersproject/web@^5.0.0", "@ethersproject/web@^5.4.0":
"@ethersproject/web@5.4.0", "@ethersproject/web@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.4.0.tgz#49fac173b96992334ed36a175538ba07a7413d1f"
integrity sha512-1bUusGmcoRLYgMn6c1BLk1tOKUIFuTg8j+6N8lYlbMpDesnle+i3pGSagGNvwjaiLo4Y5gBibwctpPRmjrh4Og==
......@@ -1423,7 +1382,7 @@
"@ethersproject/properties" "^5.4.0"
"@ethersproject/strings" "^5.4.0"
"@ethersproject/web@5.5.1", "@ethersproject/web@^5.5.1":
"@ethersproject/web@5.5.1", "@ethersproject/web@^5.5.0", "@ethersproject/web@^5.5.1":
version "5.5.1"
resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.5.1.tgz#cfcc4a074a6936c657878ac58917a61341681316"
integrity sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg==
......@@ -1434,18 +1393,7 @@
"@ethersproject/properties" "^5.5.0"
"@ethersproject/strings" "^5.5.0"
"@ethersproject/web@^5.5.0":
version "5.5.0"
resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.5.0.tgz#0e5bb21a2b58fb4960a705bfc6522a6acf461e28"
integrity sha512-BEgY0eL5oH4mAo37TNYVrFeHsIXLRxggCRG/ksRIxI2X5uj5IsjGmcNiRN/VirQOlBxcUhCgHhaDLG4m6XAVoA==
dependencies:
"@ethersproject/base64" "^5.5.0"
"@ethersproject/bytes" "^5.5.0"
"@ethersproject/logger" "^5.5.0"
"@ethersproject/properties" "^5.5.0"
"@ethersproject/strings" "^5.5.0"
"@ethersproject/wordlists@5.4.0", "@ethersproject/wordlists@^5.0.0", "@ethersproject/wordlists@^5.4.0":
"@ethersproject/wordlists@5.4.0", "@ethersproject/wordlists@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.4.0.tgz#f34205ec3bbc9e2c49cadaee774cf0b07e7573d7"
integrity sha512-FemEkf6a+EBKEPxlzeVgUaVSodU7G0Na89jqKjmWMlDB0tomoU8RlEMgUvXyqtrg8N4cwpLh8nyRnm1Nay1isA==
......@@ -2541,11 +2489,6 @@
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.3.2.tgz#ff80affd6d352dbe1bbc5b4e1833c41afd6283b6"
integrity sha512-AybF1cesONZStg5kWf6ao9OlqTZuPqddvprc0ky7lrUVOjXeKpmQ2Y9FK+6ygxasb+4aic4O5pneFBfwVsRRRg==
"@openzeppelin/contracts@^3.3.0":
version "3.4.2"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.2.tgz#d81f786fda2871d1eb8a8c5a73e455753ba53527"
integrity sha512-z0zMCjyhhp4y7XKAcDAi3Vgms4T2PstwBdahiO0+9NaGICQKjynK3wduSRplTgk4LXmoO1yfDGO5RbjKYxtuxA==
"@openzeppelin/contracts@^4.3.2":
version "4.3.3"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.3.3.tgz#ff6ee919fc2a1abaf72b22814bfb72ed129ec137"
......@@ -2741,20 +2684,13 @@
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1":
"@sinonjs/commons@^1.7.0":
version "1.8.3"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"
integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==
dependencies:
type-detect "4.0.8"
"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40"
integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==
dependencies:
"@sinonjs/commons" "^1.7.0"
"@sinonjs/fake-timers@^7.1.0":
version "7.1.2"
resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz#2524eae70c4910edccf99b2f4e6efc5894aff7b5"
......@@ -2762,20 +2698,6 @@
dependencies:
"@sinonjs/commons" "^1.7.0"
"@sinonjs/samsam@^5.3.1":
version "5.3.1"
resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.3.1.tgz#375a45fe6ed4e92fca2fb920e007c48232a6507f"
integrity sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg==
dependencies:
"@sinonjs/commons" "^1.6.0"
lodash.get "^4.4.2"
type-detect "^4.0.8"
"@sinonjs/text-encoding@^0.7.1":
version "0.7.1"
resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5"
integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==
"@solidity-parser/parser@^0.11.0":
version "0.11.1"
resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.11.1.tgz#fa840af64840c930f24a9c82c08d4a092a068add"
......@@ -2882,11 +2804,6 @@
resolved "https://registry.yarnpkg.com/@types/abstract-leveldown/-/abstract-leveldown-5.0.2.tgz#ee81917fe38f770e29eec8139b6f16ee4a8b0a5f"
integrity sha512-+jA1XXF3jsz+Z7FcuiNqgK53hTa/luglT2TyTpKPqoYbxVY+mCPF22Rm+q3KPBrMHJwNXFrTViHszBOfU4vftQ==
"@types/bluebird@^3.5.34":
version "3.5.36"
resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.36.tgz#00d9301d4dc35c2f6465a8aec634bb533674c652"
integrity sha512-HBNx4lhkxN7bx6P0++W8E289foSu8kO8GCk2unhuVggO+cE7rh9DhZUyPhUxNRG9m+5B5BTKxZQ5ZP92x/mx9Q==
"@types/bn.js@*", "@types/bn.js@^5.1.0":
version "5.1.0"
resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68"
......@@ -3193,7 +3110,7 @@
"@types/mime" "^1"
"@types/node" "*"
"@types/sinon-chai@^3.2.3", "@types/sinon-chai@^3.2.5":
"@types/sinon-chai@^3.2.3":
version "3.2.5"
resolved "https://registry.yarnpkg.com/@types/sinon-chai/-/sinon-chai-3.2.5.tgz#df21ae57b10757da0b26f512145c065f2ad45c48"
integrity sha512-bKQqIpew7mmIGNRlxW6Zli/QVyc3zikpGzCa797B/tRnD9OtHvZ/ts8sYXV+Ilj9u3QRaUEM8xrjgd1gwm1BpQ==
......@@ -3208,18 +3125,6 @@
dependencies:
"@sinonjs/fake-timers" "^7.1.0"
"@types/sinon@^9.0.10":
version "9.0.11"
resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.11.tgz#7af202dda5253a847b511c929d8b6dda170562eb"
integrity sha512-PwP4UY33SeeVKodNE37ZlOsR9cReypbMJOhZ7BVE0lB+Hix3efCOxiJWiE5Ia+yL9Cn2Ch72EjFTRze8RZsNtg==
dependencies:
"@types/sinonjs__fake-timers" "*"
"@types/sinonjs__fake-timers@*":
version "6.0.3"
resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.3.tgz#79df6f358ae8f79e628fe35a63608a0ea8e7cf08"
integrity sha512-E1dU4fzC9wN2QK2Cr1MLCfyHM8BoNnRFvuf45LYMPNDA+WqbNzC45S4UzPxvp1fFJ1rvSGU0bPvdd35VLmXG8g==
"@types/tz-offset@*":
version "0.0.0"
resolved "https://registry.yarnpkg.com/@types/tz-offset/-/tz-offset-0.0.0.tgz#d58f1cebd794148d245420f8f0660305d320e565"
......@@ -4612,7 +4517,7 @@ blakejs@^1.1.0:
resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.1.tgz#bf313053978b2cd4c444a48795710be05c785702"
integrity sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg==
bluebird@^3.5.0, bluebird@^3.5.2, bluebird@^3.5.3, bluebird@^3.5.5, bluebird@^3.7.2:
bluebird@^3.5.0, bluebird@^3.5.2, bluebird@^3.5.3, bluebird@^3.5.5:
version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
......@@ -4850,6 +4755,11 @@ bufferutil@^4.0.1:
dependencies:
node-gyp-build "^4.2.0"
bufio@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/bufio/-/bufio-1.0.7.tgz#b7f63a1369a0829ed64cc14edf0573b3e382a33e"
integrity sha512-bd1dDQhiC+bEbEfg56IdBv7faWa6OipMs/AFFFvtFnB3wAYjlwQpQRZ0pm6ZkgtfL0pILRXhKxOiQj6UzoMR7A==
builtin-modules@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887"
......@@ -6178,7 +6088,7 @@ diff@5.0.0, diff@^5.0.0:
resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
diff@^4.0.1, diff@^4.0.2:
diff@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
......@@ -7251,41 +7161,6 @@ ethereumjs-wallet@0.6.5:
utf8 "^3.0.0"
uuid "^3.3.2"
ethers@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.0.0.tgz#76558a3020766f310a49f4e1a4c6c1e331761abd"
integrity sha512-uOSACd2E8dg8XuiOewpL42uFH7SvrkA5k0oGkHoqSJl2lflrMPV+7ciWzyuPBjyHnOFvAPPJUpsXrwpFKaLFww==
dependencies:
"@ethersproject/abi" "^5.0.0"
"@ethersproject/abstract-provider" "^5.0.0"
"@ethersproject/abstract-signer" "^5.0.0"
"@ethersproject/address" "^5.0.0"
"@ethersproject/base64" "^5.0.0"
"@ethersproject/bignumber" "^5.0.0"
"@ethersproject/bytes" "^5.0.0"
"@ethersproject/constants" "^5.0.0"
"@ethersproject/contracts" "^5.0.0"
"@ethersproject/hash" "^5.0.0"
"@ethersproject/hdnode" "^5.0.0"
"@ethersproject/json-wallets" "^5.0.0"
"@ethersproject/keccak256" "^5.0.0"
"@ethersproject/logger" "^5.0.0"
"@ethersproject/networks" "^5.0.0"
"@ethersproject/pbkdf2" "^5.0.0"
"@ethersproject/properties" "^5.0.0"
"@ethersproject/providers" "^5.0.0"
"@ethersproject/random" "^5.0.0"
"@ethersproject/rlp" "^5.0.0"
"@ethersproject/sha2" "^5.0.0"
"@ethersproject/signing-key" "^5.0.0"
"@ethersproject/solidity" "^5.0.0"
"@ethersproject/strings" "^5.0.0"
"@ethersproject/transactions" "^5.0.0"
"@ethersproject/units" "^5.0.0"
"@ethersproject/wallet" "^5.0.0"
"@ethersproject/web" "^5.0.0"
"@ethersproject/wordlists" "^5.0.0"
ethers@^4.0.32, ethers@^4.0.40:
version "4.0.49"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.49.tgz#0eb0e9161a0c8b4761be547396bbe2fb121a8894"
......@@ -7301,7 +7176,7 @@ ethers@^4.0.32, ethers@^4.0.40:
uuid "2.0.1"
xmlhttprequest "1.8.0"
ethers@^5.0.0, ethers@^5.0.1, ethers@^5.0.2, ethers@^5.4.0, ethers@^5.4.5:
ethers@^5.0.0, ethers@^5.0.1, ethers@^5.0.2, ethers@^5.4.5:
version "5.4.5"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.4.5.tgz#cec133b9f5b514dc55e2561ee7aa7218c33affd7"
integrity sha512-PPZ6flOAj230sXEWf/r/It6ZZ5c7EOVWx+PU87Glkbg79OtT7pLE1WgL4MRdwx6iF7HzSOvUUI+8cAmcdzo12w==
......@@ -8082,7 +7957,7 @@ ganache-cli@^6.12.2:
source-map-support "0.5.12"
yargs "13.2.4"
ganache-core@^2.12.1, ganache-core@^2.13.2:
ganache-core@^2.13.2:
version "2.13.2"
resolved "https://registry.yarnpkg.com/ganache-core/-/ganache-core-2.13.2.tgz#27e6fc5417c10e6e76e2e646671869d7665814a3"
integrity sha512-tIF5cR+ANQz0+3pHWxHjIwHqFXcVo0Mb+kcsNhglNFALcYo49aQpnS9dqHartqPfMFjiHh/qFoD3mYK0d/qGgw==
......@@ -9878,11 +9753,6 @@ jsprim@^1.2.2:
array-includes "^3.1.2"
object.assign "^4.1.2"
just-extend@^4.0.2:
version "4.2.1"
resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744"
integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==
keccak@3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.1.tgz#ae30a0e94dbe43414f741375cff6d64c8bea0bff"
......@@ -10390,11 +10260,6 @@ lodash.flattendeep@^4.4.0:
resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
......@@ -11376,17 +11241,6 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
nise@^4.0.4:
version "4.1.0"
resolved "https://registry.yarnpkg.com/nise/-/nise-4.1.0.tgz#8fb75a26e90b99202fa1e63f448f58efbcdedaf6"
integrity sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA==
dependencies:
"@sinonjs/commons" "^1.7.0"
"@sinonjs/fake-timers" "^6.0.0"
"@sinonjs/text-encoding" "^0.7.1"
just-extend "^4.0.2"
path-to-regexp "^1.7.0"
node-abi@^2.18.0, node-abi@^2.21.0, node-abi@^2.7.0:
version "2.30.0"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.30.0.tgz#8be53bf3e7945a34eea10e0fc9a5982776cf550b"
......@@ -11890,18 +11744,6 @@ oboe@2.1.5:
dependencies:
http-https "^1.0.0"
"old-contracts@npm:@eth-optimism/contracts@^0.0.2-alpha.7":
version "0.0.2-alpha.15"
resolved "https://registry.yarnpkg.com/@eth-optimism/contracts/-/contracts-0.0.2-alpha.15.tgz#d602bdb6f1186d111ce9f7c282fc80e023086892"
integrity sha512-upJfYHDWQY7nM0AYT9MKQiuOus2uMUhvqS962qiBq3Ly/9GUq5mS0UALynsrZBGbzT6pflOMKFFEv7jQEORGmA==
dependencies:
"@eth-optimism/solc" "^0.6.12-alpha.1"
"@ethersproject/contracts" "^5.0.5"
"@ethersproject/hardware-wallets" "^5.0.8"
"@openzeppelin/contracts" "^3.3.0"
ethers "5.0.0"
ganache-core "^2.12.1"
on-finished@^2.3.0, on-finished@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
......@@ -12362,13 +12204,6 @@ path-to-regexp@0.1.7:
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
path-to-regexp@^1.7.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a"
integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
dependencies:
isarray "0.0.1"
path-type@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
......@@ -13902,23 +13737,6 @@ simple-get@^3.0.3:
once "^1.3.1"
simple-concat "^1.0.0"
sinon-chai@^3.5.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-3.7.0.tgz#cfb7dec1c50990ed18c153f1840721cf13139783"
integrity sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g==
sinon@^9.2.4:
version "9.2.4"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.2.4.tgz#e55af4d3b174a4443a8762fa8421c2976683752b"
integrity sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==
dependencies:
"@sinonjs/commons" "^1.8.1"
"@sinonjs/fake-timers" "^6.0.1"
"@sinonjs/samsam" "^5.3.1"
diff "^4.0.2"
nise "^4.0.4"
supports-color "^7.1.0"
slash@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
......@@ -15216,7 +15034,7 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"
type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8:
type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5:
version "4.0.8"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
......
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