Dev-Blog: On the Derivation of a Safe Price Formula for Balancer Pool Tokens
For Resonate to properly function, we needed a good way to price Balancer Pool Tokens (BPTs) — the Balancer version of Uniswap’s Liquidity Pool Tokens (LPs). This proved to be more of a challenge than had initially been anticipated, and this Dev-Blog describes the novel formulae and Solidity-based approaches we took to solving this problem.
Robustly pricing LPs (Liquidity Providers) is an important undertaking for any DeFi protocol dealing with, well, pricing LPs. We’ve seen the negative consequences of not doing so correctly: The Warp Finance Hack being a prime example; resulting in a loss of $7.8 million. A potential vulnerability lies in naively using token reserve amounts from on-chain pair data; which can be easily manipulated and cause the oracle to misprice the LP.
Christoph Michel wrote about this in more detail here and Alpha Ventures developed a formula to robustly price Uniswap LPs here.
It quickly became clear to us that we would need a method to robustly price LPs with an arbitrary number of tokens with arbitrary weights; not a simple 2-token-equal-weighted Uniswap LP. Our first step was utilizing Alpha’s method for deriving the formula for robustly pricing Uniswap LPs, and then subsequently modifying it so that we were able to derive a formula for robustly pricing 2-token LPs with arbitrary weights. Where Wi is the weight of the ith token, pi is the price of the ith token according to a safe-price oracle, Γ is the total supply of the LP tokens, and V is the pool invariant:
Next, we transformed this formula into a recursive formula. The idea being that a 3-token LP is just a 2-token LP where one of the two tokens is itself a 2-token LP; and a 4-token LP is just a 2-token LP where one of the two tokens is a 3-token LP, and so on. Expanding this recursive formula, we had a very good gauge of what a closed-form general-case formula should look like from the pattern that emerged, and proceeded to prove it through mathematical induction. Mathematical induction is a fundamental proof technique where: if a hypothesis is true for a certain base case, and if the hypothesis can be shown to be true for the (k+1)th case–assuming that it’s true for the kth case; then the hypothesis is true for all cases. The formula for an n-token LP token is:
So we had arrived at a proof that the general-case formula hypothesized was equivalent to the recursive formula developed earlier. This would be a sufficient, indirect proof that our closed-form formula was robust (since the general-case formula was proven equivalent to the recursive formula, and since the recursive formula at any step is really just a 2-token formula for arbitrary weights that we already knew was robust, it confirms that the general-case formula is, indeed, robust).
However, not being satisfied with that we set out to prove, directly, the robosity of the general-case formula. We did so by showing that the price (according to our formula) of an arbitrary LP was equal to the price of that LP:
1. After one token within that LP was traded for another
2. When new LPs were minted or old ones were burnt.
You can read the entire proof with complete details here.
You might think that this is the end of our struggles with trying to robustly price LPs with an arbitrary number of tokens and arbitrary weights, and that we’d be on our merry way from here. We thought so too; but development life is seldom so simple.
We ran into a road-block when trying to implement this formula in Solidity. As we came to find out, since Solidity does mathematics utilizing fixed-point arithmetic, and the formula in question requires raising a decimal to a decimal, this proved to be a real challenge for two reasons. First, using fixed point arithmetic with large numbers denominated in 1e18 often leads to overflows outside of the bounds of the 256-bit integers making the exponentiation of many numbers on-chain difficult. Second, when you attempt to divide, the lack of decimal places means that numbers can get rounded down, most often to zero.
The solution involved using an obscure mathematical library in solidity, PRBMathSD59x18, that makes use of many predetermined constants and obscure optimizations to keep our numbers accurate and within range.
The implementation of this oracle has been approved by BlockSec and represents the first oracle to be able to truly handle an arbitrary number of tokens with arbitrary weights. Consequently, this will be able to robustly price any Balancer LP token (BPT) moving into the future.
A public good from us here at Revest Finance, to the Balancer, BeethovenX, and DeFi community as a whole!
A big thank you to our Head of Operations — Andany, and Senior Solidity Developer — Josh, for completing this grand undertaking.