Optimal One-sided Supply to Uniswap πŸ¦„

Optimal One-sided Supply to Uniswap πŸ¦„

We have just launched Alpha Lending on BSC testnet here: https://bsctestnet.alphafinance.io/
More information on how to participate:

Yet, we're already working on our second Alpha product for users to yield farm with ETH, and we have stumbled upon this engineering/design question:

A user wants to yield farm UNI on Uniswap. How do we do it best?

If the user has both of ETH and the other token in proportionate amount to the pool, then he/she can just directly supply them, obtaining maximum efficiency without facing any impermanent loss.

But this is not always the case. Some users might not have the exact proportion of tokens, and even worse they might only have only ETH.

How to yield farm on Uniswap with only ETH?

How do we solve this scenario while maximizing user's asset efficiency. One approach is to swap ETH to get proportionate amount of the other token, then supply those tokens to the pool.

Swap-then-Supply πŸ”„ ➑️

By swapping, the user loses value due to price slippage and is prone to impermanent loss in case an arbitrageur comes and rebalances the pool.

For small deposits, however, price slippage is insignificant if the pool is deep enough, and the loss can be covered by the yield to be obtained. Now, the question becomes:

What is the optimal amount of ETH to swap to get the exact proportionate amount of another token?

In Uniswap, assuming the pool is at equilibrium, the total value of each asset in the pool should be equal. So, to supply to the pool, we’d also need an equal value of the two assets too.

A simple, natural solution: swap half of ETH to another asset in equal value!

First try: Swap half of the asset value

Step 1: The pool has 12,000 ETH + 520 WBTC. The user wants to supply 2,500 ETH.

Step 2: The user swaps in 1,250 ETH and receives 48.92 WBTC (receives slightly less due to 0.3% swap fee). The pool now has 13,250 ETH + 471.08 WBTC.

Step 3: The user supplies 1,250 ETH + 44.44 WBTC to the pool. The pool now has 15,000 ETH + 512.52 WBTC, but the user has 0 ETH + 4.48 WBTC remaining.

There is a total of unutilized 4.48 WBTC remaining in the user's balance.

So, what went wrong? ☠️

Two major problems arise when swapping from one asset to another:

  • Swap fee πŸ’° (0.3% for Uniswap) - With swap fee, the user receives slightly less amount of the swap out asset.
  • The new reserve's asset ratio πŸ“Š - The swap alters the reserve ratio, increasing the amount of supplied asset and decreasing the amount of the withdrawn asset.

Thus, the optimal swap amount needs to take into account these issues. In this article, we explore the formula (with proofs!) to magically solve the optimal swap amount for supplying one-sided liquidity.

Solving with Mathemagic πŸ§™β€β™‚οΈ

Let the two assets be asset \(A\) and asset \(B\), and let

$$\begin{aligned}&res_A = \text{amount of asset \(A\) in the \(A\)-\(B\) Uniswap pool reserve,} \\ &res_B = \text{amount of asset \(B\) in the \(A\)-\(B\) Uniswap pool reserve,} \\ &amt_A = \text{amount of asset \(A\) the user wants to supply optimally, and} \\ &f = \text{swap fee (0.3\% for Uniswap).} \end{aligned}$$

The goal is to find the optimal \(swapAmt_A\) to get a corresponding amount of asset B, so that the proportion of assets the user holds is equal to the proportion of assets in reserves after swap. We proceed in 2 steps:

  • calculating the amount of asset \(B\) received from swap, and
  • the user's asset ratio should now be the same as reserve's after the swap.

Calculating the amount of asset \(B\) received

The initial constant product is given by

$$k = res_A \cdot res_B.$$

According to Uniswap's implementation, swap fee is deducted from input asset amount, so the new \(res'_B\) should satisfy:

$$ k = (res_A + (1-f) \cdot swapAmt_A)\cdot res'_B. $$

This means the user will receive amount of asset \(B\) equal to

$$ \begin{aligned} rcvAmt_B &= res_B - res'_B \\ &= res_B - \frac{k}{res_A + (1-f)\cdot swapAmt_A} \\ &= res_B - \frac{res_A \cdot res_B}{res_A + (1-f)\cdot swapAmt_A} \\ &= \frac{(1-f)\cdot res_B \cdot swapAmt_A}{res_A + (1-f)\cdot swapAmt_A}.\end{aligned} $$

Note: The swap fee is only for calculating amount of the output asset. The new reserve of asset \(A\) is still \(res_A + swapAmt_A\).

User's asset ratio = reserve's asset ratio

The optimal \(swapAmt_A\) should satisfy the equality constraint on user's asset ratio and reserve's asset ratio:

$$\begin{aligned} \frac{amt_A - swapAmt_A}{res_A + swapAmt_A} = \frac{rcvAmt_B}{res'_B}. \end{aligned}$$

Substituting known variables \(rcvAmt_A\) and \(res'_B\), and rearranging the equation yields a quadratic equation in variable \(swapAmount_A\) as follows:

$$\begin{aligned}&(1-f)\cdot (swapAmt_A)^2 Β + \left((2-f)\cdot res_A\right) \cdot swapAmt_A - amt_A \cdot res_A = 0.\end{aligned}$$

Solving for \(swapAmt_A\) 🧠

Solving the above equation for a non-negative root yields:

$$\begin{aligned} swapAmt_A = \frac{\sqrt{((2-f)\cdot res_A)^2 + 4(1-f) \cdot amt_A \cdot res_A} - (2-f)\cdot res_A}{2(1-f)}.\end{aligned}$$

Observation: \(swapAmt_A\) only depends on \(res_A\), \(amt_A\), and \(f\). The amount of asset \(B\) in reserve pool does not affect \(swapAmt_A\) at all!

In actual implementation (\(f\) = 0.003), we can avoid floating point numbers by multiplying both the numerator and the denominator by 1000, i.e.,

$$\begin{aligned} swapAmt_A = \left\lfloor\frac{\left\lfloor\sqrt{1997^2\cdot res_A^2 + 4\cdot 997\cdot 1000 \cdot amt_A \cdot res_A}\right\rfloor - 1997\cdot res_A}{2 \cdot 997}\right\rfloor.\end{aligned}$$

Back to our Example

Step 1: The pool has 12,000 ETH + 520 WBTC. The user wants to supply 2,500 ETH.

Step 2: The user swaps in 1,192.70 ETH and receives 46.88 WBTC (receives slightly less due to 0.3% swap fee). The pool now has 13,192.70 ETH + 473.12 WBTC.

Step 3: The user supplies 1,307.30 ETH + 46.88 WBTC to the pool. The pool now has 15,000 ETH + 520 WBTC, and the user has 0 ETH + 0 WBTC remaining!

The user no longer has leftover assets! πŸ₯³

In practice, there might be a very insignificant amount leftovers due to floating-point rounding.

\(swapAmt_A\) Rounding Error Estimation

How large can the rounding be off by? Each floor function in the last formula can contribute to at most value of 1 difference from the actual value without floor function. So,

$$\text{Total \(swapAmt_A\) rounding error} \le 2.$$

This minimal offset can be used to show that the user's unutilized (remaining) asset value is also minimal.

How does the formula look in real implementation? πŸ’»

Using the Babylonian iterative square-root method for sqrt, the optimal swap amount is fairly straightforward from the last formula:

So, we can now supply liquidity to Uniswap pool having only ETH πŸŽ‰ πŸŽ‰ πŸŽ‰!

Note: While writing this article, we've noticed that zapper.fi supports one-sided liquidity supply to Uniswap pool. After checking their smart contracts, we are glad to see that the calculation formula used for the optimal swap amount turns out to be exactly the same, confirming our finding!

What's next?

So, here in this article we have presented an algorithm to efficiently supply one-sided liquidity to Uniswap pool, which works well for small liquidity providers (taking insignificant slippage from swaps). Stay tuned for our announcement on the second Alpha product, allowing users to efficiently yield farm with ETH on Uniswap!

About Alpha

Alpha Finance Lab is an ecosystem of DeFi products, starting on Binance Smart Chain and Ethereum. Alpha Finance Lab is focused on building an ecosystem of automated yield-maximizing Alpha products that interoperate to bring optimal alpha to users on a cross-chain level.

We are moving at a rapid pace, so we encourage everyone to join our Discord for the latest updates, follow us on Twitter, or read more about us on our Blog!