September 14, 2023: Recovery Snapshot 🚑📸
New repository captures pre-hack balances in CRV/ETH, $alETH and $msETH
Since the Vyper exploit, many protocols are returning to normal operations.
Notably, JPEG’d is basically back up and running, with just a few lingering amounts to be addressed.
Alchemix revived alETH earlier this month as well.
Metronome voted on recovery earlier this month.
Unrelated to the main hack, but the separate Zunami hack also has an active recovery contract.
The biggest question market to be addressed is the largest loss, in the CRV/ETH hack. This particular pool has seen no recovery, despite a very public bounty.
With public details scarce, affected LPs (such as the author) have had to be patient in an industry that’s notoriously impatient. The Curve Telegram chat lately has been this same conversation repeated about a thousand times:
Pilot Vietnam and Mimaklas, with the patience of saints, have been fielding the same question about the hack recovery for months. This chorus is only briefly punctuated by miscellaneous FUD-fueled inquests that mysteriously coincide with unusually severe downward price action. Not the most fun time to be hanging around the support channels.
Curve Snapshot
Finally though, affected users have received a glimpse beneath the veil. This morning a new repository was made public providing a snapshot of the carnage. As we walk through the guts of how it works, hopefully you’ll gain an appreciation of how this repository took so long to generate.
As you can divine from the screenshot of the repo here, the overall intent of the repository is to take a snapshot of three affected pools at the specific block when the hack occurred. This is done to get an exact accounting of each affected user’s stake. It doesn’t take a huge stretch of imagination to recognize that this is the necessary groundwork for offering users anything between a partial and full recovery.
The precise recovery mechanics aren’t yet divulged, but we offered some speculation here:
Specifically, Scoopy explained in the Alchemix Discord that the recovery is likely to involve tokens streamed over a period of time. Due to the volatile nature of the constituent token prices ($CRV and $ETH), this may result in users being under-compensated or over-compensated.
Action Item
Before we dive into the repo, we’ll recommend an action item for all affected users. The outputs of all these scripts are stored in the data directory. If your address was affected, check the list of over 3000 affected users in all_users.csv and search for your wallet address.
If you believe you were a victim of the hack and don’t see your address, please contact support channels immediately to debug.
You should also browse to the appropriate snapshot file depending on where you were staked and check if your balance is what you expect. My back of the envelope based on transactions just before the hack:
In dollar value, this puts the price of one LP token at around $75 just before the hack. You can get your share of LP tokens just before the hack by looking at the LP Balance column and dividing by 10 ** 18.
The Guts
As the data folder hints, you can note that this is not as easy as looking at a single contract, but rather required separate scripts to parse out exact positions from several different protocols.
Looking into the import directory, you can see that users may have staked their LP position into Convex, Stake DAO, Yearn, directly into the pool’s gauge, or sitting in the pool.
The first step in the process is simply coming up with the master user list, which is done by looking at the entire history of events emitted by the relevant LP, Gauge, and Yearn contracts
Armed with a master user list, each other scraper iterates through the entire set of users for every place they could have moved their funds (ie Curve gauge, Convex, Yearn, Stake DAO). By listening for relevant events, you can trace every inflow and outflow of funds to arrive at a final balance.
For example, here is the relevant code tracking events within the main CRV/ETH liquidity pool.
These get used to generate two reports. Firstly, a top level summary.
And second, a detailed line by line accounting.
There’s yet another catch though… like nesting dolls, some of these positions are counted multiple times. That is, a user in Convex is also counted via Convex’s aggregate deposit into the gauge. And the aggregate deposits in the gauge is reflected in the overall pool deposits.
You can see in the above accounting that the fourth column denotes when there’s interesting exceptions. The overwhelming majority of pool balances sits in the gauge.
Then within the gauge, the overwhelming majority are handled by a staking service
As we get this deep into the weeds, one imagine even further complications. Here is the balances of Convex holders
For the cases where the balance is held by a smart contract, what do you do? If the smart contract was coded immutably to interact with Convex, it may work perfectly under normal circumstances. But such developers probably didn’t plan the ability to call an arbitrary claim function on the contract’s behalf.
So maybe Curve could just wire tokens directly to these contracts? But then what if the contracts don’t have a function to recover these tokens? You can wire the funds to an associated address, but how do you confirm this address is actually correct?
For Convex, this presents seventeen corner cases they need to figure out a solution before the recovery mechanism can be finalized, to say nothing of similar issues for all the other protocols.
In short, it’s not so simple as “why didn’t Curve just refund losses immediately?!?”
The logistics of getting everybody made precisely right are complex. Curve never had to deal with an exploit before, so it’s never had to manage such intricacies before. We certainly hope this is the first and last time this feat must be accomplished.