11/* Imports: External */
2- import { Wallet } from 'ethers'
2+ import { Signer } from 'ethers'
33import { sleep } from '@eth-optimism/core-utils'
44import {
55 BaseServiceV2 ,
@@ -15,6 +15,7 @@ type MessageRelayerOptions = {
1515 l2RpcProvider : Provider
1616 l1Wallet : Signer
1717 fromL2TransactionIndex ?: number
18+ }
1819
1920type MessageRelayerMetrics = {
2021 highestCheckedL2Tx : Gauge
@@ -74,28 +75,22 @@ export class MessageRelayerService extends BaseServiceV2<
7475 } )
7576 }
7677
77- private state : {
78- messenger : CrossChainMessenger
79- highestCheckedL2Tx : number
80- } = { } as any
81-
82- protected async _init ( ) : Promise < void > {
83- this . logger . info ( 'Initializing message relayer' , {
84- relayGasLimit : this . options . relayGasLimit ,
85- fromL2TransactionIndex : this . options . fromL2TransactionIndex ,
86- pollingInterval : this . options . pollingInterval ,
87- getLogsInterval : this . options . getLogsInterval ,
88- } )
78+ protected async init ( ) : Promise < void > {
79+ this . state . wallet = this . options . l1Wallet . connect (
80+ this . options . l1RpcProvider
81+ )
8982
90- const l1Network = await this . options . l1Wallet . provider . getNetwork ( )
83+ const l1Network = await this . state . wallet . provider . getNetwork ( )
9184 const l1ChainId = l1Network . chainId
9285 this . state . messenger = new CrossChainMessenger ( {
93- l1SignerOrProvider : this . options . l1Wallet ,
86+ l1SignerOrProvider : this . state . wallet ,
9487 l2SignerOrProvider : this . options . l2RpcProvider ,
9588 l1ChainId,
9689 } )
9790
9891 this . state . highestCheckedL2Tx = this . options . fromL2TransactionIndex || 1
92+ this . state . highestKnownL2Tx =
93+ await this . state . messenger . l2Provider . getBlockNumber ( )
9994 }
10095
10196 protected async main ( ) : Promise < void > {
@@ -132,6 +127,40 @@ export class MessageRelayerService extends BaseServiceV2<
132127 block . transactions [ 0 ] . hash
133128 )
134129
130+ // No messages in this transaction so we can move on to the next one.
131+ if ( messages . length === 0 ) {
132+ this . state . highestCheckedL2Tx ++
133+ return
134+ }
135+
136+ // Make sure that all messages sent within the transaction are finalized. If any messages
137+ // are not finalized, then we're going to break the loop which will trigger the sleep and
138+ // wait for a few seconds before we check again to see if this transaction is finalized.
139+ let isFinalized = true
140+ for ( const message of messages ) {
141+ const status = await this . state . messenger . getMessageStatus ( message )
142+ if (
143+ status === MessageStatus . IN_CHALLENGE_PERIOD ||
144+ status === MessageStatus . STATE_ROOT_NOT_PUBLISHED
145+ ) {
146+ isFinalized = false
147+ }
148+ }
149+
150+ if ( ! isFinalized ) {
151+ this . logger . info (
152+ `tx not yet finalized, waiting: ${ this . state . highestCheckedL2Tx } `
153+ )
154+ return
155+ } else {
156+ this . logger . info (
157+ `tx is finalized, relaying: ${ this . state . highestCheckedL2Tx } `
158+ )
159+ }
160+
161+ // If we got here then all messages in the transaction are finalized. Now we can relay
162+ // each message to L1.
163+ for ( const message of messages ) {
135164 try {
136165 const tx = await this . state . messenger . finalizeMessage ( message )
137166 this . logger . info ( `relayer sent tx: ${ tx . hash } ` )
@@ -142,13 +171,16 @@ export class MessageRelayerService extends BaseServiceV2<
142171 } else {
143172 throw err
144173 }
145- } catch ( err ) {
146- this . logger . error ( 'Caught an unhandled error' , {
147- message : err . toString ( ) ,
148- stack : err . stack ,
149- code : err . code ,
150- } )
151174 }
175+ await this . state . messenger . waitForMessageReceipt ( message )
152176 }
177+
178+ // All messages have been relayed so we can move on to the next block.
179+ this . state . highestCheckedL2Tx ++
153180 }
154181}
182+
183+ if ( require . main === module ) {
184+ const service = new MessageRelayerService ( )
185+ service . run ( )
186+ }
0 commit comments