A Practical Guide to Using Smart Contracts on Ethereum

·

Ethereum offers a variety of tools for writing, compiling, deploying, and interacting with smart contracts. Developers can choose the right tools based on their specific needs and development environment.

This guide provides a hands-on walkthrough for setting up a local test blockchain using the Geth client and deploying and interacting with a smart contract on it.

Setting Up a Local Test Blockchain

Testing smart contracts on the Ethereum mainnet requires spending real Ether. For development and testing purposes, it's more efficient to set up a local, private test network. A smart contract developed on this local chain can easily be deployed to a public network later. Note that a test network requires some manual, non-default configuration.

Configuring the Initial State

First, configure the initial state of your private blockchain network. Create a new file named genesis.json with the following content.

{
"config": {
"chainId":22,
"homesteadBlock":0,
"eip155Block":0,
"eip158Block":0
 },
"alloc" : {},
"coinbase" : "0x0000000000000000000000000000000000000000",
"difficulty" : "0x400",
"extraData" : "",
"gasLimit" : "0x2fefd8",
"nonce" : "0x0000000000000038",
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp" : "0x00"
}

Key parameters include:

Starting the Blockchain

Initialize the blockchain using the following command to generate the genesis block and initial state.

$ geth --datadir /path/to/datadir init /path/to/genesis.json

The --datadir option specifies where the blockchain data will be stored.

Next, start the node and enter the Geth console with this command.

$ geth --identity "TestNode" --rpc --rpcport "8545" --datadir /path/to/datadir --port "30303" --nodiscover console

Here’s what these options mean:

Creating an Account

The Geth console uses JavaScript syntax. Create a new account with this command.

> personal.newAccount()
Passphrase:
Repeat passphrase:
"0x1b6eaa5c016af9a3d7549c8679966311183f129e"

After entering a passphrase twice, a new account address is generated. You can check its balance.

> myAddress = "0x1b6eaa5c016af9a3d7549c8679966311183f129e"
> eth.getBalance(myAddress)
0

The balance will be 0 initially. Use miner.start() to begin mining. Because the initial difficulty is set low, you will quickly mine Ether to fund your account. Use miner.stop() to halt mining.

Creating and Compiling a Smart Contract

This example uses a smart contract written in Solidity. You will need the Solidity compiler, solc, to compile the code into EVM bytecode.

Create a new file named testContract.sol with the following content. This simple contract contains a function, multiply, that returns the input integer multiplied by 7.

pragma solidity ^0.4.0;
contract testContract {
 function multiply(uint a) returns(uint d) {
 d = a * 7;
 }
}

Use solc to obtain the compiled EVM bytecode.

$ solc --bin testContract.sol
======= testContract.sol:testContract =======
Binary:
6060604052341561000c57fe5b5b60a58061001b6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa114603a575bfe5b3415604157fe5b60556004808035906020019091905050606b565b6040518082815260200191505060405180910390f35b60006007820290505b9190505600a165627a7a72305820748467daab52f2f1a63180df2c4926f3431a2aa82dcdfbcbde5e7d036742a94b0029

Next, use solc to get the contract's JSON ABI (Application Binary Interface). The ABI defines the contract's interface, including its methods, variables, and events.

$ solc --abi testContract.sol
======= testContract.sol:testContract =======
Contract JSON ABI
[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"payable":false,"type":"function"}]

Return to the Geth console and store these two values in variables. Remember to prefix the bytecode with 0x.

> code = "0x6060604052341561000c57fe5b5b60a58061001b6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa114603a575bfe5b34156041fe5b60556004808035906020019091905050606b565b6040518082815260200191505060405180910390f35b60006007820290505b9190505600a165627a7a72305820748467daab52f2f1a63180df2c4926f3431a2aa82dcdfbcbde5e7d036742a94b0029"
> abi = [{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"payable":false,"type":"function"}]

Deploying the Smart Contract

In the Geth console, you must first unlock your account to send transactions.

> personal.unlockAccount(myAddress)
Unlock account 0x1b6eaa5c016af9a3d7549c8679966311183f129e
Passphrase:
true

Next, send a transaction to deploy the contract.

> myContract = eth.contract(abi)
> contract = myContract.new({from:myAddress,data:code,gas:1000000})

If mining is not active, you can check the transaction pool for pending transactions using txpool.status. To inspect pending transactions:

> eth.getBlock("pending",true).transactions
[{
 blockHash: "0xbf0619ca48d9e3cc27cd0ab0b433a49a2b1bed90ab57c0357071b033aca1f2cf",
 blockNumber: 17,
 from: "0x1b6eaa5c016af9a3d7549c8679966311183f129e",
 gas: 90000,
 gasPrice: 20000000000,
 hash: "0xa019c2e5367b3ad2bbfa427b046ab65c81ce2590672a512cc973b84610eee53e",
 input: "0x6060604052341561000c57fe5b5b60a58061001b6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa114603a575bfe5b34156041fe5b60556004808035906020019091905050606b565b6040518082815260200191505060405180910390f35b60006007820290505b9190505600a165627a7a72305820748467daab52f2f1a63180df2c4926f3431a2aa82dcdfbcbde5e7d036742a94b0029",
 nonce: 1,
 r: "0xbcb2ba94f45dfb900a0533be3c2c603c2b358774e5fe89f3344031b202995a41",
 s: "0x5f55fb1f76aa11953e12746bc2d19fbea6aeb1b9f9f1c53a2eefab7058515d99",
 to: null,
 transactionIndex: 0,
 v: "0x4f",
 value: 0
}]

Start mining with miner.start(). After some time, the transaction will be confirmed and included in a new block on the blockchain.

Interacting with the Smart Contract

You can send a transaction to execute a contract function. The sendTransaction method will broadcast the call, and it will be recorded on the blockchain through mining. This is used for functions that change the contract's state.

> contract.multiply.sendTransaction(10, {from:myAddress})

If you simply want to execute a function locally to read its return value without spending gas or creating a transaction, use the call method.

> contract.multiply.call(10)
70

👉 Explore advanced blockchain development tools

Frequently Asked Questions

What is the main advantage of using a local test blockchain?

A local test blockchain allows developers to write, deploy, and test their smart contracts without spending real Ether on gas fees. It provides a safe, controlled, and free environment for experimentation and debugging before deploying to a public testnet or the mainnet.

What is the purpose of the ABI?

The Application Binary Interface (ABI) is a JSON array that describes the public interface of a smart contract. It defines how to encode data to send to the contract and how to decode the data the contract returns. Wallets and dApps use the ABI to generate the user interface for interacting with the contract's functions.

What's the difference between call and sendTransaction?

Using call executes a contract function locally on your own node without creating a blockchain transaction. It is used for reading data from the contract state and is free. sendTransaction creates a signed transaction that is broadcast to the network, mined into a block, and alters the contract's state. It requires gas and is used for writing data.

Why is unlocking an account necessary?

Unlocking an account is a security measure. It decrypts the account's private key stored in the node's keystore and holds it in memory for a short period. This allows the node to digitally sign transactions on behalf of that account without constantly requiring the password. Remember to lock accounts after use.

Can I connect multiple local nodes to form a private network?

Yes, you can. To create a multi-node private network, ensure all nodes use the same genesis.json file and chainId. When starting each node, use the --bootnodes flag to point them to a common node for discovery, and avoid using the --nodiscover flag. This is great for simulating a more realistic multi-party network environment.