Purpose of Uniswap/Pancake Factory and Router, and integration in OpenZeppelin

Hi there,

I'm developer but new to solidity, I've done the tutorials available in the OpenZeppelin doc, absolutely awsome! I'd like to create my own token for my project and, to this end, I've reviewed some contracts of already available tokens in the wild (on BSC Scan mainly)

On all tokens I could find, I saw the reference to the Pancakeswap/Uniswap Factory / Router (except the contract address, APIs are the same for both as I understood as the first is a fork of the second).

I was wondering: "what are their uses?" but couldn't find any documentation on PancakeSwap nor UniSwap. I had the feeling that this is necessary to enable the creation of a pool on their platform, but I created a token without these interfaces (using OpenZeppelin framework) and I have been able to create LPs and make exchanges on PancakeSwap without any problem.

Sooo.... what's the deal? Is it if ever we want to be listed on their Pools/Farms stuff? Or id the API already embedded in OpenZeppelin?

If someone could explain or give a link, that'd be really great.

Thank you very much.

Best,
Jon

can you be more specify about what method do you confuse? UniSwap already make a full documentation, you can refer to their website. PancakeSwap? do not expect to much.

Hi @gsx, thanks for your answer.

So, on top of the ERC-20 interface, most tokens have another interface for UniSwap/PancakeSwap router like this:

interface IUniswapV2Factory {
    event PairCreated(address indexed token0, address indexed token1, address pair, uint);

    function feeTo() external view returns (address);
    function feeToSetter() external view returns (address);

    function getPair(address tokenA, address tokenB) external view returns (address pair);
    function allPairs(uint) external view returns (address pair);
    function allPairsLength() external view returns (uint);

    function createPair(address tokenA, address tokenB) external returns (address pair);

    function setFeeTo(address) external;
    function setFeeToSetter(address) external;
}


// pragma solidity >=0.5.0;

interface IUniswapV2Pair {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function kLast() external view returns (uint);

    function mint(address to) external returns (uint liquidity);
    function burn(address to) external returns (uint amount0, uint amount1);
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function skim(address to) external;
    function sync() external;

    function initialize(address, address) external;
}

// pragma solidity >=0.6.2;

interface IUniswapV2Router01 {
    function factory() external pure returns (address);
    function WETH() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity);
    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountToken, uint amountETH);
    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETHWithPermit(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountToken, uint amountETH);
    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapTokensForExactTokens(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);
    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);

    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}

interface IUniswapV2Router02 is IUniswapV2Router01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}

(Randomly taken from: https://bscscan.com/address/0xe1f8c6ce0d69cbeb67c6bc3685e52c96291394a8#code)

I'm not really sure why. What's the point of being able to interact directly from the contract to the UniSwap/PancakeSwap router / Factory?

With a bit of research, it feels like this is needed for "re-injecting" some tax into the liquidity pool, however it looks like most of the tokens just have it because they are blind forks of other tokens (which are themselves blind forks, etc....). The Safemoon contract has a function "swap_and_liquify" (or something like that) where the aforementioned interfaces are necessary because overloaded and used with this function.

So I think, please correct me if I'm wrong, these interfaces are needed only if you want to automate liquidity providing, or make some fancy token economics beyond just transaction fee to burn and transaction fee to holders, but most of the tokens embed those interfaces not because they need it, but because only a few know what they're doing.

When it comes to the documentation, I headed to UniSwap but couldn't find much more than the definition of the interface. Not much of explanations for a newcomer so that I haven't been really able to connect the dots so far (and simply answer thequestion "Why would I need it?"). If you know a resource that explains it in a gentler manner, I'm all in.

Thanks a lot.

The answer is they exist for a bigger purpose than to provide liquidity for some tokens. Router and Factory contracts are backbones of decentralized exchanges, where any ERC20 tokens, after having sufficient liquidity injected, can be traded. This is a revolutionary idea since prior to DEXs, CEXs are the ultimate evil of the industry. The token contracts you saw simple took the liquidity injection idea and incorporated it in their contracts. By doing this, tokens have a stable source of liquidity inflows and this can largely reduce the operation burden from project owners. As a result, projects get more autonomous and decentralized.

1 Like

yes, your on the right way. they include the interface to directly interact with UniSwap, PancakeSwap, or whatever it is.

most of token with unique tokenomic such as SafeMoon, etc, they use the router directly to automatically swap tokens and providing liquidity based on some trigger.

so for each transaction, the contract will check all the params, if every params is satisfied then contract will trigger Swap() and Liquidity() function.

like @maxaero said, its not only limited to Swap and Providing Liquidity only, it has more usage, such as generate pair address, check pair address, etc. even UniSwap has creating some library for devs.

2 Likes

Looks like yet another safemoon clone, SpaghettiInu (sp?) did some interesting fixes and is a nicer place to start -- for instance, the altered Ownable.sol is removed and he addresses liquidity provisioning on deployment pretty elegantly:

That said, specifically to your questions, the PCS router address is passed to the constructor in contract formation, see line 747 for how they're forming a liquidity pool immediately upon deployment.

PCS is a clone of uniswap so the uni code works just fine.

These coins call it "reflection" and on every buy/sell they're taking a percentage of the buy/sell, selling some of the coins and putting it into the established routers' liquidity pool. Those are the primary functions of the uniswap code in here.

If you do use it, consider making the router and pool addresses upgradable -- in a year we might be on PCS V6, or you might be your own DEX, and you don't want to be stuck in V2.

2 Likes

Great, I know get it. Thank you very much for your explanations.