← Blog

2026-08-03

Locker rewards and auto-compounder noise: Stake DAO and Concentrator

Curve liquidity lockers and their auto-compounding wrappers are a good example of why "just parse the events" doesn't scale past the handful of protocols every tax tool already supports. We added two of them - Stake DAO's sdCRV gauge and Concentrator's (AladdinDAO) aCRV vault - and they turned out to need opposite fixes.

Stake DAO: a claim that emits nothing

Staking and unstaking sdCRV in Stake DAO's gauge is unremarkable: Deposit and Withdraw events, decoded the same way as any other Curve-style gauge.

Claiming rewards is where it gets interesting. We pulled a real claim_rewards() transaction from mainnet and checked its full receipt: the gauge contract itself emits zero logs. Not a missing field, not an event we hadn't mapped yet - the contract doesn't fire anything at all when a reward claim happens. The only on-chain signal is four plain ERC20 transfers (CRV, SDT, 3Crv, crvUSD) sent directly from the gauge to the claimer.

That meant the normal decode path - match a log's address to a known protocol contract, then read its event args - had nothing to anchor on. We added a second mechanism: match on the transaction's target contract instead of a log's address, then reconstruct the claim from the plain transfers that land in the same receipt. It only attributes a claim correctly when the wallet that paid gas is also the one receiving the tokens - a direct claim from your own wallet. A claim routed through a keeper bot or an auto-compounding strategy (which we also found in the wild while verifying this) won't show up, by design: we'd rather return nothing than guess wrong and hand back a mislabeled line.

Concentrator: an event that fires but isn't yours

Concentrator's aCRV vault is a standard ERC-4626 auto-compounder over cvxCRV, so Deposit and Withdraw decode cleanly with no special handling.

The vault also emits a Harvest event every time anyone calls the auto-compound function - which can happen inside your own deposit or withdraw transaction as a side effect, or in a transaction you have nothing to do with, triggered by a keeper bot compounding on behalf of everyone in the vault. It's not a claim by whoever sent the transaction, and it's not something that belongs in your transaction history at all.

We could have let it fall through to an "unclassified" line, but that would tell you to go look something up that isn't actually yours to look up. So that event is explicitly suppressed at the config level: recognized, and deliberately produces no output line, rather than a misleading one.

The pattern

Both cases boil down to the same principle: a missing label is an inconvenience, a wrong label is a bug in your tax return. Where the on-chain data doesn't cleanly support attributing an event to a specific user, we leave it out rather than guess. As always, what comes back is a classification - "this is a Stake DAO reward claim" - never a cost basis or a tax position.