Providing liquidity
Adding liquidity
Anyone can add liquidity to a pool, as long as the expiry has not been reached yet.
Portfolio provides some utility functions that can help liquidity providers calculate
the optimal amounts they need to provide to the allocate
function:
getMaxLiquidity
calculates the exact amount of liquidity that can be provided from an amount of asset and quote tokens.getLiquidityDeltas
calculates the exact amount of asset and quote tokens that are required to provide an exact amount of liquidity.
Together both functions can be used to calculate exactly how much asset and quote tokens need to be provided for a given amount of liquidity.
// Assuming we want to allocate into the pool `1099511627777`:
uint64 poolId = 1099511627777;
// Let's check how many tokens we have.
uint256 assetBalance = IERC20(asset).balanceOf(address(this));
uint256 quoteBalance = IERC20(quote).balanceOf(address(this));
// Now we check the maximum amount of liquidity we can get from both amounts
uint128 maxLiquidity = portfolio.getMaxLiquidity(poolId, assetBalance, quoteBalance);
// Let's use this liquidity amount to get some more precise deltas.
(uint128 deltaAsset, uint128 deltaQuote) = portfolio.getLiquidityDeltas(poolId, int128(maxLiquidity));
Note that using getMaxLiquidity
and getLiquidityDeltas
onchain is not ideal
because pools can be manipulated to alter the results.
Once we calculate all of these amounts, we can simply call the allocate
function
by providing the following parameters:
Name | Type | Description |
---|---|---|
useMax | bool | Passing true will use the asset and quote amounts of the transient balance. |
poolId | uint64 | Id of the pool where the liquidity will be allocated into. |
deltaLiquidity | uint128 | Quantity of liquidity to mint in WAD units. |
maxDeltaAsset | uint128 | Maximum quantity of asset tokens paid in WAD units. |
maxDeltaQuote | uint128 | Maximum quantity of quote tokens paid in WAD units. |
Passing 0
as a poolId
will act as a magic value and will use the poolId
from _getLastPoolId
.
Note that this feature should only be used with multicall
to avoid being tricked into allocating
into the wrong pool.
This is what a complete allocation looks like:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "portfolio/interfaces/IPortfolio.sol";
import "portfolio/interfaces/IERC20.sol";
contract AllocateExample {
IPortfolio public portfolio;
address public asset;
address public quote;
constructor(address portfolio_, address asset_, address quote_) {
portfolio = IPortfolio(portfolio_);
asset = asset_;
quote = quote_;
}
function allocate() external {
// Assuming we want to allocate into the pool `1099511627777`:
uint64 poolId = 1099511627777;
// Let's check how many tokens we have.
uint256 assetBalance = IERC20(asset).balanceOf(address(this));
uint256 quoteBalance = IERC20(quote).balanceOf(address(this));
// Now we check the maximum amount of liquidity we can get from our
// current token balances.
uint128 maxLiquidity =
portfolio.getMaxLiquidity(poolId, assetBalance, quoteBalance);
// Let's use this liquidity amount to get some more precise deltas.
(uint128 deltaAsset, uint128 deltaQuote) =
portfolio.getLiquidityDeltas(poolId, int128(maxLiquidity));
// We allow the portfolio contract to move our tokens.
IERC20(asset).approve(address(portfolio), deltaAsset);
IERC20(quote).approve(address(portfolio), deltaQuote);
// Finally, we call the `allocate` function.
portfolio.allocate(false, poolId, maxLiquidity, deltaAsset, deltaQuote);
}
}
You can check out the full code source here (opens in a new tab).
Removing liquidity
Removing liquidity is as simple as calling the deallocate
function and passing
the right parameters:
Name | Type | Description |
---|---|---|
useMax | bool | Pass true to remove all the liquidity of the position. |
poolId | uint64 | Id of the pool to remove the liquidity from. |
deltaLiquidity | uint128 | Quantity of liquidity to burn in WAD units. |
minDeltaAsset | uint128 | Minimum quantity of asset tokens to receive in WAD units. |
minDeltaQuote | uint128 | Minimum quantity of quote tokens to receive in WAD units. |
Using the positions
function, we can check how much liquidity a user owns:
uint128 liquidity = portfolio.positions(user, poolId);
Alternatively, the same functions that we used to allocate
can be used again to
calculate how much liquidity we might want to deallocate and how much asset and
quote tokens we will receive:
(uint128 minDeltaAsset, uint128 minDeltaQuote) = portfolio.getLiquidityDeltas(poolId, int128(liquidity));
The following example shows how to allocate
and deallocate
funds:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "portfolio/interfaces/IPortfolio.sol";
import "portfolio/interfaces/IERC20.sol";
contract DeallocateExample {
IPortfolio public portfolio;
address public asset;
address public quote;
constructor(address portfolio_, address asset_, address quote_) {
portfolio = IPortfolio(portfolio_);
asset = asset_;
quote = quote_;
}
function deallocate() external {
// Assuming we want to allocate into the pool `1099511627777`:
uint64 poolId = 1099511627777;
// Let's check how many tokens we have.
uint256 assetBalance = IERC20(asset).balanceOf(address(this));
uint256 quoteBalance = IERC20(quote).balanceOf(address(this));
// Now we check the maximum amount of liquidity we can get from our
// current token balances.
uint128 maxLiquidity =
portfolio.getMaxLiquidity(poolId, assetBalance, quoteBalance);
// Let's use this liquidity amount to get some more precise deltas.
(uint128 deltaAsset, uint128 deltaQuote) =
portfolio.getLiquidityDeltas(poolId, int128(maxLiquidity));
// We allow the portfolio contract to move our tokens.
IERC20(asset).approve(address(portfolio), deltaAsset);
IERC20(quote).approve(address(portfolio), deltaQuote);
// Finally, we call the `allocate` function.
portfolio.allocate(false, poolId, maxLiquidity, deltaAsset, deltaQuote);
// Since we were the first liquidity provider, a part of our liquidity
// was burnt, we need to remove that from our calculations:
uint128 liquidity = maxLiquidity - uint128(1e9);
// To avoid being sandwiched, we can check the minimum amount of
// tokens that we can expect from our liquidity balance.
(uint128 minDeltaAsset, uint128 minDeltaQuote) =
portfolio.getLiquidityDeltas(poolId, int128(liquidity));
// We can now deallocate the liquidity we just allocated.
// Note that we have to remove the burnt liquidity (1e9), since we were
// the first to provide liquidity in this pool.
portfolio.deallocate(false, poolId, liquidity, 0, 0);
}
}
You can check out the full code source here (opens in a new tab).