Hongbao: ZK-based Web3 Donation Application


Hongbao is a ZK-based/Web3 donation application that anyone can setup a donation campaign while keeping the donors’ activities completely anonymous.

Application Type


Problem Statement

Today, more and more content creators make money through web2 applications (YouTube, TikTok etc). However, these apps usually take a big cut from their work. A possible solution is to embed Paypal donate button in their content. But this centralized solution introduces high fee and may cause privacy concerns on givers.

When people ask friends to do a favor on emergent finance need (for example: curing a disease) or organize a group giving (for example: wedding wish list). Such donation activities are done publicly, i.e., the requester and (probably) the donators can see who and how much each friend donate. This causes social pressure on some friends, who for some reason, may either don’t want to give or can’t afford the amount other friends do. It also introduces unhealthy competition environment and encourage displaying of loyalty by money.

ZK Solution

This application is based on Tornado.cash source code and migrated to latest circom (2.0.3) and snarkjs (0.4.16) for better performance and code quality. Asker uses this app to setup a campaign, then donor can transfer fund to this campaign anonymously. The requester can see how many people and how much in total made to this campaign but can never find out individual donation (who and by how much).

Technically, Hongbao uses zkSnarks to shield fund transfer, thus hides donors’ activity.

Use Cases

  • Tipping: Content creator embed a Tip button (similar to PayPal Donate Button) in their content. Audiences can tip the author (with token) without need to worry about revealing their identities and donation amount.
  • Charity: NGO setup a charity campaign to request fund from public to support a project. Not only donors can make donation anonymously but also thanks to the nature of block chain, all donations and withdrawals are transparent and traceable to public.
  • Money-Raising: Susan is organizing a money-raising campaign among her friends and will use the money to reimburse her medical expense. She setup a Hongbao campaign and send a link of this campaign to her friends via social network. Her friends can then make the donation without pressure.

Competitive Landscape

Paypay donate button is centralized and non-blockchain/crypto solution.

Proposal Ask

Hongbao will become community-driven and self-funded by its own DAO and relayer fees charged for each donation. In order to get this up and running, we will be requesting the $15k/year stable basic income to take care of initial development, later improvements and operations costs.

This ask will be in line with the laid down milestones as detailed below

  1. launching a feature-complete product on our testnet
  2. forming a DAO with 5-out-of-9 multisig
  3. launching on mainnet with audit
  4. attracting 1k daily active users (with launch video, full PR promotion)
  5. attracting 10k daily active users (with a detailed roadmap, governance process)

Road Map

Objective Date Status
MVP Mainnet Launch April 30, 2022 Done
Beta Testing May 31, 2022 Done
Tip Button and UI Improvement July 15, 2022 Pending
Smart Contract Audit July 31, 2022 Pending
Mainnet Launch August 31, 2022 Pending

External links

  1. Demo Video
  2. Hongbao Application on MainNet
  3. Hongbao Application on TestNet
  4. Circuits and Contracts
  5. Web UI
  6. Relayer

The application doesn’t seem to be working properly. When clicked into my own campaign, I was shown the template campaign “Simon’s…” even though my own campaign is named totally differently with a different description.

Thanks Cathie for this quick check. I will take a look tomorrow morning

Just take a quick look and the problem seems related to the new way I deployed the whole website to OSS (in order to spare a web server). I fixed this issue and made a new deployment. You are welcome to take a try.

Please note: once a donantion went through, the amount won’t appear in the campaign immediately. It take up to 1 hour by the relayer (run every 1 hour) to send the proofs in batches.

Can you explain a little bit how the current deposit process preserves anonymity? As far as Tornado cash is concerned, deposits are not anonymous there. Simply adding a relayer will also not make deposits anonymous as deposits can still be traced.

You are right, the deposit is not anonymous. But you can only track the fund transfer from specific donor address to a Hongbao contract. Because Hongbao contract (as a fund pool) is shared among many users, you cannot link the deposit with the specific donation campaign. To obscure this process further, relayer will only run once every 1 hour (adjustable parameter). Therefore, the best you can tell is during one hour, some users make some deposits and some campaign contracts get some fund transfers. But no 1:1 link among these 2 activities.

I guess an improvement is to allow user separates the deposit and donation activities completely. Like paypal, user can make deposit at any time (and get UTXO), and later once needed, she can make anonymous donation without the need to do a deposit first. However, this also means most times, user needs to take 2 steps to make a simple donation. Therefore, I incline to make this process as light as possible to lower the bar for user adoption.

I also love to know if there is any other way to shield deposit.

Correct me if I am wrong. My understanding of the contract so far is that HongBao.sol is a Tornado pool, in which deposits are not anonymous but withdrawals are anonymized by shielding the connections between deposits and withdrawals.

What I don’t understand is the following:

  • Since relayers are the ones responsible for withdrawing from the pool to the individual contract, why do these withdrawals have to be anonymous? What is the role of Tornado here?
  • The mixing of the deposits is purely done by obscuring through multiple transactions and relayer right? Is there any role that ZK plays here?

Would be great if you can clarify the design a bit. Thanks!

1 Like

Thanks for these excellent questions, Cathie! I also had similar confusions when I first started to learn tornado.cash :grinning:

Yes, Hongbao.sol is Tornado pool contract. There are four of these contracts deployed and each for a fixed amount (1, 10, 100, 1000 ONEs).

Please allow me to explain the design for the donation process:

  1. In User’s browser:

    1. User makes a deposit to Hongbao contract (the tornado pool), besides the fund, a commitment (computed as: hash(secrete, nullifier)) is also sent and stored in Hongbao contract’s Merkle tree.

    2. User generates a proof (for later withdraw) to prove the knowledge of secret and nullifier, and commitment inclusion. It also makes use of public inputs as: campaign contract address (to receive the fund), merkle root, relayer, fee etc. Note: the commitment is not revealed and thus by just looking at the proof and public inputs data, no one can tell who made the withdrawal request and which deposit is mapped to this withdraw.

    3. At present, user could simply send the proof to Hongbao contract by calling withdraw() method. After proof verification, Hongbao contract transfers the fund to campaign contract using the address specified in public inputs. Sounds good, right? However, there is one problem here: the withdraw() method consumes gas to do the work and this call is logged to blockchain. This means, in Honbao contract call log, one can see a user called withdraw() to transfer fund to a campaign contract address as specified in public input. Oops, the user’s good will is exposed publicly.

    4. To fix above problem, we introduce a relayer. Instead of sending the proof to Hongbao contract directly, user post the proof to a relayer and have the relayer finished the last shot. Of course, by paying an agreed upon fees.

  2. In Relayer:

Relayer is a trustless and decentralized web service. The only thing it does is receive proof data and public inputs from (browser) client and call Hongbao contract withdraw() method to initiate fund transfer. It cannot make any changes to the proof and public inputs without being caught by withdraw() verification process. I did 2 implementations, one for node.js and one for Function as a Service (FaaS) for AliCloud. As side note, relayer is part of business model for this project. Thanks to the almost zero gas fees in Harmony, it only consumes very small fraction of the whole fund to make the donation. Theoretically, anyone can make a relayer and deploy to the system to earn fee, as did by Tornado. Below is the process of relayer run as FaaS.

  1. One FaaS function listen on a http port and get the post from client, make some checks and save good data to OSS.

  2. Another FaaS function run every n hours (n is adjustable), check OSS path and if new files found, submit the data to Hongbao contract withdraw() method and pay the gas fee.

  3. Hongbao withdraw() does the verification and fund transfer work. It also transfers the fee to the designated relayer address.

  4. By leveraging relayer, we break the link from user/donor address to campaign address.

To answer your questions,

  1. I’m more of incline to think withdrawal is done by Hongbao contract, not relayer. Relayer, as dumb service only responsible for forwarding the proof data produced by browser. It is an anonymous service but also need to pass anonymous proof data in order to make the whole process untracable. It resolves the gas fee problem if we do the withdraw directly from the browser. Except refusing to make the withdraw(), relayer cannot do anything bad. Tornado is used here to generate commitment inclusion proof to convince Hongbao contract to make the transfer without the need to know who own the underline fund.

  2. Obscuring multiple transactions is a way to add difficulties to link users to donations through guess. I implemented this mechanism more like to solve the cold start problem. Assuming once debut, this app has few transactions (say 1 transaction every 1 minute in average) and if after deposit, the Relayer immediately do withdrawal (which takes at most 1 minute to complete), it is not too hard to link these 2 transactions because they are the only transactions in the past minute. Batch more transactions across 1 hour makes it more difficult to do the guess work. However, transaction batching it’s not the core idea of shield transfer. The core idea is to use ZK proof is to convince Hongbao contract that a fund is deposited by the proof producer without knowing who exactly made the deposit, and thus safe to transfer the fund per instructions. Once more users are using the app, we can safely remove the batch and as a bonus, campaign owner receives fund faster (in less than 1 minute).

Yeah, I understand the design. But even the one-hour time frame currently is a bit too optimistic (even as big as the real Tornado cash, funds are recommended to sit there for longer to withdraw), but that’s a solvable issue by adjusting the time anyways (though not ideal, as the utility will become lower).

Let me put this scenario here: If we are assuming the Campaign owner doesn’t know their friends’ wallet addresses, then there is no need for this whole app to exist, 'cause direct deposits are fine if they don’t know who is who anyways.

So the assumption must be that, the Campaign owner knows his friends’ wallet addresses. Then the obscuring process would be rather useless because everything that is deposited into the HongBao contract is public. It doesn’t matter whether multiple transactions are obscured together.

Say my friend knows my wallet address is 0x01, for example. If I only deposited once, say for 1 ONE, and the campaign also gains 1 ONE - it will be incredibly obvious. Even if I deposited multiple times to multiple campaigns for 1 ONEs, it will still be obvious that I have donated one 1 ONE to my friend’s campaign (or nothing at all). The obscureness is only meaningful if I donated multiple times, in different denominations.

You are absolutely right, Cathie. Poor utilization will make this app useless. I feel this also somewhat true for Tornado.Cash and Aztec. Please let me know if this is not the case.

My thinking to solve such dilemma was to increase the use cases. Not only to make it a useful tool for social donation but also to make it work like PayPal donate button, to support tipping to strangers (say: author of a content).

Another tricky (and a little nasty :blush:) solution came to my mind was to allow donor to specify a recipient address to receive the fund instead of using the default campaign contract address. User then can feel free to route the money to another address he owns. Thus, in your scenario, the campaign owner can see deposits made from a lot of friends but aren’t sure some friends just did loops (to save face) to transfer fund from one pocket to another.

I also believe the ultimate solution could be to make the deposit totally anonymous but not sure how this is possible.

The same pitfall doesn’t exist in Tornado Cash, since Tornado Cash aims to disconnect the connections between deposits and withdrawals - not between deposits and where they will go.

Your application works purely on the assumption that the Campaign owner doesn’t know the addresses of their own donors. But if the Campaign owner doesn’t know the address of their donors, this application is simply not needed.

I don’t think the trick you mentioned solves my doubt necessarily.

Poor utilization will make this app useless but the whole idea of the app is that users can deposit how much they want to donate and do so anonymously. The design pushes users into “using it poorly” if that’s what you want to call it

Not sure why you have the assumption that this app assume user doesn’t know their friends’ addresses. I actually want the user feel the opposit. :grinning:

I think Tornado Cash also have your mentioned utilization problem because I assume most users use Tornado as money transfer channel instead of a bank account and not comfortable to keep the money on Tornado pool for long time. Say if a deposit is made and then withdraws within a period of time (say couple of hours), and N withdrawals were made during this time frame. You can link this deposit to N withdrawals. The guess difficulty depends on N and it’s not that big even today. Based on their current statistics, it’s about 1-2 every hour. Not to mention this number was supposed much lower when they first release the product.

Regardless of potential problem in Tornado Cash, I do understand your concerns about how this app can realistically serve its purpose. I will take some time to think about this. Please do let me know if you have any suggestions. Thank you!

Thanks Martin for your comments. But I’m not sure I understand your point. Can you please elaborate a little big more?

We can agree to disagree on tornado having this design problem. Their only claim on what the app does it that is hides the link between depositor and withdrawer. They do so very transparently and have guides on how to anonymize your transaction properly.

Assumptions are not arguments, they’re just that, assumptions, and, btw, your assumption is a very naive one. Users of tornado cash are very privacy savvy persons since the only usage of tornado cash is this: to give you more privacy over your blockchain transactions.

Thanks Martin for your comments. But I’m not sure I understand your point. Can you please elaborate a little big more?

My point it the same as Cathie’s, let’s cite one of the problems you’re trying to solve as an example:

Let say Alice needs a donation for medical problems and requests donation through your app

Bob decides to help, and, according to your claims, he can donate however much he wants without having any social pressure.

However, Alice knows how blockchain works, afterall, she deployed her donation onto the blockchain and, thus, can easily search for transactions sent to the pool. She knows Bob’s address, he’s her friend afterall and thus can see exactly how much he donated

1 Like