Commit 43e3bbe4 authored by Indeavr's avatar Indeavr Committed by GitHub

Merge branch 'ethereum-optimism:develop' into develop

parents 410e4094 ff5a87fc
---
'@eth-optimism/proxyd': patch
---
Force proxyd build
---
'@eth-optimism/l2geth': patch
---
expose ErrNonceTooHigh from miner
...@@ -119,8 +119,6 @@ func NewBatchSubmitter(cfg Config, gitVersion string) (*BatchSubmitter, error) { ...@@ -119,8 +119,6 @@ func NewBatchSubmitter(cfg Config, gitVersion string) (*BatchSubmitter, error) {
log.Root().SetHandler(log.LvlFilterHandler(logLevel, logHandler)) log.Root().SetHandler(log.LvlFilterHandler(logLevel, logHandler))
log.Info("Config", "config", fmt.Sprintf("%#v", cfg))
// Parse sequencer private key and CTC contract address. // Parse sequencer private key and CTC contract address.
sequencerPrivKey, ctcAddress, err := parseWalletPrivKeyAndContractAddr( sequencerPrivKey, ctcAddress, err := parseWalletPrivKeyAndContractAddr(
"Sequencer", cfg.Mnemonic, cfg.SequencerHDPath, "Sequencer", cfg.Mnemonic, cfg.SequencerHDPath,
...@@ -171,7 +169,7 @@ func NewBatchSubmitter(cfg Config, gitVersion string) (*BatchSubmitter, error) { ...@@ -171,7 +169,7 @@ func NewBatchSubmitter(cfg Config, gitVersion string) (*BatchSubmitter, error) {
var batchTxService *Service var batchTxService *Service
if cfg.RunTxBatchSubmitter { if cfg.RunTxBatchSubmitter {
batchTxDriver, err := sequencer.NewDriver(sequencer.Config{ batchTxDriver, err := sequencer.NewDriver(sequencer.Config{
Name: "SEQUENCER", Name: "Sequencer",
L1Client: l1Client, L1Client: l1Client,
L2Client: l2Client, L2Client: l2Client,
BlockOffset: cfg.BlockOffset, BlockOffset: cfg.BlockOffset,
...@@ -196,7 +194,7 @@ func NewBatchSubmitter(cfg Config, gitVersion string) (*BatchSubmitter, error) { ...@@ -196,7 +194,7 @@ func NewBatchSubmitter(cfg Config, gitVersion string) (*BatchSubmitter, error) {
var batchStateService *Service var batchStateService *Service
if cfg.RunStateBatchSubmitter { if cfg.RunStateBatchSubmitter {
batchStateDriver, err := proposer.NewDriver(proposer.Config{ batchStateDriver, err := proposer.NewDriver(proposer.Config{
Name: "PROPOSER", Name: "Proposer",
L1Client: l1Client, L1Client: l1Client,
L2Client: l2Client, L2Client: l2Client,
BlockOffset: cfg.BlockOffset, BlockOffset: cfg.BlockOffset,
......
...@@ -5,9 +5,11 @@ import ( ...@@ -5,9 +5,11 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"fmt" "fmt"
"math/big" "math/big"
"time"
"github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/ctc" "github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/ctc"
"github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/scc" "github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/scc"
"github.com/ethereum-optimism/optimism/go/batch-submitter/metrics"
l2types "github.com/ethereum-optimism/optimism/l2geth/core/types" l2types "github.com/ethereum-optimism/optimism/l2geth/core/types"
l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient" l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
...@@ -36,6 +38,7 @@ type Driver struct { ...@@ -36,6 +38,7 @@ type Driver struct {
sccContract *scc.StateCommitmentChain sccContract *scc.StateCommitmentChain
ctcContract *ctc.CanonicalTransactionChain ctcContract *ctc.CanonicalTransactionChain
walletAddr common.Address walletAddr common.Address
metrics *metrics.Metrics
} }
func NewDriver(cfg Config) (*Driver, error) { func NewDriver(cfg Config) (*Driver, error) {
...@@ -60,6 +63,7 @@ func NewDriver(cfg Config) (*Driver, error) { ...@@ -60,6 +63,7 @@ func NewDriver(cfg Config) (*Driver, error) {
sccContract: sccContract, sccContract: sccContract,
ctcContract: ctcContract, ctcContract: ctcContract,
walletAddr: walletAddr, walletAddr: walletAddr,
metrics: metrics.NewMetrics(cfg.Name),
}, nil }, nil
} }
...@@ -73,6 +77,11 @@ func (d *Driver) WalletAddr() common.Address { ...@@ -73,6 +77,11 @@ func (d *Driver) WalletAddr() common.Address {
return d.walletAddr return d.walletAddr
} }
// Metrics returns the subservice telemetry object.
func (d *Driver) Metrics() *metrics.Metrics {
return d.metrics
}
// GetBatchBlockRange returns the start and end L2 block heights that need to be // GetBatchBlockRange returns the start and end L2 block heights that need to be
// processed. Note that the end value is *exclusive*, therefore if the returned // processed. Note that the end value is *exclusive*, therefore if the returned
// values are identical nothing needs to be processed. // values are identical nothing needs to be processed.
...@@ -121,6 +130,8 @@ func (d *Driver) SubmitBatchTx( ...@@ -121,6 +130,8 @@ func (d *Driver) SubmitBatchTx(
ctx context.Context, ctx context.Context,
start, end, nonce, gasPrice *big.Int) (*types.Transaction, error) { start, end, nonce, gasPrice *big.Int) (*types.Transaction, error) {
batchTxBuildStart := time.Now()
var blocks []*l2types.Block var blocks []*l2types.Block
for i := new(big.Int).Set(start); i.Cmp(end) < 0; i.Add(i, bigOne) { for i := new(big.Int).Set(start); i.Cmp(end) < 0; i.Add(i, bigOne) {
block, err := d.cfg.L2Client.BlockByNumber(ctx, i) block, err := d.cfg.L2Client.BlockByNumber(ctx, i)
...@@ -139,6 +150,10 @@ func (d *Driver) SubmitBatchTx( ...@@ -139,6 +150,10 @@ func (d *Driver) SubmitBatchTx(
stateRoots = append(stateRoots, block.Root()) stateRoots = append(stateRoots, block.Root())
} }
batchTxBuildTime := float64(time.Since(batchTxBuildStart) / time.Millisecond)
d.metrics.BatchTxBuildTime.Set(batchTxBuildTime)
d.metrics.NumTxPerBatch.Observe(float64(len(blocks)))
opts, err := bind.NewKeyedTransactorWithChainID( opts, err := bind.NewKeyedTransactorWithChainID(
d.cfg.PrivKey, d.cfg.ChainID, d.cfg.PrivKey, d.cfg.ChainID,
) )
......
...@@ -7,8 +7,10 @@ import ( ...@@ -7,8 +7,10 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"strings" "strings"
"time"
"github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/ctc" "github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/ctc"
"github.com/ethereum-optimism/optimism/go/batch-submitter/metrics"
l2types "github.com/ethereum-optimism/optimism/l2geth/core/types" l2types "github.com/ethereum-optimism/optimism/l2geth/core/types"
l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient" l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
...@@ -43,6 +45,7 @@ type Driver struct { ...@@ -43,6 +45,7 @@ type Driver struct {
rawCtcContract *bind.BoundContract rawCtcContract *bind.BoundContract
walletAddr common.Address walletAddr common.Address
ctcABI *abi.ABI ctcABI *abi.ABI
metrics *metrics.Metrics
} }
func NewDriver(cfg Config) (*Driver, error) { func NewDriver(cfg Config) (*Driver, error) {
...@@ -78,6 +81,7 @@ func NewDriver(cfg Config) (*Driver, error) { ...@@ -78,6 +81,7 @@ func NewDriver(cfg Config) (*Driver, error) {
rawCtcContract: rawCtcContract, rawCtcContract: rawCtcContract,
walletAddr: walletAddr, walletAddr: walletAddr,
ctcABI: ctcABI, ctcABI: ctcABI,
metrics: metrics.NewMetrics(cfg.Name),
}, nil }, nil
} }
...@@ -91,6 +95,11 @@ func (d *Driver) WalletAddr() common.Address { ...@@ -91,6 +95,11 @@ func (d *Driver) WalletAddr() common.Address {
return d.walletAddr return d.walletAddr
} }
// Metrics returns the subservice telemetry object.
func (d *Driver) Metrics() *metrics.Metrics {
return d.metrics
}
// GetBatchBlockRange returns the start and end L2 block heights that need to be // GetBatchBlockRange returns the start and end L2 block heights that need to be
// processed. Note that the end value is *exclusive*, therefore if the returned // processed. Note that the end value is *exclusive*, therefore if the returned
// values are identical nothing needs to be processed. // values are identical nothing needs to be processed.
...@@ -136,6 +145,8 @@ func (d *Driver) SubmitBatchTx( ...@@ -136,6 +145,8 @@ func (d *Driver) SubmitBatchTx(
log.Info(name+" submitting batch tx", "start", start, "end", end, log.Info(name+" submitting batch tx", "start", start, "end", end,
"gasPrice", gasPrice) "gasPrice", gasPrice)
batchTxBuildStart := time.Now()
var blocks []*l2types.Block var blocks []*l2types.Block
for i := new(big.Int).Set(start); i.Cmp(end) < 0; i.Add(i, bigOne) { for i := new(big.Int).Set(start); i.Cmp(end) < 0; i.Add(i, bigOne) {
block, err := d.cfg.L2Client.BlockByNumber(ctx, i) block, err := d.cfg.L2Client.BlockByNumber(ctx, i)
...@@ -176,6 +187,11 @@ func (d *Driver) SubmitBatchTx( ...@@ -176,6 +187,11 @@ func (d *Driver) SubmitBatchTx(
panic("call data too large") panic("call data too large")
} }
// Record the batch_tx_build_time.
batchTxBuildTime := float64(time.Since(batchTxBuildStart) / time.Millisecond)
d.metrics.BatchTxBuildTime.Set(batchTxBuildTime)
d.metrics.NumTxPerBatch.Observe(float64(len(blocks)))
log.Info(name+" batch call data", "data", hex.EncodeToString(batchCallData)) log.Info(name+" batch call data", "data", hex.EncodeToString(batchCallData))
opts, err := bind.NewKeyedTransactorWithChainID( opts, err := bind.NewKeyedTransactorWithChainID(
......
package metrics
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
type Metrics struct {
// ETHBalance tracks the amount of ETH in the submitter's account.
ETHBalance prometheus.Gauge
// BatchSizeInBytes tracks the size of batch submission transactions.
BatchSizeInBytes prometheus.Histogram
// NumTxPerBatch tracks the number of L2 transactions in each batch
// submission.
NumTxPerBatch prometheus.Histogram
// SubmissionGasUsed tracks the amount of gas used to submit each batch.
SubmissionGasUsed prometheus.Histogram
// BatchsSubmitted tracks the total number of successful batch submissions.
BatchesSubmitted prometheus.Counter
// FailedSubmissions tracks the total number of failed batch submissions.
FailedSubmissions prometheus.Counter
// BatchTxBuildTime tracks the duration it takes to construct a batch
// transaction.
BatchTxBuildTime prometheus.Gauge
// BatchConfirmationTime tracks the duration it takes to confirm a batch
// transaction.
BatchConfirmationTime prometheus.Gauge
}
func NewMetrics(subsystem string) *Metrics {
return &Metrics{
ETHBalance: promauto.NewGauge(prometheus.GaugeOpts{
Name: "batch_submitter_eth_balance",
Help: "ETH balance of the batch submitter",
Subsystem: subsystem,
}),
BatchSizeInBytes: promauto.NewHistogram(prometheus.HistogramOpts{
Name: "batch_submitter_batch_size_in_bytes",
Help: "Size of batches in bytes",
Subsystem: subsystem,
}),
NumTxPerBatch: promauto.NewHistogram(prometheus.HistogramOpts{
Name: "batch_submitter_num_txs_per_batch",
Help: "Number of transaction in each batch",
Subsystem: subsystem,
}),
SubmissionGasUsed: promauto.NewHistogram(prometheus.HistogramOpts{
Name: "batch_submitter_submission_gas_used",
Help: "Gas used to submit each batch",
Subsystem: subsystem,
}),
BatchesSubmitted: promauto.NewCounter(prometheus.CounterOpts{
Name: "batch_submitter_batches_submitted",
Help: "Count of batches submitted",
Subsystem: subsystem,
}),
FailedSubmissions: promauto.NewCounter(prometheus.CounterOpts{
Name: "batch_submitter_failed_submissions",
Help: "Count of failed batch submissions",
Subsystem: subsystem,
}),
BatchTxBuildTime: promauto.NewGauge(prometheus.GaugeOpts{
Name: "batch_submitter_batch_tx_build_time",
Help: "Time to construct batch transactions",
Subsystem: subsystem,
}),
BatchConfirmationTime: promauto.NewGauge(prometheus.GaugeOpts{
Name: "batch_submitter_batch_confirmation_time",
Help: "Time to confirm batch transactions",
Subsystem: subsystem,
}),
}
}
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/ethereum-optimism/optimism/go/batch-submitter/metrics"
"github.com/ethereum-optimism/optimism/go/batch-submitter/txmgr" "github.com/ethereum-optimism/optimism/go/batch-submitter/txmgr"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
...@@ -13,6 +14,11 @@ import ( ...@@ -13,6 +14,11 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
var (
// weiToGwei is the conversion rate from wei to gwei.
weiToGwei = new(big.Float).SetFloat64(1e-18)
)
// Driver is an interface for creating and submitting batch transactions for a // Driver is an interface for creating and submitting batch transactions for a
// specific contract. // specific contract.
type Driver interface { type Driver interface {
...@@ -23,6 +29,9 @@ type Driver interface { ...@@ -23,6 +29,9 @@ type Driver interface {
// fees. // fees.
WalletAddr() common.Address WalletAddr() common.Address
// Metrics returns the subservice telemetry object.
Metrics() *metrics.Metrics
// GetBatchBlockRange returns the start and end L2 block heights that // GetBatchBlockRange returns the start and end L2 block heights that
// need to be processed. Note that the end value is *exclusive*, // need to be processed. Note that the end value is *exclusive*,
// therefore if the returned values are identical nothing needs to be // therefore if the returned values are identical nothing needs to be
...@@ -51,7 +60,8 @@ type Service struct { ...@@ -51,7 +60,8 @@ type Service struct {
ctx context.Context ctx context.Context
cancel func() cancel func()
txMgr txmgr.TxManager txMgr txmgr.TxManager
metrics *metrics.Metrics
wg sync.WaitGroup wg sync.WaitGroup
} }
...@@ -64,10 +74,11 @@ func NewService(cfg ServiceConfig) *Service { ...@@ -64,10 +74,11 @@ func NewService(cfg ServiceConfig) *Service {
) )
return &Service{ return &Service{
cfg: cfg, cfg: cfg,
ctx: ctx, ctx: ctx,
cancel: cancel, cancel: cancel,
txMgr: txMgr, txMgr: txMgr,
metrics: cfg.Driver.Metrics(),
} }
} }
...@@ -91,21 +102,35 @@ func (s *Service) eventLoop() { ...@@ -91,21 +102,35 @@ func (s *Service) eventLoop() {
for { for {
select { select {
case <-time.After(s.cfg.PollInterval): case <-time.After(s.cfg.PollInterval):
log.Info(name + " fetching current block range") // Record the submitter's current ETH balance. This is done first in
// case any of the remaining steps fail, we can at least have an
// accurate view of the submitter's balance.
balance, err := s.cfg.L1Client.BalanceAt(
s.ctx, s.cfg.Driver.WalletAddr(), nil,
)
if err != nil {
log.Error(name+" unable to get current balance", "err", err)
continue
}
s.metrics.ETHBalance.Set(weiToGwei64(balance))
// Determine the range of L2 blocks that the batch submitter has not
// processed, and needs to take action on.
log.Info(name + " fetching current block range")
start, end, err := s.cfg.Driver.GetBatchBlockRange(s.ctx) start, end, err := s.cfg.Driver.GetBatchBlockRange(s.ctx)
if err != nil { if err != nil {
log.Error(name+" unable to get block range", "err", err) log.Error(name+" unable to get block range", "err", err)
continue continue
} }
log.Info(name+" block range", "start", start, "end", end)
// No new updates. // No new updates.
if start.Cmp(end) == 0 { if start.Cmp(end) == 0 {
log.Info(name+" no updates", "start", start, "end", end)
continue continue
} }
log.Info(name+" block range", "start", start, "end", end)
// Query for the submitter's current nonce.
nonce64, err := s.cfg.L1Client.NonceAt( nonce64, err := s.cfg.L1Client.NonceAt(
s.ctx, s.cfg.Driver.WalletAddr(), nil, s.ctx, s.cfg.Driver.WalletAddr(), nil,
) )
...@@ -116,6 +141,8 @@ func (s *Service) eventLoop() { ...@@ -116,6 +141,8 @@ func (s *Service) eventLoop() {
} }
nonce := new(big.Int).SetUint64(nonce64) nonce := new(big.Int).SetUint64(nonce64)
// Construct the transaction submission clousure that will attempt
// to send the next transaction at the given nonce and gas price.
sendTx := func( sendTx := func(
ctx context.Context, ctx context.Context,
gasPrice *big.Int, gasPrice *big.Int,
...@@ -123,20 +150,38 @@ func (s *Service) eventLoop() { ...@@ -123,20 +150,38 @@ func (s *Service) eventLoop() {
log.Info(name+" attempting batch tx", "start", start, log.Info(name+" attempting batch tx", "start", start,
"end", end, "nonce", nonce, "end", end, "nonce", nonce,
"gasPrice", gasPrice) "gasPrice", gasPrice)
return s.cfg.Driver.SubmitBatchTx(
tx, err := s.cfg.Driver.SubmitBatchTx(
ctx, start, end, nonce, gasPrice, ctx, start, end, nonce, gasPrice,
) )
if err != nil {
return nil, err
}
s.metrics.BatchSizeInBytes.Observe(float64(tx.Size()))
return tx, nil
} }
// Wait until one of our submitted transactions confirms. If no
// receipt is received it's likely our gas price was too low.
batchConfirmationStart := time.Now()
receipt, err := s.txMgr.Send(s.ctx, sendTx) receipt, err := s.txMgr.Send(s.ctx, sendTx)
if err != nil { if err != nil {
log.Error(name+" unable to publish batch tx", log.Error(name+" unable to publish batch tx",
"err", err) "err", err)
s.metrics.FailedSubmissions.Inc()
continue continue
} }
// The transaction was successfully submitted.
log.Info(name+" batch tx successfully published", log.Info(name+" batch tx successfully published",
"tx_hash", receipt.TxHash) "tx_hash", receipt.TxHash)
batchConfirmationTime := time.Since(batchConfirmationStart) /
time.Millisecond
s.metrics.BatchConfirmationTime.Set(float64(batchConfirmationTime))
s.metrics.BatchesSubmitted.Inc()
s.metrics.SubmissionGasUsed.Observe(float64(receipt.GasUsed))
case err := <-s.ctx.Done(): case err := <-s.ctx.Done():
log.Error(name+" service shutting down", "err", err) log.Error(name+" service shutting down", "err", err)
...@@ -144,3 +189,10 @@ func (s *Service) eventLoop() { ...@@ -144,3 +189,10 @@ func (s *Service) eventLoop() {
} }
} }
} }
func weiToGwei64(wei *big.Int) float64 {
gwei := new(big.Float).SetInt(wei)
gwei.Mul(gwei, weiToGwei)
gwei64, _ := gwei.Float64()
return gwei64
}
# @eth-optimism/proxyd # @eth-optimism/proxyd
## 3.4.1
### Patch Changes
- 415164e1: Force proxyd build
## 3.4.0 ## 3.4.0
### Minor Changes ### Minor Changes
......
{ {
"name": "@eth-optimism/proxyd", "name": "@eth-optimism/proxyd",
"version": "3.4.0", "version": "3.4.1",
"private": true, "private": true,
"dependencies": {} "dependencies": {}
} }
# Changelog # Changelog
## 0.5.5
### Patch Changes
- 2924845d: expose ErrNonceTooHigh from miner
## 0.5.4 ## 0.5.4
### Patch Changes ### Patch Changes
......
{ {
"name": "@eth-optimism/l2geth", "name": "@eth-optimism/l2geth",
"version": "0.5.4", "version": "0.5.5",
"private": true, "private": true,
"devDependencies": {} "devDependencies": {}
} }
/* eslint-disable @typescript-eslint/no-unused-vars */
import {
Provider,
BlockTag,
TransactionReceipt,
} from '@ethersproject/abstract-provider'
import { BigNumber } from 'ethers'
import {
ICrossChainProvider,
OEContracts,
OEContractsLike,
MessageLike,
TransactionLike,
AddressLike,
NumberLike,
ProviderLike,
CrossChainMessage,
MessageDirection,
MessageStatus,
TokenBridgeMessage,
MessageReceipt,
} from './interfaces'
import {
toProvider,
toBigNumber,
toTransactionHash,
DeepPartial,
getAllOEContracts,
} from './utils'
export class CrossChainProvider implements ICrossChainProvider {
public l1Provider: Provider
public l2Provider: Provider
public l1ChainId: number
public contracts: OEContracts
/**
* Creates a new CrossChainProvider instance.
*
* @param opts Options for the provider.
* @param opts.l1Provider Provider for the L1 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.contracts Optional contract address overrides.
*/
constructor(opts: {
l1Provider: ProviderLike
l2Provider: ProviderLike
l1ChainId: NumberLike
contracts?: DeepPartial<OEContractsLike>
}) {
this.l1Provider = toProvider(opts.l1Provider)
this.l2Provider = toProvider(opts.l2Provider)
this.l1ChainId = toBigNumber(opts.l1ChainId).toNumber()
this.contracts = getAllOEContracts(this.l1ChainId, {
l1SignerOrProvider: this.l1Provider,
l2SignerOrProvider: this.l2Provider,
overrides: opts.contracts,
})
}
public async getMessagesByTransaction(
transaction: TransactionLike,
opts: {
direction?: MessageDirection
} = {}
): Promise<CrossChainMessage[]> {
const txHash = toTransactionHash(transaction)
let receipt: TransactionReceipt
if (opts.direction !== undefined) {
// Get the receipt for the requested direction.
if (opts.direction === MessageDirection.L1_TO_L2) {
receipt = await this.l1Provider.getTransactionReceipt(txHash)
} else {
receipt = await this.l2Provider.getTransactionReceipt(txHash)
}
} else {
// Try both directions, starting with L1 => L2.
receipt = await this.l1Provider.getTransactionReceipt(txHash)
if (receipt) {
opts.direction = MessageDirection.L1_TO_L2
} else {
receipt = await this.l2Provider.getTransactionReceipt(txHash)
opts.direction = MessageDirection.L2_TO_L1
}
}
if (!receipt) {
throw new Error(`unable to find transaction receipt for ${txHash}`)
}
// By this point opts.direction will always be defined.
const messenger =
opts.direction === MessageDirection.L1_TO_L2
? this.contracts.l1.L1CrossDomainMessenger
: this.contracts.l2.L2CrossDomainMessenger
return receipt.logs
.filter((log) => {
// Only look at logs emitted by the messenger address
return log.address === messenger.address
})
.filter((log) => {
// Only look at SentMessage logs specifically
const parsed = messenger.interface.parseLog(log)
return parsed.name === 'SentMessage'
})
.map((log) => {
// Convert each SentMessage log into a message object
const parsed = messenger.interface.parseLog(log)
return {
direction: opts.direction,
target: parsed.args.target,
sender: parsed.args.sender,
message: parsed.args.message,
messageNonce: parsed.args.messageNonce,
}
})
}
public async getMessagesByAddress(
address: AddressLike,
opts?: {
direction?: MessageDirection
fromBlock?: NumberLike
toBlock?: NumberLike
}
): Promise<CrossChainMessage[]> {
throw new Error('Not implemented')
}
public async getTokenBridgeMessagesByAddress(
address: AddressLike,
opts?: {
direction?: MessageDirection
fromBlock?: BlockTag
toBlock?: BlockTag
}
): Promise<TokenBridgeMessage[]> {
throw new Error('Not implemented')
}
public async getMessageStatus(message: MessageLike): Promise<MessageStatus> {
throw new Error('Not implemented')
}
public async getMessageReceipt(
message: MessageLike
): Promise<MessageReceipt> {
throw new Error('Not implemented')
}
public async waitForMessageReciept(
message: MessageLike,
opts?: {
confirmations?: number
pollIntervalMs?: number
timeoutMs?: number
}
): Promise<MessageReceipt> {
throw new Error('Not implemented')
}
public async estimateL2MessageGasLimit(
message: MessageLike
): Promise<BigNumber> {
throw new Error('Not implemented')
}
public async estimateMessageWaitTimeSeconds(
message: MessageLike
): Promise<number> {
throw new Error('Not implemented')
}
public async estimateMessageWaitTimeBlocks(
message: MessageLike
): Promise<number> {
throw new Error('Not implemented')
}
}
export * from './interfaces' export * from './interfaces'
export * from './utils' export * from './utils'
export * from './cross-chain-provider'
...@@ -33,11 +33,6 @@ export interface ICrossChainProvider { ...@@ -33,11 +33,6 @@ export interface ICrossChainProvider {
*/ */
l1ChainId: number l1ChainId: number
/**
* Chain ID for the L2 network.
*/
l2ChainId: number
/** /**
* Contract objects attached to their respective providers and addresses. * Contract objects attached to their respective providers and addresses.
*/ */
...@@ -129,9 +124,9 @@ export interface ICrossChainProvider { ...@@ -129,9 +124,9 @@ export interface ICrossChainProvider {
* *
* @param message Message to wait for. * @param message Message to wait for.
* @param opts Options to pass to the waiting function. * @param opts Options to pass to the waiting function.
* - `confirmations` (number): Number of transaction confirmations to wait for before returning. * @param opts.confirmations Number of transaction confirmations to wait for before returning.
* - `pollIntervalMs` (number): Number of milliseconds to wait between polling for the receipt. * @param opts.pollIntervalMs Number of milliseconds to wait between polling for the receipt.
* - `loopsBeforeTimeout` (number): Number of times to poll before timing out. * @param opts.timeoutMs Milliseconds to wait before timing out.
* @returns CrossChainMessage receipt including receipt of the transaction that relayed the * @returns CrossChainMessage receipt including receipt of the transaction that relayed the
* given message. * given message.
*/ */
...@@ -140,7 +135,7 @@ export interface ICrossChainProvider { ...@@ -140,7 +135,7 @@ export interface ICrossChainProvider {
opts?: { opts?: {
confirmations?: number confirmations?: number
pollIntervalMs?: number pollIntervalMs?: number
loopsBeforeTimeout?: number timeoutMs?: number
} }
): Promise<MessageReceipt> ): Promise<MessageReceipt>
......
...@@ -6,37 +6,65 @@ import { ...@@ -6,37 +6,65 @@ import {
import { Signer } from '@ethersproject/abstract-signer' import { Signer } from '@ethersproject/abstract-signer'
import { Contract, BigNumber, Overrides } from 'ethers' import { Contract, BigNumber, Overrides } from 'ethers'
/**
* L1 contract references.
*/
export interface OEL1Contracts {
AddressManager: Contract
L1CrossDomainMessenger: Contract
L1StandardBridge: Contract
StateCommitmentChain: Contract
CanonicalTransactionChain: Contract
BondManager: Contract
}
/**
* L2 contract references.
*/
export interface OEL2Contracts {
L2CrossDomainMessenger: Contract
L2StandardBridge: Contract
OVM_L1BlockNumber: Contract
OVM_L2ToL1MessagePasser: Contract
OVM_DeployerWhitelist: Contract
OVM_ETH: Contract
OVM_GasPriceOracle: Contract
OVM_SequencerFeeVault: Contract
WETH: Contract
}
/** /**
* Represents Optimistic Ethereum contracts, assumed to be connected to their appropriate * Represents Optimistic Ethereum contracts, assumed to be connected to their appropriate
* providers and addresses. * providers and addresses.
*/ */
export interface OEContracts { export interface OEContracts {
/** l1: OEL1Contracts
* L1 contract references. l2: OEL2Contracts
*/ }
l1: {
AddressManager: Contract
L1CrossDomainMessenger: Contract
L1StandardBridge: Contract
StateCommitmentChain: Contract
CanonicalTransactionChain: Contract
BondManager: Contract
}
/** /**
* L2 contract references. * Convenience type for something that looks like the L1 OE contract interface but could be
*/ * addresses instead of actual contract objects.
l2: { */
L2CrossDomainMessenger: Contract export type OEL1ContractsLike = {
L2StandardBridge: Contract [K in keyof OEL1Contracts]: AddressLike
OVM_L1BlockNumber: Contract }
OVM_L2ToL1MessagePasser: Contract
OVM_DeployerWhitelist: Contract /**
OVM_ETH: Contract * Convenience type for something that looks like the L2 OE contract interface but could be
OVM_GasPriceOracle: Contract * addresses instead of actual contract objects.
OVM_SequencerFeeVault: Contract */
WETH: Contract export type OEL2ContractsLike = {
} [K in keyof OEL2Contracts]: AddressLike
}
/**
* Convenience type for something that looks like the OE contract interface but could be
* addresses instead of actual contract objects.
*/
export interface OEContractsLike {
l1: OEL1ContractsLike
l2: OEL2ContractsLike
} }
/** /**
...@@ -164,13 +192,6 @@ export interface StateRootBatch { ...@@ -164,13 +192,6 @@ export interface StateRootBatch {
stateRoots: string[] stateRoots: string[]
} }
/**
* Utility type for deep partials.
*/
export type DeepPartial<T> = {
[P in keyof T]?: DeepPartial<T[P]>
}
/** /**
* Extended Ethers overrides object with an l2GasLimit field. * Extended Ethers overrides object with an l2GasLimit field.
* Only meant to be used for L1 to L2 messages, since L2 to L1 messages don't have a specified gas * Only meant to be used for L1 to L2 messages, since L2 to L1 messages don't have a specified gas
......
...@@ -4,46 +4,13 @@ import { ...@@ -4,46 +4,13 @@ import {
TransactionReceipt, TransactionReceipt,
TransactionResponse, TransactionResponse,
} from '@ethersproject/abstract-provider' } from '@ethersproject/abstract-provider'
import { getContractInterface } from '@eth-optimism/contracts' import { ethers, BigNumber } from 'ethers'
import { ethers } from 'ethers'
import { import {
ProviderLike, ProviderLike,
TransactionLike, TransactionLike,
DirectionlessCrossChainMessage, NumberLike,
} from './interfaces' AddressLike,
} from '../interfaces'
/**
* Returns the canonical encoding of a cross chain message. This encoding is used in various
* locations within the Optimistic Ethereum smart contracts.
*
* @param message Cross chain message to encode.
* @returns Canonical encoding of the message.
*/
export const encodeCrossChainMessage = (
message: DirectionlessCrossChainMessage
): string => {
return getContractInterface('L2CrossDomainMessenger').encodeFunctionData(
'relayMessage',
[message.target, message.sender, message.message, message.messageNonce]
)
}
/**
* Returns the canonical hash of a cross chain message. This hash is used in various locations
* within the Optimistic Ethereum smart contracts and is the keccak256 hash of the result of
* encodeCrossChainMessage.
*
* @param message Cross chain message to hash.
* @returns Canonical hash of the message.
*/
export const hashCrossChainMessage = (
message: DirectionlessCrossChainMessage
): string => {
return ethers.utils.solidityKeccak256(
['bytes'],
[encodeCrossChainMessage(message)]
)
}
/** /**
* Converts a ProviderLike into a provider. Assumes that if the ProviderLike is a string then * Converts a ProviderLike into a provider. Assumes that if the ProviderLike is a string then
...@@ -84,3 +51,29 @@ export const toTransactionHash = (transaction: TransactionLike): string => { ...@@ -84,3 +51,29 @@ export const toTransactionHash = (transaction: TransactionLike): string => {
throw new Error('Invalid transaction') throw new Error('Invalid transaction')
} }
} }
/**
* Converts a number-like into an ethers BigNumber.
*
* @param num Number-like to convert into a BigNumber.
* @returns Number-like as a BigNumber.
*/
export const toBigNumber = (num: NumberLike): BigNumber => {
return ethers.BigNumber.from(num)
}
/**
* Converts an address-like into a 0x-prefixed address string.
*
* @param addr Address-like to convert into an address.
* @returns Address-like as an address.
*/
export const toAddress = (addr: AddressLike): string => {
if (typeof addr === 'string') {
assert(ethers.utils.isAddress(addr), 'Invalid address')
return ethers.utils.getAddress(addr)
} else {
assert(ethers.utils.isAddress(addr.address), 'Invalid address')
return ethers.utils.getAddress(addr.address)
}
}
import { getContractInterface, predeploys } from '@eth-optimism/contracts'
import { ethers, Contract } from 'ethers'
import {
OEContracts,
OEL1Contracts,
OEL2Contracts,
OEContractsLike,
OEL2ContractsLike,
AddressLike,
} from '../interfaces'
import { toAddress } from './coercion'
import { DeepPartial } from './type-utils'
/**
* Full list of default L2 contract addresses.
*/
export const DEFAULT_L2_CONTRACT_ADDRESSES: OEL2ContractsLike = {
L2CrossDomainMessenger: predeploys.L2CrossDomainMessenger,
L2StandardBridge: predeploys.L2StandardBridge,
OVM_L1BlockNumber: predeploys.OVM_L1BlockNumber,
OVM_L2ToL1MessagePasser: predeploys.OVM_L2ToL1MessagePasser,
OVM_DeployerWhitelist: predeploys.OVM_DeployerWhitelist,
OVM_ETH: predeploys.OVM_ETH,
OVM_GasPriceOracle: predeploys.OVM_GasPriceOracle,
OVM_SequencerFeeVault: predeploys.OVM_SequencerFeeVault,
WETH: predeploys.WETH9,
}
/**
* We've changed some contract names in this SDK to be a bit nicer. Here we remap these nicer names
* back to the original contract names so we can look them up.
*/
const NAME_REMAPPING = {
AddressManager: 'Lib_AddressManager',
OVM_L1BlockNumber: 'iOVM_L1BlockNumber',
WETH: 'WETH9',
}
/**
* Mapping of L1 chain IDs to the appropriate contract addresses for the OE deployments to the
* given network. Simplifies the process of getting the correct contract addresses for a given
* contract name.
*/
export const CONTRACT_ADDRESSES: {
[l1ChainId: number]: OEContractsLike
} = {
// Mainnet
1: {
l1: {
AddressManager: '0xdE1FCfB0851916CA5101820A69b13a4E276bd81F',
L1CrossDomainMessenger: '0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1',
L1StandardBridge: '0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1',
StateCommitmentChain: '0xBe5dAb4A2e9cd0F27300dB4aB94BeE3A233AEB19',
CanonicalTransactionChain: '0x5E4e65926BA27467555EB562121fac00D24E9dD2',
BondManager: '0xcd626E1328b41fCF24737F137BcD4CE0c32bc8d1',
},
l2: DEFAULT_L2_CONTRACT_ADDRESSES,
},
// Kovan
42: {
l1: {
AddressManager: '0x100Dd3b414Df5BbA2B542864fF94aF8024aFdf3a',
L1CrossDomainMessenger: '0x4361d0F75A0186C05f971c566dC6bEa5957483fD',
L1StandardBridge: '0x22F24361D548e5FaAfb36d1437839f080363982B',
StateCommitmentChain: '0xD7754711773489F31A0602635f3F167826ce53C5',
CanonicalTransactionChain: '0xf7B88A133202d41Fe5E2Ab22e6309a1A4D50AF74',
BondManager: '0xc5a603d273E28185c18Ba4d26A0024B2d2F42740',
},
l2: DEFAULT_L2_CONTRACT_ADDRESSES,
},
// Goerli
5: {
l1: {
AddressManager: '0x2F7E3cAC91b5148d336BbffB224B4dC79F09f01D',
L1CrossDomainMessenger: '0xEcC89b9EDD804850C4F343A278Be902be11AaF42',
L1StandardBridge: '0x73298186A143a54c20ae98EEE5a025bD5979De02',
StateCommitmentChain: '0x1afcA918eff169eE20fF8AB6Be75f3E872eE1C1A',
CanonicalTransactionChain: '0x2ebA8c4EfDB39A8Cd8f9eD65c50ec079f7CEBD81',
BondManager: '0xE5AE60bD6F8DEe4D0c2BC9268e23B92F1cacC58F',
},
l2: DEFAULT_L2_CONTRACT_ADDRESSES,
},
// Hardhat local
// TODO: Get the actual addresses for this, temporary addresses here are fine for now until we
// start using this package in the integration tests.
31337: {
l1: {
AddressManager: '0x2F7E3cAC91b5148d336BbffB224B4dC79F09f01D',
L1CrossDomainMessenger: '0xEcC89b9EDD804850C4F343A278Be902be11AaF42',
L1StandardBridge: '0x73298186A143a54c20ae98EEE5a025bD5979De02',
StateCommitmentChain: '0x1afcA918eff169eE20fF8AB6Be75f3E872eE1C1A',
CanonicalTransactionChain: '0x2ebA8c4EfDB39A8Cd8f9eD65c50ec079f7CEBD81',
BondManager: '0xE5AE60bD6F8DEe4D0c2BC9268e23B92F1cacC58F',
},
l2: DEFAULT_L2_CONTRACT_ADDRESSES,
},
}
/**
* Returns an ethers.Contract object for the given name, connected to the appropriate address for
* the given L1 chain ID. Users can also provide a custom address to connect the contract to
* instead. If the chain ID is not known then the user MUST provide a custom address or this
* function will throw an error.
*
* @param contractName Name of the contract to connect to.
* @param l1ChainId Chain ID for the L1 network where the OE contracts are deployed.
* @param opts Additional options for connecting to the contract.
* @param opts.address Custom address to connect to the contract.
* @param opts.signerOrProvider Signer or provider to connect to the contract.
* @returns An ethers.Contract object connected to the appropriate address and interface.
*/
export const getOEContract = (
contractName: keyof OEL1Contracts | keyof OEL2Contracts,
l1ChainId: number,
opts: {
address?: AddressLike
signerOrProvider?: ethers.Signer | ethers.providers.Provider
} = {}
): Contract => {
const addresses = CONTRACT_ADDRESSES[l1ChainId]
if (addresses === undefined && opts.address === undefined) {
throw new Error(
`cannot get contract ${contractName} for unknown L1 chain ID ${l1ChainId}, you must provide an address`
)
}
return new Contract(
toAddress(
opts.address || addresses.l1[contractName] || addresses.l2[contractName]
),
getContractInterface(NAME_REMAPPING[contractName] || contractName),
opts.signerOrProvider
)
}
/**
* Automatically connects to all contract addresses, both L1 and L2, for the given L1 chain ID. The
* user can provide custom contract address overrides for L1 or L2 contracts. If the given chain ID
* is not known then the user MUST provide custom contract addresses for ALL L1 contracts or this
* function will throw an error.
*
* @param l1ChainId Chain ID for the L1 network where the OE contracts are deployed.
* @param opts Additional options for connecting to the contracts.
* @param opts.l1SignerOrProvider: Signer or provider to connect to the L1 contracts.
* @param opts.l2SignerOrProvider: Signer or provider to connect to the L2 contracts.
* @param opts.overrides Custom contract address overrides for L1 or L2 contracts.
* @returns An object containing ethers.Contract objects connected to the appropriate addresses on
* both L1 and L2.
*/
export const getAllOEContracts = (
l1ChainId: number,
opts: {
l1SignerOrProvider?: ethers.Signer | ethers.providers.Provider
l2SignerOrProvider?: ethers.Signer | ethers.providers.Provider
overrides?: DeepPartial<OEContractsLike>
} = {}
): OEContracts => {
const addresses = CONTRACT_ADDRESSES[l1ChainId] || {
l1: {
AddressManager: undefined,
L1CrossDomainMessenger: undefined,
L1StandardBridge: undefined,
StateCommitmentChain: undefined,
CanonicalTransactionChain: undefined,
BondManager: undefined,
},
l2: DEFAULT_L2_CONTRACT_ADDRESSES,
}
// Attach all L1 contracts.
const l1Contracts: OEL1Contracts = {} as any
for (const [contractName, contractAddress] of Object.entries(addresses.l1)) {
l1Contracts[contractName] = getOEContract(contractName as any, l1ChainId, {
address: opts.overrides?.l1?.[contractName] || contractAddress,
signerOrProvider: opts.l1SignerOrProvider,
})
}
// Attach all L2 contracts.
const l2Contracts: OEL2Contracts = {} as any
for (const [contractName, contractAddress] of Object.entries(addresses.l2)) {
l2Contracts[contractName] = getOEContract(contractName as any, l1ChainId, {
address: opts.overrides?.l2?.[contractName] || contractAddress,
signerOrProvider: opts.l2SignerOrProvider,
})
}
return {
l1: l1Contracts,
l2: l2Contracts,
}
}
export * from './coercion'
export * from './contracts'
export * from './message-encoding'
export * from './type-utils'
import { getContractInterface } from '@eth-optimism/contracts'
import { ethers } from 'ethers'
import { DirectionlessCrossChainMessage } from '../interfaces'
/**
* Returns the canonical encoding of a cross chain message. This encoding is used in various
* locations within the Optimistic Ethereum smart contracts.
*
* @param message Cross chain message to encode.
* @returns Canonical encoding of the message.
*/
export const encodeCrossChainMessage = (
message: DirectionlessCrossChainMessage
): string => {
return getContractInterface('L2CrossDomainMessenger').encodeFunctionData(
'relayMessage',
[message.target, message.sender, message.message, message.messageNonce]
)
}
/**
* Returns the canonical hash of a cross chain message. This hash is used in various locations
* within the Optimistic Ethereum smart contracts and is the keccak256 hash of the result of
* encodeCrossChainMessage.
*
* @param message Cross chain message to hash.
* @returns Canonical hash of the message.
*/
export const hashCrossChainMessage = (
message: DirectionlessCrossChainMessage
): string => {
return ethers.utils.solidityKeccak256(
['bytes'],
[encodeCrossChainMessage(message)]
)
}
/**
* Utility type for deep partials.
*/
export type DeepPartial<T> = {
[P in keyof T]?: DeepPartial<T[P]>
}
pragma solidity ^0.8.9;
// Right now this is copy/pasted from the contracts package. We need to do this because we don't
// currently copy the contracts into the root of the contracts package in the correct way until
// we bundle the contracts package for publication. As a result, we can't properly use the
// package the way we want to from inside the monorepo (yet). Needs to be fixed as part of a
// separate pull request.
interface ICrossDomainMessenger {
/**********
* Events *
**********/
event SentMessage(
address indexed target,
address sender,
bytes message,
uint256 messageNonce,
uint256 gasLimit
);
event RelayedMessage(bytes32 indexed msgHash);
event FailedRelayedMessage(bytes32 indexed msgHash);
/*************
* Variables *
*************/
function xDomainMessageSender() external view returns (address);
/********************
* Public Functions *
********************/
/**
* Sends a cross domain message to the target messenger.
* @param _target Target contract address.
* @param _message Message to send to the target.
* @param _gasLimit Gas limit for the provided message.
*/
function sendMessage(
address _target,
bytes calldata _message,
uint32 _gasLimit
) external;
}
pragma solidity ^0.8.9;
import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol";
contract MockMessenger is ICrossDomainMessenger {
function xDomainMessageSender() public view returns (address) {
return address(0);
}
// Empty function to satisfy the interface.
function sendMessage(
address _target,
bytes calldata _message,
uint32 _gasLimit
) public {
return;
}
struct SentMessageEventParams {
address target;
address sender;
bytes message;
uint256 messageNonce;
uint256 gasLimit;
}
function doNothing() public {
return;
}
function triggerSentMessageEvents(
SentMessageEventParams[] memory _params
) public {
for (uint256 i = 0; i < _params.length; i++) {
emit SentMessage(
_params[i].target,
_params[i].sender,
_params[i].message,
_params[i].messageNonce,
_params[i].gasLimit
);
}
}
function triggerRelayedMessageEvents(
bytes32[] memory _params
) public {
for (uint256 i = 0; i < _params.length; i++) {
emit RelayedMessage(_params[i]);
}
}
function triggerFailedRelayedMessageEvents(
bytes32[] memory _params
) public {
for (uint256 i = 0; i < _params.length; i++) {
emit FailedRelayedMessage(_params[i]);
}
}
}
import { expect } from './setup' import { expect } from '../setup'
import { Provider } from '@ethersproject/abstract-provider' import { Provider } from '@ethersproject/abstract-provider'
import { Contract, Signer } from 'ethers' import { Contract } from 'ethers'
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { getContractFactory } from '@eth-optimism/contracts' import { toProvider, toTransactionHash } from '../../src'
import {
toProvider,
toTransactionHash,
CrossChainMessage,
MessageDirection,
encodeCrossChainMessage,
hashCrossChainMessage,
} from '../src'
describe('utils', () => {
let signers: Signer[]
before(async () => {
signers = (await ethers.getSigners()) as any
})
describe('encodeCrossChainMessage', () => {
let Lib_CrossDomainUtils: Contract
before(async () => {
Lib_CrossDomainUtils = (await getContractFactory(
'TestLib_CrossDomainUtils',
signers[0]
).deploy()) as any
})
it('should properly encode a message', async () => {
const message: CrossChainMessage = {
direction: MessageDirection.L1_TO_L2,
target: '0x' + '11'.repeat(20),
sender: '0x' + '22'.repeat(20),
message: '0x' + '1234'.repeat(32),
messageNonce: 1234,
}
const actual = encodeCrossChainMessage(message)
const expected = await Lib_CrossDomainUtils.encodeXDomainCalldata(
message.target,
message.sender,
message.message,
message.messageNonce
)
expect(actual).to.equal(expected)
})
})
describe('hashCrossChainMessage', () => {
let MessageEncodingHelper: Contract
before(async () => {
MessageEncodingHelper = (await (
await ethers.getContractFactory('MessageEncodingHelper')
).deploy()) as any
})
it('should properly hash a message', async () => {
const message: CrossChainMessage = {
direction: MessageDirection.L1_TO_L2,
target: '0x' + '11'.repeat(20),
sender: '0x' + '22'.repeat(20),
message: '0x' + '1234'.repeat(32),
messageNonce: 1234,
}
const actual = hashCrossChainMessage(message)
const expected = await MessageEncodingHelper.hashXDomainCalldata(
message.target,
message.sender,
message.message,
message.messageNonce
)
expect(actual).to.equal(expected)
})
})
describe('type coercion utils', () => {
describe('toProvider', () => { describe('toProvider', () => {
it('should convert a string to a JsonRpcProvider', () => { it('should convert a string to a JsonRpcProvider', () => {
const provider = toProvider('http://localhost:8545') const provider = toProvider('http://localhost:8545')
......
This diff is collapsed.
import { expect } from '../setup'
import { Contract, Signer } from 'ethers'
import { ethers } from 'hardhat'
import { getContractFactory } from '@eth-optimism/contracts'
import {
CrossChainMessage,
MessageDirection,
encodeCrossChainMessage,
hashCrossChainMessage,
} from '../../src'
describe('message encoding utils', () => {
let signers: Signer[]
before(async () => {
signers = (await ethers.getSigners()) as any
})
describe('encodeCrossChainMessage', () => {
let Lib_CrossDomainUtils: Contract
before(async () => {
Lib_CrossDomainUtils = (await getContractFactory(
'TestLib_CrossDomainUtils',
signers[0]
).deploy()) as any
})
it('should properly encode a message', async () => {
const message: CrossChainMessage = {
direction: MessageDirection.L1_TO_L2,
target: '0x' + '11'.repeat(20),
sender: '0x' + '22'.repeat(20),
message: '0x' + '1234'.repeat(32),
messageNonce: 1234,
}
const actual = encodeCrossChainMessage(message)
const expected = await Lib_CrossDomainUtils.encodeXDomainCalldata(
message.target,
message.sender,
message.message,
message.messageNonce
)
expect(actual).to.equal(expected)
})
})
describe('hashCrossChainMessage', () => {
let MessageEncodingHelper: Contract
before(async () => {
MessageEncodingHelper = (await (
await ethers.getContractFactory('MessageEncodingHelper')
).deploy()) as any
})
it('should properly hash a message', async () => {
const message: CrossChainMessage = {
direction: MessageDirection.L1_TO_L2,
target: '0x' + '11'.repeat(20),
sender: '0x' + '22'.repeat(20),
message: '0x' + '1234'.repeat(32),
messageNonce: 1234,
}
const actual = hashCrossChainMessage(message)
const expected = await MessageEncodingHelper.hashXDomainCalldata(
message.target,
message.sender,
message.message,
message.messageNonce
)
expect(actual).to.equal(expected)
})
})
})
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