In our previous post on L2 internal transfers, we looked at why crypto tax platforms should evaluate L2 support as a reconciliation problem and not just a chain coverage problem.
Internal transfers are one part of that problem. They help explain native value movement that happens inside contract execution.
L1 → L2 native deposits are another. When a user bridges ETH from Ethereum to an L2 like Base, the activity spans two chains:
Ethereum L1: ETH leaves the wallet
Base L2: ETH appears in the walletFor the user, this is one economic action: moving ETH from one network to another.
For a tax platform, the event is typically treated as a self-transfer or bridge deposit, with basis carried from the L1 asset to the L2 asset.
For a data provider, it isn’t a simple single-chain transaction. The provider has to identify the L1 bridge transaction, surface the bridge message, find the corresponding L2 finalization transaction, decode the relevant events, and make the relationship legible to whatever application is consuming the data.
Most wallet history APIs work one chain at a time. You ask for Ethereum activity. Then Base. Then Arbitrum.
That model works for many use cases, but a native bridge deposit is a cross-chain lifecycle event. A tax platform doesn’t only need to know that ETH left a wallet on Ethereum. It also needs to know where the ETH arrived, whether the same wallet received it, whether the amount matches, what gas was paid, and whether the resulting activity is a bridge transfer or something else (income, disposal, an unexplained inflow).
Without that linkage, the platform sees two disconnected facts:
Ethereum: user sent ETH to a bridge contract
Base: user received ETH on BaseThe tax-ready interpretation is:
User bridged ETH from Ethereum to Base
Classification: self-transfer / bridge deposit
Basis: carried forwardInternal transfers explain native value movement within a chain. Bridge deposits explain it across chains. The two problems share a workflow, but they need separate treatment.
The same problem appears in reverse for L2 → L1 native withdrawals, though the lifecycle is different. On OP Stack chains, for example, a withdrawal is initiated on L2 and finalized later on L1. This post focuses on the L1 → L2 path, but the product need is the same: wallet history should preserve the relationship between both sides of the bridge movement.
Wallet: 0xe5ef01fdeaa9a9777e58cd97406e01a3d69060f1
L1 chain: eth-mainnet
L1 transaction: 0x8832cdda4a84c46a20b2327ff9650608d78b0130443d56d3d15d276cf99a4cf3
Bridge contract: 0x3154cf16ccdb4c6d922629664174b904d80f2c35
Amount: 116.907042158321588902 ETHThe interesting question is not whether a data API can show this transaction. The better questions are:
Did the wallet send ETH to the Base bridge on Ethereum?
Do the L1 logs confirm this as a bridge deposit?
Is the OP Stack deposit message visible?
Can we find the corresponding Base-side deposit transaction?
Does the Base-side event confirm the same user and amount?
What would a normalized bridge-deposit object need to include?
A per-chain view of Ethereum identifies the L1 side of the bridge deposit. It shows that the wallet sent ETH to the Base bridge contract, the amount transferred, and the L1 gas paid:
{
"chain_name": "eth-mainnet",
"tx_hash": "0x8832cdda4a84c46a20b2327ff9650608d78b0130443d56d3d15d276cf99a4cf3",
"from_address": "0xe5ef01fdeaa9a9777e58cd97406e01a3d69060f1",
"to_address": "0x3154cf16ccdb4c6d922629664174b904d80f2c35",
"value": "116907042158321588902",
"fees_paid": "620498000000000",
"successful": true
}In wei terms:
116.907042158321588902 ETH bridged
0.000620498 ETH paid in L1 gasUseful, but incomplete.
The bridge lifecycle doesn’t end on Ethereum. The accounting question is whether the same value arrived on Base, whether the recipient matches, and whether the two sides can be treated as one bridge movement.
A chain-by-chain API leaves the tax platform to find and join the Base side on its own. That is the gap a multichain API closes.
The GoldRush Multichain Transactions API returns activity across chains for the same wallet.
For this wallet, the response includes both sides of the bridge lifecycle: the Ethereum L1 bridge initiation and the Base L2 bridge finalization. The relevant evidence isn’t naturally chain-scoped; it’s distributed across two chains. The multichain endpoint brings the pieces into one wallet-level response:
L1 Ethereum bridge transaction
L1 decoded bridge initiation events
OP Stack TransactionDeposited event
Base L2 deposit transaction
Base ETHBridgeFinalized event
Post-deposit Base wallet activityThe Ethereum side includes decoded bridge events. The first is ETHDepositInitiated:
{
"decoded": {
"name": "ETHDepositInitiated",
"signature": "ETHDepositInitiated(indexed address from, indexed address to, uint256 amount, bytes extraData)",
"params": [
{ "name": "from", "value": "0xe5ef01fdeaa9a9777e58cd97406e01a3d69060f1" },
{ "name": "to", "value": "0xe5ef01fdeaa9a9777e58cd97406e01a3d69060f1" },
{ "name": "amount", "value": "116907042158321588902" }
]
}
}The same transaction also emits ETHBridgeInitiated, again showing the same wallet as sender and recipient, and the same ETH amount.
{
"decoded": {
"name": "ETHBridgeInitiated",
"signature": "ETHBridgeInitiated(indexed address from, indexed address to, uint256 amount, bytes extraData)",
"params": [
{ "name": "from", "value": "0xe5ef01fdeaa9a9777e58cd97406e01a3d69060f1" },
{ "name": "to", "value": "0xe5ef01fdeaa9a9777e58cd97406e01a3d69060f1" },
{ "name": "amount", "value": "116907042158321588902" }
]
}
}That gives the tax platform a strong L1 signal: the wallet sent ETH to the Base bridge, the intended L2 recipient is the same wallet, and both the bridged amount and the L1 gas paid are known.
The L1 response carries one more signal worth calling out separately.
The L1 response also includes the OP Stack TransactionDeposited event emitted by the Base Portal.
{
"decoded": {
"name": "TransactionDeposited",
"signature": "TransactionDeposited(indexed address from, indexed address to, indexed uint256 version, bytes opaqueData)",
"params": [
{ "name": "from", "value": "0x977f82a600a1414e583f7f13623f1ac5d58b1c0b" },
{ "name": "to", "value": "0x4200000000000000000000000000000000000007" },
{ "name": "version", "value": "0" },
{ "name": "opaqueData", "value": "..." }
]
}
}This event is lower-level than ETHDepositInitiated or ETHBridgeInitiated. It is the OP Stack deposit message that leads to execution on Base.
For a data engineering team, it is the important raw bridge signal: the message payload that connects the L1 deposit transaction to the L2 deposit lifecycle.
It isn’t the object a tax platform wants to consume directly. The opaqueData payload still has to be interpreted, matched to the L2 result, and normalized into something accounting-friendly. A tax platform shouldn’t have to build OP Stack message decoding logic just to classify a bridge deposit.
The raw message is useful, but not sufficient on its own.
The Base-side deposit transaction appears in the multichain response as:
{
"chain_name": "base-mainnet",
"tx_hash": "0x683c187d53baf39e05804ec4a76e63099fb5988e62769e02256f6c54901e670a",
"from_address": "0x977f82a600a1414e583f7f13623f1ac5d58b1c0b",
"to_address": "0x4200000000000000000000000000000000000007",
"value": "116907042158321588902",
"gas_price": "0",
"fees_paid": "0",
"successful": true,
"chain_id": "8453"
}This is the matching Base-side transaction for the same deposit lifecycle.
The match shouldn’t be inferred from amount alone. For OP Stack deposits, the canonical link starts with the L1 TransactionDeposited event emitted by the portal. That message is what leads to the L2 deposit execution. The Base-side ETHBridgeFinalized event then confirms the user-level recipient and amount.
In short, the reliable join is the bridge message lifecycle, not a matching ETH value across two chains. In this example, GoldRush surfaces the ingredients to follow that lifecycle: the L1 TransactionDeposited event, the Base-side deposit transaction, and the decoded ETHBridgeFinalized event.
Two details about the Base-side transaction are worth flagging.
First, gas_price and fees_paid are zero. That is because this is an OP Stack deposit execution; the user paid the cost on the L1 transaction, which is why the L1 gas field is the one that matters for accounting.
Second, the transaction-level from_address and to_address are not the user wallet. They are Base/OP Stack system addresses involved in executing the deposit on L2. The user wallet appears only inside the decoded bridge finalization event.
A tax platform that only looks at top-level from and to fields will miss the user-level accounting meaning of the transaction. The decoded event is what tells us that the same wallet received the bridged ETH on Base.
ETHBridgeFinalizedThe Base transaction includes the event that confirms the bridge was finalized on L2.
{
"decoded": {
"name": "ETHBridgeFinalized",
"signature": "ETHBridgeFinalized(indexed address from, indexed address to, uint256 amount, bytes extraData)",
"params": [
{ "name": "from", "value": "0xe5ef01fdeaa9a9777e58cd97406e01a3d69060f1" },
{ "name": "to", "value": "0xe5ef01fdeaa9a9777e58cd97406e01a3d69060f1" },
{ "name": "amount", "value": "116907042158321588902" }
]
}
}The top-level Base transaction addresses tell us how the deposit was executed by the L2 system. The decoded ETHBridgeFinalized event tells us the accounting meaning: ETH was finalized from the original user to the same user on Base, for the same amount.
That is the semantic confirmation that closes the loop.
The multichain response also shows what happened after the bridge completed. Shortly after the Base deposit, the same wallet sends almost the same amount out on Base:
{
"chain_name": "base-mainnet",
"tx_hash": "0x1070b56a820cecf7edc46b7a4342cf3a397eb27dd753c0889bd467ac8a6c4c10",
"from_address": "0xe5ef01fdeaa9a9777e58cd97406e01a3d69060f1",
"to_address": "0x26b610a059de2488ebe3e0eda02ae17907917419",
"value": "116906992158321588902",
"fees_paid": "171120160819",
"successful": true
}A bridge deposit is often only one step in a longer wallet lifecycle. A user may bridge ETH to Base and immediately use it elsewhere.
If the Base receipt isn’t linked back to the Ethereum deposit, that later Base activity can look like it was funded by an unexplained inflow. With the bridge relationship preserved, the tax platform can follow the asset across chains before continuing with the Base activity.
For this example, GoldRush exposes the raw evidence needed to detect and reconstruct an ETH → Base native deposit. You can make this call from your browser with your GoldRush API key appended to the URL.
The underlying data is available. GoldRush already exposes the cross-chain evidence in one wallet-level response; the next step is normalizing it into a classified accounting event.
The ideal object would not force the consumer to understand every bridge contract, event signature, system address, message payload, and finalization event. It would package the lifecycle directly:
{
"type": "native_bridge_deposit",
"classification": "self_bridge_deposit",
"asset": "ETH",
"amount": "116.907042158321588902",
"amount_wei": "116907042158321588902",
"from_chain": "eth-mainnet",
"to_chain": "base-mainnet",
"from_address": "0xe5ef01fdeaa9a9777e58cd97406e01a3d69060f1",
"to_address": "0xe5ef01fdeaa9a9777e58cd97406e01a3d69060f1",
"l1_tx_hash": "0x8832cdda4a84c46a20b2327ff9650608d78b0130443d56d3d15d276cf99a4cf3",
"l2_tx_hash": "0x683c187d53baf39e05804ec4a76e63099fb5988e62769e02256f6c54901e670a",
"l1_gas_paid_wei": "620498000000000",
"l2_gas_paid_wei": "0",
"bridge": "Base Bridge"
}This is the abstraction tax and accounting platforms actually want. They don’t want to parse bridge internals for every L2. They want wallet history that already knows when native value has moved from L1 to L2, who controlled both sides, which transactions are linked, and how the event should be classified.
Cost basis continuity is the harder problem for tax platforms, not balance display.
If a user buys or receives ETH on Ethereum and then bridges it to Base, the cost basis shouldn’t disappear at the bridge boundary. The bridge should preserve the asset’s history as it moves between chains.
Without bridge-aware classification, the Base-side receipt can look like a new inflow. Depending on the platform’s rules and user workflow, that creates manual review, incorrect assumptions, or basis gaps.
With bridge-aware classification, the platform can treat the event as a transfer:
Ethereum ETH position decreases
Base ETH position increases
Basis carries forward
No unexplained income event
No missing acquisition historyNative bridge events belong in the same reconciliation workflow as internal transfers, gas, fees, and decoded transaction activity.
For this ETH → Base example, the Multichain Transactions API surfaces the important pieces: the L1 bridge transaction, the decoded bridge initiation events, the OP Stack TransactionDeposited event, the Base L2 deposit transaction, the Base ETHBridgeFinalized event, and the post-deposit Base wallet activity.
That gives tax platforms a stronger starting point than querying chain by chain and stitching together bridge activity from scratch.
When ETH moves from Ethereum to Base, an accounting system needs more than two chain-specific transactions. It needs the relationship between them. When the API response preserves that relationship, the Base receipt is no longer a standalone inflow. It’s part of the same asset movement that began on Ethereum.
That is the shift from transaction history to bridge-aware wallet history.
We’re exploring how to make this normalization easier to consume directly in GoldRush. If your tax or accounting workflow depends on native bridge event matching, we’d like to hear which chains, bridges, and output fields matter most to your team.