Uniswap – Generating LP Contract Addresses Using Uniswap V3 SDK: Step-by-Step Guide

npmpolygontypescriptuniswapuniswapv3

I'm in a bit of a coding jam, and could use some help understanding how to utilize the Uniswap V3 SDK to generate accurate Liquidity Pool Addresses. I have already gone through the uniswap documentation and other stack exchange questions, and have determined that I would benefit from direct assistance.

This code is generated with large influence from the official Uniswap V3 SDK mint-position example, so I am very confused as to why this method is not correctly generating pool addresses.

For this example, I will attempt to generate the address for the CRV-MATIC Uniswap 0.3% Liquidity Pool on the POLYGON NETWORK.

import { FeeAmount, POOL_INIT_CODE_HASH, computePoolAddress } from '@uniswap/v3-sdk'
// ... (Other imports)
// ...
export const MATIC_TOKEN_ON_POLYGON = new Token(
  SupportedChainId.POLYGON,
  '0x0000000000000000000000000000000000001010',
  18,
  'MATIC',
  'MATIC'
)

export const CRV_TOKEN_ON_POLYGON = new Token(
  SupportedChainId.POLYGON,
  '0x172370d5cd63279efa6d502dab29171933a610af',
  18,
  'CRV',
  'CRV'
)

// Bad Address is generated: 0x458d609c135935d9Bc0733c7ED53b20E350C55B7
// Correct Address is: "0x4d05f2a005e6f36633778416764e82d1d12e7fbb"
const currentPoolAddress: string = computePoolAddress({
  factoryAddress: POOL_FACTORY_CONTRACT_ADDRESS, // 0x1F98431c8aD98523631AE4a59f267346ea31F984
  tokenA: MATIC_TOKEN_ON_POLYGON,
  tokenB: CRV_TOKEN_ON_POLYGON,
  fee: poolFee, // FeeAmount.MEDIUM, which is 3000
  initCodeHashManualOverride: POOL_INIT_CODE_HASH // 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54
})

The Pool Factory Address ("0x1F98431c8aD98523631AE4a59f267346ea31F984") is the address specified in the Uniswap V3 Documentation for the UniswapV3Factory Address on Polygon.

The initCodeHashManualOverride is set to the default POOL_INIT_HASH_CODE from the uniswap SDK located here ("0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54") I have NOT changed the Uniswap SDK. The default pool init hash code should be working (FYI, I have tried with and without specifying the init hash code override. Same result).

Matic Token is indeed at address "0x0000000000000000000000000000000000001010" (ref)

CRV Token is indeed at address "0x172370d5cd63279efa6d502dab29171933a610af" (ref)

You can view the computeAddress source code from the Uniswap SDK. I've copied the code from the official SDK below for reference.

export function computePoolAddress({
  factoryAddress,
  tokenA,
  tokenB,
  fee,
  initCodeHashManualOverride
}: {
  factoryAddress: string
  tokenA: Token
  tokenB: Token
  fee: FeeAmount
  initCodeHashManualOverride?: string
}): string {
  const [token0, token1] = tokenA.sortsBefore(tokenB) ? [tokenA, tokenB] : [tokenB, tokenA] // does safety checks
  return getCreate2Address(
    factoryAddress,
    keccak256(
      ['bytes'],
      [defaultAbiCoder.encode(['address', 'address', 'uint24'], [token0.address, token1.address, fee])]
    ),
    initCodeHashManualOverride ?? POOL_INIT_CODE_HASH
  )
}

From what I can tell, all of the information used to calculate the Pool Address is "verified", but yet the SDK is not generating the correct pool address.

What am I doing wrong? I can provide additional information upon request. Thank you for your time.

Best Answer

The issue is in the MATIC token address that you put in the code:

export const MATIC_TOKEN_ON_POLYGON = new Token(
  SupportedChainId.POLYGON,
  '0x0000000000000000000000000000000000001010',

You are putting in the base currency for the network (MATIC) whereas the LP you are referring to is a Wrapped Matic ERC-20 token with the address 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270.

Here's the fixed code based on your sample that computes the correct address: 0x4D05f2A005e6F36633778416764E82d1D12E7fbb:

import { Token } from '@uniswap/sdk-core';
import { FeeAmount, POOL_INIT_CODE_HASH, computePoolAddress } from '@uniswap/v3-sdk';

const SupportedChainId = {
  POLYGON: 137,
};

const MATIC_TOKEN_ON_POLYGON = new Token(
  SupportedChainId.POLYGON,
  '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270',
  18,
  'WMATIC',
  'WMATIC'
);

const CRV_TOKEN_ON_POLYGON = new Token(
  SupportedChainId.POLYGON,
  '0x172370d5cd63279efa6d502dab29171933a610af',
  18,
  'CRV',
  'CRV'
);


const POOL_FACTORY_CONTRACT_ADDRESS = '0x1F98431c8aD98523631AE4a59f267346ea31F984';
const poolFee = FeeAmount.MEDIUM;

const currentPoolAddress = computePoolAddress({
  factoryAddress: POOL_FACTORY_CONTRACT_ADDRESS,
  tokenA: MATIC_TOKEN_ON_POLYGON,
  tokenB: CRV_TOKEN_ON_POLYGON,
  fee: poolFee,
  initCodeHashManualOverride: POOL_INIT_CODE_HASH
});

console.log(`LP Address for WMATIC-CRV pool: ${currentPoolAddress}`);

The standard for LPs is to hold two ERC-20 tokens, so for the base currency (MATIC, ETH) is usually a wrapped version, which is an ERC-20.

Related Topic