[Ethereum] Issues with Uniswap V2 addLiquidityETH()

etherremixsolidityuniswap

I've been learning how Solidity and Uniswap works.

I found a smart contract that appeals to me enough to want to use it to learn more Solidity with.

https://etherscan.io/address/0xa1fc6d28b2fc00ff0ff93b2da3f2b3c8ae85ad64#code

function openTrading() external onlyOwner() {
    require(!tradingOpen,"trading is already open");
    IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
    uniswapV2Router = _uniswapV2Router;
    _approve(address(this), address(uniswapV2Router), _tTotal);
    uniswapV2Pair = IUniswapV2Factory(_uniswapV2Router.factory()).createPair(address(this), _uniswapV2Router.WETH());
    uniswapV2Router.addLiquidityETH{value: address(this).balance}(address(this),balanceOf(address(this)),0,0,owner(),block.timestamp);
    swapEnabled = true;
    cooldownEnabled = true;
    _maxTxAmount = 4250000000 * 10**9;
    tradingOpen = true;
    IERC20(uniswapV2Pair).approve(address(uniswapV2Router), type(uint).max);
}

looks simple enough? merely running openTrading() should work, right?

Instead I get an error of "Warning! Error encountered during contract execution [Reverted]". It's a bit baffling. I've commented out each line to see what could be causing that blocker and it seems to do everything else when it doesn't call uniswapV2Router.addLiquidityETH(). I've looked at the documentation for Uniswap, checked the syntax, balances and they all seem fine.

So, for a noob, in the context of Solidity, how to debug this properly and efficiently? I've used Remix to deploy this. For Uniswap, any ideas?

The etherscan transaction is at https://kovan.etherscan.io/tx/0x85772a6da056371fdbc797498dcb2fd3fc3cbca52375698bc6d9f9787928e2e2

Best Answer

I wrote that contract :-D

It's a basic Safemoon fork that includes fixes for all of the issues found in the HashEx and CertiK audits. That plus there's a dev fee feature that taxes individual transactions so devs can earn something without having to do the traditional "team wallet" method of compensation that so many traders are weary of.

The whole reason behind the openTrading() function is that I didn't want certain "listing" bots to list the token before we were ready to launch. Other parts of the contract require the token's Uniswap pair address in order to enforce conditions designed to prevent "sniping" bots from buying too many tokens too early, but I couldn't create that pair address without those listing bots noticing. Those listing bots listen to any createPair() function calls made to the Uniswap factory contract. If I called createPair() in the constructor, which would create the pair as soon as the contract was deployed, then those bots would list the token as not sellable (which is accurate, there's no liquidity at that point), and that would scare off a lot of traders. If I used the Uniswap UI to add liquidity like normal (which also does a createPair() call), then I wouldn't be able to set the pair address in the contract, so the sniping bots would get in. I could have used a setPair() type of function that would be called after adding liquidity through the Uniswap UI, but that would create a window between adding liquidity and setting the pair address where the sniping bots could get in.

openTrading() seemed like the best solution to these problems. It creates the pair, then adds liquidity, then enables the anti-sniping bot protections, all in the same transaction. So the listing bots are happy, and no more window where the sniping bots can get in.

I didn't include any sanity checks (or instructions) for the parameters given to addLiquidityETH() because I didn't expect anyone else to use this code. It seems to have gained some popularity though.

The address(this).balance in the addLiquidityETH() call refers to the ETH balance of the contract itself. That's used for the initial liquidity of the Uniswap pair, and it gets converted to WETH by addLiquidityETH(). The other half of the liquidity pair would be the ERC20 tokens themselves, which is what that balanceOf(address(this)) is referring to. owner() in that line is used by Uniswap as the address that will receive the LP tokens that represent the pooled WETH/ERC20 tokens.

TLDR
You need to send the tokens and the ETH you plan on using for the initial liquidity to the contract itself. Once you do that, you should be able to call the openTrading() function. If something goes horribly wrong, you can use the manualsend() function to send the ETH from the contract to the first dev fee address. Also, that function can only be called by that first dev fee address, not the owner.

P.S.
The 4250000000 in the line _maxTxAmount = 4250000000 * 10**9; sets the maximum amount of tokens you're allowed to buy to 4.25 billion. Our initial liquidity was 850,000,000,000 tokens (1 trillion total supply, 15% burned), so 4.25 billion would be 0.5%, which seemed like a low enough number that it would prevent anyone from becoming a whale too early. After a minute or so the limit was raised to 1%, then 10%, then 100% (disabling the limit).

Related Topic