1

I am making a test token on remix with Ganache open on my computer. I'll paste the code below and then to the questions and error. I got here by combining some samples online and fixing a few errors as they came up.

pragma solidity >=0.7.0 <0.9.0;

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address tokenOwner) external view returns (uint256 balance);
    function allowance(address tokenOwner, address spender) external view returns (uint256 remaining);
    function transfer(address to, uint tokens) external returns (bool success);
    function approve(address spender, uint tokens) external returns (bool success);
    function transferFrom(address from, address to, uint256 tokens) external returns (bool success);

    event Transfer(address indexed from, address indexed to, uint256 tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint256 tokens);
}

// ----------------------------------------------------------------------------
// Safe Math Library
// ----------------------------------------------------------------------------
contract SafeMath {
    function safeAdd(uint a, uint b) public pure returns (uint c) {
        c = a + b;
        require(c >= a);
    }
    function safeSub(uint a, uint b) public pure returns (uint c) {
        require(b <= a); c = a - b; 
        
    } 
    function safeMul(uint a, uint b) public pure returns (uint c) { 
        c = a * b; require(a == 0 || c / a == b); 
        
    } 
    function safeDiv(uint a, uint b) public pure returns (uint c) { 
        require(b > 0);
        c = a / b;
    }
}


contract TTKN is IERC20, SafeMath {
    string public name;
    string public symbol;
    uint8 public decimals; // 18 decimals is the strongly suggested default, avoid changing it

    uint256 public _totalSupply;

    mapping(address => uint256) balances;
    mapping(address => mapping(address => uint256)) allowed;

    /**
     * Constrctor function
     *
     * Initializes contract with initial supply tokens to the creator of the contract
     */
    constructor() {
        name = "Test Token";
        symbol = "TTKN";
        decimals = 18;
        _totalSupply = 100000000000000000000000000;

        balances[msg.sender] = _totalSupply;
        emit Transfer(address(0), msg.sender, _totalSupply);
    }

    function totalSupply() public view returns (uint256) {
        return _totalSupply  - balances[address(0)];
    }

    function balanceOf(address tokenOwner) public view returns (uint256 balance) {
        return balances[tokenOwner];
    }

    function allowance(address tokenOwner, address spender) public view returns (uint256 remaining) {
        return allowed[tokenOwner][spender];
    }

    function approve(address spender, uint256 tokens) public returns (bool success) {
        allowed[msg.sender][spender] = tokens;
        emit Approval(msg.sender, spender, tokens);
        return true;
    }

    function transfer(address to, uint256 tokens) public returns (bool success) {
        balances[msg.sender] = safeSub(balances[msg.sender], tokens);
        balances[to] = safeAdd(balances[to], tokens);
        emit Transfer(msg.sender, to, tokens);
        return true;
    }

    function transferFrom(address from, address to, uint256 tokens) public returns (bool success) {
        balances[from] = safeSub(balances[from], tokens);
        allowed[from][msg.sender] = safeSub(allowed[from][msg.sender], tokens);
        balances[to] = safeAdd(balances[to], tokens);
        emit Transfer(from, to, tokens);
        return true;
    }
}

So deploying works, calling totalSupply() works and show that the address which deployed it (owner) has all the supply, cool. balanceOf(), also good. And I can transfer to other accounts on my Ganache test network, via transfer() from the owner.

I transfer from owner to addr1, then I try transferFrom() addr2 to a new addr2, and I get the error below:

enter image description here

Clearly addr1 (the 0xD97...) has enough balance to send the required amount to addr2. In Ganache, all these accounts have close to 100 ETH as well. The error is below when transferFrom() is clicked:

enter image description here

The console aftermath when I click send:

enter image description here


So I have a few questions here,

  • In general, is my token starter code sufficient? Anything I should change just to get the barebones functionality of create tokens, send tokens?

  • The error above: why can't addr2 send to addr3 with transferFrom()? The owner addr cannot call this function either

  • What is all this allowance code? It is from templated code I am using, I don't understand this particular portion though. Is it really necessary? I just want to be able to send coins, I figure Metamask or similar will ask the user to confirm anyways.

  • How do new tokens like this get 'wallets'? E.g. right now I can't view my Token's balance for any of these addresses in Ganache, all I can do is use the remix tools. I suppose I can make a website and use web3 to interact with my contract. If/when this contract is eventually deployed to the mainnet, how can someone with e.g. an Exodus wallet add this token to their wallet?

  • Wallets and addresses: other ERC20 tokens have unique addresses for their tokens, but for now what I can see with Ganache, it's just using the default Ethereum address. Surely for this contract (token) users should have a new address specific to my token?

Thanks so much to the Ethereum community, really great stuff!

1
  • From a glance, your code should be enough.

    You should know about the OpenZeppelin libraries - they provide you with implementations and "plug and play" templates for common functionality, such as ERC20 tokens.

    Nothing wrong with trying to code your own of course, I'm just mentioning in case you don't know it.

  • Did you call approve before calling transferFrom?

    In a regular transfer, the user can send funds to your contract. But what if you want your contract to pull funds from the user?

    This is what the allowance mechanism in for. It has 2 steps: first the user calls .approve(account, amount). This allows "account" to spend "amount" of the users tokens. Then, the account will call .transferFrom(originalUser, toAccount, amount), which will transfer the amount from originalUser to toAccount. So probably your transaction was failing because you didn't call approve before transferFrom. If you just want a regular transfer, use `transfer1.

  • Not sure what you mean about the wallets question. You can see your balance by querying the balanceOf method in the deployed contract, like you already did. To add a token to a wallet, the wallet would need the token's contract address. The wallet would then call balanceOf to see the balance of the user.

  • Not sure what you mean by "default Ethereum address", there's no such thing really. Every deployed contract (including your token) will have his own unique address once deployed.

6
  • 1
    Thanks for your answer! Yes, I tried with approve and that did the trick. I guess now the only function that doesn't make sense is allowance. Regarding last point, oh, is it the case that every ERC20 token will use the same base ETH address? I just took another look at my wallet and realized that my SHIB and ETH addresses are the same.
    – JDS
    Oct 29 '21 at 2:22
  • 1
    allowance will return the amount that was previously .approved. Regarding ERC20 token addresses: here's the thing: you have your wallet address - that's your main Ethereum address. But SHIB token is implemented just like you implemented your token above: so your SHIB balance is not saved on your "ethereum address", but it is saved in a balances mapping in SHIB's contract. And when somebody will query SHIB.balanceOf(yourBaseEthAddress), he will see your balance. So for all ERC20 tokens, you will use your Eth address to deposit to them; but the balance is sitting on the ERC20 contract. Oct 29 '21 at 2:31
  • 1
    Ohhh sweet I think I get it now:) Final question would just be - practically when would someone need that allowance feature? I can't really think of a use case. So maybe I'm not getting that part
    – JDS
    Oct 29 '21 at 2:34
  • 1
    Let's say you have an app which allows the users to buy an NFT: The user sends USDT token, and receives NFT in return. How would your contract knows it was sent USDT? It can't know, because it's just an inner balance that is updated on the USDT contract. So you want the user to execute a "buy" function on your contract, so you would know to send the user the NFT. But if the user executes a "buy" on your contract, how will your contract receieve the money? This is what the approve mechanism is for: The user will first of all "approve" your contract, then call "buy", and then your contract would Oct 29 '21 at 5:33
  • 1
    ...be able to pull the user's funds using transferFrom, and send him the NFT. Oct 29 '21 at 5:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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