1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#!/usr/bin/env ts-node
/**
* Utility that will relay all L2 => L1 messages created within a given L2 transaction.
*/
/* Imports: External */
import { ethers } from 'ethers'
import { predeploys, getContractInterface } from '@eth-optimism/contracts'
import { sleep } from '@eth-optimism/core-utils'
import dotenv from 'dotenv'
/* Imports: Internal */
import { getMessagesAndProofsForL2Transaction } from '../relay-tx'
dotenv.config()
const l1RpcProviderUrl = process.env.WITHDRAW__L1_RPC_URL
const l2RpcProviderUrl = process.env.WITHDRAW__L2_RPC_URL
const l1PrivateKey = process.env.WITHDRAW__L1_PRIVATE_KEY
const l1StateCommitmentChainAddress =
process.env.WITHDRAW__STATE_COMMITMENT_CHAIN_ADDRESS
const l1CrossDomainMessengerAddress =
process.env.WITHDRAW__L1_CROSS_DOMAIN_MESSENGER_ADDRESS
const main = async () => {
const l2TransactionHash = process.argv[2]
if (l2TransactionHash === undefined) {
throw new Error(`must provide l2 transaction hash`)
}
const l1RpcProvider = new ethers.providers.JsonRpcProvider(l1RpcProviderUrl)
const l1Wallet = new ethers.Wallet(l1PrivateKey, l1RpcProvider)
const l1WalletBalance = await l1Wallet.getBalance()
console.log(`relayer address: ${l1Wallet.address}`)
console.log(`relayer balance: ${ethers.utils.formatEther(l1WalletBalance)}`)
const l1CrossDomainMessenger = new ethers.Contract(
l1CrossDomainMessengerAddress,
getContractInterface('L1CrossDomainMessenger'),
l1Wallet
)
console.log(`searching for messages in transaction: ${l2TransactionHash}`)
let messagePairs = []
while (true) {
try {
messagePairs = await getMessagesAndProofsForL2Transaction(
l1RpcProviderUrl,
l2RpcProviderUrl,
l1StateCommitmentChainAddress,
predeploys.L2CrossDomainMessenger,
l2TransactionHash
)
break
} catch (err) {
if (err.message.includes('unable to find state root batch for tx')) {
console.log(`no state root batch for tx yet, trying again in 5s...`)
await sleep(5000)
} else {
throw err
}
}
}
console.log(`found ${messagePairs.length} messages`)
for (let i = 0; i < messagePairs.length; i++) {
console.log(`relaying message ${i + 1}/${messagePairs.length}`)
const { message, proof } = messagePairs[i]
while (true) {
try {
const result = await l1CrossDomainMessenger.relayMessage(
message.target,
message.sender,
message.message,
message.messageNonce,
proof
)
await result.wait()
console.log(
`relayed message ${i + 1}/${messagePairs.length}! L1 tx hash: ${
result.hash
}`
)
break
} catch (err) {
if (err.message.includes('execution failed due to an exception')) {
console.log(`fraud proof may not be elapsed, trying again in 5s...`)
await sleep(5000)
} else if (err.message.includes('message has already been received')) {
console.log(
`message ${i + 1}/${
messagePairs.length
} was relayed by someone else`
)
break
} else {
throw err
}
}
}
}
}
main()