Developer Docs

Liquidity Provision

Liquidity provider (LP) interactions with Increment are also fairly straightforward. There are 2 possible actions: 1) provide liquidity and 2) remove liquidity.
LPs earns 1) trader fees while 2) taking the opposing side of the aggregated price movements of the traders in one market.
A liquidity provider may provide liquidity multiple times (e.g. first 1000, then again 1000 for a new total of 2000) and remove liquidity multiple times (e.g. remove 500 then 300 for a new balance of 1200).
When the liquidity provider provides liquidity, the system will use half of the total funds to mint vBase and the other half to mint vQuote and then deposit both into the pool.
There is a short lock period when liquidity provided, currently 1 hour, but can be changed through governance. This lock period is meant to prevent flashloans and liquidity manipulation.

Providing liquidity

Anyone can provide liquidity. The sequence of actions for depositing liquidity is as follows:
  1. 1.
    Deposit one of the supported allowlisted collateral into the Vault with the deposit function
  2. 2.
    Call the provideLiquidity function with 1) the market in which you'd like to provide liquidity to, 2) the amounts of virtual tokens to provide and 3) a minimum amount of LP tokens the you wish to receive (use 0 if you do not know what constitutes a reasonable value).
Liquidity providers can provide liquidity in a ratio different than that of the pool they provide liquidity to, with a limit of 10% deviation compared to the current index price of the pool.
Here's more fine-grained vision of what's happening when a user provides liquidity:

Removing liquidity

The sequence of actions for removing liquidity is as follows:
  1. 1.
    Get the number of LP tokens the user owns using the removeLiquidity function, i.e. liquidityBalance.
  2. 2.
    Remove the liquidity by calling the removeLiquidity function with 1) the index of the market in which you'd like to remove liquidity from, 2) the amount of LP tokens you'd like to remove from the pool (i.e. something lower or equal to liquidityBalance), 3) the minimum amount of vQuote and vBase tokens to withdraw from the pool (use [0, 0] if you don't know what constitute a reasonable value given the current market conditions), 4) a proposed vQuote or vBase amount at which to sell the active position created after removing liquidity (see to find out how to determine the right proposedAmount) and 5) the minimum amount of vQuote (if the active position looks like a SHORT) or vBase (if the active position looks like a LONG) you'd accept when closing the position.
It's worth noting that reducing or closing a liquidity position involves 2 distinct steps.
The first step is to swap some LP tokens in exchange for some vQuote and vBase tokens in the Curve pool of the market. The 2nd and 3rd arguments of the removeLiquidity function are used in this step.
In a high likelihood where the ratio in the market pool has shifted between the moment the LP provided liquidity and the moment the user withdraws, the LP will end up with an active position which is the opposite of the direction in which the market moved. For example, if vBase became stronger during the time a LP provided liquidity, then their active position will be negative on vBase. From the protocol's standpoint, active positions are similar to trading positions.
The second step is about closing the active position. The 4th and 5th arguments of the removeLiquidity function are used at this step.
Here's a more fine-grained view of what is happening when LP removes liquidity:

Lifecycle example: Provide liquidity, trades happnen in the pool, and remove liquidity

Before liquidity is provided, we assume the following protocol and pool states:
  • totalvETH in the pool = 1
  • totalvUSD in the pool = 2000
  • total USDC locked in the vault = 4000
  • total liquidity tokens = 4000
  • totalTradingFeesGrowth = 2%
  • totalBaseFeesGrowth = 1%
  • totalQuoteFeesGrowth = 1%
Market price = 2000 / 1 = 2000 USD per ETH or 2000 ETHUSD
Tom decides to provide liquidity to the ETHUSD trading pair with the provideLiquidity function. He wants to provide 100 USDC. So he first deposits this USDC amount in the Vault, then he calls the provideLiquidity to deposit the equivalent of 100 USDC in the EUTHUSD pool.
Assuming a market price of ETH at 2000 USD and assuming Tom wants to deposit at the same ratio as the current market price, he passes [50, 0.025] as amounts to mint 100 vUSD and 0.025 vETH. These vETH and vUSD tokens are added to the ETHUSD pool. Upon receiving these tokens, the Curve pool returns some newly minted LP tokens that Perpetual immediately assigns to Tom.
At the end of this operation, Tom has a LP position that looks like the following:
  • liquidityBalance (i.e. LP tokens owned by Tom) = 97.56. We assume Curve returned 97.56 LP tokens for the virtual tokens Tom provided.
  • positionSize = -0.025
  • notionalAmount = -50
  • totalTradingFeesGrowth = 2%
  • totalBaseFeesGrowth = 1%
  • totalQuoteFeesGrowth = 1%
After liquidity is provided, the protocol and pool states are the following:
  • totalvETH = 1 + 0.025 = 1.025 (0.01 are fees or 1%)
  • totalvUSD = 2000 + 50 = 2050 (20 are fees or 1%)
  • total USDC locked in the vault = 4100
  • total liquidity tokens = 4097.56
  • totalTradingFeesGrowth = 2%
  • totalBaseFeesGrowth = 1%
  • totalQuoteFeesGrowth = 1%
Let's imagine that a few trades happen so that the ratio between the vEUR and vUSD tokens in the pool change and the amount of fee increases.
Alice opens a LONG position (with the changePosition function) Alice opens a long position and buys 0.047674 vETH with 100 USDC (100 vUSD) at 1x leverage and pays 2 in fees. Math: 1.025 - (1.025*2050/(2050+100) = 0.047674 vETH
We use the constant product formula here (x\*y = k) for simplicity but the math of the CryptoSwap pool we rely on is more complex. The market price changes after every trade with the pool, because every trade generates a shift in the token ratio in the pool.
In Curve, Alice receives 0.04762633 vETH instead of the 0.047674 vETH she would receive otherwise, because of the trading fees incurred in the pool for swapping tokens. That's where the tricky part comes in because Increment cancels out Curve's fees in exchange for an harmonized fee in vQuote only. So even though, in this example, Alice receives 0.04762633 vETH from Curve, Increment gives 0.047674 vETH to Alice (i.e. ignores the 0.00004767 vETH trading fee that Curve charges) then it charges its own trading fee in vQuote (which is still somewhat close to the trading fee Curve charges).
In addition, Alice pays an insurance fee of 1% on the notional of the trade. That accounts for 1% \* 100 vUSD = 1 USDC
At this point, the protocol and pool states look like the following:
  • totalvETH = 1.025 - 0.04762633 = 0.97737 (0.00004767 are fees for Curve or 0.1% -> Increment cancels out Curve's fees in exchange for an harmonized fee in vQuote only)
  • totalvUSD = 2050 + 100 = 2150 (Out of the 100, 2 are fees or 2%)
  • total UA locked in the vault = 4200
  • total liquidity tokens = 4097.56
  • totalTradingFeesGrowth = 2% + 2/4097.56 = 2.04881%
  • totalBaseFeesGrowth = 1% + 0.00004767/0.97737 = 1.004877%
  • totalQuoteFeesGrowth = 1%
Market price = vUSD / vETH = 2150/0.97737 = 2199.78 USD per ETH or 2199.78 ETHUSD
Tom now decides to withdraw all his liquidity from the liquidity pool of the ETHUSD pair with the removeLiquidity function. Putting aside some checks and verifications, the way Increment processes this request proceeds as follows, with 2 main steps:
  1. 1.
    The first step is to remove the liquidity from the pool. More explicitly, Increment burns the 97.56 LP tokens from Tom in exchange for:
  • 97.56 / 4097.56 * 2150 = 51.19 vUSD
  • 97.56 / 4097.56 * 0.97737 = 0.02327 vETH
The market state on the vAMM is now:
  • totalvETH = 0.97737 - 0.02327 = 0.9541
  • totalvUSD = 2150 - 51.19 = 2098.81
We need to find the amount of fees to burn in the Curve pool. In this case, the amount of vUSD fees (in the Curve pool) hasn't moved but the amount of vETH fees changed. Indeed, we know that Tom did not earn any additional fees on the vUSD he provided to the pool. But, inside of Curve, he earned some fees on the vETH provided to the pool. The fee (per unit of token) grew from 1% to 1.004877%. So, 0.004877%% of the tokens withdrawn by Tom are fees earned.
We define the amount of vETH returned after the vETH Curve fees are disregarded as the amount:
  • 0.02327 / (1+(1.004877%-1%)) = 0.023269 vETH
Liquidity providers earned an additional 0.04881% fees per unit of quote provided. So, he receives:
  • 50 + (2.04881% - 2%) * 97.56 = 50.0476 vUSD
Tom's position is now:
  • liquidityBalance (i.e. LP tokens owned by Tom) = 0
  • positionSize = -0.025 + 0.023269 = -0.001731
  • notionalAmount = -50 + 51.19 + 0.0476 = 1.2376
  1. 2.
    The second step of withdrawing liquidity consists of simulating a sale of the LP accounting position in the market to determine if the movement of the market between the moment he provided liquidity and now has played in his favor or not. A LP is taking the opposing side of the aggregated movements of traders in a specific market.
In this case, Tom position looks like an active SHORT since positionSize < 0 and notionalAmount > 0.
Tom submits a number of vUSD to sell which is large enough to pay back his 0.001731 vETH debt in full. It can be shown that the LP will have to sell ~3.81 vUSD.
Math: 0.954 - (0.954 * 2098.81 / (2098.81 + x)) = 0.001731 x = 3.81
Tom LP position is now:
  • positionSize = -0.001731 + 0.001731 = 0
  • notionalAmount = 1.2376 - 3.81 = -2.5724
In this example where liquidity was limited and price impact was magnified, Tom made a loss of 2.57 USDC. LP can withdraw up to 97.43 USDC.
Last modified 20d ago