See Get user positions for details how to retrieve all positions of a user including fees earned if any.
Function name: collect
⛽ Recommended gas: 300,000 gwei (~ $0.026 USD)
Struct Parameter Name
Description
uint256 tokenSN
The serial number of the token for which liquidity is being increased
address recipient
EVM address to receive the claimed swap fees
uint256 amount0Max
The maximum amount for the first token in its smallest unit
uint256 amount1Max
The maximum amount for the second token in its smallest unit
Solidity Interface & Function Body
//INonfungiblePositionManager.solstructCollectParams {uint256 tokenSN;address recipient;uint128 amount0Max;uint128 amount1Max;}/// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient/// @param params tokenSN The serial number of the NFT for which tokens are being collected,/// recipient The account that should receive the tokens,/// amount0Max The maximum amount of token0 to collect,/// amount1Max The maximum amount of token1 to collect/// @return amount0 The amount of fees collected in token0/// @return amount1 The amount of fees collected in token1functioncollect(CollectParamscalldata params) externalpayablereturns (uint256 amount0,uint256 amount1);
//NonfungiblePositionManager.sol/// @inheritdoc INonfungiblePositionManagerfunctioncollect(CollectParamscalldata params)externalpayableoverrideisAuthorizedForToken(params.tokenSN)returns (uint256 amount0,uint256 amount1){require(params.amount0Max >0|| params.amount1Max >0);// allow collecting to the nft position manager address with address 0address recipient = params.recipient ==address(0) ?address(this) : params.recipient; Position storage position = _positions[params.tokenSN]; PoolAddress.PoolKey memory poolKey = _poolIdToPoolKey[position.poolId]; IUniswapV3Pool pool =IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); (uint128 tokensOwed0,uint128 tokensOwed1) = (position.tokensOwed0, position.tokensOwed1);// trigger an update of the position fees owed and fee growth snapshots if it has any liquidityif (position.liquidity >0) { pool.burn(position.tickLower, position.tickUpper,0); (,uint256 feeGrowthInside0LastX128,uint256 feeGrowthInside1LastX128,, ) = pool.positions( PositionKey.compute(address(this), position.tickLower, position.tickUpper) ); tokensOwed0 +=uint128( FullMath.mulDiv( feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128, position.liquidity, FixedPoint128.Q128 ) ); tokensOwed1 +=uint128( FullMath.mulDiv( feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128, position.liquidity, FixedPoint128.Q128 ) ); position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128; position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128; }// compute the arguments to give to the pool#collect method (uint128 amount0Collect,uint128 amount1Collect) = ( params.amount0Max > tokensOwed0 ? tokensOwed0 : params.amount0Max, params.amount1Max > tokensOwed1 ? tokensOwed1 : params.amount1Max );// the actual amounts collected are returned (amount0, amount1) = pool.collect( recipient, position.tickLower, position.tickUpper, amount0Collect, amount1Collect ); // sometimes there will be a few less wei than expected due to rounding down in core, but we just subtract the full amount expected
// instead of the actual amount so we can burn the token (position.tokensOwed0, position.tokensOwed1) = (tokensOwed0 - amount0Collect, tokensOwed1 - amount1Collect);emitCollect(params.tokenSN, recipient, amount0Collect, amount1Collect);}
Code Overview
The following code demonstrates how to claim all swap fees from a pool.
When claiming fees from a pool that involves HBAR, include unwrapWHBAR in your multicall to convert the Wrapped HBAR (WHBAR) output token back into the native HBAR cryptocurrency.
import*as ethers from'ethers'; //V6import { Pool, Position, nearestUsableTick, priceToClosestTick } from'@uniswap/v3-sdk';import { Fraction, Percent, Token, Price } from'@uniswap/sdk-core';import { ContractExecuteTransaction, .. } from'@hashgraph/sdk';//Set one of Hedera's JSON RPC Relay as the providerconstprovider=newethers.JsonRpcProvider(hederaJsonRelayUrl,'', { batchMaxCount:1,//workaround for V6});//Load the ABI data for NonfungiblePositionManagerconstnftManagerInterfaces=newethers.Interface(nftManagerAbi);//get max possible value for amount0Max and amount1MaxconstMAX_UINT128=newBigNumber(2).pow(128).minus(1).toFixed(0);//CollectParams structconstparams= { tokenSN: tokenSN, recipient: recipientAddress, amount0Max:MAX_UINT128,//collect max fees amount1Max:MAX_UINT128,//collect max fees};//Construct encoded data for each functionconstcollectEncoded=nftManagerInterfaces.encodeFunctionData('collect', [params]); //Not needed if HBAR isn't include in the poolconstunwrapWHBAREncoded=nftManagerInterfaces.encodeFunctionData('unwrapWHBAR', [0, recipientAddress]);//Build encoded data for the multicallconstencodedData=nftManagerInterfaces.encodeFunctionData('multicall', [[collectEncoded, unwrapWHBAREncoded]]); constencodedDataAsUint8Array=hexToUint8Array(encodedData.substring(2));//Execute the paid contract callconstresponse=awaitnewContractExecuteTransaction().setContractId(nftManagerContractId).setGas(gasGwei).setFunctionParameters(encodedDataAsUint8Array).execute(client);//Fetch the resultconstrecord=awaitresponse.getRecord(client); constresult=record.contractFunctionResult!;constresults=nftManagerInterfaces.decodeFunctionResult('multicall',result.bytes)[0];constcollectResult=nftManagerInterfaces.decodeFunctionResult('collect', results[0]);//Retrieve the collected amounts for informative purposesconstamount0=BigNumber(collectResult.amount0);constamount1=BigNumber(collectResult.amount1);