3

I create two ERC20 tokens, deploy them with hardhat on hardhat network, then create Uniswap pair, then retrieve its address with SDK's Fetcher and with my contract. Fetcher returns different address than the one I get in my contract. If I manually substitute the address in sdk.cjs.development.js with the address from the contract, all works, otherwise Fetcher reverts.

So, here is the hardhat script:

const UniswapV2FactoryArtifact = require('@uniswap/v2-core/build/UniswapV2Factory.json');
const UniswapV2Router02Artifact = require('@uniswap/v2-periphery/build/UniswapV2Router02.json');


const moment = require('moment');

const { expect } = require("chai");
const { ethers } = require('hardhat');

// Uniswap sdk
const { Token, Fetcher } = require('@uniswap/sdk')
const hre = require('hardhat');
const chainId = hre.config.networks.hardhat.chainId;


async function testSDK(skillTokenAddress, fakeDAEAddress) {
  const SKILL = await Fetcher.fetchTokenData(chainId, skillTokenAddress,  ethers.provider);
  const FakeDAE = await Fetcher.fetchTokenData(chainId, fakeDAEAddress,  ethers.provider);
  const pair = await Fetcher.fetchPairData(FakeDAE, SKILL, ethers.provider);
  console.log(pair)
}

async function main() {

  const signers = await ethers.getSigners();
  // for (const signer of signers) {
  //   const balance = await signer.getBalance();
  //   console.log(signer.address, ethers.utils.formatUnits(balance))

  // }

  const UniswapV2Factory = await ethers.getContractFactory(
    UniswapV2FactoryArtifact.abi,
    UniswapV2FactoryArtifact.bytecode
  );

  const uniswapV2Factory = await UniswapV2Factory.deploy(signers[0].address);

  const UniswapV2Router02 = await ethers.getContractFactory(
    UniswapV2Router02Artifact.abi,
    UniswapV2Router02Artifact.bytecode
  );
  const uniswapV2Router02 = await UniswapV2Router02.deploy(uniswapV2Factory.address, signers[0].address);
  await uniswapV2Router02.deployed();
  
  const SkillToken = await ethers.getContractFactory('SkillToken');
  const skillToken = await SkillToken.deploy();
  await skillToken.deployed();
  
  const FakeDAE = await ethers.getContractFactory('FakeDAE');
  const fakeDAE = await FakeDAE.deploy();
  await fakeDAE.deployed();
  
  await fakeDAE.approve(uniswapV2Router02.address, 10000);
  await skillToken.approve(uniswapV2Router02.address, 10000);

  console.log(`Skill Address: ${skillToken.address}, FakeDAE address: ${fakeDAE.address}`)

  await expect(uniswapV2Factory.createPair(skillToken.address, fakeDAE.address))
    .to.emit(uniswapV2Factory, "PairCreated"); 

  
  await uniswapV2Router02.addLiquidity(
    skillToken.address, 
    fakeDAE.address,
    8000,
    8000,
    7999,
    7999,
    signers[0].address,
    moment().add(10, 'seconds').unix()
  );

  await uniswapV2Router02.swapTokensForExactTokens(
    1,
    2,
    [fakeDAE.address, skillToken.address],
    signers[0].address,
    moment().add(1, 'minutes').unix()
  );

  const LiqCalc = await ethers.getContractFactory('LiqCalc');
  const liqCalc = await LiqCalc.deploy(uniswapV2Factory.address);
  await liqCalc.deployed();
  const info = await liqCalc.pairInfo(skillToken.address, fakeDAE.address);
  console.log(info)
  
  await testSDK(skillToken.address, fakeDAE.address);



  
}

main()
  .then(() => process.exit(0))
  .catch(error => {
    console.error(error);
    process.exit(1);
  });

We deployed two ERC20 tokens, Skill and FakeDAE:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";

// for debug
import "hardhat/console.sol";


contract SkillToken is ERC20, AccessControl {
  uint public INITIAL_SUPPLY = 12000;
  bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

  function decimals() public view override returns (uint8) {
    console.log('Skill token decimals');
    return 18;
  }

  constructor() ERC20("SkillToken", "SKILL") {
    _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    _mint(_msgSender(), INITIAL_SUPPLY);
  }

  function burn(address from, uint256 amount) public {
    require(hasRole(BURNER_ROLE, _msgSender()), "Caller is not a burner");
    _burn(from, amount);
  }

}

and FakeDAE is analogeous.

LiqCal is a contract which reports the valid pair address:

//SPDX-License-Identifier: Unlicense

pragma solidity ^0.6.6;

import '@uniswap/v2-periphery/contracts/libraries/UniswapV2Library.sol';
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';

import "hardhat/console.sol";


contract LiqCalc  {
    address public factory;
    constructor(address factory_) public {
        factory = factory_;
    }

    function pairInfo(address tokenA, address tokenB) public view returns (uint reserveA, uint reserveB, uint totalSupply) {
        IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, tokenA, tokenB));
        console.log('LiqCal, pair addr:', address(pair));
        totalSupply = pair.totalSupply();
        (uint reserves0, uint reserves1,) = pair.getReserves();
        (reserveA, reserveB) = tokenA == pair.token0() ? (reserves0, reserves1) : (reserves1, reserves0);
    }
 
}

which is 0x21e7b0ced250469051a249252a33c7c5eb13a1f6

However, Fetcher returns invalid pair:

Fetcher.fetchPairData = function fetchPairData(tokenA, tokenB, provider) {
    console.log(`====> chainA=${tokenA.chainId}, addrA: ${tokenA.address}, chainB =${tokenB.chainId}, addrB: ${tokenB.address}`)
    try {
      console.log(`typeof provider: ${typeof provider}`)
      if (provider === undefined) provider = providers.getDefaultProvider(networks.getNetwork(tokenA.chainId));
      !(tokenA.chainId === tokenB.chainId) ? "development" !== "production" ? invariant(false, 'CHAIN_ID') : invariant(false) : void 0;
      var address = Pair.getAddress(tokenA, tokenB);
      console.log(`Pair address in Fetcher: ${address}, substituted: 0x21e7b0ced250469051a249252a33c7c5eb13a1f6`)

      address = '0x21e7b0ced250469051a249252a33c7c5eb13a1f6';
      return Promise.resolve(new contracts.Contract(address, IUniswapV2Pair.abi, provider).getReserves()).then(function (_ref) {
        var reserves0 = _ref[0],
            reserves1 = _ref[1];
        var balances = tokenA.sortsBefore(tokenB) ? [reserves0, reserves1] : [reserves1, reserves0];
        return new Pair(new TokenAmount(tokenA, balances[0]), new TokenAmount(tokenB, balances[1]));
      });
    } catch (e) {
      return Promise.reject(e);
    }
  };

from console.log:

Pair address in Fetcher: 0x7818b27560325CD29600237a5a14880b6f392Ea8, substituted: 0x21e7b0ced250469051a249252a33c7c5eb13a1f6

When I don't not substitute inside Fetcher my pair, getReserves() reverts obviously, because no such pair exists.

The entire log from the run with substituted pair address (all works):

npx hardhat run scripts/uniswap-script.js 
Skill Address: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0, FakeDAE address: 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9
LiqCal, pair addr: 0x21e7b0ced250469051a249252a33c7c5eb13a1f6
[
  BigNumber { _hex: '0x1f3f', _isBigNumber: true },
  BigNumber { _hex: '0x1f42', _isBigNumber: true },
  BigNumber { _hex: '0x1f40', _isBigNumber: true },
  reserveA: BigNumber { _hex: '0x1f3f', _isBigNumber: true },
  reserveB: BigNumber { _hex: '0x1f42', _isBigNumber: true },
  totalSupply: BigNumber { _hex: '0x1f40', _isBigNumber: true }
]
Skill token decimals
Fake token decmals
====> chainA=31337, addrA: 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9, chainB =31337, addrB: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
typeof provider: object
Calling Pair get address for A: 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9, B: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
Pair address in Fetcher: 0x7818b27560325CD29600237a5a14880b6f392Ea8, substituted: 0x21e7b0ced250469051a249252a33c7c5eb13a1f6
Calling Pair get address for A: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0, B: 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9
Pair {
  liquidityToken: Token {
    decimals: 18,
    symbol: 'UNI-V2',
    name: 'Uniswap V2',
    chainId: 31337,
    address: '0x7818b27560325CD29600237a5a14880b6f392Ea8'
  },
  tokenAmounts: [
    TokenAmount {
      numerator: [JSBI],
      denominator: [JSBI],
      currency: [Token],
      token: [Token]
    },
    TokenAmount {
      numerator: [JSBI],
      denominator: [JSBI],
      currency: [Token],
      token: [Token]
    }
  ]
}

What am I doing incorrect?

2
  • Facing the exact same error. Did you ever find a solution? Pair.getAddress simply returns an invalid address for many different token pairs that I tried. Quite confusing
    – NamanyayG
    May 20, 2021 at 19:23
  • 1
    @NamanyayG I gave up on using their SDK and communicate with the contract itself through ethers.js.
    – rlib
    May 20, 2021 at 20:41

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.