title: ERC20: mint to the zero address exception when trying to buy my own ERC20 token (based on OpenZeppelin) from my own simple DEX
body: <p>I have implemented my own ERC20 token (based on OpenZeppelin's implementation). I created a supply for my token and used that supply to create a very simple decentralized exchange (DEX) based on <a href="https://ethereum.org/en/developers/tutorials/transfers-and-approval-of-erc-20-tokens-from-a-solidity-smart-contract/" rel="nofollow noreferrer">this guide</a>. I do not understand why I keep getting the <code>ERC20: mint to the zero address exception</code> even when I comment out the part of the code which causes it. I made sure that I ran <code>truffle migrate --reset</code> after I changed my contract. I also restarted Ganache, then re-compiled and migrated all the contracts again to no avail.</p>
  <p>Here's the code for my ERC20 token (it's the same as the OpenZeppelin implementation) <code>MyCoin.sol</code>:</p>
  <pre><code>/*
      Taken from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol
  */
  
  // SPDX-License-Identifier: MIT
  // OpenZeppelin Contracts v4.3.2 (token/ERC20/ERC20.sol)
  
  pragma solidity ^0.8.0;
  
  import &quot;./IERC20.sol&quot;;
  import &quot;./extensions/IERC20Metadata.sol&quot;;
  import &quot;./utils/Context.sol&quot;;
  
  /**
   * @dev Implementation of the {IERC20} interface.
   *
   * This implementation is agnostic to the way tokens are created. This means
   * that a supply mechanism has to be added in a derived contract using {_mint}.
   * For a generic mechanism see {ERC20PresetMinterPauser}.
   *
   * TIP: For a detailed writeup see our guide
   * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
   * to implement supply mechanisms].
   *
   * We have followed general OpenZeppelin Contracts guidelines: functions revert
   * instead returning `false` on failure. This behavior is nonetheless
   * conventional and does not conflict with the expectations of ERC20
   * applications.
   *
   * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
   * This allows applications to reconstruct the allowance for all accounts just
   * by listening to said events. Other implementations of the EIP may not emit
   * these events, as it isn't required by the specification.
   *
   * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
   * functions have been added to mitigate the well-known issues around setting
   * allowances. See {IERC20-approve}.
   */
  contract MyCoin is Context, IERC20, IERC20Metadata {
      mapping(address =&gt; uint256) private _balances;
  
      mapping(address =&gt; mapping(address =&gt; uint256)) private _allowances;
  
      uint256 private _totalSupply;
  
      string private _name;
      string private _symbol;
  
      /**
       * @dev Sets the values for {name} and {symbol}.
       *
       * The default value of {decimals} is 18. To select a different value for
       * {decimals} you should overload it.
       *
       * All two of these values are immutable: they can only be set once during
       * construction.
       */
      constructor(string memory name_, string memory symbol_) {
          _name = name_;
          _symbol = symbol_;
      }
  
      /**
       * @dev Returns the name of the token.
       */
      function name() public view virtual override returns (string memory) {
          return _name;
      }
  
      /**
       * @dev Returns the symbol of the token, usually a shorter version of the
       * name.
       */
      function symbol() public view virtual override returns (string memory) {
          return _symbol;
      }
  
      /**
       * @dev Returns the number of decimals used to get its user representation.
       * For example, if `decimals` equals `2`, a balance of `505` tokens should
       * be displayed to a user as `5.05` (`505 / 10 ** 2`).
       *
       * Tokens usually opt for a value of 18, imitating the relationship between
       * Ether and Wei. This is the value {ERC20} uses, unless this function is
       * overridden;
       *
       * NOTE: This information is only used for _display_ purposes: it in
       * no way affects any of the arithmetic of the contract, including
       * {IERC20-balanceOf} and {IERC20-transfer}.
       */
      function decimals() public view virtual override returns (uint8) {
          return 18;
      }
  
      /**
       * @dev See {IERC20-totalSupply}.
       */
      function totalSupply() public view virtual override returns (uint256) {
          return _totalSupply;
      }
  
      /**
       * @dev See {IERC20-balanceOf}.
       */
      function balanceOf(address account) public view virtual override returns (uint256) {
          return _balances[account];
      }
  
      /**
       * @dev See {IERC20-transfer}.
       *
       * Requirements:
       *
       * - `recipient` cannot be the zero address.
       * - the caller must have a balance of at least `amount`.
       */
      function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
          _transfer(_msgSender(), recipient, amount);
          return true;
      }
  
      /**
       * @dev See {IERC20-allowance}.
       */
      function allowance(address owner, address spender) public view virtual override returns (uint256) {
          return _allowances[owner][spender];
      }
  
      /**
       * @dev See {IERC20-approve}.
       *
       * Requirements:
       *
       * - `spender` cannot be the zero address.
       */
      function approve(address spender, uint256 amount) public virtual override returns (bool) {
          _approve(_msgSender(), spender, amount);
          return true;
      }
  
      /**
       * @dev See {IERC20-transferFrom}.
       *
       * Emits an {Approval} event indicating the updated allowance. This is not
       * required by the EIP. See the note at the beginning of {ERC20}.
       *
       * Requirements:
       *
       * - `sender` and `recipient` cannot be the zero address.
       * - `sender` must have a balance of at least `amount`.
       * - the caller must have allowance for ``sender``'s tokens of at least
       * `amount`.
       */
      function transferFrom(
          address sender,
          address recipient,
          uint256 amount
      ) public virtual override returns (bool) {
          _transfer(sender, recipient, amount);
  
          uint256 currentAllowance = _allowances[sender][_msgSender()];
          require(currentAllowance &gt;= amount, &quot;ERC20: transfer amount exceeds allowance&quot;);
          unchecked {
              _approve(sender, _msgSender(), currentAllowance - amount);
          }
  
          return true;
      }
  
      /**
       * @dev Atomically increases the allowance granted to `spender` by the caller.
       *
       * This is an alternative to {approve} that can be used as a mitigation for
       * problems described in {IERC20-approve}.
       *
       * Emits an {Approval} event indicating the updated allowance.
       *
       * Requirements:
       *
       * - `spender` cannot be the zero address.
       */
      function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
          _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
          return true;
      }
  
      /**
       * @dev Atomically decreases the allowance granted to `spender` by the caller.
       *
       * This is an alternative to {approve} that can be used as a mitigation for
       * problems described in {IERC20-approve}.
       *
       * Emits an {Approval} event indicating the updated allowance.
       *
       * Requirements:
       *
       * - `spender` cannot be the zero address.
       * - `spender` must have allowance for the caller of at least
       * `subtractedValue`.
       */
      function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
          uint256 currentAllowance = _allowances[_msgSender()][spender];
          require(currentAllowance &gt;= subtractedValue, &quot;ERC20: decreased allowance below zero&quot;);
          unchecked {
              _approve(_msgSender(), spender, currentAllowance - subtractedValue);
          }
  
          return true;
      }
  
      /**
       * @dev Moves `amount` of tokens from `sender` to `recipient`.
       *
       * This internal function is equivalent to {transfer}, and can be used to
       * e.g. implement automatic token fees, slashing mechanisms, etc.
       *
       * Emits a {Transfer} event.
       *
       * Requirements:
       *
       * - `sender` cannot be the zero address.
       * - `recipient` cannot be the zero address.
       * - `sender` must have a balance of at least `amount`.
       */
      function _transfer(
          address sender,
          address recipient,
          uint256 amount
      ) internal virtual {
          require(sender != address(0), &quot;ERC20: transfer from the zero address&quot;);
          require(recipient != address(0), &quot;ERC20: transfer to the zero address&quot;);
  
          _beforeTokenTransfer(sender, recipient, amount);
  
          uint256 senderBalance = _balances[sender];
          require(senderBalance &gt;= amount, &quot;ERC20: transfer amount exceeds balance&quot;);
          unchecked {
              _balances[sender] = senderBalance - amount;
          }
          _balances[recipient] += amount;
  
          emit Transfer(sender, recipient, amount);
  
          _afterTokenTransfer(sender, recipient, amount);
      }
  
      /** @dev Creates `amount` tokens and assigns them to `account`, increasing
       * the total supply.
       *
       * Emits a {Transfer} event with `from` set to the zero address.
       *
       * Requirements:
       *
       * - `account` cannot be the zero address.
       */
      function _mint(address account, uint256 amount) internal virtual {
          // require(account != address(0), &quot;ERC20: mint to the zero address&quot;); // this is commented out for development purposes; TODO: uncomment this in production
  
          _beforeTokenTransfer(address(0), account, amount);
  
          _totalSupply += amount;
          _balances[account] += amount;
          emit Transfer(address(0), account, amount);
  
          _afterTokenTransfer(address(0), account, amount);
      }
  
      /**
       * @dev Destroys `amount` tokens from `account`, reducing the
       * total supply.
       *
       * Emits a {Transfer} event with `to` set to the zero address.
       *
       * Requirements:
       *
       * - `account` cannot be the zero address.
       * - `account` must have at least `amount` tokens.
       */
      function _burn(address account, uint256 amount) internal virtual {
          require(account != address(0), &quot;ERC20: burn from the zero address&quot;);
  
          _beforeTokenTransfer(account, address(0), amount);
  
          uint256 accountBalance = _balances[account];
          require(accountBalance &gt;= amount, &quot;ERC20: burn amount exceeds balance&quot;);
          unchecked {
              _balances[account] = accountBalance - amount;
          }
          _totalSupply -= amount;
  
          emit Transfer(account, address(0), amount);
  
          _afterTokenTransfer(account, address(0), amount);
      }
  
      /**
       * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
       *
       * This internal function is equivalent to `approve`, and can be used to
       * e.g. set automatic allowances for certain subsystems, etc.
       *
       * Emits an {Approval} event.
       *
       * Requirements:
       *
       * - `owner` cannot be the zero address.
       * - `spender` cannot be the zero address.
       */
      function _approve(
          address owner,
          address spender,
          uint256 amount
      ) internal virtual {
          require(owner != address(0), &quot;ERC20: approve from the zero address&quot;);
          require(spender != address(0), &quot;ERC20: approve to the zero address&quot;);
  
          _allowances[owner][spender] = amount;
          emit Approval(owner, spender, amount);
      }
  
      /**
       * @dev Hook that is called before any transfer of tokens. This includes
       * minting and burning.
       *
       * Calling conditions:
       *
       * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
       * will be transferred to `to`.
       * - when `from` is zero, `amount` tokens will be minted for `to`.
       * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
       * - `from` and `to` are never both zero.
       *
       * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
       */
      function _beforeTokenTransfer(
          address from,
          address to,
          uint256 amount
      ) internal virtual {}
  
      /**
       * @dev Hook that is called after any transfer of tokens. This includes
       * minting and burning.
       *
       * Calling conditions:
       *
       * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
       * has been transferred to `to`.
       * - when `from` is zero, `amount` tokens have been minted for `to`.
       * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
       * - `from` and `to` are never both zero.
       *
       * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
       */
      function _afterTokenTransfer(
          address from,
          address to,
          uint256 amount
      ) internal virtual {}
  }
  </code></pre>
  <p>Notice how I commented out the <code>require</code> line in <code>_mint</code>.</p>
  <p>Here's the code for <code>MyCoinSupply.sol</code>:</p>
  <pre><code>/*
      Adapted from from https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226
  */
  
  pragma solidity ^0.8.0;
  
  import &quot;./MyCoin.sol&quot;;
  
  contract MyCoinSupply is MyCoin(&quot;MyCoin&quot;, &quot;MyC&quot;) // MyCoin is ERC20
  {
      constructor() public // gives 1000 tokens to the owner
      {
          _mint(msg.sender, 1000);
      }
  
      function _mintMinerReward() internal // gives 20 tokens to the miner
      {
          _mint(block.coinbase, 20);
      }
  
      function _transfer(address from, address to, uint256 value) override internal
      {
          _mintMinerReward();
          super._transfer(from, to, value);
      }
  }
  </code></pre>
  <p>and here's the code for <code>MyCoinDEX.sol</code>:</p>
  <pre><code>/*
      Adapted from https://ethereum.org/en/developers/tutorials/transfers-and-approval-of-erc-20-tokens-from-a-solidity-smart-contract/
  
      I didn't try to code my own DEX from scratch because I wasn't familiar with token to Ether conversion dynamics,
      so I thought a better idea would be to modify existing solutions.
  */
  
  pragma solidity ^0.8.0;
  
  import &quot;./IERC20.sol&quot;;
  import &quot;./MyCoinSupply.sol&quot;;
  
  contract MyCoinDEX
  {
      IERC20 public token;
  
      event Bought(uint256 amount);
      event Sold(uint256 amount);
  
      constructor() public
      {
          token = new MyCoinSupply();
      }
  
      function showSender() public view returns (address) // for debugging purposes
      {
          return (msg.sender);
      }
  
      function buy() payable public // send ether and get tokens in exchange; 1 token == 1 ether
      {
        uint256 amountTobuy = msg.value;
        uint256 dexBalance = token.balanceOf(address(this));
        require(amountTobuy &gt; 0, &quot;You need to send some ether&quot;);
        require(amountTobuy &lt;= dexBalance, &quot;Not enough tokens in the reserve&quot;);
        token.transfer(msg.sender, amountTobuy);
        emit Bought(amountTobuy);
      }
  
      function sell(uint256 amount) public // send tokens to get ether back
      {
        require(amount &gt; 0, &quot;You need to sell at least some tokens&quot;);
        uint256 allowance = token.allowance(msg.sender, address(this));
        require(allowance &gt;= amount, &quot;Check the token allowance&quot;);
        token.transferFrom(msg.sender, address(this), amount);
        payable(msg.sender).transfer(amount);
        emit Sold(amount);
      }
  
  }
  </code></pre>
  <p>Here's how I interact with the contract:</p>
  <pre><code>truffle(development)&gt; let instance = await MyCoinDEX.deployed()
  undefined
  truffle(development)&gt; instance.buy({value: 1})
  Uncaught:
  Error: Returned error: VM Exception while processing transaction: revert ERC20: mint to the zero address -- Reason given: ERC20: mint to the zero address.
      at evalmachine.&lt;anonymous&gt;
      at sigintHandlersWrap (node:vm:268:12)
      at Script.runInContext (node:vm:137:14)
      at runScript (/home/john/.nvm/versions/node/v16.13.0/lib/node_modules/truffle/build/webpack:/packages/core/lib/console.js:364:1)
      at Console.interpret (/home/john/.nvm/versions/node/v16.13.0/lib/node_modules/truffle/build/webpack:/packages/core/lib/console.js:379:1)
      at bound (node:domain:421:15)
      at REPLServer.runBound [as eval] (node:domain:432:12)
      at REPLServer.onLine (node:repl:889:10)
      at REPLServer.emit (node:events:390:28)
      at REPLServer.emit (node:domain:475:12)
      at REPLServer.Interface._onLine (node:readline:487:10)
      at REPLServer.Interface._line (node:readline:864:8)
      at REPLServer.Interface._ttyWrite (node:readline:1216:14)
      at REPLServer.self._ttyWrite (node:repl:984:9) {
    data: {
      '0xae1903eb07c1e80c3b8ddef277d81496afdfa9a599e5be75d3234ecf19003078': {
        error: 'revert',
        program_counter: 663,
        return: '0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001f45524332303a206d696e7420746f20746865207a65726f206164647265737300',
        reason: 'ERC20: mint to the zero address'
      },
      stack: 'RuntimeError: VM Exception while processing transaction: revert ERC20: mint to the zero address\n' +
        '    at Function.RuntimeError.fromResults (/tmp/.mount_ganach2RAdVJ/resources/static/node/node_modules/ganache-core/lib/utils/runtimeerror.js:94:13)\n' +
        '    at BlockchainDouble.processBlock (/tmp/.mount_ganach2RAdVJ/resources/static/node/node_modules/ganache-core/lib/blockchain_double.js:627:24)\n' +
        '    at runMicrotasks (&lt;anonymous&gt;)\n' +
        '    at processTicksAndRejections (internal/process/task_queues.js:93:5)',
      name: 'RuntimeError'
    },
    reason: 'ERC20: mint to the zero address',
    hijackedStack: 'Error: Returned error: VM Exception while processing transaction: revert ERC20: mint to the zero address -- Reason given: ERC20: mint to the zero address.\n' +
      '    at Object.ErrorResponse (/home/john/.nvm/versions/node/v16.13.0/lib/node_modules/truffle/build/webpack:/node_modules/web3-core-helpers/lib/errors.js:28:1)\n' +
      '    at /home/john/.nvm/versions/node/v16.13.0/lib/node_modules/truffle/build/webpack:/node_modules/web3/node_modules/web3-core-requestmanager/lib/index.js:302:1\n' +
      '    at /home/john/.nvm/versions/node/v16.13.0/lib/node_modules/truffle/build/webpack:/packages/provider/wrapper.js:107:1\n' +
      '    at XMLHttpRequest.request.onreadystatechange (/home/john/.nvm/versions/node/v16.13.0/lib/node_modules/truffle/build/webpack:/node_modules/web3/node_modules/web3-providers-http/lib/index.js:98:1)\n' +
      '    at XMLHttpRequestEventTarget.dispatchEvent (/home/john/.nvm/versions/node/v16.13.0/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request-event-target.js:34:1)\n' +
      '    at XMLHttpRequest.exports.modules.996763.XMLHttpRequest._setReadyState (/home/john/.nvm/versions/node/v16.13.0/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:208:1)\n' +
      '    at XMLHttpRequest.exports.modules.996763.XMLHttpRequest._onHttpResponseEnd (/home/john/.nvm/versions/node/v16.13.0/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:318:1)\n' +
      '    at IncomingMessage.&lt;anonymous&gt; (/home/john/.nvm/versions/node/v16.13.0/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:289:47)\n' +
      '    at IncomingMessage.emit (node:events:402:35)\n' +
      '    at IncomingMessage.emit (node:domain:537:15)\n' +
      '    at endReadableNT (node:internal/streams/readable:1343:12)\n' +
      '    at processTicksAndRejections (node:internal/process/task_queues:83:21)'
  }
  </code></pre>
  <p>I tested the hypothesis that msg.sender might be 0, but it isn't, because:</p>
  <pre><code>truffle(development)&gt; instance.showSender()
  '0x9292726A62cAeE7CDCd573ca02F11FE03306d950'
  </code></pre>
  <p>and even if it was, I commented out the code that throws the specific exception I get.</p>
  <p><strong>Can somebody take a look and tell me what is going on here?</strong> I'm bamboozled by this problem.</p>