Note Lifecycle
This section walks you through the entire lifecycle of a note — from creation to successful withdrawal — and explains how GothamCash maintains privacy at each step.
1. ✍️ Note Generation (Client-Side)
A note is generated entirely off-chain in the frontend. It looks like this for a 0.1 BNB deposit:
gothamcash-01:<nullifier>:<secret>
<nullifier>
and<secret>
are two randomly generated 32-byte values.The commitment is computed as:
commitment = keccak256(nullifier || secret)
Only the commitment is ever sent to the blockchain.
The full note is saved locally by the user (e.g., in browser or file).
If the note is lost, funds are permanently unrecoverable.
2. 💰 Deposit
The user submits the commitment via:
contract.deposit(commitment)
Requires an exact amount of 0.1 BNB (or slightly more if fees are active).
The contract checks that:
The commitment hasn’t been used before.
The value sent matches expectations.
Emits a
Deposited(commitment, timestamp)
event.
💡 This action cannot be linked to a future withdrawal unless secrets are exposed.
3. 🕒 Waiting Period (Anonymity Set)
To improve privacy, the user should wait before withdrawing:
More deposits in the pool = better anonymity.
A delay breaks the link between the deposit and the eventual withdrawal.
The note is useless to others without both secrets.
4. ✅ Authorization
Before withdrawing, the user must authorize an address to claim the funds using:
contract.authorizeWithdrawal(
nullifier,
secret,
deadline,
signature
)
The frontend:
Parses the note.
Generates a message hash:
keccak256(nullifier, secret, recipient, deadline)
.Signs the message with the recipient's wallet (usually the user themselves).
The contract:
Verifies the ECDSA signature.
Links the commitment to the authorized address (cannot be changed later).
🛡️ This protects the withdrawal from being front-run by bots or malicious actors.
5. 🔐 Withdrawal
Once authorized, the recipient (or relayer) calls:
contract.withdraw(nullifier, secret)
The contract:
Recomputes the commitment and nullifier hash.
Verifies the commitment was deposited.
Verifies the nullifier hasn't been used.
Verifies that the sender matches the previously authorized address.
Transfers the funds (minus fee if active).
Marks both commitment and nullifier as used.
✅ Emits a Withdrawn(to, nullifierHash, commitment)
event.
6. 🗑️ Expiry & Reclaiming
If the funds are never withdrawn:
Commitments expire after 365 days.
The owner can recover them using:
reclaimExpired()
reclaimExpiredBatch(start, end)
Funds are transferred to the protocol owner (to support development).
Expired commitments are removed from circulation.
Anyone can check expiration status with:
isExpired(commitment)
🔐 Summary of New Flow
1️⃣
Generate note (nullifier
, secret
)
❌
Off-chain privacy
2️⃣
Hash to commitment
, call deposit()
✅
Record deposit
3️⃣
Wait for anonymity set to grow
❌
Privacy
4️⃣
Sign message, call authorizeWithdrawal()
✅
Secure withdrawal
5️⃣
Call withdraw(nullifier, secret)
✅
Claim funds
Last updated