> ## Documentation Index
> Fetch the complete documentation index at: https://docs.saucerswap.finance/llms.txt
> Use this file to discover all available pages before exploring further.

# Decreasing Liquidity

> Decrease liquidity or completely exit an existing liquidity position

<Info>
  **Contract ID:** [SaucerSwapV2NonfungiblePositionManager](https://hashscan.io/mainnet/contract/0.0.4053945)
</Info>

***

## Decrease Liquidity

Function name: **decreaseLiquidity**

⛽ *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 |
| *uint128 liquidity*   | Liquidity amount to remove                                            |
| *uint256 amount0Min*  | The minimum amount for the first token in its smallest unit           |
| *uint256 amount1Min*  | The minimum amount for the second token in its smallest unit          |
| *uint256 deadline*    | Deadline in Unix seconds                                              |

<CodeGroup>
  ```solidity INonfungiblePositionManager.sol theme={null}
  struct DecreaseLiquidityParams {
    uint256 tokenSN;
    uint128 liquidity;
    uint256 amount0Min;
    uint256 amount1Min;
    uint256 deadline;
  }

  /// @notice Decreases the amount of liquidity in a position and accounts it to the position
  /// @param params tokenSN The serial number of the token for which liquidity is being decreased,
  /// amount The amount by which liquidity will be decreased,
  /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,
  /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,
  /// deadline The time by which the transaction must be included to effect the change
  /// @return amount0 The amount of token0 accounted to the position's tokens owed
  /// @return amount1 The amount of token1 accounted to the position's tokens owed
  function decreaseLiquidity(DecreaseLiquidityParams calldata params)
    external
    payable
    returns (uint256 amount0, uint256 amount1);
  ```

  ```solidity NonfungiblePositionManager.sol theme={null}
  /// @inheritdoc INonfungiblePositionManager
  function decreaseLiquidity(DecreaseLiquidityParams calldata params)
    external
    payable
    override
    isAuthorizedForToken(params.tokenSN)
    checkDeadline(params.deadline)
    returns (uint256 amount0, uint256 amount1)
  {
    require(params.liquidity > 0);
    Position storage position = _positions[params.tokenSN];

    uint128 positionLiquidity = position.liquidity;
    require(positionLiquidity >= params.liquidity);

    PoolAddress.PoolKey memory poolKey = _poolIdToPoolKey[position.poolId];
    IUniswapV3Pool pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey));
    (amount0, amount1) = pool.burn(position.tickLower, position.tickUpper, params.liquidity);

    require(amount0 >= params.amount0Min && amount1 >= params.amount1Min, 'Price slippage check');

    bytes32 positionKey = PositionKey.compute(address(this), position.tickLower, position.tickUpper);
    // this is now updated to the current transaction
    (, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) = pool.positions(positionKey);

    position.tokensOwed0 +=
      uint128(amount0) +
      uint128(
        FullMath.mulDiv(
          feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128,
          positionLiquidity,
          FixedPoint128.Q128
        )
      );
    position.tokensOwed1 +=
      uint128(amount1) +
      uint128(
        FullMath.mulDiv(
          feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128,
          positionLiquidity,
          FixedPoint128.Q128
        )
      );

    position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128;
    position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128;
    // subtraction is safe because we checked positionLiquidity is gte params.liquidity
    position.liquidity = positionLiquidity - params.liquidity;

    emit DecreaseLiquidity(params.tokenSN, params.liquidity, amount0, amount1);
  }
  ```
</CodeGroup>

### Code Overview

The following code demonstrates how to remove all liquidity from an existing position, collect the swap fees, and return the deposit amounts.

<Tip>
  When removing liquidity from a pool that involves HBAR, include **unwrapWHBAR** in your call to convert the Wrapped HBAR (WHBAR) output token back into the native HBAR cryptocurrency.
</Tip>

<Tip>
  Call the **collect** function after removing the liquidity to withdraw the amounts to the recipient address. It will also collect any swap fees earned in the position.
</Tip>

<Note>
  To burn the NFT after completely exiting the position, include **burn** in the multi-call. See [Burning the NFT](/v/developer/saucerswap-v2/liquidity-operations/decreasing-liquidity#burning-the-nft-position).
</Note>

<Info>
  Hedera's EVM chain ID can be retrieved from [https://chainlist.org](https://chainlist.org/?testnets=true\&search=Hedera).
</Info>

<Warning>
  The following code is intended for guidance purposes and does not include checks and safeguards.
</Warning>

<Tabs>
  <Tab title="JavaScript SDK">
    **Resources:**

    * [SaucerSwap deployed contract IDs](/developerx/contract-deployments)
    * [Ethers.js docs (v6)](https://docs.ethers.org/v6/)
    * [Hedera JavaScript SDK](https://github.com/hashgraph/hedera-sdk-js)
    * [Uniswap V3 SDK](https://docs.uniswap.org/sdk/v3/overview)
    * [Token approve allowance](https://docs.hedera.com/hedera/sdks-and-apis/sdks/cryptocurrency/approve-an-allowance)
    * [Hedera JSON RPC Relay](https://docs.hedera.com/hedera/core-concepts/smart-contracts/deploying-smart-contracts/json-rpc-relay)
    * [Calling a smart contract function](https://docs.hedera.com/hedera/sdks-and-apis/sdks/smart-contracts/call-a-smart-contract-function)

    ```typescript Typescript theme={null}
    import * as ethers from 'ethers'; //V6
    import { 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 provider
    const provider = new ethers.JsonRpcProvider(hederaJsonRelayUrl, '', {
      batchMaxCount: 1, //workaround for V6
    });

    //Load the ABI data for UniswapV3Pool
    const poolInterfaces = new ethers.Interface(poolAbi);

    //Load the ABI data for NonfungiblePositionManager
    const nftManagerInterfaces = new ethers.Interface(nftManagerAbi);

    //Construct the pool contract
    const poolContract = new ethers.Contract(poolEvmAddress, 
      poolInterfaces.fragments, provider);

    //Construct the NFT Manager contract
    const nftManagerContract = new ethers.Contract(nftManagerEvmAddress, 
      nftManagerInterfaces.fragments, provider);

    //Get current position data for the given NFT token serial number
    const lp = await nftManagerContract.positions(tokenSN);
    const token0Address = lp.token0;
    const token1Address = lp.token1;
    const feeTier = Number(lp.fee);
    const tickLower = Number(lp.tickLower);
    const tickUpper = Number(lp.tickUpper);
    const liquidity = lp.liquidity.toString();

    //Get current slot0 and liquidity data from the pool
    const [slot0, poolLiquidity] = await Promise.all([
      poolContract.slot0(),
      poolContract.liquidity()
    ]);

    //Construct the tokens
    //For Hedera chain id, see https://chainlist.org/?testnets=true&search=Hedera
    const token0 = new Token(hederaChainId, token0Address, token0Decimals);
    const token1 = new Token(hederaChainId, token1Address, token1Decimals);

    //Construct the pool using the latest data
    const pool = new Pool(
      token0, token1, 
      feeTier, slot0.sqrtPriceX96.toString(),
      poolLiquidity.toString(), Number(slot0.tick)
    );

    //Construct a position from liquidity and range
    const position = new Position({
      pool: pool,
      tickUpper: tickUpper,
      tickLower: tickLower,
      liquidity: liquidity
    });

    //Calculate the maximum amounts factoring in the price slippage % and range
    const priceSlippagePercent = new Percent(1, 100); //1% price slippage
    const burnAmounts = position.burnAmountsWithSlippage(priceSlippagePercent);
    const amount0Min = burnAmounts.amount0.toString();
    const amount1Min = burnAmounts.amount1.toString();

    //DecreaseLiquidityParams struct
    const params = {
      tokenSN: tokenSN,
      liquidity: liquidity, //liquidity amount to remove
      amount0Min: amount0Min, //in smallest unit
      amount1Min: amount1Min, //in smallest unit
      deadline: deadline, //Unix seconds
    };

    //get max possible value for amount0Max and amount1Max
    const MAX_UINT128 = new BigNumber(2).pow(128).minus(1).toFixed(0);

    //CollectParams struct
    const collectParams = {
      tokenSN: tokenSN,
      recipient: recipientAddress, //0x..   
      amount0Max: MAX_UINT128, //collect max fees and amount
      amount1Max: MAX_UINT128, //collect max fees and amount
    };

    //Construct encoded data for each function
    //The unwrapWHBAR is needed when collecting the HBAR swap fees
    //Optionally include 'collect' here to collect fees.
    //Optionally include 'burn' to burn the NFT if all liquidity is removed.
    const decreaseEncoded = nftManagerInterfaces.encodeFunctionData('decreaseLiquidity', [params]);  
    const collectEncoded = nftManagerInterfaces.encodeFunctionData('collect', [collectParams]);  

    //The unwrapWHBAR is only needed when removing liquidity that includes HBAR
    const unwrapWHBAREncoded = nftManagerInterfaces.encodeFunctionData('unwrapWHBAR', [0, recipientAddress]);

    //Build encoded data for the multicall
    const encodedData = nftManagerInterfaces.encodeFunctionData('multicall', 
      [[decreaseEncoded, collectEncoded, unwrapWHBAREncoded]]);  
    const encodedDataAsUint8Array = hexToUint8Array(encodedData.substring(2));

    //Execute the contract call
    const response = await new ContractExecuteTransaction()
      .setContractId(nftManagerContractId)
      .setGas(gasGwei)
      .setFunctionParameters(encodedDataAsUint8Array)
      .execute(client);

    //Fetch the result
    const record = await response.getRecord(client);    
    const result = record.contractFunctionResult!;
    const results = nftManagerInterfaces.decodeFunctionResult('multicall', result.bytes)[0];
    const collectResult = nftManagerInterfaces.decodeFunctionResult('collect', results[1]);

    //Retrieve the amounts removed for informative purposes
    const removedAmount0 = BigNumber(collectResult.amount0);
    const removedAmount1 = BigNumber(collectResult.amount1);
    ```
  </Tab>
</Tabs>

***

## Burning the NFT

After completely exiting a position, you may burn the NFT position if it is no longer needed. The following code demonstrates how to set a spender allowance for the NFT and include the burn function in the multicall.

<Note>
  A Note allowance for the NFT Manager contract must be approved by the token holder to enable the contract to retrieve the NFT to burn it.
</Note>

<Tabs>
  <Tab title="JavaScript SDK">
    **Resources:**

    * [SaucerSwap deployed contract IDs](/developerx/contract-deployments)
    * [Token approve allowance](https://docs.hedera.com/hedera/sdks-and-apis/sdks/cryptocurrency/approve-an-allowance)

    ```typescript Typescript theme={null}
    //Updated code to include burning of the NFT token

    //Construct encoded data for each function
    //The unwrapWHBAR is needed when collecting the HBAR swap fees
    //Optionally include 'collect' here to collect fees.
    //Optionally include 'burn' to burn the NFT if all liquidity is removed.
    const decreaseEncoded = nftManagerInterfaces.encodeFunctionData('decreaseLiquidity', [params]);  
    const collectEncoded = nftManagerInterfaces.encodeFunctionData('collect', [collectParams]);  
    const burnEncoded = nftManagerInterfaces.encodeFunctionData('burn', [tokenSN]);

    //The unwrapWHBAR is only needed when removing liquidity that includes HBAR
    const unwrapWHBAREncoded = nftManagerInterfaces.encodeFunctionData('unwrapWHBAR', [0, recipientAddress]);

    //Build encoded data for the multicall
    const encodedData = nftManagerInterfaces.encodeFunctionData('multicall', 
      [[decreaseEncoded, collectEncoded, unwrapWHBAREncoded, burnEncoded]]);  
    const encodedDataAsUint8Array = hexToUint8Array(encodedData.substring(2));

    //Give NFT spender allowance to NFT Manager contract
    const nftId = new NftId(lpTokenId, tokenSN);
    const approveResult = await new AccountAllowanceApproveTransaction()
      .approveTokenNftAllowance(nftId, ownerId, nftManagerContractId)
      .execute(client);

    const allowanceReceipt = await approveResult.getReceipt(client);
    console.log(`NFT allowance status: ${allowanceReceipt.status}`);

    //Execute the contract call
    const response = await new ContractExecuteTransaction()
      .setContractId(nftManagerContractId)
      .setGas(gasGwei)
      .setFunctionParameters(encodedDataAsUint8Array)
      .execute(client);
    ```
  </Tab>
</Tabs>
