Part 1: Solidity Contract

Start a New Project

Create a directory for the project:

$ mkdir projects projects/donations
$ cd projects/donations
$ populus init

Wrote default populus configuration to `./project.json`.
Created Directory: ./contracts
Created Example Contract: ./contracts/Greeter.sol
Created Directory: ./tests
Created Example Tests: ./tests/test_greeter.py

You just created a new populus project. Populus created an example contract called Greeter and some tests. To learn about the greeter example see the Quickstart.

We will need a local private blockchain. This local blockchain is an excellent tool for development: it runs on your machine, does not take the time to sync the real blockchain, does not costs real gas and fast to respond. Yet the local chain works with the same Ethereum protocol. If a contract runs on the local chain, it should run on mainnet as well.

Note

If you are familiar to web development, then running a local blockchain is similar to running a local website on 127.0.0.1, before publishing it to the internet.

We will create a a local chain we’ll name “horton”:

$ populus chain new horton
$ chains/horton/./init_chain.sh

INFO [10-14|12:31:31] Allocated cache and file handles         database=/home/mary/projects/donations/chains/horton/chain_data/geth/chaindata cache=16 handles=16
INFO [10-14|12:31:31] Writing custom genesis block
INFO [10-14|12:31:31] Successfully wrote genesis state         database=chaindata                                                                        hash=faa498…370bf1
INFO [10-14|12:31:31] Allocated cache and file handles         database=/home/mary/projects/donations/chains/horton/chain_data/geth/lightchaindata cache=16 handles=16
INFO [10-14|12:31:31] Writing custom genesis block
INFO [10-14|12:31:31] Successfully wrote genesis state         database=lightchaindata                                                                        hash=faa498…370bf1

Add this local chain to the project config.

$ nano project.json

The file should look as follows. Update ipc_path to the actual path on your machine (if you are not sure about the path, take a look at chains/horton/run_chain.sh).

{
  "version":"7",
  "compilation":{
    "contracts_source_dirs": ["./contracts"],
    "import_remappings": []
  },
  "chains": {
    "horton": {
      "chain": {
        "class": "populus.chain.ExternalChain"
      },
      "web3": {
        "provider": {
          "class": "web3.providers.ipc.IPCProvider",
        "settings": {
          "ipc_path":"/home/mary/projects/donations/chains/horton/chain_data/geth.ipc"
        }
       }
      },
      "contracts": {
        "backends": {
          "JSONFile": {"$ref": "contracts.backends.JSONFile"},
          "ProjectContracts": {
            "$ref": "contracts.backends.ProjectContracts"
          }
        }
      }
    }
  }
}

For more on the horton local chain see Running the Local Blockchain .

Everything is ready.

Add a Contract

Ok, time to add a new contract.

$ nano contracts/Donator.sol

Note

You can work with your favourite IDE. Check for Solidity extention/package. Atom.io has some nice Solidity packages.

In this example we will work with a very simple contract that accepts donations for later use. The contract will also handle the donations value in USD.

Since the ETH/USD exchange rate fluctates, typically upward, we want to track not only how much ETH the contract collected, but also the accumulating USD value of the donations at the time of the donation. If the ETH rate is rising, then we will probably see smaller donations in terms of Ether, but similar donations in terms of USD.

In other words, two donations of say $30 will have different amounts in ETH if the exchange rate changed between the donations. As a simple solution, we will ask donators to provide the effective ETH/USD exchange rate when they send their (hopefully generous) donations.

Here is the new contract code:

pragma solidity ^0.4.11;

/// TUTORIAL CONTRACT DO NOT USE IN PRODUCTION
/// @title Donations collecting contract


contract Donator {

    uint public donationsTotal;
    uint public donationsUsd;
    uint public donationsCount;
    uint public defaultUsdRate;

    function Donator() {
        defaultUsdRate = 350;
    }

    // fallback function
    function () payable {
        donate(defaultUsdRate);
    }

    modifier nonZeroValue() { if (!msg.value > 0) throw; _; }

    function donate(uint usd_rate) public payable nonZeroValue {
        donationsTotal += msg.value;
        donationsCount += 1;
        defaultUsdRate = usd_rate;
        uin inUsd = msg.value * usd_rate;
        donationsUsd += inUsd;
    }
}

Save the code to contracts/Donator.sol.

Quick Solidity Overview

Pragma: Every Solidity source should provide the compiler compatability: pragma solidity ^0.4.11;

Contract definition: The contract keyword starts a new contract definition, named Donator.

Note

Contracts names should follow class naming rules (like MyWallet, GoodLuck or WhyNot).

State variables: The contract has 4 state variables: donationsTotal, donationsUsd, donationsCount and defaultUsdRate. A state variable is defined in the contract scope. State variables are saved in the contract’s persisten storage, kept after the transaction run ends, and synced to every node on the blockchain.

Visibility: The public decleration ensures that all state variables and the donate function will be available for the callers of the contrat, in the contract’s interface.

Note

For the public state variables, the compiler actually creates an accessor function which if you had to type manually could look like: function total() public returns (uint) {return donationsTotal;}

Data types: Since we are dealing with numbers, the only data type we use here is uint, unsigned integer. The int and uint are declated in steps of 8 bits, unint8, uint16 etc. When the bits indicator is omitted, like int or uint, the compiler will assumes uint256.

Note

If you know in advance the the maximum size of a variable, better to limit the type and save the gas of extra memory or storage.

As of version 0.4.17 Solidity does not support decimal point types. If you need decimal point, you will have to manauly handle the fixed point calculations with integers. For the sake of simplicty, the example uses only ints.

Constructor: The function function Donator() is a constructor. A constructor function’s name is always identical to the contract’s name. It runs once, when the contract is created, and can’t be called again. Here we set the defaultUsdRate, to be used when the donator didn’t provide the effective exchange rate. Providing a constructor function is optional.

Functions: The donate function accepts one argument: usd_rate. Then the function updates the total donated, both of Ether and USD value. It also updates the default USD rate and the donations counter.

Magic Variables: In every contract you get three magic variables in the global scope: msg, block and tx. You can use these variable without prior decleration or assignment. To find out how much Ether was sent, use msg.value.

Modifiers: modifier nonZeroValue() { if (!msg.value > 0) throw; _; }. The term “modifier” is a bit confusing. A modifier of a function is another function that injects, or modifies, code, typically to verify some pre-existing condition. Since the donate function uses the modifier function donate(uint usd_rate) public payable nonZeroValue {...}, then nonZeroValue will run before donate. The code in donate will run only if msg.value > 0, and make sure that the donationsCount does not increase by a zero donation.

Note

The modifier syntax uses _; to tell solidity where to insert the modified function. We can of course check the include the modifier condition the original function, but a declared modifier is handy when you want to use the same pre-condition validation in more than one function.

Fallback: The weired function without a name, function () payable {...}, is the “fallback”. It calls donate, so when somebody just send Ether to the contract address without explicitly call donate, we can still accept the Ether. A fallback function is what the contract runs when called without an explicit function name. This happens (a) when you call a contract with address.call, and (b) when just send just Ether, in a transaction that don’t call anything.

Note

If a contract has a fallback function, any transaction or sending of ether to an address with code will result in it’s code being invoked.

Payable: function donate(uint usd_rate) public payable nonZeroValue {...} and function () payable {...} use the payable builtin modifier, in order to accept Ether. Otherwise, without this modifier, a transaction that sends Ether will fail. If none of the contract functions has a payable modifier, the contract can’t accept Ether.

Initial Values: Note that donationsTotal += msg.value; was used before any assignment to donationsTotal. The variables are auto initiated with default values.

Side Note

This Donator example is fairly simple.

If you are following the Ethereum world for a while, you probably noticed that many Ethereum projects are much more complex. People and companies try to use contracts to manage distributed activity among very large groups, assuming you need special, usually complex, code and strategies that defend against bad actores. Some noticeable initiatives are the decentrelized autonomous organizations (DAO), getting groups decisions where the voting rights are proportional to the Ether the voter sent to the contract, or crowd funding with Ether, initial coin offerings (ICO), feeds that send the contract up-to-date data from the “outside world”, etc.

Don’t let these projects intimidate you.

If you have a simple Ethereum based idea that is useful, even for you personally, or to family and friends, go ahead and implement it. A small group of people that already know each other and trust each other don’t need the complex overhead. Just make sure the contract code is correct. You can do really nice things, some are not possible without Ethereum.

We would be delighted to hear how it worked!

Interim Summary

So far you have:

  • Initiated a project
  • Initiated a local blockchain
  • Added a new contract

Great. Next step is compiling and first deployment.