Commit 3a726175 authored by Matthew Slipper's avatar Matthew Slipper

Merge branch 'develop' into fix/eip2930

parents a5254658 d14ee9fa
---
'@eth-optimism/integration-tests': minor
---
Updates to work with a live network
---
'@eth-optimism/batch-submitter-service': patch
---
Adds confirmation depth awareness to txmgr
---
'@eth-optimism/proxyd': minor
---
proxyd: Cache block-dependent RPCs
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
'@eth-optimism/proxyd': minor '@eth-optimism/proxyd': minor
--- ---
Add integration tests and batching Add debug cache status header to proxyd responses
---
'@eth-optimism/integration-tests': patch
---
Use hardhat-ethers for importing factories in integration tests
---
'@eth-optimism/l2geth': patch
---
Add reinitialize-by-url command, add dump chain state command
---
'@eth-optimism/integration-tests': patch
---
Split OVMMulticall.sol into Multicall.sol & OVMContext.sol
---
'@eth-optimism/l2geth': patch
---
Fix blocknumber monotonicity logging bug
...@@ -101,7 +101,17 @@ module.exports = { ...@@ -101,7 +101,17 @@ module.exports = {
'id-match': 'off', 'id-match': 'off',
'import/no-extraneous-dependencies': ['error'], 'import/no-extraneous-dependencies': ['error'],
'import/no-internal-modules': 'off', 'import/no-internal-modules': 'off',
'import/order': 'off', 'import/order': [
"error",
{
groups: [
'builtin',
'external',
'internal',
],
'newlines-between': 'always',
},
],
indent: 'off', indent: 'off',
'jsdoc/check-alignment': 'error', 'jsdoc/check-alignment': 'error',
'jsdoc/check-indentation': 'error', 'jsdoc/check-indentation': 'error',
......
...@@ -6,6 +6,8 @@ on: ...@@ -6,6 +6,8 @@ on:
- 'master' - 'master'
- 'develop' - 'develop'
pull_request: pull_request:
paths:
- 'go/proxyd/**'
workflow_dispatch: workflow_dispatch:
defaults: defaults:
...@@ -13,7 +15,9 @@ defaults: ...@@ -13,7 +15,9 @@ defaults:
working-directory: ./go/proxyd working-directory: ./go/proxyd
jobs: jobs:
test: tests:
runs-on: ubuntu-latest
steps: steps:
- name: Install Go - name: Install Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
......
...@@ -29,6 +29,7 @@ jobs: ...@@ -29,6 +29,7 @@ jobs:
rpc-proxy : ${{ steps.packages.outputs.rpc-proxy }} rpc-proxy : ${{ steps.packages.outputs.rpc-proxy }}
op-exporter : ${{ steps.packages.outputs.op-exporter }} op-exporter : ${{ steps.packages.outputs.op-exporter }}
l2geth-exporter : ${{ steps.packages.outputs.l2geth-exporter }} l2geth-exporter : ${{ steps.packages.outputs.l2geth-exporter }}
batch-submitter-service : ${{ steps.packages.outputs.batch-submitter-service }}
steps: steps:
- name: Check out source code - name: Check out source code
...@@ -506,3 +507,29 @@ jobs: ...@@ -506,3 +507,29 @@ jobs:
file: ./ops/docker/Dockerfile.rpc-proxy file: ./ops/docker/Dockerfile.rpc-proxy
push: true push: true
tags: ethereumoptimism/rpc-proxy:${{ needs.canary-publish.outputs.rpc-proxy }} tags: ethereumoptimism/rpc-proxy:${{ needs.canary-publish.outputs.rpc-proxy }}
batch-submitter-service:
name: Publish batch-submitter-service Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish
if: needs.canary-publish.outputs.batch-submitter-service != ''
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: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.batch-submitter-service
push: true
tags: ethereumoptimism/batch-submitter-service:${{ needs.canary-publish.outputs.batch-submitter-service }}
...@@ -25,6 +25,7 @@ jobs: ...@@ -25,6 +25,7 @@ jobs:
hardhat-node: ${{ steps.packages.outputs.hardhat-node }} hardhat-node: ${{ steps.packages.outputs.hardhat-node }}
op-exporter : ${{ steps.packages.outputs.op-exporter }} op-exporter : ${{ steps.packages.outputs.op-exporter }}
l2geth-exporter : ${{ steps.packages.outputs.l2geth-exporter }} l2geth-exporter : ${{ steps.packages.outputs.l2geth-exporter }}
batch-submitter-service : ${{ steps.packages.outputs.batch-submitter-service }}
steps: steps:
- name: Checkout Repo - name: Checkout Repo
...@@ -502,3 +503,29 @@ jobs: ...@@ -502,3 +503,29 @@ jobs:
push: true push: true
tags: ethereumoptimism/replica-healthcheck:${{ needs.builder.outputs.replica-healthcheck }},ethereumoptimism/replica-healthcheck:latest tags: ethereumoptimism/replica-healthcheck:${{ needs.builder.outputs.replica-healthcheck }},ethereumoptimism/replica-healthcheck:latest
build-args: BUILDER_TAG=${{ needs.builder.outputs.builder }} build-args: BUILDER_TAG=${{ needs.builder.outputs.builder }}
batch-submitter-service:
name: Publish batch-submitter-service Version ${{ needs.release.outputs.batch-submitter-service }}
needs: release
if: needs.release.outputs.batch-submitter-service != ''
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: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.batch-submitter-service
push: true
tags: ethereumoptimism/batch-submitter-service:${{ needs.release.outputs.batch-submitter-service }},ethereumoptimism/batch-submitter-service:latest
# @eth-optimism/batch-submitter-service # @eth-optimism/batch-submitter-service
## 0.1.0
### Minor Changes
- 356b7271: Add multi-tx support, clear pending txs on startup
### Patch Changes
- 85aa148d: Adds confirmation depth awareness to txmgr
## 0.0.2 ## 0.0.2
### Patch Changes ### Patch Changes
- d6e0de5a: Fix metrics server - d6e0de5a: Fix metrics server
{ {
"name": "@eth-optimism/batch-submitter-service", "name": "@eth-optimism/batch-submitter-service",
"version": "0.0.2", "version": "0.1.0",
"private": true, "private": true,
"devDependencies": {} "devDependencies": {}
} }
# @eth-optimism/proxyd # @eth-optimism/proxyd
## 3.6.0
### Minor Changes
- 096c5f20: proxyd: Allow cached RPCs to be evicted by redis
- 71d64834: Add caching for block-dependent RPCs
- fd2e1523: proxyd: Cache block-dependent RPCs
- 1760613c: Add integration tests and batching
## 3.5.0 ## 3.5.0
### Minor Changes ### Minor Changes
......
...@@ -2,6 +2,7 @@ package proxyd ...@@ -2,6 +2,7 @@ package proxyd
import ( import (
"context" "context"
"time"
"github.com/go-redis/redis/v8" "github.com/go-redis/redis/v8"
"github.com/golang/snappy" "github.com/golang/snappy"
...@@ -16,6 +17,8 @@ type Cache interface { ...@@ -16,6 +17,8 @@ type Cache interface {
const ( const (
// assuming an average RPCRes size of 3 KB // assuming an average RPCRes size of 3 KB
memoryCacheLimit = 4096 memoryCacheLimit = 4096
// Set a large ttl to avoid expirations. However, a ttl must be set for volatile-lru to take effect.
redisTTL = 30 * 7 * 24 * time.Hour
) )
type cache struct { type cache struct {
...@@ -67,7 +70,7 @@ func (c *redisCache) Get(ctx context.Context, key string) (string, error) { ...@@ -67,7 +70,7 @@ func (c *redisCache) Get(ctx context.Context, key string) (string, error) {
} }
func (c *redisCache) Put(ctx context.Context, key string, value string) error { func (c *redisCache) Put(ctx context.Context, key string, value string) error {
err := c.rdb.Set(ctx, key, value, 0).Err() err := c.rdb.SetEX(ctx, key, value, redisTTL).Err()
if err != nil { if err != nil {
RecordRedisError("CacheSet") RecordRedisError("CacheSet")
} }
......
...@@ -16,12 +16,14 @@ func TestCaching(t *testing.T) { ...@@ -16,12 +16,14 @@ func TestCaching(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
defer redis.Close() defer redis.Close()
backend := NewMockBackend(RPCResponseHandler(map[string]string{ hdlr := NewRPCResponseHandler(map[string]string{
"eth_chainId": "0x420", "eth_chainId": "0x420",
"net_version": "0x1234", "net_version": "0x1234",
"eth_blockNumber": "0x64", "eth_blockNumber": "0x64",
"eth_getBlockByNumber": "dummy_block", "eth_getBlockByNumber": "dummy_block",
})) "eth_call": "dummy_call",
})
backend := NewMockBackend(hdlr)
defer backend.Close() defer backend.Close()
require.NoError(t, os.Setenv("GOOD_BACKEND_RPC_URL", backend.URL())) require.NoError(t, os.Setenv("GOOD_BACKEND_RPC_URL", backend.URL()))
...@@ -39,16 +41,19 @@ func TestCaching(t *testing.T) { ...@@ -39,16 +41,19 @@ func TestCaching(t *testing.T) {
method string method string
params []interface{} params []interface{}
response string response string
backendCalls int
}{ }{
{ {
"eth_chainId", "eth_chainId",
nil, nil,
"{\"jsonrpc\": \"2.0\", \"result\": \"0x420\", \"id\": 999}", "{\"jsonrpc\": \"2.0\", \"result\": \"0x420\", \"id\": 999}",
1,
}, },
{ {
"net_version", "net_version",
nil, nil,
"{\"jsonrpc\": \"2.0\", \"result\": \"0x1234\", \"id\": 999}", "{\"jsonrpc\": \"2.0\", \"result\": \"0x1234\", \"id\": 999}",
1,
}, },
{ {
"eth_getBlockByNumber", "eth_getBlockByNumber",
...@@ -57,23 +62,79 @@ func TestCaching(t *testing.T) { ...@@ -57,23 +62,79 @@ func TestCaching(t *testing.T) {
true, true,
}, },
"{\"jsonrpc\": \"2.0\", \"result\": \"dummy_block\", \"id\": 999}", "{\"jsonrpc\": \"2.0\", \"result\": \"dummy_block\", \"id\": 999}",
1,
},
{
"eth_call",
[]interface{}{
struct {
To string `json:"to"`
}{
"0x1234",
},
"0x60",
},
"{\"id\":999,\"jsonrpc\":\"2.0\",\"result\":\"dummy_call\"}",
1,
},
{
"eth_blockNumber",
nil,
"{\"id\":999,\"jsonrpc\":\"2.0\",\"result\":\"0x64\"}",
0,
},
{
"eth_call",
[]interface{}{
struct {
To string `json:"to"`
}{
"0x1234",
},
"latest",
},
"{\"id\":999,\"jsonrpc\":\"2.0\",\"result\":\"dummy_call\"}",
2,
},
{
"eth_call",
[]interface{}{
struct {
To string `json:"to"`
}{
"0x1234",
},
"pending",
},
"{\"id\":999,\"jsonrpc\":\"2.0\",\"result\":\"dummy_call\"}",
2,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.method, func(t *testing.T) { t.Run(tt.method, func(t *testing.T) {
_, _, err := client.SendRPC(tt.method, tt.params) resRaw, _, err := client.SendRPC(tt.method, tt.params)
require.NoError(t, err) require.NoError(t, err)
res, _, err := client.SendRPC(tt.method, tt.params) resCache, _, err := client.SendRPC(tt.method, tt.params)
require.NoError(t, err) require.NoError(t, err)
RequireEqualJSON(t, []byte(tt.response), res) RequireEqualJSON(t, []byte(tt.response), resCache)
RequireEqualJSON(t, resRaw, resCache)
require.Equal(t, tt.backendCalls, countRequests(backend, tt.method))
backend.Reset()
})
}
hdlr.SetResponse("eth_blockNumber", "0x100")
time.Sleep(1500 * time.Millisecond)
resRaw, _, err := client.SendRPC("eth_blockNumber", nil)
RequireEqualJSON(t, []byte("{\"id\":999,\"jsonrpc\":\"2.0\",\"result\":\"0x100\"}"), resRaw)
}
func countRequests(backend *MockBackend, name string) int {
var count int var count int
for _, req := range backend.Requests() { for _, req := range backend.Requests() {
if bytes.Contains(req.Body, []byte(tt.method)) { if bytes.Contains(req.Body, []byte(name)) {
count++ count++
} }
} }
require.Equal(t, 1, count) return count
backend.Reset()
})
}
} }
...@@ -31,8 +31,24 @@ func SingleResponseHandler(code int, response string) http.HandlerFunc { ...@@ -31,8 +31,24 @@ func SingleResponseHandler(code int, response string) http.HandlerFunc {
} }
} }
func RPCResponseHandler(rpcResponses map[string]string) http.HandlerFunc { type RPCResponseHandler struct {
return func(w http.ResponseWriter, r *http.Request) { mtx sync.RWMutex
rpcResponses map[string]string
}
func NewRPCResponseHandler(rpcResponses map[string]string) *RPCResponseHandler {
return &RPCResponseHandler{
rpcResponses: rpcResponses,
}
}
func (h *RPCResponseHandler) SetResponse(method, response string) {
h.mtx.Lock()
defer h.mtx.Unlock()
h.rpcResponses[method] = response
}
func (h *RPCResponseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body) body, err := ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
panic(err) panic(err)
...@@ -41,7 +57,9 @@ func RPCResponseHandler(rpcResponses map[string]string) http.HandlerFunc { ...@@ -41,7 +57,9 @@ func RPCResponseHandler(rpcResponses map[string]string) http.HandlerFunc {
if err != nil { if err != nil {
panic(err) panic(err)
} }
res := rpcResponses[req.Method] h.mtx.RLock()
res := h.rpcResponses[req.Method]
h.mtx.RUnlock()
if res == "" { if res == "" {
w.WriteHeader(400) w.WriteHeader(400)
return return
...@@ -56,7 +74,6 @@ func RPCResponseHandler(rpcResponses map[string]string) http.HandlerFunc { ...@@ -56,7 +74,6 @@ func RPCResponseHandler(rpcResponses map[string]string) http.HandlerFunc {
if err := enc.Encode(out); err != nil { if err := enc.Encode(out); err != nil {
panic(err) panic(err)
} }
}
} }
func NewMockBackend(handler http.Handler) *MockBackend { func NewMockBackend(handler http.Handler) *MockBackend {
......
...@@ -25,3 +25,5 @@ backends = ["good"] ...@@ -25,3 +25,5 @@ backends = ["good"]
eth_chainId = "main" eth_chainId = "main"
net_version = "main" net_version = "main"
eth_getBlockByNumber = "main" eth_getBlockByNumber = "main"
eth_blockNumber = "main"
eth_call = "main"
{ {
"name": "@eth-optimism/proxyd", "name": "@eth-optimism/proxyd",
"version": "3.5.0", "version": "3.6.0",
"private": true, "private": true,
"dependencies": {} "dependencies": {}
} }
...@@ -15,16 +15,46 @@ type RPCReq struct { ...@@ -15,16 +15,46 @@ type RPCReq struct {
} }
type RPCRes struct { type RPCRes struct {
JSONRPC string
Result interface{}
Error *RPCErr
ID json.RawMessage
}
type rpcResJSON struct {
JSONRPC string `json:"jsonrpc"` JSONRPC string `json:"jsonrpc"`
Result interface{} `json:"result,omitempty"` Result interface{} `json:"result,omitempty"`
Error *RPCErr `json:"error,omitempty"` Error *RPCErr `json:"error,omitempty"`
ID json.RawMessage `json:"id"` ID json.RawMessage `json:"id"`
} }
type nullResultRPCRes struct {
JSONRPC string `json:"jsonrpc"`
Result interface{} `json:"result"`
ID json.RawMessage `json:"id"`
}
func (r *RPCRes) IsError() bool { func (r *RPCRes) IsError() bool {
return r.Error != nil return r.Error != nil
} }
func (r *RPCRes) MarshalJSON() ([]byte, error) {
if r.Result == nil && r.Error == nil {
return json.Marshal(&nullResultRPCRes{
JSONRPC: r.JSONRPC,
Result: nil,
ID: r.ID,
})
}
return json.Marshal(&rpcResJSON{
JSONRPC: r.JSONRPC,
Result: r.Result,
Error: r.Error,
ID: r.ID,
})
}
type RPCErr struct { type RPCErr struct {
Code int `json:"code"` Code int `json:"code"`
Message string `json:"message"` Message string `json:"message"`
......
package proxyd
import (
"encoding/json"
"github.com/stretchr/testify/require"
"testing"
)
func TestRPCResJSON(t *testing.T) {
tests := []struct {
name string
in *RPCRes
out string
}{
{
"string result",
&RPCRes{
JSONRPC: JSONRPCVersion,
Result: "foobar",
ID: []byte("123"),
},
`{"jsonrpc":"2.0","result":"foobar","id":123}`,
},
{
"object result",
&RPCRes{
JSONRPC: JSONRPCVersion,
Result: struct {
Str string `json:"str"`
}{
"test",
},
ID: []byte("123"),
},
`{"jsonrpc":"2.0","result":{"str":"test"},"id":123}`,
},
{
"nil result",
&RPCRes{
JSONRPC: JSONRPCVersion,
Result: nil,
ID: []byte("123"),
},
`{"jsonrpc":"2.0","result":null,"id":123}`,
},
{
"error result",
&RPCRes{
JSONRPC: JSONRPCVersion,
Error: &RPCErr{
Code: 1234,
Message: "test err",
},
ID: []byte("123"),
},
`{"jsonrpc":"2.0","error":{"code":1234,"message":"test err"},"id":123}`,
},
{
"string ID",
&RPCRes{
JSONRPC: JSONRPCVersion,
Result: "foobar",
ID: []byte("\"123\""),
},
`{"jsonrpc":"2.0","result":"foobar","id":"123"}`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
out, err := json.Marshal(tt.in)
require.NoError(t, err)
require.Equal(t, tt.out, string(out))
})
}
}
...@@ -25,6 +25,7 @@ const ( ...@@ -25,6 +25,7 @@ const (
ContextKeyReqID = "req_id" ContextKeyReqID = "req_id"
ContextKeyXForwardedFor = "x_forwarded_for" ContextKeyXForwardedFor = "x_forwarded_for"
MaxBatchRPCCalls = 100 MaxBatchRPCCalls = 100
cacheStatusHdr = "X-Proxyd-Cache-Status"
) )
type Server struct { type Server struct {
...@@ -159,6 +160,7 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) { ...@@ -159,6 +160,7 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) {
} }
batchRes := make([]*RPCRes, len(reqs), len(reqs)) batchRes := make([]*RPCRes, len(reqs), len(reqs))
var batchContainsCached bool
for i := 0; i < len(reqs); i++ { for i := 0; i < len(reqs); i++ {
req, err := ParseRPCReq(reqs[i]) req, err := ParseRPCReq(reqs[i])
if err != nil { if err != nil {
...@@ -167,9 +169,14 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) { ...@@ -167,9 +169,14 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) {
continue continue
} }
batchRes[i] = s.handleSingleRPC(ctx, req) var cached bool
batchRes[i], cached = s.handleSingleRPC(ctx, req)
if cached {
batchContainsCached = true
}
} }
setCacheHeader(w, batchContainsCached)
writeBatchRPCRes(ctx, w, batchRes) writeBatchRPCRes(ctx, w, batchRes)
return return
} }
...@@ -181,14 +188,15 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) { ...@@ -181,14 +188,15 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) {
return return
} }
backendRes := s.handleSingleRPC(ctx, req) backendRes, cached := s.handleSingleRPC(ctx, req)
setCacheHeader(w, cached)
writeRPCRes(ctx, w, backendRes) writeRPCRes(ctx, w, backendRes)
} }
func (s *Server) handleSingleRPC(ctx context.Context, req *RPCReq) *RPCRes { func (s *Server) handleSingleRPC(ctx context.Context, req *RPCReq) (*RPCRes, bool) {
if err := ValidateRPCReq(req); err != nil { if err := ValidateRPCReq(req); err != nil {
RecordRPCError(ctx, BackendProxyd, MethodUnknown, err) RecordRPCError(ctx, BackendProxyd, MethodUnknown, err)
return NewRPCErrorRes(nil, err) return NewRPCErrorRes(nil, err), false
} }
group := s.rpcMethodMappings[req.Method] group := s.rpcMethodMappings[req.Method]
...@@ -202,7 +210,7 @@ func (s *Server) handleSingleRPC(ctx context.Context, req *RPCReq) *RPCRes { ...@@ -202,7 +210,7 @@ func (s *Server) handleSingleRPC(ctx context.Context, req *RPCReq) *RPCRes {
"method", req.Method, "method", req.Method,
) )
RecordRPCError(ctx, BackendProxyd, MethodUnknown, ErrMethodNotWhitelisted) RecordRPCError(ctx, BackendProxyd, MethodUnknown, ErrMethodNotWhitelisted)
return NewRPCErrorRes(req.ID, ErrMethodNotWhitelisted) return NewRPCErrorRes(req.ID, ErrMethodNotWhitelisted), false
} }
var backendRes *RPCRes var backendRes *RPCRes
...@@ -215,7 +223,7 @@ func (s *Server) handleSingleRPC(ctx context.Context, req *RPCReq) *RPCRes { ...@@ -215,7 +223,7 @@ func (s *Server) handleSingleRPC(ctx context.Context, req *RPCReq) *RPCRes {
) )
} }
if backendRes != nil { if backendRes != nil {
return backendRes return backendRes, true
} }
backendRes, err = s.backendGroups[group].Forward(ctx, req) backendRes, err = s.backendGroups[group].Forward(ctx, req)
...@@ -226,7 +234,7 @@ func (s *Server) handleSingleRPC(ctx context.Context, req *RPCReq) *RPCRes { ...@@ -226,7 +234,7 @@ func (s *Server) handleSingleRPC(ctx context.Context, req *RPCReq) *RPCRes {
"req_id", GetReqID(ctx), "req_id", GetReqID(ctx),
"err", err, "err", err,
) )
return NewRPCErrorRes(req.ID, err) return NewRPCErrorRes(req.ID, err), false
} }
if backendRes.Error == nil { if backendRes.Error == nil {
...@@ -239,7 +247,7 @@ func (s *Server) handleSingleRPC(ctx context.Context, req *RPCReq) *RPCRes { ...@@ -239,7 +247,7 @@ func (s *Server) handleSingleRPC(ctx context.Context, req *RPCReq) *RPCRes {
} }
} }
return backendRes return backendRes, false
} }
func (s *Server) HandleWS(w http.ResponseWriter, r *http.Request) { func (s *Server) HandleWS(w http.ResponseWriter, r *http.Request) {
...@@ -322,6 +330,14 @@ func (s *Server) populateContext(w http.ResponseWriter, r *http.Request) context ...@@ -322,6 +330,14 @@ func (s *Server) populateContext(w http.ResponseWriter, r *http.Request) context
) )
} }
func setCacheHeader(w http.ResponseWriter, cached bool) {
if cached {
w.Header().Set(cacheStatusHdr, "HIT")
} else {
w.Header().Set(cacheStatusHdr, "MISS")
}
}
func writeRPCError(ctx context.Context, w http.ResponseWriter, id json.RawMessage, err error) { func writeRPCError(ctx context.Context, w http.ResponseWriter, id json.RawMessage, err error) {
var res *RPCRes var res *RPCRes
if r, ok := err.(*RPCErr); ok { if r, ok := err.(*RPCErr); ok {
......
# @eth-optimism/integration-tests # @eth-optimism/integration-tests
## 0.5.0
### Minor Changes
- c1e923f9: Updates to work with a live network
### Patch Changes
- 968fb38d: Use hardhat-ethers for importing factories in integration tests
- a7fbafa8: Split OVMMulticall.sol into Multicall.sol & OVMContext.sol
## 0.4.2 ## 0.4.2
### Patch Changes ### Patch Changes
......
import { utils, Wallet, BigNumber } from 'ethers' import { utils, Wallet, BigNumber } from 'ethers'
import { expect } from 'chai'
import { setupActor, setupRun, actor, run } from './lib/convenience' import { setupActor, setupRun, actor, run } from './lib/convenience'
import { OptimismEnv } from '../test/shared/env' import { OptimismEnv } from '../test/shared/env'
import { Direction } from '../test/shared/watcher-utils' import { Direction } from '../test/shared/watcher-utils'
import { expect } from 'chai'
interface BenchContext { interface BenchContext {
l1Wallet: Wallet l1Wallet: Wallet
......
import { performance } from 'perf_hooks'
import { Mutex } from 'async-mutex' import { Mutex } from 'async-mutex'
import { sleep } from '../../test/shared/utils'
import { import {
sanitizeForMetrics, sanitizeForMetrics,
benchDurationsSummary, benchDurationsSummary,
...@@ -9,7 +11,7 @@ import { ...@@ -9,7 +11,7 @@ import {
failedBenchRunsTotal, failedBenchRunsTotal,
} from './metrics' } from './metrics'
import { ActorLogger, WorkerLogger } from './logger' import { ActorLogger, WorkerLogger } from './logger'
import { performance } from 'perf_hooks' import { sleep } from '../../test/shared/utils'
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function
const asyncNoop = async () => {} const asyncNoop = async () => {}
......
import fs from 'fs' import fs from 'fs'
import client from 'prom-client'
import http from 'http' import http from 'http'
import url from 'url' import url from 'url'
import client from 'prom-client'
export const metricsRegistry = new client.Registry() export const metricsRegistry = new client.Registry()
const metricName = (name: string) => { const metricName = (name: string) => {
......
import * as path from 'path' import * as path from 'path'
import { Command } from 'commander'
import { defaultRuntime } from './convenience' import { defaultRuntime } from './convenience'
import { RunOpts } from './actor' import { RunOpts } from './actor'
import { Command } from 'commander'
import pkg from '../../package.json'
import { serveMetrics } from './metrics' import { serveMetrics } from './metrics'
import pkg from '../../package.json'
const program = new Command() const program = new Command()
program.version(pkg.version) program.version(pkg.version)
......
import { utils, Wallet, Contract } from 'ethers' import { utils, Wallet, Contract } from 'ethers'
import { expect } from 'chai'
import { actor, run, setupActor, setupRun } from './lib/convenience' import { actor, run, setupActor, setupRun } from './lib/convenience'
import { OptimismEnv } from '../test/shared/env' import { OptimismEnv } from '../test/shared/env'
import ERC721 from '../artifacts/contracts/NFT.sol/NFT.json' import ERC721 from '../artifacts/contracts/NFT.sol/NFT.json'
import { expect } from 'chai'
interface Context { interface Context {
wallet: Wallet wallet: Wallet
......
import { utils, Wallet, BigNumber } from 'ethers' import { utils, Wallet, BigNumber } from 'ethers'
import { expect } from 'chai' import { expect } from 'chai'
import { actor, setupRun, setupActor, run } from './lib/convenience' import { actor, setupRun, setupActor, run } from './lib/convenience'
import { OptimismEnv } from '../test/shared/env' import { OptimismEnv } from '../test/shared/env'
......
import { utils, Wallet, Contract } from 'ethers' import { utils, Wallet, Contract } from 'ethers'
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { expect } from 'chai'
import { actor, setupActor, run, setupRun } from './lib/convenience' import { actor, setupActor, run, setupRun } from './lib/convenience'
import { OptimismEnv } from '../test/shared/env' import { OptimismEnv } from '../test/shared/env'
import { expect } from 'chai'
interface Context { interface Context {
wallet: Wallet wallet: Wallet
......
import { Contract, utils, Wallet } from 'ethers' import { Contract, utils, Wallet } from 'ethers'
import { actor, run, setupActor, setupRun } from './lib/convenience'
import { OptimismEnv } from '../test/shared/env'
import { FeeAmount } from '@uniswap/v3-sdk' import { FeeAmount } from '@uniswap/v3-sdk'
import ERC20 from '../artifacts/contracts/ERC20.sol/ERC20.json'
import { abi as NFTABI } from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json' 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 { abi as RouterABI } from '@uniswap/v3-periphery/artifacts/contracts/SwapRouter.sol/SwapRouter.json'
import { actor, run, setupActor, setupRun } from './lib/convenience'
import { OptimismEnv } from '../test/shared/env'
import ERC20 from '../artifacts/contracts/ERC20.sol/ERC20.json'
interface Context { interface Context {
contracts: { [name: string]: Contract } contracts: { [name: string]: Contract }
wallet: Wallet wallet: Wallet
......
{ {
"private": true, "private": true,
"name": "@eth-optimism/integration-tests", "name": "@eth-optimism/integration-tests",
"version": "0.4.2", "version": "0.5.0",
"description": "[Optimism] Integration tests", "description": "[Optimism] Integration tests",
"scripts": { "scripts": {
"lint": "yarn lint:fix && yarn lint:check", "lint": "yarn lint:fix && yarn lint:check",
...@@ -28,9 +28,9 @@ ...@@ -28,9 +28,9 @@
"url": "https://github.com/ethereum-optimism/optimism.git" "url": "https://github.com/ethereum-optimism/optimism.git"
}, },
"devDependencies": { "devDependencies": {
"@eth-optimism/contracts": "0.5.8", "@eth-optimism/contracts": "0.5.9",
"@eth-optimism/core-utils": "0.7.3", "@eth-optimism/core-utils": "0.7.4",
"@eth-optimism/message-relayer": "0.2.12", "@eth-optimism/message-relayer": "0.2.13",
"@ethersproject/abstract-provider": "^5.5.1", "@ethersproject/abstract-provider": "^5.5.1",
"@ethersproject/providers": "^5.4.5", "@ethersproject/providers": "^5.4.5",
"@ethersproject/transactions": "^5.4.0", "@ethersproject/transactions": "^5.4.0",
......
import { expect } from './shared/setup'
/* Imports: External */ /* Imports: External */
import { Contract, ContractFactory } from 'ethers' import { Contract, ContractFactory } from 'ethers'
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { applyL1ToL2Alias, awaitCondition } from '@eth-optimism/core-utils' import { applyL1ToL2Alias, awaitCondition } from '@eth-optimism/core-utils'
/* Imports: Internal */ /* Imports: Internal */
import { expect } from './shared/setup'
import { Direction } from './shared/watcher-utils' import { Direction } from './shared/watcher-utils'
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
import { import {
......
import { expect } from './shared/setup'
import { BigNumber, Contract, ContractFactory, utils, Wallet } from 'ethers' import { BigNumber, Contract, ContractFactory, utils, Wallet } from 'ethers'
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import * as L2Artifact from '@eth-optimism/contracts/artifacts/contracts/standards/L2StandardERC20.sol/L2StandardERC20.json' import * as L2Artifact from '@eth-optimism/contracts/artifacts/contracts/standards/L2StandardERC20.sol/L2StandardERC20.json'
import { expect } from './shared/setup'
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
import { withdrawalTest } from './shared/utils' import { withdrawalTest } from './shared/utils'
import { Direction } from './shared/watcher-utils' import { Direction } from './shared/watcher-utils'
......
import { expect } from './shared/setup'
import { BigNumber, Contract, ContractFactory, utils, Wallet } from 'ethers' import { BigNumber, Contract, ContractFactory, utils, Wallet } from 'ethers'
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { UniswapV3Deployer } from 'uniswap-v3-deploy-plugin/dist/deployer/UniswapV3Deployer' import { UniswapV3Deployer } from 'uniswap-v3-deploy-plugin/dist/deployer/UniswapV3Deployer'
import { OptimismEnv } from './shared/env'
import { FeeAmount, TICK_SPACINGS } from '@uniswap/v3-sdk' 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 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 { abi as RouterABI } from '@uniswap/v3-periphery/artifacts/contracts/SwapRouter.sol/SwapRouter.json'
import { OptimismEnv } from './shared/env'
import { expect } from './shared/setup'
// Below methods taken from the Uniswap test suite, see // Below methods taken from the Uniswap test suite, see
// https://github.com/Uniswap/v3-periphery/blob/main/test/shared/ticks.ts // https://github.com/Uniswap/v3-periphery/blob/main/test/shared/ticks.ts
const getMinTick = (tickSpacing: number) => const getMinTick = (tickSpacing: number) =>
......
import { expect } from './shared/setup'
/* Imports: External */ /* Imports: External */
import { BigNumber, utils } from 'ethers' import { BigNumber, utils } from 'ethers'
import { serialize } from '@ethersproject/transactions' import { serialize } from '@ethersproject/transactions'
import { predeploys, getContractFactory } from '@eth-optimism/contracts' import { predeploys, getContractFactory } from '@eth-optimism/contracts'
/* Imports: Internal */ /* Imports: Internal */
import { expect } from './shared/setup'
import { hardhatTest } from './shared/utils' import { hardhatTest } from './shared/utils'
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
import { Direction } from './shared/watcher-utils' import { Direction } from './shared/watcher-utils'
......
import { expect } from './shared/setup'
import { BigNumber, Contract, ContractFactory, Wallet } from 'ethers' import { BigNumber, Contract, ContractFactory, Wallet } from 'ethers'
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { expect } from './shared/setup'
import { import {
fundUser, fundUser,
encodeSolidityRevertMessage, encodeSolidityRevertMessage,
......
import { expect } from './shared/setup'
/* Imports: External */ /* Imports: External */
import { Wallet, utils, BigNumber } from 'ethers' import { Wallet, utils, BigNumber } from 'ethers'
import { serialize } from '@ethersproject/transactions' import { serialize } from '@ethersproject/transactions'
...@@ -7,8 +5,8 @@ import { predeploys } from '@eth-optimism/contracts' ...@@ -7,8 +5,8 @@ import { predeploys } from '@eth-optimism/contracts'
import { expectApprox } from '@eth-optimism/core-utils' import { expectApprox } from '@eth-optimism/core-utils'
/* Imports: Internal */ /* Imports: Internal */
import { expect } from './shared/setup'
import { Direction } from './shared/watcher-utils' import { Direction } from './shared/watcher-utils'
import { import {
DEFAULT_TEST_GAS_L1, DEFAULT_TEST_GAS_L1,
DEFAULT_TEST_GAS_L2, DEFAULT_TEST_GAS_L2,
......
import { expect } from './shared/setup'
/* Imports: External */ /* Imports: External */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { injectL2Context, expectApprox } from '@eth-optimism/core-utils' import { injectL2Context, expectApprox } from '@eth-optimism/core-utils'
...@@ -7,6 +5,7 @@ import { predeploys } from '@eth-optimism/contracts' ...@@ -7,6 +5,7 @@ import { predeploys } from '@eth-optimism/contracts'
import { Contract, BigNumber } from 'ethers' import { Contract, BigNumber } from 'ethers'
/* Imports: Internal */ /* Imports: Internal */
import { expect } from './shared/setup'
import { import {
l2Provider, l2Provider,
l1Provider, l1Provider,
......
import { expect } from './shared/setup'
/* Imports: Internal */ /* Imports: Internal */
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { predeploys, getContractInterface } from '@eth-optimism/contracts' import { predeploys, getContractInterface } from '@eth-optimism/contracts'
/* Imports: External */ /* Imports: External */
import { expect } from './shared/setup'
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
describe('predeploys', () => { describe('predeploys', () => {
......
import { expect } from './shared/setup'
/* Imports: Internal */ /* Imports: Internal */
import { providers } from 'ethers' import { providers } from 'ethers'
import { injectL2Context, applyL1ToL2Alias } from '@eth-optimism/core-utils' import { injectL2Context, applyL1ToL2Alias } from '@eth-optimism/core-utils'
/* Imports: External */ /* Imports: External */
import { expect } from './shared/setup'
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
import { Direction } from './shared/watcher-utils' import { Direction } from './shared/watcher-utils'
import { DEFAULT_TEST_GAS_L1, envConfig } from './shared/utils' import { DEFAULT_TEST_GAS_L1, envConfig } from './shared/utils'
......
import { TransactionReceipt } from '@ethersproject/abstract-provider'
import { expect } from './shared/setup' import { expect } from './shared/setup'
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
import { import {
...@@ -6,7 +8,6 @@ import { ...@@ -6,7 +8,6 @@ import {
sleep, sleep,
envConfig, envConfig,
} from './shared/utils' } from './shared/utils'
import { TransactionReceipt } from '@ethersproject/abstract-provider'
describe('Replica Tests', () => { describe('Replica Tests', () => {
let env: OptimismEnv let env: OptimismEnv
......
import { expect } from './shared/setup'
import { expectApprox, injectL2Context } from '@eth-optimism/core-utils' import { expectApprox, injectL2Context } from '@eth-optimism/core-utils'
import { Wallet, BigNumber, Contract, ContractFactory, constants } from 'ethers' import { Wallet, BigNumber, Contract, ContractFactory, constants } from 'ethers'
import { serialize } from '@ethersproject/transactions' import { serialize } from '@ethersproject/transactions'
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import {
TransactionReceipt,
TransactionRequest,
} from '@ethersproject/providers'
import { import {
sleep, sleep,
l2Provider, l2Provider,
...@@ -16,10 +19,7 @@ import { ...@@ -16,10 +19,7 @@ import {
envConfig, envConfig,
} from './shared/utils' } from './shared/utils'
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
import { import { expect } from './shared/setup'
TransactionReceipt,
TransactionRequest,
} from '@ethersproject/providers'
describe('Basic RPC tests', () => { describe('Basic RPC tests', () => {
let env: OptimismEnv let env: OptimismEnv
......
...@@ -4,7 +4,6 @@ import { ...@@ -4,7 +4,6 @@ import {
TransactionResponse, TransactionResponse,
} from '@ethersproject/providers' } from '@ethersproject/providers'
import { Watcher } from '@eth-optimism/core-utils' import { Watcher } from '@eth-optimism/core-utils'
import { Contract, Transaction } from 'ethers' import { Contract, Transaction } from 'ethers'
export const initWatcher = async ( export const initWatcher = async (
......
import { expect } from './shared/setup'
/* Imports: External */ /* Imports: External */
import { Contract, Wallet, utils } from 'ethers' import { Contract, Wallet, utils } from 'ethers'
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
/* Imports: Internal */ /* Imports: Internal */
import { expect } from './shared/setup'
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
import { import {
executeL1ToL2TransactionsParallel, executeL1ToL2TransactionsParallel,
...@@ -15,7 +14,6 @@ import { ...@@ -15,7 +14,6 @@ import {
executeRepeatedL2Transactions, executeRepeatedL2Transactions,
fundRandomWallet, fundRandomWallet,
} from './shared/stress-test-helpers' } from './shared/stress-test-helpers'
/* Imports: Artifacts */ /* Imports: Artifacts */
import { envConfig, fundUser } from './shared/utils' import { envConfig, fundUser } from './shared/utils'
......
import { expect } from './shared/setup'
/* Imports: External */ /* Imports: External */
import { ContractFactory } from 'ethers' import { ContractFactory } from 'ethers'
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { predeploys } from '@eth-optimism/contracts' import { predeploys } from '@eth-optimism/contracts'
/* Imports: Internal */ /* Imports: Internal */
import { expect } from './shared/setup'
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
import { l2Provider } from './shared/utils' import { l2Provider } from './shared/utils'
......
# Changelog # Changelog
## 0.5.8
### Patch Changes
- 949916f8: Add a better error message for when the sequencer url is not configured when proxying user requests to the sequencer for `eth_sendRawTransaction` when running as a verifier/replica
- 300f79bf: Fix nonce issue
- ae96d784: Add reinitialize-by-url command, add dump chain state command
- c7569a16: Fix blocknumber monotonicity logging bug
## 0.5.7 ## 0.5.7
### Patch Changes ### Patch Changes
......
...@@ -209,14 +209,13 @@ func (st *StateTransition) buyGas() error { ...@@ -209,14 +209,13 @@ func (st *StateTransition) buyGas() error {
func (st *StateTransition) preCheck() error { func (st *StateTransition) preCheck() error {
// Make sure this transaction's nonce is correct. // Make sure this transaction's nonce is correct.
if st.msg.CheckNonce() { if st.msg.CheckNonce() {
nonce := st.state.GetNonce(st.msg.From())
if nonce < st.msg.Nonce() {
if rcfg.UsingOVM { if rcfg.UsingOVM {
// The nonce never increments for L1ToL2 txs
if st.msg.QueueOrigin() == types.QueueOriginL1ToL2 { if st.msg.QueueOrigin() == types.QueueOriginL1ToL2 {
return st.buyGas() return st.buyGas()
} }
} }
nonce := st.state.GetNonce(st.msg.From())
if nonce < st.msg.Nonce() {
return ErrNonceTooHigh return ErrNonceTooHigh
} else if nonce > st.msg.Nonce() { } else if nonce > st.msg.Nonce() {
return ErrNonceTooLow return ErrNonceTooLow
......
...@@ -50,7 +50,10 @@ import ( ...@@ -50,7 +50,10 @@ import (
"github.com/tyler-smith/go-bip39" "github.com/tyler-smith/go-bip39"
) )
var errOVMUnsupported = errors.New("OVM: Unsupported RPC Method") var (
errOVMUnsupported = errors.New("OVM: Unsupported RPC Method")
errNoSequencerURL = errors.New("sequencer transaction forwarding not configured")
)
const ( const (
// defaultDialTimeout is default duration the service will wait on // defaultDialTimeout is default duration the service will wait on
...@@ -1680,7 +1683,11 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod ...@@ -1680,7 +1683,11 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod
} }
if s.b.IsVerifier() { if s.b.IsVerifier() {
client, err := dialSequencerClientWithTimeout(ctx, s.b.SequencerClientHttp()) sequencerURL := s.b.SequencerClientHttp()
if sequencerURL == "" {
return common.Hash{}, errNoSequencerURL
}
client, err := dialSequencerClientWithTimeout(ctx, sequencerURL)
if err != nil { if err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
......
{ {
"name": "@eth-optimism/l2geth", "name": "@eth-optimism/l2geth",
"version": "0.5.7", "version": "0.5.8",
"private": true, "private": true,
"devDependencies": {} "devDependencies": {}
} }
...@@ -32,6 +32,10 @@ docker-compose \ ...@@ -32,6 +32,10 @@ docker-compose \
*Note*: This generates a large amount of log data which docker stores by default. See [Disk Usage](#disk-usage). *Note*: This generates a large amount of log data which docker stores by default. See [Disk Usage](#disk-usage).
Also note that Docker Desktop only allocates 2GB of memory by default, which isn't enough to run the docker-compose services reliably.
To allocate more memory, go to Settings > Resources in the Docker UI and use the slider to change the value (_4GB recommended_). Make sure to click Apply & Restart for the changes to take effect.
To start the stack with monitoring enabled, just add the metric composition file. To start the stack with monitoring enabled, just add the metric composition file.
``` ```
docker-compose \ docker-compose \
......
# Changelog # Changelog
## 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 ## 0.4.13
### Patch Changes ### Patch Changes
......
{ {
"private": true, "private": true,
"name": "@eth-optimism/batch-submitter", "name": "@eth-optimism/batch-submitter",
"version": "0.4.13", "version": "0.4.14",
"description": "[Optimism] Service for submitting transactions and transaction results", "description": "[Optimism] Service for submitting transactions and transaction results",
"main": "dist/index", "main": "dist/index",
"types": "dist/index", "types": "dist/index",
...@@ -34,8 +34,8 @@ ...@@ -34,8 +34,8 @@
}, },
"dependencies": { "dependencies": {
"@eth-optimism/common-ts": "0.2.1", "@eth-optimism/common-ts": "0.2.1",
"@eth-optimism/contracts": "0.5.8", "@eth-optimism/contracts": "0.5.9",
"@eth-optimism/core-utils": "0.7.3", "@eth-optimism/core-utils": "0.7.4",
"@eth-optimism/ynatm": "^0.2.2", "@eth-optimism/ynatm": "^0.2.2",
"@ethersproject/abstract-provider": "^5.4.1", "@ethersproject/abstract-provider": "^5.4.1",
"@ethersproject/providers": "^5.4.5", "@ethersproject/providers": "^5.4.5",
......
...@@ -14,6 +14,7 @@ import { Gauge, Histogram, Counter } from 'prom-client' ...@@ -14,6 +14,7 @@ import { Gauge, Histogram, Counter } from 'prom-client'
import { RollupInfo, sleep } from '@eth-optimism/core-utils' import { RollupInfo, sleep } from '@eth-optimism/core-utils'
import { Logger, Metrics } from '@eth-optimism/common-ts' import { Logger, Metrics } from '@eth-optimism/common-ts'
import { getContractFactory } from 'old-contracts' import { getContractFactory } from 'old-contracts'
/* Internal Imports */ /* Internal Imports */
import { TxSubmissionHooks } from '..' import { TxSubmissionHooks } from '..'
......
...@@ -12,8 +12,8 @@ import { ...@@ -12,8 +12,8 @@ import {
import { Logger, Metrics } from '@eth-optimism/common-ts' import { Logger, Metrics } from '@eth-optimism/common-ts'
/* Internal Imports */ /* Internal Imports */
import { BlockRange, BatchSubmitter } from '.'
import { TransactionSubmitter } from '../utils' import { TransactionSubmitter } from '../utils'
import { BlockRange, BatchSubmitter } from '.'
export class StateBatchSubmitter extends BatchSubmitter { export class StateBatchSubmitter extends BatchSubmitter {
// TODO: Change this so that we calculate start = scc.totalElements() and end = ctc.totalElements()! // TODO: Change this so that we calculate start = scc.totalElements() and end = ctc.totalElements()!
......
...@@ -20,9 +20,8 @@ import { ...@@ -20,9 +20,8 @@ import {
BatchContext, BatchContext,
AppendSequencerBatchParams, AppendSequencerBatchParams,
} from '../transaction-chain-contract' } from '../transaction-chain-contract'
import { BlockRange, BatchSubmitter } from '.'
import { TransactionSubmitter } from '../utils' import { TransactionSubmitter } from '../utils'
import { BlockRange, BatchSubmitter } from '.'
export interface AutoFixBatchOptions { export interface AutoFixBatchOptions {
fixDoublePlayedDeposits: boolean fixDoublePlayedDeposits: boolean
......
/* External Imports */ /* External Imports */
import { exit } from 'process'
import { injectL2Context, Bcfg } from '@eth-optimism/core-utils' import { injectL2Context, Bcfg } from '@eth-optimism/core-utils'
import * as Sentry from '@sentry/node' import * as Sentry from '@sentry/node'
import { Logger, Metrics, createMetricsServer } from '@eth-optimism/common-ts' import { Logger, Metrics, createMetricsServer } from '@eth-optimism/common-ts'
import { exit } from 'process'
import { Signer, Wallet } from 'ethers' import { Signer, Wallet } from 'ethers'
import { import {
StaticJsonRpcProvider, StaticJsonRpcProvider,
......
import { expect } from '../setup'
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import '@nomiclabs/hardhat-ethers' import '@nomiclabs/hardhat-ethers'
import { Signer, ContractFactory, Contract, BigNumber } from 'ethers' import { Signer, ContractFactory, Contract, BigNumber } from 'ethers'
import sinon from 'sinon' import sinon from 'sinon'
import scc from '@eth-optimism/contracts/artifacts/contracts/L1/rollup/StateCommitmentChain.sol/StateCommitmentChain.json' import scc from '@eth-optimism/contracts/artifacts/contracts/L1/rollup/StateCommitmentChain.sol/StateCommitmentChain.json'
import { getContractInterface } from '@eth-optimism/contracts' import { getContractInterface } from '@eth-optimism/contracts'
import { smockit, MockContract } from '@eth-optimism/smock' import { smockit, MockContract } from '@eth-optimism/smock'
import { getContractFactory } from 'old-contracts' import { getContractFactory } from 'old-contracts'
import { QueueOrigin, Batch, remove0x } from '@eth-optimism/core-utils'
import { Logger, Metrics } from '@eth-optimism/common-ts'
/* Internal Imports */ /* Internal Imports */
import { MockchainProvider } from './mockchain-provider' import { MockchainProvider } from './mockchain-provider'
import { import { expect } from '../setup'
makeAddressManager,
setProxyTarget,
FORCE_INCLUSION_PERIOD_SECONDS,
} from '../helpers'
import { import {
CanonicalTransactionChainContract, CanonicalTransactionChainContract,
TransactionBatchSubmitter as RealTransactionBatchSubmitter, TransactionBatchSubmitter as RealTransactionBatchSubmitter,
...@@ -28,9 +22,11 @@ import { ...@@ -28,9 +22,11 @@ import {
YnatmTransactionSubmitter, YnatmTransactionSubmitter,
ResubmissionConfig, ResubmissionConfig,
} from '../../src' } from '../../src'
import {
import { QueueOrigin, Batch, remove0x } from '@eth-optimism/core-utils' makeAddressManager,
import { Logger, Metrics } from '@eth-optimism/common-ts' setProxyTarget,
FORCE_INCLUSION_PERIOD_SECONDS,
} from '../helpers'
const EXAMPLE_STATE_ROOT = const EXAMPLE_STATE_ROOT =
'0x16b7f83f409c7195b1f4fde5652f1b54a4477eacb6db7927691becafba5f8801' '0x16b7f83f409c7195b1f4fde5652f1b54a4477eacb6db7927691becafba5f8801'
......
import { expect } from '../setup'
import { ethers, BigNumber, Signer } from 'ethers' import { ethers, BigNumber, Signer } from 'ethers'
import { submitTransactionWithYNATM } from '../../src/utils/tx-submission'
import { ResubmissionConfig } from '../../src'
import { import {
TransactionReceipt, TransactionReceipt,
TransactionResponse, TransactionResponse,
} from '@ethersproject/abstract-provider' } from '@ethersproject/abstract-provider'
import { expect } from '../setup'
import { submitTransactionWithYNATM } from '../../src/utils/tx-submission'
import { ResubmissionConfig } from '../../src'
const nullFunction = () => undefined const nullFunction = () => undefined
const nullHooks = { const nullHooks = {
beforeSendTransaction: nullFunction, beforeSendTransaction: nullFunction,
......
import { Server } from 'net'
import prometheus, { import prometheus, {
collectDefaultMetrics, collectDefaultMetrics,
DefaultMetricsCollectorConfiguration, DefaultMetricsCollectorConfiguration,
Registry, Registry,
} from 'prom-client' } from 'prom-client'
import express from 'express' import express from 'express'
import { Server } from 'net'
import { Logger } from './logger' import { Logger } from './logger'
......
# Changelog # Changelog
## 0.5.9
### Patch Changes
- Updated dependencies [ba96a455]
- Updated dependencies [c3e85fef]
- @eth-optimism/core-utils@0.7.4
## 0.5.8 ## 0.5.8
### Patch Changes ### Patch Changes
......
/* External Imports */ /* External Imports */
import * as fs from 'fs' import * as fs from 'fs'
import * as path from 'path' import * as path from 'path'
import * as mkdirp from 'mkdirp' import * as mkdirp from 'mkdirp'
const ensure = (value, key) => { const ensure = (value, key) => {
......
/* eslint @typescript-eslint/no-var-requires: "off" */ /* eslint @typescript-eslint/no-var-requires: "off" */
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import { awaitCondition } from '@eth-optimism/core-utils'
import { import {
getContractFromArtifact, getContractFromArtifact,
fundAccount, fundAccount,
...@@ -8,7 +10,6 @@ import { ...@@ -8,7 +10,6 @@ import {
BIG_BALANCE, BIG_BALANCE,
} from '../src/deploy-utils' } from '../src/deploy-utils'
import { names } from '../src/address-names' import { names } from '../src/address-names'
import { awaitCondition } from '@eth-optimism/core-utils'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
if ((hre as any).deployConfig.forked !== 'true') { if ((hre as any).deployConfig.forked !== 'true') {
......
/* Imports: Internal */ /* Imports: Internal */
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { names } from '../src/address-names' import { names } from '../src/address-names'
/* Imports: External */ /* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const { deploy } = hre.deployments const { deploy } = hre.deployments
......
{ {
"name": "@eth-optimism/contracts", "name": "@eth-optimism/contracts",
"version": "0.5.8", "version": "0.5.9",
"description": "[Optimism] L1 and L2 smart contracts for Optimism", "description": "[Optimism] L1 and L2 smart contracts for Optimism",
"main": "dist/index", "main": "dist/index",
"types": "dist/index", "types": "dist/index",
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
"url": "https://github.com/ethereum-optimism/optimism.git" "url": "https://github.com/ethereum-optimism/optimism.git"
}, },
"dependencies": { "dependencies": {
"@eth-optimism/core-utils": "0.7.3", "@eth-optimism/core-utils": "0.7.4",
"@ethersproject/abstract-provider": "^5.4.1", "@ethersproject/abstract-provider": "^5.4.1",
"@ethersproject/abstract-signer": "^5.4.1", "@ethersproject/abstract-signer": "^5.4.1",
"@ethersproject/hardware-wallets": "^5.4.0" "@ethersproject/hardware-wallets": "^5.4.0"
......
import path from 'path' import path from 'path'
import glob from 'glob'
import fs from 'fs' import fs from 'fs'
import glob from 'glob'
/** /**
* Script for automatically generating a file which has a series of `require` statements for * Script for automatically generating a file which has a series of `require` statements for
* importing JSON contract artifacts. We do this to preserve browser compatibility. * importing JSON contract artifacts. We do this to preserve browser compatibility.
......
import path from 'path' import path from 'path'
import glob from 'glob'
import fs from 'fs' import fs from 'fs'
import glob from 'glob'
/** /**
* Script for automatically generating a TypeScript file for retrieving deploy artifact JSON files. * Script for automatically generating a TypeScript file for retrieving deploy artifact JSON files.
* We do this to make sure that this package remains browser compatible. * We do this to make sure that this package remains browser compatible.
......
import dirtree from 'directory-tree'
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
import dirtree from 'directory-tree'
import { predeploys } from '../src' import { predeploys } from '../src'
interface DeploymentInfo { interface DeploymentInfo {
......
/* External Imports */ /* External Imports */
import { promisify } from 'util'
import { exec } from 'child_process' import { exec } from 'child_process'
import { promisify } from 'util'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { import {
computeStorageSlots, computeStorageSlots,
......
import { createInterface } from 'readline' import { createInterface } from 'readline'
import { hexStringEquals } from '@eth-optimism/core-utils' import { hexStringEquals } from '@eth-optimism/core-utils'
export const getInput = (query) => { export const getInput = (query) => {
......
...@@ -4,9 +4,9 @@ import { ethers } from 'ethers' ...@@ -4,9 +4,9 @@ import { ethers } from 'ethers'
import { task } from 'hardhat/config' import { task } from 'hardhat/config'
import * as types from 'hardhat/internal/core/params/argumentTypes' import * as types from 'hardhat/internal/core/params/argumentTypes'
import { hexStringEquals } from '@eth-optimism/core-utils' import { hexStringEquals } from '@eth-optimism/core-utils'
import { getContractFactory, getContractDefinition } from '../src/contract-defs' import { getContractFactory, getContractDefinition } from '../src/contract-defs'
import { names } from '../src/address-names' import { names } from '../src/address-names'
import { import {
getInput, getInput,
color as c, color as c,
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { task } from 'hardhat/config' import { task } from 'hardhat/config'
import * as types from 'hardhat/internal/core/params/argumentTypes' import * as types from 'hardhat/internal/core/params/argumentTypes'
import { getContractFactory, getContractDefinition } from '../src/contract-defs'
import { getContractFactory, getContractDefinition } from '../src/contract-defs'
import { import {
getInput, getInput,
color as c, color as c,
......
'use strict' 'use strict'
import fs from 'fs' import fs from 'fs'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { task } from 'hardhat/config' import { task } from 'hardhat/config'
import * as types from 'hardhat/internal/core/params/argumentTypes' import * as types from 'hardhat/internal/core/params/argumentTypes'
import { LedgerSigner } from '@ethersproject/hardware-wallets' import { LedgerSigner } from '@ethersproject/hardware-wallets'
import { getContractFactory } from '../src/contract-defs' import { getContractFactory } from '../src/contract-defs'
import { predeploys } from '../src/predeploys' import { predeploys } from '../src/predeploys'
......
...@@ -4,6 +4,7 @@ import { ethers } from 'ethers' ...@@ -4,6 +4,7 @@ import { ethers } from 'ethers'
import { task } from 'hardhat/config' import { task } from 'hardhat/config'
import * as types from 'hardhat/internal/core/params/argumentTypes' import * as types from 'hardhat/internal/core/params/argumentTypes'
import { LedgerSigner } from '@ethersproject/hardware-wallets' import { LedgerSigner } from '@ethersproject/hardware-wallets'
import { getContractFactory } from '../src/contract-defs' import { getContractFactory } from '../src/contract-defs'
import { predeploys } from '../src/predeploys' import { predeploys } from '../src/predeploys'
......
import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract, BigNumber } from 'ethers' import { Signer, ContractFactory, Contract, BigNumber } from 'ethers'
...@@ -11,6 +9,7 @@ import { ...@@ -11,6 +9,7 @@ import {
} from '@eth-optimism/core-utils' } from '@eth-optimism/core-utils'
/* Internal Imports */ /* Internal Imports */
import { expect } from '../../../setup'
import { import {
makeAddressManager, makeAddressManager,
setProxyTarget, setProxyTarget,
......
import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract, constants } from 'ethers' import { Signer, ContractFactory, Contract, constants } from 'ethers'
...@@ -7,6 +5,7 @@ import { Interface } from 'ethers/lib/utils' ...@@ -7,6 +5,7 @@ import { Interface } from 'ethers/lib/utils'
import { smockit, MockContract, smoddit } from '@eth-optimism/smock' import { smockit, MockContract, smoddit } from '@eth-optimism/smock'
/* Internal Imports */ /* Internal Imports */
import { expect } from '../../../setup'
import { NON_NULL_BYTES32, NON_ZERO_ADDRESS } from '../../../helpers' import { NON_NULL_BYTES32, NON_ZERO_ADDRESS } from '../../../helpers'
import { getContractInterface, predeploys } from '../../../../src' import { getContractInterface, predeploys } from '../../../../src'
......
import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers' import { Signer, ContractFactory, Contract } from 'ethers'
...@@ -13,6 +11,7 @@ import { keccak256 } from 'ethers/lib/utils' ...@@ -13,6 +11,7 @@ import { keccak256 } from 'ethers/lib/utils'
import _ from 'lodash' import _ from 'lodash'
/* Internal Imports */ /* Internal Imports */
import { expect } from '../../../setup'
import { import {
makeAddressManager, makeAddressManager,
setProxyTarget, setProxyTarget,
......
import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract, constants } from 'ethers' import { Signer, ContractFactory, Contract, constants } from 'ethers'
import { smockit, MockContract } from '@eth-optimism/smock' import { smockit, MockContract } from '@eth-optimism/smock'
/* Internal Imports */ /* Internal Imports */
import { expect } from '../../../setup'
import { import {
makeAddressManager, makeAddressManager,
setProxyTarget, setProxyTarget,
......
import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Signer, Contract } from 'ethers' import { Signer, Contract } from 'ethers'
/* Internal Imports */ /* Internal Imports */
import { expect } from '../../../setup'
import { makeAddressManager } from '../../../helpers' import { makeAddressManager } from '../../../helpers'
describe('BondManager', () => { describe('BondManager', () => {
......
import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import hre, { ethers } from 'hardhat' import hre, { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers' import { Signer, ContractFactory, Contract } from 'ethers'
...@@ -7,6 +5,7 @@ import { smockit, MockContract } from '@eth-optimism/smock' ...@@ -7,6 +5,7 @@ import { smockit, MockContract } from '@eth-optimism/smock'
import { applyL1ToL2Alias } from '@eth-optimism/core-utils' import { applyL1ToL2Alias } from '@eth-optimism/core-utils'
/* Internal Imports */ /* Internal Imports */
import { expect } from '../../../setup'
import { import {
NON_NULL_BYTES32, NON_NULL_BYTES32,
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
......
import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers' import { Signer, ContractFactory, Contract } from 'ethers'
...@@ -11,8 +9,8 @@ import { ...@@ -11,8 +9,8 @@ import {
} from '@eth-optimism/smock' } from '@eth-optimism/smock'
/* Internal Imports */ /* Internal Imports */
import { expect } from '../../../setup'
import { NON_NULL_BYTES32, NON_ZERO_ADDRESS } from '../../../helpers' import { NON_NULL_BYTES32, NON_ZERO_ADDRESS } from '../../../helpers'
import { getContractInterface } from '../../../../src' import { getContractInterface } from '../../../../src'
const ERR_INVALID_MESSENGER = 'OVM_XCHAIN: messenger contract unauthenticated' const ERR_INVALID_MESSENGER = 'OVM_XCHAIN: messenger contract unauthenticated'
......
import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers' import { Signer, ContractFactory, Contract } from 'ethers'
import { smoddit } from '@eth-optimism/smock' import { smoddit } from '@eth-optimism/smock'
/* Internal Imports */ /* Internal Imports */
import { expect } from '../../../setup'
import { predeploys, getContractInterface } from '../../../../src' import { predeploys, getContractInterface } from '../../../../src'
describe('L2StandardTokenFactory', () => { describe('L2StandardTokenFactory', () => {
......
import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { ContractFactory, Contract, Signer } from 'ethers' import { ContractFactory, Contract, Signer } from 'ethers'
import { expect } from '../../../setup'
describe('OVM_ETH', () => { describe('OVM_ETH', () => {
let signer1: Signer let signer1: Signer
let signer2: Signer let signer2: Signer
......
import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { ContractFactory, Contract, Signer } from 'ethers' import { ContractFactory, Contract, Signer } from 'ethers'
import { calculateL1GasUsed, calculateL1Fee } from '@eth-optimism/core-utils' import { calculateL1GasUsed, calculateL1Fee } from '@eth-optimism/core-utils'
import { expect } from '../../../setup'
describe('OVM_GasPriceOracle', () => { describe('OVM_GasPriceOracle', () => {
const initialGasPrice = 0 const initialGasPrice = 0
let signer1: Signer let signer1: Signer
......
import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { ContractFactory, Contract } from 'ethers' import { ContractFactory, Contract } from 'ethers'
...@@ -8,6 +6,7 @@ import { remove0x } from '@eth-optimism/core-utils' ...@@ -8,6 +6,7 @@ import { remove0x } from '@eth-optimism/core-utils'
import { keccak256 } from 'ethers/lib/utils' import { keccak256 } from 'ethers/lib/utils'
/* Internal Imports */ /* Internal Imports */
import { expect } from '../../../setup'
import { NON_ZERO_ADDRESS } from '../../../helpers/constants' import { NON_ZERO_ADDRESS } from '../../../helpers/constants'
const ELEMENT_TEST_SIZES = [1, 2, 4, 8, 16] const ELEMENT_TEST_SIZES = [1, 2, 4, 8, 16]
......
import { expect } from '../../../setup'
/* Imports: External */ /* Imports: External */
import hre from 'hardhat' import hre from 'hardhat'
import { MockContract, smockit } from '@eth-optimism/smock' import { MockContract, smockit } from '@eth-optimism/smock'
import { Contract, Signer } from 'ethers' import { Contract, Signer } from 'ethers'
/* Imports: Internal */ /* Imports: Internal */
import { expect } from '../../../setup'
import { predeploys } from '../../../../src' import { predeploys } from '../../../../src'
describe('OVM_SequencerFeeVault', () => { describe('OVM_SequencerFeeVault', () => {
......
import { expect } from '../../setup'
/* Imports: External */ /* Imports: External */
import hre from 'hardhat' import hre from 'hardhat'
import { Contract, Signer } from 'ethers' import { Contract, Signer } from 'ethers'
import { smockit } from '@eth-optimism/smock' import { smockit } from '@eth-optimism/smock'
/* Imports: Internal */ /* Imports: Internal */
import { expect } from '../../setup'
import { getContractInterface } from '../../../src' import { getContractInterface } from '../../../src'
describe('L1ChugSplashProxy', () => { describe('L1ChugSplashProxy', () => {
......
import '../../../setup'
/* Internal Imports */ /* Internal Imports */
import '../../../setup'
import { Lib_OVMCodec_TEST_JSON } from '../../../data' import { Lib_OVMCodec_TEST_JSON } from '../../../data'
import { runJsonTest } from '../../../helpers' import { runJsonTest } from '../../../helpers'
......
import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Contract } from 'ethers' import { Contract } from 'ethers'
/* Internal Imports */ /* Internal Imports */
import { expect } from '../../../setup'
import { Lib_RLPWriter_TEST_JSON } from '../../../data' import { Lib_RLPWriter_TEST_JSON } from '../../../data'
const encode = async (Lib_RLPWriter: Contract, input: any): Promise<void> => { const encode = async (Lib_RLPWriter: Contract, input: any): Promise<void> => {
......
import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Contract } from 'ethers' import { Contract } from 'ethers'
import { applyL1ToL2Alias, undoL1ToL2Alias } from '@eth-optimism/core-utils' import { applyL1ToL2Alias, undoL1ToL2Alias } from '@eth-optimism/core-utils'
import { expect } from '../../../setup'
describe('AddressAliasHelper', () => { describe('AddressAliasHelper', () => {
let AddressAliasHelper: Contract let AddressAliasHelper: Contract
before(async () => { before(async () => {
......
import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import * as rlp from 'rlp' import * as rlp from 'rlp'
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
...@@ -8,6 +6,7 @@ import { fromHexString, toHexString } from '@eth-optimism/core-utils' ...@@ -8,6 +6,7 @@ import { fromHexString, toHexString } from '@eth-optimism/core-utils'
import { Trie } from 'merkle-patricia-tree/dist/baseTrie' import { Trie } from 'merkle-patricia-tree/dist/baseTrie'
/* Internal Imports */ /* Internal Imports */
import { expect } from '../../../setup'
import { TrieTestGenerator } from '../../../helpers' import { TrieTestGenerator } from '../../../helpers'
import * as officialTestJson from '../../../data/json/libraries/trie/trietest.json' import * as officialTestJson from '../../../data/json/libraries/trie/trietest.json'
import * as officialTestAnyOrderJson from '../../../data/json/libraries/trie/trieanyorder.json' import * as officialTestAnyOrderJson from '../../../data/json/libraries/trie/trieanyorder.json'
......
import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Contract } from 'ethers' import { Contract } from 'ethers'
/* Internal Imports */ /* Internal Imports */
import { expect } from '../../../setup'
import { TrieTestGenerator } from '../../../helpers' import { TrieTestGenerator } from '../../../helpers'
const NODE_COUNTS = [1, 2, 128] const NODE_COUNTS = [1, 2, 128]
......
import { expect } from '../../../setup'
import hre from 'hardhat' import hre from 'hardhat'
import { Contract, ethers } from 'ethers' import { Contract, ethers } from 'ethers'
import { expect } from '../../../setup'
describe('Lib_Buffer', () => { describe('Lib_Buffer', () => {
let Lib_Buffer: Contract let Lib_Buffer: Contract
beforeEach(async () => { beforeEach(async () => {
......
/* Internal Imports */ /* Internal Imports */
import { Lib_BytesUtils_TEST_JSON } from '../../../data'
import { runJsonTest } from '../../../helpers'
/* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Contract } from 'ethers' import { Contract } from 'ethers'
/* External Imports */
import { Lib_BytesUtils_TEST_JSON } from '../../../data'
import { runJsonTest } from '../../../helpers'
import { expect } from '../../../setup' import { expect } from '../../../setup'
describe('Lib_BytesUtils', () => { describe('Lib_BytesUtils', () => {
......
import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Contract, BigNumber } from 'ethers' import { Contract, BigNumber } from 'ethers'
...@@ -7,6 +5,7 @@ import { MerkleTree } from 'merkletreejs' ...@@ -7,6 +5,7 @@ import { MerkleTree } from 'merkletreejs'
import { fromHexString, toHexString } from '@eth-optimism/core-utils' import { fromHexString, toHexString } from '@eth-optimism/core-utils'
/* Internal Imports */ /* Internal Imports */
import { expect } from '../../../setup'
import { NON_NULL_BYTES32 } from '../../../helpers' import { NON_NULL_BYTES32 } from '../../../helpers'
const NODE_COUNTS = [ const NODE_COUNTS = [
......
import { NON_ZERO_ADDRESS } from '../constants'
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { NON_ZERO_ADDRESS } from '../constants'
export const DUMMY_BATCH_HEADERS = [ export const DUMMY_BATCH_HEADERS = [
{ {
batchIndex: 0, batchIndex: 0,
......
import { expect } from '../../setup'
/* External Imports */ /* External Imports */
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { Contract, BigNumber } from 'ethers' import { Contract, BigNumber } from 'ethers'
import { expect } from '../../setup'
const bigNumberify = (arr: any[]) => { const bigNumberify = (arr: any[]) => {
return arr.map((el: any) => { return arr.map((el: any) => {
if (typeof el === 'number') { if (typeof el === 'number') {
......
# @eth-optimism/core-utils # @eth-optimism/core-utils
## 0.7.4
### Patch Changes
- ba96a455: Improved docstrings for BCFG typings
- c3e85fef: Cleans up the internal file and folder structure for the typings exported by core-utils
## 0.7.3 ## 0.7.3
### Patch Changes ### Patch Changes
......
{ {
"name": "@eth-optimism/core-utils", "name": "@eth-optimism/core-utils",
"version": "0.7.3", "version": "0.7.4",
"description": "[Optimism] Core typescript utilities", "description": "[Optimism] Core typescript utilities",
"main": "dist/index", "main": "dist/index",
"types": "dist/index", "types": "dist/index",
......
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { bnToAddress } from './bn' import { bnToAddress } from './bn'
// Constant representing the alias to apply to the msg.sender when a contract sends an L1 => L2 // Constant representing the alias to apply to the msg.sender when a contract sends an L1 => L2
......
export interface Bcfg {
load: (options: { env?: boolean; argv?: boolean }) => void
str: (name: string, defaultValue?: string) => string
uint: (name: string, defaultValue?: number) => number
bool: (name: string, defaultValue?: boolean) => boolean
ufloat: (name: string, defaultValue?: number) => number
has: (name: string) => boolean
}
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { remove0x, add0x } from './common/hex-strings' import { remove0x, add0x } from './common/hex-strings'
/** /**
......
export * from './types'
export * from './sequencer-batch' export * from './sequencer-batch'
import { add0x, remove0x, encodeHex } from '../common'
import { BigNumber, ethers } from 'ethers' import { BigNumber, ethers } from 'ethers'
import { add0x, remove0x, encodeHex } from '../common'
export interface BatchContext { export interface BatchContext {
numSequencedTransactions: number numSequencedTransactions: number
numSubsequentQueueTransactions: number numSubsequentQueueTransactions: number
......
import { expect } from 'chai' import { expect } from 'chai'
import { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
import { sleep } from './misc' import { sleep } from './misc'
interface deviationRanges { interface deviationRanges {
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
*/ */
import { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
import { remove0x } from './common' import { remove0x } from './common'
const txDataZeroGas = 4 const txDataZeroGas = 4
......
...@@ -2,8 +2,7 @@ export * from './coders' ...@@ -2,8 +2,7 @@ export * from './coders'
export * from './common' export * from './common'
export * from './watcher' export * from './watcher'
export * from './l2context' export * from './l2context'
export * from './batches' export * from './types'
export * from './bcfg'
export * from './fees' export * from './fees'
export * from './provider' export * from './provider'
export * from './alias' export * from './alias'
......
// Use this file for simple types that aren't necessarily associated with a specific project or
// package. Often used for alias types like Address = string.
export interface Signature { export interface Signature {
r: string r: string
s: string s: string
......
/**
* TypeScript typings for bcoin's BCFG config parser (https://github.com/bcoin-org/bcfg)
* This is NOT a complete set of typings, just what we use at Optimism at the moment.
* We could consider expanding this into a full set of typings in the future.
*/
export interface Bcfg {
/**
* Loads configuration values from the environment. Must be called before environment variables
* can be accessed with other methods like str(...) or uint(...).
*
* @param options Options to use when loading arguments.
* @param options.env Boolean, whether or not to load from process.env.
* @param options.argv Boolean, whether or not to load from process.argv.
*/
load: (options: { env?: boolean; argv?: boolean }) => void
/**
* Returns the variable with the given name and casts it as a string. Queries from the
* environment or from argv depending on which were loaded when load() was called.
*
* @param name Name of the variable to query.
* @param defaultValue Optional default value if the variable does not exist.
* @returns Variable cast to a string.
*/
str: (name: string, defaultValue?: string) => string
/**
* Returns the variable with the given name and casts it as a uint. Will throw an error if the
* variable cannot be cast into a uint. Queries from the environment or from argv depending on
* which were loaded when load() was called.
*
* @param name Name of the variable to query.
* @param defaultValue Optional default value if the variable does not exist.
* @returns Variable cast to a uint.
*/
uint: (name: string, defaultValue?: number) => number
/**
* Returns the variable with the given name and casts it as a bool. Will throw an error if the
* variable cannot be cast into a bool. Queries from the environment or from argv depending on
* which were loaded when load() was called.
*
* @param name Name of the variable to query.
* @param defaultValue Optional default value if the variable does not exist.
* @returns Variable cast to a bool.
*/
bool: (name: string, defaultValue?: boolean) => boolean
/**
* Returns the variable with the given name and casts it as a ufloat. Will throw an error if the
* variable cannot be cast into a ufloat. Queries from the environment or from argv depending on
* which were loaded when load() was called.
*
* @param name Name of the variable to query.
* @param defaultValue Optional default value if the variable does not exist.
* @returns Variable cast to a ufloat.
*/
ufloat: (name: string, defaultValue?: number) => number
/**
* Checks if the given variable exists.
*
* @param name Name of the variable to query.
* @returns True if the variable exists, false otherwise.
*/
has: (name: string) => boolean
}
// Optimism PBC 2021 // Types explicitly related to dealing with Geth.
// Represents the ethereum state /**
* Represents the Ethereum state, in the format that Geth expects it.
*/
export interface State { export interface State {
[address: string]: { [address: string]: {
nonce: number nonce: number
...@@ -14,7 +16,9 @@ export interface State { ...@@ -14,7 +16,9 @@ export interface State {
} }
} }
// Represents a genesis file that geth can consume /**
* Represents Geth's genesis file format.
*/
export interface Genesis { export interface Genesis {
config: { config: {
chainId: number chainId: number
......
export * from './geth'
export * from './bcfg'
export * from './rollup'
export * from './basic'
...@@ -4,6 +4,9 @@ import { ...@@ -4,6 +4,9 @@ import {
TransactionResponse, TransactionResponse,
} from '@ethersproject/abstract-provider' } from '@ethersproject/abstract-provider'
/**
* Structure of the response returned by L2Geth nodes when querying the `rollup_getInfo` endpoint.
*/
export interface RollupInfo { export interface RollupInfo {
mode: 'sequencer' | 'verifier' mode: 'sequencer' | 'verifier'
syncing: boolean syncing: boolean
...@@ -17,14 +20,18 @@ export interface RollupInfo { ...@@ -17,14 +20,18 @@ export interface RollupInfo {
} }
} }
/**
* Enum used for the two transaction types (queue and direct to Sequencer).
*/
export enum QueueOrigin { export enum QueueOrigin {
Sequencer = 'sequencer', Sequencer = 'sequencer',
L1ToL2 = 'l1', L1ToL2 = 'l1',
} }
/** /**
* Transaction & Blocks. These are the true data-types we expect * JSON transaction representation when returned by L2Geth nodes. This is simply an extension to
* from running a batch submitter. * the standard transaction response type. You do NOT need to use this type unless you care about
* having typed access to L2-specific fields.
*/ */
export interface L2Transaction extends TransactionResponse { export interface L2Transaction extends TransactionResponse {
l1BlockNumber: number l1BlockNumber: number
...@@ -33,21 +40,32 @@ export interface L2Transaction extends TransactionResponse { ...@@ -33,21 +40,32 @@ export interface L2Transaction extends TransactionResponse {
rawTransaction: string rawTransaction: string
} }
/**
* JSON block representation when returned by L2Geth nodes. Just a normal block but with
* L2Transaction objects instead of the standard transaction response object.
*/
export interface L2Block extends BlockWithTransactions { export interface L2Block extends BlockWithTransactions {
stateRoot: string stateRoot: string
transactions: [L2Transaction] transactions: [L2Transaction]
} }
/** /**
* BatchElement & Batch. These are the data-types of the compressed / batched * Generic batch element, either a state root batch element or a transaction batch element.
* block data we submit to L1.
*/ */
export interface BatchElement { export interface BatchElement {
// Only exists on state root batch elements.
stateRoot: string stateRoot: string
// Only exists on transaction batch elements.
isSequencerTx: boolean isSequencerTx: boolean
rawTransaction: undefined | string rawTransaction: undefined | string
// Batch element context, exists on all batch elements.
timestamp: number timestamp: number
blockNumber: number blockNumber: number
} }
/**
* List of batch elements.
*/
export type Batch = BatchElement[] export type Batch = BatchElement[]
import './setup' import './setup'
/* Internal Imports */ /* Internal Imports */
import { expect } from 'chai'
import { import {
encodeAppendSequencerBatch, encodeAppendSequencerBatch,
decodeAppendSequencerBatch, decodeAppendSequencerBatch,
sequencerBatch, sequencerBatch,
} from '../src' } from '../src'
import { expect } from 'chai'
describe('BatchEncoder', () => { describe('BatchEncoder', () => {
describe('appendSequencerBatch', () => { describe('appendSequencerBatch', () => {
......
import { expect } from './setup'
import { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
/* Imports: Internal */ /* Imports: Internal */
import { expect } from './setup'
import { import {
toRpcHexString, toRpcHexString,
remove0x, remove0x,
......
import { expect } from './setup'
/* Imports: Internal */ /* Imports: Internal */
import { expect } from './setup'
import { sleep } from '../src' import { sleep } from '../src'
describe('sleep', async () => { describe('sleep', async () => {
......
import { expect } from './setup' import { assert } from 'chai'
/* Imports: Internal */ /* Imports: Internal */
import { expect } from './setup'
import { expectApprox, awaitCondition } from '../src' import { expectApprox, awaitCondition } from '../src'
import { assert } from 'chai'
describe('awaitCondition', () => { describe('awaitCondition', () => {
it('should try the condition fn until it returns true', async () => { it('should try the condition fn until it returns true', async () => {
......
# data transport layer # data transport layer
## 0.5.12
### Patch Changes
- Updated dependencies [ba96a455]
- Updated dependencies [c3e85fef]
- @eth-optimism/core-utils@0.7.4
- @eth-optimism/contracts@0.5.9
## 0.5.11 ## 0.5.11
### Patch Changes ### Patch Changes
......
{ {
"private": true, "private": true,
"name": "@eth-optimism/data-transport-layer", "name": "@eth-optimism/data-transport-layer",
"version": "0.5.11", "version": "0.5.12",
"description": "[Optimism] Service for shuttling data from L1 into L2", "description": "[Optimism] Service for shuttling data from L1 into L2",
"main": "dist/index", "main": "dist/index",
"types": "dist/index", "types": "dist/index",
...@@ -37,8 +37,8 @@ ...@@ -37,8 +37,8 @@
}, },
"dependencies": { "dependencies": {
"@eth-optimism/common-ts": "0.2.1", "@eth-optimism/common-ts": "0.2.1",
"@eth-optimism/contracts": "0.5.8", "@eth-optimism/contracts": "0.5.9",
"@eth-optimism/core-utils": "0.7.3", "@eth-optimism/core-utils": "0.7.4",
"@ethersproject/providers": "^5.4.5", "@ethersproject/providers": "^5.4.5",
"@ethersproject/transactions": "^5.4.0", "@ethersproject/transactions": "^5.4.0",
"@sentry/node": "^6.3.1", "@sentry/node": "^6.3.1",
......
...@@ -3,6 +3,7 @@ import { LevelUp } from 'levelup' ...@@ -3,6 +3,7 @@ import { LevelUp } from 'levelup'
import { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
/* Imports: Internal */ /* Imports: Internal */
import { SimpleDB } from './simple-db'
import { import {
EnqueueEntry, EnqueueEntry,
StateRootBatchEntry, StateRootBatchEntry,
...@@ -10,7 +11,6 @@ import { ...@@ -10,7 +11,6 @@ import {
TransactionBatchEntry, TransactionBatchEntry,
TransactionEntry, TransactionEntry,
} from '../types/database-types' } from '../types/database-types'
import { SimpleDB } from './simple-db'
const TRANSPORT_DB_KEYS = { const TRANSPORT_DB_KEYS = {
ENQUEUE: `enqueue`, ENQUEUE: `enqueue`,
......
...@@ -9,6 +9,7 @@ import { ...@@ -9,6 +9,7 @@ import {
import { SequencerBatchAppendedEvent } from '@eth-optimism/contracts/dist/types/CanonicalTransactionChain' import { SequencerBatchAppendedEvent } from '@eth-optimism/contracts/dist/types/CanonicalTransactionChain'
/* Imports: Internal */ /* Imports: Internal */
import { MissingElementError } from './errors'
import { import {
DecodedSequencerBatchTransaction, DecodedSequencerBatchTransaction,
SequencerBatchAppendedExtraData, SequencerBatchAppendedExtraData,
...@@ -18,7 +19,6 @@ import { ...@@ -18,7 +19,6 @@ import {
EventHandlerSet, EventHandlerSet,
} from '../../../types' } from '../../../types'
import { SEQUENCER_GAS_LIMIT, parseSignatureVParam } from '../../../utils' import { SEQUENCER_GAS_LIMIT, parseSignatureVParam } from '../../../utils'
import { MissingElementError } from './errors'
export const handleEventsSequencerBatchAppended: EventHandlerSet< export const handleEventsSequencerBatchAppended: EventHandlerSet<
SequencerBatchAppendedEvent, SequencerBatchAppendedEvent,
......
...@@ -4,6 +4,7 @@ import { getContractFactory } from '@eth-optimism/contracts' ...@@ -4,6 +4,7 @@ import { getContractFactory } from '@eth-optimism/contracts'
import { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
/* Imports: Internal */ /* Imports: Internal */
import { MissingElementError } from './errors'
import { import {
StateRootBatchEntry, StateRootBatchEntry,
StateBatchAppendedExtraData, StateBatchAppendedExtraData,
...@@ -11,7 +12,6 @@ import { ...@@ -11,7 +12,6 @@ import {
StateRootEntry, StateRootEntry,
EventHandlerSet, EventHandlerSet,
} from '../../../types' } from '../../../types'
import { MissingElementError } from './errors'
export const handleEventsStateBatchAppended: EventHandlerSet< export const handleEventsStateBatchAppended: EventHandlerSet<
StateBatchAppendedEvent, StateBatchAppendedEvent,
......
...@@ -3,8 +3,8 @@ import { BigNumber } from 'ethers' ...@@ -3,8 +3,8 @@ import { BigNumber } from 'ethers'
import { TransactionEnqueuedEvent } from '@eth-optimism/contracts/dist/types/CanonicalTransactionChain' import { TransactionEnqueuedEvent } from '@eth-optimism/contracts/dist/types/CanonicalTransactionChain'
/* Imports: Internal */ /* Imports: Internal */
import { EnqueueEntry, EventHandlerSet } from '../../../types'
import { MissingElementError } from './errors' import { MissingElementError } from './errors'
import { EnqueueEntry, EventHandlerSet } from '../../../types'
export const handleEventsTransactionEnqueued: EventHandlerSet< export const handleEventsTransactionEnqueued: EventHandlerSet<
TransactionEnqueuedEvent, TransactionEnqueuedEvent,
......
...@@ -8,6 +8,10 @@ import { constants } from 'ethers' ...@@ -8,6 +8,10 @@ import { constants } from 'ethers'
import { Gauge, Counter } from 'prom-client' import { Gauge, Counter } from 'prom-client'
/* Imports: Internal */ /* Imports: Internal */
import { handleEventsTransactionEnqueued } from './handlers/transaction-enqueued'
import { handleEventsSequencerBatchAppended } from './handlers/sequencer-batch-appended'
import { handleEventsStateBatchAppended } from './handlers/state-batch-appended'
import { MissingElementError } from './handlers/errors'
import { TransportDB } from '../../db/transport-db' import { TransportDB } from '../../db/transport-db'
import { import {
OptimismContracts, OptimismContracts,
...@@ -17,11 +21,7 @@ import { ...@@ -17,11 +21,7 @@ import {
validators, validators,
} from '../../utils' } from '../../utils'
import { EventHandlerSet } from '../../types' import { EventHandlerSet } from '../../types'
import { handleEventsTransactionEnqueued } from './handlers/transaction-enqueued'
import { handleEventsSequencerBatchAppended } from './handlers/sequencer-batch-appended'
import { handleEventsStateBatchAppended } from './handlers/state-batch-appended'
import { L1DataTransportServiceOptions } from '../main/service' import { L1DataTransportServiceOptions } from '../main/service'
import { MissingElementError } from './handlers/errors'
interface L1IngestionMetrics { interface L1IngestionMetrics {
highestSyncedL1Block: Gauge<string> highestSyncedL1Block: Gauge<string>
......
...@@ -8,10 +8,10 @@ import bfj from 'bfj' ...@@ -8,10 +8,10 @@ import bfj from 'bfj'
import { Gauge } from 'prom-client' import { Gauge } from 'prom-client'
/* Imports: Internal */ /* Imports: Internal */
import { handleSequencerBlock } from './handlers/transaction'
import { TransportDB } from '../../db/transport-db' import { TransportDB } from '../../db/transport-db'
import { sleep, toRpcHexString, validators } from '../../utils' import { sleep, toRpcHexString, validators } from '../../utils'
import { L1DataTransportServiceOptions } from '../main/service' import { L1DataTransportServiceOptions } from '../main/service'
import { handleSequencerBlock } from './handlers/transaction'
interface L2IngestionMetrics { interface L2IngestionMetrics {
highestSyncedL2Block: Gauge<string> highestSyncedL2Block: Gauge<string>
......
...@@ -2,13 +2,13 @@ ...@@ -2,13 +2,13 @@
import { BaseService, Metrics } from '@eth-optimism/common-ts' import { BaseService, Metrics } from '@eth-optimism/common-ts'
import { LevelUp } from 'levelup' import { LevelUp } from 'levelup'
import level from 'level' import level from 'level'
import { Counter } from 'prom-client'
/* Imports: Internal */ /* Imports: Internal */
import { L1IngestionService } from '../l1-ingestion/service' import { L1IngestionService } from '../l1-ingestion/service'
import { L1TransportServer } from '../server/service' import { L1TransportServer } from '../server/service'
import { validators } from '../../utils' import { validators } from '../../utils'
import { L2IngestionService } from '../l2-ingestion/service' import { L2IngestionService } from '../l2-ingestion/service'
import { Counter } from 'prom-client'
export interface L1DataTransportServiceOptions { export interface L1DataTransportServiceOptions {
nodeEnv: string nodeEnv: string
......
...@@ -2,13 +2,13 @@ import { BaseProvider } from '@ethersproject/providers' ...@@ -2,13 +2,13 @@ import { BaseProvider } from '@ethersproject/providers'
import { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
import { TypedEvent } from '@eth-optimism/contracts/dist/types/common' import { TypedEvent } from '@eth-optimism/contracts/dist/types/common'
import { TransportDB } from '../db/transport-db'
import { import {
TransactionBatchEntry, TransactionBatchEntry,
TransactionEntry, TransactionEntry,
StateRootBatchEntry, StateRootBatchEntry,
StateRootEntry, StateRootEntry,
} from './database-types' } from './database-types'
import { TransportDB } from '../db/transport-db'
export type GetExtraDataHandler<TEvent extends TypedEvent, TExtraData> = ( export type GetExtraDataHandler<TEvent extends TypedEvent, TExtraData> = (
event?: TEvent, event?: TEvent,
......
import { fromHexString } from '@eth-optimism/core-utils'
import * as url from 'url' import * as url from 'url'
import { fromHexString } from '@eth-optimism/core-utils'
export const validators = { export const validators = {
isBoolean: (val: any): boolean => { isBoolean: (val: any): boolean => {
return typeof val === 'boolean' return typeof val === 'boolean'
......
import { expect } from '../../../../setup'
/* Imports: External */ /* Imports: External */
import { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
import { Block } from '@ethersproject/abstract-provider' import { Block } from '@ethersproject/abstract-provider'
/* Imports: Internal */ /* Imports: Internal */
import { expect } from '../../../../setup'
import { handleEventsStateBatchAppended } from '../../../../../src/services/l1-ingestion/handlers/state-batch-appended' import { handleEventsStateBatchAppended } from '../../../../../src/services/l1-ingestion/handlers/state-batch-appended'
import { StateBatchAppendedExtraData } from '../../../../../src/types' import { StateBatchAppendedExtraData } from '../../../../../src/types'
import { l1StateBatchData } from '../../../examples/l1-data' import { l1StateBatchData } from '../../../examples/l1-data'
......
import { expect } from '../../../../setup'
/* Imports: External */ /* Imports: External */
import { ethers, BigNumber } from 'ethers' import { ethers, BigNumber } from 'ethers'
/* Imports: Internal */ /* Imports: Internal */
import { expect } from '../../../../setup'
import { handleEventsTransactionEnqueued } from '../../../../../src/services/l1-ingestion/handlers/transaction-enqueued' import { handleEventsTransactionEnqueued } from '../../../../../src/services/l1-ingestion/handlers/transaction-enqueued'
const MAX_ITERATIONS = 128 const MAX_ITERATIONS = 128
......
import { expect } from '../../../../setup'
/* Imports: Internal */ /* Imports: Internal */
import { expect } from '../../../../setup'
import { l2Block } from '../../../examples/l2-data' import { l2Block } from '../../../examples/l2-data'
import { handleSequencerBlock } from '../../../../../src/services/l2-ingestion/handlers/transaction' import { handleSequencerBlock } from '../../../../../src/services/l2-ingestion/handlers/transaction'
......
# @eth-optimism/message-relayer # @eth-optimism/message-relayer
## 0.2.13
### Patch Changes
- Updated dependencies [ba96a455]
- Updated dependencies [c3e85fef]
- @eth-optimism/core-utils@0.7.4
- @eth-optimism/contracts@0.5.9
## 0.2.12 ## 0.2.12
### Patch Changes ### Patch Changes
......
{ {
"name": "@eth-optimism/message-relayer", "name": "@eth-optimism/message-relayer",
"version": "0.2.12", "version": "0.2.13",
"description": "[Optimism] Service for automatically relaying L2 to L1 transactions", "description": "[Optimism] Service for automatically relaying L2 to L1 transactions",
"main": "dist/index", "main": "dist/index",
"types": "dist/index", "types": "dist/index",
...@@ -35,8 +35,8 @@ ...@@ -35,8 +35,8 @@
}, },
"dependencies": { "dependencies": {
"@eth-optimism/common-ts": "0.2.1", "@eth-optimism/common-ts": "0.2.1",
"@eth-optimism/contracts": "0.5.8", "@eth-optimism/contracts": "0.5.9",
"@eth-optimism/core-utils": "0.7.3", "@eth-optimism/core-utils": "0.7.4",
"@sentry/node": "^6.3.1", "@sentry/node": "^6.3.1",
"bcfg": "^0.1.6", "bcfg": "^0.1.6",
"dotenv": "^10.0.0", "dotenv": "^10.0.0",
......
import { Wallet, providers } from 'ethers' import { Wallet, providers } from 'ethers'
import { MessageRelayerService } from '../service'
import { Bcfg } from '@eth-optimism/core-utils' import { Bcfg } from '@eth-optimism/core-utils'
import { Logger, LoggerOptions } from '@eth-optimism/common-ts' import { Logger, LoggerOptions } from '@eth-optimism/common-ts'
import * as Sentry from '@sentry/node' import * as Sentry from '@sentry/node'
import * as dotenv from 'dotenv' import * as dotenv from 'dotenv'
import Config from 'bcfg' import Config from 'bcfg'
import { MessageRelayerService } from '../service'
dotenv.config() dotenv.config()
const main = async () => { const main = async () => {
......
...@@ -2,16 +2,15 @@ ...@@ -2,16 +2,15 @@
import { Contract, ethers, Wallet, BigNumber, providers } from 'ethers' import { Contract, ethers, Wallet, BigNumber, providers } from 'ethers'
import * as rlp from 'rlp' import * as rlp from 'rlp'
import { MerkleTree } from 'merkletreejs' import { MerkleTree } from 'merkletreejs'
/* Imports: Internal */
import { fromHexString, sleep } from '@eth-optimism/core-utils' import { fromHexString, sleep } from '@eth-optimism/core-utils'
import { Logger, BaseService, Metrics } from '@eth-optimism/common-ts' import { Logger, BaseService, Metrics } from '@eth-optimism/common-ts'
import { import {
loadContract, loadContract,
loadContractFromManager, loadContractFromManager,
predeploys, predeploys,
} from '@eth-optimism/contracts' } from '@eth-optimism/contracts'
/* Imports: Internal */
import { StateRootBatchHeader, SentMessage, SentMessageProof } from './types' import { StateRootBatchHeader, SentMessage, SentMessageProof } from './types'
interface MessageRelayerOptions { interface MessageRelayerOptions {
......
import { expect } from '../setup'
/* Imports: External */ /* Imports: External */
import hre from 'hardhat' import hre from 'hardhat'
import { Contract, Signer } from 'ethers' import { Contract, Signer } from 'ethers'
...@@ -8,6 +6,7 @@ import { smockit } from '@eth-optimism/smock' ...@@ -8,6 +6,7 @@ import { smockit } from '@eth-optimism/smock'
import { toPlainObject } from 'lodash' import { toPlainObject } from 'lodash'
/* Imports: Internal */ /* Imports: Internal */
import { expect } from '../setup'
import { import {
getMerkleTreeProof, getMerkleTreeProof,
getMessagesAndProofsForL2Transaction, getMessagesAndProofsForL2Transaction,
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
}, },
"devDependencies": { "devDependencies": {
"@discoveryjs/json-ext": "^0.5.3", "@discoveryjs/json-ext": "^0.5.3",
"@eth-optimism/core-utils": "0.7.3", "@eth-optimism/core-utils": "0.7.4",
"@ethersproject/abstract-provider": "^5.5.1", "@ethersproject/abstract-provider": "^5.5.1",
"@ethersproject/abi": "^5.5.0", "@ethersproject/abi": "^5.5.0",
"@ethersproject/bignumber": "^5.5.0", "@ethersproject/bignumber": "^5.5.0",
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
"ethers": "^5.4.5", "ethers": "^5.4.5",
"lint-staged": "11.0.0", "lint-staged": "11.0.0",
"mocha": "^9.1.2", "mocha": "^9.1.2",
"node-fetch": "2.6.5", "node-fetch": "2.6.7",
"solc": "0.8.7-fixed", "solc": "0.8.7-fixed",
"ts-mocha": "^8.0.0", "ts-mocha": "^8.0.0",
"ts-node": "^10.0.0" "ts-node": "^10.0.0"
......
...@@ -6,6 +6,7 @@ import { ...@@ -6,6 +6,7 @@ import {
POOL_INIT_CODE_HASH_OPTIMISM_KOVAN, POOL_INIT_CODE_HASH_OPTIMISM_KOVAN,
} from '@uniswap/v3-sdk' } from '@uniswap/v3-sdk'
import { Token } from '@uniswap/sdk-core' import { Token } from '@uniswap/sdk-core'
import { UNISWAP_V3_FACTORY_ADDRESS } from './constants' import { UNISWAP_V3_FACTORY_ADDRESS } from './constants'
import { downloadAllSolcVersions } from './solc' import { downloadAllSolcVersions } from './solc'
import { import {
......
...@@ -5,6 +5,7 @@ import { ...@@ -5,6 +5,7 @@ import {
POOL_INIT_CODE_HASH_OPTIMISM_KOVAN, POOL_INIT_CODE_HASH_OPTIMISM_KOVAN,
} from '@uniswap/v3-sdk' } from '@uniswap/v3-sdk'
import { sleep, add0x, remove0x, clone } from '@eth-optimism/core-utils' import { sleep, add0x, remove0x, clone } from '@eth-optimism/core-utils'
import { import {
OLD_ETH_ADDRESS, OLD_ETH_ADDRESS,
WETH_TRANSFER_ADDRESSES, WETH_TRANSFER_ADDRESSES,
......
/* eslint @typescript-eslint/no-var-requires: "off" */ /* eslint @typescript-eslint/no-var-requires: "off" */
import fetch from 'node-fetch'
import path from 'path'
import fs from 'fs' import fs from 'fs'
import path from 'path'
import fetch from 'node-fetch'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { clone } from '@eth-optimism/core-utils' import { clone } from '@eth-optimism/core-utils'
import setupMethods from 'solc/wrapper' import setupMethods from 'solc/wrapper'
import { import {
COMPILER_VERSIONS_TO_SOLC, COMPILER_VERSIONS_TO_SOLC,
EMSCRIPTEN_BUILD_LIST, EMSCRIPTEN_BUILD_LIST,
......
import { ethers } from 'ethers'
import fs from 'fs' import fs from 'fs'
import { ethers } from 'ethers'
import { add0x, remove0x, clone } from '@eth-optimism/core-utils' import { add0x, remove0x, clone } from '@eth-optimism/core-utils'
import { StateDump, SurgeryDataSources, AccountType } from './types' import { StateDump, SurgeryDataSources, AccountType } from './types'
import { findAccount } from './utils' import { findAccount } from './utils'
import { handlers } from './handlers' import { handlers } from './handlers'
......
/* eslint @typescript-eslint/no-var-requires: "off" */ /* eslint @typescript-eslint/no-var-requires: "off" */
import { createReadStream } from 'fs'
import * as fs from 'fs'
import * as assert from 'assert'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { abi as UNISWAP_FACTORY_ABI } from '@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json' import { abi as UNISWAP_FACTORY_ABI } from '@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json'
import { Interface } from '@ethersproject/abi' import { Interface } from '@ethersproject/abi'
import { parseChunked } from '@discoveryjs/json-ext' import { parseChunked } from '@discoveryjs/json-ext'
import { createReadStream } from 'fs'
import * as fs from 'fs'
import byline from 'byline' import byline from 'byline'
import * as dotenv from 'dotenv' import * as dotenv from 'dotenv'
import * as assert from 'assert'
import { reqenv, getenv, remove0x } from '@eth-optimism/core-utils' import { reqenv, getenv, remove0x } from '@eth-optimism/core-utils'
import { import {
Account, Account,
EtherscanContract, EtherscanContract,
......
import { KECCAK256_RLP_S, KECCAK256_NULL_S } from 'ethereumjs-util' import { KECCAK256_RLP_S, KECCAK256_NULL_S } from 'ethereumjs-util'
import { add0x } from '@eth-optimism/core-utils' import { add0x } from '@eth-optimism/core-utils'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { expect, env } from './setup' import { expect, env } from './setup'
import { AccountType } from '../scripts/types' import { AccountType } from '../scripts/types'
......
import { KECCAK256_RLP_S, KECCAK256_NULL_S } from 'ethereumjs-util' import { KECCAK256_RLP_S, KECCAK256_NULL_S } from 'ethereumjs-util'
import { add0x } from '@eth-optimism/core-utils' import { add0x } from '@eth-optimism/core-utils'
import { expect, env } from './setup' import { expect, env } from './setup'
import { AccountType, Account } from '../scripts/types' import { AccountType, Account } from '../scripts/types'
......
import { expect } from '@eth-optimism/core-utils/test/setup' import { expect } from '@eth-optimism/core-utils/test/setup'
import { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
import { env } from './setup' import { env } from './setup'
describe('erc20', () => { describe('erc20', () => {
......
import { ethers, BigNumber, Contract } from 'ethers' import { ethers, BigNumber, Contract } from 'ethers'
import { expect, env, ERC20_ABI } from './setup' import { expect, env, ERC20_ABI } from './setup'
import { AccountType } from '../scripts/types'
import { GenesisJsonProvider } from './provider' import { GenesisJsonProvider } from './provider'
import { AccountType } from '../scripts/types'
describe('predeploys', () => { describe('predeploys', () => {
const predeploys = { const predeploys = {
......
import { expect } from '@eth-optimism/core-utils/test/setup' import { expect } from '@eth-optimism/core-utils/test/setup'
import { ethers, BigNumber } from 'ethers' import { ethers, BigNumber } from 'ethers'
import { GenesisJsonProvider } from './provider'
import { Genesis } from '@eth-optimism/core-utils/src/types' import { Genesis } from '@eth-optimism/core-utils/src/types'
import { import {
remove0x, remove0x,
...@@ -8,6 +7,8 @@ import { ...@@ -8,6 +7,8 @@ import {
} from '@eth-optimism/core-utils/src/common/hex-strings' } from '@eth-optimism/core-utils/src/common/hex-strings'
import { KECCAK256_RLP_S, KECCAK256_NULL_S } from 'ethereumjs-util' import { KECCAK256_RLP_S, KECCAK256_NULL_S } from 'ethereumjs-util'
import { GenesisJsonProvider } from './provider'
const account = '0x66a84544bed4ca45b3c024776812abf87728fbaf' const account = '0x66a84544bed4ca45b3c024776812abf87728fbaf'
const genesis: Genesis = { const genesis: Genesis = {
......
import path from 'path'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { BigNumber } from '@ethersproject/bignumber' import { BigNumber } from '@ethersproject/bignumber'
import { Deferrable } from '@ethersproject/properties' import { Deferrable } from '@ethersproject/properties'
...@@ -16,8 +18,6 @@ import { ...@@ -16,8 +18,6 @@ import {
Listener, Listener,
} from '@ethersproject/abstract-provider' } from '@ethersproject/abstract-provider'
import { KECCAK256_RLP_S, KECCAK256_NULL_S } from 'ethereumjs-util' import { KECCAK256_RLP_S, KECCAK256_NULL_S } from 'ethereumjs-util'
import path from 'path'
import { bytes32ify, remove0x, add0x } from '@eth-optimism/core-utils' import { bytes32ify, remove0x, add0x } from '@eth-optimism/core-utils'
// Represents the ethereum state // Represents the ethereum state
......
...@@ -6,10 +6,11 @@ import * as dotenv from 'dotenv' ...@@ -6,10 +6,11 @@ import * as dotenv from 'dotenv'
import { getenv, remove0x } from '@eth-optimism/core-utils' import { getenv, remove0x } from '@eth-optimism/core-utils'
import { providers, BigNumber } from 'ethers' import { providers, BigNumber } from 'ethers'
import { solidity } from 'ethereum-waffle' import { solidity } from 'ethereum-waffle'
import { GenesisJsonProvider } from './provider'
import { SurgeryDataSources, Account, AccountType } from '../scripts/types' import { SurgeryDataSources, Account, AccountType } from '../scripts/types'
import { loadSurgeryData } from '../scripts/data' import { loadSurgeryData } from '../scripts/data'
import { classify, classifiers } from '../scripts/classifiers' import { classify, classifiers } from '../scripts/classifiers'
import { GenesisJsonProvider } from './provider'
// Chai plugins go here. // Chai plugins go here.
chai.use(chaiAsPromised) chai.use(chaiAsPromised)
......
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { abi as UNISWAP_POOL_ABI } from '@uniswap/v3-core/artifacts/contracts/UniswapV3Pool.sol/UniswapV3Pool.json' import { abi as UNISWAP_POOL_ABI } from '@uniswap/v3-core/artifacts/contracts/UniswapV3Pool.sol/UniswapV3Pool.json'
import { expect, env, ERC20_ABI } from './setup'
import { UNISWAP_V3_NFPM_ADDRESS } from '../scripts/constants' import { UNISWAP_V3_NFPM_ADDRESS } from '../scripts/constants'
import { getUniswapV3Factory, replaceWETH } from '../scripts/utils' import { getUniswapV3Factory, replaceWETH } from '../scripts/utils'
import { expect, env, ERC20_ABI } from './setup'
import { AccountType } from '../scripts/types' import { AccountType } from '../scripts/types'
describe('uniswap contracts', () => { describe('uniswap contracts', () => {
......
import { expect } from '@eth-optimism/core-utils/test/setup'
import fs from 'fs/promises' import fs from 'fs/promises'
import path from 'path' import path from 'path'
import { expect } from '@eth-optimism/core-utils/test/setup'
import { isBytecodeERC20 } from '../scripts/utils' import { isBytecodeERC20 } from '../scripts/utils'
describe('Utils', () => { describe('Utils', () => {
......
# @eth-optimism/replica-healthcheck # @eth-optimism/replica-healthcheck
## 0.3.4
### Patch Changes
- Updated dependencies [ba96a455]
- Updated dependencies [c3e85fef]
- @eth-optimism/core-utils@0.7.4
## 0.3.3 ## 0.3.3
### Patch Changes ### Patch Changes
......
{ {
"private": true, "private": true,
"name": "@eth-optimism/replica-healthcheck", "name": "@eth-optimism/replica-healthcheck",
"version": "0.3.3", "version": "0.3.4",
"description": "[Optimism] Service for monitoring the health of replica nodes", "description": "[Optimism] Service for monitoring the health of replica nodes",
"main": "dist/index", "main": "dist/index",
"types": "dist/index", "types": "dist/index",
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
}, },
"dependencies": { "dependencies": {
"@eth-optimism/common-ts": "0.2.1", "@eth-optimism/common-ts": "0.2.1",
"@eth-optimism/core-utils": "0.7.3", "@eth-optimism/core-utils": "0.7.4",
"dotenv": "^10.0.0", "dotenv": "^10.0.0",
"ethers": "^5.4.5", "ethers": "^5.4.5",
"express": "^4.17.1", "express": "^4.17.1",
......
import express from 'express'
import { Server } from 'net' import { Server } from 'net'
import express from 'express'
import promBundle from 'express-prom-bundle' import promBundle from 'express-prom-bundle'
import { Gauge, Histogram } from 'prom-client' import { Gauge, Histogram } from 'prom-client'
import cron from 'node-cron' import cron from 'node-cron'
......
# @eth-optimism/sdk # @eth-optimism/sdk
## 0.0.5
### Patch Changes
- Updated dependencies [ba96a455]
- Updated dependencies [c3e85fef]
- @eth-optimism/core-utils@0.7.4
- @eth-optimism/contracts@0.5.9
## 0.0.4 ## 0.0.4
### Patch Changes ### Patch Changes
......
{ {
"name": "@eth-optimism/sdk", "name": "@eth-optimism/sdk",
"version": "0.0.4", "version": "0.0.5",
"description": "[Optimism] Tools for working with Optimism", "description": "[Optimism] Tools for working with Optimism",
"main": "dist/index", "main": "dist/index",
"types": "dist/index", "types": "dist/index",
...@@ -59,8 +59,8 @@ ...@@ -59,8 +59,8 @@
"typescript": "^4.3.5" "typescript": "^4.3.5"
}, },
"dependencies": { "dependencies": {
"@eth-optimism/contracts": "0.5.8", "@eth-optimism/contracts": "0.5.9",
"@eth-optimism/core-utils": "0.7.3", "@eth-optimism/core-utils": "0.7.4",
"@ethersproject/abstract-provider": "^5.5.1", "@ethersproject/abstract-provider": "^5.5.1",
"@ethersproject/abstract-signer": "^5.5.0", "@ethersproject/abstract-signer": "^5.5.0",
"ethers": "^5.5.2" "ethers": "^5.5.2"
......
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Overrides, Signer, BigNumber } from 'ethers'
import {
TransactionRequest,
TransactionResponse,
} from '@ethersproject/abstract-provider'
import {
CrossChainMessageRequest,
ICrossChainMessenger,
ICrossChainProvider,
L1ToL2Overrides,
MessageLike,
NumberLike,
MessageDirection,
} from './interfaces'
import { omit } from './utils'
export class CrossChainMessenger implements ICrossChainMessenger {
provider: ICrossChainProvider
l1Signer: Signer
l2Signer: Signer
/**
* Creates a new CrossChainMessenger instance.
*
* @param opts Options for the messenger.
* @param opts.provider CrossChainProvider to use to send messages.
* @param opts.l1Signer Signer to use to send messages on L1.
* @param opts.l2Signer Signer to use to send messages on L2.
*/
constructor(opts: {
provider: ICrossChainProvider
l1Signer: Signer
l2Signer: Signer
}) {
this.provider = opts.provider
this.l1Signer = opts.l1Signer
this.l2Signer = opts.l2Signer
}
public async sendMessage(
message: CrossChainMessageRequest,
overrides?: L1ToL2Overrides
): Promise<TransactionResponse> {
const tx = await this.populateTransaction.sendMessage(message, overrides)
if (message.direction === MessageDirection.L1_TO_L2) {
return this.l1Signer.sendTransaction(tx)
} else {
return this.l2Signer.sendTransaction(tx)
}
}
public async resendMessage(
message: MessageLike,
messageGasLimit: NumberLike,
overrides?: Overrides
): Promise<TransactionResponse> {
const tx = await this.populateTransaction.resendMessage(
message,
messageGasLimit,
overrides
)
return this.l1Signer.sendTransaction(tx)
}
public async finalizeMessage(
message: MessageLike,
overrides?: Overrides
): Promise<TransactionResponse> {
throw new Error('Not implemented')
}
public async depositETH(
amount: NumberLike,
overrides?: L1ToL2Overrides
): Promise<TransactionResponse> {
throw new Error('Not implemented')
}
public async withdrawETH(
amount: NumberLike,
overrides?: Overrides
): Promise<TransactionResponse> {
throw new Error('Not implemented')
}
populateTransaction = {
sendMessage: async (
message: CrossChainMessageRequest,
overrides?: L1ToL2Overrides
): Promise<TransactionRequest> => {
if (message.direction === MessageDirection.L1_TO_L2) {
return this.provider.contracts.l1.L1CrossDomainMessenger.connect(
this.l1Signer
).populateTransaction.sendMessage(
message.target,
message.message,
overrides?.l2GasLimit ||
(await this.provider.estimateL2MessageGasLimit(message)),
omit(overrides || {}, 'l2GasLimit')
)
} else {
return this.provider.contracts.l2.L2CrossDomainMessenger.connect(
this.l2Signer
).populateTransaction.sendMessage(
message.target,
message.message,
0, // Gas limit goes unused when sending from L2 to L1
omit(overrides || {}, 'l2GasLimit')
)
}
},
resendMessage: async (
message: MessageLike,
messageGasLimit: NumberLike,
overrides?: Overrides
): Promise<TransactionRequest> => {
const resolved = await this.provider.toCrossChainMessage(message)
if (resolved.direction === MessageDirection.L2_TO_L1) {
throw new Error(`cannot resend L2 to L1 message`)
}
return this.provider.contracts.l1.L1CrossDomainMessenger.connect(
this.l1Signer
).populateTransaction.replayMessage(
resolved.target,
resolved.sender,
resolved.message,
resolved.messageNonce,
resolved.gasLimit,
messageGasLimit,
overrides || {}
)
},
finalizeMessage: async (
message: MessageLike,
overrides?: Overrides
): Promise<TransactionRequest> => {
throw new Error('Not implemented')
},
depositETH: async (
amount: NumberLike,
overrides?: L1ToL2Overrides
): Promise<TransactionRequest> => {
throw new Error('Not implemented')
},
withdrawETH: async (
amount: NumberLike,
overrides?: Overrides
): Promise<TransactionRequest> => {
throw new Error('Not implemented')
},
}
estimateGas = {
sendMessage: async (
message: CrossChainMessageRequest,
overrides?: L1ToL2Overrides
): Promise<BigNumber> => {
const tx = await this.populateTransaction.sendMessage(message, overrides)
if (message.direction === MessageDirection.L1_TO_L2) {
return this.provider.l1Provider.estimateGas(tx)
} else {
return this.provider.l2Provider.estimateGas(tx)
}
},
resendMessage: async (
message: MessageLike,
messageGasLimit: NumberLike,
overrides?: Overrides
): Promise<BigNumber> => {
const tx = await this.populateTransaction.resendMessage(
message,
messageGasLimit,
overrides
)
return this.provider.l1Provider.estimateGas(tx)
},
finalizeMessage: async (
message: MessageLike,
overrides?: Overrides
): Promise<BigNumber> => {
throw new Error('Not implemented')
},
depositETH: async (
amount: NumberLike,
overrides?: L1ToL2Overrides
): Promise<BigNumber> => {
throw new Error('Not implemented')
},
withdrawETH: async (
amount: NumberLike,
overrides?: Overrides
): Promise<BigNumber> => {
throw new Error('Not implemented')
},
}
}
...@@ -6,16 +6,19 @@ import { ...@@ -6,16 +6,19 @@ import {
} from '@ethersproject/abstract-provider' } from '@ethersproject/abstract-provider'
import { ethers, BigNumber, Event } from 'ethers' import { ethers, BigNumber, Event } from 'ethers'
import { sleep } from '@eth-optimism/core-utils' import { sleep } from '@eth-optimism/core-utils'
import { import {
ICrossChainProvider, ICrossChainProvider,
OEContracts, OEContracts,
OEContractsLike, OEContractsLike,
MessageLike, MessageLike,
MessageRequestLike,
TransactionLike, TransactionLike,
AddressLike, AddressLike,
NumberLike, NumberLike,
ProviderLike, ProviderLike,
CrossChainMessage, CrossChainMessage,
CrossChainMessageRequest,
MessageDirection, MessageDirection,
MessageStatus, MessageStatus,
TokenBridgeMessage, TokenBridgeMessage,
...@@ -23,6 +26,8 @@ import { ...@@ -23,6 +26,8 @@ import {
MessageReceiptStatus, MessageReceiptStatus,
CustomBridges, CustomBridges,
CustomBridgesLike, CustomBridgesLike,
StateRoot,
StateRootBatch,
} from './interfaces' } from './interfaces'
import { import {
toProvider, toProvider,
...@@ -49,6 +54,7 @@ export class CrossChainProvider implements ICrossChainProvider { ...@@ -49,6 +54,7 @@ export class CrossChainProvider implements ICrossChainProvider {
* @param opts.l2Provider Provider for the L2 chain, or a JSON-RPC url. * @param opts.l2Provider Provider for the L2 chain, or a JSON-RPC url.
* @param opts.l1ChainId Chain ID for the L1 chain. * @param opts.l1ChainId Chain ID for the L1 chain.
* @param opts.contracts Optional contract address overrides. * @param opts.contracts Optional contract address overrides.
* @param opts.bridges Optional bridge address list.
*/ */
constructor(opts: { constructor(opts: {
l1Provider: ProviderLike l1Provider: ProviderLike
...@@ -128,6 +134,7 @@ export class CrossChainProvider implements ICrossChainProvider { ...@@ -128,6 +134,7 @@ export class CrossChainProvider implements ICrossChainProvider {
sender: parsed.args.sender, sender: parsed.args.sender,
message: parsed.args.message, message: parsed.args.message,
messageNonce: parsed.args.messageNonce, messageNonce: parsed.args.messageNonce,
gasLimit: parsed.args.gasLimit,
logIndex: log.logIndex, logIndex: log.logIndex,
blockNumber: log.blockNumber, blockNumber: log.blockNumber,
transactionHash: log.transactionHash, transactionHash: log.transactionHash,
...@@ -335,7 +342,44 @@ export class CrossChainProvider implements ICrossChainProvider { ...@@ -335,7 +342,44 @@ export class CrossChainProvider implements ICrossChainProvider {
} }
public async getMessageStatus(message: MessageLike): Promise<MessageStatus> { public async getMessageStatus(message: MessageLike): Promise<MessageStatus> {
throw new Error('Not implemented') const resolved = await this.toCrossChainMessage(message)
const receipt = await this.getMessageReceipt(resolved)
if (resolved.direction === MessageDirection.L1_TO_L2) {
if (receipt === null) {
return MessageStatus.UNCONFIRMED_L1_TO_L2_MESSAGE
} else {
if (receipt.receiptStatus === MessageReceiptStatus.RELAYED_SUCCEEDED) {
return MessageStatus.RELAYED
} else {
return MessageStatus.FAILED_L1_TO_L2_MESSAGE
}
}
} else {
if (receipt === null) {
const stateRoot = await this.getMessageStateRoot(resolved)
if (stateRoot === null) {
return MessageStatus.STATE_ROOT_NOT_PUBLISHED
} else {
const challengePeriod = await this.getChallengePeriodSeconds()
const targetBlock = await this.l1Provider.getBlock(
stateRoot.blockNumber
)
const latestBlock = await this.l1Provider.getBlock('latest')
if (targetBlock.timestamp + challengePeriod > latestBlock.timestamp) {
return MessageStatus.IN_CHALLENGE_PERIOD
} else {
return MessageStatus.READY_FOR_RELAY
}
}
} else {
if (receipt.receiptStatus === MessageReceiptStatus.RELAYED_SUCCEEDED) {
return MessageStatus.RELAYED
} else {
return MessageStatus.READY_FOR_RELAY
}
}
}
} }
public async getMessageReceipt( public async getMessageReceipt(
...@@ -420,9 +464,36 @@ export class CrossChainProvider implements ICrossChainProvider { ...@@ -420,9 +464,36 @@ export class CrossChainProvider implements ICrossChainProvider {
} }
public async estimateL2MessageGasLimit( public async estimateL2MessageGasLimit(
message: MessageLike message: MessageRequestLike,
opts?: {
bufferPercent?: number
from?: string
}
): Promise<BigNumber> { ): Promise<BigNumber> {
throw new Error('Not implemented') let resolved: CrossChainMessage | CrossChainMessageRequest
let from: string
if ((message as CrossChainMessage).messageNonce === undefined) {
resolved = message as CrossChainMessageRequest
from = opts?.from
} else {
resolved = await this.toCrossChainMessage(message as MessageLike)
from = opts?.from || (resolved as CrossChainMessage).sender
}
// L2 message gas estimation is only used for L1 => L2 messages.
if (resolved.direction === MessageDirection.L2_TO_L1) {
throw new Error(`cannot estimate gas limit for L2 => L1 message`)
}
const estimate = await this.l2Provider.estimateGas({
from,
to: resolved.target,
data: resolved.message,
})
// Return the estimate plus a buffer of 20% just in case.
const bufferPercent = opts?.bufferPercent || 20
return estimate.mul(100 + bufferPercent).div(100)
} }
public async estimateMessageWaitTimeSeconds( public async estimateMessageWaitTimeSeconds(
...@@ -431,9 +502,162 @@ export class CrossChainProvider implements ICrossChainProvider { ...@@ -431,9 +502,162 @@ export class CrossChainProvider implements ICrossChainProvider {
throw new Error('Not implemented') throw new Error('Not implemented')
} }
public async estimateMessageWaitTimeBlocks( public async getChallengePeriodSeconds(): Promise<number> {
const challengePeriod =
await this.contracts.l1.StateCommitmentChain.FRAUD_PROOF_WINDOW()
return challengePeriod.toNumber()
}
public async getMessageStateRoot(
message: MessageLike message: MessageLike
): Promise<number> { ): Promise<StateRoot | null> {
throw new Error('Not implemented') const resolved = await this.toCrossChainMessage(message)
// State roots are only a thing for L2 to L1 messages.
if (resolved.direction === MessageDirection.L1_TO_L2) {
throw new Error(`cannot get a state root for an L1 to L2 message`)
}
// We need the block number of the transaction that triggered the message so we can look up the
// state root batch that corresponds to that block number.
const messageTxReceipt = await this.l2Provider.getTransactionReceipt(
resolved.transactionHash
)
// Every block has exactly one transaction in it. Since there's a genesis block, the
// transaction index will always be one less than the block number.
const messageTxIndex = messageTxReceipt.blockNumber - 1
// Pull down the state root batch, we'll try to pick out the specific state root that
// corresponds to our message.
const stateRootBatch = await this.getStateRootBatchByTransactionIndex(
messageTxIndex
)
// No state root batch, no state root.
if (stateRootBatch === null) {
return null
}
// We have a state root batch, now we need to find the specific state root for our transaction.
// First we need to figure out the index of the state root within the batch we found. This is
// going to be the original transaction index offset by the total number of previous state
// roots.
const indexInBatch =
messageTxIndex - stateRootBatch.header.prevTotalElements.toNumber()
// Just a sanity check.
if (stateRootBatch.stateRoots.length <= indexInBatch) {
// Should never happen!
throw new Error(`state root does not exist in batch`)
}
return {
blockNumber: stateRootBatch.blockNumber,
header: stateRootBatch.header,
stateRoot: stateRootBatch.stateRoots[indexInBatch],
}
}
public async getStateBatchAppendedEventByBatchIndex(
batchIndex: number
): Promise<ethers.Event | null> {
const events = await this.contracts.l1.StateCommitmentChain.queryFilter(
this.contracts.l1.StateCommitmentChain.filters.StateBatchAppended(
batchIndex
)
)
if (events.length === 0) {
return null
} else if (events.length > 1) {
// Should never happen!
throw new Error(`found more than one StateBatchAppended event`)
} else {
return events[0]
}
}
public async getStateBatchAppendedEventByTransactionIndex(
transactionIndex: number
): Promise<ethers.Event | null> {
const isEventHi = (event: ethers.Event, index: number) => {
const prevTotalElements = event.args._prevTotalElements.toNumber()
return index < prevTotalElements
}
const isEventLo = (event: ethers.Event, index: number) => {
const prevTotalElements = event.args._prevTotalElements.toNumber()
const batchSize = event.args._batchSize.toNumber()
return index >= prevTotalElements + batchSize
}
const totalBatches: ethers.BigNumber =
await this.contracts.l1.StateCommitmentChain.getTotalBatches()
if (totalBatches.eq(0)) {
return null
}
let lowerBound = 0
let upperBound = totalBatches.toNumber() - 1
let batchEvent: ethers.Event | null =
await this.getStateBatchAppendedEventByBatchIndex(upperBound)
if (isEventLo(batchEvent, transactionIndex)) {
// Upper bound is too low, means this transaction doesn't have a corresponding state batch yet.
return null
} else if (!isEventHi(batchEvent, transactionIndex)) {
// Upper bound is not too low and also not too high. This means the upper bound event is the
// one we're looking for! Return it.
return batchEvent
}
// Binary search to find the right event. The above checks will guarantee that the event does
// exist and that we'll find it during this search.
while (lowerBound < upperBound) {
const middleOfBounds = Math.floor((lowerBound + upperBound) / 2)
batchEvent = await this.getStateBatchAppendedEventByBatchIndex(
middleOfBounds
)
if (isEventHi(batchEvent, transactionIndex)) {
upperBound = middleOfBounds
} else if (isEventLo(batchEvent, transactionIndex)) {
lowerBound = middleOfBounds
} else {
break
}
}
return batchEvent
}
public async getStateRootBatchByTransactionIndex(
transactionIndex: number
): Promise<StateRootBatch | null> {
const stateBatchAppendedEvent =
await this.getStateBatchAppendedEventByTransactionIndex(transactionIndex)
if (stateBatchAppendedEvent === null) {
return null
}
const stateBatchTransaction = await stateBatchAppendedEvent.getTransaction()
const [stateRoots] =
this.contracts.l1.StateCommitmentChain.interface.decodeFunctionData(
'appendStateBatch',
stateBatchTransaction.data
)
return {
blockNumber: stateBatchAppendedEvent.blockNumber,
stateRoots,
header: {
batchIndex: stateBatchAppendedEvent.args._batchIndex,
batchRoot: stateBatchAppendedEvent.args._batchRoot,
batchSize: stateBatchAppendedEvent.args._batchSize,
prevTotalElements: stateBatchAppendedEvent.args._prevTotalElements,
extraData: stateBatchAppendedEvent.args._extraData,
},
}
} }
} }
export * from './interfaces' export * from './interfaces'
export * from './utils' export * from './utils'
export * from './cross-chain-provider' export * from './cross-chain-provider'
export * from './cross-chain-messenger'
...@@ -3,6 +3,7 @@ import { ...@@ -3,6 +3,7 @@ import {
TransactionRequest, TransactionRequest,
TransactionResponse, TransactionResponse,
} from '@ethersproject/abstract-provider' } from '@ethersproject/abstract-provider'
import { NumberLike, L1ToL2Overrides } from './types' import { NumberLike, L1ToL2Overrides } from './types'
import { ICrossChainMessenger } from './cross-chain-messenger' import { ICrossChainMessenger } from './cross-chain-messenger'
......
import { Overrides, Signer } from 'ethers' import { Overrides, Signer, BigNumber } from 'ethers'
import { import {
TransactionRequest, TransactionRequest,
TransactionResponse, TransactionResponse,
} from '@ethersproject/abstract-provider' } from '@ethersproject/abstract-provider'
import { import {
MessageLike, MessageLike,
NumberLike, NumberLike,
...@@ -21,9 +22,14 @@ export interface ICrossChainMessenger { ...@@ -21,9 +22,14 @@ export interface ICrossChainMessenger {
provider: ICrossChainProvider provider: ICrossChainProvider
/** /**
* Signer that will carry out L1/L2 transactions. * Signer that will carry out L1 transactions.
*/
l1Signer: Signer
/**
* Signer that will carry out L2 transactions.
*/ */
signer: Signer l2Signer: Signer
/** /**
* Sends a given cross chain message. Where the message is sent depends on the direction attached * Sends a given cross chain message. Where the message is sent depends on the direction attached
...@@ -106,7 +112,7 @@ export interface ICrossChainMessenger { ...@@ -106,7 +112,7 @@ export interface ICrossChainMessenger {
sendMessage: ( sendMessage: (
message: CrossChainMessageRequest, message: CrossChainMessageRequest,
overrides?: L1ToL2Overrides overrides?: L1ToL2Overrides
) => Promise<TransactionResponse> ) => Promise<TransactionRequest>
/** /**
* Generates a transaction that resends a given cross chain message. Only applies to L1 to L2 * Generates a transaction that resends a given cross chain message. Only applies to L1 to L2
...@@ -177,7 +183,7 @@ export interface ICrossChainMessenger { ...@@ -177,7 +183,7 @@ export interface ICrossChainMessenger {
sendMessage: ( sendMessage: (
message: CrossChainMessageRequest, message: CrossChainMessageRequest,
overrides?: L1ToL2Overrides overrides?: L1ToL2Overrides
) => Promise<TransactionResponse> ) => Promise<BigNumber>
/** /**
* Estimates gas required to resend a cross chain message. Only applies to L1 to L2 messages. * Estimates gas required to resend a cross chain message. Only applies to L1 to L2 messages.
...@@ -191,7 +197,7 @@ export interface ICrossChainMessenger { ...@@ -191,7 +197,7 @@ export interface ICrossChainMessenger {
message: MessageLike, message: MessageLike,
messageGasLimit: NumberLike, messageGasLimit: NumberLike,
overrides?: Overrides overrides?: Overrides
): Promise<TransactionRequest> ): Promise<BigNumber>
/** /**
* Estimates gas required to finalize a cross chain message. Only applies to L2 to L1 messages. * Estimates gas required to finalize a cross chain message. Only applies to L2 to L1 messages.
...@@ -203,7 +209,7 @@ export interface ICrossChainMessenger { ...@@ -203,7 +209,7 @@ export interface ICrossChainMessenger {
finalizeMessage( finalizeMessage(
message: MessageLike, message: MessageLike,
overrides?: Overrides overrides?: Overrides
): Promise<TransactionRequest> ): Promise<BigNumber>
/** /**
* Estimates gas required to deposit some ETH into the L2 chain. * Estimates gas required to deposit some ETH into the L2 chain.
...@@ -215,7 +221,7 @@ export interface ICrossChainMessenger { ...@@ -215,7 +221,7 @@ export interface ICrossChainMessenger {
depositETH( depositETH(
amount: NumberLike, amount: NumberLike,
overrides?: L1ToL2Overrides overrides?: L1ToL2Overrides
): Promise<TransactionRequest> ): Promise<BigNumber>
/** /**
* Estimates gas required to withdraw some ETH back to the L1 chain. * Estimates gas required to withdraw some ETH back to the L1 chain.
...@@ -224,9 +230,6 @@ export interface ICrossChainMessenger { ...@@ -224,9 +230,6 @@ export interface ICrossChainMessenger {
* @param overrides Optional transaction overrides. * @param overrides Optional transaction overrides.
* @returns Transaction that can be signed and executed to withdraw the tokens. * @returns Transaction that can be signed and executed to withdraw the tokens.
*/ */
withdrawETH( withdrawETH(amount: NumberLike, overrides?: Overrides): Promise<BigNumber>
amount: NumberLike,
overrides?: Overrides
): Promise<TransactionRequest>
} }
} }
import { BigNumber } from 'ethers' import { Event, BigNumber } from 'ethers'
import { Provider, BlockTag } from '@ethersproject/abstract-provider' import { Provider, BlockTag } from '@ethersproject/abstract-provider'
import { import {
MessageLike, MessageLike,
MessageRequestLike,
TransactionLike, TransactionLike,
AddressLike, AddressLike,
NumberLike, NumberLike,
...@@ -12,6 +14,8 @@ import { ...@@ -12,6 +14,8 @@ import {
OEContracts, OEContracts,
MessageReceipt, MessageReceipt,
CustomBridges, CustomBridges,
StateRoot,
StateRootBatch,
} from './types' } from './types'
/** /**
...@@ -200,9 +204,18 @@ export interface ICrossChainProvider { ...@@ -200,9 +204,18 @@ export interface ICrossChainProvider {
* L1 => L2 messages. You would supply this gas limit when sending the message to L2. * L1 => L2 messages. You would supply this gas limit when sending the message to L2.
* *
* @param message Message get a gas estimate for. * @param message Message get a gas estimate for.
* @param opts Options object.
* @param opts.bufferPercent Percentage of gas to add to the estimate. Defaults to 20.
* @param opts.from Address to use as the sender.
* @returns Estimates L2 gas limit. * @returns Estimates L2 gas limit.
*/ */
estimateL2MessageGasLimit(message: MessageLike): Promise<BigNumber> estimateL2MessageGasLimit(
message: MessageRequestLike,
opts?: {
bufferPercent?: number
from?: string
}
): Promise<BigNumber>
/** /**
* Returns the estimated amount of time before the message can be executed. When this is a * Returns the estimated amount of time before the message can be executed. When this is a
...@@ -216,13 +229,52 @@ export interface ICrossChainProvider { ...@@ -216,13 +229,52 @@ export interface ICrossChainProvider {
estimateMessageWaitTimeSeconds(message: MessageLike): Promise<number> estimateMessageWaitTimeSeconds(message: MessageLike): Promise<number>
/** /**
* Returns the estimated amount of time before the message can be executed (in L1 blocks). * Queries the current challenge period in seconds from the StateCommitmentChain.
* When this is a message being sent to L1, this will return the estimated time until the message
* will complete its challenge period. When this is a message being sent to L2, this will return
* the estimated amount of time until the message will be picked up and executed on L2.
* *
* @param message Message to estimate the time remaining for. * @returns Current challenge period in seconds.
* @returns Estimated amount of time remaining (in blocks) before the message can be executed. */
getChallengePeriodSeconds(): Promise<number>
/**
* Returns the state root that corresponds to a given message. This is the state root for the
* block in which the transaction was included, as published to the StateCommitmentChain. If the
* state root for the given message has not been published yet, this function returns null.
*
* @param message Message to find a state root for.
* @returns State root for the block in which the message was created.
*/
getMessageStateRoot(message: MessageLike): Promise<StateRoot | null>
/**
* Returns the StateBatchAppended event that was emitted when the batch with a given index was
* created. Returns null if no such event exists (the batch has not been submitted).
*
* @param batchIndex Index of the batch to find an event for.
* @returns StateBatchAppended event for the batch, or null if no such batch exists.
*/
getStateBatchAppendedEventByBatchIndex(
batchIndex: number
): Promise<Event | null>
/**
* Returns the StateBatchAppended event for the batch that includes the transaction with the
* given index. Returns null if no such event exists.
*
* @param transactionIndex Index of the L2 transaction to find an event for.
* @returns StateBatchAppended event for the batch that includes the given transaction by index.
*/
getStateBatchAppendedEventByTransactionIndex(
transactionIndex: number
): Promise<Event | null>
/**
* Returns information about the state root batch that included the state root for the given
* transaction by index. Returns null if no such state root has been published yet.
*
* @param transactionIndex Index of the L2 transaction to find a state root batch for.
* @returns State root batch for the given transaction index, or null if none exists yet.
*/ */
estimateMessageWaitTimeBlocks(message: MessageLike): Promise<number> getStateRootBatchByTransactionIndex(
transactionIndex: number
): Promise<StateRootBatch | null>
} }
...@@ -143,7 +143,6 @@ export interface CrossChainMessageRequest { ...@@ -143,7 +143,6 @@ export interface CrossChainMessageRequest {
direction: MessageDirection direction: MessageDirection
target: string target: string
message: string message: string
l2GasLimit: NumberLike
} }
/** /**
...@@ -162,6 +161,7 @@ export interface CoreCrossChainMessage { ...@@ -162,6 +161,7 @@ export interface CoreCrossChainMessage {
*/ */
export interface CrossChainMessage extends CoreCrossChainMessage { export interface CrossChainMessage extends CoreCrossChainMessage {
direction: MessageDirection direction: MessageDirection
gasLimit: number
logIndex: number logIndex: number
blockNumber: number blockNumber: number
transactionHash: string transactionHash: string
...@@ -212,9 +212,19 @@ export interface StateRootBatchHeader { ...@@ -212,9 +212,19 @@ export interface StateRootBatchHeader {
} }
/** /**
* State root batch, including header and actual state roots. * Information about a state root, including header, block number, and root iself.
*/
export interface StateRoot {
blockNumber: number
header: StateRootBatchHeader
stateRoot: string
}
/**
* Information about a batch of state roots.
*/ */
export interface StateRootBatch { export interface StateRootBatch {
blockNumber: number
header: StateRootBatchHeader header: StateRootBatchHeader
stateRoots: string[] stateRoots: string[]
} }
...@@ -225,7 +235,7 @@ export interface StateRootBatch { ...@@ -225,7 +235,7 @@ export interface StateRootBatch {
* limit field (gas used depends on the amount of gas provided). * limit field (gas used depends on the amount of gas provided).
*/ */
export type L1ToL2Overrides = Overrides & { export type L1ToL2Overrides = Overrides & {
l2GasLimit: NumberLike l2GasLimit?: NumberLike
} }
/** /**
...@@ -234,13 +244,22 @@ export type L1ToL2Overrides = Overrides & { ...@@ -234,13 +244,22 @@ export type L1ToL2Overrides = Overrides & {
export type TransactionLike = string | TransactionReceipt | TransactionResponse export type TransactionLike = string | TransactionReceipt | TransactionResponse
/** /**
* Stuff that can be coerced into a message. * Stuff that can be coerced into a CrossChainMessage.
*/ */
export type MessageLike = export type MessageLike =
| CrossChainMessage | CrossChainMessage
| TransactionLike | TransactionLike
| TokenBridgeMessage | TokenBridgeMessage
/**
* Stuff that can be coerced into a CrossChainMessageRequest.
*/
export type MessageRequestLike =
| CrossChainMessageRequest
| CrossChainMessage
| TransactionLike
| TokenBridgeMessage
/** /**
* Stuff that can be coerced into a provider. * Stuff that can be coerced into a provider.
*/ */
......
import assert from 'assert' import assert from 'assert'
import { import {
Provider, Provider,
TransactionReceipt, TransactionReceipt,
TransactionResponse, TransactionResponse,
} from '@ethersproject/abstract-provider' } from '@ethersproject/abstract-provider'
import { ethers, BigNumber } from 'ethers' import { ethers, BigNumber } from 'ethers'
import { import {
ProviderLike, ProviderLike,
TransactionLike, TransactionLike,
......
import { getContractInterface, predeploys } from '@eth-optimism/contracts' import { getContractInterface, predeploys } from '@eth-optimism/contracts'
import { ethers, Contract } from 'ethers' import { ethers, Contract } from 'ethers'
import { toAddress } from './coercion'
import { DeepPartial } from './type-utils'
import { import {
OEContracts, OEContracts,
OEL1Contracts, OEL1Contracts,
...@@ -10,8 +13,6 @@ import { ...@@ -10,8 +13,6 @@ import {
CustomBridges, CustomBridges,
CustomBridgesLike, CustomBridgesLike,
} from '../interfaces' } from '../interfaces'
import { toAddress } from './coercion'
import { DeepPartial } from './type-utils'
/** /**
* Full list of default L2 contract addresses. * Full list of default L2 contract addresses.
......
import { getContractInterface } from '@eth-optimism/contracts' import { getContractInterface } from '@eth-optimism/contracts'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { CoreCrossChainMessage } from '../interfaces' import { CoreCrossChainMessage } from '../interfaces'
/** /**
......
...@@ -7,13 +7,40 @@ contract MockMessenger is ICrossDomainMessenger { ...@@ -7,13 +7,40 @@ contract MockMessenger is ICrossDomainMessenger {
return address(0); return address(0);
} }
uint256 public nonce;
// Empty function to satisfy the interface. // Empty function to satisfy the interface.
function sendMessage( function sendMessage(
address _target, address _target,
bytes calldata _message, bytes calldata _message,
uint32 _gasLimit uint32 _gasLimit
) public { ) public {
return; emit SentMessage(
_target,
msg.sender,
_message,
nonce,
_gasLimit
);
nonce++;
}
function replayMessage(
address _target,
address _sender,
bytes calldata _message,
uint256 _queueIndex,
uint32 _oldGasLimit,
uint32 _newGasLimit
) public {
emit SentMessage(
_target,
_sender,
_message,
nonce,
_newGasLimit
);
nonce++;
} }
struct SentMessageEventParams { struct SentMessageEventParams {
......
pragma solidity ^0.8.9;
contract MockSCC {
event StateBatchAppended(
uint256 indexed _batchIndex,
bytes32 _batchRoot,
uint256 _batchSize,
uint256 _prevTotalElements,
bytes _extraData
);
struct StateBatchAppendedArgs {
uint256 batchIndex;
bytes32 batchRoot;
uint256 batchSize;
uint256 prevTotalElements;
bytes extraData;
}
// Window in seconds, will resolve to 100 blocks.
uint256 public FRAUD_PROOF_WINDOW = 1500;
uint256 public batches = 0;
StateBatchAppendedArgs public sbaParams;
function getTotalBatches() public view returns (uint256) {
return batches;
}
function setSBAParams(
StateBatchAppendedArgs memory _args
) public {
sbaParams = _args;
}
function appendStateBatch(
bytes32[] memory _roots,
uint256 _shouldStartAtIndex
) public {
batches++;
emit StateBatchAppended(
sbaParams.batchIndex,
sbaParams.batchRoot,
sbaParams.batchSize,
sbaParams.prevTotalElements,
sbaParams.extraData
);
}
}
import './setup' import { Contract } from 'ethers'
import { ethers } from 'hardhat'
import { expect } from './setup'
import {
CrossChainProvider,
CrossChainMessenger,
MessageDirection,
} from '../src'
describe('CrossChainMessenger', () => { describe('CrossChainMessenger', () => {
let l1Signer: any
let l2Signer: any
before(async () => {
;[l1Signer, l2Signer] = await ethers.getSigners()
})
describe('sendMessage', () => { describe('sendMessage', () => {
let l1Messenger: Contract
let l2Messenger: Contract
let provider: CrossChainProvider
let messenger: CrossChainMessenger
beforeEach(async () => {
l1Messenger = (await (
await ethers.getContractFactory('MockMessenger')
).deploy()) as any
l2Messenger = (await (
await ethers.getContractFactory('MockMessenger')
).deploy()) as any
provider = new CrossChainProvider({
l1Provider: ethers.provider,
l2Provider: ethers.provider,
l1ChainId: 31337,
contracts: {
l1: {
L1CrossDomainMessenger: l1Messenger.address,
},
l2: {
L2CrossDomainMessenger: l2Messenger.address,
},
},
})
messenger = new CrossChainMessenger({
provider,
l1Signer,
l2Signer,
})
})
describe('when the message is an L1 to L2 message', () => {
describe('when no l2GasLimit is provided', () => { describe('when no l2GasLimit is provided', () => {
it('should send a message with an estimated l2GasLimit') it('should send a message with an estimated l2GasLimit', async () => {
const message = {
direction: MessageDirection.L1_TO_L2,
target: '0x' + '11'.repeat(20),
message: '0x' + '22'.repeat(32),
}
const estimate = await provider.estimateL2MessageGasLimit(message)
await expect(messenger.sendMessage(message))
.to.emit(l1Messenger, 'SentMessage')
.withArgs(
message.target,
await l1Signer.getAddress(),
message.message,
0,
estimate
)
})
}) })
describe('when an l2GasLimit is provided', () => { describe('when an l2GasLimit is provided', () => {
it('should send a message with the provided l2GasLimit') it('should send a message with the provided l2GasLimit', async () => {
const message = {
direction: MessageDirection.L1_TO_L2,
target: '0x' + '11'.repeat(20),
message: '0x' + '22'.repeat(32),
}
await expect(
messenger.sendMessage(message, {
l2GasLimit: 1234,
})
)
.to.emit(l1Messenger, 'SentMessage')
.withArgs(
message.target,
await l1Signer.getAddress(),
message.message,
0,
1234
)
})
})
})
describe('when the message is an L2 to L1 message', () => {
it('should send a message', async () => {
const message = {
direction: MessageDirection.L2_TO_L1,
target: '0x' + '11'.repeat(20),
message: '0x' + '22'.repeat(32),
}
await expect(messenger.sendMessage(message))
.to.emit(l2Messenger, 'SentMessage')
.withArgs(
message.target,
await l2Signer.getAddress(),
message.message,
0,
0
)
})
}) })
}) })
describe('resendMessage', () => { describe('resendMessage', () => {
describe('when the message being resent exists', () => { let l1Messenger: Contract
it('should resend the message with the new gas limit') let l2Messenger: Contract
let provider: CrossChainProvider
let messenger: CrossChainMessenger
beforeEach(async () => {
l1Messenger = (await (
await ethers.getContractFactory('MockMessenger')
).deploy()) as any
l2Messenger = (await (
await ethers.getContractFactory('MockMessenger')
).deploy()) as any
provider = new CrossChainProvider({
l1Provider: ethers.provider,
l2Provider: ethers.provider,
l1ChainId: 31337,
contracts: {
l1: {
L1CrossDomainMessenger: l1Messenger.address,
},
l2: {
L2CrossDomainMessenger: l2Messenger.address,
},
},
}) })
describe('when the message being resent does not exist', () => { messenger = new CrossChainMessenger({
it('should throw an error') provider,
l1Signer,
l2Signer,
})
})
describe('when resending an L1 to L2 message', () => {
it('should resend the message with the new gas limit', async () => {
const message = {
direction: MessageDirection.L1_TO_L2,
target: '0x' + '11'.repeat(20),
message: '0x' + '22'.repeat(32),
}
const sent = await messenger.sendMessage(message, {
l2GasLimit: 1234,
})
await expect(messenger.resendMessage(sent, 10000))
.to.emit(l1Messenger, 'SentMessage')
.withArgs(
message.target,
await l1Signer.getAddress(),
message.message,
1, // nonce is now 1
10000
)
})
})
describe('when resending an L2 to L1 message', () => {
it('should throw an error', async () => {
const message = {
direction: MessageDirection.L2_TO_L1,
target: '0x' + '11'.repeat(20),
message: '0x' + '22'.repeat(32),
}
const sent = await messenger.sendMessage(message, {
l2GasLimit: 1234,
})
await expect(messenger.resendMessage(sent, 10000)).to.be.rejected
})
}) })
}) })
......
import { expect } from './setup'
import { Provider } from '@ethersproject/abstract-provider' import { Provider } from '@ethersproject/abstract-provider'
import { expectApprox } from '@eth-optimism/core-utils'
import { Contract } from 'ethers' import { Contract } from 'ethers'
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { expect } from './setup'
import { import {
CrossChainProvider, CrossChainProvider,
MessageDirection, MessageDirection,
CONTRACT_ADDRESSES, CONTRACT_ADDRESSES,
hashCrossChainMessage, hashCrossChainMessage,
omit, omit,
MessageStatus,
CrossChainMessage,
} from '../src' } from '../src'
import { DUMMY_MESSAGE } from './helpers'
describe('CrossChainProvider', () => { describe('CrossChainProvider', () => {
describe('construction', () => { describe('construction', () => {
...@@ -250,13 +255,7 @@ describe('CrossChainProvider', () => { ...@@ -250,13 +255,7 @@ describe('CrossChainProvider', () => {
for (const n of [1, 2, 4, 8]) { for (const n of [1, 2, 4, 8]) {
it(`should find ${n} messages when the transaction emits ${n} messages`, async () => { it(`should find ${n} messages when the transaction emits ${n} messages`, async () => {
const messages = [...Array(n)].map(() => { const messages = [...Array(n)].map(() => {
return { return DUMMY_MESSAGE
target: '0x' + '11'.repeat(20),
sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64),
messageNonce: 1234,
gasLimit: 100000,
}
}) })
const tx = await l1Messenger.triggerSentMessageEvents(messages) const tx = await l1Messenger.triggerSentMessageEvents(messages)
...@@ -271,6 +270,7 @@ describe('CrossChainProvider', () => { ...@@ -271,6 +270,7 @@ describe('CrossChainProvider', () => {
target: message.target, target: message.target,
message: message.message, message: message.message,
messageNonce: ethers.BigNumber.from(message.messageNonce), messageNonce: ethers.BigNumber.from(message.messageNonce),
gasLimit: ethers.BigNumber.from(message.gasLimit),
logIndex: i, logIndex: i,
blockNumber: tx.blockNumber, blockNumber: tx.blockNumber,
transactionHash: tx.hash, transactionHash: tx.hash,
...@@ -309,13 +309,7 @@ describe('CrossChainProvider', () => { ...@@ -309,13 +309,7 @@ describe('CrossChainProvider', () => {
for (const n of [1, 2, 4, 8]) { for (const n of [1, 2, 4, 8]) {
it(`should find ${n} messages when the transaction emits ${n} messages`, async () => { it(`should find ${n} messages when the transaction emits ${n} messages`, async () => {
const messages = [...Array(n)].map(() => { const messages = [...Array(n)].map(() => {
return { return DUMMY_MESSAGE
target: '0x' + '11'.repeat(20),
sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64),
messageNonce: 1234,
gasLimit: 100000,
}
}) })
const tx = await l1Messenger.triggerSentMessageEvents(messages) const tx = await l1Messenger.triggerSentMessageEvents(messages)
...@@ -328,6 +322,7 @@ describe('CrossChainProvider', () => { ...@@ -328,6 +322,7 @@ describe('CrossChainProvider', () => {
target: message.target, target: message.target,
message: message.message, message: message.message,
messageNonce: ethers.BigNumber.from(message.messageNonce), messageNonce: ethers.BigNumber.from(message.messageNonce),
gasLimit: ethers.BigNumber.from(message.gasLimit),
logIndex: i, logIndex: i,
blockNumber: tx.blockNumber, blockNumber: tx.blockNumber,
transactionHash: tx.hash, transactionHash: tx.hash,
...@@ -692,6 +687,7 @@ describe('CrossChainProvider', () => { ...@@ -692,6 +687,7 @@ describe('CrossChainProvider', () => {
sender: '0x' + '22'.repeat(20), sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64), message: '0x' + '33'.repeat(64),
messageNonce: 1234, messageNonce: 1234,
gasLimit: 0,
logIndex: 0, logIndex: 0,
blockNumber: 1234, blockNumber: 1234,
transactionHash: '0x' + '44'.repeat(32), transactionHash: '0x' + '44'.repeat(32),
...@@ -736,15 +732,7 @@ describe('CrossChainProvider', () => { ...@@ -736,15 +732,7 @@ describe('CrossChainProvider', () => {
describe('when the input is a TransactionLike', () => { describe('when the input is a TransactionLike', () => {
describe('when the transaction sent exactly one message', () => { describe('when the transaction sent exactly one message', () => {
it('should return the CrossChainMessage sent in the transaction', async () => { it('should return the CrossChainMessage sent in the transaction', async () => {
const message = { const tx = await l1Messenger.triggerSentMessageEvents([DUMMY_MESSAGE])
target: '0x' + '11'.repeat(20),
sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64),
messageNonce: 1234,
gasLimit: 100000,
}
const tx = await l1Messenger.triggerSentMessageEvents([message])
const foundCrossChainMessages = const foundCrossChainMessages =
await provider.getMessagesByTransaction(tx) await provider.getMessagesByTransaction(tx)
const resolved = await provider.toCrossChainMessage(tx) const resolved = await provider.toCrossChainMessage(tx)
...@@ -755,13 +743,7 @@ describe('CrossChainProvider', () => { ...@@ -755,13 +743,7 @@ describe('CrossChainProvider', () => {
describe('when the transaction sent more than one message', () => { describe('when the transaction sent more than one message', () => {
it('should throw an error', async () => { it('should throw an error', async () => {
const messages = [...Array(2)].map(() => { const messages = [...Array(2)].map(() => {
return { return DUMMY_MESSAGE
target: '0x' + '11'.repeat(20),
sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64),
messageNonce: 1234,
gasLimit: 100000,
}
}) })
const tx = await l1Messenger.triggerSentMessageEvents(messages) const tx = await l1Messenger.triggerSentMessageEvents(messages)
...@@ -783,45 +765,200 @@ describe('CrossChainProvider', () => { ...@@ -783,45 +765,200 @@ describe('CrossChainProvider', () => {
}) })
describe('getMessageStatus', () => { describe('getMessageStatus', () => {
let scc: Contract
let l1Messenger: Contract
let l2Messenger: Contract
let provider: CrossChainProvider
beforeEach(async () => {
// TODO: Get rid of the nested awaits here. Could be a good first issue for someone.
scc = (await (await ethers.getContractFactory('MockSCC')).deploy()) as any
l1Messenger = (await (
await ethers.getContractFactory('MockMessenger')
).deploy()) as any
l2Messenger = (await (
await ethers.getContractFactory('MockMessenger')
).deploy()) as any
provider = new CrossChainProvider({
l1Provider: ethers.provider,
l2Provider: ethers.provider,
l1ChainId: 31337,
contracts: {
l1: {
L1CrossDomainMessenger: l1Messenger.address,
StateCommitmentChain: scc.address,
},
l2: {
L2CrossDomainMessenger: l2Messenger.address,
},
},
})
})
const sendAndGetDummyMessage = async (direction: MessageDirection) => {
const messenger =
direction === MessageDirection.L1_TO_L2 ? l1Messenger : l2Messenger
const tx = await messenger.triggerSentMessageEvents([DUMMY_MESSAGE])
return (
await provider.getMessagesByTransaction(tx, {
direction,
})
)[0]
}
const submitStateRootBatchForMessage = async (
message: CrossChainMessage
) => {
await scc.setSBAParams({
batchIndex: 0,
batchRoot: ethers.constants.HashZero,
batchSize: 1,
prevTotalElements: message.blockNumber,
extraData: '0x',
})
await scc.appendStateBatch([ethers.constants.HashZero], 0)
}
describe('when the message is an L1 => L2 message', () => { describe('when the message is an L1 => L2 message', () => {
describe('when the message has not been executed on L2 yet', () => { describe('when the message has not been executed on L2 yet', () => {
it('should return a status of UNCONFIRMED_L1_TO_L2_MESSAGE') it('should return a status of UNCONFIRMED_L1_TO_L2_MESSAGE', async () => {
const message = await sendAndGetDummyMessage(
MessageDirection.L1_TO_L2
)
expect(await provider.getMessageStatus(message)).to.equal(
MessageStatus.UNCONFIRMED_L1_TO_L2_MESSAGE
)
})
}) })
describe('when the message has been executed on L2', () => { describe('when the message has been executed on L2', () => {
it('should return a status of RELAYED') it('should return a status of RELAYED', async () => {
const message = await sendAndGetDummyMessage(
MessageDirection.L1_TO_L2
)
await l2Messenger.triggerRelayedMessageEvents([
hashCrossChainMessage(message),
])
expect(await provider.getMessageStatus(message)).to.equal(
MessageStatus.RELAYED
)
})
}) })
describe('when the message has been executed but failed', () => { describe('when the message has been executed but failed', () => {
it('should return a status of FAILED_L1_TO_L2_MESSAGE') it('should return a status of FAILED_L1_TO_L2_MESSAGE', async () => {
const message = await sendAndGetDummyMessage(
MessageDirection.L1_TO_L2
)
await l2Messenger.triggerFailedRelayedMessageEvents([
hashCrossChainMessage(message),
])
expect(await provider.getMessageStatus(message)).to.equal(
MessageStatus.FAILED_L1_TO_L2_MESSAGE
)
})
}) })
}) })
describe('when the message is an L2 => L1 message', () => { describe('when the message is an L2 => L1 message', () => {
describe('when the message state root has not been published', () => { describe('when the message state root has not been published', () => {
it('should return a status of STATE_ROOT_NOT_PUBLISHED') it('should return a status of STATE_ROOT_NOT_PUBLISHED', async () => {
const message = await sendAndGetDummyMessage(
MessageDirection.L2_TO_L1
)
expect(await provider.getMessageStatus(message)).to.equal(
MessageStatus.STATE_ROOT_NOT_PUBLISHED
)
})
}) })
describe('when the message state root is still in the challenge period', () => { describe('when the message state root is still in the challenge period', () => {
it('should return a status of IN_CHALLENGE_PERIOD') it('should return a status of IN_CHALLENGE_PERIOD', async () => {
const message = await sendAndGetDummyMessage(
MessageDirection.L2_TO_L1
)
await submitStateRootBatchForMessage(message)
expect(await provider.getMessageStatus(message)).to.equal(
MessageStatus.IN_CHALLENGE_PERIOD
)
})
}) })
describe('when the message is no longer in the challenge period', () => { describe('when the message is no longer in the challenge period', () => {
describe('when the message has been relayed successfully', () => { describe('when the message has been relayed successfully', () => {
it('should return a status of RELAYED') it('should return a status of RELAYED', async () => {
const message = await sendAndGetDummyMessage(
MessageDirection.L2_TO_L1
)
await submitStateRootBatchForMessage(message)
const challengePeriod = await provider.getChallengePeriodSeconds()
ethers.provider.send('evm_increaseTime', [challengePeriod + 1])
ethers.provider.send('evm_mine', [])
await l1Messenger.triggerRelayedMessageEvents([
hashCrossChainMessage(message),
])
expect(await provider.getMessageStatus(message)).to.equal(
MessageStatus.RELAYED
)
})
}) })
describe('when the message has been relayed but the relay failed', () => { describe('when the message has been relayed but the relay failed', () => {
it('should return a status of READY_FOR_RELAY') it('should return a status of READY_FOR_RELAY', async () => {
const message = await sendAndGetDummyMessage(
MessageDirection.L2_TO_L1
)
await submitStateRootBatchForMessage(message)
const challengePeriod = await provider.getChallengePeriodSeconds()
ethers.provider.send('evm_increaseTime', [challengePeriod + 1])
ethers.provider.send('evm_mine', [])
await l1Messenger.triggerFailedRelayedMessageEvents([
hashCrossChainMessage(message),
])
expect(await provider.getMessageStatus(message)).to.equal(
MessageStatus.READY_FOR_RELAY
)
})
}) })
describe('when the message has not been relayed', () => { describe('when the message has not been relayed', () => {
it('should return a status of READY_FOR_RELAY') it('should return a status of READY_FOR_RELAY', async () => {
const message = await sendAndGetDummyMessage(
MessageDirection.L2_TO_L1
)
await submitStateRootBatchForMessage(message)
const challengePeriod = await provider.getChallengePeriodSeconds()
ethers.provider.send('evm_increaseTime', [challengePeriod + 1])
ethers.provider.send('evm_mine', [])
expect(await provider.getMessageStatus(message)).to.equal(
MessageStatus.READY_FOR_RELAY
)
})
}) })
}) })
}) })
describe('when the message does not exist', () => { describe('when the message does not exist', () => {
// TODO: Figure out if this is the correct behavior. Mark suggests perhaps returning null.
it('should throw an error') it('should throw an error')
}) })
}) })
...@@ -872,6 +1009,7 @@ describe('CrossChainProvider', () => { ...@@ -872,6 +1009,7 @@ describe('CrossChainProvider', () => {
sender: '0x' + '22'.repeat(20), sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64), message: '0x' + '33'.repeat(64),
messageNonce: 1234, messageNonce: 1234,
gasLimit: 0,
logIndex: 0, logIndex: 0,
blockNumber: 1234, blockNumber: 1234,
transactionHash: '0x' + '44'.repeat(32), transactionHash: '0x' + '44'.repeat(32),
...@@ -902,6 +1040,7 @@ describe('CrossChainProvider', () => { ...@@ -902,6 +1040,7 @@ describe('CrossChainProvider', () => {
sender: '0x' + '22'.repeat(20), sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64), message: '0x' + '33'.repeat(64),
messageNonce: 1234, messageNonce: 1234,
gasLimit: 0,
logIndex: 0, logIndex: 0,
blockNumber: 1234, blockNumber: 1234,
transactionHash: '0x' + '44'.repeat(32), transactionHash: '0x' + '44'.repeat(32),
...@@ -932,6 +1071,7 @@ describe('CrossChainProvider', () => { ...@@ -932,6 +1071,7 @@ describe('CrossChainProvider', () => {
sender: '0x' + '22'.repeat(20), sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64), message: '0x' + '33'.repeat(64),
messageNonce: 1234, messageNonce: 1234,
gasLimit: 0,
logIndex: 0, logIndex: 0,
blockNumber: 1234, blockNumber: 1234,
transactionHash: '0x' + '44'.repeat(32), transactionHash: '0x' + '44'.repeat(32),
...@@ -967,6 +1107,7 @@ describe('CrossChainProvider', () => { ...@@ -967,6 +1107,7 @@ describe('CrossChainProvider', () => {
sender: '0x' + '22'.repeat(20), sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64), message: '0x' + '33'.repeat(64),
messageNonce: 1234, messageNonce: 1234,
gasLimit: 0,
logIndex: 0, logIndex: 0,
blockNumber: 1234, blockNumber: 1234,
transactionHash: '0x' + '44'.repeat(32), transactionHash: '0x' + '44'.repeat(32),
...@@ -1011,6 +1152,7 @@ describe('CrossChainProvider', () => { ...@@ -1011,6 +1152,7 @@ describe('CrossChainProvider', () => {
sender: '0x' + '22'.repeat(20), sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64), message: '0x' + '33'.repeat(64),
messageNonce: 1234, messageNonce: 1234,
gasLimit: 0,
logIndex: 0, logIndex: 0,
blockNumber: 1234, blockNumber: 1234,
transactionHash: '0x' + '44'.repeat(32), transactionHash: '0x' + '44'.repeat(32),
...@@ -1042,6 +1184,7 @@ describe('CrossChainProvider', () => { ...@@ -1042,6 +1184,7 @@ describe('CrossChainProvider', () => {
sender: '0x' + '22'.repeat(20), sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64), message: '0x' + '33'.repeat(64),
messageNonce: 1234, messageNonce: 1234,
gasLimit: 0,
logIndex: 0, logIndex: 0,
blockNumber: 1234, blockNumber: 1234,
transactionHash: '0x' + '44'.repeat(32), transactionHash: '0x' + '44'.repeat(32),
...@@ -1074,6 +1217,7 @@ describe('CrossChainProvider', () => { ...@@ -1074,6 +1217,7 @@ describe('CrossChainProvider', () => {
sender: '0x' + '22'.repeat(20), sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64), message: '0x' + '33'.repeat(64),
messageNonce: 1234, messageNonce: 1234,
gasLimit: 0,
logIndex: 0, logIndex: 0,
blockNumber: 1234, blockNumber: 1234,
transactionHash: '0x' + '44'.repeat(32), transactionHash: '0x' + '44'.repeat(32),
...@@ -1090,7 +1234,93 @@ describe('CrossChainProvider', () => { ...@@ -1090,7 +1234,93 @@ describe('CrossChainProvider', () => {
}) })
describe('estimateL2MessageGasLimit', () => { describe('estimateL2MessageGasLimit', () => {
it('should perform a gas estimation of the L2 action') let provider: CrossChainProvider
beforeEach(async () => {
provider = new CrossChainProvider({
l1Provider: ethers.provider,
l2Provider: ethers.provider,
l1ChainId: 31337,
})
})
describe('when the message is an L1 to L2 message', () => {
it('should return an accurate gas estimate plus a ~20% buffer', async () => {
const message = {
direction: MessageDirection.L1_TO_L2,
target: '0x' + '11'.repeat(20),
sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64),
messageNonce: 1234,
logIndex: 0,
blockNumber: 1234,
transactionHash: '0x' + '44'.repeat(32),
}
const estimate = await ethers.provider.estimateGas({
to: message.target,
from: message.sender,
data: message.message,
})
// Approximately 20% greater than the estimate, +/- 1%.
expectApprox(
await provider.estimateL2MessageGasLimit(message),
estimate.mul(120).div(100),
{
percentUpperDeviation: 1,
percentLowerDeviation: 1,
}
)
})
it('should return an accurate gas estimate when a custom buffer is provided', async () => {
const message = {
direction: MessageDirection.L1_TO_L2,
target: '0x' + '11'.repeat(20),
sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64),
messageNonce: 1234,
logIndex: 0,
blockNumber: 1234,
transactionHash: '0x' + '44'.repeat(32),
}
const estimate = await ethers.provider.estimateGas({
to: message.target,
from: message.sender,
data: message.message,
})
// Approximately 30% greater than the estimate, +/- 1%.
expectApprox(
await provider.estimateL2MessageGasLimit(message, {
bufferPercent: 30,
}),
estimate.mul(130).div(100),
{
percentUpperDeviation: 1,
percentLowerDeviation: 1,
}
)
})
})
describe('when the message is an L2 to L1 message', () => {
it('should throw an error', async () => {
const message = {
direction: MessageDirection.L2_TO_L1,
target: '0x' + '11'.repeat(20),
sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64),
messageNonce: 1234,
logIndex: 0,
blockNumber: 1234,
transactionHash: '0x' + '44'.repeat(32),
}
await expect(provider.estimateL2MessageGasLimit(message)).to.be.rejected
})
})
}) })
describe('estimateMessageWaitTimeBlocks', () => { describe('estimateMessageWaitTimeBlocks', () => {
......
export const DUMMY_MESSAGE = {
target: '0x' + '11'.repeat(20),
sender: '0x' + '22'.repeat(20),
message: '0x' + '33'.repeat(64),
messageNonce: 1234,
gasLimit: 100000,
}
export * from './constants'
import { expect } from '../setup'
import { Provider } from '@ethersproject/abstract-provider' import { Provider } from '@ethersproject/abstract-provider'
import { Contract } from 'ethers' import { Contract } from 'ethers'
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { expect } from '../setup'
import { toProvider, toTransactionHash } from '../../src' import { toProvider, toTransactionHash } from '../../src'
describe('type coercion utils', () => { describe('type coercion utils', () => {
......
/* eslint-disable @typescript-eslint/no-empty-function */ /* eslint-disable @typescript-eslint/no-empty-function */
import { Signer } from 'ethers'
import { ethers } from 'hardhat'
import { expect } from '../setup' import { expect } from '../setup'
import { import {
getOEContract, getOEContract,
...@@ -6,8 +9,6 @@ import { ...@@ -6,8 +9,6 @@ import {
CONTRACT_ADDRESSES, CONTRACT_ADDRESSES,
DEFAULT_L2_CONTRACT_ADDRESSES, DEFAULT_L2_CONTRACT_ADDRESSES,
} from '../../src' } from '../../src'
import { Signer } from 'ethers'
import { ethers } from 'hardhat'
describe('contract connection utils', () => { describe('contract connection utils', () => {
let signers: Signer[] let signers: Signer[]
......
import { expect } from '../setup'
import { Contract, Signer } from 'ethers' import { Contract, Signer } from 'ethers'
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { getContractFactory } from '@eth-optimism/contracts' import { getContractFactory } from '@eth-optimism/contracts'
import { expect } from '../setup'
import { import {
CoreCrossChainMessage, CoreCrossChainMessage,
encodeCrossChainMessage, encodeCrossChainMessage,
......
...@@ -11523,15 +11523,15 @@ node-fetch@*: ...@@ -11523,15 +11523,15 @@ node-fetch@*:
data-uri-to-buffer "^3.0.1" data-uri-to-buffer "^3.0.1"
fetch-blob "^3.1.2" fetch-blob "^3.1.2"
node-fetch@2.6.1, node-fetch@^2.6.0, node-fetch@^2.6.1: node-fetch@2.6.1:
version "2.6.1" version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
node-fetch@2.6.5: node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.1:
version "2.6.5" version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.5.tgz#42735537d7f080a7e5f78b6c549b7146be1742fd" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ== integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies: dependencies:
whatwg-url "^5.0.0" whatwg-url "^5.0.0"
......
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