Collecting Fees

This section explains how to collect fees from a liquidity position on the Storyhunt V3 protocol.

Introduction

This section explains how to collect fees from a liquidity position on the Storyhunt V3 protocol. Liquidity positions accrue fees from trades made within the position’s price range. Using the NonfungiblePositionManager contract, we can collect these fees. The inputs include the tokens pooled, pool fee, and maximum accrued fees to collect for each token.

Setting Up Fee Collection

To begin, fetch the position details from the NonfungiblePositionManager contract to determine the fees owed:

import { ethers } from 'ethers';
import JSBI from 'jsbi';

const nfpmContract = new ethers.Contract(
    NONFUNGIBLE_POSITION_MANAGER_ADDRESS,
    INONFUNGIBLE_POSITION_MANAGER.abi,
    provider
);
const position = await nfpmContract.positions(positionId);

Next, construct a CollectOptions object to specify the fee collection details:

import { CurrencyAmount } from '@storyhunt/sdk-core';

const collectOptions: CollectOptions = {
  tokenId: positionId,
  expectedCurrencyOwed0: CurrencyAmount.fromRawAmount(
    CurrentConfig.tokens.token0,
    JSBI.BigInt(position.tokensOwed0)
  ),
  expectedCurrencyOwed1: CurrencyAmount.fromRawAmount(
    CurrentConfig.tokens.token1,
    JSBI.BigInt(position.tokensOwed1)
  ),
  recipient: address,
};

Here:

  • tokenId: The ID of the position.

  • expectedCurrencyOwed0 and expectedCurrencyOwed1: The maximum amounts of each token expected to collect.

  • recipient: The wallet address to receive the collected fees.

You can fetch the tokensOwed0 and tokensOwed1 values using the positions function of the NonfungiblePositionManager contract:

const positionInfos = callResponses.map((position) => {
    return {
        tickLower: position.tickLower,
        tickUpper: position.tickUpper,
        liquidity: JSBI.BigInt(position.liquidity),
        feeGrowthInside0LastX128: JSBI.BigInt(position.feeGrowthInside0LastX128),
        feeGrowthInside1LastX128: JSBI.BigInt(position.feeGrowthInside1LastX128),
        tokensOwed0: JSBI.BigInt(position.tokensOwed0),
        tokensOwed1: JSBI.BigInt(position.tokensOwed1),
    };
});

Submitting the Fee Collection Transaction

Use the NonfungiblePositionManager to retrieve the call parameters required for the fee collection transaction:

const { calldata, value } =
  NonfungiblePositionManager.collectCallParameters(collectOptions);

Construct and execute the transaction:

const transaction = {
  data: calldata,
  to: NONFUNGIBLE_POSITION_MANAGER_CONTRACT_ADDRESS,
  value: value,
  from: address,
  maxFeePerGas: MAX_FEE_PER_GAS,
  maxPriorityFeePerGas: MAX_PRIORITY_FEE_PER_GAS,
};

const txRes = await wallet.sendTransaction(transaction);

After the transaction is confirmed, the balances of the pooled tokens (e.g., USDC and WIP) will increase if fees have been accrued.