"ERC20Token" hit a require or revert statement somewhere in its constructor

I am starting to get a grasp of both Solidity and Openzepplin and I can compile my contract with no issue (using Truffle), however when deploying it I get the following issue:

Migrations dry-run (simulation)
===============================
> Network name:    'ropsten-fork'
> Network id:      3
> Block gas limit: 8000000 (0x7a1200)

1_initial_migration.js
======================

   Deploying 'Migrations'
   ----------------------
   > block number:        10124487
   > block timestamp:     1619530689
   > account:             0x7F87bBc2Eb7911c8961c8506032d31d624894725
   > balance:             0.810175734
   > gas used:            160213 (0x271d5)
   > gas price:           2 gwei
   > value sent:          0 ETH
   > total cost:          0.000320426 ETH

   -------------------------------------
   > Total cost:         0.000320426 ETH

2_deploy.js
===========

   Deploying 'ERC20Token'
   ----------------------

Error:  *** Deployment Failed ***

"ERC20Token" hit a require or revert statement somewhere in its constructor. Try:
   * Verifying that your constructor params satisfy all require conditions.
   * Adding reason strings to your require statements.

    at C:\Users\ateyar\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\deployer\src\deployment.js:365:1
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at Migration._deploy (C:\Users\ateyar\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\Migration.js:74:1)
    at Migration._load (C:\Users\ateyar\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\Migration.js:61:1)
    at Migration.run (C:\Users\ateyar\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\Migration.js:212:1)
    at Object.runMigrations (C:\Users\ateyar\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\index.js:150:1)
    at Object.runFrom (C:\Users\ateyar\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\index.js:110:1)
    at Object.run (C:\Users\ateyar\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\index.js:87:1)
    at runMigrations (C:\Users\ateyar\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\commands\migrate.js:263:1)
    at setupDryRunEnvironmentThenRunMigrations (C:\Users\ateyar\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\commands\migrate.js:251:1)
    at Object.run (C:\Users\ateyar\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\commands\migrate.js:220:1)
    at Command.run (C:\Users\ateyar\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\command.js:140:1)
Truffle v5.3.2 (core: 5.3.2)
Node v14.16.0

This is the relevant code (extending Openzepplin):

--- SNIP ---
uint8 private _decimals;
uint256 private _totalSupply;
--- SNIP ---

constructor(
        string memory name_,
        string memory symbol_,
        uint8 decimals_,
        uint256 totalSupply_,
        address router_
    ) public ERC20(name_, symbol_) {
        _decimals = decimals_;
        _totalSupply = totalSupply_ * 10**decimals_;
        _rOwned[_msgSender()] = _rTotal;
        IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(router_);
        // Create a uniswap pair for this new token
        uniswapV2Pair = IUniswapV2Factory(_uniswapV2Router.factory())
            .createPair(address(this), _uniswapV2Router.WETH());
        // set the rest of the contract variables
        uniswapV2Router = _uniswapV2Router;
        //exclude owner and this contract sender fee
        _isExcludedFromFee[owner()] = true;
        _isExcludedFromFee[address(this)] = true;
        emit Transfer(address(0), _msgSender(), _totalSupply);
    }

    function decimals() public view virtual override returns (uint8) {
        return _decimals;
    }

    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }

--- SNIP ---

And my 2_deploy.js script:

const ERC20Token = artifacts.require("ERC20Token");

module.exports = async function (deployer) {
  const name = "Foo";
  const symbol = "$Foo";
  const decimals = 18;
  const totalSupply = 10 ** 15;

  const routers = {
    uniswap: {
      mainnet: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
      ropsten: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
      rinkeby: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
      gorli: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
      kovan: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
    },
  };

  await deployer.deploy(
    ERC20Token,
    name,
    symbol,
    decimals,
    totalSupply,
    routers.uniswap.ropsten
  );
};

Can anyone please please help?

Could you please share the full code?

And I am not sure, but where did you pass the parameter _rTotal

That’s basically the code from the Safemoon contract that I am adapting.

It is available there:

I can deploy this safemoon contract on the BSC-Chain correctly, when I try to deploy on the kovan with your code, your code is full ,so I get error.

What do you mean by the code is full? I am trying to deploy it on the ropsten testnet.

In the safemoon, I do not find this dependency.

Yeah I said that I adapted the Safemoon contract by making it extend ERC20 from openzeppelin the only thing that change is the construction I have attached on the first post and therefore I do not understand why it fails…

I am refactoring Safemoon, and ran into this issue.

Sorry, I do not have the full code, so I do not know what is wrong.

This is the contract, hope this helps! :pray:t4:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

import "@openzeppelin/contracts/access/Ownable.sol";

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

import "@openzeppelin/contracts/utils/Address.sol";

// (Uni|Pancake)Swap libs are interchangeable

import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";

import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";

import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router01.sol";

import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";

contract ERC20Token is ERC20, Ownable {

    using Address for address;

    uint8 private _decimals;

    uint256 private _totalSupply;

    mapping(address => uint256) private _rOwned;

    mapping(address => uint256) private _tOwned;

    mapping(address => mapping(address => uint256)) private _allowances;

    mapping(address => bool) private _isExcludedFromFee;

    mapping(address => bool) private _isExcluded;

    address[] private _excluded;

    uint256 private constant MAX = ~uint256(0);

    uint256 private _rTotal = (MAX - (MAX % _totalSupply));

    uint256 private _tFeeTotal;

    uint256 public _liquidityFee = 5;

    uint256 private _previousLiquidityFee = _liquidityFee;

    uint256 public _taxFee = 5;

    uint256 private _previousTaxFee = _taxFee;

    IUniswapV2Router02 public immutable uniswapV2Router;

    address public immutable uniswapV2Pair;

    bool inSwapAndLiquify;

    bool public swapAndLiquifyEnabled = true;

    uint256 public _maxTxAmount = 5000000 * 10**6 * 10**9;

    uint256 private numTokensSellToAddToLiquidity = 500000 * 10**6 * 10**9;

    event MinTokensBeforeSwapUpdated(uint256 minTokensBeforeSwap);

    event SwapAndLiquifyEnabledUpdated(bool enabled);

    event SwapAndLiquify(

        uint256 tokensSwapped,

        uint256 ethReceived,

        uint256 tokensIntoLiqudity

    );

    modifier lockTheSwap {

        inSwapAndLiquify = true;

        _;

        inSwapAndLiquify = false;

    }

    constructor(

        string memory name_,

        string memory symbol_,

        uint8 decimals_,

        uint256 totalSupply_,

        address router_

    ) public ERC20(name_, symbol_) {

        _decimals = decimals_;

        _totalSupply = totalSupply_ * 10**uint256(decimals_);

        _rOwned[_msgSender()] = _rTotal;

        IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(router_);

        // Create a uniswap pair for this new token

        uniswapV2Pair = IUniswapV2Factory(_uniswapV2Router.factory())

            .createPair(address(this), _uniswapV2Router.WETH());

        // set the rest of the contract variables

        uniswapV2Router = _uniswapV2Router;

        //exclude owner and this contract sender fee

        _isExcludedFromFee[owner()] = true;

        _isExcludedFromFee[address(this)] = true;

        emit Transfer(address(0), _msgSender(), _totalSupply);

    }

    function decimals() public view virtual override returns (uint8) {

        return _decimals;

    }

    function totalSupply() public view override returns (uint256) {

        return _totalSupply;

    }

    function balanceOf(address account) public view override returns (uint256) {

        if (_isExcluded[account]) return _tOwned[account];

        return tokenFromReflection(_rOwned[account]);

    }

    function _transfer(

        address sender,

        address recipient,

        uint256 amount

    ) internal virtual override {

        require(

            sender != address(0),

            "ERC20: transfer sender the zero address"

        );

        require(recipient != address(0), "ERC20: transfer to the zero address");

        require(amount > 0, "Transfer amount must be greater than zero");

        if (sender != owner() && recipient != owner())

            require(

                amount <= _maxTxAmount,

                "Transfer amount exceeds the maxTxAmount."

            );

        // is the token balance of this contract address over the min number of

        // tokens that we need to initiate a swap + liquidity lock?

        // also, don't get caught in a circular liquidity event.

        // also, don't swap & liquify if sender is uniswap pair.

        uint256 contractTokenBalance = balanceOf(address(this));

        if (contractTokenBalance >= _maxTxAmount) {

            contractTokenBalance = _maxTxAmount;

        }

        bool overMinTokenBalance =

            contractTokenBalance >= numTokensSellToAddToLiquidity;

        if (

            overMinTokenBalance &&

            !inSwapAndLiquify &&

            sender != uniswapV2Pair &&

            swapAndLiquifyEnabled

        ) {

            contractTokenBalance = numTokensSellToAddToLiquidity;

            //add liquidity

            swapAndLiquify(contractTokenBalance);

        }

        //indicates if fee should be deducted sender transfer

        bool takeFee = true;

        //if any account belongs to _isExcludedFromFee account then remove the fee

        if (_isExcludedFromFee[sender] || _isExcludedFromFee[recipient]) {

            takeFee = false;

        }

        //transfer amount, it will take tax, burn, liquidity fee

        _tokenTransfer(sender, recipient, amount, takeFee);

    }

    function isExcludedFromReward(address account) public view returns (bool) {

        return _isExcluded[account];

    }

    function totalFees() public view returns (uint256) {

        return _tFeeTotal;

    }

    function deliver(uint256 tAmount) public {

        address sender = _msgSender();

        require(

            !_isExcluded[sender],

            "Excluded addresses cannot call this function"

        );

        (uint256 rAmount, , , , , ) = _getValues(tAmount);

        _rOwned[sender] = _rOwned[sender] - rAmount;

        _rTotal = _rTotal - rAmount;

        _tFeeTotal = _tFeeTotal + tAmount;

    }

    function reflectionFromToken(uint256 tAmount, bool deductTransferFee)

        public

        view

        returns (uint256)

    {

        require(tAmount <= _totalSupply, "Amount must be less than supply");

        if (!deductTransferFee) {

            (uint256 rAmount, , , , , ) = _getValues(tAmount);

            return rAmount;

        } else {

            (, uint256 rTransferAmount, , , , ) = _getValues(tAmount);

            return rTransferAmount;

        }

    }

    function tokenFromReflection(uint256 rAmount)

        public

        view

        returns (uint256)

    {

        require(

            rAmount <= _rTotal,

            "Amount must be less than total reflections"

        );

        uint256 currentRate = _getRate();

        return rAmount / currentRate;

    }

    function excludeFromReward(address account) public onlyOwner() {

        // require(account != 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D, 'We can not exclude Uniswap router.');

        require(!_isExcluded[account], "Account is already excluded");

        if (_rOwned[account] > 0) {

            _tOwned[account] = tokenFromReflection(_rOwned[account]);

        }

        _isExcluded[account] = true;

        _excluded.push(account);

    }

    function includeInReward(address account) external onlyOwner() {

        require(_isExcluded[account], "Account is already excluded");

        for (uint256 i = 0; i < _excluded.length; i++) {

            if (_excluded[i] == account) {

                _excluded[i] = _excluded[_excluded.length - 1];

                _tOwned[account] = 0;

                _isExcluded[account] = false;

                _excluded.pop();

                break;

            }

        }

    }

    function _transferBothExcluded(

        address sender,

        address recipient,

        uint256 tAmount

    ) private {

        (

            uint256 rAmount,

            uint256 rTransferAmount,

            uint256 rFee,

            uint256 tTransferAmount,

            uint256 tFee,

            uint256 tLiquidity

        ) = _getValues(tAmount);

        _tOwned[sender] = _tOwned[sender] - tAmount;

        _rOwned[sender] = _rOwned[sender] - rAmount;

        _tOwned[recipient] = _tOwned[recipient] + tTransferAmount;

        _rOwned[recipient] = _rOwned[recipient] + rTransferAmount;

        _takeLiquidity(tLiquidity);

        _reflectFee(rFee, tFee);

        emit Transfer(sender, recipient, tTransferAmount);

    }

    function excludeFromFee(address account) public onlyOwner {

        _isExcludedFromFee[account] = true;

    }

    function includeInFee(address account) public onlyOwner {

        _isExcludedFromFee[account] = false;

    }

    function setTaxFeePercent(uint256 taxFee) external onlyOwner() {

        _taxFee = taxFee;

    }

    function setLiquidityFeePercent(uint256 liquidityFee) external onlyOwner() {

        _liquidityFee = liquidityFee;

    }

    function setMaxTxPercent(uint256 maxTxPercent) external onlyOwner() {

        _maxTxAmount = (_totalSupply * maxTxPercent) / (10**2);

    }

    function setSwapAndLiquifyEnabled(bool _enabled) public onlyOwner {

        swapAndLiquifyEnabled = _enabled;

        emit SwapAndLiquifyEnabledUpdated(_enabled);

    }

    //to recieve ETH sender uniswapV2Router when swaping

    receive() external payable {}

    function _reflectFee(uint256 rFee, uint256 tFee) private {

        _rTotal = _rTotal - rFee;

        _tFeeTotal = _tFeeTotal + tFee;

    }

    function _getValues(uint256 tAmount)

        private

        view

        returns (

            uint256,

            uint256,

            uint256,

            uint256,

            uint256,

            uint256

        )

    {

        (uint256 tTransferAmount, uint256 tFee, uint256 tLiquidity) =

            _getTValues(tAmount);

        (uint256 rAmount, uint256 rTransferAmount, uint256 rFee) =

            _getRValues(tAmount, tFee, tLiquidity, _getRate());

        return (

            rAmount,

            rTransferAmount,

            rFee,

            tTransferAmount,

            tFee,

            tLiquidity

        );

    }

    function _getTValues(uint256 tAmount)

        private

        view

        returns (

            uint256,

            uint256,

            uint256

        )

    {

        uint256 tFee = calculateTaxFee(tAmount);

        uint256 tLiquidity = calculateLiquidityFee(tAmount);

        uint256 tTransferAmount = tAmount - tFee - tLiquidity;

        return (tTransferAmount, tFee, tLiquidity);

    }

    function _getRValues(

        uint256 tAmount,

        uint256 tFee,

        uint256 tLiquidity,

        uint256 currentRate

    )

        private

        pure

        returns (

            uint256,

            uint256,

            uint256

        )

    {

        uint256 rAmount = tAmount * currentRate;

        uint256 rFee = tFee * currentRate;

        uint256 rLiquidity = tLiquidity * currentRate;

        uint256 rTransferAmount = rAmount - rFee - rLiquidity;

        return (rAmount, rTransferAmount, rFee);

    }

    function _getRate() private view returns (uint256) {

        (uint256 rSupply, uint256 tSupply) = _getCurrentSupply();

        return rSupply / tSupply;

    }

    function _getCurrentSupply() private view returns (uint256, uint256) {

        uint256 rSupply = _rTotal;

        uint256 tSupply = _totalSupply;

        for (uint256 i = 0; i < _excluded.length; i++) {

            if (

                _rOwned[_excluded[i]] > rSupply ||

                _tOwned[_excluded[i]] > tSupply

            ) return (_rTotal, _totalSupply);

            rSupply = rSupply - _rOwned[_excluded[i]];

            tSupply = tSupply - _tOwned[_excluded[i]];

        }

        if (rSupply < (_rTotal / _totalSupply)) return (_rTotal, _totalSupply);

        return (rSupply, tSupply);

    }

    function _takeLiquidity(uint256 tLiquidity) private {

        uint256 currentRate = _getRate();

        uint256 rLiquidity = tLiquidity * currentRate;

        _rOwned[address(this)] = _rOwned[address(this)] + rLiquidity;

        if (_isExcluded[address(this)])

            _tOwned[address(this)] = _tOwned[address(this)] + tLiquidity;

    }

    function calculateTaxFee(uint256 _amount) private view returns (uint256) {

        return (_amount * _taxFee) / (10**2);

    }

    function calculateLiquidityFee(uint256 _amount)

        private

        view

        returns (uint256)

    {

        return (_amount * _liquidityFee) / (10**2);

    }

    function removeAllFee() private {

        if (_taxFee == 0 && _liquidityFee == 0) return;

        _previousTaxFee = _taxFee;

        _previousLiquidityFee = _liquidityFee;

        _taxFee = 0;

        _liquidityFee = 0;

    }

    function restoreAllFee() private {

        _taxFee = _previousTaxFee;

        _liquidityFee = _previousLiquidityFee;

    }

    function isExcludedFromFee(address account) public view returns (bool) {

        return _isExcludedFromFee[account];

    }

    function swapAndLiquify(uint256 contractTokenBalance) private lockTheSwap {

        // split the contract balance into halves

        uint256 half = contractTokenBalance / 2;

        uint256 otherHalf = contractTokenBalance - half;

        // capture the contract's current ETH balance.

        // this is so that we can capture exactly the amount of ETH that the

        // swap creates, and not make the liquidity event include any ETH that

        // has been manually sent to the contract

        uint256 initialBalance = address(this).balance;

        // swap tokens for ETH

        swapTokensForEth(half); // <- this breaks the ETH -> HATE swap when swap+liquify is triggered

        // how much ETH did we just swap into?

        uint256 newBalance = address(this).balance - initialBalance;

        // add liquidity to uniswap

        addLiquidity(otherHalf, newBalance);

        emit SwapAndLiquify(half, newBalance, otherHalf);

    }

    function swapTokensForEth(uint256 tokenAmount) private {

        // generate the uniswap pair path of token -> weth

        address[] memory path = new address[](2);

        path[0] = address(this);

        path[1] = uniswapV2Router.WETH();

        _approve(address(this), address(uniswapV2Router), tokenAmount);

        // make the swap

        uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(

            tokenAmount,

            0, // accept any amount of ETH

            path,

            address(this),

            block.timestamp

        );

    }

    function addLiquidity(uint256 tokenAmount, uint256 ethAmount) private {

        // approve token transfer to cover all possible scenarios

        _approve(address(this), address(uniswapV2Router), tokenAmount);

        // add the liquidity

        uniswapV2Router.addLiquidityETH{value: ethAmount}(

            address(this),

            tokenAmount,

            0, // slippage is unavoidable

            0, // slippage is unavoidable

            owner(),

            block.timestamp

        );

    }

    //this method is responsible for taking all fee, if takeFee is true

    function _tokenTransfer(

        address sender,

        address recipient,

        uint256 amount,

        bool takeFee

    ) private {

        if (!takeFee) removeAllFee();

        if (_isExcluded[sender] && !_isExcluded[recipient]) {

            _transferFromExcluded(sender, recipient, amount);

        } else if (!_isExcluded[sender] && _isExcluded[recipient]) {

            _transferToExcluded(sender, recipient, amount);

        } else if (!_isExcluded[sender] && !_isExcluded[recipient]) {

            _transferStandard(sender, recipient, amount);

        } else if (_isExcluded[sender] && _isExcluded[recipient]) {

            _transferBothExcluded(sender, recipient, amount);

        } else {

            _transferStandard(sender, recipient, amount);

        }

        if (!takeFee) restoreAllFee();

    }

    function _transferStandard(

        address sender,

        address recipient,

        uint256 tAmount

    ) private {

        (

            uint256 rAmount,

            uint256 rTransferAmount,

            uint256 rFee,

            uint256 tTransferAmount,

            uint256 tFee,

            uint256 tLiquidity

        ) = _getValues(tAmount);

        _rOwned[sender] = _rOwned[sender] - rAmount;

        _rOwned[recipient] = _rOwned[recipient] + rTransferAmount;

        _takeLiquidity(tLiquidity);

        _reflectFee(rFee, tFee);

        emit Transfer(sender, recipient, tTransferAmount);

    }

    function _transferToExcluded(

        address sender,

        address recipient,

        uint256 tAmount

    ) private {

        (

            uint256 rAmount,

            uint256 rTransferAmount,

            uint256 rFee,

            uint256 tTransferAmount,

            uint256 tFee,

            uint256 tLiquidity

        ) = _getValues(tAmount);

        _rOwned[sender] = _rOwned[sender] - rAmount;

        _tOwned[recipient] = _tOwned[recipient] + tTransferAmount;

        _rOwned[recipient] = _rOwned[recipient] + rTransferAmount;

        _takeLiquidity(tLiquidity);

        _reflectFee(rFee, tFee);

        emit Transfer(sender, recipient, tTransferAmount);

    }

    function _transferFromExcluded(

        address sender,

        address recipient,

        uint256 tAmount

    ) private {

        (

            uint256 rAmount,

            uint256 rTransferAmount,

            uint256 rFee,

            uint256 tTransferAmount,

            uint256 tFee,

            uint256 tLiquidity

        ) = _getValues(tAmount);

        _tOwned[sender] = _tOwned[sender] - tAmount;

        _rOwned[sender] = _rOwned[sender] - rAmount;

        _rOwned[recipient] = _rOwned[recipient] + rTransferAmount;

        _takeLiquidity(tLiquidity);

        _reflectFee(rFee, tFee);

        emit Transfer(sender, recipient, tTransferAmount);

    }

}

Sorry, need more time to have a look.

No worries @Skyge, I really appreciate you taking time to try to help me with this issue. As a side question, would you by any chance know how to fork BSC test net with ganache/truffle to run offline tests?

Sorry, I am not familiar with the BSC-Chain, maybe you should ask for help in their forum: Home | Binance Chain Forum

How about their document: Introduction - Binance Chain Docs

I think it just like fork the mainnet on the ethereum.