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).
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.
However this had 2 problems so I changed the implementation.
- Sending ether back to all the participants could run out of gas.
- Sending ether to unknown addresses could lead to security vulnerabilities.
Onward with Ethereum Smart Contract Security
If you’re new to Ethereum development, I recommend you read our Hitchhiker’s Guide to Smart Contracts in Ethereum…
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.
Recommendations for Smart Contract Security in Solidity
This page demonstrates a number of solidity patterns which should generally be followed when writing smart contracts…
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.