Commit 53f12201 authored by coolhill's avatar coolhill Committed by GitHub

Merge branch 'ethereum-optimism:develop' into increase-coverage-issue

parents 9c635da8 fde2c76a
---
'@eth-optimism/data-transport-layer': patch
---
Smaller filter query for searching for L1 start height. This number should be configured so that the search does not need to happen because using a smaller filter will cause it to take too long.
---
'@eth-optimism/gas-oracle': patch
'@eth-optimism/contracts': patch
'@eth-optimism/data-transport-layer': patch
---
String update to change the system name from OE to Optimism
---
'@eth-optimism/message-relayer': patch
---
Fix docker build
---
'@eth-optimism/l2geth': patch
---
expose ErrNonceTooHigh from miner
---
'@eth-optimism/contracts': patch
---
Remove legacy bin/deploy.ts script
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
post-steps:
- slack/notify:
channel: $SLACK_DEFAULT_CHANNEL
event: fail
custom: |
{
"text": "",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "🔴 Nightly build failed!"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Job"
},
"url": "${CIRCLE_BUILD_URL}"
}
]
}
]
}
commands:
build-dockerfile:
parameters:
image-name:
description: Image name
type: string
target:
description: Dockerfile target
type: string
default: ""
dockerfile:
description: Dockerfile to use
type: string
steps:
- checkout
- setup_remote_docker:
version: 19.03.13
- run:
name: Build
command: |
echo -n "$STACKMAN_REPO_AUTH" | docker login -u _json_key --password-stdin https://us-east4-docker.pkg.dev
docker build -t "$STACKMAN_REPO/<<parameters.image-name>>:nightly" -f <<parameters.dockerfile>> <<#parameters.target>>--target <<parameters.target>><</parameters.target>> .
docker push "$STACKMAN_REPO/<<parameters.image-name>>:nightly"
jobs:
build-dtl:
docker:
- image: cimg/base:2021.04
steps:
- build-dockerfile:
image-name: data-transport-layer
target: data-transport-layer
dockerfile: ./ops/docker/Dockerfile.packages
build-batch-submitter:
docker:
- image: cimg/base:2021.04
steps:
- build-dockerfile:
image-name: batch-submitter
target: batch-submitter
dockerfile: ./ops/docker/Dockerfile.packages
build-go-batch-submitter:
docker:
- image: cimg/base:2021.04
steps:
- build-dockerfile:
image-name: go-batch-submitter
dockerfile: ./ops/docker/Dockerfile.batch-submitter-service
build-deployer:
docker:
- image: cimg/base:2021.04
steps:
- build-dockerfile:
image-name: deployer
target: deployer
dockerfile: ./ops/docker/Dockerfile.packages
build-l2geth:
docker:
- image: cimg/base:2021.04
steps:
- build-dockerfile:
image-name: l2geth
dockerfile: ./ops/docker/Dockerfile.geth
build-gas-oracle:
docker:
- image: cimg/base:2021.04
steps:
- build-dockerfile:
image-name: gas-oracle
dockerfile: ./ops/docker/Dockerfile.gas-oracle
build-integration-tests:
docker:
- image: cimg/base:2021.04
steps:
- build-dockerfile:
image-name: integration-tests
target: integration-tests
dockerfile: ./ops/docker/Dockerfile.packages
build-proxyd:
docker:
- image: cimg/base:2021.04
steps:
- build-dockerfile:
image-name: proxyd
dockerfile: ./go/proxyd/Dockerfile
deploy-nightly:
docker:
- image: cimg/base:2021.04
steps:
- gcp-gke/install
- gcp-gke/update-kubeconfig-with-credentials:
cluster: $STACKMAN_CLUSTER
gcloud-service-key: STACKMAN_SERVICE_KEY
google-compute-region: STACKMAN_COMPUTE_REGION
google-compute-zone: STACKMAN_COMPUTE_ZONE
google-project-id: STACKMAN_PROJECT_ID
install-kubectl: yes
perform-login: yes
- run:
name: Deploy
command: |
echo "Current nightly pods:"
kubectl get pods --namespace nightly
echo "Redeploying pods:"
kubectl rollout restart statefulset nightly-sequencer --namespace nightly
kubectl rollout restart statefulset nightly-go-batch-submitter --namespace nightly
kubectl rollout restart statefulset nightly-dtl --namespace nightly
kubectl rollout restart deployment nightly-gas-oracle --namespace nightly
kubectl rollout restart deployment edge-proxyd --namespace nightly
notify:
docker:
- image: cimg/base:2021.04
steps:
- run:
name: Success
command: |
echo "Dummy job."
workflows:
nightly:
triggers:
- schedule:
cron: "0 0 * * * "
filters:
branches:
only:
- develop
jobs:
- build-dtl:
context:
- optimism
- slack
<<: *slack-fail-post-step
- build-batch-submitter:
context:
- optimism
- slack
<<: *slack-fail-post-step
- build-deployer:
context:
- optimism
- slack
<<: *slack-fail-post-step
- build-l2geth:
context:
- optimism
- slack
<<: *slack-fail-post-step
- build-gas-oracle:
context:
- optimism
- slack
<<: *slack-fail-post-step
- build-integration-tests:
context:
- optimism
- slack
<<: *slack-fail-post-step
- build-go-batch-submitter:
context:
- optimism
- slack
<<: *slack-fail-post-step
- build-proxyd:
context:
- optimism
- slack
<<: *slack-fail-post-step
- deploy-nightly:
context:
- optimism
- slack
<<: *slack-fail-post-step
requires:
- build-dtl
- build-batch-submitter
- build-go-batch-submitter
- build-deployer
- build-l2geth
- build-gas-oracle
- build-integration-tests
- build-proxyd
- notify:
context: slack
requires:
- deploy-nightly
post-steps:
- slack/notify:
custom: |
{
"text": "",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "✅ Nightly successfully deployed."
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Job"
},
"url": "${CIRCLE_BUILD_URL}"
}
]
}
]
}
event: always
\ No newline at end of file
......@@ -28,6 +28,7 @@ jobs:
proxyd: ${{ steps.packages.outputs.proxyd }}
rpc-proxy : ${{ steps.packages.outputs.rpc-proxy }}
op-exporter : ${{ steps.packages.outputs.op-exporter }}
l2geth-exporter : ${{ steps.packages.outputs.l2geth-exporter }}
steps:
- name: Check out source code
......@@ -443,6 +444,43 @@ jobs:
GITCOMMIT=${{ steps.build_args.outputs.GITCOMMIT }}
GITVERSION=${{ steps.build_args.outputs.GITVERSION }}
l2geth-exporter:
name: Publish l2geth-exporter Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish
if: needs.canary-publish.outputs.l2geth-exporter != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Set build args
id: build_args
run: |
echo ::set-output name=GITDATE::"$(date +%d-%m-%Y)"
echo ::set-output name=GITVERSION::$(jq -r .version ./go/l2geth-exporter/package.json)
echo ::set-output name=GITCOMMIT::"$GITHUB_SHA"
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.l2geth-exporter
push: true
tags: ethereumoptimism/l2geth-exporter:${{ needs.canary-publish.outputs.l2geth-exporter }}
build-args: |
GITDATE=${{ steps.build_args.outputs.GITDATE }}
GITCOMMIT=${{ steps.build_args.outputs.GITCOMMIT }}
GITVERSION=${{ steps.build_args.outputs.GITVERSION }}
rpc-proxy:
name: Publish rpc-proxy Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish
......
......@@ -24,6 +24,7 @@ jobs:
rpc-proxy: ${{ steps.packages.outputs.rpc-proxy }}
hardhat-node: ${{ steps.packages.outputs.hardhat-node }}
op-exporter : ${{ steps.packages.outputs.op-exporter }}
l2geth-exporter : ${{ steps.packages.outputs.l2geth-exporter }}
steps:
- name: Checkout Repo
......@@ -196,6 +197,43 @@ jobs:
GITCOMMIT=${{ steps.build_args.outputs.GITCOMMIT }}
GITVERSION=${{ steps.build_args.outputs.GITVERSION }}
l2geth-exporter:
name: Publish l2geth-exporter Version ${{ needs.release.outputs.l2geth-exporter}}
needs: release
if: needs.release.outputs.l2geth-exporter != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Set build args
id: build_args
run: |
echo ::set-output name=GITDATE::"$(date +%d-%m-%Y)"
echo ::set-output name=GITVERSION::$(jq -r .version ./go/l2geth-exporter/package.json)
echo ::set-output name=GITCOMMIT::"$GITHUB_SHA"
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.l2geth-exporter
push: true
tags: ethereumoptimism/l2geth-exporter:${{ needs.release.outputs.l2geth-exporter }},ethereumoptimism/l2geth-exporter:latest
build-args: |
GITDATE=${{ steps.build_args.outputs.GITDATE }}
GITCOMMIT=${{ steps.build_args.outputs.GITCOMMIT }}
GITVERSION=${{ steps.build_args.outputs.GITVERSION }}
op-exporter:
name: Publish op-exporter Version ${{ needs.release.outputs.op-exporter}}
needs: release
......
......@@ -131,6 +131,13 @@ jobs:
fail_ci_if_error: false
verbose: true
flags: message-relayer
- uses: codecov/codecov-action@v1
with:
files: ./packages/sdk/coverage.json
fail_ci_if_error: false
verbose: true
flags: sdk
lint:
name: Linting
runs-on: ubuntu-latest
......
......@@ -98,7 +98,7 @@ Use the above commands to recompile the packages.
### Building the rest of the system
If you want to run an Optimistic Ethereum node OR **if you want to run the integration tests**, you'll need to build the rest of the system.
If you want to run an Optimism node OR **if you want to run the integration tests**, you'll need to build the rest of the system.
```bash
cd ops
......
......@@ -11,7 +11,7 @@
## TL;DR
This is the primary place where [Optimism](https://optimism.io) works on stuff related to [Optimistic Ethereum](https://optimistic.etherscan.io/).
This is where [Optimism](https://optimism.io) gets built.
## Documentation
......@@ -31,16 +31,16 @@ Then check out our list of [good first issues](https://github.com/ethereum-optim
<pre>
root
├── <a href="./packages">packages</a>
│ ├── <a href="./packages/contracts">contracts</a>: L1 and L2 smart contracts for Optimistic Ethereum
│ ├── <a href="./packages/core-utils">core-utils</a>: Low-level utilities that make building Optimistic Ethereum easier
│ ├── <a href="./packages/contracts">contracts</a>: L1 and L2 smart contracts for Optimism
│ ├── <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 Optimistic Ethereum-related L1 data
│ ├── <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="./l2geth">l2geth</a>: Optimistic Ethereum client software, a fork of <a href="https://github.com/ethereum/go-ethereum/tree/v1.9.10">geth v1.9.10</a>
├── <a href="./integration-tests">integration-tests</a>: Various integration tests for an Optimistic Ethereum network
└── <a href="./ops">ops</a>: Tools for running Optimistic Ethereum nodes and networks
├── <a href="./l2geth">l2geth</a>: Optimism client software, a fork of <a href="https://github.com/ethereum/go-ethereum/tree/v1.9.10">geth v1.9.10</a>
├── <a href="./integration-tests">integration-tests</a>: Various integration tests for the Optimism network
└── <a href="./ops">ops</a>: Tools for running Optimism nodes and networks
</pre>
## Branching Model and Releases
......@@ -64,7 +64,7 @@ Please read the linked post if you're planning to make frequent PRs into this re
The `master` branch contains the code for our latest "stable" releases.
Updates from `master` always come from the `develop` branch.
We only ever update the `master` branch when we intend to deploy code within the `develop` to the Optimistic Ethereum mainnet.
We only ever update the `master` branch when we intend to deploy code within the `develop` to the Optimism mainnet.
Our update process takes the form of a PR merging the `develop` branch into the `master` branch.
### The `develop` branch
......
# @eth-optimism/batch-submitter-service
## 0.0.2
### Patch Changes
- d6e0de5a: Fix metrics server
......@@ -119,8 +119,6 @@ func NewBatchSubmitter(cfg Config, gitVersion string) (*BatchSubmitter, error) {
log.Root().SetHandler(log.LvlFilterHandler(logLevel, logHandler))
log.Info("Config", "config", fmt.Sprintf("%#v", cfg))
// Parse sequencer private key and CTC contract address.
sequencerPrivKey, ctcAddress, err := parseWalletPrivKeyAndContractAddr(
"Sequencer", cfg.Mnemonic, cfg.SequencerHDPath,
......@@ -171,7 +169,7 @@ func NewBatchSubmitter(cfg Config, gitVersion string) (*BatchSubmitter, error) {
var batchTxService *Service
if cfg.RunTxBatchSubmitter {
batchTxDriver, err := sequencer.NewDriver(sequencer.Config{
Name: "SEQUENCER",
Name: "Sequencer",
L1Client: l1Client,
L2Client: l2Client,
BlockOffset: cfg.BlockOffset,
......@@ -196,7 +194,7 @@ func NewBatchSubmitter(cfg Config, gitVersion string) (*BatchSubmitter, error) {
var batchStateService *Service
if cfg.RunStateBatchSubmitter {
batchStateDriver, err := proposer.NewDriver(proposer.Config{
Name: "PROPOSER",
Name: "Proposer",
L1Client: l1Client,
L2Client: l2Client,
BlockOffset: cfg.BlockOffset,
......@@ -295,7 +293,7 @@ func parseWalletPrivKeyAndContractAddr(
// NOTE: This method MUST be run as a goroutine.
func runMetricsServer(hostname string, port uint64) {
metricsPortStr := strconv.FormatUint(port, 10)
metricsAddr := fmt.Sprintf("%s: %s", hostname, metricsPortStr)
metricsAddr := fmt.Sprintf("%s:%s", hostname, metricsPortStr)
http.Handle("/metrics", promhttp.Handler())
_ = http.ListenAndServe(metricsAddr, nil)
......
......@@ -5,9 +5,11 @@ import (
"crypto/ecdsa"
"fmt"
"math/big"
"time"
"github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/ctc"
"github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/scc"
"github.com/ethereum-optimism/optimism/go/batch-submitter/metrics"
l2types "github.com/ethereum-optimism/optimism/l2geth/core/types"
l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
......@@ -36,6 +38,7 @@ type Driver struct {
sccContract *scc.StateCommitmentChain
ctcContract *ctc.CanonicalTransactionChain
walletAddr common.Address
metrics *metrics.Metrics
}
func NewDriver(cfg Config) (*Driver, error) {
......@@ -60,6 +63,7 @@ func NewDriver(cfg Config) (*Driver, error) {
sccContract: sccContract,
ctcContract: ctcContract,
walletAddr: walletAddr,
metrics: metrics.NewMetrics(cfg.Name),
}, nil
}
......@@ -73,6 +77,11 @@ func (d *Driver) WalletAddr() common.Address {
return d.walletAddr
}
// Metrics returns the subservice telemetry object.
func (d *Driver) Metrics() *metrics.Metrics {
return d.metrics
}
// 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.
......@@ -121,6 +130,8 @@ func (d *Driver) SubmitBatchTx(
ctx context.Context,
start, end, nonce, gasPrice *big.Int) (*types.Transaction, error) {
batchTxBuildStart := time.Now()
var blocks []*l2types.Block
for i := new(big.Int).Set(start); i.Cmp(end) < 0; i.Add(i, bigOne) {
block, err := d.cfg.L2Client.BlockByNumber(ctx, i)
......@@ -139,6 +150,10 @@ func (d *Driver) SubmitBatchTx(
stateRoots = append(stateRoots, block.Root())
}
batchTxBuildTime := float64(time.Since(batchTxBuildStart) / time.Millisecond)
d.metrics.BatchTxBuildTime.Set(batchTxBuildTime)
d.metrics.NumTxPerBatch.Observe(float64(len(blocks)))
opts, err := bind.NewKeyedTransactorWithChainID(
d.cfg.PrivKey, d.cfg.ChainID,
)
......
......@@ -7,8 +7,10 @@ import (
"fmt"
"math/big"
"strings"
"time"
"github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/ctc"
"github.com/ethereum-optimism/optimism/go/batch-submitter/metrics"
l2types "github.com/ethereum-optimism/optimism/l2geth/core/types"
l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum/go-ethereum/accounts/abi"
......@@ -43,6 +45,7 @@ type Driver struct {
rawCtcContract *bind.BoundContract
walletAddr common.Address
ctcABI *abi.ABI
metrics *metrics.Metrics
}
func NewDriver(cfg Config) (*Driver, error) {
......@@ -78,6 +81,7 @@ func NewDriver(cfg Config) (*Driver, error) {
rawCtcContract: rawCtcContract,
walletAddr: walletAddr,
ctcABI: ctcABI,
metrics: metrics.NewMetrics(cfg.Name),
}, nil
}
......@@ -91,6 +95,11 @@ func (d *Driver) WalletAddr() common.Address {
return d.walletAddr
}
// Metrics returns the subservice telemetry object.
func (d *Driver) Metrics() *metrics.Metrics {
return d.metrics
}
// 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.
......@@ -136,6 +145,8 @@ func (d *Driver) SubmitBatchTx(
log.Info(name+" submitting batch tx", "start", start, "end", end,
"gasPrice", gasPrice)
batchTxBuildStart := time.Now()
var blocks []*l2types.Block
for i := new(big.Int).Set(start); i.Cmp(end) < 0; i.Add(i, bigOne) {
block, err := d.cfg.L2Client.BlockByNumber(ctx, i)
......@@ -176,6 +187,11 @@ func (d *Driver) SubmitBatchTx(
panic("call data too large")
}
// Record the batch_tx_build_time.
batchTxBuildTime := float64(time.Since(batchTxBuildStart) / time.Millisecond)
d.metrics.BatchTxBuildTime.Set(batchTxBuildTime)
d.metrics.NumTxPerBatch.Observe(float64(len(blocks)))
log.Info(name+" batch call data", "data", hex.EncodeToString(batchCallData))
opts, err := bind.NewKeyedTransactorWithChainID(
......
package metrics
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
type Metrics struct {
// ETHBalance tracks the amount of ETH in the submitter's account.
ETHBalance prometheus.Gauge
// BatchSizeInBytes tracks the size of batch submission transactions.
BatchSizeInBytes prometheus.Histogram
// NumTxPerBatch tracks the number of L2 transactions in each batch
// submission.
NumTxPerBatch prometheus.Histogram
// SubmissionGasUsed tracks the amount of gas used to submit each batch.
SubmissionGasUsed prometheus.Histogram
// BatchsSubmitted tracks the total number of successful batch submissions.
BatchesSubmitted prometheus.Counter
// FailedSubmissions tracks the total number of failed batch submissions.
FailedSubmissions prometheus.Counter
// BatchTxBuildTime tracks the duration it takes to construct a batch
// transaction.
BatchTxBuildTime prometheus.Gauge
// BatchConfirmationTime tracks the duration it takes to confirm a batch
// transaction.
BatchConfirmationTime prometheus.Gauge
}
func NewMetrics(subsystem string) *Metrics {
return &Metrics{
ETHBalance: promauto.NewGauge(prometheus.GaugeOpts{
Name: "batch_submitter_eth_balance",
Help: "ETH balance of the batch submitter",
Subsystem: subsystem,
}),
BatchSizeInBytes: promauto.NewHistogram(prometheus.HistogramOpts{
Name: "batch_submitter_batch_size_in_bytes",
Help: "Size of batches in bytes",
Subsystem: subsystem,
}),
NumTxPerBatch: promauto.NewHistogram(prometheus.HistogramOpts{
Name: "batch_submitter_num_txs_per_batch",
Help: "Number of transaction in each batch",
Subsystem: subsystem,
}),
SubmissionGasUsed: promauto.NewHistogram(prometheus.HistogramOpts{
Name: "batch_submitter_submission_gas_used",
Help: "Gas used to submit each batch",
Subsystem: subsystem,
}),
BatchesSubmitted: promauto.NewCounter(prometheus.CounterOpts{
Name: "batch_submitter_batches_submitted",
Help: "Count of batches submitted",
Subsystem: subsystem,
}),
FailedSubmissions: promauto.NewCounter(prometheus.CounterOpts{
Name: "batch_submitter_failed_submissions",
Help: "Count of failed batch submissions",
Subsystem: subsystem,
}),
BatchTxBuildTime: promauto.NewGauge(prometheus.GaugeOpts{
Name: "batch_submitter_batch_tx_build_time",
Help: "Time to construct batch transactions",
Subsystem: subsystem,
}),
BatchConfirmationTime: promauto.NewGauge(prometheus.GaugeOpts{
Name: "batch_submitter_batch_confirmation_time",
Help: "Time to confirm batch transactions",
Subsystem: subsystem,
}),
}
}
{
"name": "@eth-optimism/batch-submitter-service",
"version": "0.0.1",
"version": "0.0.2",
"private": true,
"devDependencies": {}
}
......@@ -6,6 +6,7 @@ import (
"sync"
"time"
"github.com/ethereum-optimism/optimism/go/batch-submitter/metrics"
"github.com/ethereum-optimism/optimism/go/batch-submitter/txmgr"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
......@@ -13,6 +14,11 @@ import (
"github.com/ethereum/go-ethereum/log"
)
var (
// weiToGwei is the conversion rate from wei to gwei.
weiToGwei = new(big.Float).SetFloat64(1e-18)
)
// Driver is an interface for creating and submitting batch transactions for a
// specific contract.
type Driver interface {
......@@ -23,6 +29,9 @@ type Driver interface {
// fees.
WalletAddr() common.Address
// Metrics returns the subservice telemetry object.
Metrics() *metrics.Metrics
// 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
......@@ -52,6 +61,7 @@ type Service struct {
cancel func()
txMgr txmgr.TxManager
metrics *metrics.Metrics
wg sync.WaitGroup
}
......@@ -68,6 +78,7 @@ func NewService(cfg ServiceConfig) *Service {
ctx: ctx,
cancel: cancel,
txMgr: txMgr,
metrics: cfg.Driver.Metrics(),
}
}
......@@ -91,21 +102,35 @@ func (s *Service) eventLoop() {
for {
select {
case <-time.After(s.cfg.PollInterval):
log.Info(name + " fetching current block range")
// Record the submitter's current ETH balance. This is done first in
// case any of the remaining steps fail, we can at least have an
// accurate view of the submitter's balance.
balance, err := s.cfg.L1Client.BalanceAt(
s.ctx, s.cfg.Driver.WalletAddr(), nil,
)
if err != nil {
log.Error(name+" unable to get current balance", "err", err)
continue
}
s.metrics.ETHBalance.Set(weiToGwei64(balance))
// Determine the range of L2 blocks that the batch submitter has not
// processed, and needs to take action on.
log.Info(name + " fetching current block range")
start, end, err := s.cfg.Driver.GetBatchBlockRange(s.ctx)
if err != nil {
log.Error(name+" unable to get block range", "err", err)
continue
}
log.Info(name+" block range", "start", start, "end", end)
// No new updates.
if start.Cmp(end) == 0 {
log.Info(name+" no updates", "start", start, "end", end)
continue
}
log.Info(name+" block range", "start", start, "end", end)
// Query for the submitter's current nonce.
nonce64, err := s.cfg.L1Client.NonceAt(
s.ctx, s.cfg.Driver.WalletAddr(), nil,
)
......@@ -116,6 +141,8 @@ func (s *Service) eventLoop() {
}
nonce := new(big.Int).SetUint64(nonce64)
// Construct the transaction submission clousure that will attempt
// to send the next transaction at the given nonce and gas price.
sendTx := func(
ctx context.Context,
gasPrice *big.Int,
......@@ -123,20 +150,38 @@ func (s *Service) eventLoop() {
log.Info(name+" attempting batch tx", "start", start,
"end", end, "nonce", nonce,
"gasPrice", gasPrice)
return s.cfg.Driver.SubmitBatchTx(
tx, err := s.cfg.Driver.SubmitBatchTx(
ctx, start, end, nonce, gasPrice,
)
if err != nil {
return nil, err
}
s.metrics.BatchSizeInBytes.Observe(float64(tx.Size()))
return tx, nil
}
// Wait until one of our submitted transactions confirms. If no
// receipt is received it's likely our gas price was too low.
batchConfirmationStart := time.Now()
receipt, err := s.txMgr.Send(s.ctx, sendTx)
if err != nil {
log.Error(name+" unable to publish batch tx",
"err", err)
s.metrics.FailedSubmissions.Inc()
continue
}
// The transaction was successfully submitted.
log.Info(name+" batch tx successfully published",
"tx_hash", receipt.TxHash)
batchConfirmationTime := time.Since(batchConfirmationStart) /
time.Millisecond
s.metrics.BatchConfirmationTime.Set(float64(batchConfirmationTime))
s.metrics.BatchesSubmitted.Inc()
s.metrics.SubmissionGasUsed.Observe(float64(receipt.GasUsed))
case err := <-s.ctx.Done():
log.Error(name+" service shutting down", "err", err)
......@@ -144,3 +189,10 @@ func (s *Service) eventLoop() {
}
}
}
func weiToGwei64(wei *big.Int) float64 {
gwei := new(big.Float).SetInt(wei)
gwei.Mul(gwei, weiToGwei)
gwei64, _ := gwei.Float64()
return gwei64
}
......@@ -40,7 +40,7 @@ options.
```
NAME:
gas-oracle - Remotely Control the Optimistic Ethereum Gas Price
gas-oracle - Remotely Control the Optimism Gas Price
USAGE:
gas-oracle [global options] command [command options] [arguments...]
......@@ -49,7 +49,7 @@ VERSION:
0.0.0-1.10.4-stable
DESCRIPTION:
Configure with a private key and an Optimistic Ethereum HTTP endpoint to send transactions that update the L2 gas price.
Configure with a private key and an Optimism HTTP endpoint to send transactions that update the L2 gas price.
COMMANDS:
help, h Shows a list of commands or help for one command
......
......@@ -26,8 +26,8 @@ func main() {
app.Version = GitVersion + "-" + params.VersionWithCommit(GitCommit, GitDate)
app.Name = "gas-oracle"
app.Usage = "Remotely Control the Optimistic Ethereum Gas Price"
app.Description = "Configure with a private key and an Optimistic Ethereum HTTP endpoint " +
app.Usage = "Remotely Control the Optimism Gas Price"
app.Description = "Configure with a private key and an Optimism HTTP endpoint " +
"to send transactions that update the L2 gas price."
// Configure the logging
......
# @eth-optimism/l2geth-exporter
## 0.0.2
### Patch Changes
- 71bfa3fe: Initial build
SHELL := /bin/bash
VERSION := `git describe --abbrev=0`
GITCOMMIT := `git rev-parse HEAD`
BUILDDATE := `date +%Y-%m-%d`
BUILDUSER := `whoami`
LDFLAGSSTRING +=-X main.GitCommit=$(GITCOMMIT)
LDFLAGSSTRING +=-X main.GitDate=$(GITDATE)
LDFLAGSSTRING +=-X main.GitVersion=$(GITVERSION)
LDFLAGS :=-ldflags "$(LDFLAGSSTRING)"
all: build
build:
CGO_ENABLED=0 go build $(LDFLAGS)
clean:
rm l2geth-exporter
test:
go test -v ./...
lint:
golangci-lint run ./...
binding:
$(eval temp := $(shell mktemp))
cat ../../packages/contracts/deployments/mainnet/CanonicalTransactionChain.json \
| jq -r .bytecode > $(temp)
cat ../../packages/contracts/deployments/mainnet/CanonicalTransactionChain.json \
| jq .abi \
| abigen --pkg bindings \
--abi - \
--out bindings/CanonicalTransactionChain.go \
--type CanonicalTransactionChain \
--bin $(temp)
rm $(temp)
This diff is collapsed.
package main
import (
"github.com/prometheus/client_golang/prometheus"
)
//Define the metrics we wish to expose
var (
ctcTotalElements = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "l2geth_ctc_total_elements",
Help: "CTC GetTotalElements value."},
[]string{"state"},
)
ctcTotalElementsCallSuccess = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "l2geth_ctc_total_elements_call_success",
Help: "CTC GetTotalElements call success."},
)
)
func init() {
//Register metrics with prometheus
prometheus.MustRegister(ctcTotalElements)
prometheus.MustRegister(ctcTotalElementsCallSuccess)
}
module github.com/ethereum-optimism/optimism/go/l2geth-exporter
go 1.16
require (
github.com/ethereum/go-ethereum v1.10.8
github.com/prometheus/client_golang v1.11.0
)
This diff is collapsed.
package l1contracts
import (
"context"
"math/big"
"github.com/ethereum-optimism/optimism/go/l2geth-exporter/bindings"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
)
// CTC interacts with the OVM CTC contract
type CTC struct {
Address common.Address
Client *ethclient.Client
}
func (ctc *CTC) GetTotalElements(ctx context.Context) (*big.Int, error) {
contract, err := bindings.NewCanonicalTransactionChainCaller(ctc.Address, ctc.Client)
if err != nil {
return nil, err
}
totalElements, err := contract.GetTotalElements(&bind.CallOpts{
Context: ctx,
})
if err != nil {
return nil, err
}
return totalElements, nil
}
package main
import (
"context"
"math/big"
"net/http"
"os"
"time"
"github.com/ethereum-optimism/optimism/go/l2geth-exporter/l1contracts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
l1TimeoutSeconds = 5
)
func main() {
listenAddress := os.Getenv("LISTEN_ADDRESS")
if listenAddress == "" {
listenAddress = ":9100"
}
log.Root().SetHandler(log.CallerFileHandler(log.StdoutHandler))
l1Url := os.Getenv("L1_URL")
if l1Url == "" {
log.Error("L1_URL environmental variable is required")
os.Exit(1)
}
ctcAddress := os.Getenv("CTC_ADDRESS")
if ctcAddress == "" {
log.Error("CTC_ADDRESS environmental variable is required")
os.Exit(1)
}
client, err := ethclient.Dial(l1Url)
if err != nil {
log.Error("Problem connecting to L1: %s", err)
}
http.Handle("/metrics", promhttp.Handler())
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`<html>
<head><title>L2geth Exporter</title></head>
<body>
<h1>L2geth Exporter</h1>
<p><a href="/metrics">Metrics</a></p>
</body>
</html>`))
})
go getCTCTotalElements(ctcAddress, client)
log.Info("Program starting", "listenAddress", listenAddress, "GETH_URL", l1Url, "CTC_ADDRESS", ctcAddress)
if err := http.ListenAndServe(listenAddress, nil); err != nil {
log.Error("Can't start http server", "error", err)
}
}
func getCTCTotalElements(address string, client *ethclient.Client) {
ctc := l1contracts.CTC{
Address: common.HexToAddress(address),
Client: client,
}
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(l1TimeoutSeconds))
totalElements, err := ctc.GetTotalElements(ctx)
if err != nil {
ctcTotalElementsCallSuccess.Set(0)
log.Error("Error calling GetTotalElements", "error", err)
cancel()
continue
}
ctcTotalElementsCallSuccess.Set(1)
totalElementsFloat, _ := new(big.Float).SetInt(totalElements).Float64()
ctcTotalElements.WithLabelValues(
"latest").Set(totalElementsFloat)
log.Info("ctc updated", "ctcTotalElements", totalElementsFloat)
cancel()
<-ticker.C
}
}
{
"name": "@eth-optimism/l2geth-exporter",
"version": "0.0.2",
"private": true,
"devDependencies": {}
}
# op_exporter
A prometheus exporter to collect information from an Optimistic Ethereum node and serve metrics for collection
A prometheus exporter to collect information from an Optimism node and serve metrics for collection
## Usage
......
# @eth-optimism/proxyd
## 3.4.1
### Patch Changes
- 415164e1: Force proxyd build
## 3.4.0
### Minor Changes
......
{
"name": "@eth-optimism/proxyd",
"version": "3.4.0",
"version": "3.4.1",
"private": true,
"dependencies": {}
}
......@@ -14,7 +14,7 @@ program
.option('-r, --runs <n>', 'number of runs. cannot be use with -t/--time')
.option(
'-t, --time <ms>',
'how long to run in milliseconds. cannot be used with -r/--runs',
'how long to run in milliseconds. cannot be used with -r/--runs'
)
.option('-c, --concurrency <n>', 'number of concurrent workers to spawn', '1')
.option('--think-time <n>', 'how long to wait between each run', '0')
......
......@@ -87,7 +87,10 @@ actor('Uniswap swapper', () => {
let tx = await token.transfer(wallet.address, 1000000)
await tx.wait()
const boundToken = token.connect(wallet)
tx = await boundToken.approve(contracts.positionManager.address, 1000000000)
tx = await boundToken.approve(
contracts.positionManager.address,
1000000000
)
await tx.wait()
tx = await boundToken.approve(contracts.router.address, 1000000000)
await tx.wait()
......
......@@ -6,7 +6,7 @@
"scripts": {
"lint": "yarn lint:fix && yarn lint:check",
"lint:fix": "yarn lint:check --fix",
"lint:check": "eslint .",
"lint:check": "eslint . --max-warnings=0",
"build": "hardhat compile",
"test:integration": "hardhat --network optimism test",
"test:actor": "IS_LIVE_NETWORK=true ts-node actor-tests/lib/runner.ts",
......@@ -30,7 +30,7 @@
"devDependencies": {
"@eth-optimism/contracts": "0.5.7",
"@eth-optimism/core-utils": "0.7.3",
"@eth-optimism/message-relayer": "0.2.10",
"@eth-optimism/message-relayer": "0.2.11",
"@ethersproject/abstract-provider": "^5.5.1",
"@ethersproject/providers": "^5.4.5",
"@ethersproject/transactions": "^5.4.0",
......@@ -57,7 +57,6 @@
"envalid": "^7.1.0",
"eslint": "^7.27.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-ban": "^1.5.2",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsdoc": "^35.1.2",
"eslint-plugin-prefer-arrow": "^1.2.3",
......
import { expect } from 'chai'
import { expect } from './shared/setup'
/* Imports: External */
import { Contract, ContractFactory } from 'ethers'
......
import { expect } from './shared/setup'
import { BigNumber, Contract, ContractFactory, utils, Wallet } from 'ethers'
import { ethers } from 'hardhat'
import * as L2Artifact from '@eth-optimism/contracts/artifacts/contracts/standards/L2StandardERC20.sol/L2StandardERC20.json'
import { expect } from 'chai'
import { OptimismEnv } from './shared/env'
import { isLiveNetwork, isMainnet } from './shared/utils'
......
import { expect } from './shared/setup'
import { BigNumber, Contract, ContractFactory, utils, Wallet } from 'ethers'
import { ethers } from 'hardhat'
import { solidity } from 'ethereum-waffle'
import chai, { expect } from 'chai'
import { UniswapV3Deployer } from 'uniswap-v3-deploy-plugin/dist/deployer/UniswapV3Deployer'
import { OptimismEnv } from './shared/env'
......@@ -10,8 +10,6 @@ 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'
chai.use(solidity)
// Below methods taken from the Uniswap test suite, see
// https://github.com/Uniswap/v3-periphery/blob/main/test/shared/ticks.ts
const getMinTick = (tickSpacing: number) =>
......
import chai, { expect } from 'chai'
import chaiAsPromised from 'chai-as-promised'
chai.use(chaiAsPromised)
import { expect } from './shared/setup'
/* Imports: External */
import { BigNumber, utils } from 'ethers'
......
import { expect } from './shared/setup'
import { BigNumber, Contract, ContractFactory, Wallet } from 'ethers'
import { ethers } from 'hardhat'
import chai, { expect } from 'chai'
import {
fundUser,
encodeSolidityRevertMessage,
gasPriceForL2,
} from './shared/utils'
import { OptimismEnv } from './shared/env'
import { solidity } from 'ethereum-waffle'
chai.use(solidity)
describe('Native ETH value integration tests', () => {
let env: OptimismEnv
......
import { expect } from 'chai'
import { expect } from './shared/setup'
/* Imports: External */
import { Wallet, utils, BigNumber } from 'ethers'
......
import { expect } from 'chai'
import { expect } from './shared/setup'
/* Imports: External */
import { ethers } from 'hardhat'
......
import chai, { expect } from 'chai'
import { solidity } from 'ethereum-waffle'
chai.use(solidity)
import { expect } from './shared/setup'
/* Imports: Internal */
import { ethers } from 'ethers'
......
import { expect } from 'chai'
import { expect } from './shared/setup'
/* Imports: Internal */
import { providers } from 'ethers'
......
import { expect } from './shared/setup'
import { OptimismEnv } from './shared/env'
import {
defaultTransactionFactory,
......@@ -5,7 +6,6 @@ import {
sleep,
isLiveNetwork,
} from './shared/utils'
import { expect } from 'chai'
import { TransactionReceipt } from '@ethersproject/abstract-provider'
describe('Replica Tests', () => {
......
import { expect } from './shared/setup'
import { expectApprox, injectL2Context } from '@eth-optimism/core-utils'
import { Wallet, BigNumber, Contract, ContractFactory } from 'ethers'
import { serialize } from '@ethersproject/transactions'
import { ethers } from 'hardhat'
import chai, { expect } from 'chai'
import {
sleep,
l2Provider,
......@@ -12,18 +13,13 @@ import {
isLiveNetwork,
gasPriceForL2,
} from './shared/utils'
import chaiAsPromised from 'chai-as-promised'
import { OptimismEnv } from './shared/env'
import {
TransactionReceipt,
TransactionRequest,
} from '@ethersproject/providers'
import { solidity } from 'ethereum-waffle'
import simpleStorageJson from '../artifacts/contracts/SimpleStorage.sol/SimpleStorage.json'
chai.use(chaiAsPromised)
chai.use(solidity)
describe('Basic RPC tests', () => {
let env: OptimismEnv
let wallet: Wallet
......@@ -286,7 +282,7 @@ describe('Basic RPC tests', () => {
expect(receipt.status).to.eq(0)
})
// Optimistic Ethereum special fields on the receipt
// Optimism special fields on the receipt
it('includes L1 gas price and L1 gas used', async () => {
const tx = await env.l2Wallet.populateTransaction({
to: env.l2Wallet.address,
......
/* External Imports */
import chai = require('chai')
import chaiAsPromised from 'chai-as-promised'
import { solidity } from 'ethereum-waffle'
chai.use(solidity)
chai.use(chaiAsPromised)
const expect = chai.expect
export { expect }
import { expect } from 'chai'
import { expect } from './shared/setup'
/* Imports: External */
import { Contract, ContractFactory, Wallet, utils } from 'ethers'
......
import { expect } from './shared/setup'
/* Imports: External */
import { ContractFactory } from 'ethers'
import { ethers } from 'hardhat'
import chai, { expect } from 'chai'
import { solidity } from 'ethereum-waffle'
import { predeploys } from '@eth-optimism/contracts'
/* Imports: Internal */
import { OptimismEnv } from './shared/env'
import { l2Provider } from './shared/utils'
chai.use(solidity)
describe('Whitelist', async () => {
const initialAmount = 1000
const tokenName = 'OVM Test'
......
# Changelog
## 0.5.5
### Patch Changes
- 2924845d: expose ErrNonceTooHigh from miner
## 0.5.4
### Patch Changes
......
{
"name": "@eth-optimism/l2geth",
"version": "0.5.4",
"version": "0.5.5",
"private": true,
"devDependencies": {}
}
......@@ -22,12 +22,20 @@ services:
target: deployer
entrypoint: ./deployer.sh
environment:
FRAUD_PROOF_WINDOW_SECONDS: 0
L1_NODE_WEB3_URL: http://l1_chain:8545
# these keys are hardhat's first 3 accounts, DO NOT use in production
DEPLOYER_PRIVATE_KEY: "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
SEQUENCER_PRIVATE_KEY: "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
PROPOSER_PRIVATE_KEY: "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a"
# 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"
SCC_FRAUD_PROOF_WINDOW: 0
NUM_DEPLOY_CONFIRMATIONS: 0
# skip compilation when run in docker-compose, since the contracts
# were already compiled in the builder step
NO_COMPILE: 1
# Env vars for the dump script.
# Default hardhat account 5
GAS_PRICE_ORACLE_OWNER: "0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc"
# setting the whitelist owner to address(0) disables the whitelist
......@@ -36,14 +44,11 @@ services:
L2_CHAIN_ID: 420
L2_BLOCK_GAS_LIMIT: 15000000
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"
# skip compilation when run in docker-compose, since the contracts
# were already compiled in the builder step
NO_COMPILE: 1
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
- ${DEPLOYER_PORT:-8080}:8081
......
......@@ -34,4 +34,4 @@ COPY packages/contracts/test/helpers/constants.ts ./test/helpers/constants.ts
COPY packages/contracts/scripts ./scripts
COPY ./ops/scripts/deployer.sh .
ENTRYPOINT yarn run deploy
CMD ./ops/scripts/deployer.sh
FROM golang:1.16 as builder
ADD ./go/l2geth-exporter /app/
WORKDIR /app/
RUN make build
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/l2geth-exporter /usr/local/bin/
ENTRYPOINT ["l2geth-exporter"]
CMD ["--help"]
......@@ -28,7 +28,6 @@ COPY --from=builder /optimism/packages/contracts/artifacts ./packages/contracts/
WORKDIR /opt/optimism/packages/message-relayer
COPY --from=builder /optimism/packages/message-relayer/dist ./dist
COPY --from=builder /optimism/packages/message-relayer/package.json .
COPY --from=builder /optimism/packages/message-relayer/exec ./exec
COPY --from=builder /optimism/packages/message-relayer/node_modules ./node_modules
# copy this over in case you want to run alongside other services
......
......@@ -11,7 +11,7 @@ RUN apt-get update -y && apt-get install -y git curl jq python3
# copy over the needed configs to run the dep installation
# note: this approach can be a bit unhandy to maintain, but it allows
# us to cache the installation steps
WORKDIR /optimism
WORKDIR /opt/optimism
COPY *.json yarn.lock ./
COPY packages/core-utils/package.json ./packages/core-utils/package.json
COPY packages/common-ts/package.json ./packages/common-ts/package.json
......@@ -33,30 +33,30 @@ RUN yarn build
FROM base as deployer
WORKDIR /optimism/packages/contracts
WORKDIR /opt/optimism/packages/contracts
COPY ./ops/scripts/deployer.sh .
CMD ["yarn", "run", "deploy"]
FROM base as batch-submitter
WORKDIR /optimism/packages/batch-submitter
WORKDIR /opt/optimism/packages/batch-submitter
COPY ./ops/scripts/batches.sh .
CMD ["npm", "run", "start"]
FROM base as data-transport-layer
WORKDIR /optimism/packages/data-transport-layer
WORKDIR /opt/optimism/packages/data-transport-layer
COPY ./ops/scripts/dtl.sh .
CMD ["node", "dist/src/services/run.js"]
FROM base as integration-tests
WORKDIR /optimism/integration-tests
WORKDIR /opt/optimism/integration-tests
COPY ./ops/scripts/integration-tests.sh ./
CMD ["yarn", "test:integration"]
FROM base as relayer
WORKDIR /optimism/packages/message-relayer
WORKDIR /opt/optimism/packages/message-relayer
COPY ./ops/scripts/relayer.sh .
CMD ["npm", "run", "start"]
#!/bin/bash
set -e
set -euo
RETRIES=${RETRIES:-20}
JSON='{"jsonrpc":"2.0","id":0,"method":"net_version","params":[]}'
if [ -z "$CONTRACTS_RPC_URL" ]; then
echo "Must specify \$CONTRACTS_RPC_URL."
exit 1
fi
# wait for the base layer to be up
curl \
--fail \
......@@ -14,30 +19,82 @@ curl \
--retry $RETRIES \
--retry-delay 1 \
-d $JSON \
$L1_NODE_WEB3_URL
$CONTRACTS_RPC_URL > /dev/null
yarn run deploy
echo "Connected to L1."
echo "Building deployment command."
function envSet() {
VAR=$1
export $VAR=$(cat ./dist/dumps/addresses.json | jq -r ".$2")
DEPLOY_CMD="npx hardhat deploy"
# Helper method to concatenate things onto $DEPLOY_CMD.
# Usage: concat_cmd "str-to-concat"
function concat_cmd() {
DEPLOY_CMD="$DEPLOY_CMD $1"
}
# set the address to the proxy gateway if possible
envSet L1_STANDARD_BRIDGE_ADDRESS Proxy__OVM_L1StandardBridge
if [ $L1_STANDARD_BRIDGE_ADDRESS == null ]; then
envSet L1_STANDARD_BRIDGE_ADDRESS L1StandardBridge
fi
# Helper method to conditionally concatenate CLI arguments
# when a given env var is defined.
# Usage: concat_arg "--arg-name" "env-var-name"
function concat_arg() {
ARG=$1
ENV_VAR=$2
if [ -n "${!ENV_VAR+x}" ]; then
echo "$ENV_VAR set to ${!ENV_VAR}, applying $ARG argument."
concat_cmd "$ARG \"${!ENV_VAR}\""
else
echo "$ENV_VAR is not not set, skipping $ARG argument."
fi
}
concat_arg "--network" "CONTRACTS_TARGET_NETWORK"
concat_arg "--ovm-address-manager-owner" "OVM_ADDRESS_MANAGER_OWNER"
concat_arg "--ovm-proposer-address" "OVM_PROPOSER_ADDRESS"
concat_arg "--ovm-sequencer-address" "OVM_SEQUENCER_ADDRESS"
concat_arg "--l1-block-time-seconds" "L1_BLOCK_TIME_SECONDS"
concat_arg "--ctc-max-transaction-gas-limit" "CTC_MAX_TRANSACTION_GAS_LIMIT"
concat_arg "--ctc-l2-gas-discount-divisor" "CTC_L2_GAS_DISCOUNT_DIVISOR"
concat_arg "--ctc-enqueue-gas-cost" "CTC_ENQUEUE_GAS_COST"
concat_arg "--scc-fraud-proof-window" "SCC_FRAUD_PROOF_WINDOW"
concat_arg "--num-deploy-confirmations" "NUM_DEPLOY_CONFIRMATIONS"
concat_arg "--forked" "FORKED"
echo "Deploying contracts. Deployment command:"
echo "$DEPLOY_CMD"
eval "$DEPLOY_CMD"
echo "Building addresses.json."
export ADDRESS_MANAGER_ADDRESS=$(cat "./deployments/$CONTRACTS_TARGET_NETWORK/Lib_AddressManager.json" | jq -r .address)
# First, create two files. One of them contains a list of addresses, the other contains a list of contract names.
find "./deployments/$CONTRACTS_TARGET_NETWORK" -maxdepth 1 -name '*.json' | xargs cat | jq -r '.address' > addresses.txt
find "./deployments/$CONTRACTS_TARGET_NETWORK" -maxdepth 1 -name '*.json' | sed -e "s/.\/deployments\/$CONTRACTS_TARGET_NETWORK\///g" | sed -e 's/.json//g' > filenames.txt
# Start building addresses.json.
echo "{" >> addresses.json
# Zip the two files describe above together, then, switch their order and format as JSON.
paste addresses.txt filenames.txt | sed -e "s/^\([^ ]\+\)\s\+\([^ ]\+\)/\"\2\": \"\1\",/" >> addresses.json
# Add the address manager alias.
echo "\"AddressManager\": \"$ADDRESS_MANAGER_ADDRESS\"" >> addresses.json
# End addresses.json
echo "}" >> addresses.json
echo "Built addresses.json. Content:"
jq . addresses.json
echo "Env vars for the dump script:"
export L1_STANDARD_BRIDGE_ADDRESS=$(cat "./addresses.json" | jq -r .Proxy__OVM_L1StandardBridge)
export L1_CROSS_DOMAIN_MESSENGER_ADDRESS=$(cat "./addresses.json" | jq -r .Proxy__OVM_L1CrossDomainMessenger)
echo "ADDRESS_MANAGER_ADDRESS=$ADDRESS_MANAGER_ADDRESS"
echo "L1_STANDARD_BRIDGE_ADDRESS=$L1_STANDARD_BRIDGE_ADDRESS"
echo "L1_CROSS_DOMAIN_MESSENGER_ADDRESS=$L1_CROSS_DOMAIN_MESSENGER_ADDRESS"
envSet L1_CROSS_DOMAIN_MESSENGER_ADDRESS Proxy__OVM_L1CrossDomainMessenger
if [ $L1_CROSS_DOMAIN_MESSENGER_ADDRESS == null ]; then
envSet L1_CROSS_DOMAIN_MESSENGER_ADDRESS L1CrossDomainMessenger
fi
# build the dump file
echo "Building dump file."
yarn run build:dump
mv addresses.json ./dist/dumps
# service the addresses and dumps
echo "Starting server."
python3 -m http.server \
--bind "0.0.0.0" 8081 \
--directory ./dist/dumps
......@@ -11,6 +11,7 @@
"specs",
"go/gas-oracle",
"go/batch-submitter",
"go/l2geth-exporter",
"go/proxyd",
"go/op-exporter",
"ops/docker/rpc-proxy",
......@@ -27,7 +28,6 @@
"babel-eslint": "^10.1.0",
"eslint": "^7.27.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-ban": "^1.5.2",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsdoc": "^35.1.2",
"eslint-plugin-prefer-arrow": "^1.2.3",
......
# Changelog
## 0.4.12
### Patch Changes
- 07f1ad01: Fix the numTxPerBatch metric
## 0.4.11
### Patch Changes
......
{
"private": true,
"name": "@eth-optimism/batch-submitter",
"version": "0.4.11",
"version": "0.4.12",
"description": "[Optimism] Service for submitting transactions and transaction results",
"main": "dist/index",
"types": "dist/index",
......@@ -15,7 +15,7 @@
"lint": "yarn lint:fix && yarn lint:check",
"pre-commit": "lint-staged",
"lint:fix": "yarn lint:check --fix",
"lint:check": "eslint .",
"lint:check": "eslint . --max-warnings=0",
"test": "hardhat test --show-stack-traces",
"test:coverage": "nyc hardhat test && nyc merge .nyc_output coverage.json"
},
......@@ -63,7 +63,6 @@
"chai": "^4.3.4",
"eslint": "^7.27.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-ban": "^1.5.2",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsdoc": "^35.1.2",
"eslint-plugin-prefer-arrow": "^1.2.3",
......
......@@ -154,7 +154,6 @@ export class StateBatchSubmitter extends BatchSubmitter {
startBlock: number,
endBlock: number
): Promise<TransactionReceipt> {
const batchTxBuildStart = performance.now()
const batch = await this._generateStateCommitmentBatch(startBlock, endBlock)
......
......@@ -232,7 +232,7 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
const batchTxBuildEnd = performance.now()
this.metrics.batchTxBuildTime.set(batchTxBuildEnd - batchTxBuildStart)
this.metrics.numTxPerBatch.observe(endBlock - startBlock)
this.metrics.numTxPerBatch.observe(batchParams.totalElementsToAppend)
const l1tipHeight = await this.signer.provider.getBlockNumber()
this.logger.debug('Submitting batch.', {
calldata: batchParams,
......
......@@ -11,7 +11,7 @@
"all": "yarn clean && yarn build && yarn test && yarn lint:fix && yarn lint",
"build": "tsc -p tsconfig.build.json",
"clean": "rimraf dist/ ./tsconfig.build.tsbuildinfo",
"lint:check": "eslint .",
"lint:check": "eslint . --max-warnings=0",
"lint:fix": "yarn lint:check --fix",
"lint": "yarn lint:fix && yarn lint:check",
"pre-commit": "lint-staged",
......@@ -50,7 +50,6 @@
"chai": "^4.3.4",
"eslint": "^7.27.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-ban": "^1.5.2",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsdoc": "^35.1.2",
"eslint-plugin-prefer-arrow": "^1.2.3",
......
[![codecov](https://codecov.io/gh/ethereum-optimism/optimism/branch/master/graph/badge.svg?token=0VTG7PG7YR&flag=contracts)](https://codecov.io/gh/ethereum-optimism/optimism)
# Optimistic Ethereum Smart Contracts
# Optimism Smart Contracts
`@eth-optimism/contracts` contains the various Solidity smart contracts used within the Optimistic Ethereum system.
Some of these contracts are deployed on Ethereum ("Layer 1"), while others are meant to be deployed to Optimistic Ethereum ("Layer 2").
`@eth-optimism/contracts` contains the various Solidity smart contracts used within the Optimism system.
Some of these contracts are deployed on Ethereum ("Layer 1"), while others are meant to be deployed to Optimism ("Layer 2").
Within each contract file you'll find a comment that lists:
1. The compiler with which a contract is intended to be compiled, `solc` or `optimistic-solc`.
......
// WARNING: DO NOT USE THIS FILE TO DEPLOY CONTRACTS TO PRODUCTION
// WE ARE REMOVING THIS FILE IN A FUTURE RELEASE, IT IS ONLY TO BE USED AS PART OF THE LOCAL
// DEPLOYMENT PROCESS. USE A DEPLOYMENT SCRIPT LOCATED IN scripts/deploy-scripts/ WHEN DEPLOYING
// TO A PRODUCTION ENVIRONMENT.
import { Wallet } from 'ethers'
import path from 'path'
import dirtree from 'directory-tree'
import fs from 'fs'
// Ensures that all relevant environment vars are properly set. These lines *must* come before the
// hardhat import because importing will load the config (which relies on these vars). Necessary
// because CI currently uses different var names than the ones we've chosen here.
// TODO: Update CI so that we don't have to do this anymore.
process.env.HARDHAT_NETWORK = 'custom' // "custom" here is an arbitrary name. only used for CI.
process.env.CONTRACTS_TARGET_NETWORK = 'custom'
process.env.CONTRACTS_DEPLOYER_KEY = process.env.DEPLOYER_PRIVATE_KEY
process.env.CONTRACTS_RPC_URL =
process.env.L1_NODE_WEB3_URL || 'http://127.0.0.1:8545'
import hre from 'hardhat'
const sequencer = new Wallet(process.env.SEQUENCER_PRIVATE_KEY)
const proposer = new Wallet(process.env.PROPOSER_PRIVATE_KEY)
const deployer = new Wallet(process.env.DEPLOYER_PRIVATE_KEY)
const parseEnv = () => {
const ensure = (env, type) => {
if (typeof process.env[env] === 'undefined') {
return undefined
}
if (type === 'number') {
return parseInt(process.env[env], 10)
}
return process.env[env]
}
return {
l1BlockTimeSeconds: ensure('BLOCK_TIME_SECONDS', 'number'),
ctcMaxTransactionGasLimit: ensure('MAX_TRANSACTION_GAS_LIMIT', 'number'),
ctcL2GasDiscountDivisor: ensure('L2_GAS_DISCOUNT_DIVISOR', 'number'),
ctcEnqueueGasCost: ensure('ENQUEUE_GAS_COST', 'number'),
sccFraudProofWindow: ensure('FRAUD_PROOF_WINDOW_SECONDS', 'number'),
sccSequencerPublishWindow: ensure(
'SEQUENCER_PUBLISH_WINDOW_SECONDS',
'number'
),
}
}
const main = async () => {
// Just be really verbose about this...
console.log(
`WARNING: DO NOT USE THIS FILE IN PRODUCTION! FOR LOCAL DEVELOPMENT ONLY!`
)
const config = parseEnv()
await hre.run('deploy', {
l1BlockTimeSeconds: config.l1BlockTimeSeconds,
ctcMaxTransactionGasLimit: config.ctcMaxTransactionGasLimit,
ctcL2GasDiscountDivisor: config.ctcL2GasDiscountDivisor,
ctcEnqueueGasCost: config.ctcEnqueueGasCost,
sccFraudProofWindow: config.sccFraudProofWindow,
sccSequencerPublishWindow: config.sccFraudProofWindow,
ovmSequencerAddress: sequencer.address,
ovmProposerAddress: proposer.address,
ovmAddressManagerOwner: deployer.address,
numDeployConfirmations: 0,
noCompile: process.env.NO_COMPILE ? true : false,
})
// Stuff below this line is currently required for CI to work properly. We probably want to
// update our CI so this is no longer necessary. But I'm adding it for backwards compat so we can
// get the hardhat-deploy stuff merged. Woot.
const nicknames = {
Lib_AddressManager: 'AddressManager',
}
const contracts: any = dirtree(
path.resolve(__dirname, `../deployments/custom`)
)
.children.filter((child) => {
return child.extension === '.json'
})
.reduce((contractsAccumulator, child) => {
const contractName = child.name.replace('.json', '')
// eslint-disable-next-line @typescript-eslint/no-var-requires
const artifact = require(path.resolve(
__dirname,
`../deployments/custom/${child.name}`
))
contractsAccumulator[nicknames[contractName] || contractName] =
artifact.address
return contractsAccumulator
}, {})
contracts.OVM_Sequencer = await sequencer.getAddress()
contracts.Deployer = await deployer.getAddress()
const addresses = JSON.stringify(contracts, null, 2)
const dumpsPath = path.resolve(__dirname, '../dist/dumps')
if (!fs.existsSync(dumpsPath)) {
fs.mkdirSync(dumpsPath)
}
const addrsPath = path.resolve(dumpsPath, 'addresses.json')
fs.writeFileSync(addrsPath, addresses)
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.log(
JSON.stringify({ error: error.message, stack: error.stack }, null, 2)
)
process.exit(1)
})
# Optimistic Ethereum Deployments
- [Optimistic Ethereum (mainnet)](./mainnet#readme)
- [Optimistic Kovan (public testnet)](./kovan#readme)
- [Optimistic Goerli (internal devnet)](./goerli#readme)
# Optimism Deployments
- [Optimism (mainnet)](./mainnet#readme)
- [Optimism Kovan (public testnet)](./kovan#readme)
- [Optimism Goerli (internal devnet)](./goerli#readme)
# Optimistic Goerli (internal devnet)
# Optimism Goerli (internal devnet)
## Notice
Optimistic Goerli is an internal Optimism development network. You're probably looking for [Optimistic Kovan](../kovan#readme), the public Optimistic Ethereum testnet.
Optimism Goerli is an internal Optimism development network. You're probably looking for [Optimism Kovan](../kovan#readme), the public Optimism testnet.
## Network Info
- **Chain ID**: 420
## Layer 1 Contracts
......
# Optimistic Kovan (public testnet)
# Optimism Kovan (public testnet)
## Network Info
- **Chain ID**: 69
- **Public RPC**: https://kovan.optimism.io
......
# Optimistic Ethereum (mainnet)
# Optimism (mainnet)
## Network Info
- **Chain ID**: 10
- **Public RPC**: https://mainnet.optimism.io
......
{
"name": "@eth-optimism/contracts",
"version": "0.5.7",
"description": "[Optimism] L1 and L2 smart contracts for Optimistic Ethereum",
"description": "[Optimism] L1 and L2 smart contracts for Optimism",
"main": "dist/index",
"types": "dist/index",
"files": [
......@@ -29,7 +29,7 @@
"test:slither": "slither .",
"pretest:slither": "rm -f @openzeppelin && rm -f @ens && rm -f hardhat && ln -s ../../node_modules/@openzeppelin @openzeppelin && ln -s ../../node_modules/@ens @ens && ln -s ../../node_modules/hardhat hardhat",
"posttest:slither": "rm -f @openzeppelin && rm -f @ens && rm -f hardhat",
"lint:ts:check": "eslint .",
"lint:ts:check": "eslint . --max-warnings=0",
"lint:contracts:check": "yarn solhint -f table 'contracts/**/*.sol'",
"lint:check": "yarn lint:contracts:check && yarn lint:ts:check",
"lint:ts:fix": "eslint --fix .",
......@@ -37,7 +37,6 @@
"lint:fix": "yarn lint:contracts:fix && yarn lint:ts:fix",
"lint": "yarn lint:fix && yarn lint:check",
"clean": "rm -rf ./dist ./artifacts ./cache ./coverage ./tsconfig.build.tsbuildinfo",
"deploy": "ts-node bin/deploy.ts && yarn autogen:markdown",
"prepublishOnly": "yarn copyfiles -u 1 -e \"**/test-*/**/*\" \"contracts/**/*\" ./",
"postpublish": "rimraf chugsplash L1 L2 libraries standards",
"prepack": "yarn prepublishOnly",
......@@ -88,7 +87,6 @@
"dotenv": "^10.0.0",
"eslint": "^7.27.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-ban": "^1.5.2",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsdoc": "^35.1.2",
"eslint-plugin-prefer-arrow": "^1.2.3",
......
......@@ -16,7 +16,7 @@ interface DeploymentInfo {
const PUBLIC_DEPLOYMENTS: DeploymentInfo[] = [
{
folder: 'mainnet',
name: 'Optimistic Ethereum (mainnet)',
name: 'Optimism (mainnet)',
chainid: 10,
rpc: 'https://mainnet.optimism.io',
l1Explorer: 'https://etherscan.io',
......@@ -24,7 +24,7 @@ const PUBLIC_DEPLOYMENTS: DeploymentInfo[] = [
},
{
folder: 'kovan',
name: 'Optimistic Kovan (public testnet)',
name: 'Optimism Kovan (public testnet)',
chainid: 69,
rpc: 'https://kovan.optimism.io',
l1Explorer: 'https://kovan.etherscan.io',
......@@ -32,9 +32,9 @@ const PUBLIC_DEPLOYMENTS: DeploymentInfo[] = [
},
{
folder: 'goerli',
name: 'Optimistic Goerli (internal devnet)',
name: 'Optimism Goerli (internal devnet)',
chainid: 420,
notice: `Optimistic Goerli is an internal Optimism development network. You're probably looking for [Optimistic Kovan](../kovan#readme), the public Optimistic Ethereum testnet.`,
notice: `Optimism Goerli is an internal Optimism development network. You're probably looking for [Optimism Kovan](../kovan#readme), the public Optimism testnet.`,
l1Explorer: 'https://goerli.etherscan.io',
},
]
......@@ -221,7 +221,7 @@ const main = async () => {
}
let primary = ``
primary = addline(primary, `# Optimistic Ethereum Deployments`)
primary = addline(primary, `# Optimism Deployments`)
for (const deployment of PUBLIC_DEPLOYMENTS) {
primary = addline(
primary,
......
......@@ -12,7 +12,7 @@
"build": "tsc -p tsconfig.build.json",
"clean": "rimraf dist/ ./tsconfig.build.tsbuildinfo",
"lint": "yarn lint:fix && yarn lint:check",
"lint:check": "eslint .",
"lint:check": "eslint . --max-warnings=0",
"lint:fix": "yarn lint:check --fix",
"pre-commit": "lint-staged",
"test": "ts-mocha test/*.spec.ts",
......@@ -49,7 +49,6 @@
"babel-eslint": "^10.1.0",
"eslint": "^7.27.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-ban": "^1.5.2",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsdoc": "^35.1.2",
"eslint-plugin-prefer-arrow": "^1.2.3",
......
......@@ -3,7 +3,7 @@
## What is this?
The Optimistic Ethereum Data Transport Layer is a long-running software service (written in TypeScript) designed to reliably index Optimistic Ethereum transaction data from Layer 1 (Ethereum). Specifically, this service indexes:
The Optimism Data Transport Layer is a long-running software service (written in TypeScript) designed to reliably index Optimism transaction data from Layer 1 (Ethereum). Specifically, this service indexes:
* Transactions that have been enqueued for submission to the CanonicalTransactionChain via [`CanonicalTransactionChain.enqueue`].
* Transactions that have been included in the CanonicalTransactionChain via [`CanonicalTransactionChain.appendQueueBatch`] or [`CanonicalTransactionChain.appendSequencerBatch`].
......
......@@ -13,7 +13,7 @@
"clean:db": "rimraf ./db",
"lint": "yarn run lint:fix && yarn run lint:check",
"lint:fix": "yarn lint:check --fix",
"lint:check": "eslint .",
"lint:check": "eslint . --max-warnings=0",
"start": "ts-node ./src/services/run.ts",
"start:local": "ts-node ./src/services/run.ts | pino-pretty",
"test": "hardhat --config test/config/hardhat.config.ts test",
......@@ -74,7 +74,6 @@
"chai-as-promised": "^7.1.1",
"eslint": "^7.27.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-ban": "^1.5.2",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsdoc": "^35.1.2",
"eslint-plugin-prefer-arrow": "^1.2.3",
......
......@@ -167,7 +167,7 @@ export class L1IngestionService extends BaseService<L1IngestionServiceOptions> {
startingL1BlockNumber = this.options.l1StartHeight
} else {
this.logger.info(
'Attempting to find an appropriate L1 block height to begin sync...'
'Attempting to find an appropriate L1 block height to begin sync. This may take a long time.'
)
startingL1BlockNumber = await this._findStartingL1BlockNumber()
}
......@@ -453,12 +453,18 @@ export class L1IngestionService extends BaseService<L1IngestionServiceOptions> {
private async _findStartingL1BlockNumber(): Promise<number> {
const currentL1Block = await this.state.l1RpcProvider.getBlockNumber()
const filter =
this.state.contracts.Lib_AddressManager.filters.OwnershipTransferred()
for (let i = 0; i < currentL1Block; i += 2000) {
const start = i
const end = Math.min(i + 2000, currentL1Block)
this.logger.info(`Searching for ${filter} from ${start} to ${end}`)
for (let i = 0; i < currentL1Block; i += 1000000) {
const events = await this.state.contracts.Lib_AddressManager.queryFilter(
this.state.contracts.Lib_AddressManager.filters.OwnershipTransferred(),
i,
Math.min(i + 1000000, currentL1Block)
filter,
start,
end
)
if (events.length > 0) {
......
......@@ -121,7 +121,7 @@ export class L2IngestionService extends BaseService<L2IngestionServiceOptions> {
}
this.logger.info(
'Synchronizing unconfirmed transactions from Layer 2 (Optimistic Ethereum)',
'Synchronizing unconfirmed transactions from Layer 2 (Optimism)',
{
fromBlock: highestSyncedL2BlockNumber,
toBlock: targetL2Block,
......
# @eth-optimism/message-relayer
## 0.2.11
### Patch Changes
- 3a673322: Removed old node.js service running script
## 0.2.10
### Patch Changes
......
#!/usr/bin/env node
const main = require('../dist/exec/run').default
;(async () => {
await main()
})().catch((err) => {
console.log(err)
process.exit(1)
})
{
"name": "@eth-optimism/message-relayer",
"version": "0.2.10",
"version": "0.2.11",
"description": "[Optimism] Service for automatically relaying L2 to L1 transactions",
"main": "dist/index",
"types": "dist/index",
......@@ -11,13 +11,13 @@
"withdraw": "./src/exec/withdraw.ts"
},
"scripts": {
"start": "node ./exec/run-message-relayer.js",
"start": "ts-node ./src/exec/run.ts",
"build": "tsc -p ./tsconfig.build.json",
"clean": "rimraf dist/ ./tsconfig.build.tsbuildinfo",
"lint": "yarn lint:fix && yarn lint:check",
"pre-commit": "lint-staged",
"lint:fix": "yarn lint:check --fix",
"lint:check": "eslint .",
"lint:check": "eslint . --max-warnings=0",
"test": "hardhat test --show-stack-traces",
"test:coverage": "nyc hardhat test && nyc merge .nyc_output coverage.json"
},
......@@ -59,7 +59,6 @@
"chai-as-promised": "^7.1.1",
"eslint": "^7.27.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-ban": "^1.5.2",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsdoc": "^35.1.2",
"eslint-plugin-prefer-arrow": "^1.2.3",
......@@ -72,6 +71,7 @@
"lodash": "^4.17.21",
"mocha": "^8.4.0",
"prettier": "^2.3.1",
"ts-node": "^10.0.0",
"typescript": "^4.3.5"
}
}
......@@ -12,7 +12,7 @@
"clean": "rimraf ./dist ./tsconfig.build.tsbuildinfo",
"lint": "yarn run lint:fix && yarn run lint:check",
"lint:fix": "yarn lint:check --fix",
"lint:check": "eslint .",
"lint:check": "eslint . --max-warnings=0",
"pre-commit": "lint-staged",
"test:surgery": "ts-mocha --timeout 50000000 test/*",
"start": "ts-node ./scripts/surgery.ts"
......
......@@ -68,7 +68,7 @@ describe('EOAs', () => {
// eslint-disable-next-line
before(function() {
if (env.surgeryDataSources.configs.l2NetworkName === 'kovan') {
console.log('1inch deployer does not exist on optimistic kovan')
console.log('1inch deployer does not exist on Optimism Kovan')
this.skip()
}
......
......@@ -12,7 +12,7 @@
"clean": "rimraf ./dist ./tsconfig.build.tsbuildinfo",
"lint": "yarn run lint:fix && yarn run lint:check",
"lint:fix": "yarn lint:check --fix",
"lint:check": "eslint .",
"lint:check": "eslint . --max-warnings=0",
"build": "tsc -p tsconfig.build.json",
"pre-commit": "lint-staged",
"test": "ts-mocha test/*.spec.ts",
......
......@@ -2,7 +2,14 @@
# @eth-optimism/sdk
The `@eth-optimism/sdk` package provides a set of tools for interacting with Optimistic Ethereum.
The `@eth-optimism/sdk` package provides a set of tools for interacting with Optimism.
## NOTICE
WARNING: This package is currently under construction.
You can definitely try to use it, but you probably won't have a good time.
Lots of pieces are missing.
We will announce the 1.0.0 release on Discord and on Twitter once it's ready, keep an eye out!
## Installation
......
{
"name": "@eth-optimism/sdk",
"version": "0.0.3",
"description": "[Optimism] Tools for working with Optimistic Ethereum",
"description": "[Optimism] Tools for working with Optimism",
"main": "dist/index",
"types": "dist/index",
"files": [
......@@ -12,7 +12,7 @@
"build": "tsc -p tsconfig.build.json",
"clean": "rimraf dist/ ./tsconfig.build.tsbuildinfo",
"lint": "yarn lint:fix && yarn lint:check",
"lint:check": "eslint .",
"lint:check": "eslint . --max-warnings=0",
"lint:fix": "yarn lint:check --fix",
"pre-commit": "lint-staged",
"test": "hardhat test",
......@@ -43,7 +43,6 @@
"chai-as-promised": "^7.1.1",
"eslint": "^7.27.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-ban": "^1.5.2",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsdoc": "^35.1.2",
"eslint-plugin-prefer-arrow": "^1.2.3",
......
This diff is collapsed.
export * from './interfaces'
export * from './utils'
export * from './cross-chain-provider'
......@@ -11,6 +11,7 @@ import {
TokenBridgeMessage,
OEContracts,
MessageReceipt,
CustomBridges,
} from './types'
/**
......@@ -34,14 +35,14 @@ export interface ICrossChainProvider {
l1ChainId: number
/**
* Chain ID for the L2 network.
* Contract objects attached to their respective providers and addresses.
*/
l2ChainId: number
contracts: OEContracts
/**
* Contract objects attached to their respective providers and addresses.
* List of custom bridges for the given network.
*/
contracts: OEContracts
bridges: CustomBridges
/**
* Retrieves all cross chain messages sent within a given transaction.
......@@ -85,7 +86,7 @@ export interface ICrossChainProvider {
/**
* Finds all cross chain messages that correspond to token deposits or withdrawals sent by a
* particular address. Useful for finding deposits/withdrawals because the sender of the message
* will appear to be the StandardBridge contract and not the actual end user. Returns
* will appear to be the StandardBridge contract and not the actual end user.
*
* @param address Address to search for messages from.
* @param opts Options object.
......@@ -106,6 +107,56 @@ export interface ICrossChainProvider {
}
): Promise<TokenBridgeMessage[]>
/**
* Alias for getTokenBridgeMessagesByAddress with a drection of L1_TO_L2.
*
* @param address Address to search for messages from.
* @param opts Options object.
* @param opts.fromBlock Block to start searching for messages from. If not provided, will start
* from the first block (block #0).
* @param opts.toBlock Block to stop searching for messages at. If not provided, will stop at the
* latest known block ("latest").
* @returns All deposit token bridge messages sent by the given address.
*/
getDepositsByAddress(
address: AddressLike,
opts?: {
fromBlock?: BlockTag
toBlock?: BlockTag
}
): Promise<TokenBridgeMessage[]>
/**
* Alias for getTokenBridgeMessagesByAddress with a drection of L2_TO_L1.
*
* @param address Address to search for messages from.
* @param opts Options object.
* @param opts.fromBlock Block to start searching for messages from. If not provided, will start
* from the first block (block #0).
* @param opts.toBlock Block to stop searching for messages at. If not provided, will stop at the
* latest known block ("latest").
* @returns All withdrawal token bridge messages sent by the given address.
*/
getWithdrawalsByAddress(
address: AddressLike,
opts?: {
fromBlock?: BlockTag
toBlock?: BlockTag
}
): Promise<TokenBridgeMessage[]>
/**
* Resolves a MessageLike into a CrossChainMessage object.
* Unlike other coercion functions, this function is stateful and requires making additional
* requests. For now I'm going to keep this function here, but we could consider putting a
* similar function inside of utils/coercion.ts if people want to use this without having to
* create an entire CrossChainProvider object.
*
* @param message MessageLike to resolve into a CrossChainMessage.
* @returns Message coerced into a CrossChainMessage.
*/
toCrossChainMessage(message: MessageLike): Promise<CrossChainMessage>
/**
* Retrieves the status of a particular message as an enum.
*
......@@ -129,9 +180,9 @@ export interface ICrossChainProvider {
*
* @param message Message to wait for.
* @param opts Options to pass to the waiting function.
* - `confirmations` (number): Number of transaction confirmations to wait for before returning.
* - `pollIntervalMs` (number): Number of milliseconds to wait between polling for the receipt.
* - `loopsBeforeTimeout` (number): Number of times to poll before timing out.
* @param opts.confirmations Number of transaction confirmations to wait for before returning.
* @param opts.pollIntervalMs Number of milliseconds to wait between polling for the receipt.
* @param opts.timeoutMs Milliseconds to wait before timing out.
* @returns CrossChainMessage receipt including receipt of the transaction that relayed the
* given message.
*/
......@@ -140,7 +191,7 @@ export interface ICrossChainProvider {
opts?: {
confirmations?: number
pollIntervalMs?: number
loopsBeforeTimeout?: number
timeoutMs?: number
}
): Promise<MessageReceipt>
......
......@@ -7,26 +7,21 @@ import { Signer } from '@ethersproject/abstract-signer'
import { Contract, BigNumber, Overrides } from 'ethers'
/**
* Represents Optimistic Ethereum contracts, assumed to be connected to their appropriate
* providers and addresses.
*/
export interface OEContracts {
/**
* L1 contract references.
*/
l1: {
export interface OEL1Contracts {
AddressManager: Contract
L1CrossDomainMessenger: Contract
L1StandardBridge: Contract
StateCommitmentChain: Contract
CanonicalTransactionChain: Contract
BondManager: Contract
}
}
/**
/**
* L2 contract references.
*/
l2: {
export interface OEL2Contracts {
L2CrossDomainMessenger: Contract
L2StandardBridge: Contract
OVM_L1BlockNumber: Contract
......@@ -36,6 +31,63 @@ export interface OEContracts {
OVM_GasPriceOracle: Contract
OVM_SequencerFeeVault: Contract
WETH: Contract
}
/**
* Represents Optimism contracts, assumed to be connected to their appropriate
* providers and addresses.
*/
export interface OEContracts {
l1: OEL1Contracts
l2: OEL2Contracts
}
/**
* Convenience type for something that looks like the L1 OE contract interface but could be
* addresses instead of actual contract objects.
*/
export type OEL1ContractsLike = {
[K in keyof OEL1Contracts]: AddressLike
}
/**
* Convenience type for something that looks like the L2 OE contract interface but could be
* addresses instead of actual contract objects.
*/
export type OEL2ContractsLike = {
[K in keyof OEL2Contracts]: AddressLike
}
/**
* Convenience type for something that looks like the OE contract interface but could be
* addresses instead of actual contract objects.
*/
export interface OEContractsLike {
l1: OEL1ContractsLike
l2: OEL2ContractsLike
}
/**
* Represents list of custom bridges.
*/
export interface CustomBridges {
l1: {
[name: string]: Contract
}
l2: {
[name: string]: Contract
}
}
/**
* Something that looks like the list of custom bridges.
*/
export interface CustomBridgesLike {
l1: {
[K in keyof CustomBridges['l1']]: AddressLike
}
l2: {
[K in keyof CustomBridges['l2']]: AddressLike
}
}
......@@ -95,11 +147,9 @@ export interface CrossChainMessageRequest {
}
/**
* Describes a message that is sent between L1 and L2. Direction determines where the message was
* sent from and where it's being sent to.
* Core components of a cross chain message.
*/
export interface CrossChainMessage {
direction: MessageDirection
export interface CoreCrossChainMessage {
sender: string
target: string
message: string
......@@ -107,12 +157,15 @@ export interface CrossChainMessage {
}
/**
* Convenience type for when you don't care which direction the message is going in.
* Describes a message that is sent between L1 and L2. Direction determines where the message was
* sent from and where it's being sent to.
*/
export type DirectionlessCrossChainMessage = Omit<
CrossChainMessage,
'direction'
>
export interface CrossChainMessage extends CoreCrossChainMessage {
direction: MessageDirection
logIndex: number
blockNumber: number
transactionHash: string
}
/**
* Describes a token withdrawal or deposit, along with the underlying raw cross chain message
......@@ -125,22 +178,24 @@ export interface TokenBridgeMessage {
l1Token: string
l2Token: string
amount: BigNumber
raw: CrossChainMessage
data: string
logIndex: number
blockNumber: number
transactionHash: string
}
/**
* Enum describing the status of a CrossDomainMessage message receipt.
*/
export enum MessageReceiptStatus {
RELAYED_SUCCEEDED,
RELAYED_FAILED,
RELAYED_SUCCEEDED,
}
/**
* CrossDomainMessage receipt.
*/
export interface MessageReceipt {
messageHash: string
receiptStatus: MessageReceiptStatus
transactionReceipt: TransactionReceipt
}
......@@ -164,13 +219,6 @@ export interface StateRootBatch {
stateRoots: string[]
}
/**
* Utility type for deep partials.
*/
export type DeepPartial<T> = {
[P in keyof T]?: DeepPartial<T[P]>
}
/**
* Extended Ethers overrides object with an l2GasLimit field.
* Only meant to be used for L1 to L2 messages, since L2 to L1 messages don't have a specified gas
......
......@@ -4,46 +4,13 @@ import {
TransactionReceipt,
TransactionResponse,
} from '@ethersproject/abstract-provider'
import { getContractInterface } from '@eth-optimism/contracts'
import { ethers } from 'ethers'
import { ethers, BigNumber } from 'ethers'
import {
ProviderLike,
TransactionLike,
DirectionlessCrossChainMessage,
} from './interfaces'
/**
* Returns the canonical encoding of a cross chain message. This encoding is used in various
* locations within the Optimistic Ethereum smart contracts.
*
* @param message Cross chain message to encode.
* @returns Canonical encoding of the message.
*/
export const encodeCrossChainMessage = (
message: DirectionlessCrossChainMessage
): string => {
return getContractInterface('L2CrossDomainMessenger').encodeFunctionData(
'relayMessage',
[message.target, message.sender, message.message, message.messageNonce]
)
}
/**
* Returns the canonical hash of a cross chain message. This hash is used in various locations
* within the Optimistic Ethereum smart contracts and is the keccak256 hash of the result of
* encodeCrossChainMessage.
*
* @param message Cross chain message to hash.
* @returns Canonical hash of the message.
*/
export const hashCrossChainMessage = (
message: DirectionlessCrossChainMessage
): string => {
return ethers.utils.solidityKeccak256(
['bytes'],
[encodeCrossChainMessage(message)]
)
}
NumberLike,
AddressLike,
} from '../interfaces'
/**
* Converts a ProviderLike into a provider. Assumes that if the ProviderLike is a string then
......@@ -84,3 +51,29 @@ export const toTransactionHash = (transaction: TransactionLike): string => {
throw new Error('Invalid transaction')
}
}
/**
* Converts a number-like into an ethers BigNumber.
*
* @param num Number-like to convert into a BigNumber.
* @returns Number-like as a BigNumber.
*/
export const toBigNumber = (num: NumberLike): BigNumber => {
return ethers.BigNumber.from(num)
}
/**
* Converts an address-like into a 0x-prefixed address string.
*
* @param addr Address-like to convert into an address.
* @returns Address-like as an address.
*/
export const toAddress = (addr: AddressLike): string => {
if (typeof addr === 'string') {
assert(ethers.utils.isAddress(addr), 'Invalid address')
return ethers.utils.getAddress(addr)
} else {
assert(ethers.utils.isAddress(addr.address), 'Invalid address')
return ethers.utils.getAddress(addr.address)
}
}
This diff is collapsed.
export * from './coercion'
export * from './contracts'
export * from './message-encoding'
export * from './type-utils'
export * from './misc-utils'
import { getContractInterface } from '@eth-optimism/contracts'
import { ethers } from 'ethers'
import { CoreCrossChainMessage } from '../interfaces'
/**
* Returns the canonical encoding of a cross chain message. This encoding is used in various
* locations within the Optimism smart contracts.
*
* @param message Cross chain message to encode.
* @returns Canonical encoding of the message.
*/
export const encodeCrossChainMessage = (
message: CoreCrossChainMessage
): string => {
return getContractInterface('L2CrossDomainMessenger').encodeFunctionData(
'relayMessage',
[message.target, message.sender, message.message, message.messageNonce]
)
}
/**
* Returns the canonical hash of a cross chain message. This hash is used in various locations
* within the Optimism smart contracts and is the keccak256 hash of the result of
* encodeCrossChainMessage.
*
* @param message Cross chain message to hash.
* @returns Canonical hash of the message.
*/
export const hashCrossChainMessage = (
message: CoreCrossChainMessage
): string => {
return ethers.utils.solidityKeccak256(
['bytes'],
[encodeCrossChainMessage(message)]
)
}
// TODO: A lot of this stuff could probably live in core-utils instead.
// Review this file eventually for stuff that could go into core-utils.
/**
* Returns a copy of the given object ({ ...obj }) with the given keys omitted.
*
* @param obj Object to return with the keys omitted.
* @param keys Keys to omit from the returned object.
* @returns A copy of the given object with the given keys omitted.
*/
export const omit = (obj: any, ...keys: string[]) => {
const copy = { ...obj }
for (const key of keys) {
delete copy[key]
}
return copy
}
/**
* Utility type for deep partials.
*/
export type DeepPartial<T> = {
[P in keyof T]?: DeepPartial<T[P]>
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment