diff --git a/README.md b/README.md
index 849d47ccb7..886cde3a76 100644
--- a/README.md
+++ b/README.md
@@ -75,7 +75,7 @@ as well as a docker-compose file for bringing up local testnets easily
* [`contracts`](./packages/boba/contracts): Solidity smart contracts implementing the fast bridges, the DAO, etc.
* [`gas-price-oracle`](./packages/boba/gas-price-oracle): A custom gas price oracle
* [`gateway`](./packages/boba/gateway): The Boba Web gateway
- * [`message-relayer-fast`](./packages/boba/message-relayer-fast): A fast message relayer without a 7 day delay
+ * [`message-relayer-fast`](./packages/message-relayer): A batch message relayer that can be run for the fast mode without a 7 day delay
* [`register`](./packages/boba/register): Code for registering addresses in the AddressManager
* [`subgraph`](./packages/boba/subgraph): Subgraphs for indexing the **StandardBridge** and **LiquidityPool** contracts
diff --git a/ops/docker-compose.yml b/ops/docker-compose.yml
index 1de68109a9..4c27572446 100644
--- a/ops/docker-compose.yml
+++ b/ops/docker-compose.yml
@@ -293,7 +293,8 @@ services:
image: bobanetwork/boba_message-relayer-fast:latest
build:
context: ..
- dockerfile: ./ops/docker/Dockerfile.boba_message-relayer-fast
+ dockerfile: ./ops/docker/Dockerfile.message-relayer
+ entrypoint: ["./wait-for-l1-and-l2.sh", "./relayer-fast.sh"]
environment:
L1_NODE_WEB3_URL: http://l1_chain:8545
L2_NODE_WEB3_URL: http://l2geth:8545
@@ -308,8 +309,7 @@ services:
FILTER_POLLING_INTERVAL: 30000
MAX_WAIT_TIME_S: 5
MAX_WAIT_TX_TIME_S: 5
- # Integration tests require 0 confirmations. Suggested to use 5 for production.
- NUM_EVENT_CONFIRMATIONS: 0
+ FAST_RELAYER: 'true'
batch_submitter:
depends_on:
diff --git a/ops/docker/Dockerfile.boba_message-relayer-fast b/ops/docker/Dockerfile.boba_message-relayer-fast
deleted file mode 100644
index 9e2071c86d..0000000000
--- a/ops/docker/Dockerfile.boba_message-relayer-fast
+++ /dev/null
@@ -1,42 +0,0 @@
-FROM bobanetwork/builder AS builder
-FROM node:14-alpine
-
-RUN apk add --no-cache git curl bash jq
-WORKDIR /opt/optimism/
-
-COPY --from=builder /optimism/*.json /optimism/yarn.lock ./
-COPY --from=builder /optimism/node_modules ./node_modules
-
-# copy deps (would have been nice if docker followed the symlinks required)
-COPY --from=builder /optimism/packages/core-utils/package.json ./packages/core-utils/package.json
-COPY --from=builder /optimism/packages/core-utils/dist ./packages/core-utils/dist
-COPY --from=builder /optimism/packages/common-ts/package.json ./packages/common-ts/package.json
-COPY --from=builder /optimism/packages/common-ts/dist ./packages/common-ts/dist
-
-# get the needed built artifacts
-WORKDIR /opt/optimism/packages/contracts
-COPY --from=builder /optimism/packages/contracts/dist ./dist
-COPY --from=builder /optimism/packages/contracts/*.json ./
-COPY --from=builder /optimism/packages/contracts/node_modules ./node_modules
-COPY --from=builder /optimism/packages/contracts/artifacts ./artifacts
-COPY --from=builder /optimism/packages/contracts/deployments ./deployments
-
-# get non-build artifacts from the host
-COPY packages/contracts/bin ./bin
-COPY packages/contracts/contracts ./contracts
-COPY packages/contracts/hardhat.config.ts ./
-COPY packages/contracts/deploy ./deploy
-COPY packages/contracts/tasks ./tasks
-COPY packages/contracts/src ./src
-COPY packages/contracts/test/helpers/constants.ts ./test/helpers/constants.ts
-COPY packages/contracts/scripts ./scripts
-
-# copy the service
-WORKDIR /opt/optimism/packages/boba/message-relayer-fast
-COPY packages/boba/message-relayer-fast/dist ./dist
-COPY packages/boba/message-relayer-fast/package.json .
-COPY packages/boba/message-relayer-fast/exec ./exec
-COPY packages/boba/message-relayer-fast/node_modules ./node_modules
-COPY packages/boba/message-relayer-fast/scripts ./scripts
-
-ENTRYPOINT ["./scripts/wait-for-l1-and-l2.sh", "./scripts/relayer-fast.sh"]
diff --git a/ops/docker/Dockerfile.message-relayer b/ops/docker/Dockerfile.message-relayer
index f4fd5d234e..c626a5fabd 100644
--- a/ops/docker/Dockerfile.message-relayer
+++ b/ops/docker/Dockerfile.message-relayer
@@ -34,4 +34,9 @@ COPY --from=builder /optimism/packages/message-relayer/node_modules ./node_modul
# copy this over in case you want to run alongside other services
COPY ./ops/scripts/relayer.sh .
+COPY ./ops/scripts/relayer-fast.sh .
+COPY ./ops/scripts/wait-for-l1-and-l2.sh .
+
+RUN chmod +x ./relayer-fast.sh
+RUN chmod +x ./wait-for-l1-and-l2.sh
ENTRYPOINT ["npm", "run", "start"]
diff --git a/ops/docker/Dockerfile.monorepo b/ops/docker/Dockerfile.monorepo
index 51c72cbd98..d7836264d5 100644
--- a/ops/docker/Dockerfile.monorepo
+++ b/ops/docker/Dockerfile.monorepo
@@ -51,7 +51,6 @@ COPY integration-tests/package.json ./integration-tests/package.json
COPY packages/boba/contracts/package.json ./packages/boba/contracts/package.json
COPY packages/boba/gas-price-oracle/package.json ./packages/boba/gas-price-oracle/package.json
-COPY packages/boba/message-relayer-fast/package.json ./packages/boba/message-relayer-fast/package.json
COPY packages/boba/turing/package.json ./packages/boba/turing/package.json
# copy over the patches, if any...
diff --git a/packages/boba/message-relayer-fast/scripts/relayer-fast.sh b/ops/scripts/relayer-fast.sh
old mode 100755
new mode 100644
similarity index 96%
rename from packages/boba/message-relayer-fast/scripts/relayer-fast.sh
rename to ops/scripts/relayer-fast.sh
index 52b1113033..fb066af23e
--- a/packages/boba/message-relayer-fast/scripts/relayer-fast.sh
+++ b/ops/scripts/relayer-fast.sh
@@ -46,4 +46,4 @@ if [[ ! -z "$BOBA_URL" ]]; then
fi
# go go go
-exec node ./exec/run-message-relayer-fast.js
\ No newline at end of file
+exec yarn start
\ No newline at end of file
diff --git a/packages/boba/message-relayer-fast/scripts/wait-for-l1-and-l2.sh b/ops/scripts/wait-for-l1-and-l2.sh
old mode 100755
new mode 100644
similarity index 100%
rename from packages/boba/message-relayer-fast/scripts/wait-for-l1-and-l2.sh
rename to ops/scripts/wait-for-l1-and-l2.sh
diff --git a/packages/boba/message-relayer-fast/.env.example b/packages/boba/message-relayer-fast/.env.example
deleted file mode 100644
index 63c626aecf..0000000000
--- a/packages/boba/message-relayer-fast/.env.example
+++ /dev/null
@@ -1,14 +0,0 @@
-#local
-NODE_ENV=local
-L1_NODE_WEB3_URL=http://localhost:9545
-L2_NODE_WEB3_URL=http://localhost:8545
-ADDRESS_MANAGER_ADDRESS=0x5FbDB2315678afecb367f032d93F642f64180aa3
-TEST_PRIVATE_KEY_1=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
-TEST_PRIVATE_KEY_2=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
-TEST_PRIVATE_KEY_3=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a
-TARGET_GAS_LIMIT=9000000000
-CHAIN_ID=28
-FAST_RELAYER_PRIVATE_KEY=0xde9be858da4a475276426320d5e9262ecfc3ba460bfac56360bfa6c4c28b4ee0
-L1_TARGET=0x0
-URL=http://localhost:8080/addresses.json
-BOBA_URL=http://localhost:8078/addresses.json
diff --git a/packages/boba/message-relayer-fast/.prettierrc.json b/packages/boba/message-relayer-fast/.prettierrc.json
deleted file mode 100644
index be295d9d8d..0000000000
--- a/packages/boba/message-relayer-fast/.prettierrc.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "$schema": "http://json.schemastore.org/prettierrc",
- "trailingComma": "es5",
- "tabWidth": 2,
- "semi": false,
- "singleQuote": true,
- "arrowParens": "always"
-}
diff --git a/packages/boba/message-relayer-fast/README.md b/packages/boba/message-relayer-fast/README.md
deleted file mode 100644
index b8b23dd755..0000000000
--- a/packages/boba/message-relayer-fast/README.md
+++ /dev/null
@@ -1,72 +0,0 @@
-# BOBA_Messenger_Relayer_Fast
-
-Customized L1 Cross Domain Messenger without dispute period time restrictions and associated message relayer service.
-
-The custom `L1CrossDomainMessenger` works with the default `L2CrossDomainMessenger`. The messages from the L2_Messenger can be relayed by the custom messenger instead to skip the dispute period and to do that, the bridge/token contract should specify the custom messenger to be the messenger for relays. The custom messenger cannot be used to send cross domain messages. For sending messages the bridge contracts use the default L1_Messenger.
-
-## Using the custom messenger
-
-- Deploy `contracts/OVM_L1_CrossDomainMessenger.sol` on L1, this will be the contract used by your contracts for L2->L1 message passing
-
-- Your bridge/gateway contract must implement `contracts/libraries/OVM_CrossDomainEnabled.sol` instead. This uses the default L1 Messenger to send messages and the custom L1 Messenger to relay.
-
-## Running the Custom Messenger + Relayer
-
-- To deploy the custom messenger and start up the associated relayer run-
-```
-yarn start
-```
-
-- To run tests
-```
-yarn test:integration
-```
-
-## env settings
-
-Use .env.example for quick tests
-
-```
-
-ADDRESS_MANAGER_ADDRESS=
-L1_NODE_WEB3_URL=
-L2_NODE_WEB3_URL=
-L1_MESSENGER_ADDRESS=
-FAST_RELAYER_PRIVATE_KEY=
-L1_TARGET=
-
-```
-
-for tests
-```
-
-TEST_PRIVATE_KEY_1=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
-TEST_PRIVATE_KEY_2=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
-TEST_PRIVATE_KEY_3=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a
-
-```
-
-optional - while running message relayer for specific custom messenger
-```
-L1_MESSENGER_FAST=
-```
-
-### Additional Setting for stopping failed relay attempts
-
-- Add the target contract to the default message relayer's blacklist
-- Set the L1_TARGET= for custom message relayer
-
-### Deploying only Custom messenger
-
-Deploy with the deployer private key to also register the custom messenger
-```
-yarn deploy:contracts
-```
-
-#### Deploying only Custom Relayer
-
-```
-yarn start:service
-```
-
-This starts the service for the registered custom messenger. Specify L1_MESSENGER_FAST= to spin up the relayer for your messenger
diff --git a/packages/boba/message-relayer-fast/deployment/local/addresses.json b/packages/boba/message-relayer-fast/deployment/local/addresses.json
deleted file mode 100644
index 08a23457a8..0000000000
--- a/packages/boba/message-relayer-fast/deployment/local/addresses.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "L2LiquidityPool": "0x5FbDB2315678afecb367f032d93F642f64180aa3",
- "L1LiquidityPool": "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF",
- "L1Message": "0x1291Be112d480055DaFd8a610b7d1e203891C274",
- "L2Message": "0x0165878A594ca255338adfa4d48449f69242Eb8F"
-}
\ No newline at end of file
diff --git a/packages/boba/message-relayer-fast/exec/run-message-relayer-fast.js b/packages/boba/message-relayer-fast/exec/run-message-relayer-fast.js
deleted file mode 100644
index 734b4a1499..0000000000
--- a/packages/boba/message-relayer-fast/exec/run-message-relayer-fast.js
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/env node
-
-const main = require("../dist/exec/run").default
-
-;(async () => {
- await main()
-})().catch((err) => {
- console.log(err)
- process.exit(1)
-})
diff --git a/packages/boba/message-relayer-fast/hardhat.config.ts b/packages/boba/message-relayer-fast/hardhat.config.ts
deleted file mode 100644
index 9d7ddbe411..0000000000
--- a/packages/boba/message-relayer-fast/hardhat.config.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { HardhatUserConfig } from 'hardhat/types'
-import '@nomiclabs/hardhat-ethers'
-import '@nomiclabs/hardhat-waffle'
-import '@eth-optimism/hardhat-ovm'
-import 'hardhat-deploy'
-
-const config: HardhatUserConfig = {
- mocha: {
- timeout: 200000,
- },
- networks: {
- boba: {
- url: 'http://localhost:8545',
- // This sets the gas price to 0 for all transactions on L2. We do this
- // because account balances are not automatically initiated with an ETH
- // balance.
- gasPrice: 15000000,
- },
- localhost: {
- url: 'http://localhost:9545',
- allowUnlimitedContractSize: true,
- timeout: 1800000,
- accounts: [
- '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
- ],
- },
- },
- solidity: '0.7.6',
-}
-
-export default config
diff --git a/packages/boba/message-relayer-fast/package.json b/packages/boba/message-relayer-fast/package.json
deleted file mode 100644
index d8260b484e..0000000000
--- a/packages/boba/message-relayer-fast/package.json
+++ /dev/null
@@ -1,72 +0,0 @@
-{
- "name": "@boba/message-relayer-fast",
- "version": "0.1.1",
- "private": true,
- "description": "Fast Cross Domain Message Relayer service",
- "main": "dist/index",
- "types": "dist/index",
- "files": [
- "dist/index",
- "test/**/*.ts"
- ],
- "scripts": {
- "start:service": "node ./exec/run-message-relayer-fast.js",
- "start": "yarn start:service",
- "build": "tsc -p ./tsconfig.build.json",
- "clean": "rimraf dist/ ./tsconfig.build.tsbuildinfo",
- "test:integration": "hardhat --network boba test",
- "lint": "yarn lint:fix && yarn lint:check",
- "lint:fix": "prettier --config .prettierrc.json --write \"{src,exec,test}/**/*.ts\"",
- "lint:check": "tslint --format stylish --project ."
- },
- "keywords": [
- "optimism",
- "ethereum",
- "fast relayer"
- ],
- "license": "MIT",
- "author": "Optimism",
- "repository": {
- "type": "git",
- "url": "https://github.com/ethereum-optimism/optimism.git"
- },
- "devDependencies": {
- "@eth-optimism/contracts": "^0.5.11",
- "@ethersproject/providers": "^5.0.24",
- "@nomiclabs/hardhat-ethers": "^2.0.2",
- "@openzeppelin/test-helpers": "0.5.4",
- "@types/mocha": "^8.2.2",
- "chai": "^4.3.6",
- "chai-as-promised": "^7.1.1",
- "directory-tree": "^2.2.7",
- "ethereum-waffle": "^3.3.0",
- "hardhat": "^2.3.0",
- "mocha": "^8.3.1",
- "node-fetch": "^2.6.1",
- "prettier": "^2.2.1",
- "rimraf": "^3.0.2",
- "shx": "^0.3.3",
- "ts-node": "^9.1.1",
- "tslint": "^6.1.3",
- "tslint-config-prettier": "^1.18.0",
- "tslint-no-focused-test": "^0.5.0",
- "tslint-plugin-prettier": "^2.3.0",
- "typescript": "^4.2.4"
- },
- "dependencies": {
- "@eth-optimism/common-ts": "^0.2.2",
- "@eth-optimism/core-utils": "0.8.1",
- "@eth-optimism/ynatm": "^0.2.2",
- "@openzeppelin/contracts": "3.4.1",
- "bcfg": "^0.1.6",
- "chalk": "^4.1.1",
- "dotenv": "^8.2.0",
- "ethers": "^5.1.0",
- "ganache-core": "^2.13.2",
- "glob": "^7.1.6",
- "google-spreadsheet": "^3.1.15",
- "merkletreejs": "^0.2.18",
- "patch-package": "^6.4.7",
- "rlp": "^2.2.6"
- }
-}
diff --git a/packages/boba/message-relayer-fast/src/exec/run.ts b/packages/boba/message-relayer-fast/src/exec/run.ts
deleted file mode 100644
index ea3c0fa6ed..0000000000
--- a/packages/boba/message-relayer-fast/src/exec/run.ts
+++ /dev/null
@@ -1,151 +0,0 @@
-import { Wallet, providers } from 'ethers'
-import { MessageRelayerService } from '../service'
-import { Bcfg } from '@eth-optimism/core-utils'
-import * as dotenv from 'dotenv'
-import Config from 'bcfg'
-
-dotenv.config()
-
-const main = async () => {
- const config: Bcfg = new Config('message-relayer')
- config.load({
- env: true,
- argv: true,
- })
-
- const env = process.env
- const L2_NODE_WEB3_URL = config.str('l2-node-web3-url', env.L2_NODE_WEB3_URL)
- const L1_NODE_WEB3_URL = config.str('l1-node-web3-url', env.L1_NODE_WEB3_URL)
- const ADDRESS_MANAGER_ADDRESS = config.str(
- 'address-manager-address',
- env.ADDRESS_MANAGER_ADDRESS
- )
- const L1_MESSENGER_FAST = config.str(
- 'l1-messenger-fast',
- env.L1_MESSENGER_FAST
- )
- const FAST_RELAYER_PRIVATE_KEY = config.str(
- 'fast-relayer-private-key',
- env.FAST_RELAYER_PRIVATE_KEY
- )
- const MNEMONIC = config.str('mnemonic', env.MNEMONIC)
- const HD_PATH = config.str('hd-path', env.HD_PATH)
- //batch system
- const MIN_BATCH_SIZE = config.uint(
- 'min-batch-size',
- parseInt(env.MIN_BATCH_SIZE, 10) || 2
- )
- const MAX_WAIT_TIME_S = config.uint(
- 'max-wait-time-s',
- parseInt(env.MAX_WAIT_TIME_S, 10) || 60
- )
- const MAX_WAIT_TX_TIME_S = config.uint(
- 'max-wait-tx-time-s',
- parseInt(env.MAX_WAIT_TX_TIME_S, 10) || 180
- )
- const RELAY_GAS_LIMIT = config.uint(
- 'relay-gas-limit',
- parseInt(env.RELAY_GAS_LIMIT, 10) || 4000000
- )
- const POLLING_INTERVAL = config.uint(
- 'polling-interval',
- parseInt(env.POLLING_INTERVAL, 10) || 5000
- )
- const GET_LOGS_INTERVAL = config.uint(
- 'get-logs-interval',
- parseInt(env.GET_LOGS_INTERVAL, 10) || 2000
- )
- const L2_BLOCK_OFFSET = config.uint(
- 'l2-start-offset',
- parseInt(env.L2_BLOCK_OFFSET, 10) || 1
- )
- const L1_START_OFFSET = config.uint(
- 'l1-start-offset',
- parseInt(env.L1_BLOCK_OFFSET, 10) || 1
- )
- const FROM_L2_TRANSACTION_INDEX = config.uint(
- 'from-l2-transaction-index',
- parseInt(env.FROM_L2_TRANSACTION_INDEX, 10) || 0
- )
- const FILTER_ENDPOINT =
- config.str('filter-endpoint', env.FILTER_ENDPOINT) || ''
- const FILTER_POLLING_INTERVAL = config.uint(
- 'filter-polling-interval',
- parseInt(env.FILTER_POLLING_INTERVAL, 10) || 60000
- )
- const MAX_GAS_PRICE_IN_GWEI = config.uint(
- 'max-gas-price-in-gwei',
- parseInt(env.MAX_GAS_PRICE_IN_GWEI, 10) || 100
- )
- const GAS_RETRY_INCREMENT = config.uint(
- 'gas-retry-increment',
- parseInt(env.GAS_RETRY_INCREMENT, 10) || 5
- )
- const RESUBMISSION_TIMEOUT = config.uint(
- 'resubmission-timeout',
- parseInt(env.RESUBMISSION_TIMEOUT, 10) || 60
- )
- const NUM_CONFIRMATIONS = config.uint(
- 'num-confirmations',
- parseInt(env.NUM_CONFIRMATIONS, 10) || 1
- )
- const NUM_EVENT_CONFIRMATIONS = config.uint(
- 'num-event-confirmations',
- parseInt(env.NUM_EVENT_CONFIRMATIONS, 10) || 0
- )
- const MULTI_RELAY_LIMIT = config.uint(
- 'multi-relay-limit',
- parseInt(env.MULTI_RELAY_LIMIT, 10) || 10
- )
-
- if (!ADDRESS_MANAGER_ADDRESS) {
- throw new Error('Must pass ADDRESS_MANAGER_ADDRESS')
- }
- if (!L1_NODE_WEB3_URL) {
- throw new Error('Must pass L1_NODE_WEB3_URL')
- }
- if (!L2_NODE_WEB3_URL) {
- throw new Error('Must pass L2_NODE_WEB3_URL')
- }
-
- const l2Provider = new providers.StaticJsonRpcProvider(L2_NODE_WEB3_URL)
- const l1Provider = new providers.StaticJsonRpcProvider(L1_NODE_WEB3_URL)
-
- let wallet: Wallet
- if (FAST_RELAYER_PRIVATE_KEY) {
- wallet = new Wallet(FAST_RELAYER_PRIVATE_KEY, l1Provider)
- } else if (MNEMONIC) {
- wallet = Wallet.fromMnemonic(MNEMONIC, HD_PATH)
- wallet = wallet.connect(l1Provider)
- } else {
- throw new Error('Must pass one of FAST_RELAYER_PRIVATE_KEY or MNEMONIC')
- }
-
- const service = new MessageRelayerService({
- l1RpcProvider: l1Provider,
- l2RpcProvider: l2Provider,
- addressManagerAddress: ADDRESS_MANAGER_ADDRESS,
- l1MessengerFast: L1_MESSENGER_FAST,
- l1Wallet: wallet,
- relayGasLimit: RELAY_GAS_LIMIT,
- minBatchSize: MIN_BATCH_SIZE,
- maxWaitTimeS: MAX_WAIT_TIME_S,
- maxWaitTxTimeS: MAX_WAIT_TX_TIME_S,
- fromL2TransactionIndex: FROM_L2_TRANSACTION_INDEX,
- pollingInterval: POLLING_INTERVAL,
- l2BlockOffset: L2_BLOCK_OFFSET,
- l1StartOffset: L1_START_OFFSET,
- getLogsInterval: GET_LOGS_INTERVAL,
- filterEndpoint: FILTER_ENDPOINT,
- filterPollingInterval: FILTER_POLLING_INTERVAL,
- maxGasPriceInGwei: MAX_GAS_PRICE_IN_GWEI,
- gasRetryIncrement: GAS_RETRY_INCREMENT,
- numConfirmations: NUM_CONFIRMATIONS,
- numEventConfirmations: NUM_EVENT_CONFIRMATIONS,
- multiRelayLimit: MULTI_RELAY_LIMIT,
- resubmissionTimeout: RESUBMISSION_TIMEOUT * 1000,
- })
-
- await service.start()
-}
-export default main
diff --git a/packages/boba/message-relayer-fast/src/service.ts b/packages/boba/message-relayer-fast/src/service.ts
deleted file mode 100644
index cc67b2c78f..0000000000
--- a/packages/boba/message-relayer-fast/src/service.ts
+++ /dev/null
@@ -1,902 +0,0 @@
-/* Imports: External */
-import { Contract, ethers, Wallet, BigNumber, providers } from 'ethers'
-import * as rlp from 'rlp'
-import { MerkleTree } from 'merkletreejs'
-import fetch from 'node-fetch'
-import * as ynatm from '@eth-optimism/ynatm'
-
-/* Imports: Internal */
-import { fromHexString, sleep } from '@eth-optimism/core-utils'
-import { BaseService } from '@eth-optimism/common-ts'
-
-import { loadContract, loadContractFromManager } from '@eth-optimism/contracts'
-import {
- StateRootBatchHeader,
- SentMessage,
- SentMessageProof,
- BatchMessage,
-} from './types'
-
-interface MessageRelayerOptions {
- // Providers for interacting with L1 and L2.
- l1RpcProvider: providers.StaticJsonRpcProvider
- l2RpcProvider: providers.StaticJsonRpcProvider
-
- // Address of the AddressManager contract, used to resolve the various addresses we'll need
- // within this service.
- addressManagerAddress: string
-
- l1MessengerFast: string
-
- // Wallet instance, used to sign and send the L1 relay transactions.
- l1Wallet: Wallet
-
- // Max gas to relay messages with.
- relayGasLimit: number
-
- // Batch system configuration
- minBatchSize: number
- maxWaitTimeS: number
-
- // Height of the L2 transaction to start searching for L2->L1 messages.
- fromL2TransactionIndex?: number
-
- // Interval in seconds to wait between loops.
- pollingInterval?: number
-
- // Number of blocks that L2 is "ahead" of transaction indices. Can happen if blocks are created
- // on L2 after the genesis but before the first state commitment is published.
- l2BlockOffset?: number
-
- // L1 block to start querying events from. Recommended to set to the StateCommitmentChain deploy height
- l1StartOffset?: number
-
- // Number of blocks within each getLogs query - max is 2000
- getLogsInterval?: number
-
- // Filter configuration - makes sure that the fast message relayer only deals with the messages it needs to
- filterEndpoint?: string
-
- filterPollingInterval?: number
-
- // gas fee
- maxGasPriceInGwei?: number
-
- gasRetryIncrement?: number
-
- numConfirmations?: number
-
- numEventConfirmations?: number
-
- multiRelayLimit?: number
-
- resubmissionTimeout?: number
-
- maxWaitTxTimeS: number
-}
-
-const optionSettings = {
- relayGasLimit: { default: 4_000_000 },
- minBatchSize: { default: 2 },
- maxWaitTimeS: { default: 60 },
- fromL2TransactionIndex: { default: 0 },
- pollingInterval: { default: 5000 },
- l2BlockOffset: { default: 1 },
- l1StartOffset: { default: 0 },
- getLogsInterval: { default: 2000 },
- filterPollingInterval: { default: 60000 },
- maxWaitTxTimeS: { default: 180 },
-}
-
-export class MessageRelayerService extends BaseService {
- constructor(options: MessageRelayerOptions) {
- super('Message_Relayer', options, optionSettings)
- }
-
- private state: {
- lastFinalizedTxHeight: number
- nextUnfinalizedTxHeight: number
- lastQueriedL1Block: number
- eventCache: ethers.Event[]
- Lib_AddressManager: Contract
- StateCommitmentChain: Contract
- L1CrossDomainMessenger: Contract
- L1MultiMessageRelayerFast: Contract
- L2CrossDomainMessenger: Contract
- OVM_L2ToL1MessagePasser: Contract
- filter: Array
- lastFilterPollingTimestamp: number
- //batch system
- timeSinceLastRelayS: number
- timeOfLastRelayS: number
- messageBuffer: Array
- timeOfLastPendingRelay: any
- didWork: boolean
- }
-
- protected async _init(): Promise {
- this.logger.info('Initializing message relayer', {
- relayGasLimit: this.options.relayGasLimit,
- fromL2TransactionIndex: this.options.fromL2TransactionIndex,
- pollingInterval: this.options.pollingInterval,
- l2BlockOffset: this.options.l2BlockOffset,
- getLogsInterval: this.options.getLogsInterval,
- filterPollingInterval: this.options.filterPollingInterval,
- minBatchSize: this.options.minBatchSize,
- maxWaitTimeS: this.options.maxWaitTimeS,
- })
- // Need to improve this, sorry.
- this.state = {} as any
-
- const address = await this.options.l1Wallet.getAddress()
- this.logger.info('Using L1 EOA', { address })
-
- this.state.Lib_AddressManager = loadContract(
- 'Lib_AddressManager',
- this.options.addressManagerAddress,
- this.options.l1RpcProvider
- )
-
- this.logger.info('Connecting to StateCommitmentChain...')
- this.state.StateCommitmentChain = await loadContractFromManager({
- name: 'StateCommitmentChain',
- Lib_AddressManager: this.state.Lib_AddressManager,
- provider: this.options.l1RpcProvider,
- })
- this.logger.info('Connected to StateCommitmentChain', {
- address: this.state.StateCommitmentChain.address,
- })
-
- this.logger.info('Connecting to L1CrossDomainMessenger...')
- const l1MessengerAddress =
- this.options.l1MessengerFast ||
- (await this.state.Lib_AddressManager.getAddress(
- 'Proxy__L1CrossDomainMessengerFast'
- ))
- this.state.L1CrossDomainMessenger = loadContract(
- 'L1CrossDomainMessenger',
- l1MessengerAddress,
- this.options.l1RpcProvider
- )
- this.logger.info('Connected to L1CrossDomainMessenger', {
- address: this.state.L1CrossDomainMessenger.address,
- })
-
- this.logger.info('Connecting to L1MultiMessageRelayerFast...')
- const l1MultiMessageRelayerFastAddress =
- await this.state.Lib_AddressManager.getAddress(
- 'L1MultiMessageRelayerFast'
- )
- this.state.L1MultiMessageRelayerFast = loadContract(
- 'L1MultiMessageRelayer',
- l1MultiMessageRelayerFastAddress,
- this.options.l1RpcProvider
- )
- this.logger.info('Connected to L1MultiMessageRelayerFast', {
- address: this.state.L1MultiMessageRelayerFast.address,
- })
-
- this.logger.info('Connecting to L2CrossDomainMessenger...')
- this.state.L2CrossDomainMessenger = await loadContractFromManager({
- name: 'L2CrossDomainMessenger',
- Lib_AddressManager: this.state.Lib_AddressManager,
- provider: this.options.l2RpcProvider,
- })
- this.logger.info('Connected to L2CrossDomainMessenger', {
- address: this.state.L2CrossDomainMessenger.address,
- })
-
- this.logger.info('Connecting to OVM_L2ToL1MessagePasser...')
- this.state.OVM_L2ToL1MessagePasser = loadContract(
- 'OVM_L2ToL1MessagePasser',
- '0x4200000000000000000000000000000000000000',
- this.options.l2RpcProvider
- )
- this.logger.info('Connected to OVM_L2ToL1MessagePasser', {
- address: this.state.OVM_L2ToL1MessagePasser.address,
- })
-
- this.logger.info('Connected to all contracts.')
-
- this.state.lastQueriedL1Block = this.options.l1StartOffset
- this.state.eventCache = []
-
- this.state.lastFinalizedTxHeight = this.options.fromL2TransactionIndex || 0
- this.state.nextUnfinalizedTxHeight =
- this.options.fromL2TransactionIndex || 0
- this.state.lastFilterPollingTimestamp = 0
-
- this.logger.info('Starting at', {
- lastFinalizedTxHeight: this.state.lastFinalizedTxHeight,
- nextUnfinalizedTxHeight: this.state.nextUnfinalizedTxHeight,
- lastQueriedL1Block: this.state.lastQueriedL1Block,
- numEventConfirmations: this.options.numEventConfirmations,
- })
-
- //batch system
- this.state.timeOfLastRelayS = Date.now()
- this.state.timeSinceLastRelayS = 0
- this.state.messageBuffer = []
- this.state.timeOfLastPendingRelay = false
- }
-
- protected async _start(): Promise {
- while (this.running) {
- if (!this.state.didWork) {
- await sleep(this.options.pollingInterval)
- }
- this.state.didWork = false
-
- await this._getFilter()
-
- try {
- // Check that the correct address is set in the address manager
- const relayer = await this.state.Lib_AddressManager.getAddress(
- 'OVM_L2MessageRelayer'
- )
- // If it is address(0), then message relaying is not authenticated
- if (relayer !== ethers.constants.AddressZero) {
- const address = await this.options.l1Wallet.getAddress()
- if (relayer !== address) {
- throw new Error(
- `OVM_L2MessageRelayer (${relayer}) is not set to message-passer EOA ${address}`
- )
- }
- }
-
- //Batch flushing logic
- const secondsElapsed = Math.floor(
- (Date.now() - this.state.timeOfLastRelayS) / 1000
- )
- console.log('\n***********************************')
- console.log('Seconds elapsed since last batch push:', secondsElapsed)
- const timeOut =
- secondsElapsed > this.options.maxWaitTimeS ? true : false
-
- let pendingTXTimeOut = true
- if (this.state.timeOfLastPendingRelay !== false) {
- const pendingTXSecondsElapsed = Math.floor(
- (Date.now() - this.state.timeOfLastPendingRelay) / 1000
- )
- console.log('\n***********************************')
- console.log(
- 'Next tx since last tx submitted',
- pendingTXSecondsElapsed
- )
- pendingTXTimeOut =
- pendingTXSecondsElapsed > this.options.maxWaitTxTimeS ? true : false
- }
-
- //console.log('Current buffer size:', this.state.messageBuffer.length)
- const bufferFull =
- this.state.messageBuffer.length >= this.options.minBatchSize
- ? true
- : false
-
- // check gas price
- const gasPrice = await this.options.l1RpcProvider.getGasPrice()
- const gasPriceGwei = Number(ethers.utils.formatUnits(gasPrice, 'gwei'))
- const gasPriceAcceptable =
- gasPriceGwei < this.options.maxGasPriceInGwei ? true : false
-
- if (
- this.state.messageBuffer.length !== 0 &&
- (bufferFull || timeOut) &&
- pendingTXTimeOut
- ) {
- if (gasPriceAcceptable) {
- this.state.didWork = true
- if (bufferFull) {
- console.log('Buffer full: flushing')
- }
- if (timeOut) {
- console.log('Buffer timeout: flushing')
- }
-
- // Filter out messages which have been processed
- let newMB = []
- for (const cur of this.state.messageBuffer) {
- if (
- !(await this._wasMessageRelayed(cur.message)) &&
- !(await this._wasMessageFailed(cur.message))
- ) {
- newMB.push(cur)
- }
- }
- this.state.messageBuffer = newMB
-
- if (this.state.messageBuffer.length === 0) {
- this.state.timeOfLastPendingRelay = false
- } else {
- // Limit the number of messages to be submitted per batch.
- const subBuffer = this.state.messageBuffer.slice(
- 0,
- this.options.multiRelayLimit
- )
- this.logger.info('Prepared message subBuffer', {
- subLen: subBuffer.length,
- bufLen: this.state.messageBuffer.length,
- limit: this.options.multiRelayLimit,
- })
-
- const receipt = await this._relayMultiMessageToL1(
- subBuffer.reduce((acc, cur) => {
- acc.push(cur.payload)
- return acc
- }, [])
- )
-
- if (!receipt) {
- this.logger.error(
- 'No receipt for relayMultiMessage transaction'
- )
- } else if (receipt.status === 1) {
- this.logger.info('Successful relayMultiMessage', {
- blockNumber: receipt.blockNumber,
- transactionIndex: receipt.transactionIndex,
- status: receipt.status,
- msgCount: subBuffer.length,
- gasUsed: receipt.gasUsed.toString(),
- effectiveGasPrice: receipt.effectiveGasPrice.toString(),
- })
- } else {
- this.logger.warning('Unsuccessful relayMultiMessage', {
- blockNumber: receipt.blockNumber,
- transactionIndex: receipt.transactionIndex,
- status: receipt.status,
- msgCount: subBuffer.length,
- gasUsed: receipt.gasUsed.toString(),
- effectiveGasPrice: receipt.effectiveGasPrice.toString(),
- })
- }
-
- // add a delay between two tx
- this.state.timeOfLastPendingRelay = Date.now()
- }
- } else {
- console.log('Current gas price is unacceptable')
- // add a delay between two tx
- this.state.timeOfLastPendingRelay = Date.now()
- }
-
- this.state.timeOfLastRelayS = Date.now()
- } else {
- console.log(
- 'Buffer still too small - current buffer length:',
- this.state.messageBuffer.length
- )
- console.log('Buffer flush size set to:', this.options.minBatchSize)
- console.log('***********************************\n')
- }
-
- // scanning for new messages only if pending messages are relayed
- // to l1
- if (this.state.timeOfLastPendingRelay === false) {
- this.logger.info('Checking for newly finalized transactions...')
- if (
- !(await this._isTransactionFinalized(
- this.state.nextUnfinalizedTxHeight
- ))
- ) {
- this.logger.info('Did not find any newly finalized transactions', {
- retryAgainInS: Math.floor(this.options.pollingInterval / 1000),
- })
-
- continue
- }
-
- this.state.didWork = true
- this.state.lastFinalizedTxHeight = this.state.nextUnfinalizedTxHeight
- while (
- await this._isTransactionFinalized(
- this.state.nextUnfinalizedTxHeight
- )
- ) {
- const batch = (
- await this._getStateBatchHeader(
- this.state.nextUnfinalizedTxHeight
- )
- ).batch
- const size = batch.batchSize.toNumber()
- const batchStart = batch.prevTotalElements.toNumber()
-
- this.logger.info(
- 'Found a batch of finalized transaction(s), checking for more...',
- {
- batchSize: size,
- atHeight: this.state.nextUnfinalizedTxHeight,
- batchStart,
- }
- )
- // This must point to the first txHeight within the next batch. If the service starts
- // with a misaligned fromL2TransactionIndex then this should realign it to avoid
- // missed messages.
- this.state.nextUnfinalizedTxHeight = batchStart + size
-
- // Only deal with ~1000 transactions at a time so we can limit the amount of stuff we
- // need to keep in memory. We operate on full batches at a time so the actual amount
- // depends on the size of the batches we're processing.
- const numTransactionsToProcess =
- this.state.nextUnfinalizedTxHeight -
- this.state.lastFinalizedTxHeight
-
- if (numTransactionsToProcess > 1000) {
- break
- }
- }
-
- this.logger.info('Found finalized transactions', {
- totalNumber:
- this.state.nextUnfinalizedTxHeight -
- this.state.lastFinalizedTxHeight,
- })
-
- const messages = await this._getSentMessages(
- this.state.lastFinalizedTxHeight,
- this.state.nextUnfinalizedTxHeight
- )
-
- for (const message of messages) {
- this.logger.info('Found a message sent during transaction', {
- index: message.parentTransactionIndex,
- })
- if (await this._wasMessageRelayed(message)) {
- this.logger.info('Message has already been relayed, skipping.')
- continue
- }
-
- if (await this._wasMessageBlocked(message)) {
- this.logger.info('Message has been blocked, skipping.')
- continue
- }
-
- if (await this._wasMessageFailed(message)) {
- this.logger.info('Last message was failed, skipping.')
- continue
- }
-
- if (!this.state.filter.includes(message.target)) {
- this.logger.info('Message not intended for target, skipping.')
- continue
- }
-
- this.logger.info(
- 'Message not yet relayed. Attempting to generate a proof...'
- )
- const proof = await this._getMessageProof(message)
- this.logger.info(
- 'Successfully generated a proof. Attempting to relay to Layer 1...'
- )
-
- const messageToSend = {
- payload: {
- target: message.target,
- message: message.message,
- sender: message.sender,
- messageNonce: message.messageNonce,
- proof,
- },
- message,
- }
- this.state.messageBuffer.push(messageToSend)
- }
-
- if (messages.length === 0) {
- this.logger.info('Did not find any L2->L1 messages', {
- retryAgainInS: Math.floor(this.options.pollingInterval / 1000),
- })
- } else {
- // Clear the event cache to avoid keeping every single event in memory and eventually
- // getting OOM killed. Messages are already sorted in ascending order so the last message
- // will have the highest batch index.
- const lastMessage = messages[messages.length - 1]
-
- // Find the batch corresponding to the last processed message.
- const lastProcessedBatch = await this._getStateBatchHeader(
- lastMessage.parentTransactionIndex
- )
- this.logger.info('Pending messages', {
- numMessages: messages.length,
- firstTxnIdx: messages[0].parentTransactionIndex,
- lastTxnIdx: lastMessage.parentTransactionIndex,
- lastBatch: lastProcessedBatch.batch.batchIndex,
- })
-
- // Remove any events from the cache for batches that should've been processed by now.
- const oldLen = this.state.eventCache.length
- this.logger.info('eventCache before filter', {
- size: oldLen,
- firstIdx: oldLen
- ? this.state.eventCache[0].args._batchIndex
- : 'n/a',
- lastIdx: oldLen
- ? this.state.eventCache[oldLen - 1].args._batchIndex
- : 'n/a',
- })
- this.state.eventCache = this.state.eventCache.filter((event) => {
- return (
- Number(event.args._batchIndex) >
- Number(lastProcessedBatch.batch.batchIndex)
- )
- })
- const newLen = this.state.eventCache.length
- this.logger.info('eventCache after filter', {
- size: newLen,
- firstIdx: newLen
- ? this.state.eventCache[0].args._batchIndex
- : 'n/a',
- lastIdx: newLen
- ? this.state.eventCache[newLen - 1].args._batchIndex
- : 'n/a',
- })
- }
-
- this.logger.info(
- 'Finished searching through newly finalized transactions',
- {
- retryAgainInS: Math.floor(this.options.pollingInterval / 1000),
- }
- )
- } else {
- this.logger.info('Waiting for the pending tx to be finalized')
- }
- } catch (err) {
- this.logger.error('Caught an unhandled error', {
- message: err.toString(),
- stack: err.stack,
- code: err.code,
- })
- }
- }
- }
-
- private async _getStateBatchHeader(height: number): Promise<
- | {
- batch: StateRootBatchHeader
- stateRoots: string[]
- }
- | undefined
- > {
- const getStateBatchAppendedEventForIndex = async (
- txIndex: number
- ): Promise => {
- const selectedEvent = this.state.eventCache.find((cachedEvent) => {
- const prevTotalElements = cachedEvent.args._prevTotalElements.toNumber()
- const batchSize = cachedEvent.args._batchSize.toNumber()
-
- // Height should be within the bounds of the batch.
- return (
- txIndex >= prevTotalElements &&
- txIndex < prevTotalElements + batchSize
- )
- })
- // No event found
- if (selectedEvent === undefined) {
- return undefined
- }
- // query the new SCC event. event.args._extraData in eventCache might be wrong
- const SCCEvents: ethers.Event[] =
- await this.state.StateCommitmentChain.queryFilter(
- this.state.StateCommitmentChain.filters.StateBatchAppended(),
- selectedEvent.blockNumber,
- selectedEvent.blockNumber
- )
- // cover a special case that multiple transactions are in one block
- for (const SCCEvent of SCCEvents) {
- const prevTotalElements = SCCEvent.args._prevTotalElements.toNumber()
- const batchSize = SCCEvent.args._batchSize.toNumber()
- if (
- txIndex >= prevTotalElements &&
- txIndex < prevTotalElements + batchSize
- ) {
- return SCCEvent
- }
- }
- return SCCEvents[0]
- }
-
- let startingBlock = this.state.lastQueriedL1Block + 1
- const maxBlock =
- (await this.options.l1RpcProvider.getBlockNumber()) -
- this.options.numEventConfirmations
- while (startingBlock <= maxBlock) {
- const endBlock = Math.min(
- startingBlock + this.options.getLogsInterval,
- maxBlock
- )
-
- this.logger.info('Querying events', {
- startingBlock,
- endBlock,
- })
-
- const events: ethers.Event[] =
- await this.state.StateCommitmentChain.queryFilter(
- this.state.StateCommitmentChain.filters.StateBatchAppended(),
- startingBlock,
- endBlock
- )
-
- const ebn = []
- events.forEach((e) => {
- ebn.push(e.blockNumber)
- })
- this.logger.info('Queried Events', { startingBlock, endBlock, ebn })
-
- this.state.eventCache = this.state.eventCache.concat(events)
-
- this.state.lastQueriedL1Block = endBlock
- startingBlock = endBlock + 1
-
- // We need to stop syncing early once we find the event we're looking for to avoid putting
- // *all* events into memory at the same time. Otherwise we'll get OOM killed.
- if ((await getStateBatchAppendedEventForIndex(height)) !== undefined) {
- break
- }
- }
-
- const event = await getStateBatchAppendedEventForIndex(height)
- if (event === undefined) {
- return undefined
- }
-
- const transaction = await this.options.l1RpcProvider.getTransaction(
- event.transactionHash
- )
-
- const [stateRoots] =
- this.state.StateCommitmentChain.interface.decodeFunctionData(
- 'appendStateBatch',
- transaction.data
- )
-
- return {
- batch: {
- batchIndex: event.args._batchIndex,
- batchRoot: event.args._batchRoot,
- batchSize: event.args._batchSize,
- prevTotalElements: event.args._prevTotalElements,
- extraData: event.args._extraData,
- },
- stateRoots,
- }
- }
-
- private async _isTransactionFinalized(height: number): Promise {
- this.logger.info('Checking if tx is finalized', { height })
- const header = await this._getStateBatchHeader(height)
-
- if (header === undefined) {
- this.logger.info('No state batch header found.', {
- height,
- lastF: this.state.lastFinalizedTxHeight,
- nextU: this.state.nextUnfinalizedTxHeight,
- })
- return false
- } else {
- this.logger.info('Got state batch header', { header })
- }
-
- // decode to get timestamp and check for time elapsed for non-zero dispute period messenger
- // This is major difference WRT to the normal message-relayer
- // the message-relayer-fast does not check the SCC for .insideFraudProofWindow
-
- // return !(await this.state.StateCommitmentChain.insideFraudProofWindow(
- // header.batch
- // ))
- return true
- }
-
- /**
- * Returns all sent message events between some start height (inclusive) and an end height
- * (exclusive).
- * @param startHeight Start height to start finding messages from.
- * @param endHeight End height to finish finding messages at.
- * @returns All sent messages between start and end height, sorted by transaction index in
- * ascending order.
- */
- private async _getSentMessages(
- startHeight: number,
- endHeight: number
- ): Promise {
- const filter = this.state.L2CrossDomainMessenger.filters.SentMessage()
- const events = await this.state.L2CrossDomainMessenger.queryFilter(
- filter,
- startHeight + this.options.l2BlockOffset,
- endHeight + this.options.l2BlockOffset - 1
- )
-
- const messages = events.map((event) => {
- const encodedMessage =
- this.state.L2CrossDomainMessenger.interface.encodeFunctionData(
- 'relayMessage',
- [
- event.args.target,
- event.args.sender,
- event.args.message,
- event.args.messageNonce,
- ]
- )
-
- return {
- target: event.args.target,
- sender: event.args.sender,
- message: event.args.message,
- messageNonce: event.args.messageNonce,
- encodedMessage,
- encodedMessageHash: ethers.utils.keccak256(encodedMessage),
- parentTransactionIndex: event.blockNumber - this.options.l2BlockOffset,
- parentTransactionHash: event.transactionHash,
- }
- })
-
- // Sort in ascending order based on tx index and return.
- return messages.sort((a, b) => {
- return a.parentTransactionIndex - b.parentTransactionIndex
- })
- }
-
- private async _wasMessageRelayed(message: SentMessage): Promise {
- return this.state.L1CrossDomainMessenger.successfulMessages(
- message.encodedMessageHash
- )
- }
-
- private async _wasMessageBlocked(message: SentMessage): Promise {
- return this.state.L1CrossDomainMessenger.blockedMessages(
- message.encodedMessageHash
- )
- }
-
- private async _wasMessageFailed(message: SentMessage): Promise {
- return this.state.L1CrossDomainMessenger.failedMessages(
- message.encodedMessageHash
- )
- }
-
- private async _getMessageProof(
- message: SentMessage
- ): Promise {
- const messageSlot = ethers.utils.keccak256(
- ethers.utils.keccak256(
- message.encodedMessage +
- this.state.L2CrossDomainMessenger.address.slice(2)
- ) + '00'.repeat(32)
- )
-
- // TODO: Complain if the proof doesn't exist.
- const proof = await this.options.l2RpcProvider.send('eth_getProof', [
- this.state.OVM_L2ToL1MessagePasser.address,
- [messageSlot],
- '0x' +
- BigNumber.from(
- message.parentTransactionIndex + this.options.l2BlockOffset
- )
- .toHexString()
- .slice(2)
- .replace(/^0+/, ''),
- ])
-
- // TODO: Complain if the batch doesn't exist.
- const header = await this._getStateBatchHeader(
- message.parentTransactionIndex
- )
-
- const elements = []
- for (
- let i = 0;
- i < Math.pow(2, Math.ceil(Math.log2(header.stateRoots.length)));
- i++
- ) {
- if (i < header.stateRoots.length) {
- elements.push(header.stateRoots[i])
- } else {
- elements.push(ethers.utils.keccak256('0x' + '00'.repeat(32)))
- }
- }
-
- const hash = (el: Buffer | string): Buffer => {
- return Buffer.from(ethers.utils.keccak256(el).slice(2), 'hex')
- }
-
- const leaves = elements.map((element) => {
- return fromHexString(element)
- })
-
- const tree = new MerkleTree(leaves, hash)
- const index =
- message.parentTransactionIndex - header.batch.prevTotalElements.toNumber()
- const treeProof = tree.getProof(leaves[index], index).map((element) => {
- return element.data
- })
-
- return {
- stateRoot: header.stateRoots[index],
- stateRootBatchHeader: header.batch,
- stateRootProof: {
- index,
- siblings: treeProof,
- },
- stateTrieWitness: rlp.encode(proof.accountProof),
- storageTrieWitness: rlp.encode(proof.storageProof[0].proof),
- }
- }
-
- private async _relayMultiMessageToL1(
- messages: Array
- ): Promise {
- const sendTxAndWaitForReceipt = async (gasPrice): Promise => {
- // Generate the transaction we will repeatedly submit
- const nonce = await this.options.l1Wallet.getTransactionCount()
- this.logger.info('Relay message nonce', { nonce })
- const txResponse = await this.state.L1MultiMessageRelayerFast.connect(
- this.options.l1Wallet
- ).batchRelayMessages(messages, { gasPrice, nonce })
- const tx = await this.options.l1Wallet.provider.waitForTransaction(
- txResponse.hash,
- this.options.numConfirmations
- )
- return tx
- }
-
- const minGasPrice = await this._getGasPriceInGwei(this.options.l1Wallet)
-
- let receipt
- try {
- receipt = await ynatm.send({
- sendTransactionFunction: sendTxAndWaitForReceipt,
- minGasPrice: ynatm.toGwei(minGasPrice),
- maxGasPrice: ynatm.toGwei(this.options.maxGasPriceInGwei),
- gasPriceScalingFunction: ynatm.LINEAR(this.options.gasRetryIncrement),
- delay: this.options.resubmissionTimeout,
- })
-
- this.logger.info('Relay message transaction sent', { receipt })
- } catch (err) {
- this.logger.error('Relay attempt failed, skipping.', {
- message: err.toString(),
- stack: err.stack,
- code: err.code,
- })
- return
- }
-
- this.logger.info('Message Batch successfully relayed to Layer 1!')
- return receipt
- }
-
- private async _getGasPriceInGwei(signer): Promise {
- return parseInt(
- ethers.utils.formatUnits(await signer.getGasPrice(), 'gwei'),
- 10
- )
- }
-
- /* The filter makes sure that the message-relayer-fast only handles message traffic
- intended for it
- */
-
- private async _getFilter(): Promise {
- try {
- if (this.options.filterEndpoint) {
- if (
- this.state.lastFilterPollingTimestamp === 0 ||
- new Date().getTime() >
- this.state.lastFilterPollingTimestamp +
- this.options.filterPollingInterval
- ) {
- const response = await fetch(this.options.filterEndpoint)
- const filter = await response.json()
-
- const filterSelect = [filter.Proxy__L1LiquidityPool, filter.L1Message]
-
- this.state.lastFilterPollingTimestamp = new Date().getTime()
- this.state.filter = filterSelect
- this.logger.info('Found the filter', { filterSelect })
- }
- }
- } catch {
- this.logger.error('CRITICAL ERROR: Failed to fetch the Filter')
- }
- }
-}
diff --git a/packages/boba/message-relayer-fast/src/types.ts b/packages/boba/message-relayer-fast/src/types.ts
deleted file mode 100644
index 57e7dc1649..0000000000
--- a/packages/boba/message-relayer-fast/src/types.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { BigNumber } from 'ethers'
-
-export interface StateRootBatchHeader {
- batchIndex: BigNumber
- batchRoot: string
- batchSize: BigNumber
- prevTotalElements: BigNumber
- extraData: string
-}
-
-export interface SentMessage {
- target: string
- sender: string
- message: string
- messageNonce: number
- encodedMessage: string
- encodedMessageHash: string
- parentTransactionIndex: number
- parentTransactionHash: string
-}
-
-export interface SentMessageProof {
- stateRoot: string
- stateRootBatchHeader: StateRootBatchHeader
- stateRootProof: StateRootProof
- stateTrieWitness: string | Buffer
- storageTrieWitness: string | Buffer
-}
-
-export interface StateRootProof {
- index: number
- siblings: string[]
-}
-
-interface BatchMessagePayload {
- target: string
- message: string
- sender: string
- messageNonce: number
- proof: SentMessageProof
-}
-
-export interface BatchMessage {
- payload: BatchMessagePayload
- message: SentMessage
-}
diff --git a/packages/boba/message-relayer-fast/tsconfig.build.json b/packages/boba/message-relayer-fast/tsconfig.build.json
deleted file mode 100644
index 989b7319c8..0000000000
--- a/packages/boba/message-relayer-fast/tsconfig.build.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "extends": "../../../tsconfig.build.json",
-
- "compilerOptions": {
- "rootDir": "./src",
- "outDir": "./dist"
- },
-
- "include": [
- "src/**/*", "contracts/**/*"
- ]
-}
diff --git a/packages/boba/message-relayer-fast/tsconfig.json b/packages/boba/message-relayer-fast/tsconfig.json
deleted file mode 100644
index 859dc1c9c4..0000000000
--- a/packages/boba/message-relayer-fast/tsconfig.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "extends": "../../../tsconfig.json",
-
- "compilerOptions": {
- "resolveJsonModule": true
- }
-}
\ No newline at end of file
diff --git a/packages/contracts/contracts/L1/messaging/L1MultiMessageRelayerFast.sol b/packages/contracts/contracts/L1/messaging/L1MultiMessageRelayerFast.sol
new file mode 100644
index 0000000000..d01aed1072
--- /dev/null
+++ b/packages/contracts/contracts/L1/messaging/L1MultiMessageRelayerFast.sol
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: MIT
+pragma solidity >0.7.5;
+pragma experimental ABIEncoderV2;
+
+/* Interface Imports */
+import { IL1CrossDomainMessengerFast } from "./IL1CrossDomainMessengerFast.sol";
+
+/* Library Imports */
+import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
+
+/**
+ * @title L1MultiMessageRelayerFast
+ * @dev The L1 Multi-Message Relayer Fast contract is a gas efficiency optimization which enables the
+ * relayer to submit multiple messages in a single transaction to be relayed by the Fast L1 Cross Domain
+ * Message Sender.
+ *
+ * Compiler used: solc
+ * Runtime target: EVM
+ */
+contract L1MultiMessageRelayerFast is Lib_AddressResolver {
+ /***************
+ * Structure *
+ ***************/
+
+ struct L2ToL1Message {
+ address target;
+ address sender;
+ bytes message;
+ uint256 messageNonce;
+ IL1CrossDomainMessengerFast.L2MessageInclusionProof proof;
+ }
+
+ /***************
+ * Constructor *
+ ***************/
+
+ /**
+ * @param _libAddressManager Address of the Address Manager.
+ */
+ constructor(address _libAddressManager) Lib_AddressResolver(_libAddressManager) {}
+
+ /**********************
+ * Function Modifiers *
+ **********************/
+
+ modifier onlyBatchRelayer() {
+ require(
+ msg.sender == resolve("L2BatchFastMessageRelayer"),
+ // solhint-disable-next-line max-line-length
+ "L1MultiMessageRelayerFast: Function can only be called by the L2BatchFastMessageRelayer"
+ );
+ _;
+ }
+
+ /********************
+ * Public Functions *
+ ********************/
+
+ /**
+ * @notice Forwards multiple cross domain messages to the L1 Cross Domain Messenger Fast for relaying
+ * @param _messages An array of L2 to L1 messages
+ */
+ function batchRelayMessages(L2ToL1Message[] calldata _messages) external onlyBatchRelayer {
+ IL1CrossDomainMessengerFast messenger = IL1CrossDomainMessengerFast(
+ resolve("Proxy__L1CrossDomainMessengerFast")
+ );
+
+ for (uint256 i = 0; i < _messages.length; i++) {
+ L2ToL1Message memory message = _messages[i];
+ messenger.relayMessage(
+ message.target,
+ message.sender,
+ message.message,
+ message.messageNonce,
+ message.proof
+ );
+ }
+ }
+
+ /**
+ * @notice Forwards multiple cross domain messages to the L1 Cross Domain Messenger Fast for relaying
+ * @param _messages An array of L2 to L1 messages
+ * @param _standardBridgeDepositHash current deposit hash of standard bridges
+ * @param _lpDepositHash current deposit hash of LP1
+ */
+ function batchRelayMessages(
+ L2ToL1Message[] calldata _messages,
+ bytes32 _standardBridgeDepositHash,
+ bytes32 _lpDepositHash
+ ) external onlyBatchRelayer {
+ IL1CrossDomainMessengerFast messenger = IL1CrossDomainMessengerFast(
+ resolve("Proxy__L1CrossDomainMessengerFast")
+ );
+
+ for (uint256 i = 0; i < _messages.length; i++) {
+ L2ToL1Message memory message = _messages[i];
+ messenger.relayMessage(
+ message.target,
+ message.sender,
+ message.message,
+ message.messageNonce,
+ message.proof,
+ _standardBridgeDepositHash,
+ _lpDepositHash
+ );
+ }
+ }
+}
diff --git a/packages/message-relayer/.env.example b/packages/message-relayer/.env.example
index 23b66bd6e8..7d0bf81d89 100644
--- a/packages/message-relayer/.env.example
+++ b/packages/message-relayer/.env.example
@@ -5,3 +5,12 @@ RELAYER_PRIVATE_KEY=0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620e
FILTER_ENDPOINT=http://localhost:8080/boba-addr.json
MAX_WAIT_TIME_S=5
MAX_WAIT_TX_TIME_S=5
+
+#local - message relayer fast
+L1_NODE_WEB3_URL=http://localhost:9545
+L2_NODE_WEB3_URL=http://localhost:8545
+RELAYER_PRIVATE_KEY=0xde9be858da4a475276426320d5e9262ecfc3ba460bfac56360bfa6c4c28b4ee0
+FILTER_ENDPOINT=http://localhost:8080/boba-addr.json
+MAX_WAIT_TIME_S=5
+MAX_WAIT_TX_TIME_S=5
+FAST_RELAYER=true
diff --git a/packages/message-relayer/README.md b/packages/message-relayer/README.md
index 9bd4f9cac6..dc406634f9 100644
--- a/packages/message-relayer/README.md
+++ b/packages/message-relayer/README.md
@@ -1,70 +1,23 @@
[](https://codecov.io/gh/ethereum-optimism/optimism)
# @eth-optimism/message-relayer
-This package contains:
+This package contains: A service for relaying messages from L2 to L1. This service can also be used to relay fast messages for the L1CrossDomainMessengerFast without waiting for the dispute period.
-1. A service for relaying messages from L2 to L1.
-2. Utilities for finding these messages and relaying them.
+The message relayer uses L1MultiMessageRelayer/L1MultiMessageRelayerFast to relay messages together in batches.
-## Installation
+## Usage
+**To start the classic message relayer:**
+- specify the env variables, use env.example for reference
+- run:
```
-yarn add @eth-optimism/message-relayer
+yarn start
```
-## Relay Utilities
-
-### getMessagesAndProofsForL2Transaction
-
-Finds all L2 => L1 messages sent in a given L2 transaction and generates proof for each.
-
-#### Usage
-
-```typescript
-import { getMessagesAndProofsForL2Transaction } from '@eth-optimism/message-relayer'
-
-const main = async () => {
- const l1RpcProviderUrl = 'https://layer1.endpoint'
- const l2RpcProviderUrl = 'https://layer2.endpoint'
- const l1StateCommitmentChainAddress = 'address of StateCommitmentChain from deployments page'
- const l2CrossDomainMessengerAddress = 'address of L2CrossDomainMessenger from deployments page'
- const l2TransactionHash = 'hash of the transaction with messages to relay'
-
- const messagePairs = await getMessagesAndProofsForL2Transaction(
- l1RpcProviderUrl,
- l2RpcProviderUrl,
- l1StateCommitmentChainAddress,
- l2CrossDomainMessengerAddress,
- l2TransactionHash
- )
-
- console.log(messagePairs)
- // Will log something along the lines of:
- // [
- // {
- // message: {
- // target: '0x...',
- // sender: '0x...',
- // message: '0x...',
- // messageNonce: 1234...
- // },
- // proof: {
- // // complicated
- // }
- // }
- // ]
-
- // You can then do something along the lines of:
- // for (const { message, proof } of messagePairs) {
- // await l1CrossDomainMessenger.relayMessage(
- // message.target,
- // message.sender,
- // message.message,
- // message.messageNonce,
- // proof
- // )
- // }
-}
-
-main()
+**To start the message relayer fast:**
+- specify the env variables, use env.example for reference
+- set FAST_RELAYER=true
+- run:
+```
+yarn start
```
diff --git a/packages/message-relayer/src/exec/run.ts b/packages/message-relayer/src/exec/run.ts
index 87055e81b5..b770cd4f06 100644
--- a/packages/message-relayer/src/exec/run.ts
+++ b/packages/message-relayer/src/exec/run.ts
@@ -40,12 +40,21 @@ const main = async () => {
const L2_NODE_WEB3_URL = config.str('l2-node-web3-url', env.L2_NODE_WEB3_URL)
const L1_NODE_WEB3_URL = config.str('l1-node-web3-url', env.L1_NODE_WEB3_URL)
- const RELAYER_PRIVATE_KEY = config.str(
- 'l1-wallet-key',
- env.RELAYER_PRIVATE_KEY
- )
+ let RELAYER_PRIVATE_KEY = config.str('l1-wallet-key', env.RELAYER_PRIVATE_KEY)
const MNEMONIC = config.str('mnemonic', env.MNEMONIC)
const HD_PATH = config.str('hd-path', env.HD_PATH)
+
+ // run as message relayer fast
+ const FAST_RELAYER = config.bool('fast-relayer', env.FAST_RELAYER === 'true')
+ // check if FAST_RELAYER_PRIVATE_KEY is passed
+ const FAST_RELAYER_PRIVATE_KEY = config.str(
+ 'l1-wallet-key-fast',
+ env.FAST_RELAYER_PRIVATE_KEY
+ )
+ // if this exists and is fast-relayer mode, use this account
+ if (FAST_RELAYER_PRIVATE_KEY && FAST_RELAYER) {
+ RELAYER_PRIVATE_KEY = FAST_RELAYER_PRIVATE_KEY
+ }
//batch system
const MIN_BATCH_SIZE = config.uint(
'min-batch-size',
@@ -148,6 +157,7 @@ const main = async () => {
numConfirmations: NUM_CONFIRMATIONS,
multiRelayLimit: MULTI_RELAY_LIMIT,
resubmissionTimeout: RESUBMISSION_TIMEOUT * 1000,
+ isFastRelayer: FAST_RELAYER,
})
await service.start()
diff --git a/packages/message-relayer/src/service.ts b/packages/message-relayer/src/service.ts
index c1b2e60838..4e136acb5a 100644
--- a/packages/message-relayer/src/service.ts
+++ b/packages/message-relayer/src/service.ts
@@ -1,6 +1,6 @@
/* Imports: External */
import { Wallet, utils, constants, BigNumber } from 'ethers'
-import { sleep } from '@eth-optimism/core-utils'
+import { Address, sleep } from '@eth-optimism/core-utils'
import fetch from 'node-fetch'
import { Logger, BaseService, Metrics } from '@eth-optimism/common-ts'
import * as ynatm from '@eth-optimism/ynatm'
@@ -73,6 +73,8 @@ interface MessageRelayerOptions {
resubmissionTimeout?: number
maxWaitTxTimeS: number
+
+ isFastRelayer: boolean
}
export class MessageRelayerService extends BaseService {
@@ -127,6 +129,7 @@ export class MessageRelayerService extends BaseService {
getLogsInterval: this.options.getLogsInterval,
minBatchSize: this.options.minBatchSize,
maxWaitTimeS: this.options.maxWaitTimeS,
+ isFastRelayer: this.options.isFastRelayer,
})
const l1Network = await this.options.l1Wallet.provider.getNetwork()
@@ -135,6 +138,7 @@ export class MessageRelayerService extends BaseService {
l1SignerOrProvider: this.options.l1Wallet,
l2SignerOrProvider: this.options.l2RpcProvider,
l1ChainId,
+ fastRelayer: this.options.isFastRelayer,
})
this.state.highestCheckedL2Tx = this.options.fromL2TransactionIndex || 1
@@ -162,10 +166,18 @@ export class MessageRelayerService extends BaseService {
try {
// Check that the correct address is set in the address manager,
// this is a requirement for batch-relayer
- const relayer =
- await this.state.messenger.contracts.l1.AddressManager.getAddress(
- 'L2BatchMessageRelayer'
- )
+ let relayer: Address
+ if (this.options.isFastRelayer) {
+ relayer =
+ await this.state.messenger.contracts.l1.AddressManager.getAddress(
+ 'L2BatchFastMessageRelayer'
+ )
+ } else {
+ relayer =
+ await this.state.messenger.contracts.l1.AddressManager.getAddress(
+ 'L2BatchMessageRelayer'
+ )
+ }
// If it is address(0), then message relaying is not authenticated
if (relayer !== constants.AddressZero) {
const address = await this.options.l1Wallet.getAddress()
@@ -417,6 +429,19 @@ export class MessageRelayerService extends BaseService {
// If we got here then all messages in the transaction are finalized. Now we can relay
// each message to L1.
for (const message of messages) {
+ // filter out messages not meant for this relayer
+ if (this.options.isFastRelayer) {
+ if (!this.state.filter.includes(message.target)) {
+ this.logger.info('Message not intended for target, skipping.')
+ continue
+ }
+ } else {
+ if (this.state.filter.includes(message.target)) {
+ this.logger.info('Message not intended for target, skipping.')
+ continue
+ }
+ }
+
const status = await this.state.messenger.getMessageStatus(
message
)
@@ -430,12 +455,6 @@ export class MessageRelayerService extends BaseService {
continue
}
- // filter out messages not meant for this relayer
- if (this.state.filter.includes(message.target)) {
- this.logger.info('Message not intended for target, skipping.')
- continue
- }
-
if (message.target === canonicalTransactionChain) {
this.logger.info('Message target is CTC, skipping.')
continue
diff --git a/packages/sdk/src/cross-chain-messenger.ts b/packages/sdk/src/cross-chain-messenger.ts
index 7fd9c915d0..7958639144 100644
--- a/packages/sdk/src/cross-chain-messenger.ts
+++ b/packages/sdk/src/cross-chain-messenger.ts
@@ -386,6 +386,7 @@ export class CrossChainMessenger implements ICrossChainMessenger {
if (stateRoot === null) {
return MessageStatus.STATE_ROOT_NOT_PUBLISHED
} else {
+ // for the fast relayer this should be zero
const challengePeriod = await this.getChallengePeriodSeconds()
const targetBlock = await this.l1Provider.getBlock(
stateRoot.batch.blockNumber
@@ -669,6 +670,10 @@ export class CrossChainMessenger implements ICrossChainMessenger {
}
public async getChallengePeriodSeconds(): Promise {
+ // if fast relayer return no challenge period
+ if (this.fastRelayer) {
+ return 0
+ }
const challengePeriod =
await this.contracts.l1.StateCommitmentChain.FRAUD_PROOF_WINDOW()
return challengePeriod.toNumber()
@@ -1118,7 +1123,9 @@ export class CrossChainMessenger implements ICrossChainMessenger {
const proof = await this.getMessageProof(resolved)
if (this.fastRelayer) {
- return this.contracts.l1.L1CrossDomainMessengerFast.populateTransaction.relayMessage(
+ return this.contracts.l1.L1CrossDomainMessengerFast.populateTransaction[
+ 'relayMessage(address,address,bytes,uint256,(bytes32,(uint256,bytes32,uint256,uint256,bytes),(uint256,bytes32[]),bytes,bytes))'
+ ](
resolved.target,
resolved.sender,
resolved.message,
@@ -1161,10 +1168,17 @@ export class CrossChainMessenger implements ICrossChainMessenger {
})
}
- return this.contracts.l1.L1MultiMessageRelayer.populateTransaction.batchRelayMessages(
- batchMessage,
- opts?.overrides || {}
- )
+ if (this.fastRelayer) {
+ // ethers.js v5 does not handle overloading
+ return this.contracts.l1.L1MultiMessageRelayerFast.populateTransaction[
+ 'batchRelayMessages((address,address,bytes,uint256,(bytes32,(uint256,bytes32,uint256,uint256,bytes),(uint256,bytes32[]),bytes,bytes))[])'
+ ](batchMessage, opts?.overrides || {})
+ } else {
+ return this.contracts.l1.L1MultiMessageRelayer.populateTransaction.batchRelayMessages(
+ batchMessage,
+ opts?.overrides || {}
+ )
+ }
},
depositETH: async (
diff --git a/packages/sdk/src/interfaces/types.ts b/packages/sdk/src/interfaces/types.ts
index a9ed3fd9b2..1d53fcdc93 100644
--- a/packages/sdk/src/interfaces/types.ts
+++ b/packages/sdk/src/interfaces/types.ts
@@ -21,6 +21,7 @@ export interface OEL1Contracts {
CanonicalTransactionChain: Contract
BondManager: Contract
L1MultiMessageRelayer: Contract
+ L1MultiMessageRelayerFast: Contract
}
/**
diff --git a/packages/sdk/src/utils/contracts.ts b/packages/sdk/src/utils/contracts.ts
index aed875c15f..45eb444038 100644
--- a/packages/sdk/src/utils/contracts.ts
+++ b/packages/sdk/src/utils/contracts.ts
@@ -64,6 +64,7 @@ export const CONTRACT_ADDRESSES: {
CanonicalTransactionChain: '0xfBd2541e316948B259264c02f370eD088E04c3Db',
BondManager: '0x60660e6CDEb423cf847dD11De4C473130D65b627',
L1MultiMessageRelayer: '0x5fD2CF99586B9D92f56CbaD0A3Ea4DF256A0070B',
+ L1MultiMessageRelayerFast: '0x2d6134Ac3e480fBDD263B7163d333dCA285f9622',
},
l2: DEFAULT_L2_CONTRACT_ADDRESSES,
},
@@ -78,6 +79,7 @@ export const CONTRACT_ADDRESSES: {
CanonicalTransactionChain: '0x321Bf0Df8F02FE665a7f7CcE31684A6dadB014b0',
BondManager: '0xAF41D3399A91C43E8f2d70d9b47741b10CFA4Fc4',
L1MultiMessageRelayer: '0x5C6263BCAa00C7f5988E148dB3CA178e1262E69f',
+ L1MultiMessageRelayerFast: '0x94BC5F5330B9EF9f520551cDB6bD8FC707760Af6',
},
l2: DEFAULT_L2_CONTRACT_ADDRESSES,
},
@@ -92,6 +94,7 @@ export const CONTRACT_ADDRESSES: {
CanonicalTransactionChain: '0x2ebA8c4EfDB39A8Cd8f9eD65c50ec079f7CEBD81',
BondManager: '0xE5AE60bD6F8DEe4D0c2BC9268e23B92F1cacC58F',
L1MultiMessageRelayer: '0x0000000000000000000000000000000000000000',
+ L1MultiMessageRelayerFast: '0x0000000000000000000000000000000000000000',
},
l2: DEFAULT_L2_CONTRACT_ADDRESSES,
},
@@ -106,6 +109,7 @@ export const CONTRACT_ADDRESSES: {
CanonicalTransactionChain: '0xf7B88A133202d41Fe5E2Ab22e6309a1A4D50AF74',
BondManager: '0xc5a603d273E28185c18Ba4d26A0024B2d2F42740',
L1MultiMessageRelayer: '0x0000000000000000000000000000000000000000',
+ L1MultiMessageRelayerFast: '0x0000000000000000000000000000000000000000',
},
l2: DEFAULT_L2_CONTRACT_ADDRESSES,
},
@@ -120,6 +124,7 @@ export const CONTRACT_ADDRESSES: {
CanonicalTransactionChain: '0x0B306BF915C4d645ff596e518fAf3F9669b97016',
BondManager: '0x3Aa5ebB10DC797CAC828524e59A333d0A371443c',
L1MultiMessageRelayer: '0x9E545E3C0baAB3E08CdfD552C960A1050f373042',
+ L1MultiMessageRelayerFast: '0xeF31027350Be2c7439C1b0BE022d49421488b72C',
},
l2: DEFAULT_L2_CONTRACT_ADDRESSES,
},
@@ -307,6 +312,7 @@ export const getAllOEContracts = (
CanonicalTransactionChain: undefined,
BondManager: undefined,
L1MultiMessageRelayer: undefined,
+ L1MultiMessageRelayerFast: undefined,
},
l2: DEFAULT_L2_CONTRACT_ADDRESSES,
}
diff --git a/packages/sdk/test/cross-chain-messenger.spec.ts b/packages/sdk/test/cross-chain-messenger.spec.ts
index b61291213d..5d417456e1 100644
--- a/packages/sdk/test/cross-chain-messenger.spec.ts
+++ b/packages/sdk/test/cross-chain-messenger.spec.ts
@@ -176,6 +176,7 @@ describe('CrossChainMessenger', () => {
CanonicalTransactionChain: '0x' + '15'.repeat(20),
BondManager: '0x' + '16'.repeat(20),
L1MultiMessageRelayer: '0x' + '17'.repeat(20),
+ L1MultiMessageRelayerFast: '0x' + '18'.repeat(20),
},
l2: {
L2CrossDomainMessenger: '0x' + '22'.repeat(20),
diff --git a/yarn.lock b/yarn.lock
index 3b5cc9b89d..63b6868139 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3699,15 +3699,6 @@
dependencies:
"@octokit/openapi-types" "^11.2.0"
-"@openzeppelin/contract-loader@^0.4.0":
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/@openzeppelin/contract-loader/-/contract-loader-0.4.0.tgz#c28c05d09df94c634d968ed175b5777dfc675872"
- integrity sha512-K+Pl4tn0FbxMSP0H9sgi61ayCbecpqhQmuBshelC7A3q2MlpcqWRJan0xijpwdtv6TORNd5oZNe/+f3l+GD6tw==
- dependencies:
- find-up "^4.1.0"
- fs-extra "^8.1.0"
- try-require "^1.2.1"
-
"@openzeppelin/contract-loader@^0.6.2":
version "0.6.3"
resolved "https://registry.yarnpkg.com/@openzeppelin/contract-loader/-/contract-loader-0.6.3.tgz#61a7b44de327e40b7d53f39e0fb59bbf847335c3"
@@ -3721,11 +3712,6 @@
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.3.2.tgz#92df481362e366c388fc02133cf793029c744cea"
integrity sha512-i/pOaOtcqDk4UqsrOv735uYyTbn6dvfiuVu5hstsgV6c4ZKUtu88/31zT2BzkCg+3JfcwOfgg2TtRKVKKZIGkQ==
-"@openzeppelin/contracts@3.4.1":
- version "3.4.1"
- resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.1.tgz#03c891fec7f93be0ae44ed74e57a122a38732ce7"
- integrity sha512-cUriqMauq1ylzP2TxePNdPqkwI7Le3Annh4K9rrpvKfSBB/bdW+Iu1ihBaTIABTAAJ85LmKL5SSPPL9ry8d1gQ==
-
"@openzeppelin/contracts@3.4.1-solc-0.7-2":
version "3.4.1-solc-0.7-2"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.1-solc-0.7-2.tgz#371c67ebffe50f551c3146a9eec5fe6ffe862e92"
@@ -3736,22 +3722,6 @@
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.3.2.tgz#ff80affd6d352dbe1bbc5b4e1833c41afd6283b6"
integrity sha512-AybF1cesONZStg5kWf6ao9OlqTZuPqddvprc0ky7lrUVOjXeKpmQ2Y9FK+6ygxasb+4aic4O5pneFBfwVsRRRg==
-"@openzeppelin/test-helpers@0.5.4":
- version "0.5.4"
- resolved "https://registry.yarnpkg.com/@openzeppelin/test-helpers/-/test-helpers-0.5.4.tgz#2366703c37849dc1b3768bff41ab9213aee28c5a"
- integrity sha512-sSjft0CcmC6Pwsd/j1uakk2MLamEH4mmphVlF5hzUUVyoxL0KGCbxQgBoLoEpUbw2M9HtFAEMJJPGccEcb9Cog==
- dependencies:
- "@openzeppelin/contract-loader" "^0.4.0"
- "@truffle/contract" "^4.0.35"
- ansi-colors "^3.2.3"
- chai "^4.2.0"
- chai-bn "^0.2.0"
- ethjs-abi "^0.2.1"
- lodash.flatten "^4.4.0"
- semver "^5.6.0"
- web3 "^1.2.1"
- web3-utils "^1.2.1"
-
"@openzeppelin/test-helpers@^0.5.15":
version "0.5.15"
resolved "https://registry.yarnpkg.com/@openzeppelin/test-helpers/-/test-helpers-0.5.15.tgz#7727d4bb1535e1fa2372d65d1dcee335ce8d36af"
@@ -7514,7 +7484,7 @@ chai-as-promised@^7.1.1:
dependencies:
check-error "^1.0.2"
-chai-bn@^0.2.0, chai-bn@^0.2.1:
+chai-bn@^0.2.1:
version "0.2.2"
resolved "https://registry.yarnpkg.com/chai-bn/-/chai-bn-0.2.2.tgz#4dcf30dbc79db2378a00781693bc749c972bf34f"
integrity sha512-MzjelH0p8vWn65QKmEq/DLBG1Hle4WeyqT79ANhXZhn/UxRWO0OogkAxi5oGGtfzwU9bZR8mvbvYdoqNVWQwFg==
@@ -22612,11 +22582,6 @@ truncate-middle@^1.0.6:
resolved "https://registry.yarnpkg.com/truncate-middle/-/truncate-middle-1.0.6.tgz#17c6272ec5a469b75a82fbca38b388b61b50473b"
integrity sha512-oJLDTdHAk27V+JUUu1vKYezKehx/tECV0vnJ1e8JV/rvre5oLoFMaCLP53ZwiPsw4ZIJzyLoZr/TKQABnaNF6A==
-try-require@^1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/try-require/-/try-require-1.2.1.tgz#34489a2cac0c09c1cc10ed91ba011594d4333be2"
- integrity sha1-NEiaLKwMCcHMEO2RugEVlNQzO+I=
-
tryer@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
@@ -24445,7 +24410,7 @@ web3-utils@1.6.0:
randombytes "^2.1.0"
utf8 "3.0.0"
-web3-utils@1.7.0, web3-utils@^1.0.0-beta.31, web3-utils@^1.2.1, web3-utils@^1.2.5, web3-utils@^1.3.0, web3-utils@^1.3.4:
+web3-utils@1.7.0, web3-utils@^1.0.0-beta.31, web3-utils@^1.2.5, web3-utils@^1.3.0, web3-utils@^1.3.4:
version "1.7.0"
resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.7.0.tgz#c59f0fd43b2449357296eb54541810b99b1c771c"
integrity sha512-O8Tl4Ky40Sp6pe89Olk2FsaUkgHyb5QAXuaKo38ms3CxZZ4d3rPGfjP9DNKGm5+IUgAZBNpF1VmlSmNCqfDI1w==
@@ -24497,7 +24462,7 @@ web3@1.6.0:
web3-shh "1.6.0"
web3-utils "1.6.0"
-web3@^1.2.1, web3@^1.2.5, web3@^1.6.1:
+web3@^1.2.5, web3@^1.6.1:
version "1.7.0"
resolved "https://registry.yarnpkg.com/web3/-/web3-1.7.0.tgz#5867cd10a2bebb5c33fc218368e3f6f826f6897e"
integrity sha512-n39O7QQNkpsjhiHMJ/6JY6TaLbdX+2FT5iGs8tb3HbIWOhPm4+a7UDbr5Lkm+gLa9aRKWesZs5D5hWyEvg4aJA==