Ethereum’s Solidity 101: Part 2— Development Tools, Gas, Transactions and Smart Contracts

Barry Lavides
14 min readJan 3, 2018

--

Ethereum's Solidity 101: Part 1 — Nodes, Clients and Networks.

Solidity is a contract-based, high level language, it is not really that hard to learn if you have a programming background especially if you know Javascript. What’s more challenging to grasp at first are the concept of Gas and account-based system.

The best analogy of account-based systems that I can think of is Uber. For you to use Uber’s service, you need to (1)create an account, link a (2)credit card or have a cash, (3)create a transaction or request for an uber, then (4)pay for the service(gas, driver, Uber’s system). It’s the same thing in an account-based blockchain, so let’s see how it works in Ethereum.

Accounts and Account Types

For you to use Ethereum’s blockchain you first need to create an account, but unlike Uber, you don’t need to fill out anything except for your account’s password, once an account is created, Ethereum will generate an address for your account.

There are two types of accounts in Ethereum:

  • EOA or Externally Owned Accounts which is controlled by private keys and
  • Contract accounts or Smart contract, controlled by their contract code

Both types of accounts have an address(we usually call it wallet address) and both must have an ether balance to create a transaction because that’s where the payment for the gas will be coming from.

Gas and Ether

Just like Bitcoin, Ethereum is using Proof of Work as its consensus algorithm right now. Meaning, there has to be someone called a miner(mining node) who has to provide a computing power to mint new coins and secure the blockchain. That computing power is not free, so as a user of the blockchain, we have to pay for that computation, that payment is calculated in Gas and gas is paid in Ether.

In Homestead network, five ethers are created every block(roughly 15 seconds) to the miner of the block. So the miners get paid by the gas and rewarded by new ethers at the same time.

Note that after the Byzantium update was implemented, the mining and the uncle reward was reduced to 3 ethers and 0.625-2.625 ethers, respectively.

Gas computation

All Ethereum transactions has a base(Gtransaction 21000 paid for every transaction) gas cost of 21,000(Gas usage). So if you are only transferring ether and not making any state changes transactions to the blockchain, your transaction takes 21,000 gas. If you are making state changes to the blockchain(usually through contract interaction), your transaction takes 21,000 gas, plus the gas associated with the changing of the blockchain state, as to how the additional gas is computed, it is the protocol that determines it.

Transaction Cost = Gas usage(≥21000) * Gas price(in Wei)

Gas price is dynamic and dictated by market conditions.

Transactions

  • Ether transfer, this is the simplest form of transaction in Ethereum. EOA can transfer ether to another EOA or to a contract account. And a contract account can transfer ether to another contract account or EOA
  • State changes, the idea comes from the fact that Ethereum is an append-only transaction ledger, every time we append data to the ledger/blockchain, state root is changed from previous state to the updated/current state. This includes, ether transfer and smart contract interaction that involves state changes(e.g contract deployment, contract modification/contract state change)

Ethereum is all about ledger of transactions. First you must have an (1)EOA or contract account, second, your EOA account(not necessarily you contract account) must have an (2)ether balance, once you have an ether balance to pay for the gas, that’s the only time you can create a (3)transaction, and once a transaction is created, it will be included in a block, the block is mined by a miner, broadcasts to other miners and full nodes then recorded in the blockchain.

Development Tools and Setup

Now let’s get our hands dirty, we will need to have the following installed in our local machine:

  • Node.js
  • ethereumjs-testrpc
  • web3j
  • solc

Once Node.js is installed, create a folderelection-dappand inside, create a package.json file by running

> npm init

Oncepackage.json is created let’s install the rest of the Node.js libraries that we need

> npm install --save ethereumjs-testrpc solc web3@0.20.1

Note: Make sure web3js’ version is 0.20.1, newer API versions are not backwards-compatible and stable yet

Once everything is installed, open a new tab in your terminal and run our Ethereum node emulator

> testrpc

So what do we have here, let’s discuss what’s displayed in the terminal

  • Available Accounts — Remember the accounts in Ethereum? When we use an emulator like testrpc, it automatically creates 10 fake Etherum accounts for testing and development
  • Private Keys — Remember the account types that we discussed? These are corresponding private keys of the accounts above, and since the accounts have private keys, it means they are Externally Owned Accounts(EOA)
  • RPC port(localhost:8545) — This is where our node client will connect later

Web3, Ethereum Node Client

Now that we’re finished on the first step of creating transactions(creation of accounts), let’s move on to the next step — making sure that our accounts have enough ether balance to pay for the transaction gas.

Open a new tab in your terminal and make sure that you’re in the root directory of election-dapp then open a Node.js REPL command prompt

# In your terminal inside election-dapp
> node

Aside from automatically creating 10 fake Ethereum accounts for you, it turns out that each accounts also comes with a default of 100 ether each. Now let’s check that in our Node.js REPL command prompt — checking an account’s balance in Ethereum means reading/exploring the blockchain inside an Ethereum node, and if you remember in the first part of this tutoral, we need a node client software for that, that’s where web3js comes in, while still inside the Node.js REPL command prompt, let’s call and instantiate web3js library and explore the blockchain inside our Ethereum node emulator

# Connect to testrpc node
> Web3 = require('web3');
> web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'))
# List all accounts in testrpc node
> web3.eth.accounts

We can see all the accounts that has been created in our Ethereum node emulator, now let’s check if a balance of 100 ether really exists on the existing accounts, let’s try it on the first account, to do that we have to know the address of the account, we can either use the index or the reserve word coinbase key, you can try it on other accounts by using their indexes

# Get the first account
> web3.eth.coinbase
> web3.eth.accounts[0]

Finally let’s check the ether balance of the first account by using its account address

# Check the first account's ether balance
> web3.eth.getBalance(web3.eth.coinbase)

That’s not what we are expecting, to fix that, we need to convert it to a Javascript string type. (You can read about web3js’ big numbers here.)

> web3.eth.getBalance(web3.eth.coinbase).toString()

Still not the 100 ether that we are expecting, but we’re almost there, the reason for that is it’s because web3js’ getBalance function is by default in Wei, Wei is the smallest denomination aka base unit of ether. You can check the ether denominations here and here.

So let’s check the balance in ether then.

# Convert balance from Wei to Ether
> web3.fromWei(web3.eth.getBalance(web3.eth.coinbase), 'ether').toString()

And sure enough, we see the 100 ether balance of our coinbase.

First Transaction, Sending Ether

Now that we can confirm that we have an ether balance enough to pay for a transaction gas, let’s make our first ether transfer transaction. Using our coinbase account, let’s transfer 1 ether to the second account in our Ethereum node emulator

> web3.eth.sendTransaction({from: web3.eth.coinbase, to: web3.eth.accounts[1], value: web3.toWei(1, 'ether')})

We’ve successfully transferred 1 ether from coinbase to our second account, and web3js returned the hash of our first transaction. Let’s switch to testrpc’s tab to see how Ethereum node handled our transaction.

As you can see, since this is just an Ethereum node emulator, our transaction was immediately mined and included in a block. You can also see that the Gas usage was 21,000 because we’re only transferring an ether. For fast testing and development, testrpc’s transactions will always and immediately be included in a block and a block will always contain one transaction. So if you create another transaction, it will be included in the next block.

That is different when using a real Ethereum network(public test network and main network). In real a network, it will take some time because a miner needs to solve the puzzle to mine the blocks. Also unlike testrpc, blocks in real networks can have multiple transactions. (This is an oversimplification of how transactions and blocks work in real Ethereum networks.)

Solidity and Smart Contract Deployment

Now that we’ve successfully tested creating a transaction, let’s now dive in to creating our first smart contract in Solidity.

In the root directory of our election-dappcreate a file called Election.sol

Inside the Election.sol file, paste the following smart contract code.

pragma solidity ^0.4.11;contract Election {
string message;
function Election() public {
message = 'Lets cast the vote!';
}
function setMessage(string _message) public {
message = _message;
}
function getMessage() public constant returns (string) {
return message;
}
}

This file contains the logic of a smart contract that is built using the Solidity programming language. As you can see Solidity is very similar to other programming languages such as Javascript. In this tutorial, I decided not to focus on Solidity’s syntax, but we will discuss that and create a more useful smart contract on the next tutorial instead, for now let’s proceed to the deployment of our first smart contract.

In the previous section of this tutorial, we’ve already connected our Ethereum node client(web3js) to our node emulator(testrpc), the next thing we need to do is compile our contract and deploy it to our node emulator.

Inside our Node.js REPL command prompt, import the Solidity compiler.

> solc = require('solc')

Next thing we need to do is read the file from the filesystem and transform it to string.

> sourceCode = fs.readFileSync('Election.sol').toString()

Then compile it using solc library

> compiledCode = solc.compile(sourceCode)

As you can see there’s a lot of stuff in the results of the compiler command, it’s actually a JSON object with a lot of fields. And we’re going to use some of them to deploy the contract to our Ethereum node emulator. The first thing we need to get is what we call ABI or Application Binary Interface, it’s pretty much the same as the API. The ABI defines how to call the functions in the contract and how to get data back from it.

> electionABI = JSON.parse(compiledCode.contracts[':Election'].interface)

And this is how the ABI of the interface of our contract is described. As you can see we only have two functions, the setMessage, getMessage, the constructor and other data.

To deploy the contract, we need the contract code’s bytecode version, I will explain later why we need to convert it to a bytecode.

> byteCode = compiledCode.contracts[':Election'].bytecode

This is a binary string, it’s a binary code encoded in hexadecimal, so now we are ready to deploy the code of the contract to our Ethereum node emulator. The way we do that is first by instantiating a new contract object using our ABI object.

> electionContract = web3.eth.contract(electionABI)

Next is to use electionContract object to actually deploy the contract. When deploying a contract, we need to specify three things in the instance of our ABI(electionContract)

  • (1) data: byteCode — the bytecode version of the contract
  • (2) from: web3.eth.coinbase — which account will pay for the Gas of the contract deployment transaction
  • (3) gas: 4700000 — what’s the maximum Gas we are willing to pay for the future transactions of our smart contract
> electionDeployed = electionContract.new({data: byteCode, from: web3.eth.coinbase, gas: 4700000})

The last command deployed and stored the contract code into a transaction to our Ethereum node emulator or in a blockchain. Again, let’s switch to testrpc tab to check if the transaction was really created and deployed.

Unlike when transferring ether to another account, contract deployment will show you a Contract Created log with a corresponding unique contract address(0x6ad82075409fbc77c579910e6d7edbd704c9f883). You can see that the transaction was automatically included in the second block of the blockchain and the Gas usage was not 21,000 anymore because we didn’t just transfer an ether to another account, and even though we specify 4,700,000 as maximum Gas, the protocol only used 284,668.

Exploring the Contract using the ABI

Let’s now inspect the contract that we deployed and check the balance of the account we used to pay for the contract deployment.

The first thing we need to get is the contract address, to do that, we just use the instance of the contract

> electionDeployed.address

As you can see, it’s the same address displayed in our testrpc log when the contract was deployed, so let’s use that to get the instance of our contract

> electionInstance = electionContract.at(electionDeployed.address)

At this stage we can use the functions that are exposed by the contract, for example we can use the getMessage function

> electionInstance.getMessage()

The constructor of our smart contract was properly initialized, the interesting part is, even though we made an interaction to the smart contract, we didn’t pay for any Gas, the reason for that is we just pulled a data/state from our blockchain and didn’t append any data or made any state changes in the blockchain. Notice that in our testrpc log, no transaction was created.

Now let’s use the second function setMessage.

> electionInstance.setMessage('Hello Blockchain', {from: web3.eth.coinbase})

setMessage is a non constant function of our contract. Meaning, it is a function that changes the state of the contract, so whoever is calling it will have to pay the Gas fee, in this case we specified that the first account will be the payer. We also didn’t specify any Gas amount since we’ve already created a default maximum Gas fee of 4,700,000 when we deployed the contract. If we look at our testrpc log we can see the newly created transaction.

Now let’s check how much ether is left in our first account.

Sure enough, our first account’s ether is reduced, take note that Gas usage will be different from your machine because it’s the protocol that computes it.

EVM and OPCODES

Before we end this tutorial, let’s get back and discuss the reason why we need to convert our contract to bytecode before we can deploy it and why it’s important that we know what’s happening under the hood of our smart contract.

The value deployed to our node emulator is the binary bytecode version of the smart contract compiled by the solidity compiler.

But why a binary bytecode version? The reason is that the code has to be executable by the EVM (Ethereum Virtual Machine) that runs on every node of the network. EVM can be installed on Linux, Mac or Windows environment so it’s important that our smart contract is runnable on whatever operating system we want to execute it on.

https://hackernoon.com/heres-how-i-built-a-private-blockchain-network-and-you-can-too-62ca7db556c0

EVM does not run the binary bytecode itself, the bytecode is decoded in a series of Ethereum instructions called OPCODES, it’s an assembly language used by the EVM to execute the smart contract, we can use a special online tool to decompile a binary bytecode to the corresponding bytecode instructions.

Using the bytecode of our smart contract let’s try and decode our contract to get its corresponding OPCODES

These are the series of OPCODES that corresponds to our contract’s binary bytecode, and these OPCODES are the codes that are really executed by the EVM, now what you have to understand is that each OPCODE instruction has a specific cost in Gas unit depending on its complexity, the cost in Gas of each of the OPCODES is referenced in the Ethereum yellow paper or you can check it here.

Conclusion

When deploying a contract, you pay to store the contract code in the blockchain state, and the cost of deployment depends on the OPCODES that are executed in the smart contract code, then each time a non constant function of your contract is called — that is a function that changes the state of the contract, whoever is calling it will have to pay the Gas fee that depends on which OPCODES are executed, that’s why you have to be very careful of the datatypes you use and about the overall complexity and size of your smart contract code, as the more data you store on the chain state and the more complex your code, the more expensive it will be to execute it.

Typing all these commands to deploy and update our contract might be painful, tedious and very error prone. So in the next tutorial, to boost the development process, we will use truffle as an accelerator, truffle is the most used development framework in Ethereum right now. We will also make an in-depth look at Solidity’s syntax, make a more useful dapp and use another Ethereum node client called geth.

--

--