Case Study: Ethereum Dapp For Crowdfunded Loans

Olaf Tomalka
10 min readMar 24, 2017

--

Thanks to Stephanie Ecate
Screenshot from the end product

Abstract

Getline.in reached out to me asking to provide them with a completely working Ethereum application prototype for crowdfunded loans. The task was to create both back-end (Smart Contract) and front-end (Dapp). The end goal being a way to validate the market and understand the technology stack for further development. Since this was supposed to be quick and dirty we set-up a deadline of a week — what we build is what we end up with, no extensions. We ended up spending ~20 hours and the end-product can be seen on etherloans.netlify.com. For the website to work you need to have the Metamask plugin with an unlocked wallet (other Ethereum nodes that inject Web3 API should also work) and be on Ethereum’s test network.

First of all, what should be the high-level flow of such a loan?

  1. Request the loan by providing the funding goal, the investment period, the payback period and the goal interest rate
  2. The loan is created and listed starting with an investment period
  3. During the investment period, investors can put money for the loan. That amount can’t be yet taken out by either party
  4. If the funding goal is not reached, the investors should be able to withdraw their money and the Loan should fail
  5. Otherwise the debtor can withdraw the loan and the payback period begins
  6. If the debt is not paid the loan is defaulted and has failed
  7. If the dept is paid back the loan is successful
  8. After the payback period finished and the loan was successful the investors can withdraw their investment

Tech Stack

After we had the basics planned we could start thinking about what technologies to use.

A quick explanation what a Dapp really is: Dapp is just a normal website that can be hosted anywhere. By using Ethereum Javascript Web3 API you can interact with an Ethereum node installed either on your client’s computer, or any other public Node set-up on internet. That Node acts as a gateway to the Ethereum network and allows you to talk to the deployed smart contracts. Since non-local Nodes don’t have information about the client’s wallet we couldn’t use cloud ones to allow investments and withdrawals and opted for requiring a Node installed on the user’s computer.

In Dapp the back-end instead of living on some company owned server is your smart contract living on the blockchain. They’re written in Solidity (there are other Contract languages, but this is one is a lot more popular and nowadays the preferred way to write contracts). While you still need write Ethereum Contracts directly, frameworks such as Truffle or Embark provide additional value with easy deployments on networks, migrations for updating existing contracts and on-chain and off-chain unit testing. We ended up using Truffle supplemented with TestRPC Node. TestRPC allows faking of the whole blockchain, it simulates all transactions locally and in a speedy way. It’s perfect for testing before real deployment.

As for the front-end itself we decided on Angular 2. Alot of the community uses Meteor.js since it nicely fits into the Dapp model, but we were time-constrained and opted for something we knew better.

Smart contract

Smart Contracts’ code is immutable by design. Once you deploy something, it’s going to live in it’s current form on blockchain forever. Contracts are modeled after Object Oriented Programming, and thus their internal state can be changed by calling their methods. Such method calls are a blockchain transactions, they are mined and the Contract state is changed for every participating Etherum client, and so they need to run on every computer. This is costly and due to, by-design, Turing-completeness, we can’t know if they ever finish. Ethereum solves this problem by defining that every run instruction costs some amount of Ether (for simplicity sake, there’s abit more complex abstraction called Gas).

But Contracts also support pure functions, ones that don’t change the state of the Contract, which are not run on the blockchain, only on the Dapp-used Node instead. Such pure functions are used to get data and information about the Contract, for example to display it to the user.

Difference between calling a method vs a pure function

In our case each loan was a self-contained struct that can accessed by providing it a unique ID in the list of all the loans.

Definition of a loan

There are a few interesting tidbits here:

  • Structs are only a part of Solidity language, not the underlying virtual machine, so each property of a struct required it’s own additional getter Contract pure function
  • Ethereum VM doesn’t support neither floating nor fixed precision numbers, so we resorted to using per mils to keep track of fractions
  • There exists block.timestamp constant but it can be forced to drift from real time by the miners so secure contracts shouldn’t rely on it all that much
  • On the other hand, blocks can’t be easily mined faster or slower than needed and so setting up deadlines by getting the block.number constant is a better solution
  • You can’t iterate over mapping, an alternative to get all loans was needed

Ethereum natively supports events, which can be emitted when some interesting thing happens in the contract. Moreover, those events can be watched by the front-end, which allowed us to automatically refresh loan data when needed, instead of polling the blockchain.

Our supported events

Ethereum also strongly supports atomicity, if there’s any error, or throw, or your code runs out of gas — the state of the contract is not changed. This is often used to check for needed preconditions. For example you shouldn’t be able to invest in a loan after the investment period or if the goal was already met, and no Ether will be transferred.

Confirming the proper conditions are met

Even though the money was returned, miners had to run the program up to the point of a throw, and so Gas used to run the method is used up, and not returned to the caller. Also notice that only methods with a payable modifier can receiver Ether.

Sending Ether from a Contract is a tricky beast. Since the address to which we send money can also be a Contract, it’s methods also need to be run when money is received, and they run midway execution of the first Contract’s method, and so the second Contract can also call first Contract during it’s execution. This is a property called reentrancy. This means that if the Contract is written wrong, it’s state might be not be what you expect when a method is called. One scenario where this can be exploited is when the Contract updates it’s state only after sending money. It’s not hard to imagine an attacker who writes a Contract which calls ReceivePayment() each time it receives money, the execution would loop and all funds of the Loan contract could be siphoned out. That’s why it’s a good pattern to update the state beforehand.

Contract 2 attacks Contract 1 by constructing a recursive loop
Update the state before sending cash

Another way to handle reentrancy is to outright create a mutex on each method run — if any function is reentrantly called the Contract will throw. Also notice that the receiving contract can also fail during execution, so we propagate the error upwards not to sacrifice any Ether.

Testing

Truffle allows to write automated tests for Smart Contracts. First it deploys the contract to the provided network. In most cases to a fake one simulated by TestRPC, but the tests can also be run on real networks (and should, at least once before deployment).

You then interact with the Contract the same way as a Dapp would — in Javascript with Web3 APIs. Truffle has also a minor sugar abstraction layer on top — wrapping each call in a promise.

A very simple test that creates a new loan
Catching a throw inside the Contract was at first non-obvious

The biggest problem in testing was the ability to test deadlines of our Loans — you can’t speed up time, and TestRPC doesn’t allow jumping the block.number constant upwards. We ended up sidestepping the problem by exploiting inner workings of TestRPC — a single block is mined for every single transaction. We simply pushed as many empty transactions as many blocks we needed to move forward. This solution doesn’t work on real networks though. I don’t think there’s actually any way to test deadlines on a real network without inventing time travel.

A helper promise allowing to travel to the future

Front-end

Front-end was surprisingly pretty straightforward. A normal Angular 2 application with just a different way of getting data inside the Services (Or known as a Model in other frameworks/patterns).

Here again Truffle helped out — when compiling and deploying contracts it emits a JSON file with all the data needed to work with the contract through Web3. Since Webpack allows directly importing JSON files inside Javascript we could just get them from the compile directory, separating Truffle compilation and front-end compilation.

How to work with a Contract inside your browser

We wanted to get all existing loans and refresh the data after something interesting happens on the blockchain. Fortunately Web3 allows for event watching

Deployment

This part was the trickiest, I wanted to avoid spending the night downloading the whole blockchain, which weighs 50+gb. Light clients would allow us to deploy the contract without downloading everything. Most are in the works though. Only one right now is Metamask but it’s a Chrome plugin, which means it’s sandboxed and doesn’t listen for normal RPC commands. The only way to talk to it is through Web3 API on a website. Truffle can’t communicate with it to deploy and emit the JSON needed for the front-end!

What we ended up doing was to deploy to TestRPC Node and then open a new Chrome tab, manually deploy the compiled bytecode through Web3 and update the JSON to proper values, and recompile front-end.

Truffle’s emitted JSON

There were two things that needed changing for the Front-end to work with the JSON. Network ID and Contract address. Network ID is easy enough: web3.version.network returns “3” for the test network. Contract deployment was a longer process to read through and understand how to do it properly.

Deploying new Contract through Web3

Afterwards Contract’s address could be easily gotten from it’s instance and updated into the JSON.

Chrome’s Dev Tools are impressive, but not my favorite IDE

Improvement for next iterations

Right now the loan works on deadlines and before doing any operations, the Contract checks on which range of deadlines it is. This is error prone (especially to one-off block errors), introduces some duplication and is hard to read. Next iteration should make use of state machine pattern which is very easy to write and understand using Solidity’s modifiers, which would allow to only make an operation if in proper state instead.

We’ve run out of time so not all functionality of the smart contract is visible on the Dapp front-end. Withdrawing funds by investors and contract owner still needs to be done manually using Web3 APIs.

While the loans refreshes on the front-end properly when something interesting happens, such as an investment, or payback, it can’t detect when a period ends and refresh then. This is because the contract only runs on demand instead of being online all the time. There are some on-chain solutions such as Ethereum Alarm Clock, but the cheapest and easiest way would be to have Javascript timers on the front-end estimate when the period should end and refresh the loan then. One technical problem with that is the block mining time is variable on Ethereum, so we can only estimate the time when the deadline block is mined. This problem wouldn’t be visible to the user though. We could check the contract again after the timer ends behind the scenes and adjust timers accordingly.

To increase security, in the next iteration I would like to separate each loan into it’s own separate Contract. Currently if there’s a bug in withdrawal methods the attacker can siphon all the funds of each loan. If the loans were separate, only the affected’s loan funds could be stolen. I would design it by having a global aggregation Contract which would also deploy Loans. This design would also allow for the Loan code to be updateable in a sensible way — current loans would stay the same, guaranteeing the owner couldn’t meddle, but the new loans could have a newer version deployed during creation.

Conclusions

Programming on Ethereum blockchain is a very unusual work, breaking lots of normal conventions. Smart Contracts allow you to think of your web application in terms of events based infrastructure and not worry about providing a back-end. On the other hand, writing contracts is a lot harder than normal back-end. You need to have quite a lot more in mind — starting with security and thinking in terms of function reentrancy, through immutability needing for better planning up-front, and ending with having explicit costs to each action you do and trying to avoid dynamic loops and try to make everything work in a constant amount of time.

It was a lot of fun though!

So you’re really interested in Ethereum, huh? If you have a project in mind please do say hello@tomalka.me, and we’ll see what we can build together!

--

--