If you already run wallet reconciliation on Ethereum, the shape of the problem is familiar - external transactions alone do not always close a wallet as:
Gas has to be accounted for.
Failed transactions can still burn fees.
Internal native value movement has to be captured.
The final balance has to tie back to the spot onchain balance, or the discrepancy eventually becomes visible to the user.
On L2s, the failure modes are similar, but the data dependencies underneath them are more fragmented. The trace methods you rely on for mainnet may not be available on a given L2. A provider may expose replay tools but not address-filtered trace search. A block explorer may provide internal transaction APIs today, then later move access behind a paid tier or change coverage by chain.
This is why L2 internal transaction coverage should be evaluated as a procurement problem, and not only as an engineering problem. The question is not whether a data vendor lists a chain on its coverage page. The question is whether the data behind that claim is complete enough for accounting work.
The standard should be simple: can a wallet close?
Vendor coverage pages often collapse several different capabilities into one bullet. A platform may say it supports Base, Optimism, Arbitrum, or another EVM L2, but that can mean very different things depending on the underlying data.
Here is a simple support-level framework to help determine whether a chain is merely visible in the product or actually usable for tax and accounting reconciliation.
A chain can be Stage 1 on a vendor’s site while your roadmap assumes Stage 3. The difference may not show up during basic integration testing. It often shows up later as an unexplained balance gap, a missing internal transfer, or a support ticket from a user who knows the product is wrong.
The useful test is not whether the platform can display activity. It is whether the platform can sum native inflows, subtract native outflows, include gas and L2-specific fees, and match the wallet’s onchain balance at a known block.
If the numbers match, the data is probably complete enough to ship. If they do not, the residual is the procurement question. Is it a missing internal transaction? A failed transaction whose gas was dropped? An L1 data fee that was not modeled? A block explorer response that omitted a trace type? A provider endpoint that only has partial history?
That is where chain coverage turns into reconciliation debt.
Many tax and accounting platforms built their EVM workflows around mainnet assumptions. Those assumptions are not wrong, but they do not travel cleanly across L2s.
On mainnet, the playbook is relatively well understood. Many teams use a combination of address-indexed wallet history, receipts, gas, trace data, and sometimes Etherscan-family explorer APIs to fill the internal transaction gap. That does not make the problem easy, but the sourcing model is familiar.
On L2s, several assumptions weaken at once.
First, trace surfaces differ by client lineage and chain architecture. trace_filter is commonly associated with Parity-style trace APIs and Erigon-style infrastructure. Many L2 stacks expose Geth-style debug_* methods instead, such as debug_traceTransaction or debug_traceBlockByNumber. These can provide execution detail, but they do not automatically provide wallet-searchable internal transactions.
That distinction matters. trace_filter behaves like a search interface. It can answer a wallet-level question across a block range. Debug methods are replay interfaces. They can explain a known transaction or block, but they do not give you an address-indexed internal transaction history unless someone builds that index.
Second, provider exposure is separate from client capability. A client may support a namespace, but the hosted endpoint you use may not expose it. The method may only be available on a dedicated node, a higher tier, a shorter historical window, or not at all for that chain. Client documentation should be treated as an upper bound, not as proof of what your production endpoint can do.
Third, L2 fee accounting is more varied. On OP Stack chains such as Optimism and Base, users may pay an L1 data fee in addition to L2 execution gas. On Arbitrum Nitro, the equivalent cost is folded into Arbitrum’s own L1 posting model. After Dencun, blob-related fee mechanics also matter for rollups that use EIP-4844 data availability, including Base, Optimism, Arbitrum, Linea, and others. If a pipeline models only execution gas, the residual can look like a missing internal transaction even when the internal transfer data is correct.
Fourth, not all internal value flow is a simple CALL. CREATE and CREATE2 can transfer native value at deployment. SELFDESTRUCT transfers a contract’s balance to a beneficiary. EIP-6780 narrowed when contract code is actually deleted, but the value-transfer behavior remains, and pre-Cancun history still contains many of these flows. System-level transactions, deposits, withdrawals, and bridge flows can also move value through paths that do not look like a normal top-level user transfer.
A CALL-only internal transaction feed may look useful, but it can still miss value movement that matters for reconciliation.
For years, block explorers helped hide the complexity of internal transaction coverage. Developers could often turn to block explorer APIs for address-level transaction history and internal transaction endpoints. For many crypto tax and accounting pipelines, explorers became the practical fallback when raw RPC was not enough.
That fallback should no longer be treated as free infrastructure.
Recent public discussion around API access showed that some previously free explorer APIs have moved behind paid access as data volumes and infrastructure costs have increased. Regardless of the pricing decision, the lesson for tax platforms is straightforward: explorer APIs are commercial dependencies, not public goods.
That changes how they should be managed.
If your L2 internal transaction strategy depends on explorer APIs, then the dependency needs to be priced, monitored, and tested like any other critical vendor. You need to know which chains are covered, what rate limits apply, whether historical access is complete, which trace types are included, and what the fallback is if pricing or coverage changes.
The risk is not just that an API might become more expensive. The larger risk is that your internal transaction coverage is borrowed from an upstream system whose roadmap, pricing, and semantics you do not control.
Once internal transactions are treated as a derived data product, the sourcing options become clearer.
If a provider exposes trace_filter, arbtrace_filter, or an equivalent address-filtered method for a chain, you can query internal native value movement by wallet across block ranges. But this has to be validated per chain, per endpoint, per plan, and per historical depth. “Supported” sometimes means from a certain block forward, or only on a specific infrastructure tier.
If a provider exposes debug_traceBlockByNumber with a call tracer, you can trace blocks, flatten execution trees, extract native value movements, and index both sides of every flow by address. This is productionizable, but it is not a lightweight integration. You are taking on replay throughput, storage, reorg handling, schema design, chain-specific edge cases, and ongoing validation.
This is usually easier to integrate, but it requires careful evaluation. You need to know whether the feed is CALL-only or includes CREATE, CREATE2, SELFDESTRUCT, deposits, withdrawals, and other system-level flows. You need to know how failed and reverted frames are handled. You need to know whether gas and L2-specific fee components are included. Most importantly, you need proof that the data reconciles.
In this model, the product is not the raw trace. The product is a normalized flow table that can support accounting workflows.
A useful internal value-flow table usually needs fields such as:
chain_id
block_number
block_timestamp
tx_hash
tx_index
trace_address
trace_type
from_address
to_address
value_native
success
error
fee_componentsIf that table does not exist somewhere in your stack, you probably do not have reconciliation-ready data. You have a transaction display plus a set of assumptions.
Most platforms will end up using a mix of these paths. They may use hosted trace search where it works, normalized vendor APIs where the semantics are strong, explorer APIs where they are stable, and their own indexer for chains where the external options do not pass reconciliation tests.
For each L2 your platform claims to support, your vendor or internal data team should be able to answer a short list of questions.
Source. Is internal transaction data coming from a Trace API, Debug API, explorer API, normalized vendor API, or in-house indexer?
Historical depth. Is coverage available from genesis, or only from a later block? If it starts later, is that clearly documented and handled in the product?
Trace types. Is the feed CALL-only, or does it also include CREATE, CREATE2, SELFDESTRUCT, deposits, withdrawals, and other system-level flows?
Failure semantics. Are reverted frames excluded or classified correctly? Is gas from failed top-level transactions captured as an outflow?
Fee attribution. Are OP Stack L1 data fees, Arbitrum-style L1 posting costs, blob-related fees, and other chain-specific components modeled and attributed per transaction?
Reconciliation proof. Can you produce a wallet on this chain whose calculated native balance matches the live balance at a recent block? If not, what is the residual and why?
Continuity risk. If the upstream provider changes pricing, disables a namespace, reduces free explorer access, or changes coverage, what is the fallback?
The first six questions are about data quality. The seventh is about whether the data dependency is under your control.
The next wave of differentiation in crypto tax and accounting will not come from adding more chain logos to a coverage page. Every serious platform will support a long list of networks.
The harder question is which of those networks can actually reconcile.
That matters because reconciliation debt eventually becomes customer-facing. It shows up as unexplained balances, manual adjustment workflows, support escalations, restated user reports, and engineering time spent diagnosing gaps that should have been caught at the data procurement layer.
For each chain on the supported list, the internal standard should be clear. The platform should know how wallet history is built, where internal native value flow comes from, how gas and chain-specific fees are handled, which upstream system owns the data, and whether the final result ties back to the current onchain balance.
A practical next step is to run the checklist above against every L2 on the supported-chain list before the next quarterly data review. Any chain that cannot pass the reconciliation test should be labeled internally as transaction-visible, internals-aware, or reconciliation-ready, rather than simply marked as supported.
The standard, again, is whether the wallet closes.