Do you want to publish smart contracts on the Ethereum blockchain? We are going to use the Truffle suite to deploy our first! In this post we are going to build a trust fund holder where you can release funds to someone after a set time.

For a crash course on Ethereum and smart contracts watch this video, it helped me a lot. It has precisely the right information density. For more info on Truffle go to this site.

If you want to play around with your own local Ethereum blockchain use Ganache and take a look at this previous post on Ethereum development here to learn more. The complete code of this post can be found on github here.

Setup truffle

First off you need NodeJS (>= v8.9.4) for we are using the node package manager (npm) to install truffle.

npm install -g truffle

Truffle comes with a nice cli where you can create a project from a predefined example project.

mkdir trust-fund
cd trust-fund
truffle unbox pet-shop

Now open your new trust-fund project with your favorite editor. The directories we will focus on here are the contracts directory, containing all the smart contracts we write, and the migrations directory, where we define how to deploy the contracts on the blockchain. The latter is called migrations because a smart contract is just a transaction on the blockchain and mutates its state.

Screenshot 2020 06 26 at 10.11.34 - TRUFFLE SUITE

The rest of the project is structured like any other node project, with src, test, node_modules and of course a package.json file.

Inside the contracts directory we find a generated Mirgations.sol smart contract that keeps track of all the smart contracts we will create ourselves. The 1_initial_migration.js in the migrations directory is the first update on the blockchain that will be done. Further updates will be prefixed with 2, 3 etc. and will be executed in that order.

Note that you might want to install a solidity syntax plugin which are not standard at the time of this post. I will be using VSCode with the following plugin.

Screenshot 2020 06 26 at 09.27.31 - TRUFFLE SUITE

The smart contract

Let’s create our own contract. Let’s call it TrustFund.sol and place it in the contracts directory. We start the file with the version of solidity we want to use. After that we define a contract with a name.

pragma solidity ^0.5.0;

contract TrustFund {
}

Inside the contract we define a data structure that holds relevant information for the contract. A trustfund should have an amount and a date when it is released. We store this in the Future struct.

Now we want the contract to hold not only one trustfund so we define a map (mapping) from the address of the beneficiary to the Future information with amount and release time.

struct Future {
    uint256 amount;
    uint256 releaseTime;
}
mapping(address => Future[]) public trusts;

To interact with the contract we define functions like createTrust where you can initiate a trustfund by presenting the required information.

function createTrust(address payable _beneficiary, uint256 
  _releaseTime) public payable {

    trusts[_beneficiary].push(Future(msg.value, _releaseTime));
}

Note that the amount of the trustfund is collected from the ehter of the transaction of creating the trustfund. The payable keyword in the function definition points out this ether is available.

The last function we define is the release of the trustfund. The function can be called for a beneficiary address and the ether is transferred only if the release date is passed.

function releaseFund(address payable beneficiary) public {
    for(uint i = 0; i < trusts[beneficiary].length; i++) {
        if(trusts[beneficiary][i].releaseTime <= block.timestamp) {
            beneficiary.transfer(trusts[beneficiary][i].amount);
            delete trusts[beneficiary][i];
        }
    }
}

Note that the function can be called by anyone and has no effect if the release time is not yet passed.

Testing

As a smart contract developer we want to test our new contract. We want to ensure some behaviour before we deploy this to the production blockchain. Create the file test/trustfund.js and start off with defining the following dependencies.

const { balance, time } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const TrustFund = artifacts.require("./TrustFund.sol");

We need helpers for working with basic blockchain functionality like balances and the time. Chai is our testing framework and of course we want to import our created contract.

Now let’s create the setup for our first test. We set some ether, we pick an account from the given test accounts and we deploy our smart contract.

contract("TrustFund", function(accounts) {
  let weiToSend = web3.utils.toWei('1', 'ether');
  let beneficiary = accounts[1];
  let trustFundInstance;

  it("Funds cannot be released before release time", function() {
    return TrustFund.deployed().then(async function(instance) {
      trustFundInstance = instance;
      const balanceTracker = await balance.tracker(beneficiary);
...

Now for the real test we create a trustfund that releases in one day. We try to release it immediately and check that the fund is not released.

trustFundInstance.createTrust(
    beneficiary,
    (await time.latest()).add(time.duration.days(1)),
    { value: weiToSend }
);

trustFundInstance.releaseFund(beneficiary);
expect(await balanceTracker.delta()).to.be.bignumber.equal('0');

Note that we can send the ether along with the creation by defining the json object with the ether for the value.

I leave the test of the happy flow as an excercise. When you are done we can run the tests with the following command.

Screenshot 2019 11 21 at 08.21.45 - TRUFFLE SUITE

In the first line it says that we are using the development network. This is configured in the truffle-config.js file in our project.

Running

Truffle has development blockchain where we can run our create contract. (If you want to run it on Ganache please take a look here)

~/d/e/trust-fund λ truffle develop
Truffle Develop started at http://127.0.0.1:8545/

Accounts:
(0) 0x51e55ce12509b7cf9959d92964454df3e0fc08e0
(1) 0x484a2c65c69b2fe35435b4567ce520a3bb0562dc
(2) 0x9b645f717dba36e0ef4f9ee086d5f388931d5cda
(3) 0x5bda3acfe416cac8adb480144a5453afb6409982
(4) 0xc5bbb2d48e816a1ebe04cc02a45665a4f71860b5
(5) 0x9996aa1fbd3ca9a3e47e4d0b4eeb913fcf50dabe
(6) 0xbbc783c9a7de9e19c2a1879d29a56173b8ddc1ac
(7) 0xd81eae8b0d56c326fc9358a8c3bd830ac0a9dd7c
(8) 0x496ad352a38d9a51705be66b13e270b00db45662
(9) 0x9c2a870498374338cb29b76772751070a3b149fa

Private Keys:
(0) 833477989217b9f53d2f324e4d6f1330aa393a90caf110c832d07c478ad0d5d4
(1) bc11bc90a7776f80b5bf203b24aeac8b3baa042914618894d6f85633407080fd
(2) 5ff8cf16455a1fa2c51412897f4868747404f0f61c0cf9e772bd3487efadccfc
(3) 8f81578fe126a1c25a7201ae3d645e437ad703d0683d88817746e4262bdeed7e
(4) 196545ffb9e433f923255187fa8376ed4169996a4776d24de0049f74ec56c0f4
(5) e7cc67eeead6fde7f4c45a6c8c520811f491cda35499fcf364e69c9d5d3a9ca6
(6) 572d1d3b77b9d2192c4d79d2875b565cd6fcc2ebce93a94310f6f31ceb610ff1
(7) d0079224a56d3baf5150cbd4fc7799d29417af392c77849bb60268adec63c9d8
(8) cd4e20fffcfce871e76cc473cfb007493177d047c80675aa256353c465d534a6
(9) 60b81919f3a12679b73eded16c6bcfc4706a192b384d5c9d7e348e524de827a4

Mnemonic: sauce version awesome pill relax super tourist team decide obscure media success

⚠️  Important ⚠️  : This mnemonic was created for you by Truffle. It is not secure.
Ensure you do not use it on production blockchains, or else you risk losing funds.

truffle(develop)>

Here we see the addresses and private keys of the 10 accounts we got for running. Now we want to deploy our contract on this network. We script this in migrations directory. Create a migration script beginning with ‘2’ for they are run in sequence.

var TrustFund = artifacts.require("./TrustFund.sol");

module.exports = function(deployer) {
  deployer.deploy(TrustFund);
};

Inside the truffle develop prompt we can omit the truffle prefix.

truffle(develop)> migrate

We see the transactions in which te contracts are saved on the blockchain.

truffle(develop)> let instance = await TrustFund.deployed()
undefined
instance.createTrust(accounts[1], 1574319583, { value: 
    web3.utils.toWei('1', 'ether') })

Also here the transactions saving the trust to the blockchain is shown.

Now wait until after the epoch time is passed and we can release the fund to the beneficiary.

truffle(develop)> instance.releaseFund(accounts[1])
...
truffle(develop)> web3.eth.getBalance(accounts[1])
'101000000000000000000'

Hopefully you are now able to build, test and run your own smart contracts. Happy contracting!