Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
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,
})
// 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 OVM_StateCommitmentChain...')
this.state.OVM_StateCommitmentChain = await loadContractFromManager({
name: 'OVM_StateCommitmentChain',
Lib_AddressManager: this.state.Lib_AddressManager,
provider: this.options.l1RpcProvider,
})
this.logger.info('Connected to OVM_StateCommitmentChain', {
address: this.state.OVM_StateCommitmentChain.address,
})
this.logger.info('Connecting to OVM_L1CrossDomainMessenger...')
private async _getLastTimestampAndBlockNumber(): Promise<{
lastTimestamp: number
lastBlockNumber: number
}> {
const manager = new Contract(
this.addressManagerAddress,
getNewContractInterface('Lib_AddressManager'),
this.signer.provider
)
const addr = await manager.getAddress(
'OVM_ChainStorageContainer:CTC:batches'
)
const container = new Contract(
addr,
getNewContractInterface('iOVM_ChainStorageContainer'),
this.signer.provider
)
let meta = await container.getGlobalMetadata()
// remove 0x
meta = meta.slice(2)
// convert to bytes27
private async _getLastTimestampAndBlockNumber(): Promise<{
lastTimestamp: number
lastBlockNumber: number
}> {
const manager = new Contract(
this.addressManagerAddress,
getNewContractInterface('Lib_AddressManager'),
this.signer.provider
)
const addr = await manager.getAddress(
'OVM_ChainStorageContainer:CTC:batches'
)
const container = new Contract(
addr,
getNewContractInterface('iOVM_ChainStorageContainer'),
this.signer.provider
)
let meta = await container.getGlobalMetadata()
// remove 0x
meta = meta.slice(2)
// convert to bytes27
meta = meta.slice(10)
const totalElements = meta.slice(-10)
const nextQueueIndex = meta.slice(-20, -10)
const lastTimestamp = parseInt(meta.slice(-30, -20), 16)
const lastBlockNumber = parseInt(meta.slice(-40, -30), 16)
this.logger.debug('Retrieved timestamp and block number from CTC', {
lastTimestamp,
lastBlockNumber,
export const getMessagesByTransactionHash = async (
l2RpcProvider: ethers.providers.JsonRpcProvider,
l2CrossDomainMessengerAddress: string,
l2TransactionHash: string
): Promise => {
// Complain if we can't find the given transaction.
const transaction = await l2RpcProvider.getTransaction(l2TransactionHash)
if (transaction === null) {
throw new Error(`unable to find tx with hash: ${l2TransactionHash}`)
}
const l2CrossDomainMessenger = new ethers.Contract(
l2CrossDomainMessengerAddress,
getContractInterface('OVM_L2CrossDomainMessenger'),
l2RpcProvider
)
// Find all SentMessage events created in the same block as the given transaction. This is
// reliable because we should only have one transaction per block.
const sentMessageEvents = await l2CrossDomainMessenger.queryFilter(
l2CrossDomainMessenger.filters.SentMessage(),
transaction.blockNumber,
transaction.blockNumber
)
// Decode the messages and turn them into a nicer struct.
const sentMessages = sentMessageEvents.map((sentMessageEvent) => {
const encodedMessage = sentMessageEvent.args.message
const decodedMessage = l2CrossDomainMessenger.interface.decodeFunctionData(
'relayMessage',
getExtraData: async (event, l1RpcProvider) => {
const l1Transaction = await event.getTransaction()
const eventBlock = await event.getBlock()
// TODO: We need to update our events so that we actually have enough information to parse this
// batch without having to pull out this extra event. For the meantime, we need to find this
// "TransactonBatchAppended" event to get the rest of the data.
const OVM_CanonicalTransactionChain = getContractFactory(
'OVM_CanonicalTransactionChain'
)
.attach(event.address)
.connect(l1RpcProvider)
const batchSubmissionEvent = (
await OVM_CanonicalTransactionChain.queryFilter(
OVM_CanonicalTransactionChain.filters.TransactionBatchAppended(),
eventBlock.number,
eventBlock.number
)
).find((foundEvent: ethers.Event) => {
// We might have more than one event in this block, so we specifically want to find a
// "TransactonBatchAppended" event emitted immediately before the event in question.
return (
foundEvent.transactionHash === event.transactionHash &&
// https://github.com/ethereum-optimism/optimism/blob/c84d3450225306abbb39b4e7d6d82424341df2be/packages/contracts/contracts/optimistic-ethereum/OVM/predeploys/OVM_L2ToL1MessagePasser.sol#L23
// You can read more about how Solidity storage slots are computed for mappings here:
// https://docs.soliditylang.org/en/v0.8.4/internals/layout_in_storage.html#mappings-and-dynamic-arrays
const messageSlot = ethers.utils.keccak256(
ethers.utils.keccak256(
encodeCrossDomainMessage(message) +
remove0x(l2CrossDomainMessengerAddress)
) + '00'.repeat(32)
)
// We need a Merkle trie proof for the given storage slot. This allows us to prove to L1 that
// the message was actually sent on L2.
const stateTrieProof = await getStateTrieProof(
l2RpcProvider,
l2Transaction.blockNumber,
predeploys.OVM_L2ToL1MessagePasser,
messageSlot
)
// State roots are published in batches to L1 and correspond 1:1 to transactions. We compute a
// Merkle root for these state roots so that we only need to store the minimum amount of
// information on-chain. So we need to create a Merkle proof for the specific state root that
// corresponds to this transaction.
const stateRootMerkleProof = getMerkleTreeProof(
batch.stateRoots,
txIndexInBatch
)
// We now have enough information to create the message proof.
const proof: CrossDomainMessageProof = {
stateRoot: batch.stateRoots[txIndexInBatch],
stateRootBatchHeader: batch.header,
export const loadContract = (
name: string,
address: string,
provider: JsonRpcProvider
): Contract => {
return new Contract(address, getContractInterface(name) as any, provider)
}
export const getStateBatchAppendedEventByTransactionIndex = async (
l1RpcProvider: ethers.providers.JsonRpcProvider,
l1StateCommitmentChainAddress: string,
l2TransactionIndex: number
): Promise => {
const l1StateCommitmentChain = new ethers.Contract(
l1StateCommitmentChainAddress,
getContractInterface('OVM_StateCommitmentChain'),
l1RpcProvider
)
const getStateBatchAppendedEventByBatchIndex = async (
index: number
): Promise => {
const eventQueryResult = await l1StateCommitmentChain.queryFilter(
l1StateCommitmentChain.filters.StateBatchAppended(index)
)
if (eventQueryResult.length === 0) {
return null
} else {
return eventQueryResult[0]
}
}
const encodeCrossDomainMessage = (message: CrossDomainMessage): string => {
return getContractInterface(
'OVM_L2CrossDomainMessenger'
).encodeFunctionData('relayMessage', [
message.target,
message.sender,
message.message,
message.messageNonce,
])
}
parseEvent: (event, extraData) => {
const stateRoots = getContractFactory(
'OVM_StateCommitmentChain'
).interface.decodeFunctionData(
'appendStateBatch',
extraData.l1TransactionData
)[0]
const stateRootEntries: StateRootEntry[] = []
for (let i = 0; i < stateRoots.length; i++) {
stateRootEntries.push({
index: event.args._prevTotalElements.add(BigNumber.from(i)).toNumber(),
batchIndex: event.args._batchIndex.toNumber(),
value: stateRoots[i],
confirmed: true,
})
}