Skip to content

How to implement LP-Tokens

Table of contents

Open Table of contents

Introduction

The Peer-to-Contract model is a common pattern used in DeFi to implement protocols that need access to deep liquidity. Rather than users transacting directly with each other in a Peer-to-Peer fashion, liquidity is supplied to a Smart Contract that does the market-making. This way users interact solely with the Smart Contract which allows for asynchronous participation.

Users who supply liquidity to Smart Contracts are called “Liquidity Providers” (LPs). Smart Contracts that follow this model are usually called “Vaults” or “Pools” (in this article we’ll refer to them as Pools). To incentivize participation users oftentimes get paid in the Protocol’s native Token and / or get a cut of the fees that accumulate whenever someone transacts with the Smart Contract.

Given that LPs might unwind their positions at any point in time by withdrawing their funds, one needs to keep track of the user’s asset share in the Pool.

If Alice supplied 25% of all the ETH in the Pool, she should be able to withdraw 25% of the ETH.

If she earned fees in ETH as well, she should be allowed to withdraw the amount of ETH she initially deposited plus all the accrued fees she’s entitled to. The accrued fees are usually proportional to the liquidity contributions, meaning that if Alice contributed 25% of all the ETH in the Pool, she should also get 25% of all the fees that were accumulated.

While the concept of liquidity provisioning and reward calculation looks simple on the surface, it’s not-so trivial to implement in Smart Contracts due to constraints such as gas-efficiency and immutability.

As it turns out, other projects like Uniswap faced the exact same issues before and came up with a clever solution to mitigate these problems: Liquidity Provider Tokens a.k.a “LP-Tokens”.

Liquidity Provider Tokens (LP-Tokens)

The core idea behind LP-Tokens is that of a receipt or “Proof-of-Deposit”. LP-Tokens are created whenever liquidity is added to a Pool. The user who owns the LP-Tokens thus owns an attestation that assets were deposited into the Pool and can be withdrawn at a later point in time.

LP-Tokens are standard ERC-20s that are minted when funds are deployed into the Smart Contract. They are burnt when funds are removed from the Smart Contract.

Given that LP-Tokens are ERC-20s they can be used like any other ERC-20 Token. Anyone who has access to the LP-Tokens can withdraw the Pool’s funds proportional to the share of liquidity provided. in fact one can think of LP-Tokens as Pool shares that track the ownership of the assets residing in the Pool.

Modelling LP-Tokens

Let’s start the modelling process by writing down what we already know about LP-Tokens and their usage in dApps.

There’s a Pool Smart Contract that users can deposit funds into or withdraw from. The Pool might or might not have an existing balance of already deposited Tokens. Let’s call this Pool Token balance:

BalanceTBalance_T

As a Liquidity Provider we can deposit new Tokens into the Pool to add new liquidity or remove liquidity from the Pool by withdrawing Tokens. Let’s call the Token amount the user might deposit or withdraw:

AmountTAmount_T

When depositing Tokens into the Pool new LP-Tokens minted and transferred to the depositor. Withdrawing Tokens from the Pool requires the burning of LP-Tokens. Let’s call the amount of LP-Tokens to mint / burn:

AmountLAmount_L

And then there’s the total supply of LP-Tokens that are currently in circulation. Let’s call this LP-Token supply:

SupplyLSupply_L

With these definitions out of the way we can formulate equations that should remain true when Liquidity Providers deposit funds into the Pool or remove them from the Pool.

Depositing Funds and minting LP-Tokens

In the first case, a user decides to provide liquidity by depositing Tokens into the Pool. The users should get LP-Tokens transferred in exchange for the added liquidity.

In mathematical terms we want to ensure that:

BalanceT+AmountTBalanceT=SupplyL+AmountLSupplyL\frac{Balance_T + Amount_T}{Balance_T} = \frac{Supply_L + Amount_L}{Supply_L}

The LP-Token amount that is minted should stay proportional to the new Tokens that were deposited into the Pool.

To know how many LP-Tokens we need to mint we can use some simple Algebra to isolate the AmountLAmount_L variable:

BalanceT+AmountTBalanceT=SupplyL+AmountLSupplyL\frac{Balance_T + Amount_T}{Balance_T} = \frac{Supply_L + Amount_L}{Supply_L} (BalanceT+AmountT)SupplyL=(SupplyL+AmountL)BalanceT(Balance_T + Amount_T)Supply_L = (Supply_L + Amount_L)Balance_T BalanceTSupplyL+AmountTSupplyL=SupplyLBalanceT+AmountLBalanceTBalance_T Supply_L + Amount_T Supply_L = Supply_L Balance_T + Amount_L Balance_T AmountTSupplyL=AmountLBalanceTAmount_T Supply_L = Amount_L Balance_T AmountT×SupplyLBalanceT=AmountL\frac{Amount_T \times Supply_L}{Balance_T} = Amount_L

If you’re looking closer at the formula you might figure that there’s a possibility of division by zero if the initial Pool balance (BalanceTBalance_T) is 0. To combat this problem we can introduce a simplification and say that the amount of LP-Tokens (AmountLAmount_L) to mint is equal to the amount of Tokens deposited (AmountTAmount_T) when the Pool balance (BalanceTBalance_T) is 0:

AmountT=AmountLAmount_T = Amount_L

Burning LP-Tokens to withdraw Funds

In the second case, a user wants to withdraw Tokens from the Pool and therefore remove liquidity. To do so, LP-Tokens need to be burnt first. Using our definitions from above we can formulate the following mathematical equation:

BalanceTAmountTBalanceT=SupplyLAmountLSupplyL\frac{Balance_T - Amount_T}{Balance_T} = \frac{Supply_L - Amount_L}{Supply_L}

We want to ensure that the LP-Token amount that is burnt is proportional to the Tokens withdrawn from the Pool.

To get the amount of Tokens the user should get back when burning the LP-Tokens we need to isolate the AmountTAmount_T variable. Applying some Algebra we get:

BalanceTAmountTBalanceT=SupplyLAmountLSupplyL\frac{Balance_T - Amount_T}{Balance_T} = \frac{Supply_L - Amount_L}{Supply_L} (BalanceTAmountT)SupplyL=(SupplyLAmountL)BalanceT(Balance_T - Amount_T)Supply_L = (Supply_L - Amount_L)Balance_T BalanceTSupplyLAmountTSupplyL=SupplyLBalanceTAmountLBalanceTBalance_T Supply_L - Amount_T Supply_L = Supply_L Balance_T - Amount_L Balance_T AmountTSupplyL=AmountLBalanceT-Amount_T Supply_L = -Amount_L Balance_T AmountTSupplyL=AmountLBalanceTAmount_T Supply_L = Amount_L Balance_T AmountT=AmountL×BalanceTSupplyLAmount_T = \frac{Amount_L \times Balance_T}{Supply_L}

Testing our Model

Let’s take our formulas for a spin to see if the LP-Token implementation works as expected. For the following example we assume that there are two users called Alice and Bob. Furthermore there’s a Pool Smart Contract both users plan to deposit funds into and later on withdraw funds from.

Let’s assume that Alice owns 10 TKN Tokens she wants to deposit and Bob has 20 TKN Tokens he wants to deposit. The current Pool balance of TKN Tokens is 0, meaning that no funds were deposited into the Pool Smart Contract before. LP-TKN Tokens are used as Liquidity Provider Tokens to track the ownership of assets in the Pool.

The initial setup is as follows:

AliceBobPoolSupply
TKN10200-
LP-TKN00-0

Alice decides that she wants to deposit 10 TKN into the Pool. Using our formula form above we can calculate how many LP-Tokens she should get in return. Notice that the Pool balance is 0 which is the edge case we talked about above. Because of this we can use the simplified formula which says that the LP-Tokens to be minted is equal to the amount of Tokens deposited:

AmountT=AmountLAmount_T = Amount_L 10=AmountL10 = Amount_L

Our updated balances are as follows:

AliceBobPoolSupply
TKN02010-
LP-TKN100-10

Bob also decides to deposit his 20 TKN Tokens into the Pool. Using the formula from above we get:

AmountT×SupplyLBalanceT=AmountL\frac{Amount_T \times Supply_L}{Balance_T} = Amount_L 20×1010=AmountL\frac{20 \times 10}{10} = Amount_L 20010=AmountL\frac{200}{10} = Amount_L 20=AmountL20 = Amount_L

Keeping track of our balances we get to the following state:

AliceBobPoolSupply
TKN0030-
LP-TKN1020-30

Time passes and the dApp Alice and Bob provided liquidity to gained some traction. Due to the popularity it accumulated 30 new TKN Tokens in the form of fees. The TKN amount held by the Pool Smart Contract has doubled:

AliceBobPoolSupply
TKN0060-
LP-TKN1020-30

Alice notices this and decides to take profit by withdrawing all her funds from the Pool. Given that her TKN Tokens were used for liquidity she’s entitled to the fees accumulated by the Pool proportional to her TKN Token contributions.

We can use our formula form above to calculate how many TKN Tokens she should get back in return:

AmountT=AmountL×BalanceTSupplyLAmount_T = \frac{Amount_L \times Balance_T}{Supply_L} AmountT=10×6030Amount_T = \frac{10 \times 60}{30} AmountT=60030Amount_T = \frac{600}{30} AmountT=20Amount_T = 20

Upon withdrawal Alice burns her 10 LP-TKN Tokens and gets back 20 TKN Tokens which sounds about right given that her initial deposit was 10 TKN Tokens and the Pool balance doubled due to high demand from users using the dApp.

The updated balances are as follows:

AliceBobPoolSupply
TKN20040-
LP-TKN020-20

Bob also decides to unwind his position and withdraw all of his funds. We can use the same formula to calculate how many TKN Tokens he should get:

AmountT=AmountL×BalanceTSupplyLAmount_T = \frac{Amount_L \times Balance_T}{Supply_L} AmountT=20×4020Amount_T = \frac{20 \times 40}{20} AmountT=80020Amount_T = \frac{800}{20} AmountT=40Amount_T = 40

Bob burns his 20 LP-TKN and gets 40 TKN Tokens in return which is correct given that the Pool balance doubled while he provided liquidity to the Pool.

Upon Bob’s withdrawal, the balances are as follows:

AliceBobPoolSupply
TKN20400-
LP-TKN00-0

As we can see, all the liquidity was withdrawn from the Pool. Given that the Pool was used heavily during the time Alice and Bob provided liquidity, it accumulated enough fees to double the amount of TKN Tokens it held. Thanks to the implementation of LP-Tokens which track ownership of the supplied liquidity proportional to the funds deposited, Alice and Bob were able to claim their fair share and withdraw TKN Tokens accordingly.

Conclusion

To ensure deep liquidity DeFi Protocols employ the Peer-to-Contract model which allows Liquidity Providers to deposit funds into a Smart Contract that acts as the market-maker rather than users transacting directly with each other in a Peer-to-Peer fashion.

As it turns out, tracking ownership of deployed tokens in addition to the yield generated by the dApp is a non-trivial problem. Even simple accounting and bookkeeping implementations can consume a lot of gas and render solutions too inefficient to run in real-world scenarios.

Liquidity Provider Tokens (LP-Tokens) provide an elegant and scalable solution to combat the aforementioned limitations by minting ERC-20 Tokens upon deposit and burning them when withdrawing funds. One can think of LP-Tokens as a “Proof-of-Deposit” / receipts that track the ownership of deployed capital.

In this Blog Post we’ve derived a mathematical model for Liquidity Provider Tokens (LP-Tokens) from first principles and showed that it can be used in real-world applications by walking through an elaborate example of users providing liquidity to a hypothetical DeFi dApp.

References

The following resources have been invaluable for me to learn the concepts discussed in this article.

You should definitely give them a read if you want to dive deeper into the topic.