Minting quantity error on deployed contract

I created an ERC-20 contract below. Testing on the Remix London works great. I'm able to mint 1,000,000 tokens and using the tools I can verify the balance on the account.

Next I deploy successfully to Polygon via Remix / Metamask. When I use the same Mint function in Remix, the Mint goes through via Metamask, but the contract mints 0.000000000001 tokens. My brain has melted and I would greatly appreciate feedback, examples or any other help.

https://polygonscan.com/token/0xca47a0513fe65314d018ae3d4c5c786a8b04ab52?a=0x040B7eD99550906E879C86b3930bb8033D4568bf
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Snapshot.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20FlashMint.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Capped.sol";

contract Caffeine is ERC20, ERC20Burnable, ERC20Snapshot, Ownable, Pausable, ERC20Permit, ERC20Votes, ERC20FlashMint, ERC20Capped {
    constructor() ERC20("Caffeine", "CAFFN") ERC20Permit("Caffeine") ERC20Capped(2748986301){}

    function snapshot() public onlyOwner {
        _snapshot();
    }

    function pause() public onlyOwner {
        _pause();
    }

    function unpause() public onlyOwner {
        _unpause();
    }

    function _mint(address account, uint256 amount) internal override(ERC20, ERC20Capped, ERC20Votes) {
        require(totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded");
        super._mint(account, amount);
    }

    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }

    function _beforeTokenTransfer(address from, address to, uint256 amount)
        internal
        whenNotPaused
        override(ERC20, ERC20Snapshot)
    {
        super._beforeTokenTransfer(from, to, amount);
    }

    // The following functions are overrides required by Solidity.

    function _afterTokenTransfer(address from, address to, uint256 amount)
        internal
        override(ERC20, ERC20Votes)
    {
        super._afterTokenTransfer(from, to, amount);
    }

    function _burn(address account, uint256 amount)
        internal
        override(ERC20, ERC20Votes)
    {
        super._burn(account, amount);
    }
}

You're minting 1000000 wei, not 1000000 tokens.

For tokens, you need to scale it up by 10 ** 18.

For example, if you want to mint 1.5 tokens, then you need to pass "15 followed by 17 zeros".

Note that your cap configuration of 2748986301 is also in wei units, making it a very small amount of tokens.

Thanks! What is the best practice? Is there a way to specify ETH instead of WEI on the contract level? Any suggestions would be greatly appreciated.

Other than the decimals() function, the contract doesn't know anything about the "decimality" of a token.

And the best practice is to keep it that way, in order to avoid going into non-integer calculations in the contract, which are much harder to maintain (more expensive AND less accurate).

In other words, the token "decimality" should be used only for UI purpose, primarily in order to display human-readable amounts to users.

The general approach is:

  • On the UI, all amounts are given in token-resolution (e.g., 1.234 USDC tokens)
  • In the contract, all amounts are given in wei-resolution (e.g., 1234000 USDC weis)

Practically, it means that you should convert every amount:

  • From token units to wei units, before passing it as input from the UI to the contract
  • From wei units to token units, before passing it as output from the contract to the UI

And the term 'wei' here corresponds to 10 ** d, where d is:

  • The value returned from function decimals in the case of an ERC20 token
  • 18 in the case of ETH

Thanks, this is very helpful. I really appreciate it.

1 Like

Does this contract correctly pass the wei units to token units, from the contract to the UI?

As I explained in the original answer, the contract should not be dealing with token units whatsoever:

  • You convert every amount - before sending it to the contract - from token units to wei units
  • You convert every amount - after receiving it from the contract - from wei units to token units