# How to stake tokens with Uniswap v3 staking program

*Complete guide on how implement staking of your Uniswap V3 Staking program*

**Authors:** [Mark Curchin](/c/people/mark-curchin)

---

In our previous article, we covered
[how to create](https://holdex.io/c/learn/how-to-create-a-uniswap-v3-staking-program)
a Uniswap V3 staking program.
If you haven’t read it, we recommend you start from there.

Moving forward you probably asked yourself,
how can liquidity providers (LPs) benefit from this staking program?
Well, in this article, we explain it with real examples to help you get started.
Let’s go!

## Depositing and Staking

This part of the guide is directly related to your LPs (the end-users).
Therefore you need to build an interface with the right functionality so
that your LPs can perform these actions themselves.
For now, I will only go through the sequence of actions
that a Liquidity provider might need to perform from the interface.
But if you are looking for an interface, our team can setup one for you,
drop us a line.

### Read idle NFT positions

Idle NFT positions are the ERC721 tokens that Liquidity providers have received
when they provided liquidity into your Uniswap v3 pair pool.
We call them *idle* because they are not staked
and don’t earn your users any rewards.
Displaying them in the interface makes it easier
for Liquidity providers to start staking in your incentive program.

To read these Idle NFT positions,
you will have to read the data from the Staker contract in a multicall type of
function, where multiple calls are being executed in a chain using obtained
results from previous function returns.

Query the users’ total number of NFT positions from the
***NonfungiblePositionManager*** or ***NFT*** contract using the ***balanceOf***
method.
This will allow you to parse a known length of IDs.

Loop over the length and get each NFT position’s ID this user owns,
using the ***tokenOfOwnerByIndex*** method.
The method will return the NFT position ID
but will not return the information to which pool pair it belongs to.

Now, we need more detailed information about the NFT position.
Query every single position's detailed information from the same contract using
the ***positions*** method.

Finally, having the detailed information of every single NFT position,
you can filter them out by the **token0**,
**token1** and **fee** properties all together.
These 3 properties can be unique only to your Uniswap v3 pool.
(You can also use them to find your pool address).

### Deposit & Stake

As stated in the
[official overview](https://docs.uniswap.org/concepts/overview), before the user
can actually Stake, he must deposit the token into the Staker contract.
This will ensure that the liquidity provider won’t withdraw any liquidity
while participating in the staking program.
Luckily for us, both Deposit and Stake can be performed in a single transaction.

To deposit & stake your NFT position you will need to call the
***safeTransferFrom*** function from the
[NonfungiblePositionManager contract](https://rinkeby.etherscan.io/address/0xc36442b4a4522e871399cd717abdd847ab11fe88#writeContract)
(the NFT minter contract) and pass the following parameters:

1. ***from*** - address of current NFT owner
1. ***to*** - address of the Staker contract
1. ***tokenId*** - the ID of the NFT position
1. **data** - an array with the IncentiveId

When the transaction is executed, a hook function (
[onERC721received](https://docs.uniswap.org/protocol/reference/periphery/staker/UniswapV3Staker#onerc721received))
from the Staker contract will be triggered and create a deposit and stake the
tokenId from the user within the incentive program.

Unfortunately, when the incentive program is created,
the Staker contract doesn’t return the ***IncentiveId***.
Therefore, you will need to implement a local function
which simulates the same ***compute*** function from Uniswap Staker.
Below you will find an example
that you can run in Remix to get your incentiveId.

### Simulate compute function to receive incentiveId

1. Create a new file ***IncentiveId.sol*** and paste the following code:

[gist.github.com](https://gist.github.com/holdex-gists/5abe7b117ae0a1c9b1113beb30fe8e75)

1. Build and deploy in your JavaScriptVM
1. Open the tab to interact with your contract
   and paste the same information you used to create your incentive program.

[gist.github.com](https://gist.github.com/holdex-gists/6afe291cec30162cc981af17e5914756)

1. Execute transaction and you will receive your IncentiveID

![Image](https://storage.googleapis.com/holdex-public/thread/Guide%202%3A%20Uniswap%20V3%20Staking/uniswap_01_750x750.png)

## Read staking information

### Read staked NFT positions

Once the NFT positions have been successfully staked,
we need to display them in the interface.
Problem is, we don’t know what the IDs of the staked positions are.
The Staker contract can’t return information about it
and the Liquidity provider doesn’t own the NFT anymore either.
Which means we can’t use the previous method to read this information.

The solution to go from here is to read this information from a storage.
In our case, we are using The Graph subgraph node.
We’ve deployed our own subgraph to sync the staking events
and read the information from there.

This is how our Position GQL schema looks like:

[gist.github.com](https://gist.github.com/holdex-gists/abd9bf8279c9acb060cc88f4b0143f89)

Below is an example of how we populate the Position data:

[gist.github.com](https://gist.github.com/holdex-gists/66e4d04c56fb3ab10f242a9c2d22fd50)

And this is how we update Position data on every TokenStaked event:

[gist.github.com](https://gist.github.com/holdex-gists/c7d33da420e033885041a1271c86d768)

From here, you should be able to query the information about the NFT positions
using GraphQL and filter them by ***owner*** (users’ wallet), ***incentiveId***
and status ***isStaked***.

### Read accrued rewards

Accrued rewards are the tokens the Liquidity provider will receive from the
incentive program for staking.
They accrue in the Staker contract and the user will have to claim them later.
Since we already know the IDs of the staked NFT positions of a user from the
previous step, we can successfully query the amount of accrued rewards and
display that in the interface.

We’ll need to call the method ***getRewardInfo*** from the Staker contract
and pass the ***incentiveTuple*** and ***tokenId*** as parameters.
You can loop this method over all NFT positions owned by our user.
The ***reward*** amount of the position returned by the method will be in Wei
format, so you will need to convert that into human-readable format.

## Claim rewards

### Unstake token & withdraw

Before your user can claim the accrued rewards,
they will need to unstake and withdraw the NFT position.
In the Staker contract, these methods are separate functions without the
possibility to pass *data* attribute in order to trigger the other function.
It is important for the user to withdraw right after unstaking their positions
because otherwise he won’t be able to get back his liquidity from the Uniswap
pool.

To solve this challenge,
[Uniswap](https://uniswap.org/) offers their own ***multicall*** method from the
Staker contract that we can use to trigger chained write transactions.
The first command we need to perform is unstakeToken
and pass the ***incentiveTuple*** and ***tokenId***,
followed by ***withdrawToken*** where we pass the ***tokenId***, ***to***
(users’ Address) and ***data*** (incentiveId).

Multicall method will accept only *Uint8Array* format
and in order to pass the previous transactions into ***data***,
you will need to [arrayify](https://docs.ethers.io/v4/api-utils.html#arrayish)
these transactions.
An example of implementation can be found below:

[gist.github.com](https://gist.github.com/holdex-gists/1ad8503d8662b61c7758a1afca156fbf)

### Read claimable rewards

In the Staker contract, claimable rewards of a Liquidity provider are queried
using the ***rewards*** method.
Pass the ***addresses*** of your ***rewardToken***
and the ***liquidityProvider***,
and the contract will return the total number of tokens.

### Claim

Finally, when the Liquidity provider has unstaked
and withdrawn the NFT position, he will be able to claim the rewards.
To perform a claim, we must call the ***claimReward*** method from the Staker
contract and pass the ***rewardToken*** address, ***to*** (liquidity provider
address) and the ***amountRequested*** in Wei.
Passing the ***amountRequested*** as 0,
will claim all the available amount of tokens.

## Summary

If you followed the instruction from our guide,
you now have a dApp to run a liquidity mining campaign
and incentivize your LPs with juicy rewards.
We need to point out that this is not a beginners guide, so,
you are looking for a dApp like this, we are here to help,
get in touch and our team will help with the setup.

## Recommended to Read Next

[https://holdex.io/c/learn/how-to-create-a-uniswap-v3-staking-program](https://holdex.io/c/learn/how-to-create-a-uniswap-v3-staking-program)

[https://holdex.io/c/learn/uniswap-v3-and-staking](https://holdex.io/c/learn/uniswap-v3-and-staking)

[https://holdex.io/c/learn/list-of-defi-tokens-apps-and-tools](https://holdex.io/c/learn/list-of-defi-tokens-apps-and-tools)
