Ethereum Tutorials

SMART CONTRACTS

"Duke of Ether!" Contract (part 1)

Important!

At first, I am going to make some intentional mistakes in this contract in order to be able to illustrate some development points. For a fixed version, see "final" code in this same tutorial.

I am going to make CRITICAL errors in the code for the sake of an exercise! Until it clearly says "final version", the code is intended for the reader to look for problems; therefore, I advise against using it as is. Final version will be present in the last chapters, both for "Duke" and "ShareHolder".

What is "Duke of Ether"?

A remake of a well-known "King of Ether" contract, it is a simple auction allowing people to pay for the right to take the "throne". Each payment should be larger than the previous one, by at least certain degree, as it should be in any auction. When you pay, the previous Duke gets a refund and you become a current Duke, however, he/she gets YOUR money; in other words, a refund is larger then the original payment. It is a good way to earn some extra ethers, though (as in any pyramid scheme, for that mater) there is a risk that the payment required grows so large that there will never be the next Duke.

Initial version of "Duke of Ether"

Let's take a look at the code of the "Duke of Ether":

			
pragma solidity ^0.4.11;

contract DukeOfEther_01 
{
	address public m_addrCurrentDuke;
	uint public m_nCurrentDukePaid;
	address public m_addrOwner;

	function DukeOfEther_01()
	{
		m_addrOwner = msg.sender;
	}

	// ---

	function becomeDuke() payable
	{
		if(msg.value < 11 * m_nCurrentDukePaid / 10)
			return;

		m_addrCurrentDuke.transfer(msg.value);

		m_addrCurrentDuke = msg.sender;
		m_nCurrentDukePaid = msg.value;
	}
	
	// ---
	
	function isOwner() constant returns (bool bIsOwner) { return (m_addrOwner == msg.sender); }
	function isDuke() constant returns (bool bIs) { return (m_addrCurrentDuke == msg.sender); }
	function getMinNextBet() constant returns (uint nNextBet) { return  11 * m_nCurrentDukePaid / 10; }
	
	// ---
	
	function terminate()
	{
		// Send money back to the current Duke. To be used when we need to update code.
		if (msg.sender == m_addrOwner) 
			selfdestruct(m_addrCurrentDuke);
	}
}

The contract stores three member variables:

				
address public m_addrCurrentDuke;
uint public m_nCurrentDukePaid;
address public m_addrOwner;				

First one is an address of a current Duke, we will use it if we need to send him money.

Second is the amount the Duke paid for the throne, it is used to calculate the next minimum payment.

Third is contract owner's address: the owner will (not in this version) receive comissions players pay.

In a constructor, we set contract owners to the (address of) person that created the contract. As a constructor is called once and only once, there is no way (in this contract) to change the owner.

The "becomeDuke()" checks if amount of money sent is enough. Then it refunds the current Duke with next Duke's money and puts the next Duke on the throne.

This function contains a vulnerability which I am going to discuss in the next chapter.

Calling functions of the contract

Deploy the "Duke of Ether" contract same way you deployed "Hello, World!" contract before. Note: it is possible to deploy a contract with geth, without Mist, by calling var contract = MyContract.new(arg1, arg2, ..., {from: primaryAddress, data: evmByteCodeFromPreviousSection}) The asynchronous way of doing the same looks like this: MyContract.new([arg1, arg2, ...,]{from: primaryAccount, data: evmCode}, function(err, contract) { if (!err && contract.address) console.log(contract.address); });

Now that the contract is in block chain, it exposes functions that we can call: unlike the "Hello, World!" contract that had no non-constant functions.

First, let's take a look at calling contract's functions with Geth (note: if you prefer, you can do if from Mist, it is explained below. But you need this theory in order to work with Web UI). In Mist, go to the contract, click "Show Interface" icon and copy the contents of a popup into clipboard. The interface describes the functions a contract exposes, we need it in order to know what to expect from the contract.

From Geth's console, run the following command (type "var abi = " and paste the interface after the "=" sign):

var abi = [ { "constant": true, "inputs": [], "name": "isOwner", 
	"outputs": [ { "name": "bIsOwner", "type": "bool", "value": false } ], 
	"payable": false, "type": "function" }, 
	{ "inputs": [], "payable": false, "type": "constructor" } ]

Note that this is javascript: functions are provided by Geth via web3.js library, so later we will do the same from the Web browser.

Now as we have contract's ABI, we can get access to a contract. In the same console, type two commands:

var Duke = eth.contract(abi)
var contract = Duke.at("0x7EA56DDC45Da69505A9f9bC7c52A92e9dDA1eA38")

Here 0x7EA56DDC45Da69505A9f9bC7c52A92e9dDA1eA38 is an address of a contract, copy it from the Mist.

Now we can call functions of a "contract" variable, via web3.js:

web3.eth.defaultAccount = eth.accounts[1]
contract.isDuke()
> false
web3.eth.defaultAccount = eth.accounts[0]
contract.isDuke()
> true

The first line sets the default account (remember, we created two of them).

The second line calls a function "isDuke". As the contract was created from the first account, the function returns false.

Then we set the first account as a default one, and now the function returns true.

To work with blockchain (with non-constant functions), we need to unlock the account

personal.unlockAccount(eth.accounts[1], "password", 3000)

Here "password" is a password we entered in Mist when creating an account. This is a nice feature, but please keep in mind that the password is supposed to be a secret. Is it a good style to ask your web site visitor to enter the password? If yes, you are getting an access to his/her wallet; if no - your Web App will not be able to handle payments. I am going to talk about it later in this book.

When we call contract's functions, there are two ways: a direct call and a transaction.

contract.isOwner.call()

This is executed locally and does not alter the internal state of a contract. Also, it is not recorded in a blockchain, after all, why should we record the "someone READ the variable" event? As a bonus, it is free.

The second way looks like this:

contract.becomeDuke.sendTransaction({from: web3.eth.coinbase, value: web3.toWei(1, "ether"), gas: 1000000 },function(err, transactionHash) 
{
	if (err)console.log(err.error_message);
});

In that case, return value is the hash of a transaction. This means that the operation will be recorded in a block chain, permanently. Also, you have to pay for gas this transaction uses.

Let's select a non-Duke account again:

				
web3.eth.defaultAccount = eth.accounts[1]
contract.isDuke()
> false

We can call the function of a contract directly, same way we did with a constant function, but it does not change contract's state:

				
contract.becomeDuke()
contract.isDuke()
>false

This is because we didn't trigger any transactions. Let's do it right.

From Mist: From contract's page on Mist, select function becomeKing(), select a contract that sends money (it will become the next Duke) and send 1 ether to the contract.

Or from Geth:

contract.becomeDuke.sendTransaction({from: web3.eth.coinbase, value: web3.toWei(1, "ether"), gas: 1000000 },function(err, transactionHash) 
{
	if (err)console.log(err.error_message);
});

contract.becomeDuke()
contract.isDuke()
>true

I am going to analyze this contract's errors and vulnerabilities in the next chapter.







(C) snowcron.com, all rights reserved

Please read the disclaimer