Foreword
This actually surprised me as developers with good FED (Front End Development) skills actually have an advantageous starting point, so I decided to write this short post as a short guide on how to make the leap from WebApp to DApp development.
This post assumes, that you are fluent in JavaScript and the modern developments ecosystem/practices, familiar with the blockchain basics, and are comfortable working with a command-line interface.
The entire project can be found and cloned here.
The Contracts Language: Solidity
Still, Solidity does differ from JS by having static types, special keywords, function modifiers, events, contract inheritance and other language features, so a bit of a learning curve is required, for not a steep one though.
There are many free resources online for learning Solidity, for example this youtube series might serve as a good starting point. Again, if you’re a JavaScript developer you should learn the ropes very fast.
The Dev Ecosystem: Truffle
I find the Truffle suite to be the most convenient development framework, especially if you come from front-end background in which case you’d feel pretty comfy right away.
The suite is built around three main components:
Ganache – a local in-memory Ethereum blockchain for deploying contracts, running tests and commands and exploring the blockchain, and 10 pre-configured accounts holding 10 Ether each. It also exposes a CLI which could be used for test automation. Simply download the installable file and you’re ready to go.
Truffle – the main framework for developing smart contracts and DAPPs for Ethereum. This is a true piece of art, abstracting away a lot of the headaches pertinent to the development, testing and deployment of smart contracts, just to name a few:
- Scaffolding of an entire new project, contract, test etc. using its simple CLI
- Compiling the contracts (CLI> truffle compile)
- Easy contract deployment a.k.a. Migrations, similar to Laravel, Django, Nodejs’ Sequelize etc; (CLI> truffle migrate)
- A testing framework which can run directly against Ganache. It is based on Mocha and Chai so FEDs have near zero learning curve here (CLI> truffle test)
- A console for interacting/tinkering with your contracts (CLI> truffle console)
- A simple transactions debugger for stepping and printing values
- Ability to run external scripts to interact with contracts
To install Truffle simply run > npm install truffle -g
Drizzle – the icing on the cake. This is the perfect companion for the front-end piece of a DAPP, which includes state store integration (e.g. Redux, Saga) which keeps accounts, balances, contracts and transactions data synchronized with the store at all times (by actually subscribing to on-chain events).
It also provides a couple of convenience functions (wrapping web3.Contract methods) for reading data from the blockchain and executing transactions while keeping the store in sync, and even a few React common components.
Although the components are React oriented, the state store integration is applicable for any other frontend framework you might want to use (i.e. React, Angular, Vue.js).
Kicking the Tires
Preparation
- Make sure you have Node.js and npm installed
- Install and run Ganache (direct download)
- Install Truffle by running
> npm install truffle -g
- Create a project folder and cd into it:
> mkdir pay-me
> cd pay-me
- Initialize a new Truffle project:
> truffle init
- Open the project in your preferred IDE
- If you’re a Windows user: delete the file named truffle.js (to avoid ambiguity with the globally installed truffle command)
- Make sure Ganache is up and running
- Open the file truffle-config.js and replace its contents with the following:
module.exports = { networks: { development: { host: "127.0.0.1", port: 7545, // default Ganache port network_id: "*" // Match any network id } } };
Create a Smart Contract
- Run the command:
> truffle create contract PayMe
- Open the file contracts/PayMe.sol in your IDE
- Replace the file’s contents with the following:
pragma solidity ^0.4.22; contract PayMe { event FundingEvent(address indexed from, uint256 value); constructor() public { } // fallback method to accept transfers function () public payable { FundingEvent(msg.sender, msg.value); } }
Create a Migration
We will now create one for our PayMe contract.
- Run the following command:
> truffle create migration PayMe
- Open the file migrations/{some_number}_pay_me.js
- Replace its contents with the following:
const PayMe = artifacts.require('PayMe'); module.exports = function(deployer, network, accounts) { deployer.deploy(PayMe).then((instance) => { console.log(`PayMe has been deployed. It's address is: ${instance.address}`) }); };
Write Unit Tests
- Create a test file for the contract by running:
> truffle create test PayMe
- Open the file test/pay_me.js in your IDE
- Replace its contents with the following:
const PayMe = artifacts.require('PayMe'); const sendAmount = web3.toWei(0.001, "ether"); contract('PayMe', accounts => { it("should receive 0.01 ether", () => { return PayMe.deployed().then(instance => { return instance.send(sendAmount).then(result => { return web3.eth.getBalance(instance.address); }) }).then(balance => { assert.equal(balance.valueOf(), sendAmount, "contract balance doesn't equal 0.01 ether"); }); }); it("should raise a FundingEvent", () => { return PayMe.deployed().then(instance => { return instance.send(sendAmount); }).then(result => { const {args, event} = result.logs[0]; assert.equal(event, 'FundingEvent', "FundingEvent has not been fired"); assert.ok(args.from, "event has 'from' field"); assert.ok(args.value, "event has 'value' field"); }); }); });
Test our Contract
- Run the following command:
> truffle test
If all went well, you should see a similar output:
Using network 'development'. PayMe has been deployed. It's address is: 0xc0f3cbd4f206320f4cdfeb8593959555f803e7d8 Contract: PayMe √ should receive 0.01 ether (375ms) √ should raise a FundingEvent (42ms) 2 passing (470ms)
If you’ll now open the Ganache interface, you might notice the first account’s balance holding a bit less than 100.00 Ether, since during the first unit test we’ve transferred some 0.001 ether from this account to the PayMe contract.
Tinkering with the Contract
- At the root of the project, create a file named external.js
- Paste the following contents into it:
const artifacts = require('./build/contracts/PayMe.json'); const contract = require('truffle-contract'); const PayMe = contract(artifacts); PayMe.setProvider(web3.currentProvider); module.exports = callback => { PayMe.deployed().then(instance => { console.log(`Contract address = ${instance.address}`); callback(); }); };
- Install the truffle-contract dependency (required by the script) by running the following commands:
> npm init -y
> npm install –save truffle-contract
- Enter the console by running the following command:
> truffle console
- You should now be in the console (the prompt will change to truffle(development)>)
- In the console, let’s first ensure our contract is deployed by running:
> migrate –reset
- Run the external script:
> exec ./external.js
- You should now see a similar output:
Contract address = 0x22634fd051864b48752589ebabfabd924d6848ce
Congratulations!
That’s it! Hope you found this post a good entry point, and welcome to the blockchain!
– Zacky
Leave A Comment