Deploying A Smart Contract, The Hard Way

I am in favor of understanding the mechanics that goes under the hood of a tool, framework or technology that I am using. If not all of it, at least a general understanding so once a bugs appears I have an idea where to look at.

This post is an exercise on the steps that happen to deploy a smart contract on the Ethereum Virtual Machine. I will show here the repetitive manual manner which if you have used tools such as Truffle or Mist they are done in simple short commands.

For this, you need to install Geth and Solidity compiler.

$ sudo apt-get install -y software-properties-common
$ sudo add-apt-repository -y ppa:ethereum/ethereum
$ sudo apt-get update
$ sudo apt-get install -y ethereum
$ sudo apt-get install -y solcCheck if you have solc and geth installed$ ~$ solc --version
solc, the solidity compiler commandline interface
Version: 0.4.11-develop.2017.4.24+commit.a9f42157.Linux.g++
$ geth version
Geth
Version: 1.6.0-stable
Git Commit: facc47cb5cec97b22c815a0a6118816a98f39876
Architecture: amd64
Protocol Versions: [63 62]
...

For this exercise I am going to run geth in my own private network. This post does not go into setting up your own private blockchain. However if you are interested you can set one up by following these instructions

Make sure you start geth with the console flag and if you have not done so already go ahead and create an wallet address so we can deploy the contract with it:

$ personal.newAccount()

This is the contract we will deploy the hard way:

pragma solidity ^0.4.10;contract Storage {
  uint256 storedData;  function set(uint256 data) {
    storedData = data;
  }  function get() constant returns (uint256) {
    return storedData;
  }
}

Compilation

Create the file for the contract and add the code above to it.

$ touch Storage.sol

To compile this contract we will put it in a json format and assign a JavaScript variable, then we send it to an output file.

$ echo "var storageOutput=`solc --optimize --combined-json abi,bin,interface Storage.sol`" > storage.js
$ cat storage.js
var storageOutput={"contracts":{"Storage.sol:Storage":{"abi":"[{\"constant\":false,\"inputs\":[{\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"}]","bin":"60606040523415600b57fe5b5b60978061001a6000396000f300606060405263ffffffff60e060020a60003504166360fe47b18114602a5780636d4ce63c14603c575bfe5b3415603157fe5b603a600435605b565b005b3415604357fe5b60496064565b60408051918252519081900360200190f35b60008190555b50565b6000545b905600a165627a7a7230582052ccd035d0e1b92d60b2dbc6ffd8a86d947fcaa3199be580f3d95fc289b0c39a0029"}},"version":"0.4.11-develop.2017.4.24+commit.a9f42157.Linux.g++"}

Now in Geth console:

> loadScript('storage.js')
true
> storageOutput
{
  contracts: {
    Storage.sol:Storage: {
      abi: "[{\"constant\":false,\"inputs\":[{\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"}]",
      bin: "60606040523415600b57fe5b5b60978061001a6000396000f300606060405263ffffffff60e060020a60003504166360fe47b18114602a5780636d4ce63c14603c575bfe5b3415603157fe5b603a600435605b565b005b3415604357fe5b60496064565b60408051918252519081900360200190f35b60008190555b50565b6000545b905600a165627a7a7230582052ccd035d0e1b92d60b2dbc6ffd8a86d947fcaa3199be580f3d95fc289b0c39a0029"
    }
  },
  version: "0.4.11-develop.2017.4.24+commit.a9f42157.Linux.g++"
}

Notice how the root object is a map { contracts: Storage.sol:Storage: {...} }. Storage.sol:Storage . The map keys identify the contract name, the abi in json, the bin code and lastly the solidity compile version.

Let’s prepare the variables that will help us interact with the contract.

> var storageContractAbi = storageOutput.contracts['Storage.sol:Storage'].abi
"[{\"constant\":false,\"inputs\":[{\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"}]">var storageContract = eth.contract(JSON.parse(storageContractAbi))
undefined> var storageBinCode = "0x" + storageOutput.contracts['Storage.sol:Storage'].bin
"0x60606040523415600b57fe5b5b60978061001a6000396000f300606060405263ffffffff60e060020a60003504166360fe47b18114602a5780636d4ce63c14603c575bfe5b3415603157fe5b603a600435605b565b005b3415604357fe5b60496064565b60408051918252519081900360200190f35b60008190555b50565b6000545b905600a165627a7a7230582052ccd035d0e1b92d60b2dbc6ffd8a86d947fcaa3199be580f3d95fc289b0c39a0029"

Notice that to get the web3 contract object we need to use eth.contract + the parsed storageContractAbi from the compilation.

We also get store the bin code value in a variable. Notice that in order to have the expected hex value we need to add the "0x" at the beginning of it.

Deployment

Geth provides a facility to deploy a compiled contract. you need some ether for this step, plus it only works when you mine, so let’s start the miner:

> miner.start(1)

Now it is time to deploy. Make sure you unlock your account by typing the password you assigned to it.

> personal.unlockAccount(eth.accounts[0])

>var deployTransationObject = { from: eth.accounts[0], data: storageBinCode, gas: 1000000 };
undefined> var storageInstance = storageContract.new(deployTransationObject)
undefined> storageInstance
{
  abi: [{
      constant: false,
      inputs: [{...}],
      name: "set",
      outputs: [],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "get",
      outputs: [{...}],
      payable: false,
      type: "function"
  }],
  address: undefined,
  transactionHash: "0xcbe9921400083ca543e4e62386e3a02eed6cb327f23c9a445f3ec81a3bfad830"
}

So the contract sent a transaction to deploy an instance and returned a web3 contract instance, which unfortunately lacks an address. It should know the address but it did not because it was not mined yet. To fix it, let’s get the address by doing:

> eth.getTransactionReceipt(storageInstance.transactionHash);
{
  blockHash: "0x5712473e4b60e5c58648daee420427133db4c048a11193330a6c26b6101e5206",
  blockNumber: 11,
  contractAddress: "0xc05c7ee9b13dd103581accfead6074e0522475e0",
  cumulativeGasUsed: 94673,
  from: "0x7f77d5c8639d04f7efb221079cac40dca1a37f43",
  gasUsed: 94673,
  logs: [],
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  root: "0xd2945007d43f6d05def2d48b5c369e61915b9c3cedde933e26a5469b241e7106",
  to: null,
  transactionHash: "0xcbe9921400083ca543e4e62386e3a02eed6cb327f23c9a445f3ec81a3bfad830",
  transactionIndex: 0
}

The address returned is the unique, immutable address of the contract, it is calculated from the hash of the sender address and the transaction nonce. When you interact with this contract instance you need to mention this address. So let’s save it:

> var storageAddress = eth.getTransactionReceipt(storageInstance.transactionHash).contractAddress
undefined
> storageAddress
"0xc05c7ee9b13dd103581accfead6074e0522475e0"

Voilá, the contract is deployed.

Let’s interact with the contract:

> var storage = storageContract.at(storageAddress);
undefined> storage
{
  abi: [{
      constant: false,
      inputs: [{...}],
      name: "set",
      outputs: [],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "get",
      outputs: [{...}],
      payable: false,
      type: "function"
  }],
  address: "0xc05c7ee9b13dd103581accfead6074e0522475e0",
  transactionHash: null,
  allEvents: function(),
  get: function(),
  set: function()
}

Here are the JavaScript functions that will let us call the same ones in the contract. Let’s call a function. By the way are you mining? ;-)

> storage.get.call()
0> storage.set.sendTransaction(42, {from: eth.accounts[0], gas: 1000000})
"0x7a54ab329fcbf551432eb78c4b2a1ff48fc8b9f9aa23d94fa86330e5c1d711f3"> storage.get.call()
42

Success!

We just saw here a long and winding path of deploying a contract to the Ethereum Virtual Machine. Tools such a Truffle does all that for you under the hood with commands such as truffle compile and truffle migrate. But I believe it is good to know the innards so you are on the right track to become proficient in this new field. Hope you enjoyed this blog post. Next one I will talk about deploying smart contracts using Truffle.

1K

1K claps
Gustavo (Gus) Guimaraes

Written by

A curious mind, joie de vivre practitioner