How to stake tokens with Uniswap v3 staking program

How liquidity providers stake Uniswap v3 position NFTs into an incentive program, from deposit through unstake, withdraw, and reward claims.
By Mark Curchin Mark Curchin
TLDR

Building the staking side of a Uniswap V3 liquidity mining program? This guide covers reading idle and staked NFT positions, computing the incentiveId in Remix, depositing and staking in a single transaction, and chaining unstake, withdraw, and claimReward calls with multicall.

How to stake tokens with Uniswap v3 staking program
Updated July 2026: corrected the deposit payload (the Staker decodes an ABI-encoded IncentiveKey, not just the incentiveId), the reward-claim order (unstaking alone makes rewards claimable; withdrawing the NFT is a separate step), and the multicall input description.

In our previous article, we covered how to create 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!

Background: v3 positions and staking-as-a-service

Staking-as-a-service is a set of deployed Uniswap contracts that let anyone launch a liquidity mining program to incentivize liquidity providers on v3 pools. The concept of a v3 pool is the same as v2, with one change that matters here: liquidity providers no longer receive fungible ERC20 LP tokens. Instead, each position is minted as an NFT (ERC721) that records how much liquidity was deposited, who owns it, and the lower and upper price ticks ( Uniswap v3 introduction). That is why the staking flow below reads and transfers NFT positions rather than token balances. Note that Uniswap v4 launched in January 2025 with its hooks architecture, while the staking-as-a-service tooling covered here serves v3 positions.

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, 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 (the NFT minter contract) and pass the following parameters:

  1. from - address of current NFT owner
  2. to - address of the Staker contract
  3. tokenId - the ID of the NFT position
  4. data - the ABI-encoded IncentiveKey (rewardToken, pool, startTime, endTime, refundee), or an array of keys to stake into several incentives at once

When the transaction is executed, a hook function ( 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:
  1. Build and deploy in the Remix VM
  2. Open the tab to interact with your contract and paste the same information you used to create your incentive program.
  1. Execute transaction and you will receive your IncentiveID
Screenshot of the computed incentiveId returned in Remix

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. We’ve deployed our own subgraph through Subgraph Studio to sync the staking events and read the information from there.

This is how our Position GQL schema looks like:

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

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

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

To claim the accrued rewards, your user first needs to unstake the position. Unstaking credits the reward balance inside the Staker contract, and the user can claim it while the unstaked NFT is still deposited; withdrawing the NFT is a separate step that returns the position, and with it access to the Uniswap liquidity. In the Staker contract these are separate functions with no data attribute to chain one into the other, so we bundle them with multicall below. It is still good practice to withdraw right after unstaking so the user recovers their NFT and liquidity in the same flow.

To solve this challenge, Uniswap 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 (optional bytes forwarded to the recipient if it is a contract; leave it empty for a normal wallet).

The multicall method takes an array of ABI-encoded calls (bytes[]), so to pass the previous transactions into data, you will need to arrayify each one into the bytes the library expects. Note that our code examples target ethers v4-era APIs; in ethers v6 the equivalent of arrayify is getBytes. An example of implementation can be found below:

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

Once the Liquidity provider has unstaked the position, the rewards are claimable, whether or not the NFT has been withdrawn yet. 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.

Fees are the other half of an LP's return

The staking program above rewards liquidity providers with your incentive tokens, but that is only part of what a v3 position earns. The same position also collects trading fees from swaps routing through its price range, and forecasting that fee side is its own problem, one that got its first real tool at the v3 launch.

When Uniswap shipped v3 in May 2021, concentrated liquidity changed what it meant to provide liquidity: instead of spreading capital across the whole price curve, an LP chose a price range, with capital efficiency the Uniswap team put at up to 4000x relative to v2. Fee income now depended on where you set the range and where the price actually traded, which made it genuinely hard to predict. Flipside Crypto answered that with a fee calculator built on their Velocity platform by analysts @AngelaMinster, @TheEricStone, @LindsDMeyer, and @LaurenBeltramo: you set a position's bounds and size, and it returned a simplified 24-hour fee forecast across several pools ( original announcement).

Uniswap v4 (January 2025) added hooks that let pools customize their own fee logic, making forecasts even more pool-specific. The need never went away; it moved into the LP analytics dashboards and position managers DeFi teams now treat as table stakes. When you present staking rewards to your LPs, remember they weigh those rewards on top of this fee income, not instead of it.

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, if you are looking for a dApp like this, we are here to help, get in touch and our team will help with the setup.

Have a suggestion? Edit this page