Introduction to Ethereum development, part 3

Johannes Bertens
oneupcompany
Published in
11 min readFeb 1, 2018

This is part three of the “Introduction to Ethereum development” series. In this series you will install the essential tools, and compile, run and interact with a smart contract! Starting your blockchain development career with a 💥.

This post was first published on Github (you can find it here). It has been re-formatted for Medium, but the content is the same.

Part 1 of this series, where it all started, takes you through installing the required tooling and creating your first blockchain project using Truffle.

Part 2, in case you missed it, introduces all the elements of this project, gives an in-depth explanation on what is in a smart contract, and shows you how to compile and run the contract.

In part 3 of this series, the one you are currently reading, we will interact with this contract, first from the console and then from a webpage.

Sending MetaCoins aka Interaction

Now for the true test — let’s interact with the deployed smart contract.

Here some theory is required, as calling functions that change the state of a smart contract (in this case the balance mapping) — cost gas. Gas is the fee payed for executing the contract. Remember: the contract is being executed on the ethereum network by ethereum miners (when run in production). They will prioritize work with higher fees over work with lower fees, and if the amount of gas supplied is less than required for the actual work (computations required for the transaction), the transaction will fail and you lose your Gas.

This might sound a bit daunting, but the mechanism is in place to ensure the network is kept running and the miners are compensated for their work. More info on the how and why of Gas can be found in this excellent stackexchange post: https://ethereum.stackexchange.com/questions/3/what-is-meant-by-the-term-gas. Information on current Gas prices on the public ethereum network can be found here: https://ethgasstation.info/

So, with that theory behind us, let’s transfer some coins in our development network! We will again use the truffle console, this time calling a different function:

MetaCoin.deployed().then(function(instance) {return instance.sendCoin("0xf17f52151ebef6c7334fad080c5704d77216b732", 10, {from: "0x627306090abab3a6e1400e9345bc60c78a8bef57"})});

Which should give you the following output:

tx: '0x5d042f600ec6e7623dfff3879711bb3469c578a35836c8f7f458bfcb12211d38',
receipt:
{ transactionHash: '0x5d042f600ec6e7623dfff3879711bb3469c578a35836c8f7f458bfcb12211d38',
transactionIndex: 0,
blockHash: '0x899c2d155c58442aa0fe6545a4e3618da2c005c0692882d8a9a62abe367a02c5',
blockNumber: 6,
gasUsed: 50960,
cumulativeGasUsed: 50960,
contractAddress: null,
logs: [ [Object] ],
status: 1 },
logs:
[ { logIndex: 0,
transactionIndex: 0,
transactionHash: '0x5d042f600ec6e7623dfff3879711bb3469c578a35836c8f7f458bfcb12211d38',
blockHash: '0x899c2d155c58442aa0fe6545a4e3618da2c005c0692882d8a9a62abe367a02c5',
blockNumber: 6,
address: '0xf25186b5081ff5ce73482ad761db0eb0d25abfbf',
type: 'mined',
event: 'Transfer',
args: [Object] } ] }

Note the gasUsed and cumulativeGasUsed figures, while we didn’t specify any gas, this is the amount of gas used for the execution of this function.

Let’s now check the balance of the 1st address again:

MetaCoin.deployed().then(function(instance) {return instance.getBalance.call("0x627306090abab3a6e1400e9345bc60c78a8bef57")});

This should now give 10000–10… is: BigNumber { s: 1, e: 3, c: [ 9990 ] }

And the balance of the 2nd address:

MetaCoin.deployed().then(function(instance) {return instance.getBalance.call("0xf17f52151ebef6c7334fad080c5704d77216b732")});

This should give us 10… or BigNumber { s: 1, e: 1, c: [ 10 ] }

Ow yeah, time to divide the spoils with your friends! 🚀

But what about that gas? And… wasn’t there something about a delay when adding a new transaction to a chain?Yes, there is — this will not work when interacting with a live ethereum chain. There you need to provide enough Gas (as mentioned earlier) to make sure the transaction is executed and the transaction will take some time to execute. The gas can be provided in the same way we provide the “from” address, as parameter in this object. The call would then look like this:

MetaCoin.deployed().then(function(instance) {return instance.sendCoin("0xf17f52151ebef6c7334fad080c5704d77216b732", 10, {from: "0x627306090abab3a6e1400e9345bc60c78a8bef57", gas: 80000})});

In this command, we explicitly pass 80000 gas to the function.

When running this command on a blockchain which does not automatically mine blocks (our truffle develop does), it will only give a result when the transaction is mined - which can take a while! For most use cases, it is better to make these calls a-synchronous. This can be done by calling .then() and passing a callback. We will come back to this in the next part, in the automated tests and when calling the blockchain from a website.

Tests and interaction from websites

Now we have a running smart contract on a (local) blockchain. But how do we know that it functions correctly? This can be assessed by running tests and interacting with the contract using a User Interface.

For the curious, tests, and specifically automated tests, have become popular due to the Test Driven Developmentparadigm. This way of working has become popular due to the book with the same name written by Kent Beck in 2003. The TDD-cycle is:

1. Add a test
2. Run all tests and see if the new test fails
3. Write the code
4. Run tests
5. Refactor code … and then repeat!

In contract with traditional development work, here the test is added first, then the code. Also, in step 2 all tests are run, to see if the newly added test fails. Then in step 4 all tests are run again. This leads to a large demand for automated (and quick) tests. We will take a look at how to test smart contracts soon.

Another way to check if a contract is behaving as it should, is to interact with it using a User Interface — we will explore the possibilities to interact with the smart contract from a web browser in the second half of this part.

Automated tests within Truffle

How to run automated tests with the Truffle framework?

Well, first make sure the development blockchain network is still running (truffle develop) and then run truffle test in the MetaCoin folder. You should be getting an output like this:

Using network 'development'.Compiling ./contracts/ConvertLib.sol...
Compiling ./contracts/MetaCoin.sol...
Compiling ./test/TestMetacoin.sol...
Compiling truffle/Assert.sol...
Compiling truffle/DeployedAddresses.sol...
TestMetacoin
✓ testInitialBalanceUsingDeployedContract (55ms)
✓ testInitialBalanceWithNewMetaCoin (50ms)
Contract: MetaCoin
✓ should put 10000 MetaCoin in the first account
✓ should call a function that depends on a linked library
✓ should send coin correctly (75ms)
5 passing (619ms)

Great!

From the first line we can learn that it is using our development network (defined in truffle.js). Then it compiles not only the Contract(s), but also the tests and some help files for the tests (Assert.sol and DeployedAddresses.sol). After all this, it runs two test-suites:

  • TestMetacoin
  • Contract: MetaCoin

The first has 2 tests, the second has 3. All the tests are passing, and do this within 1 second. (hurray!)

So where can we find these test-suits? Both suites are defined in the ./test folder - let's start with ./test/TestMetacoin.sol, which has the following content:

pragma solidity ^0.4.2;import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/MetaCoin.sol";
contract TestMetacoin {function testInitialBalanceUsingDeployedContract() public {
MetaCoin meta = MetaCoin(DeployedAddresses.MetaCoin());
uint expected = 10000;Assert.equal(meta.getBalance(tx.origin), expected, "Owner should have 10000 MetaCoin initially");
}
function testInitialBalanceWithNewMetaCoin() public {
MetaCoin meta = new MetaCoin();
uint expected = 10000;Assert.equal(meta.getBalance(tx.origin), expected, "Owner should have 10000 MetaCoin initially");
}
}

This is a smart contract itself! The contract is called TestMetacoin, imports the Assert and DeployedAddresses statements (from truffle/Assert.sol and truffle/DeployedAddresses.sol), imports the MetaCoin smart contract, and has 2 functions (tests): *testInitialBalanceUsingDeployedContract() *testInitialBalanceWithNewMetaCoin()

As the name implies, the first of these tests checks (Asserts) that freshly deployed contracts set the owner balance to 10000. The second of these tests checks the same for new contracts. Both tests have the same Assert statement, which is also shown in the overview after running the tests.

Let’s change this value to 10 for the first test, and see if the test-suite still returns true. (we change the uint expected = 1000; to uint expected = 10;)

Running our tests then gives the following result:

Using network 'development'.Compiling ./contracts/ConvertLib.sol...
Compiling ./contracts/MetaCoin.sol...
Compiling ./test/TestMetacoin.sol...
Compiling truffle/Assert.sol...
Compiling truffle/DeployedAddresses.sol...
TestMetacoin
1) testInitialBalanceUsingDeployedContract
Events emitted during test:
---------------------------
TestEvent(result: <indexed>, message: Owner should have 10000 MetaCoin initially (Tested: 10000, Against: 10))---------------------------
✓ testInitialBalanceWithNewMetaCoin
Contract: MetaCoin
✓ should put 10000 MetaCoin in the first account
✓ should call a function that depends on a linked library
✓ should send coin correctly (57ms)
4 passing (644ms)
1 failing
1) TestMetacoin testInitialBalanceUsingDeployedContract:
Error: Owner should have 10000 MetaCoin initially (Tested: 10000, Against: 10)
at /usr/local/lib/node_modules/truffle/build/cli.bundled.js:319138:17
at Array.forEach (<anonymous>)
at processResult (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:319136:19)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:160:7)

As expected, one of the tests now fails, showing an expected result of 10, where it finds 10000. Let’s change it back to 10000 and make sure it runs without problems again.

The second test-suite is defined in /test/metacoin.js. The content of this Javascript file is:

var MetaCoin = artifacts.require("./MetaCoin.sol");contract('MetaCoin', function(accounts) {
it("should put 10000 MetaCoin in the first account", function() {
return MetaCoin.deployed().then(function(instance) {
return instance.getBalance.call(accounts[0]);
}).then(function(balance) {
assert.equal(balance.valueOf(), 10000, "10000 wasn't in the first account");
});
});
it("should call a function that depends on a linked library", function() {
var meta;
var metaCoinBalance;
var metaCoinEthBalance;
return MetaCoin.deployed().then(function(instance) {
meta = instance;
return meta.getBalance.call(accounts[0]);
}).then(function(outCoinBalance) {
metaCoinBalance = outCoinBalance.toNumber();
return meta.getBalanceInEth.call(accounts[0]);
}).then(function(outCoinBalanceEth) {
metaCoinEthBalance = outCoinBalanceEth.toNumber();
}).then(function() {
assert.equal(metaCoinEthBalance, 2 * metaCoinBalance, "Library function returned unexpected function, linkage may be broken");
});
});
it("should send coin correctly", function() {
var meta;
// Get initial balances of first and second account.
var account_one = accounts[0];
var account_two = accounts[1];
var account_one_starting_balance;
var account_two_starting_balance;
var account_one_ending_balance;
var account_two_ending_balance;
var amount = 10;return MetaCoin.deployed().then(function(instance) {
meta = instance;
return meta.getBalance.call(account_one);
}).then(function(balance) {
account_one_starting_balance = balance.toNumber();
return meta.getBalance.call(account_two);
}).then(function(balance) {
account_two_starting_balance = balance.toNumber();
return meta.sendCoin(account_two, amount, {from: account_one});
}).then(function() {
return meta.getBalance.call(account_one);
}).then(function(balance) {
account_one_ending_balance = balance.toNumber();
return meta.getBalance.call(account_two);
}).then(function(balance) {
account_two_ending_balance = balance.toNumber();
assert.equal(account_one_ending_balance, account_one_starting_balance - amount, "Amount wasn't correctly taken from the sender");
assert.equal(account_two_ending_balance, account_two_starting_balance + amount, "Amount wasn't correctly sent to the receiver");
});
});
});

Ok, let’s take a look at what’s happening here. First off, we import the definition of the MetaCoin by calling artifacts.require(). We assign this to the local MetaCoin variable, which we use in the tests to interact with the contract. The contract function has two arguments, a string defining the name of the test-suite (MetaCoin is passed here) and a function that is called with as argument the accounts in the test environment.

The it function is called several times here, to defined multiple tests in this test-suite. These tests are executed one-by-one in the order defined. The first test checks (again) if a newly created contract has 10000 coins in it. If this test is placed after that last test, in which 10 coins are transferred to the second account, it will fail.

Interesting to note is the chained actions in the last test here, also reading the balance of an account is an a-sync call when using Javascript, which can make the tests difficult to read.

I have created a pull request on the template repository with a rewritten test, making use of async and await which greatly increases the readability and maintainability of the tests. You can see it here: https://github.com/truffle-box/metacoin-box/pull/1 - don't forget to vote on it! 😄

In Part 3 of this guide, we will take a look at automating the tests, first we are going to interact with a smart contract from the browser.

Interaction with the contract from the browser

All fine, these interactions from the console — but we actually want a working website, right?! Right!

Let’s start with creating a new folder called “public” under “MetaCoin” and add “index.html” in it:

.
├── build
│ └── contracts
│ ├── ConvertLib.json
│ ├── MetaCoin.json
│ └── Migrations.json
├── contracts
│ ├── ConvertLib.sol
│ ├── MetaCoin.sol
│ └── Migrations.sol
├── migrations
│ ├── 1_initial_migration.js
│ └── 2_deploy_contracts.js
├── public <---- new
│ └── index.html <---- new
├── test
│ ├── TestMetacoin.sol
│ └── metacoin.js
├── truffle-config.js
└── truffle.js
6 directories, 13 files

And just put a basic HTML page in index.html, for now:

<html>
<body>
<h1>Hello</h1>
</body>
</html>

Next, install static-server, by running the following command: sudo npm -g install static-server. This gives us the ability to start a simple web-server super easy. 🦄

Let’s try it out with the public folder: static-server public/. This should give the following output:

options.index is now deprecated please use options.templates.index instead.
* Static server successfully started.
* Serving files at: http://localhost:9080
* Press Ctrl+C to shutdown.

We can safely ignore the first line, our server is successfully started and you can find it at http://localhost:9080!

Now it is time to add interaction with our contract, we will be doing this with ‘web3.js’. The easiest way to get it into our page, is to download the javascript file and importing it. It can be found at https://github.com/ethereum/web3.js/blob/develop/dist/web3.js, we will download it using wget. This step is optional, you can download it using any tool, just make sure it ends up in the public/ folder ;-)

First, install wget using brew install wget, if you haven't done this yet. Then navigate to the public folder and run wget https://raw.githubusercontent.com/ethereum/web3.js/develop/dist/web3.js. Your output should then be:

--2018-01-08 16:35:02--  https://raw.githubusercontent.com/ethereum/web3.js/develop/dist/web3.js
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 88166 (86K) [text/plain]
Saving to: 'web3.js'
web3.js 100%[===================>] 86.10K --.-KB/s in 0.04s2018-01-08 16:35:02 (2.10 MB/s) - 'web3.js' saved [495873]

Let’s now test if web3 can connect to our local blockchain, replace the content of index.html with the following:

<html>
<body>
<script src="./web3.js"></script>
<script>
var provider = new Web3.providers.HttpProvider('http://127.0.0.1:9545');
var web3 = new Web3(provider);
web3.eth.getAccounts((error, accounts) => {
document.write("Address of account 0: " + accounts[0]);
});
</script>
</body>
</html>

And refresh the page at http://localhost:9080. It should show:

Address of account 0: 0x627306090abab3a6e1400e9345bc60c78a8bef57

Well, now we have a connection with our local running ethereum blockchain 🚀, but not with our MetaCoin smart contract yet. Let’s get the balance of MetaCoin on this account next.

To interface with the deployed smart contract, we need the ABI, the Application Binary Interface, of the contract. This is a standard interface, generated from our Solidity file when it was compiled. More information on this can be found in the documentation: https://solidity.readthedocs.io/en/develop/abi-spec.html. For now, we have just included it (and the deployed address) in our html file. We will make this more flexible in less simplistic examples.

Replace the content of ./public/index.html with:

<html>
<body>
<script src="./web3.js"></script>
<script>
var MetaCoin = [
{
"constant": true,
"inputs": [
{
"name": "addr",
"type": "address"
}
],
"name": "getBalanceInEth",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "receiver",
"type": "address"
},
{
"name": "amount",
"type": "uint256"
}
],
"name": "sendCoin",
"outputs": [
{
"name": "sufficient",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "addr",
"type": "address"
}
],
"name": "getBalance",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "_from",
"type": "address"
},
{
"indexed": true,
"name": "_to",
"type": "address"
},
{
"indexed": false,
"name": "_value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
}
]
var provider = new Web3.providers.HttpProvider('http://127.0.0.1:9545');
var web3 = new Web3(provider);
web3.eth.getAccounts((error, accounts) => {
document.writeln("Address of account 0: " + accounts[0] + "<br />");
var MetaCoinContract = web3.eth.contract(MetaCoin);
var myMetaCoinInstance = MetaCoinContract.at('0xf25186b5081ff5ce73482ad761db0eb0d25abfbf');
var balance = myMetaCoinInstance.getBalance(accounts[0]);

document.writeln("Balance for account 0: " + balance.valueOf());
});
</script>
</body>
</html>

When you now refresh your page (http://localhost:9080/) you should see the following output:

Address of account 0: 0x627306090abab3a6e1400e9345bc60c78a8bef57
Balance for account 0: 10000

Wow, that is pretty nice — right! This is of course only the first step, once you are able to use the smart contract from javascript, as we just did, everything is possible!

Photo by Courtney Hedger on Unsplash, thank you for reading these posts!

Stay tuned for more parts in this series — we will take a look at automating everything and other ways of interacting with the smart contracts from webpages.

If you spot any errors, or have any suggestions on improvements: please leave either a comment here, or at the original Github page (found here). Also, if you liked these posts — don’t forget to clap! 👏

Extra: If you are into blockchain development, don’t forget to check out https://blockchain.company

--

--