mirror of https://github.com/embarklabs/embark.git
370 lines
11 KiB
Markdown
370 lines
11 KiB
Markdown
title: How to create a Token Factory with Ethereum — Part 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 we’ll create a Token Factory using Ethereum. In part 1 we’ll start by creating a DApp to interact with a single token, on part 2 we’ll 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](https://geth.ethereum.org/) and Embark installed.
|
||
|
||
{% code_block copyBtn:true %}
|
||
$ npm -g install embark
|
||
{% endcode_block %}
|
||
|
||
Now, let’s 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](/assets/images/token_factory_1/dashboard.png)
|
||
|
||
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 let’s get started!
|
||
|
||
## Adding the Token Contract
|
||
|
||
We’ll 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 Embark’s we see:
|
||
|
||
![Console](/assets/images/token_factory_1/console_1.png)
|
||
|
||
We haven't supplied any parameters to the contract and embark complains because the contract constructor takes a *initial_balance* parameter which we haven’t specified:
|
||
|
||
```
|
||
constructor(uint initial_balance) public {
|
||
_balances[msg.sender] = initial_balance;
|
||
_supply = initial_balance;
|
||
}
|
||
```
|
||
|
||
Let’s rectify this by specifying the *initial_balance* value in `config/contracts.js`
|
||
|
||
{% code_block copyBtn:true %}
|
||
module.exports = {
|
||
default: {
|
||
// .....
|
||
gas: "auto",
|
||
contracts: {
|
||
<mark id="code-3" class="highlight-inline">
|
||
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](/assets/images/token_factory_1/console_2.png)
|
||
|
||
## Creating the UI
|
||
|
||
For the sake of brevity, we wouldn’t implement every single functionality in the contract. However, we’ll 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, we’ll edit *app/index.html* and add a simple form.
|
||
|
||
{% code_block copyBtn:true %}
|
||
<html>
|
||
<head>
|
||
<title>Embark</title>
|
||
<link rel="stylesheet" href="css/app.css">
|
||
<script src="js/app.js"></script>
|
||
</head>
|
||
<body>
|
||
<div id="queryBalance">
|
||
<h3>Query Balance</h3>
|
||
<input placeholder="enter account address: e.g 0x123" />
|
||
<button>Query</button>
|
||
<div class="result"></div>
|
||
</div>
|
||
</body>
|
||
</html>
|
||
{% endcode_block %}
|
||
|
||
**Adding jQuery**
|
||
|
||
To simplify the code a bit in this tutorial, we’ll 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**
|
||
|
||
Let’s 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](/assets/images/token_factory_1/page_1.png)
|
||
|
||
Now go to http://localhost:8000 and click on the Query button, it will return 1000 as expected for our address.
|
||
|
||
## Transferring Tokens
|
||
|
||
Now let’s 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, let’s first add a simple form to the html page at *app/index.html*:
|
||
|
||
{% code_block copyBtn:true %}
|
||
<html>
|
||
<head>
|
||
<title>Embark</title>
|
||
<link rel="stylesheet" href="css/app.css">
|
||
<script src="js/app.js"></script>
|
||
</head>
|
||
<body>
|
||
<h3>Welcome to Embark!</h3>
|
||
<p>See the <a href="https://github.com/iurimatias/embark-framework/wiki">Wiki</a> to see what you can do with Embark!</p>
|
||
|
||
<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>
|
||
</body>
|
||
</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 %}
|
||
|
||
Let’s 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](/assets/images/token_factory_1/page_2.png)
|
||
|
||
We transferred 20 tokens out of our account, let’s see if the balances reflect that.
|
||
|
||
![Screenshot](/assets/images/token_factory_1/page_3.png)
|
||
|
||
![Screenshot](/assets/images/token_factory_1/page_4.png)
|
||
|
||
You can even see in the Console a receipt of the transaction:
|
||
|
||
![Screenshot](/assets/images/token_factory_1/page_5.png)
|
||
|
||
|
||
## On to Part 2
|
||
|
||
In this tutorial we deployed and interacted with single Token. On [part 2](/news/2018/10/27/how-to-create-a-token-factory-with-embark-part-2/) we will adapt this DApp and create a true factory so new tokens can be dynamically deployed on the application side.
|
||
|