# ValueDeFi Incident: Incorrect Weighted Constant Product Invariant Calculation

[Disclaimer] This analysis is based on the initial finding by @FrankResearcher!

Started at 07:41:39 PM +UTC, May 7, 2021, ValueDeFi’s `vSwap` contract was exploited to drain a number of pools at the loss of about \$11M. The incident was due to the improper use of a complex exponentiation `power()` function behind the calculation and enforcement of the weighted constant product invariant. It is worthwhile to mention that `vSwap` uses the weighted constant product invariant formula for non 50-50 ratio pools. In the following, we elaborate the technical details.

# Summary

This incident was due to the mis-calculation by the protocol on the adopted weighted constant product invariant for non 50–50 ratio pools. There is no flashloan or price manipulation involved. The consequence of mis-calculated invariant allows for draining of affected pool funds. Currently, the bug has been exploited to attack 9 non 50–50 ratio pools with the estimated loss of \$11M.

# Details

## Incorrect Weighted Constant Product Invariant Calculation

The `vSwap` pool is initially forked from `UniswapV2` but with the support of non 50-50 ratio pools. The non 50-50 ratio pools require a different approach to calculate the weighted constant product, i.e., `reserve0^(weight0/50) * reserve1^(weight1/50) <= balance0Adjusted^(weight0/50) * balance1Adjusted^(weight1/50)`. Because of the required exponentiation function, ValueDeFi makes use of the Bancor formula to calculate the above constant product as shown below.

Note the `power()` routine takes four arguments (`baseN`, `baseD`, `expN`, and `expD`) and is used to calculate an integer approximation of `(baseN/baseD)^(expN/expD)*(2^precision)` where `precision` is used for the calculated result. However, there is a caveat in how this `power()` function should be used. It makes an explicit assumption that `baseN` must be no less than `baseD`, i.e., `baseN >= baseD`. In this attack, the pool’s `swap()` function are called with a crafted input that intentionally violates the above assumption. As a result, the weighted constant product enforcement is passed while the pool funds are being drained.

We started the analysis from the transaction behind one specific hack: 2fd0…c2de. This transaction has a number of operations against the non 50–50 ratio vBSWAP-WBNB pool and our elaboration only focuses on the first `swap()` operation operation. Specifically, this `swap()` operation in essence trades in 0.05 WBNB and trades out 1 WEI vBSWAP and 3,543.083896847545353949 WBNB! The purpose of trade-out of 1 WEI leads to `balance0Adjusted = reserve0 - 1 WEI`, which causes the execution to take the `else`-branch in the above figure. And the violated assumption on
`balance1Adjusted >= reserve1` corrupts the `power()` execution, hence passing the constant product enforcement.

If we examine the root cause behind this attack, the vulnerability stems from the improper use of a complex exponentiation `power()` function, which unfortunately lies in the critical path for invariant calcuation and enforcement. In the meantime, we also strongly suggest to make the assumption explicit in the `power()` function by adding the following requirement: `require(baseN >= baseD)`!

## The Funds

This attack leads to more than \$11M loss from the affected 9 pools. And most of the attacker’s funds from the above exploitations are currently held in this wallet: b88a. We are actively monitoring this wallet for any movement.