Introduction
In previous articles in this series, we have learned how a basic smart contract is made, and discovered that Ethereum Studio is a simple and easy way to develop and deploy on a testnet before going into the Ethereum network.
Next we learned to build a simple web page in order to handle user interactions via web browser.
In this article we’ll learn how to use Web3.js, a JavaScript library, in our project to interact with the Ethereum Blockchain, enabling the Ethereum smart contract to be operational at the same time as interaction is taking place on the browser.
This is part five of a series on how to create an ERC-20 token. If you want to begin from scratch, please read part one, part two, part three and part four of this series first.
Building the bridge with Web3.js
Renaming the JS file using the same name as the HTML file and (optionally) the CSS file is recommended. We then have an ‘app’ folder containing all the files that handle user interaction, and a ‘contracts’ folder that gathers all the token logic files (the Coin.sol file we saw in part three).
If we follow the Model-View-Controller architecture, we would notice that the Model is in the ‘contracts’ folder, whilst the View (the HTML and CSS files) and the Controller (the JS file) are in the ‘app’ folder.
To bind the token logic with the functions that will be executed when an action is made on the interface, the first step is to declare the address on which the smart contract will be located, along with the ABI and the endpoint.
All this information can be created by starting a local Ethereum node (with geth, for example) or a public service (infura.io is one of the most widely known and used, to date).
Contracts['HelloWorld'] = {
abi: [],
address: "0x..",
endpoint: "http://...."
}
Code language: JavaScript (javascript)
The next step involves the need to create a Web3 instance of the smart contract, passing it as a property, allowing Web3.js to interact with it.
function Coin(Contract) {
this.web3 = null;
this.instance = null;
this.Contract = Contract;
}
Code language: JavaScript (javascript)
Next, initialize the Coin
object and create an instance of the web3js library. The initialization function first defines the interface for the contract using the web3js contract object (a Web3 object), then defines the address of the instance of the contract for the Coin
object.
The web3 variable creates a new Web3 instance using either the Metamask provider or an independent Web 3 provider created as the endpoint configured for the contract.
The contract_interface variable creates the contract interface using the ABI provided in the configuration and the instance variable creates the contract instance for the specific Ethereum address provided in the configuration. Refer to the code below.
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);
};
Code language: JavaScript (javascript)
We begin writing the showAddressBalance function triggered by the “Check Balance” button to display the account balance: we need to store the input value of the address in an address variable, validate it using the isValidAddress function, check the amount relative to it and display it in the returned message.
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());
}
})
}
Code language: JavaScript (javascript)
With the getBalance function, we get the balance of tokens found at the address of the contract:
Coin.prototype.getBalance = function(address, cb) {
this.instance.balances(address, function(error, result) {
cb(error, result);
})
}
Code language: JavaScript (javascript)
Next, we create the action code to send tokens to another address when the “send” button is clicked (the createTokens function) by getting input values for address and amount, validating those addresses and the relative amount using the isValidAddress and isValidAmount function.
Then, we transfer the amount to other address and use the public mint function from the smart contract: if there’s an error, we log it, otherwise we wait for the confirmation of transaction from the waitForReceipt function and clear form values while waiting:
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");
}
});
}
}
)
}
Code language: JavaScript (javascript)
The waitForReceipt function waits for receipt of transaction, checking for the transaction receipt using the web3 library method, and trying again every 2 seconds (2000 ms):
Coin.prototype.waitForReceipt = function(hash, cb) {
var that = this;
this.web3.eth.getTransactionReceipt(hash, function(err, receipt) {
if (err) {
error(err);
}
if (receipt !== null) {
Transaction went through
if (cb) {
cb(receipt);
}
} else {
window.setTimeout(function() {
that.waitForReceipt(hash, cb);
}, 2000);
}
});
}
Code language: JavaScript (javascript)
With the isValidAddress function, called in the functions above, we check if the address meets the basic requirements (i.e. it follows the Ethereum standard), by doing a basic validation of amount, which must be bigger than 0 and a number type. Use the code below:
function isValidAddress(address) {
return /^(0x)?[0-9a-f]{40}$/i.test(address);
}
function isValidAmount(amount) {
return amount > 0 && typeof Number(amount) == 'number';
}
Code language: JavaScript (javascript)
Then, we bind functions to the buttons defined in app.html:
Coin.prototype.bindButtons = function() {
var that = this;
$(document).on("click", "#button-create", function() {
that.createTokens();
});
$(document).on("click", "#button-check", function() {
that.showAddressBalance();
});
}
Code language: JavaScript (javascript)
This last bit of code binds all the buttons defined in the functions above and initialized the contract.
Coin.prototype.onReady = function() {
this.bindButtons();
this.init();
};
if(typeof(Contracts) === "undefined") var Contracts={ Coin: { abi: [] }};
var coin = new Coin(Contracts['Coin']);
$(document).ready(function() {
coin.onReady();
});
Code language: JavaScript (javascript)
Web3.js is one of the frameworks most frequently used to design decentralised applications. Of course, this is just a little application that does very few things. A list of the functions, along with a complete guide of web3js framework, is available in the official documentation website.
Interested in Web3? Read: What is Web3 and What New Capabilities Does it Bring?