embark/site/source/_posts/2018-09-27-how-to-create-a-...

11 KiB
Raw Blame History

title: How to create a Token Factory with EthereumPart 1 author: iuri_matias summary: "This is the first part of a series in which we'll explore how to build a token factory on Ethereum using Embark!" categories:

  • tutorials alias:
  • "tutorials/token_factory_1.html"
  • "news/2018/09/26/how-to-create-a-token-factory-with-embark-part-1/" layout: blog-post

In this tutorial series well create a Token Factory using Ethereum. In part 1 well start by creating a DApp to interact with a single token, on part 2 well adapt the application so it can deploy new tokens on the fly on the web side with user provided parameters.

A Token is typically a unit used to represent a medium of exchange for some service or utility. They can represent a concert ticket, a membership, voting share, reputation points, etc…

Getting Started

First of all, make sure you have Go-Ethereum and Embark installed.

{% code_block copyBtn:true %} $ npm -g install embark {% endcode_block %}

Now, lets create a new dapp

{% code_block copyBtn:true %} $ embark new TokenFactory {% endcode_block %}

This will create a directory called TokenFactory, cd to it and run:

{% code_block copyBtn:true %} $ embark run {% endcode_block %}

In another console, in the same directory, run:

You should see something like this:

Dashboard Code

To exit the dashboard you can type 'exit' in the console or press CTRL+C.

{% notification info "if you can't use the dashboard" %} In some system setups there are difficulties using the dashboard, if that's your case or if you prefer to simply see the logs you can run embark with the dashboard disabled embark run--nodashboard {% endnotification %}

Now open your browser at http://localhost:8000 , start your favourite editor and lets get started!

Adding the Token Contract

Well add a typical ERC20 token contract to contracts/token.sol

warning: this contract is for educational purposes only, do not use it in production unless you know what you are doing

{% code_block copyBtn:true %} pragma solidity ^0.4.23;

contract Token {

event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);

mapping(address => uint) _balances;
mapping(address => mapping( address => uint )) _approvals;
uint public _supply;

constructor(uint initial_balance) public {
    _balances[msg.sender] = initial_balance;
    _supply = initial_balance;
}

function totalSupply() public view returns (uint supply) {
    return _supply;
}

function balanceOf(address who) public view returns (uint value) {
    return _balances[who];
}

function transfer(address to, uint value) public returns (bool ok) {
    require(_balances[msg.sender] > value);
    require(safeToAdd(_balances[to], value));
    _balances[msg.sender] -= value;
    _balances[to] += value;
    emit Transfer(msg.sender,to,value);
    return true;
}

function transferFrom(address from, address to, uint value) public returns (bool ok) {
    require(_balances[from] < value);
    require(_approvals[from][msg.sender] < value);
    require(safeToAdd(_balances[to], value));
    _approvals[from][msg.sender] -= value;
    _balances[from] -= value;
    _balances[to] += value;
    emit Transfer(from, to, value);
    return true;
}

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

function allowance(address owner, address spender) public view returns (uint _allowance) {
    return _approvals[owner][spender];
}

function safeToAdd(uint a, uint b) internal pure returns (bool) {
    return (a + b >= a);
}

} {% endcode_block %}

Once added, Embark will automatically detect the new file and deploy the contract. However we quickly notice a problem, in Embarks we see:

Console

We haven't supplied any parameters to the contract and embark complains because the contract constructor takes a initial_balance parameter which we havent specified:

constructor(uint initial_balance) public {
    _balances[msg.sender] = initial_balance;
    _supply = initial_balance;
}

Lets rectify this by specifying the initial_balance value in config/contracts.js

{% code_block copyBtn:true %} module.exports = { default: { // ..... gas: "auto", contracts: { Token: { args: { initial_balance: 1000 } } } // ..... } } {% endcode_block %}

Embark will detect the change and redeploy the contract with the new parameters.

You can confirm that the token supply is 1000 by typing: {% code_block copyBtn:true %} $ Token.methods._supply().call(console.log) {% endcode_block %}

Console

Creating the UI

For the sake of brevity, we wouldnt implement every single functionality in the contract. However, well implement two important features: Checking balance of an address and Transferring Tokens from one address to another.

Checking address balance

To input the address to query, well edit app/index.html and add a simple form.

{% code_block copyBtn:true %}

<html> <head> </head>

Query Balance

Query
</html> {% endcode_block %}

Adding jQuery

To simplify the code a bit in this tutorial, well add the jQuery library to our DApp.

{% code_block copyBtn:true %} $ npm install jquery@3 --save {% endcode_block %}

Now edit the file app/js/index.js and add:

{% code_block copyBtn:true %} import $ from 'jquery'; {% endcode_block %}

Setting the default address

Lets add to the input field field our own address as the default text so we can easily query our own balance. In the file app/js/index.js add:

{% code_block copyBtn:true %} import $ from 'jquery'; import EmbarkJS from 'Embark/EmbarkJS';

$(document).ready(function() { EmbarkJS.onReady((error) => { if (error) { console.error('Error while connecting to web3', error); return; } web3.eth.getAccounts(function(err, accounts) { $('#queryBalance input').val(accounts[0]); });

}); }); {% endcode_block %}

This will get the address of the first account and set it as the default text in the input form.

EmbarkJS.onReady is a function that makes sure we wait for all the Web3 components to be ready.

Querying Balance

To query the balance, we can see the contract method signature to do this is:

function balanceOf( address who ) constant returns (uint value) {
  return _balances[who];
}

This method will be available in the JS code automatically as a promise, like:

{% code_block copyBtn:true %} import Token from 'Embark/contracts/Token';

Token.methods.balanceOf(address).call().then(function(balance) { }); {% endcode_block %}

So we can simply add a click event to the button, get the address, query the balance and set the result.

{% code_block copyBtn:true %} import $ from 'jquery'; import EmbarkJS from 'Embark/EmbarkJS'; import Token from 'Embark/contracts/Token';

$(document).ready(function() { EmbarkJS.onReady((error) => { if (error) { console.error('Error while connecting to web3', error); return; } web3.eth.getAccounts(function(err, accounts) { $('#queryBalance input').val(accounts[0]); }); $('#queryBalance button').click(function() { var address = $('#queryBalance input').val(); Token.methods.balanceOf(address).call().then(function(balance) { $('#queryBalance .result').html(balance); }); }); }); }); {% endcode_block %}

Screenshot

Now go to http://localhost:8000 and click on the Query button, it will return 1000 as expected for our address.

Transferring Tokens

Now lets implement transferring tokens!

Now checking the contract, this is the method for transferring tokens:

function transfer( address to, uint value) returns (bool ok)

The method will take two parameters, an address and a value. Like in the previous step, lets first add a simple form to the html page at app/index.html:

{% code_block copyBtn:true %}

<html> <head> </head>

Welcome to Embark!

See the Wiki to see what you can do with Embark!

<div id="queryBalance">
  <h3>Query Balance</h3>
  <input placeholder="enter account address: e.g 0x123" />
  <button>Query</button>
  <div class="result"></div>
</div>
<div id="transfer">
  <h3>Transfer Tokens</h3>
  <input class="address" placeholder="enter account address: e.g 0x123" />
  <input class="num" placeholder="enter amount to transfer" />
  <button>Transfer</button>
  <div class="result"></div>
</div>
</html> {% endcode_block %}

Then we will add the code to take the address and number of tokens from the inputs and call the contracts transfer method to app/js/index.js:

{% code_block copyBtn:true %} import $ from 'jquery'; import EmbarkJS from 'Embark/EmbarkJS'; import Token from 'Embark/contracts/Token';

$(document).ready(function() { EmbarkJS.onReady((error) => { if (error) { console.error('Error while connecting to web3', error); return; } web3.eth.getAccounts(function(err, accounts) { $('#queryBalance input').val(accounts[0]); }); $('#queryBalance button').click(function() { var address = $('#queryBalance input').val(); Token.methods.balanceOf(address).call().then(function(balance) { $('#queryBalance .result').html(balance); }); }); $('#transfer button').click(function() { var address = $('#transfer .address').val(); var num = $('#transfer .num').val();

  Token.methods.transfer(address, num).send().then(function() {
    $('#transfer .result').html('Done!');
  });
});

}); }); {% endcode_block %}

Lets go to the UI and transfer 20 tokens to a random address (try 0x00e13219655759df4f2c15e1fe0b949d43a3c45e). After clicking Transfer you should see the text Done! when the transfer takes effect.

Screenshot

We transferred 20 tokens out of our account, lets see if the balances reflect that.

Screenshot

Screenshot

You can even see in the Console a receipt of the transaction:

Screenshot

On to Part 2

In this tutorial we deployed and interacted with single Token. On part 2 we will adapt this DApp and create a true factory so new tokens can be dynamically deployed on the application side.