chrischinchilla

10 min read - Posted 06 Dec 19

A fullstack dapp for creating tokens

This dapp implements the simplest form of a cryptocurrency that uses the ERC-20 standard to define a token you can create and send to others. This tutorial is intended to be followed using the online IDE available at studio.ethereum.org, and selecting the "Coin" template.

Select Coin template

The smart contract

The contract allows only its creator to create new coins (different issuance scheme are possible). Anyone can send coins to each other without a need for registering with a username and password, and all you need is an Ethereum keypair.

The line address public minter; declares a state variable of an address type. The address type is a 160-bit value that does not allow any arithmetic operations. It is suitable for storing addresses of contracts, or a hash of the public half of a keypair belonging to external accounts.

The keyword public automatically generates a function that allows you to access the current value of the state variable from outside of the contract. Without this keyword, other contracts have no way to access the variable. The code of the function generated by the compiler is equivalent to the following (ignore external and view for now):

function minter() external view returns (address) { return minter; }

You could add a function like the above yourself, but you would have a function and a state variable with the same name. This is not necessary, the compiler figures it out for you.

The next line, mapping (address => uint) public balances; also creates a public state variable, but it is a more complex data type. The mapping type maps addresses to unsigned integers.

You can think of mappings as hash tables which are virtually initialized such that every possible key exists from the start and is mapped to a value whose byte-representation is all zeros. It is not possible to obtain a list of all keys of a mapping, nor a list of all values. Record what you added to the mapping, or use it in a context where this is not needed. Even better, keep a list, or use a more suitable data type.

The getter function created by the public keyword is more complex in the case of a mapping. It looks like the following::

function balances(address _account) external view returns (uint) {
    return balances[_account];
}

You can use this function to query the balance of a single account.

The line event Sent(address from, address to, uint amount); declares an "event", which is emitted in the last line of the function send. Ethereum clients such as web applications can listen for these events emitted on the blockchain without much cost. As soon as the event is emitted, the listener receives the arguments from, to and amount, which makes it possible to track transactions.

The constructor is a special function run during the creation of the contract, and you cannot call it afterwards. In this case, it permanently stores the address of the person creating the contract. The msg variable (together with tx and block) is a special global variable that contains properties which allow access to the blockchain. msg.sender is always the address where the current (external) function call came from.

The functions that make up the contract, and that users and contracts can call are mint and send.

The mint function sends an amount of newly created coins to another address. The require function call defines conditions that reverts all changes if not met. In this example, require(msg.sender == minter); ensures that only the creator of the contract can call mint, and require(amount < 1e60); ensures a maximum amount of tokens, without which could cause overflow errors in the future.

Anyone can use the send (who already has some of these coins) to send coins to anyone else. If the sender does not have enough coins to send, the require call fails and provides the sender with an appropriate error message string.

The Web app

This tutorial doesn't cover the HTML or CSS as it's not web3 specific, aside from the element IDs that the JavaScript manipulates. A lot of the JavaScript code follows standard patterns for object-oriented JavaScript, so this tutorial focuses on the web3js specific parts.

First create an instance of the smart contract, passing it as a property, which allows web3js to interact with it.

function Coin(Contract) {
    this.web3 = null;
    this.instance = null;
    this.Contract = Contract;
}

Initialize the Coin object and create an instance of the web3js library, passing Metamask as a provider for the contract. The initialization function then defines the interface for the contract using the web3js contract object and then defines the address of the instance of the contract for the Coin object.

Coin.prototype.init = function() {

    this.web3 = new Web3(
        (window.web3 && window.web3.currentProvider) ||
        new Web3.providers.HttpProvider(this.Contract.endpoint));

    var contract_interface = this.web3.eth.contract(this.Contract.abi);

    this.instance = contract_interface.at(this.Contract.address);
};

Add other JavaScript boilerplate to create the instance of the Coin object defined above, and bind the functions for interacting with the contract to the buttons defined in the HTML:

Coin.prototype.bindButtons = function() {
    var that = this;

    $(document).on("click", "#button-create", function() {
        that.createTokens();
    });

    $(document).on("click", "#button-check", function() {
        that.showAddressBalance();
    });
}

Coin.prototype.onReady = function() {
    this.bindButtons();
    this.init();
    this.main();
};

var coin = new Coin(Contracts['Coin']);

$(document).ready(function() {
    coin.onReady();
});

Create the function triggered when someone clicks the "send" button which creates a specified number of new coin tokens and sends them to the address specified. Before doing this, it checks the address and amount are valid using two utility functions we create later. Web3js provides access to functions in the smart contract by accessing the instance of the contract. For example, the function below calls the mint function of the contract to create the tokens with this.instance.mint, passing the variables it needs, waiting for confirmation, or returning an error.

Coin.prototype.createTokens = function() {
    var that = this;

    var address = $("#create-address").val();
    var amount = $("#create-amount").val();
    console.log(amount);

    if(!isValidAddress(address)) {
        console.log("Invalid address");
        return;
    }

    if(!isValidAmount(amount)) {
        console.log("Invalid amount");
        return;
    }

    this.instance.mint(address, amount, { from: window.web3.eth.accounts[0], gas: 100000, gasPrice: 100000, gasLimit: 100000 },
        function(error, txHash) {
            if(error) {
                console.log(error);
            }
            else {
                that.waitForReceipt(txHash, function(receipt) {
                    if(receipt.status) {
                        $("#create-address").val("");
                        $("#create-amount").val("");
                    }
                    else {
                        console.log("error");
                    }
                });
            }
        }
    )
}

The waitForReceipt function called above uses the web3js function getTransactionReceipt to wait for a receipt for the transaction, trying again if the first attempt fails:

Coin.prototype.waitForReceipt = function(hash, cb) {
    var that = this;

    this.web3.eth.getTransactionReceipt(hash, function(err, receipt) {
        if (err) {
            error(err);
        }
        if (receipt !== null) {
            if (cb) {
                cb(receipt);
            }
        } else {
            window.setTimeout(function() {
                that.waitForReceipt(hash, cb);
            }, 2000);
        }
    });
}

Create the function called when someone clicks the "Check balance" button. The function takes the address value specified, and checks it is a valid address with the same utility function used earlier. The function then calls the getBalance function which in turn calls the balances function of the instance of the Coin contract, passing the address value to it, and returning the result or an error.

Coin.prototype.showAddressBalance = function(hash, cb) {
    var that = this;

    var address = $("#balance-address").val();

    if(!isValidAddress(address)) {
        console.log("Invalid address");
        return;
    }

    this.getBalance(address, function(error, balance) {
        if(error) {
            console.log(error)
        }
        else {
            console.log(balance.toNumber());
                $("#message").text(balance.toNumber());
        }
    })
}

Coin.prototype.getBalance = function(address, cb) {
    this.instance.balances(address, function(error, result) {
        cb(error, result);
    })
}

These are the two utility functions that check for a valid address and amount:

function isValidAddress(address) {
    return /^(0x)?[0-9a-f]{40}$/i.test(address);
}

function isValidAmount(amount) {
    return amount > 0 && typeof Number(amount) == 'number';
}

And that's all the code. To see the dapp in action, click Compile, then Deploy found under the disclosure triangle of the contract file, then open the Preview tab to see the frontend of the dapp.

Created with Sketch.Content is"CC-BY-SA 4.0" licensed
Article On-chain
Article Author

Chris Ward

Technical Content Lead

64

11

5

0 Comments
Related Articles
ERC-20 Token Standard

Simple Summary A standard interface for tokens. Abstract The following standard allows for the implementation of a standard API for tokens within smart contracts. This standard provides basic functionality to transfer tokens, as well as allow tokens to be approved so they can be spent by another on-chain third party. Motivation A standard interface allows any tokens on Ethereum to be re-used by other applications: from wallets to decentralized exchanges. Specification Token Methods NOTES: - The

Kauri Team

16 Apr 19

A fullstack dapp for creating unique tradable tokens

This dapp implements is a Crypto-collectible game built on top of the ERC-721 standard for creating unique tokens.. This tutorial is intended to be followed using the online IDE available at studio.ethereum.org, and selecting the CryptoPizza template. Select CryptoPizza template The smart contract After the pragma line are a series of import statements that imports all global symbols from another file to make them available to the current contract. Adding the is keyword after the contract name a

Chris Ward

17 Oct 19