This guide is much more technical than my last article in the series. However, I've found it hard enough to get into Ethereum development that I want to at least show how I do it.
My main project is The Impermanence of Space, a DMMOG. It is also complex, and many lesser tools simply don't cut it. I have tried frameworks, but having them fail--and preventing me from developing until some future bugfix or feature addition--is unacceptable. I now work without any particular Ethereum-based framework. (Although, as you will see, I do not object to using other kinds.)
Note that this is not necessarily the best way to do this, and some parts still have wrinkles. But I spent enough time on it I'd like everyone to know how do it if necessary.
Problem 1: An IDE.
Solidity and Serpent, the two most popular languages, are so new that few syntax highlighting exists for them. The good news is that Serpent is basically Python, but if you use Solidity...
I use EMACS--originally just for its solidity highlighting, but now for nearly everything. This article, for example, is being written in EMACS.
There is also highlighting for vim and even Visual Studio. ether.camp has its own web-based IDE, including a blockchain simulator and debugger.
But, like I said, I use EMACS.
IA! IA! GNUTHULHU FTAHGN!
I also use magit, since I already use git, too.
Problem 2: web3.js, and injection thereof.
Using web3 is not difficult. Getting it into your dapp is a different story. While Mist and MetaMask will happily dump a functioning web3 instance into your dapp, I started working before either was readily available.
The simplest way I found is to use npm to get web3, then use browserify to get web3 into the page. This also allows me to use a sane dependancy system for JS. Currently I use a custom express server for this task, but in the recent past I used beefy for the same purpose. If you're getting started, just use beefy--there's almost no configuration or boilerplate involved.
Once you've got it injected, the code to connect is simple enough.
var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'))
I then use dependency injection to spread it across my code.
At the moment this does not consider that a dapp browser almost certainly has its own version of web3. As I have yet to get to that point, I have not worried about it yet.
One line that has saved me so much sorrow.
web3.js has two forms of most commands: synchronous and asynchronous. The latter all use node.js-style callbacks. Rather than create one of the infamous Ziggurats of Doom, I use bluebird's .promisifyAll() to create a promise-based node.js. truffle-artifactor (formly Pudding) does this as well, and it is the intent of web3's developers to use Promises natively in future versions of web3.
I also promisify contract objects. This requires calling .promisify() on each instance, not on the factory itself. See this file for how I do it for a large contract.
Problem 3: A node (but not the .js kind)
Invariably in every programming project, there comes THAT BUG. The one that is responsible for hours of suffering, slight depressions in your desk and corresponding concussions, as well as the slow loss of sanity as you cry out unheard against a world of madness and dysfunctional code WHICH CLEARLY SHOULD JUST FREAKING WORK. (But doesn't.)
There's also just the every day testing of your code. Or, say, unit testing. Or when that code should just work, but doesn't. In any case, you'll want to test your own code regularly and throughly, considering how many bugs have slipped through all sorts of smart contracts.
In Ethereum, you might try using one of the main clients' private testnet forms to do this. While this is technically a good idea, you'll find yourself slowly losing the will to live as you struggle with allocating ether, pointless mining, unlocking accounts, repeating the same commands endlessly, etc.
Thankfully, there's a simpler way than running a real Ethereum node: TestRPC. All you need to do is run
testrpc and, congratulations, it just works. While it is not perfect, it has some features that real nodes do not--for example, reseting the chain, or adjusting time forwards.
Further, it's quick. No need to GPU mine, or even to fake mine.
TestRPC comes in a non-server form as well, which I use in my unit tests so as not to require running a server. Be warned that this version does not support synchronous requests, due to the nature of node.js. I have found that with proper coding (and bluebird) async-only code works perfectly fine.
Do not go gentle into that good night. Use TestRPC to rage, rage against the dying of the light.
A private testnest
TestRPC has its limits. At some point you'll want to try a real node, if for no other reason than to make sure there is no subtle bug with TestRPC.
Thankfully, you don't need an actual Ropsten node. You can run your own private testnet. Unfortunately, this is not so simple to set up, but you will want to try your code on an actual node one day.
I used to avoid unit testing. Then, after a hackathon in which I spent many useless hours in repeating the same few tasks, I have distinctly changed my mind. Now I insist both on automation, but also automated testing.
I use standard mocha to unit test, and have never had trouble. With testrpc, you can use just about any JS framework.
Problem 4: A toolchain
Turning a .sol into a contract on your private testnet is not a simple process, but unfortunately it's also the most important one. Here's the steps.
- Use solc to produce the ABI and bytecode of your contract.
- If the bytecode uses libraries, you'll have to link it at some point.
- Somehow send a transaction with the bytecode and get an address.
- Get the address and the ABI into your dapp's frontend.
- Connect all that with web3.js
- Congradulations! You now have a functional contract.
Note that a framework will do this all for you. Chances are, you'll find it simpler. That said, I personally do not use a framework, as I want total control over my system. Your desires may vary.
- gulp-run uses solc --combined-json to create a file called contracts.json in build/
- I use standard node require(), which happily reads a json, to get access to it.
- A complicated (and somewhat inelegant) module called universe.js reads it and exposes methods to create and link the contracts.
- gulp writes the address to another location in build/
- Once again, I use require() through browserify
- I reuse universe.js to create a web3 contract instance.
- I inject this instance all around my code.
This is the result of much accretion, and there's a still a few warts. Nonetheless, it works.
Problem 5: Debugging.
I don't have an answer :(
There are almost no functioning solidity debuggers. The sole exceptions, as far as I am aware, are remix/browser-solidity, which will do EVM-level step-by-step debugging, and ether.camp's, which I have never tried. I don't have either integrated into my workflow, and it's beyond my ability to do so. I've usually relied on commenting and uncommenting code as psuedo-breakpoints.
That said, this is one of the largest pain points in the entire ecosystem, and I have no doubt that it will be eased. Maybe not soon, but inevitably.
Problem 6: A frontend framework.
I've done HTML/JS applications without a framework. I am now done doing HTML/JS without a framework. .innerHTML just doesn't cut it. Nor, after a certain scale, does jQuery. The more complex your dapp, the more time you will spent spinning your wheels redoing what hundreds of programmers have done before.
The entire file is like 80 LoC
I just want to use a framework!
I get it. You just want to get started. That's fine. Here's some suggestions.
- Truffle is possibly the most popular. It's got nearly everything you need. Add TestRPC and you're good to go.
- Embark is very similar, and includes .
- dapple is a framework by the makers of MakerDAO. Among other features, it has a package manager and a method of unit testing Solidity code in Solidity.
- Populus is python based, but I've never tried it. Anyone in the comments did so?
There may be other frameworks, but these are the ones I know off the top of my head.
Ethereum development is not the simplest thing to get into. The nature of the bleeding edge is that it is sharp and covered with a certain bodily fluid. I possibly haven't helped myself by choosing not to use a framework--nonetheless, I am mostly satisfied with this system.
Next time, I'll get into development techniques for actually creating your game!