Create A New Position
KyberSwap Elastic Security Incident
On 22 Nov 2023, the Elastic protocol experienced a security incident. More details can be found via our official channels.
All other KyberSwap products (Aggregator, Limit Order, & Classic) continue to be fully operational.
Introduction
The creation/minting of a new position is the very first step in contributing liquidity to Elastic contracts in exchange for market maker fees and rewards. Elastic positions are managed via the AntiSnipAttackPositionManager contract which extends the base position manager contract by adding an anti-sniping feature for liquidity additions and removals.
The logic for creating a new position can be found in the createPosition.ts
file linked below:
Signer configuration
In order to sign the transaction to be processed by the network, this example requires an Ethers Signer to be configured. Please view Provider and Signer Setup for more information.
Flow
Minting a new position
Step 1: Get the target pool
We first need to identify the pool where will create our position. Each pool can be uniquely identified by the token pair as well as the configured pool fee tier. The logic for this is covered by the getPool()
function.
Using KyberSwap Analytics, we know that there is an existing KNC-USDC 1% Fee Tier pool at the following address:
To programatically get the above address without prior knowledge of the pool, we can use the computePoolAddress()
function which returns the address of the pool created by the Factory contract based on the token pair and configured fee tier.
Params | Remarks |
---|---|
factoryAddress | The Elastic Factory address. Specified in |
tokenA | Input token for the swap. Specified in |
tokenB | Output token for the swap. Specified in |
fee | Fee configuration as per FeeAmount enum. |
initCodeHashManualOverride | In order to create contracts with a known address, KyberSwap Elastic pools utilizes an |
Based on the inputs above, you should be able to see the target pool address printed in your console. You can choose different tokens/fees and validate the result against KyberSwap Analytics.
Step 2: Configure the position range
To create a position, we must first configure the position's range based on the pool's allowable ticks. Liquidity can only be added towards discrete tick ranges which are fixed at the time of pool creation by the Factory contract.
In addition to the token combination, a pool is also uniquely identified by the fee tier which ultimately determines the tickSpacing. Consequently, we will create a new in-range position with a range that is ±3 tickSpacing away from the current tick.
Step 3: Create a position instance
The Position class enables the convenient handling of a position in a TypeScript environment prior to being created on-chain. It contains the pool, liquidity, and range info required to pre-compute token amounts that are required for the mint operation.
We can use the Position.fromAmount0() function to create a Position instance that corresponds to the intended position range and token0
amount. For the purposes of this demo, we will specify the input token amount as the value of 1 unit of the token0
that is being maintained locally.
Note that on pool creation (which is then used to create a position), the tokens are ordered hence the token0
that we have been maintaining locally (in our constants.ts
file) might not match with the token0
that is being maintained by the Position instance. To avoid this confusion, the token symbols are also logged into the console when displaying token amounts.
After getting the target0Amount
, we can create the Position instance:
Step 4: Calculate token amounts required for the mint
Based on the targetPosition
, we will need to calculate the token0
and token1
amounts that are required for minting the position.
As part of position creation, token0
and token1
amounts will be sent from the signer's address to the position manager contract in exchange for a NFT representing the position. Hence, the signer must have the required token amounts returned in the tokenMintAmounts
.
Step 5: Allow contract to manage signer tokens
In addition to the signer balances, the position manager contract must also have the necessary allowances to spend token0
and token1
from the signer's address.
We can query the existing allowances via the token contracts:
If there are insufficient token allowances, we will then send an approve transaction from the signer's address:
Once we have the necessary allowances, we can then proceed to prepare the mint transaction.
Step 6: Get the mint call parameters
As part of the call parameters, we will also need to pass in the nearest initialized ticks that corresponds to the position's lower and upper range. To get the updated tick data, we will be making use of the TicksFeesReader contract:
Additionally, we can also configure the mint transaction options:
We can then get the call parameters by utilizing the NonfungiblePositionManager
helper class:
This will return the encoded calldata that will be sent to the network.
Step 7: Execute the mint transaction
We are finally ready to execute the transaction by sending the transaction from the signer's address:
A transaction hash will be returned once the trade has been executed. You can copy this hash into a scanner (i.e. PolygonScan) and see that your transaction has been successfully completed by the network.
Last updated