Ethereum Tutorials

SMART CONTRACTS

Web UI for "Hello, World!"

Our next objective is to create actual web applications. Ethereum conrtracts "as they are" are a bit boring in terms of the UI. Let's revisit the "Hello, World!" contract:

					
contract HelloWorld_01
{
	function HelloWorld_01()
	{
	}
	
	// ---
	
	function getGreeting() constant returns (string strGreeting) { return "Hello, World!"; }
}

For the contract to "talk" to Web UI, it has to expose events. Then on the HTML side, via web3.js, the browser will have access to them - and to parameters passed with them as well. Events are TRIGGERED in a browser, and it happens ASYNCHRONOUSLY.

For a good reference, see: Ethereum programming: Events

Let's add events to our contract:

					
contract HelloWorld_02
{
	function HelloWorld_02()
	{
	}
	
	// ---
	
	function getGreeting() constant returns (string strGreeting) 
	{ 
		helloWorldCalled(msg.sender);
		return "Hello, World!"; 
	}

	// ---

	event helloWorldCalled(address indexed _from);
}

As you can see, event is triggered by the contract in its getGreeting() function.

The first thing we can now do is using Mist to access those events: in Mist we can check the "Watch event", to be notified when someone calls "getGreeting()".

Alternatively, in the Geth console, we can involve:

var abi = [ { "constant": true, "inputs": [], "name": "getGreeting", "outputs": 
	[ { "name": "strGreeting", "type": "string", "value": "Hello, World!" } ], 
	"payable": false, "type": "function" }, { "inputs": [], "payable": false, 
	"type": "constructor" }, { "anonymous": false, "inputs": 
	[ { "indexed": true, "name": "_from", "type": "address" } ], 
	"name": "helloWorldCalled", "type": "event" } ];

var HelloWorld = eth.contract(abi)
var contract = HelloWorld.at("0x42AB54558282835D71B803966833671327006edB")

var helloEvent = contract.helloWorldCalled({_from: web3.eth.coinbase});

helloEvent.watch(function(err, result) 
{
	if (err) 
	{
		console.log(err);
		return;
  	}

	console.log(/*result.args._value*/"Called!");
});

(Note that we type JavaScript commands in the Geth console, as a mater of fact, it is using JavaScript web3.js library that does the job)

To involve events, we send transaction instead of using call(), it means that events are stored in the block chain:

personal.unlockAccount(eth.accounts[0], "password", 3000)
contract.getGreeting.sendTransaction(2, {from: web3.eth.coinbase})

Note: the event is fired when mining is on, so simple call() will not trigger it.

Using events from Geth is not very nice if we want a non-programmer to use our contract. As we see in Mist, it is possible to call them from the browser-like app. So let's create an HTML UI for our contract. The code and working example are awailable in "Hello World" Ethereum Contract page.

Let's walk through the code.

<!DOCTYPE html>
<html>
<head>
	<title>Hello, World!</title>

	<script src="https://cdn.rawgit.com/ethereum/web3.js/develop/dist/web3.js"></script>
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
</head>

This is a rather standard HTML page header that includes web3.js and jquery. Jquery is not mandatory, but if you write HTML code, once you discover this little library, you probably wouldn't want to do things the old way.

<body style="background:white;margin-left:20px;margin-right:20px;"  onload="init();">
	<p><h1 class="header">Hello World</h1>

		<p>
		<button class="btn-large" type="button" value="Get Greeting" style="min-width:500px;" id="btn_get_greeting"
			onClick="getGreeting();">Get Greeting using call()</button>
		
		<p>
		<button class="btn-large" type="button" value="Get Greeting" style="min-width:500px;" id="btn_get_greeting"
			onClick="getGreetingByTransaction();">Get Greeting using transaction</button>
		
		<div id="wrong_browser_warning">This isn't an ethereum browser</div> 	
		
		<p><textarea class="panelEditor" id='events' rows="20" cols="65"></textarea>

This code is pretty standard for any HTML project: it creates a page with two buttons, a "<div>" for messages, if any and a textarea. In the header, jquery, web3 and my own CSS are included, feel free to use your own css if my design is not what you need.

Note that in body's onload() we call the init() function.

The rest of a page is a javascript code:

<script>
// Web3 is used in init(), but defined here
var Web3 = require('web3');

// Contract address, as I have already deployed the HelloWorld contract, I have an access to this address. 
var strContractAddress = "0x42AB54558282835D71B803966833671327006edB";

// As you can see, we follow same steps we used in our tutorial so far; the only difference is that 
// we are not typing commands in geth's console, instead, we put them in javascript. 
// Then we'll run them, getting exactly the same result as if we typed them, one by one.
 
var abi = [ { "constant": true, "inputs": [], "name": "getGreeting", "outputs": 
	[ { "name": "strGreeting", "type": "string", "value": "Hello, World!" } ], 
	"payable": false, "type": "function" }, 
	{ "inputs": [], "payable": false, "type": "constructor" }, 
	{ "anonymous": false, "inputs": [ { "indexed": true, "name": "_from", 
	"type": "address" } ], "name": "helloWorldCalled", "type": "event" } ];

// Forward declaration of a contract, so we can see it both in init() and outside. 
var contract = null;	
		
function init() 
{
	// Hide warning text: when we need to inform the user of something,
	// we are going to make this field visible. In an improved version of your
	// contract you would probably add verbose details, like "you need to
	// download, install and run Geth with the following command line..." 
	// instead "This is not an Ethereum Browser" that I display now.
	document.getElementById("wrong_browser_warning").style.display = "none";
	
	// Checks Web3 support. There are few scenarios, as geth and possibly Mist can 
	// create their own web3 objects, in which case we would prefer reusing them
	if(typeof web3 !== 'undefined' && typeof Web3 !== 'undefined') 
	{
		// If there's a web3 library loaded, then make your own web3
		web3 = new Web3(web3.currentProvider);
	} 
	else if (typeof Web3 !== 'undefined') 
	{
		// If there isn't then set a provider
		web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
	} 
	else if(typeof web3 == 'undefined') 
	{
		// This isn't an ethereum browser
		document.getElementById("edt_greeting").style.display = "none";
		document.getElementById("btn_get_greeting").style.display = "none";
		document.getElementById("wrong_browser_warning").style.display = "block";
		return;    
	}

	// As in chapters above, we get the contract and an object of a contract
	var helloWorld = web3.eth.contract(abi);
	contract = helloWorld.at(strContractAddress);	
		
	// Now we get an event handler and assign a watcher function to it. 
	var helloEvent = contract.helloWorldCalled({_from: web3.eth.coinbase});

	// We work with watcher in a straight forward JavaScript way: 
	// the watch() is created, and as an event happens, its handler function 
	// (one declared inside) will be called. In that function we can do whatever 
	// we want, particularly, we can display information on our web page. 
	// All watchers follow the "error first" style, so err is the first argument
	helloEvent.watch(function(err, result) 
	{
		if(err) 
		{
			// Show the "div" we use to display errors and display an error text in it
			document.getElementById("wrong_browser_warning").style.display = "block";
			document.getElementById("wrong_browser_warning").text = err;
			return;
		}

		// Hide error display
		document.getElementById("wrong_browser_warning").style.display = "none";
			
		// Add the new text to the end of an already existing one in textarea. 
		// Example:
		// 2017-08-27 11:06:04 Hello, World!
		// 2017-08-27 11:06:11 Hello, World!
		// 2017-08-27 11:06:17 Hello, World!
		 
		var strEvents = $('#events').val();
		var date = new Date().toISOString().slice(0, 10) + " " + new Date().toISOString().slice(11, 19);
		var strNewEvents = strEvents + "\n" + date + " Hello, World!";
		$('#events').html(strNewEvents);
	});
}

This function is being called when the user clicks the first button. It uses call() to access the contract's getGreeting() function. As you remember, the function returns "Hello, World!", so we put it in the "div" and make the "div" visible:

function getGreeting() 
{
	var strGreeting = contract.getGreeting.call();
	document.getElementById("wrong_browser_warning").style.display = "block";

	// Get current date / time		
	var date = new Date().toISOString().slice(0, 10) + " " + new Date().toISOString().slice(11, 19);
	document.getElementById("wrong_browser_warning").innerHTML  = "Date: " + date + "; Greeting: " + strGreeting;
}

This function uses transaction to access contract's function. Unlike the previous function, it triggers event, so we expect the event watcher to be fired shortly after we press the second button (if you use local blockchain, make sure the miner is on).

function getGreetingByTransaction()
{
	// My first account (at index 0) has password "password"
	// In order to send transactions to it, I have to unlock it.
	web3.personal.unlockAccount(web3.eth.accounts[0], "password", 3000);
	contract.getGreeting.sendTransaction(2, {from: web3.eth.coinbase});
		
	document.getElementById("wrong_browser_warning").style.display = "block";
		
	var date = new Date().toISOString().slice(0, 10) + " " + new Date().toISOString().slice(11, 19);
	document.getElementById("wrong_browser_warning").innerHTML  = "Date: " + date + "; Transaction sent.";
}
</script>

It is important to note, that in order for it to run, the geth should be running, providing web3 functionality. The command line we used in FIRST Geth window is for the local blockchain, using it is faster then the test net. You will not be able to run the page we created from a browser without geth, MIST, Metamask (Chrome plug-in) or something like that - they access the block chain and expose results via web3.js library.







(C) snowcron.com, all rights reserved

Please read the disclaimer