I'm encountering an error while trying to transfer tokens from one address to another in my Solidity contract. When I attempt to print the balance of msg.sender, it shows a value of 1000000000000000000000000, but when I try to execute the transfer function, it throws an error message stating: "VM Exception while processing transaction: revert ERC20: transfer amount exceeds balance."
Code Snippets:
Here's the relevant code from my contracts:
Token.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract Token is ERC20, Ownable {
constructor() ERC20("Token", "ANT") {
_mint(msg.sender, 1000000 * 10 ** decimals());
}
}
Registry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./Token.sol";
contract Registry {
Token public tokenContract;
address public addr;
mapping(address => address) public publicKeyToAccount;
constructor(Token _tokenContract) {
tokenContract = _tokenContract;
addr = address(this);
}
function printBalance() external view returns (uint256) {
return IERC20(tokenContract).balanceOf(msg.sender);
}
function transfer() external {
IERC20(tokenContract).transfer(addr, 10);
}
}
1_migrate.js
const Registry = artifacts.require("Registry");
const Token = artifacts.require("Token");
module.exports = async function (deployer) {
await deployer.deploy(Token);
const tokenContract = await Token.deployed();
module.exports.tokenContract = tokenContract.address;
await deployer.deploy(Registry, tokenContract.address);
};
Expected Behavior:
I expect the transfer function to execute successfully and transfer 10 tokens from msg.sender to the specified address (addr).
Actual Behavior:
The transfer function throws the error message "ERC20: transfer amount exceeds balance" even though the balance of msg.sender appears to be sufficient.
Steps Taken:
Deployed the Token contract and obtained its address.
Deployed the Registry contract, passing the Token contract address to its constructor.
Executed the printBalance function, which returned the expected balance of 1000000000000000000000000.
Attempted to execute the transfer function, which resulted in the error mentioned above.
Question:
What could be causing the "ERC20: transfer amount exceeds balance" error, even though the balance of msg.sender seems to be correct? Is there any issue with my contract code or the way I'm interacting with it? Any guidance or suggestions to resolve this issue would be greatly appreciated.
Best Answer
There is a problem with the way you are interacting with the contracts.
When the
Token
contract is deployed, it's constructor executes_mint
which mints 1M tokens for themsg.sender
which is the deployer address (EOA).Registry
contract would have a different address, which is not the deployer address.When interacting with Registry contract using your deployer address, the call trace is
deployer -> Registry
, that's why in theRegistry
contract, themsg.sender
is deployer address (since it is sending the call) and hence you are able to get the token balance for deployer.However, when you want to transfer tokens, the call trace is
deployer -> Registry -> Token
. Hence whenToken
contract usesmsg.sender
it uses theRegistry
contract address.To solve this: I suggest you not use
msg.sender
while trying this out initially and add a function argument for taking an address in theToken.constructor
,Registry.printBalance
to prevent confusion (you can later start usingmsg.sender
). If you want to useToken.transfer
, you need to mint the tokens on theRegistry
contract's address. If you want to you deployer address then you can useToken.transferFrom
, however for that you need toToken.approve
the registry from your deployer address.