I, along with my buddy Eric Tang, recently deployed and open sourced an early version of AuctionHouse, an auction platform for on-blockchain goods. It’s the first end-to-end decentralized application (DApp), that I had built, and I learned a lot of lessons along the way, so I wanted to share a few things that developers should know going in to the DApp development process.
For those who haven’t heard the term DApp before, check out this post on StackOverflow, which describes what decentralized applications are and how they run.
The tools that we used included:
- Solidity — scripting language for smart contracts on Ethereum
- Truffle — development framework for testing, deploying, and interacting with smart contracts from the web
- TestRPC — time saving local blockchain testing tool
- Geth — Ethereum node implementation
- Metamask — web wallet for signing transactions generated by your DApp
It’s possible that by using a different DApp stack, some of these tips may not apply.
Your UI needs to be adaptive to slow transaction confirmations
Transactions take a long time to confirm (~15–60 seconds), so if you’re waiting to get a confirmation before allowing your user to continue to take action, they’re going to be sitting around staring at a spinner for a long time. If you have code that looks like:
Then there’s a good chance they’ll be staring at a spinner for upwards of 60 seconds. Instead, think about a UI that shows the user that something is pending, assumes success, and then handles the case where the transaction fails later.
Similarly, when the user takes an action in your app which submits a transaction, they need to wait for the Metamask popup to appear, and then they have to hit approve. This adds more perceived latency and an extra action to their experience, and is especially painful if there are two or more transactions on chain, in response to one UI action in your app. So keep this in mind while designing your UX flows.
Beginner lessons from working with Truffle, Solidity, and Web3
None of these are groundbreaking, and most are discovered quickly by beginners, but it helps to read them in one place before you get started:
- If you have a function called
myFunc.call()will be read-only, will not use gas, and will return myFunc’s return value synchronously.
myFunc()will submit a transaction, use gas, write data, and immediately return a transactionId, but will not return the function’s return value.
- To know whether your transaction succeeded or not, call
getTransactionReceipt(transactionId), and see if the gasUsed == the gas you passed in. If so, your transaction most likely failed.
- You can not return an array of variable length from a solidity function. Instead fix the length or write a function that returns an element from the array at one index, and call it once for each element in the array.
- You can not return a struct from a function. You can return multiple values, so you can return each element in a struct, however there appears to be a limit that I couldn’t find documented. When trying to return ~13 attributes in a struct, we would get errors. If we removed the 13th, we’d be ok.
- In web3, some calls return synchronously and some return async, and some provide both options. But if you use Metamask, they don’t usually support the sync calls, so check the docs if things break on you when your users use MetaMask.
- Functions that accept ETH now need the
- Truffle tests seem great for contract unit tests, but were unpredictable and highly dependent on underlying blockchain state. This one probably comes down to best practices, which I need to learn. I saw a lot of tests that would pass sometimes and fail other times depending on the state of the TestRPC accounts and blockchain state.
- You can’t bundle two transactions into one confirmation as far as I can tell — this creates a lot of waiting for the user for confirmations.
Unlock Account seems insane
If you’re running a local Ethereum node instead of using a wallet like MetaMask manage your accounts, then you may unlock your account using the
personal.unlockAccount(account, password) method. This seems totally insane to me if you’re developing against your local node and running an RPC interface. Here’s why…
At this point, if your account is unlocked, and you’re running an RPC interface, then any browser based app that injects web3 can now connect to your local node and take unfettered control over your wallet to submit signed transactions to the Ethereum network. Essentially they can instantly drain your account of all ETH and any assets (tokens) secured by your wallet. And all they would need you to do is click a link to load a page in your web browser…or install their malware…or browser plugin/toolbar.
I think the
--rpccoorsdomain param on geth can be used to defend against the web based attack, but many of the online examples have you set it as
"*" anyway, which leaves you vulnerable. Be careful with
As such it seems like a way better idea to use Metamask or a DApp browser like Mist, which requires you to sign every transaction. It also seems like a good idea to use per-DApp accounts that contain as little ETH as necessary in order to transact in the DApp at that moment. Even better is a great management interface to easily move ETH around to these accounts, with confidence that it won’t get lost or messed up.
Even with a web wallet like Metamask however, approving a transaction can still be a terrifying experience because, unless you’ve read and audited the verified code for the transaction you’re signing, you have no idea what is going on underneath the hood in that transaction. More on this in another post, as we have some ideas for how the community can address this.
The community is highly accessible
Finally, one of the bright spots of getting involved in DApp development at this stage has been the interaction with the community. For each of the above listed tools, you can easily talk to the developers who are providing them through their Slacks, Gitters, Twitter, Github, Reddit, email, and meetups. I’m amazed at how tirelessly they answer questions for people building using their tools. If you fast forward 1–2 years from now, this may not be possible, but for now the community is small enough that it feels like most people know each other and are happy to help.
Count me as someone who’s also happy to help people get started, so email me at email@example.com or hit me up on twitter @petkanics if you have any questions as you’re getting going with DApp development.