The Spartan Incident: Root Cause Analysis

Started at 04:38:39 PM +UTC, May 1, 2021, the Spartan protocol contract was exploited to result in more than $30M loss. The incident was due to a flawed liquidity share calculation in the protocol, which is exploited to drain assets from the pool. In this blog post, we elaborate the technical details of the issue.

Summary

Details

The Improper Liquidity Share Calculation Vulnerability

  1. It borrows a flashloan from PancakeSwap with 100K WBNB, which will be returned at the last step with 260 WBNB as the flashloan fee;
  2. It swaps WBNB to SPARTAN five times through the exploited Spartan pool, with each time swapping in 1,913.172376149853767216 WBNB to get 621,865.037751148871481851 SPARTA, 555,430.671213257613862228 SPARTA, 499,085.759047974016386321 SPARTA, 450,888.746328171070956525 SPARTA, and 409,342.991760515634291439 SPARTA respectively. The resulting total 2,536,613.206101067206978364 SPARTA, plus 11,853.332738790033677468 WBNB, are then added into the pool, minting 933,350.959891510782264802 pool token (SPT1-WBNB);
  3. Similarly, it swaps WBNB to SPARTAN ten times through the same pool, with each time swapping in 1,674.025829131122046314 WBNB to get 336,553.226646584413691711 SPARTA, 316,580.407937459884368081 SPARTA, 298,333.47575083824346321 SPARTA, 281,619.23694472865873995 SPARTA, 266,270.782888292437349121 SPARTA, 252,143.313661963544185874 SPARTA, 239,110.715943602161587616 SPARTA, 227,062.743086833745362627 SPARTA, 215,902.679301559370989883 SPARTA, and 205,545.395265586231012643 SPARTA respectively, resulting in total 2,639,121.977427448690750716 SPARTA.
  4. It inflates the asset balance in the pool by transferring into the pool 21,632.147355962694186481 WBNB and all SPARTA from the above step 3, i.e., 2,639,121.977427448690750716 SPARTA.
  5. It burns the 933,350.959891510782264802 pool tokens obtained from step 2 to withdraw the liquidity. Since the pool’s asset balance is inflated, the burn operation leads to 2,538,199.153113548855179986 SPARTA and 20,694.059368262615067224 WBNB. Note that step 2 only deposits 11,853.332738790033677468 WBNB, leading to the profit of about 9K WBNB.
  6. It adds the liquidity into the pool with the added assets in step 4 with 1,414,010.159908048805295494 pool token, which is immediately burned to obtain 2,643,882.074112804607308497 SPARTA and 21,555.69728926154636986 WBNB.
  7. It repeats the above steps to continue draining funds from the pool.
  8. It returns the flashloan with 100,260 WBNB.

The vulnerability stems from the fact that the liquidity share calculation calcLiquidityShare() is querying the current balance which can then be inflated for manipulation. A correct calculation needs to make use of cached balance in baseAmountPooled/tokenAmountPooled.

The Funds

About Us