Implement ERC20 token with burn and mint functions?

Hi guys!

I am trying to implement a brun and mint function. I am using solidity 0.5.0. Could anyone tell me how I can write those functions?

I keep getting error :confused:

1 Like

Hi @chloecarvalho220,

Welcome to the community :wave:

Can you share the error that you are getting and the code for your smart contract?

If you don’t need to use Solidity 0.6 you could look at the following tutorial: Create an ERC20 using Remix, without writing Solidity

OpenZeppelin Contracts 2.x uses Solidity 0.5.
OpenZeppelin Contracts 3.x uses either Solidity 0.6 or Solidity 0.7. (https://blog.openzeppelin.com/openzeppelin-contracts-3-4/)
OpenZeppelin Contracts 4.0 will use Solidity 0.8. (Contracts 4.0 Timeline)

To create a Mintable and Burnable ERC20 with OpenZeppelin Contracts 2.x you will need to extend from ERC20Mintable: https://docs.openzeppelin.com/contracts/2.x/api/token/erc20#ERC20Mintable and ERC20Burnable: https://docs.openzeppelin.com/contracts/2.x/api/token/erc20#ERC20Burnable

You also asked about EIP1363. There currently isn’t an implementation of this in OpenZeppelin Contracts. You may want to consider using ERC777: https://docs.openzeppelin.com/contracts/3.x/erc777

Hi!

Thank you for your reply. Let me attach my code below.

The error I keep getting says “This contract may be abstract, not implement an abstract parent’s methods completely or not invoke an inherited contract’s constructor correctly.”

My code:

pragma solidity ^0.5.0;

// ----------------------------------------------------------------------------
// ERC Token Standard #20 Interface
//
// ----------------------------------------------------------------------------
contract ERC20Interface {
    string public name;
    mapping(address => uint256) public balances;
    function totalSupply() public view returns (uint);
    function balanceOf(address tokenOwner) public view returns (uint balance);
    function allowance(address tokenOwner, address spender) public view returns (uint remaining);
    function transfer(address to, uint tokens) public returns (bool success);
    function approve(address spender, uint tokens) public returns (bool success);
    function transferFrom(address from, address to, uint tokens) public returns (bool success);
    function mint() public {
        balances[msg.sender] ++;
    }

    event Transfer(address indexed from, address indexed to, uint tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint 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;
    }
    
     function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        } 
        uint256 c = a * b;
        assert(c / a == b);
        return c;
    }
    
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        // assert(b > 0); // Solidity automatically throws when dividing by 0 
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        return c;
    }
    
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b <= a);
        return a - b;
    }
    
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        assert(c >= a);
        return c;
    }
}

contract OwnableHKDT {
    address public owner;
    function Ownable() public {
        owner = msg.sender;
    }
    
    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }
    
    function transferOwnership(address newOwner) public onlyOwner {
        if (newOwner != address(0)) {
            owner = newOwner;
        }
    }
    
}


contract HKDTESTING is ERC20Interface, 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 => uint) balances;
    mapping(address => mapping(address => uint)) allowed;
    
    /**
     * Constrctor function
     *
     * Initializes contract with initial supply tokens to the creator of the contract
     */
    constructor(address payable _wallet, address _token) public {
        wallet = _wallet;
        token = _token;
        name = "HKDTESTING";
        symbol = "HKDT";
        decimals = 18;
        _totalSupply = 100000000000000000000000000;

        balances[msg.sender] = _totalSupply;
        emit Transfer(address(0), msg.sender, _totalSupply);
    }
    
    address payable wallet;
    address public token;
    
    function() external payable {
        buyToken();
    }
    
    function buyToken() public payable {
        ERC20Interface _token = ERC20Interface(address(token));
        _token.mint();
        wallet.transfer(msg.value);
    }

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

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

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

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

    function transfer(address to, uint 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, uint 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;
    }
}

Thank you again :slight_smile:

1 Like

My contract was also deployable before I added the mint function.

1 Like

Hi @chloecarvalho220,

I suggest you extend from OpenZeppelin Contracts to create an ERC20 token rather than creating your own, as there look to be multiple issues in the contract you have.

I suggest only having functionality in a token if it is required for the life of the token. So I wouldn’t include crowdsale functionality in a token as a crowdsale has a limited life.

You could look at Simple ERC20 Crowdsale.


As an aside, see how to Format code in the forum. I formatted your code using this.

Hi,

Thank you so much for you recommendation, I have learnt a lot.

However, would you be able to advise me on how to restrict the mint function to the owner/creator only?

Thanks again,
Chloe

1 Like

Hi @chloecarvalho220,

Have a look at the example in the documentation using AccessControl to limit minting to an account with a minter role.:

https://docs.openzeppelin.com/contracts/3.x/access-control#using-access-control

Hi,

Thanks again, I think now I managed to include mint and burn function in my contract. However I keep getting the following error: “This contract may be abstract, not implement an abstract parent’s methods completely or not invoke an inherited contract’s constructor correctly.”

This is the part of my code that I cant deploy:

contract ERC20Interface {
    string public name;
    mapping(address => uint256) public balances;
    function totalSupply() public view returns (uint);
    function balanceOf(address tokenOwner) public view returns (uint balance);
    function allowance(address tokenOwner, address spender) public view returns (uint remaining);
    function transfer(address to, uint tokens) public returns (bool success);
    function approve(address spender, uint tokens) public returns (bool success);
    function transferFrom(address from, address to, uint tokens) public returns (bool success);
    function mint() public {
        balances[msg.sender] ++;
    }

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

contract HKDTESTING is ERC20Interface, SafeMath {

    string public name;
    string public symbol;
    uint8 public decimals; // 18 decimals is the strongly suggested default, avoid changing it

    uint256 public _totalSupply;
    
    address public minter;
    mapping(address => uint) balances;
    mapping(address => mapping(address => uint)) allowed;
    
    /**
     * Constrctor function
     *
     * Initializes contract with initial supply tokens to the creator of the contract
     */
    constructor(address payable _wallet, address _token) public {
        minter = msg.sender;
        wallet = _wallet;
        token = _token;
        name = "HKDTESTING";
        symbol = "HKDT";
        decimals = 18;
        _totalSupply = 100000000000000000000000000;

        balances[msg.sender] = _totalSupply;
        emit Transfer(address(0), msg.sender, _totalSupply);
    }
    
    function mint(address receiver, uint amount) public {
            require(msg.sender == minter);
            require(amount < 1e60);
            balances[receiver] += amount;
        }
    
    function burn(uint _value) public returns (bool sucess) {
        require(balances[msg.sender] >= _value);
        
        balances[msg.sender] -= _value;
        _totalSupply -= _value;
        return true;
    } 
    
    address payable wallet;
    address public token;
    
    function() external payable {
        buyToken();
    }
    
    function buyToken() public payable {
        ERC20Interface _token = ERC20Interface(address(token));
        _token.mint();
        wallet.transfer(msg.value);
    }

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

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

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

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

    function transfer(address to, uint 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, uint 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;
    }
}

Could you help me understand where the error is? not sure what I am doing wrong for the error message to appear when I try to deploy.

Thank you again very much,
Chloe

1 Like

Hi Chloe (@chloecarvalho220),

Your contract appears to have multiple issues. Instead of trying to fix it I would suggest extending from an OpenZeppelin Contracts ERC20 implementation rather than creating your own.

I suggest trying the following tutorial which uses a preset ERC20:

Hi,

Would you be able to point out the problems for me? As I am really curious and eager for the knowledge.

Again, thank you
Chloe

Hi Chloe (@chloecarvalho220),

:warning: I am a community manager and not a security researcher, this is a quick (5 minute) feedback and there may be many more issues. Any code with value should have appropriate testing and auditing.

You have declared a mint function in ERC20Interface. mint isn’t part of ERC20 (https://eips.ethereum.org/EIPS/eip-20), so shouldn’t really go here.

Have a look at OpenZeppelin Contracts IERC20:
https://docs.openzeppelin.com/contracts/3.x/api/token/erc20#IERC20

mint changes the callers balance, but doesn’t change the total supply. So the total supply won’t match the sum of the balances.

totalSupply is shown as the total supply minus what has been burnt, when burning could just adjust total supply instead.

mint increments the callers balance by one token bit only, so too small an amount when using decimals of 18.

mint doesn’t have any access control, so any account can call mint.

mint doesn’t emit a Transfer event, so block explorers and other services could have trouble tracking balances.

When using SafeMath, you could make your code more readable doing as per the documentation: https://docs.openzeppelin.com/contracts/3.x/utilities#math

As I mentioned previously, I wouldn’t include buy functionality in a token as buy is unlikely to be required for the life of the token.