Tracking L1 → L2 Native Deposits for Crypto Tax Reconciliation

Harish RaisinghaniHarish Raisinghani9 min read
How GoldRush’s Multichain Transactions API exposes both sides of an ETH → Base deposit and why bridge-aware wallet history matters for tax platforms.

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 wallet

For 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.

Why native bridge deposits are hard to classify

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 Base

The tax-ready interpretation is:

User bridged ETH from Ethereum to Base Classification: self-transfer / bridge deposit Basis: carried forward

Internal 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.

An ETH → Base deposit

Wallet: 0xe5ef01fdeaa9a9777e58cd97406e01a3d69060f1 L1 chain: eth-mainnet L1 transaction: 0x8832cdda4a84c46a20b2327ff9650608d78b0130443d56d3d15d276cf99a4cf3 Bridge contract: 0x3154cf16ccdb4c6d922629664174b904d80f2c35 Amount: 116.907042158321588902 ETH

The 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?

What a per-chain transaction view shows, and what it misses

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 gas

Useful, 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.

What the GoldRush Multichain Transactions API adds

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 activity

The L1 bridge initiation

The 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 OP Stack deposit message

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

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.

The Base-side confirmation: ETHBridgeFinalized

The 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 post-deposit movement on Base

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.

What GoldRush provides today

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.

RequirementAvailable in the response?Notes
Decoded L1 bridge eventsYes`ETHDepositInitiated` and `ETHBridgeInitiated`.
OP Stack deposit messageYes`TransactionDeposited` with `opaqueData`.
Base L2 transactionYesThe matching Base transaction appears in the multichain response.
Base bridge finalization eventYes`ETHBridgeFinalized` confirms recipient and amount.
Same-wallet recipient matchingYesThe decoded events show the same address as sender and recipient.
Post-deposit Base activityYesThe later Base outbound transfer appears.
Normalized `native_bridge_deposit` objectNot yetThe ingredients are present, but not packaged as one accounting event.
Tax classificationNot yetThe response does not directly classify this as a self-transfer.
Basis carry-forward instructionNot yetThis remains application logic for the tax platform.

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.

What a tax-ready bridge deposit object could look like

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.

Why this matters for cost basis

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 history

Native bridge events belong in the same reconciliation workflow as internal transfers, gas, fees, and decoded transaction activity.

Where this fits, and what comes next

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.

Enjoyed this post?

Get more Web3 insights and updates delivered weekly. Join thousands of developers staying ahead in blockchain.

Get Started

Get started with GoldRush API in minutes. Sign up for a free API key and start building.

Support

Explore multiple support options! From FAQs for self-help to real-time interactions on Discord.

Contact Sales

Interested in our professional or enterprise plans? Contact our sales team to learn more.