Algebra Integral
HomepageSocialsIntegrate
  • Overview
    • What is Algebra?
    • Who Are These Docs For
    • Why Concentrated Liquidity & Modularity Matter
    • Partners & Ecosystem
    • Audits & Security
    • Social Media & Communities
  • Introducing Algebra Integral to Founders & Business Teams
    • Overview of Algebra Integral
      • How It Works: Core + Plugins
      • V3 vs. V4: Key Differences
      • Integral vs. Uniswap V4: Key Differences
    • Benefits of Modular Architecture
      • Perks for DEXes
      • Perks for Builders
      • Perks for Users
  • Modularity: Use Cases
  • Plugin Marketplace
  • Algebra Partner Support
  • User Guide Template For DEXes
    • Concentrated Liquidity & Modular Architecture Basics
      • Glossary
      • How Concentrated Liquidity & Modular Architecture Work
      • Benefits of Modular Concentrated Liquidity AMM for Users
        • Perks for Liquidity Providers
        • Perks for Projects
        • Perks for Traders
      • Fee Mechanics
        • Static Fee
        • Dynamic Fee
        • Sliding Fee
        • Dynamic Fee Based on Trading Volume
        • Managed Swap Fee
        • Whitelist Fee Discount
      • Farming
      • Farming FAQ
  • Price Ranges and Liquidity Strategies
    • What Are Price Ranges
    • Basic Price Range Presets
    • Advanced Range Presets
    • How Price Moves Affect Liquidity
    • Impermanent Loss: Concepts & Mitigation
    • Matching Your Liquidity Strategy to Market Moves
    • Swap & LP Strategies with Price Ranges
    • Liquidity Scenarios & Risk Profiles
  • Liquidity Provisioning: Tutorials & FAQs
    • Adding Liquidity
      • Manual Mode
      • Automated Mode
    • Managing & Adjusting Positions
    • How APR is Calculated
    • FAQ for LPs
  • Algebra Integral / Technical Reference
    • Intro
    • Audits
    • Integration Process
      • Specification and API of contracts
        • Algebra Pool
        • Algebra Factory
        • Swap Router
        • Nonfungible Position Manager
        • Quoter
        • QuoterV2
        • TickLens
      • Interaction with pools
        • Getting data from pools
      • Subgraphs and analytics
        • Examples of queries
      • Technical Guides
        • Intro
        • Swaps
          • Single swaps
          • Multihop swaps
        • Providing liquidity
          • Setting up your contract
          • Mint a new position
          • Collect fees
          • Decrease liquidity
          • Increase liquidity
          • Final Contract
        • Flashloans
          • Setting up your contract
          • Calling flash
          • Flash callback
          • Final contract
      • Migration from UniswapV3
      • FAQ
    • Core Logic
      • Pool overview
      • Swap calculation
      • Liquidity and positions
      • Ticks
        • Ticks search tree
      • Reserves
      • Flash
      • Plugins
      • AlgebraFactory and roles
    • Plugins
      • Overview
      • Farming
      • Adaptive Fee
      • Sliding Fee
      • Whitelist Discount Fee
      • Safety Switch
      • Position Limit Orders
      • Managed Swap Fee
      • FAQ
    • Guides
      • Plugin Development
      • Plugin Testing
      • Plugin Deployment
    • Changes V1
    • Changes V1.1
    • Changes v1.2
  • Changes v1.2.1
  • Other
    • Archived Documentation
Powered by GitBook
On this page
  • Setting up the Contract
  • Enabling ERC721 Interactions
  • The Constructor
  • Allowing custody of ERC721 tokens
  • Creating a Deposit
  • The Full Contract Setup
  1. Algebra Integral / Technical Reference
  2. Integration Process
  3. Technical Guides
  4. Providing liquidity

Setting up your contract

Setting up the Contract

First, declare the solidity version used to compile the contract. If needed (if you are using older versions of solidity) add abicoder v2 to allow arbitrary nested arrays and structs to be encoded and decoded in calldata, a feature that we use when transacting with a pool.

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.8.20;

After that, import the contracts needed from the npm package installation.

import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraPool.sol';
import '@cryptoalgebra/integral-core/contracts/libraries/TickMath.sol';

import '@cryptoalgebra/integral-periphery/contracts/interfaces/INonfungiblePositionManager.sol';
import '@cryptoalgebra/integral-periphery/contracts/libraries/TransferHelper.sol';
import '@cryptoalgebra/integral-periphery/contracts/base/LiquidityManagement.sol';

import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';

Then, create a contract called LiquidityExamples and inherit both IERC721Receiver and LiquidityManagement.

For this case, we've chosen to hardcode the token contract addresses. Most likely, you would use an input parameter for this in production, allowing you to change the pools and tokens you are interacting with on a per-transaction basis.

contract LiquidityExamples is IERC721Receiver, LiquidityManagement {

    address public constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
    address public constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;

Declare an immutable public variable nonfungiblePositionManager of type INonfungiblePositionManager.

    INonfungiblePositionManager public immutable nonfungiblePositionManager;

Enabling ERC721 Interactions

EEvery NFT is identified by a unique uint256 ID inside the ERC-721 smart contract, declared as the tokenId

What is NFT?

To allow deposits of ERC721 expressions of liquidity, create a struct called Deposit, a mapping of uint256 to the Deposit struct, then declare that mapping as a public variable deposits.

    struct Deposit {
        address owner;
        uint128 liquidity;
        address token0;
        address token1;
    }

    mapping(uint256 tokenId => Deposit) public deposits;

The Constructor

Declare the constructor here, which is executed once when the contract is deployed. Our constructor hard codes the address of the non-fungible position manager interface, router, and the periphery immutable state constructor, which requires the factory, pool deployer and the address of WMATIC.

    constructor(
        INonfungiblePositionManager _nonfungiblePositionManager,
        address _factory,
        address _WMATIC,
        address _poolDeployer
    ) PeripheryImmutableState(_factory, _WMATIC, _poolDeployer) {
        nonfungiblePositionManager = _nonfungiblePositionManager;
    }

Allowing custody of ERC721 tokens

To allow the contract to custody ERC721 tokens, implement the onERC721Received function within the inherited IERC721Receiver.sol contract.

IERC721Receiver Contract

The from identifier may be omitted because it is not used.

    function onERC721Received(
        address operator,
        address,
        uint256 tokenId,
        bytes calldata
    ) external override returns (bytes4) {
        // get position information
        _createDeposit(operator, tokenId);
        return this.onERC721Received.selector;
    }

Creating a Deposit

To add a Deposit instance to the deposits mapping, create an internal function called _createDeposit that destructures the positions struct returned by positions in nonfungiblePositionManager.sol. Pass the relevant variables token0, token1 and liquidity to the deposits mapping.

    function _createDeposit(address owner, uint256 tokenId) internal {
        (, , address token0, address token1, , , uint128 liquidity, , , , ) =
            nonfungiblePositionManager.positions(tokenId);

        // set the owner and data for position
        // operator is msg.sender
        deposits[tokenId] = Deposit({owner: owner, liquidity: liquidity, token0: token0, token1: token1});
    }

The Full Contract Setup

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.8.20;

import '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraPool.sol';
import '@cryptoalgebra/integral-core/contracts/libraries/TickMath.sol';

import '@cryptoalgebra/integral-periphery/contracts/libraries/TransferHelper.sol';
import '@cryptoalgebra/integral-periphery/contracts/interfaces/INonfungiblePositionManager.sol';
import '@cryptoalgebra/integral-periphery/contracts/base/LiquidityManagement.sol';

import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';

contract LiquidityExamples is IERC721Receiver, LiquidityManagement {
    address public constant DAI = 0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063;
    address public constant USDC = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174;


    INonfungiblePositionManager public immutable nonfungiblePositionManager;

    /// @notice Represents the deposit of an NFT
    struct Deposit {
        address owner;
        uint128 liquidity;
        address token0;
        address token1;
    }

    /// @dev deposits[tokenId] => Deposit
    mapping(uint256 tokenId => Deposit) public deposits;

    constructor(
        INonfungiblePositionManager _nonfungiblePositionManager,
        address _factory,
        address _WMATIC,
        address _poolDeployer
    ) PeripheryImmutableState(_factory, _WMATIC, _poolDeployer) {
        nonfungiblePositionManager = _nonfungiblePositionManager;
    }

    // Implementing `onERC721Received` so this contract can receive custody of erc721 tokens
    function onERC721Received(
        address operator,
        address,
        uint256 tokenId,
        bytes calldata
    ) external override returns (bytes4) {
        // get position information

        _createDeposit(operator, tokenId);

        return this.onERC721Received.selector;
    }

    function _createDeposit(address owner, uint256 tokenId) internal {
        (, , address token0, address token1, , , uint128 liquidity, , , , ) =
            nonfungiblePositionManager.positions(tokenId);

        // set the owner and data for position
        // operator is msg.sender
        deposits[tokenId] = Deposit({owner: owner, liquidity: liquidity, token0: token0, token1: token1});
    }
}
PreviousProviding liquidityNextMint a new position