As rollups gain more and more traction in the web3 space, the integration and interaction between Layer 1 (L1) and Layer 2 (L2) solutions are pivotal for scalability and efficiency. Recognising a gap in the Optimism rollup ecosystem, our team at Blocktorch was up for the challenge to develop & open source a robust solution aimed at linking L2 transactions from Optimism rollups with the L1 transactions in which they were submitted. Existing block explorers for Optimism do not display this relationship, leaving a void in transaction traceability and transparency.
Why do we need to track transactions between L1 and L2?
The primary goal of the @blocktorch/optimism-batch-decoder package is to forge a connection between L1 and L2 transactions. Previously, it was a cumbersome task to relate L2 transactions to the batch transactions on L1 , making it difficult for developers and analysts to track and verify transaction flows. Our solution brings clarity and accessibility to this process, enhancing the user experience and trust in the Optimism network as well as giving protocols a clearer overview of how to optimise their inter-layer operations. Strengthening the relation between the layers will bring more visibility to the flow of funds and actions on the blockchain.
How Does It Work?
Our approach involves tracking the transactions sent to a specific Batch Inbox Address on L1. For Ethereum, which serves as the L1 in this context, the address in question is 0xFF00000000000000000000000000000000000010
. Transactions sent to this address from Batchers include calldata which encapsulates the encoded and compressed L2 transactions. Opting for calldata as the transmission medium is a strategic choice by Optimism to cut down on gas fees. Given that the Batch Inbox Address is an Externally Owned Address (EOA), the calldata isn't executed but merely serves as a vessel for transferring the L2 transaction data.
The core of our solution lies in the subsequent steps - decompressing and decoding the calldata in question. This process extracts the L2 transactions, rendering them in a comprehensible and traceable format.
Processing the Batcher Transaction Format
Step 1 is to extract the frames from the raw calldata. The calldata starts with a version byte, followed by one or more frames concatenated. Each frame contains a channelId
, frameNumber
, isLast
flag and data
.
Most interesting is the data
. It is comprised of a list of compressed and RLP encoded Batches.
Therefore, the step 2 is to decompress the data. For that we use the zlib library in NodeJS, more specifically the inflate
method.
After we have the decompressed data, we will RLP-decode each batch to get the almost final data. We will need to RLP-decode the batch once more to be left with the human-readable final result. This will contain the parentHash
, epochNum
, epochHash
, timestamp
and list
of L2 transactions .
More in depth information about the Batcher Transaction format can be found in the Optimism documentation.
The blocktorch optimism batch decoder
The release of the @blocktorch/optimism-batch-decoder NPM package marks a significant milestone in L1-L2 transaction integration. By harnessing this tool, users can now actively monitor the Batch Inbox Address transactions, decode the embedded calldata, and store the results. This not only simplifies the linkage of L2 transactions to their L1 counterparts but also ensures that the relationship is bidirectional, allowing for traceability from L1 to L2 and vice versa.
In essence, the @blocktorch/optimism-batch-decoder is more than just a tool; it's a gateway to enhanced transparency, efficiency, and trust in the realm of blockchain transactions, fortifying the bridge between L1 and L2 rollups on the Optimism network.
Step by step guide to use the package
Let's take a look on how to get started to use the @blocktorch/optimism-batch-decoder open source package in NodeJS.
- First, let's install the package
npm install @blocktorch/optimism-batch-decoder
or
yarn add @blocktorch/optimism-batch-decoder
- The package exposes 3 functions, but only 2 are relevant for production use:
a. decodeBatcherTransactionCalldata(calldata)
- to decode the raw calldata hex string
b.decodeBatcherTransaction(txHash, providerUrl)
- to decode the calldata by providing just the transaction hash of the Batcher Transaction and a node provider URL (for example from QuickNode, Alchemy or Infura)
- Next, we'll need to find a Batcher Transaction and get its calldata. In order to do that we will
a. Look for the Batcher Inbox Address on etherscan (find the Batcher Inbox Address from our example here)
b. Choose a transaction, click on it, expand the 'More Details' by clicking '+ Click to see more data' and copy the Input Data
, which looks like a long hex string, starting with '0x'
- Now we can decode that calldata using the
optimism-batch-decoder
package.
import { decodeBatcherTransactionCalldata } from '@blocktorch/optimism-batch-decoder'
const batcherTransaction = await decodeBatcherTransactionCalldata(calldata)
- The
batcherTransaction
that we received contains all L2 transactions submitted in that batch.
The result itself contains a list of frames, which in turn contains a list of batches and those in turn contain a list of transactions. You can learn about the format of a Batcher Transaction in the documentation.
{
version: 0,
frames: [
{
channelId: '10aacce0484eaeb6f1d8ebb09da55daf',
frameNumber: 0,
isLast: true,
batches: [
{
inner: {
parentHash: '0x7cdcbceffee73a13250e3c2f4685ed2672806db2071db2ee7d7124fc15890e64',
epochNum: 19028173,
epochHash: '0x7fdf459fa7a04f8e71e05c38ab4d45dd8d7ef310fce466300d49ff17981846bd',
timestamp: 1705513631,
transactions: [
{
r: '0x83ca297b25fb2e7cb1b92f2b1d74765406bedd7feb70e40b576d1f8123c2c7de',
s: '0x419977c9ffe454c0396ed235ce0143312852c738bb15f022353ac5cccb4b4fd0',
v: 28n,
yParity: 1,
chainId: 10,
type: 'eip1559',
to: '0xa859bb553e59f88f5458e7929628da90e8a62f7a',
gas: 100000n,
nonce: 271683,
value: 990000000000001n,
maxFeePerGas: 58384420n,
maxPriorityFeePerGas: 11676884n,
hash: '0x2f7d02c15c1f4246e3f5534a2c2cd94ac3a809ee181d1a8ba6287317ca293d99'
},
{
type: 'legacy',
to: '0x6a38d27d00214c2a098167121862d605fb317433',
gas: 179412n,
data: '0xeb79e7da00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000040000000000000000000000006c531cef0a771865cc31dc072da1293fa242cd65000000000000000000000000eeeca76c9d9d0f9029d919a5fcdd38335753435a0000000000000000000000002850b65530eeebf8aa1319b6de361716e086351000000000000000000000000050a5bb2fcf37c1856590f19dce1f646365271ff5000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000003205af76700000000000000000000000000000000000000000000000000000002e4c5bef8fc200000000000000000000000000000000000000000000000000003205af76700000000000000000000000000000000000000000000000000000002f9522c8cbc6000000000000000000000000000000000000000000000000000000000000000200000000000000000000000094b008aa00579c1307b0ef2c499ad98a8ce58e5800000000000000000000000094b008aa00579c1307b0ef2c499ad98a8ce58e5800000000000000000000000000000000000000000000000000000000000000020000000000000000000000006c531cef0a771865cc31dc072da1293fa242cd650000000000000000000000002850b65530eeebf8aa1319b6de361716e086351000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000001f3f00000000000000000000000000000000000000000000000000000000000005da',
nonce: 397,
value: 213223075036040n,
gasPrice: 13750000n,
chainId: 10,
v: 55n,
s: '0x2e578b523a28fdef289708ea86e81cad0b166a6b3156b2d45746fb021d9363a5',
r: '0x040a91a6231add92183b34c8101a3d54df2d10eca8f92f823d1b3fadd3e4cfb5',
hash: '0xf8d1f096efe48690737a137167df596044a10315cffca07e22fb4d98a502f07f'
},
{
type: 'legacy',
to: '0x099596aa2bd893d8418f9e649c875ca73cd86688',
gas: 1500000n,
data: '0x94b918de00260065a814d200000000000000000274dbec6000000009ffe7ab92d3a305dd',
nonce: 367040,
gasPrice: 11793652n,
chainId: 10,
v: 56n,
s: '0x770ffeba9e96b0216ffa1ece892b56524bf1d63424add31146710b1348d4d253',
r: '0xbab1d473fb53679040b3469ec2dff5163f6a2f613e43f5a554bc369043557ffd',
hash: '0x070c2a2be07b8e0507b0f7d2368d0db921bfc98765804da3a0c627ba64ae1b86'
}
]
}
}
]
}
]
}
🥳 Congrats you just decoded your first Batch Transactions sent from L2 to L1 on Optimism!
Summary
The @blocktorch/optimism-batch-decoder package helps link Layer 1 (L1) and Layer 2 (L2) transactions on the Optimism network. It makes it easier to see how L2 transactions relate to their L1 origins and provides a straightforward way to decode transactions. This tool improves clarity and efficiency in handling blockchain transactions. It's handy for developers and analysts who want to follow transaction paths more easily and ensures a smoother connection between L1 and L2 rollups. The package's ability to track transactions between L1 and L2 is a practical step in enhancing how different layers interact within the Optimism network.
We highly encourage you to contribute to the package by submitting a PR. You can find the github repository here: https://github.com/blocktorch-xyz/optimism-batch-decoder
At Blocktorch we are on a mission to give web3 builders superpowers. Our web3 observability platform provides end-2-end observability like real-time tracing, logging and monitoring for developers building decentralized applications and smart contracts. We support the full stack of any dApp.
Top comments (0)