How it works
Three actors, one contract, two secrets. This page walks through the full lifecycle of a campaign and spells out exactly what is visible to whom.
Actors
| Actor | Holds | Visible on-chain |
|---|---|---|
| Creator wallet | Signs the create / close / rotate transactions | Yes — it is the campaign's creator |
| Claim key | An off-chain secp256k1 private key held only by the creator | Only its public address; it never sends a transaction |
| Donor wallet | Sends BNB to donate() | Yes — amount and sender are in the transaction |
| Collector wallet | Submits a claim() carrying a signature from the claim key | Yes as a transaction sender, but nothing ties it to the creator |
Lifecycle
1. Create
The creator's browser generates a fresh random private key — the claim key. Its address is passed to createCampaign(title, description, goal, claimKey) together with the campaign metadata. The contract stores the campaign, assigns it the next sequential id, and emits CampaignCreated. The private key is encoded into a claim code and shown to the creator exactly once.
2. Donate
Anyone calls donate(campaignId) with BNB attached. The contract increments the campaign's raised and balance and emits Donated(campaignId, amount). The BNB is held by the contract itself; there is no per-campaign vault, so the contract balance is simply the sum of all unclaimed balances.
3. Claim
To collect, the creator opens the claim page from any wallet — ideally a brand-new one. The page reads the campaign's current nonce, builds an EIP-712 message Claim(campaignId, recipient, amount, nonce), signs it locally with the claim key, and the connected wallet submits claim(campaignId, recipient, amount, signature). The contract recovers the signer, checks it equals the stored claim key, bumps the nonce, decrements the balance and transfers the BNB. It emits Claimed(campaignId, amount) — deliberately without the recipient.
4. Manage
The creator wallet can setActive(id, false) to stop accepting donations (and reopen later), and rotateClaimKey(id, newKey) if the code was lost or leaked. Rotation bumps the nonce so signatures from the old key are rejected immediately.
What stays private
- The recipient of every claim. It appears only as the destination of an internal BNB transfer inside the claim transaction, which is sent by an unrelated wallet.
- The claim key itself. It signs messages off-chain and is never used as a transaction sender, so it has no on-chain history to analyse.
- Which campaign funded which withdrawal. All balances are pooled in one treasury; a claim transfers BNB out of the shared pool.
What does not stay private
- The creator wallet. It is stored in the campaign and visible to anyone. Create campaigns from a wallet you are comfortable associating with the cause.
- Donations. Amount and sender are ordinary transactions. See donor privacy.
- Amount correlation.
Claimedevents include the amount and campaign id. If a campaign holds a unique balance and a claim for exactly that amount lands in a fresh wallet, an observer can guess the link. Claim in partial, round amounts and avoid draining a campaign in one transaction if this matters to you. - Gas. The collector wallet needs BNB for gas. If you fund it from the creator wallet you re-create the link you were trying to avoid.