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.


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


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.

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.

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).

  • Why does it have the swapEnabled = true;? Doesn't it look scammy to allow swapping to be disabled?
    – KingBoomie
    Jun 23 at 15:23
  • swapEnabled refers to the swap that happens when tokens are getting liquidated for the dev fees. Disabling that just disables the dev fees, not the swaps that occur when buying/selling.
    – dexMonkey
    Jun 24 at 16:05
  • You said after a minute or two the limit was raised. Was this automatic or through a call to the contract? Can you give details about how exactly you removed the limit? Jul 13 at 20:20
  • In later revisions/forks it was automatic, but in the contract OP mentions it was done using the setMaxTxPercent(uint256 maxTxPercent) function. That parameter is a percent of the total supply, so increase it to 1-99 to set the max to 1-99%, or 100% to disable the limit completely.
    – dexMonkey
    Jul 16 at 3:25

Not the answer you're looking for? Browse other questions tagged or ask your own question.