
find the contest here
The scope of this contest was the Metropolis liquidity book vault contracts. Since this system is completely untied to other kinds of vaults made by metropolis, we assume its a whole new project. The vaults here are smart contracts that manage liquidity into DLMM pools, the pair pools made by Trader Joe's. You can think of an DLMM as almost the same as an Uniswap V3 pool, except the fee composition is different. Bear in mind that DLMM pools were out of scope.
When adding liquidity to a DLMM, if you add it to the current price point, with an unbalanced amount of funds (more X than Y), you are charged a composition fee. 25% of it goes to the protocol, 75% to the previous LPs. The amount of fees to be paid depends both on how much imbalance is added and the size of funds currently available in the tick. So to maximize the amount of fees paid, you have to provide liquidity to one asset only.
The vault is managed by an operator, who's the creator of the vault and its manager. The operator's job is to call the rebalance() function to adjust the vault's liquidity positions in the DLMM. The amount of vault funds, as well as ratio of tokens added to each bin can be arbitrarily set by them, and the goal of these vaults is to maximize the trading fees collected with user supplied funds + yield that comes from liquidity mining.
A concern the sponsor had was to make sure there would have no faulty points, as many vault users were vibe coded bots (back in early 25 these bots were really trash).
If we assume the operator is malicious, there is a complex series of calls he can do in order to steal a large percentage of the funds in the vault. First, you provide most of the liquidity between 2 price bins, which will be heavily skewed towards one side. Then the attack unfolds as follows:
My PoC demonstrated a vault with 100k USD in user deposits (50k USDC + equivalent in AVAX). The operator collects ~150 USDC per cycle or 0.15% of vault funds.
At this rate, after \(n\) cycles, the vault value follows an exponential decay:
$$V(n) = 100,000\!USDC \times (1 - 0.0015)^n = V_0 \times (0.9985)^n$$
The amount extracted will look like this
Since each cycle requires several external calls with non trivial internal operations, the gas consumed is very high, about ~800k gwei per cycle. However, because the project was deployed on Sonic chain, the costs are negligible.
This entire operation can be executed in a single block. The number of times required to drain a certain % of the vault varies based on the configured fee in the pool. This gif shows the amount of funds remaining in the vault:
The fix is actually not that trivial, but there's one that can work due to game theory. Increase the vault cooldown to at least a few hours. The results is that, although the attack would be possible in theory, in practice it would take several weeks to pull a significant profit, but the vault itself would be wildly unprofitable for prolonged amounts of times, causing users to leave it for more successful ones, which would have more honest operators.
This finding was responsible for getting me the 1st place in the contest. What's important to take off from this, is the fact Trader Joe's DLMM pools were out of scope for the contest, but this vault contract still had issues with it. It is impossible to find this bug without auditing the DLMM pools. Now, see, bugs in external integrations are a pattern you will seldom encounter, but as far as contests goes, it is BY FAR the most +EV types of bugs, the ones very few people catch (simao has a student that earned almost 10k for a bug related to external dependencies, said student only had between 1-2k total earnings from contests before that point). If you are participating in a contest, or even a bug bounty, you NEED to audit those third party integrations, regardless of what the scope says.
return