I walked into ETHDenver 48 hours ago with near-zero understanding of Ethereum Smart Contract development, and walked away with an end-to-end DApp.
I had a lot of questions about Smart Contracts going in — what is it /the process of creating one / the standard build tools / the way to interact with one. I wanted to share some of my learnings here, hoping it will benefit other developers who want a walk-through of the process.
Be sure to have node ≥ 8 installed on your machine. This will make writing our async/await functionality easier in the future.
If you would prefer to follow along with the completed code: https://github.com/qimingfang/shares-contract
If you’re have an object oriented programming background, think of it like this:
new Object()
is an address (in memory)Truffle is a framework for developing Ethereum Smart Contracts. It provides a nice file structure layout, testing, console, debugging, and a handful of tooling.
npm install -g truffle
Starting a new truffle project is simple and straightforward.
$ mkdir shares$ cd shares$ truffle init
You’ll notice that truffle sets up 3 directories for you:
Migrations.sol
file already. This is needed in order to run migrations (to deploy your smart contract) later.There is also a truffle.config file. This is used to configure environments (which RPC node to use). Yes, you can use Truffle to deploy your Smart Contracts to mainnet and testnet.
Here is a bare bone hello world style Smart Contract. It is shamelessly taken from the uport-demo repo.
This contract is a wrapper around a mapping (address => uint)
object. You can think of a mapping
in Solidity as a Map<Address, Int> in Java, and NSDictionary in Swift.
This contract exposes 2 public methods: a getter and a setter. Simple enough, right?
Not only is it slow to deploy your contracts to the mainnet / testnet for testing purposes, but it’s also immutable and may cost you a considerate amount of money. It is therefore very important to adopt TDD when developing Smart Contracts.
We’ll use chai to test the contact, since chai gives us a lot of nice syntax for asserting that things are equal. Instead of importing assert
directly from node.js, and asserting equality with the junit styleassert.equal(a, b)
, we can instead leverage the more readable hamcrest style a.should.be.equal(b)
.
First, let’s install some dependencies
$ npm init$ npm install --save-dev --save-exact chai chai-as-promised chai-bignumber
Then we create 2 tests
Run truffle test
to make sure that you see the tests pass.
The smart contract is written and tested. We are confident of its business logic and correctness. Now it’s time to deploy this onto a local blockchain for testing.
First, let’s set up a local blockchain with ganache (download and install it). Ganache comes pre-poulated with 10 test accounts (with 100 ETH in each). Also note the RPC server URL. We’ll need it shortly.
Ganache
Next, we’ll need to create a migration
Finally, we need to add ganache into our truffle configurations. The port
should correspond to the port number from ganache.
Ready to deploy the contract to your ganache local blockchain?
$ truffle migrate --network development
You should start seeing the contract showing up under the Transactions
tab.
Ganache showing contract deployment
Contracts are more useful when you can deploy them onto public blockchains. To connect to a test net, we need to run another RPC client that points at an Ethereum Test Net (we will use Rinkeby for this, but there are a few more test nets available).
First, you’ll need geth.
Next, you’ll need to use geth to create some Rinkeby accounts
geth --rinkeby account new
List your Rinkeby geth accounts to make sure that you were able to create an account.
geth --rinkeby account list
Once that’s done, you’ll need some test ETH. You can follow the instructions on this faucet and claim some test Rinkeby ETH.
Once the transaction is confirmed, you should be able to see it on the rinkeby etherscan UI.
https://rinkeby.etherscan.io/address/<your address here>
To deploy contracts onto Rinkeby, you’ll need to start geth and point it at the rinkeby network. You’ll need to unlock the wallet you just created (with ETH from the faucet) so that truffle can interact with it.
./build/bin/geth --rinkeby --rpc --rpcapi db,eth,net,web3,personal --unlock="<your address here>"
For example, it would look like this for me
./build/bin/geth --rinkeby --rpc --rpcapi db,eth,net,web3,personal --unlock="0xcfd31d9ffb5bc1b1f4b7af99cd4792f2b11da185"
You’ll need to wait a bit for geth to sync blocks on the network. While that’s syncing, let’s set up another truffle environment for Rinkeby.
Be sure to replace the from
field to your account’s public key. The choice of gas is a bit arbitrary. Just choose a sufficiently high number that doesn’t exceed the block gas limit.
Here is how you can find out about what the block gas limit is
Your geth is probably still syncing. In the meantime we can take a look at truffle console and get an introduction to web3.
web3 is a js library (typically embedded client side into HTML) used to interface with smart contracts.
$ truffle console --network rinkeby$ truffle(rinkeby)> truffle(rinkeby)> web3.eth.getBlock('latest')
Here, you will see the latest block that your geth client has reported in. It has a lot of the contents we’ve heard about, such as gasLimit, nonce (for mining), parentHash (to form a chain), etc. Neat stuff.
You can find more docs on web3 here.
… buffering …
Alright, time to run the migration on Rinkeby!
$ truffle migrate --network rinkeby
As the contracts are deploying, you’ll notice that under the hood, it’s actually generating contract creation transactions on the Rinkeby testnet.
When you ran the migrations, you should have seen a contract address in terminal that points to the instance of our shares contract. You can also view this contract on etherscan.
Now that our contract is deployed, we can now interact with it using a simple web DApp:
/build/contracts/shares.json
Every time you click on the buy button, the url to the transaction will be posted to the console. You can click on the URL to watch your transaction complete. Once it completes, you can refresh the page, and see the number of shares increase.
Completed code here: https://github.com/qimingfang/shares-contract
In my opinion, despite the wide adoption, there are a lot of tooling that is missing in the solidity development space. Some (technical and non-technical) things I noticed:
truffle debug
did not really work for me.truffle console
. This is akin to testing node.js apps by going into node instead of using Postman or other HTTP clients.