LP function spotlight
This document explains the syntax and usage of one of Vektor's most powerful execution functions: the Liquidity Provider LP()
function. For a shorter summary with examples you could read our launch announcement here.
CALL
function in the app to learn more.Summary
Vektor's LP
function faciliates liquidity provision (hereafter "LP'ing") on multiple venues and chains supported by Vektor.
- Use the default
LP
orLP.DEPOSIT
function to provide assets to an LP venue. - Use the
LP.WITHDRAW
funtion to withdraw assets from an LP venue.
The LP
function also has a whole suite of powerful sub-functions that can be used to access current market information about LP pools, monitor existing positions, or even simulate the effect of adding liquidity without actually doing so. This is all covered in more detail below.
ASSETS.GET
function. We plan to give users the ability to register their own assets, but for now you need to let us know if there is something missing that you need - we can add it quickly. The Vektor LP Model
(skip to Function Specifications)
Liquidity pools are one of the most heterogenous DeFi primitives. Since their inception, different venues/protocols have innovated with new pool types in order to deliver features such as improved capital efficiency, reduced risk, and so forth. While some types have dominated, there is still significant variance in types amongst the largest venues, and at time of writing the top 4 venues by liquidity still represent four different pool types:
- 'Pair' type pools have 2 assets (e.g. Uniswap v2 and its clones)
- 'Multi' type pools have 2 or more assets (e.g. Curve and its clones)
- 'Range' type pools have 2 assets and pools for different fee levels (e.g. Uniswap v3 and its clones)
- 'Weighted' type pools have 2 or more assets in different target weightings (e.g. Balancer and its clones)
What makes Vektor special is the ability to use all of these different pool types with the same functions and a consistent UX, without compromising on the unique attributes of each type. There is no need for different LP functions for each pool type. Instead, a universal LP
function facilitates consistent usage patterns, function composability, and a much more intuitive set of data points.
At a high level, the Vektor LP model uses concepts of Transactions, Positions, Pools, and Venues.
TRANSACTIONS
are on-chain events that modifyLP
POSITIONS
. Examples areLP.DEPOSIT
andLP.WITHDRAW
. Multiple transactions can be used to create, add to, reduce, adjust, or removePOSITIONS
.POSITIONS
are representations of asset liquidity that have been provided to specificPOOLS
.POSITIONS
are specific to a blockchain address (i.e. user), and so include some specific information (e.g. amount).POSITIONS
generally represent multiple provided assets in different amounts.POOLS
are representations of a specific liquidity pool, on a specificVENUE
, on a specificBLOCKCHAIN
.POOLS
can have different types, as outlined above (Pair, Multi, Range, Weighted types).POOLS
generally contain two or more assets in reserve amounts, with a target reserve ratio.POOLS
are a more 'global' concept, as they represent the total aggregated liquidity associated with them (including yourPOSITIONS
plus everyone elses). SomePOOLS
support multiple distinctPOSITIONS
for the same address, while others will only support one position per address (which you can add to or reduce).VENUES
represent a specific entity / Dapp that host the variousPOOLS
where users deposit/withdraw asset liquidity. Any specific venue is composed of multiple different on-chain smart contracts. In Vektor, aVENUE
refers to a specific entity/dapp on a specific blockchain. Some venues (e.g. Sushiswap) exist on multiple blockchains; these are counted as multiple distinctVENUES
.
VENUES
or VENUES.SYMBOLS
functions in the Vektor app.For a more detailed overview of the Vektor LP model, you can refer to the below diagram which is non-exhaustive.
Function Specifications
You can use .POOL
and .POOLS
sub-functions to see current on-chain pool information, or use .DEPOSIT
and .WITHDRAW
to execute actual LP-ing transactions on-chain. Then retrieve and monitor information about your different LP positions using the .POSITION
and/or .POSITIONS
sub-functions. The various ~QUOTE
and ~QUOTES
sub-functions can be used to simulate the impact of a deposit/withdrawal without actually executing a transaction on chain (for instance, to see what impact a deposit will have on pool liquidity or your own ownership %).
Click on the function names below to skip straight to the function
Function | Description |
---|---|
LP.POOLS(...) |
Get info on all supported pools |
LP.POOL(...) |
Get info on a specific pool |
LP.POSITIONS(...) |
Get info on all provisioned LP positions |
LP.POSITION(...) |
Get info on a specific provisioned LP position |
LP.DEPOSIT(...) |
Deposit liquidity to a specific pool, creating/adding to a position |
LP.DEPOSIT_QUOTE(...) |
Simulate an LP deposit to a specific pool |
LP.WITHDRAW(...) |
Withdraw liquidity from a specific pool |
LP.WITHDRAW_QUOTE(...) |
Simulate an LP withdrawal from a specific pool |
LP.POOLS(...) Syntax and Examples
LP.POOLS
is a powerful way to get an overview of the current liquidity conditions on multiple different pools, venues, and blockchains all in one view, with live updates.
The LP.POOLS
function returns a list of POOL
objects. You can filter the list using the various filter options, extract sections of the list by wrapping with the LIST
function, or just monitor the various live-updating data points.
LP.POOLS(...) Examples
Get the current pool information of all pools, venues, and chains supported by Vektor (with sample output).
LP.POOLS
function with filters.In this list of POOLS
, every row in this table represents a POOL
object. In other words, we could process this list using functions like LIST
and SORT
, or we could extract individual POOL
objects to drill down further on their data. It's not just a static list of raw data.
Each POOL
object has multiple different data points, shown here as columns. We will explain these colmns in more detail in the LP.POOL
section below. You can learn more about Vektor's data model here.
For now though, using LP.POOLS
on its own like this is not so meaningful and quite a heavy query, so let's use filters to narrow down the results and improve speed.
Get a list of pools for Sushiswap, on all chains (with sample output).
Get a list of pools on all venues, on just Arbitrum and Optimism chains.
Get a list of pools on just Curve and Uniswap venues, on just Polygon and Ethereum chains.
Get a list of pools that contain the asset Staked ETH STETH
(with sample output).
INCLUDE_ASSETS=
option is a powerful way to query pools based on asset constitutents. You can provide one asset or multiple assets to the INCLUDE_ASSETS=
option. If you provide one asset (like in the above example), Vektor will return all the pools where at least one (i.e. "any") of the pool assets match this asset. If you provide multiple assets (e.g. INCLUDE_ASSETS=[USDT, USDC]
) then Vektor will return all the pools where all of the provided assets are found in the pool. The latter example may return just pair pools with exactly [USDT, USDC]
or multi-pools that contain both like [TUSD, DAI, USDC, USDT]
.Get a list of pools on just PancakeSwap, on all chains, where the pool must contain BNB
as one of its constituent assets.
Get a list of pools on all venues, on all chains, where the pool must contain both WETH
AND DAI
among its constituent assets..
INCLUDE_ASSETS
, EXCLUDE_ASSETS
, INCLUDE_BLOCKCHAINS
, EXCLUDE_BLOCKCHAINS
, INCLUDE_VENUES
and EXCLUDE_VENUES
are optional arguments, so they must be represent by key=value
(Python style). In these instances the value
can be a list of venues/chains/assets, so use array notation [asset1,asset2,asset3]
. As a rule, you can put optional arguments in any order as long as they come after required arguments.back to function list
LP.POOL(...) Syntax and Examples
LP.POOL
has two important but distinct uses:
- It returns information about a specific LP pool, and
- It becomes a pool identifier that is used inside other functions
(2.) is important to understand because it is necessary to use various other LP
functions. Both (1.) and (2.) will be discussed separately below
1. Using LP.POOL
to return information about a specific pool
LP.POOL
is very similar to LP.POOLS
except that it returns information about a particular liquidity pool, as specified by an array of assets, venue, and blockchain that you provide. Unlike LP.POOLS
, LP.POOL
has required arguments instead of options.
You clearly define a specific, existing pool by its assets, venue, and blockchain, and then the LP.POOL
function finds this pool and returns its data.
LP.POOL(...) Examples (returning information)
Get the current pool data for the WETH/USDC pool on Uniswap v2 on Ethereum (with sample output).
The above is an example of a structured POOL
object that is returned by the LP.POOL
function, which includes several data points:
NAME
- The name given to the pool, typically based on its constituent assets. This will not necessarily be a unique nameASSETS
- The constituent assets of the pool, in[array]
notation*RESERVE_AMOUNTS
- The current total amounts of each asset in the pool, denominated in the assets corresponding to theASSETS
array aboveRESERVE_RATIOS
- The current % split of the value of the assets in the poolTARGET_WEIGHTS
- The target/ideal % split of the value of the assets in the poolPOOL_FEE
- The current pool fee for trading (not for adding/removing liquidity)POOL_TYPE
- The type of this pool (Pair, Multi, Range, or Weighted)LIQUIDITY
- The total combined value of all the assets in the pool, as denominated in theQUOTE
asset belowQUOTE_ASSET
- The asset used to denominateLIQUIDITY
VENUE
- The specific entity/dapp hosting this poolBLOCKCHAIN
- The underlying blockchain of this venue & pool
[USDC, WETH]
instead of normal arguments (USDC
, WETH
, ...)?The goal of Vektor is to abstract away the complexities of DeFi so you can focus on your transactions and strategies. To that end, we wanted to design the
LP.POOL
function so it could handle all different pools and pool types. But as outlined in the Vektor LP model, different pool types have different structures; some contain two assets only, some contain three assets or more, and so on. Rather than have multiple different functions with different orderings of asset arguments, we designed LP.POOL
to take a flexible asset array like [USDC, WETH, ...]
so that it could acccomodate all different pool types, no need to learn a new syntax for each!Note: the order of assets inside the array don't matter; so [USDC, WETH] will be handled the same as [WETH, USDC]. Also, native assets (e.g. ETH, MATIC) will be automatically regarded as the wrapped equivalent (e.g. WETH, WMATIC) where appropriate. Vektor takes care of all this for you!
Get the current pool data for the CAKE/BNB pool on PancakeSwap on BNBChain.
Get the current pool data for the "tricrypto" pool (ETH, USDT, WBTC) on Curve on Arbitrum.
Get the current pool data for the "frax" pool (DAI, FRAX, USDC, USDT) on Curve on Ethereum.
Get the LIQUIDITY
data point from the WBTC/ETH pool data on Uniswap v2 on Ethereum, using default USDC denomination (with sample output).
Get the LIQUIDITY
data point from the WBTC/ETH pool data on Uniswap v2 on Ethereum, overriding the default USDC denomination and setting as ETH instead.
Set an alert to notify me when the ratio of ETH in the Curve ETH/STETH pool falls below 15%
In the above example, the LP.POOL
function is retrieving the pool data, the .RESERVE_RATIOS
is extracting the reserve ratios of all the pool assets as an array, the LIST.FIRST
is selecting just the first asset ratio from the array (in this case ETH), the < 0.15
is creating the condition around 15%, and the ALERT()
is setting up the alert based on this formulaic condition.
Using LP.POOL
with Range Pools
In the case of RANGE
-type pools / concentrated liquidity pools (e.g. Uniswap v3), there will be several instances of a pool that exist with the same assets, venue, and blockchain attributes, but different pool fees:
In these cases, the POOL_FEE=
option can be used to specify which pool to return:
Get the current pool data for the 1.00% fee WETH/USDC concentrated liquidity (i.e. range) pool on Uniswap V3 on Ethereum.
If POOL_FEE=
is omitted for a RANGE
type pool, then by default Vektor will return the pool with the highest liquidity.
The POOL_FEE=
option can only be used with RANGE
type pools. If you try to specify POOL_FEE
for a different pool type, you will receive an error.
2. Using LP.POOL as a pool identifier
Vektor's LP.POOL
function has been engineered to maximally accommodate the whole range of different pools occuring in DeFi. Regardless of whether the pool has two assets or several assets, or what venue and blockchain it's hosted on, the LP.POOL
function interprets and processes the pool data in a consistent way.
This property, effectively wrapping up the pool data and converting it into a consistent structured object, enables the LP.POOL
function to act as an identifier of a specific pool, because the pool object it returns is a standard composable Vektor data type.
In this way, LP.POOL
can be nested inside a whole suite of other Vektor functions that expect a pool identifier, unlocking many of the important LP functionalities such as LP.POSITION
, LP.DEPOSIT
, and more which shall be covered further down in this document.
Using LP.POOL
as a nested pool identifier in this way has many example use cases, but since these are by definition examples of other LP
functions (e.g. LP.POSITION
), these examples will be covered in the corresponding sections for those functions below.
back to function list
LP.POSITIONS(...) Syntax and Examples
After you have provided assets to to one or more liquidity pools, you can use LP.POSITIONS
to keep track of all these LP positions, including supplied amounts, venues, blockchains, associated labelled addresses, and much more.
The difference between LP.POSITIONS
and LP.POSITION
(featured next) is that LP.POSITIONS
returns a list of all the existing positions in a tabulated format, while LP.POSITION
returns a summary of a single, specific positions. LP.POSITIONS
is more powerful in that it can read multiple blockchains in one operation. Both are read-only functions.
LP.POSITIONS(...) Examples
Get the current provided liquidity positions of all pools, venues, and chains supported by Vektor, searching all labelled addresses (with sample output)
Get the current provided liquidity positions, filtering on just the labelled address MY_WALLET
and on just the Polygon blockchain.
Get the current provided liquidity positions, filtering on just Uniswap and Curve venues and on all blockchains except Arbitrum.
Get the current provided liquidity positions, filtering on just the positions that contain the STETH
asset.
Get three separate tables at the same time, one each for three specific blockchains, showing for each blockchain the provided liquidity positions across all pools and venues and labelled addresses.
Shift+Enter
to start a new line in the same command statement, or use the semicolon ;
separator. Everything will be executed like a script, probably resulting in several view panes showing the output of each part.back to function list
LP.POSITION(...) Syntax and Examples
LP.POSITION
is very similar to LP.POSITIONS
except that it returns information about a particular liquidity position, as specified by a pool and a labelled address that you provide. Unlike LP.POSITIONS
, LP.POSITION
has required arguments instead of options.
LP.POOL
function as a nested identifier for another function. You can read more about this here.LP.POSITION(...) Examples
Get information on the current liquidity position for the Uniswap_V3 USDD/USDT
pool (Ethereum), at the labelled address DEMO_ADDRESS_1
(with sample output).
Get the current ownership % represented by my above position in the USDD/USDT pool (with sample output).
.
you could alternatively use the GET
function to access data points like OWNERSHIP
above. Read more about Vektor's data model here.Set up a Vektor Alert to notify me when my pool ownership % of my USDC/XYZ position rises above 10%.
Using LP.POSITION
with Range Positions
In the case of RANGE
-type pools / concentrated liquidity pools (e.g. Uniswap v3 pools), it is possible to have multiple POSITIONS
in the same POOL
, each with different liquidity ranges. To return a RANGE
position when multiple positions exist, specify the min and max price of the position RANGE
in the form RANGE=[min, max]
(more on RANGE
in the LP.DEPOSIT specification below). Values will be rounded to the nearest 'tick'.
Using the RANGE=
option selector is only necessary when there are multiple range positions in the same range pool. If left blank, Vektor will default to pick the most recently created position.
If your range position spans across the whole range of liquidity in a RANGE
type pool, you can use the special reference RANGE=[0, -1]
as min/max range parameters. Here the [0, -1]
means 'between zero and infinity'.
back to function list
LP.DEPOSIT(...) Syntax and Examples
You can use LP.DEPOSIT
(or just LP
) to actually prepare and execute an on-chain Liquidity Provision deposit transaction. An LP deposit transaction provides your assets to a liquidity pool, typically in order to generate a return in the form of trading volume fees and/or token rewards.
There are two different methods you can use with the LP.DEPOSIT
function:
- Provide the information about a specific pool, or
- Provide the information about a specific position (which includes the pool)
Method 1 (pool) is required if you don't yet have an existing position in this specific pool. In this case a position will be created by the transaction. If a position in the pool exists already, then liquidity will normally be added to this existing position. In this case, either method 1 (pool) or method 2 (position) can be used, depending on your use case (i.e. you may be structuring your deposit transaction based on the result of a nested query of pools or query of positions.)
In other words,
- Method 1:
LP.DEPOSIT(AMOUNT, ASSET, POOL, LABEL)
- Position already exists -> Add to existing position
- Position doesn't exist -> Create new position
- Method 2:
LP.DEPOSIT(AMOUNT, ASSET, POSITION)
- Position already exists -> Add to existing position
- Position doesn't exist -> Error: Position not found
LP.POOL
function as a nested identifier for another function. You can read more about this here. In the case of Method 2, LP.POSITION
can also act as an identifier.Explanation of deposit asset amounts
LP pools that are not 'Range' type, typically have a TARGET_WEIGHTS
for their asset constituents.
- For 'Pair' type pools, the
TARGET_WEIGHTS
are 50% : 50%. - For 'Multi' type pools, the
TARGET_WEIGHTS
are balanced across the top-level assets (e.g. Curve's '3pool' hasTARGET_WEIGHTS
of 33.3% USDC : 33.3% USDT : 33.3% DAI). Some 'Multi' type pools include other pool tokens as consituent assets ("metapools"), which can change the underlyingTARGET_WEIGHTS
. - For 'Balanced' type pools, the
TARGET_WEIGHTS
are a custom parameter of the pools itself (e.g. 80% BAL : 20% ETH pool, or 50% BAL : 50% ETH pool)
When you make a deposit to a pool using Vektor's LP.DEPOSIT
function, the ratio of assets added is constrained by the pool's TARGET_WEIGHTS
parameter.
LP.DEPOSIT
function has been engineered for use with pools that are not 'Range' type - for Range pools, you should use LP.DEPOSIT_RANGE
, discussed later on. For more about pool types, read about the Vektor LP Model above.This constraint imposed by the TARGET_WEIGHTS
parameter really means when it comes to writing your deposit function, you only need to specify one ASSET
and one AMOUNT
. Vektor will determine the other assets and other amounts required, by inferring this from the pool/position information.
It's the same principle as when you use a graphical UI to add liquidity. One asset becomes the 'driving' parameter while the other becomes a 'driven' parameter.
It's up to you which ASSET
and AMOUNT
you specify when using LP.DEPOSIT
. In an ETH/USDC pool, you can either input an ETH amount or input a USDC amount. However, even if you don't specify them, you will need to have all the other implied required assets available in your labelled address, for the deposit transaction to be successful.
This means that the total USD-equivalent of the combined assets that are included in an LP.DEPOSIT
transaction is likely to be higher than just the ASSET
and AMOUNT
combination that you specify.
TARGET_WEIGHTS
ratio). However, using Curve with Vektor will impose the TARGET_WEIGHTS
constraint, but we plan to add more flexibility in future.LP.DEPOSIT(...) Examples
Deposit 0.5 ETH and the equivalent USDC to the USDC/WETH pool (Method 1) on the Uniswap v2 venue on Ethereum, using funds available at MY_WALLET
(with sample Signing Request)
Deposit another 500 USDC and equivalent ETH to an existing position (Method 2).
Deposit 1000 DAI and the equivalent USDC and USDT to the "3pool" pool on the Curve venue on Optimism, using funds available at MY_WALLET
.
Once the transaction is broadcast and confirmed on the appropriate blockchain, any changes to your positions will be updated on any active LP.POSITIONS
or LP.POSITION
view panes.
Working with Range pools
back to function list
LP.DEPOSIT_QUOTE(...) Syntax and Examples
The LP.DEPOSIT_QUOTE
function simulates depositing liquidity to a specific LP pool, without actually creating or adding to a position. There are many LP pools across DeFi, with highly varying degrees of liquidity. Depending on your deposit size, making a deposit to a LP pool may materially change the pre- and post-deposit pool attributes, including LIQUIDITY
, RESERVE_RATIOS
, and OWNERSHIP
. Use LP.DEPOSIT_QUOTE
to understand the impacts of your transaction before you execute it.
DEPOSIT_QUOTE
. Many things can happen between broadcasting a transaction and it being executed, including changes in the pool state on the blockchain. You can minimize unexpected LP.DEPOSIT
outcomes with tools like the SLIPPAGE=
option, but in any case the DEPOSIT_QUOTE
returned by Vektor should be used for illustrative purposes only.Just like LP.DEPOSIT
, with LP.DEPOSIT_QUOTE
you have two methods to use the function.
If you use Method 1 (specific pool), it will simulate a deposit to the pool as if you never had an existing position (whether this is true or not). If you use Method 2 (specific position), it will simulate adding deposit to your existing position. Method 2 will not work if you don't already have a position in this pool.
The below matrix outlines the behaviour of LP.DEPOSIT_QUOTE
under various conditions.
LP.DEPOSIT_QUOTE(...) Examples
Get a deposit quote for depositing 1,000,000 USDC (and equivalent ETH) into the USDC/WETH pool on Uniswap v2 on Ethereum, as if it were a new position (with sample output)
Extract the OWNERSHIP
data point from the above (e.g. for further calculations). (with sample output)
Get a deposit quote for depositing 5000 BUSD (and equivalent BNB) into the WBNB/BUSD pool on Pancakeswap on BNBChain, simulating adding to my already existing position.
Get a deposit quote for depositing 100,000 DAI (and equivalent USDC and USDT) into the "3pool" on Curve on Optimism, as if it were a new position.
back to function list
LP.WITHDRAW(...) Syntax and Examples
You can use LP.WITHDRAW
to actually prepare and execute an on-chain withdrawal transaction from an LP position. An LP withdrawal removes your assets from a liquidity pool, usually back to the same labelled address you used to originally deposit. Depending on what has changed in the pool's RESERVE_AMOUNTS
since your deposit, you may get back a different quantity of each asset than what you put in.
There are two different methods you can use for the LP.WITHDRAW
function:
- Withdraw a partial amount from a specific position, or
- Withdraw the full amount of a specific position
Unlike LP.DEPOSIT
which also allows you to specify a pool, LP.WITHDRAW
only works using a specified position. This is because in all cases where you want to withdraw, there will be a POSITION
that you need to withdraw from; if you didn't have a POSITION
then you wouldn't have anything to withdraw either!
LP.POSITION
function is acting as an identifier for LP.WITHDRAW
(i.e. "withdraw from this position"), and the LP.POOL
function is acting as an identifier for LP.POSITION
(i.e. "this position is in this pool).When you withdraw a partial amount from an LP position, you don't need to specify all the asset amounts individually; just specify one asset amount and Vektor will determine all the other required amounts based on the pool's TARGET_WEIGHTS
. The principle here is the same as for the LP.DEPOSIT
function, so there is no need to repeat here - you can read more at this explanation.
LP.WITHDRAW(...) Examples
Withdraw 0.5 ETH and the equivalent USDC from the USDC/WETH position on the Sushiswap venue on Arbitrum, associated with the labelled address MY_WALLET2
(with sample Signing Request).
Withdraw the entire remaining position.
LP.WITHDRAW
function has been engineered for use with pools that are not 'Range' type - for Range pools, you should use LP.WITHDRAW_RANGE
, discussed later on. For more about pool types, read about the Vektor LP Model above.Once the transaction is broadcast and confirmed on the appropriate blockchain, any changes to your positions will be updated on any active LP.POSITIONS
or LP.POSITION
view panes.
back to function list
LP.WITHDRAW_QUOTE(...) Syntax and Examples
The LP.WITHDRAW_QUOTE
function simulates withdrawing liquidity from a specific position, without actually reducing or removing it. Use LP.WITHDRAW_QUOTE
to understand the impacts of your transaction before you execute it.
WITHDRAW_QUOTE
. Many things can happen between broadcasting a transaction and it being executed, including changes in the pool state on the blockchain. You can minimize unexpected LP.WITHDRAW
outcomes with tools like the SLIPPAGE=
option, but in any case the WITHDRAW_QUOTE
returned by Vektor should be used for illustrative purposes only.LP.WITHDRAW_QUOTE(...) Examples
Get a withdraw quote for withdrawing 100 USDD (and equivalent MIM) from the USDD/MIM pool on Sushiswap on Ethereum. (with sample output)
In the output above, AMOUNTS_DELTA
refers to the actual amounts you are simulating to withdraw, and all the other numeric data points like AMOUNTS
, VALUE
, and OWNERSHIP
are displaying the post-withdrawal hypothetical figures. If you wanted to see all the current data for this you could just use LP.POSITION
.
Extract the OWNERSHIP
data point from the above (e.g. for futher calculations). (with sample output).
Get a withdrawal quote for withdrawing 5,000 BUSD (and equivalent BNB) from the WBNB/BUSD pool on Pancakeswap on BNBChain.
Get a withdrawal quote for withdrawing 100,000 DAI (and equivalent USDC and USDT) from the "3pool" on Curve on Optimism.
back to function list
Questions?
Email: [email protected]
Zoom: Use CALL
function in Vektor