So basically, even if I don't include sanity checks in interface IERC20, if I try to transfer more tokens than I actually have, I get the error "transfer amount exceeds balance", do you guys know where this error is actually coming from if I didn't implement sanity checks?

pragma solidity 0.4.16;

interface IERC20 {
    function transfer(address _to, uint256 _value) public returns (bool);

contract TokenPool {
    function batchTransfer(address _tokenAddress, address[] _receivers, uint256 _value) public returns (bool) {
        IERC20 token = IERC20(_tokenAddress);
        uint cnt = _receivers.length;
         amount = uint256(cnt) * _value;
        require(cnt > 0 && cnt <= 20);
        require(_value > 0);
        for (uint i = 0; i < cnt; i++) {
            IERC20(_tokenAddress).transfer(_receivers[i], _value);
        return true;

  • You might be using openzepelin Jul 26, 2023 at 4:56
  • paste the codes as well Jul 26, 2023 at 4:56
  • Nah bro, I just put "interface IERC20" and the transfer function, nothing else, so I don't know where the error is coming from.
    – 19216811
    Jul 26, 2023 at 5:31

2 Answers 2


The token address you used is using the ERC20 library of openZeppelin, so it inherits that hence all the internal functions of the library are added. So when you try to transfer more tokens than your balance it goes in the internal _transfer function where this check is implemented.

function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");
        _beforeTokenTransfer(from, to, amount);
        uint256 fromBalance = _balances[from];
        **require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");**
        unchecked {
            _balances[from] = fromBalance - amount;
        _balances[to] += amount;
        emit Transfer(from, to, amount);
        _afterTokenTransfer(from, to, amount);

Hope this helps :)

  • How can I make it so that it uses my own custom version of ERC20?
    – 19216811
    Jul 26, 2023 at 9:08

You are using the interface as a reference. Interfaces don't really hold the logic of the contracts. While you are calling the function on a contract, as I see in your code, that contracts function gets called, and that contract has implemented the transfer checks already.

In simple terms the deployed contract logic matters, not the interface you are using. The interface just contains the function signature and nothing els.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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