A SmartContract best practice: Push, Pull, or Give?

In this post, I will talk about the trade off between writing secure smart contract and creating better user experiences. The blog post shows some code but hopefully it’s understandable even if you don’t write any code.

How long does it take for you to withdraw the payout?

The premise of my Dapp BlockParty is that “You will get deposit +more (aka. payout)if you attend”.

To receive the payout, however, you have to call the smart contract function withdraw(). You may be surprised how many people actually don’t take their money back.

The following chart shows how many days it takes for people to withdraw their payout. There have been nearly 100 people participated so far. Though more than half people withdrew within a day after the event was over, more than 10% of people didn’t take within seven days (and there are still people who haven’t taken the payout as I write this post).

The longest time it took to withdraw was 281 days

Why 7 days is important? Because the smart contract has a function called clear() which allows contract owner (me) to take all the left over and the default cooling period is set to 1 week .

You may ask “Why doesn’t the contract just send back to all the participants when the event is over?”.

Pull Over Push

Good question. That was actually my original implementation.

Replace payback to use withdraw” commit

However this had 2 problems so I changed the implementation.

  1. Sending ether back to all the participants could run out of gas.
  2. Sending ether to unknown addresses could lead to security vulnerabilities.

The second point is now best known as “Pull over Push” pattern and advocated by reputable blockchain organisations such as OpenZeppelin and Consensys.

Every Ether transfer implies potential code execution. The receiving address can implement a fallback function that can throw an error. Thus, we should never trust that a send call will execute without error. A solution: our contracts should favor pull over push for payments.
External calls can fail accidentally or deliberately. To minimize the damage caused by such failures, it is often better to isolate each external call into its own transaction that can be initiated by the recipient of the call. This is especially relevant for payments, where it is better to let users withdraw funds rather than push funds to them automatically. (This also reduces the chance of problems with the gas limit.) Avoid combining multiple send() calls in a single transaction.

By following these best practices, I changed payback to simply mark who entitles the payout and introduced withdraw() function which allows participants to take the payout.

However, now I have been getting different messages.

It’s true. Users should not really need to interact with smart contract more than they absolutely have to as people new to Smart contract tend to make mistakes.

So I asked in the exit survey whether users want smart contract to send the money back (with extra admin cost) and here is the response(response rate is about 20%).

I would say the result is a bit mixed. While over 50 % of people are up for the idea (26% yes + 15.8&5.3% yes if cheap), 30% of people rather do it by themselves.

Share or Give?

During Ethereum London November meetup, one of the participants also suggested something interesting.

Instead of sharing the no show deposits among participants, what if we donate? That’s actually a nice idea and I am a big fan of giving to a good cause (in fact my CodeUp asks participants to donate to local charity).

However, I have a bit of concern that donating the no show deposits to charity actually weaken people’s will to turn up because it goes to a good cause rather than random participants. Also, changing the payout distribution logic depending on events may dilute the unique and strong message BlockParty has.

The alternative idea

Then I came up another idea. Rather than changing the payout() logic to share or give, why don’t I change the clear() rule to take the money and send it back to a pre defined address. This can be either the participant’s address or other addresses such as me ( :-) ) or charity address.

This gives the maximum option for people to withdrawby themselves, admins to send the payout back (with fee), or give the money you are too lazy to take back to a good cause, while minimising the change on the smart contract itself.

I am quite excited by the idea but would like to hear everybody’s opinion before I go ahead and implement it. If you liked reading this post, I would appreciate if you can vote the following.