Traditional public fundraising methods on blockchains, like initial DEX offerings (IDOs), expose participant addresses and contribution sizes, leading to front-running, whale manipulation, and privacy concerns. Encrypted commitment schemes solve this by introducing a two-phase process: a commit phase and a reveal phase. During the commit phase, participants send an encrypted hash of their intended contribution (the commitment) to the smart contract. This hash, typically generated using a cryptographic function like keccak256(amount, salt, address), locks in their intent without disclosing the actual amount or identity on-chain.
Launching a Fundraising Round with Encrypted Commitment Schemes
Introduction to Encrypted Commitment Fundraising
Encrypted commitment fundraising is a privacy-preserving mechanism for launching capital raises on-chain, allowing participants to commit funds without revealing their identity or contribution amount until a predefined reveal phase.
The core cryptographic principle is that a commitment is binding and hiding. It is binding because once submitted, a user cannot change the committed amount. It is hiding because the public commitment hash reveals no information about the underlying data. Only during the subsequent reveal phase do participants submit the original plaintext data (amount and salt). The contract then hashes this data and verifies it matches the earlier commitment. Invalid or unrevealed commitments are forfeited, ensuring the final pool consists only of verified, intended contributions.
This mechanism is critical for fair launch dynamics. It prevents large investors (whales) from seeing the total committed capital in real-time and adjusting their bids to dominate the allocation. Projects like Aztec Protocol and Semaphore have pioneered similar privacy primitives. In practice, a fundraising contract will define commit and reveal periods, often lasting 24-48 hours each, and specify a token distribution mechanism (e.g., pro-rata based on revealed amounts) for the final phase.
Implementing this requires careful smart contract design. Key functions include commit(bytes32 _commitment) for submitting hashes and reveal(uint256 _amount, bytes32 _salt) for disclosing details. The contract must securely store commitments in a mapping (mapping(address => bytes32) public commitments) and validate reveals against them. A critical security consideration is the use of a sufficiently random salt to prevent brute-force attacks guessing the amount before the reveal.
For developers, integrating encrypted commitments adds complexity but significantly enhances fairness. Tools like ETHDenver's commit-reveal scheme or OpenZeppelin libraries can provide audited templates. The final outcome is a capital formation process that reduces information asymmetry, mitigates front-running bots, and creates a more equitable distribution for early-stage projects, aligning with the decentralized ethos of Web3.
Prerequisites and Setup
Before launching a fundraising round with encrypted commitments, you need to understand the core cryptographic primitives and set up your development environment. This guide covers the essential tools and concepts.
Encrypted commitment schemes, like zk-SNARKs or zk-STARKs, are the cryptographic backbone for private fundraising. A commitment scheme allows a user to commit to a value (e.g., an investment amount) without revealing it, and later prove they committed to that specific value. For fundraising, this enables investors to submit bids confidentially during a round, preventing front-running and information leakage. You'll need a foundational understanding of zero-knowledge proofs and elliptic curve cryptography to proceed.
Your primary tool will be a zero-knowledge proof framework. For Ethereum and EVM-compatible chains, Circom with the snarkjs library is a common choice for writing circuits and generating proofs. Alternatively, consider Noir for a more developer-friendly experience or Halo2 for advanced, recursive applications. Ensure you have Node.js (v18+) and npm installed. Initialize a new project and install your chosen ZK toolkit, for example: npm install -g circom snarkjs.
You will also need access to a blockchain development environment. For testing, use a local network like Hardhat or Foundry. Configure your project to interact with a smart contract that will verify the ZK proofs. The contract will require a verification key generated from your circuit. This setup separates the off-chain proof generation (handled by your application) from the on-chain verification (a gas-efficient operation).
Design your circuit logic to enforce the fundraising rules. The circuit should take private inputs (the investor's secret bid and a randomness factor) and public inputs (a public key or a commitment hash). It outputs a commitment (e.g., a Pedersen hash) that is posted on-chain. The circuit must also generate a proof that the commitment is valid according to your rules—without revealing the bid. Test this circuit extensively offline before deploying any contracts.
Finally, prepare your deployment strategy. You'll need a verifier smart contract compiled from your circuit's verification key. Use a tool like snarkjs's zkey command to generate the Solidity verifier. Deploy this contract to your chosen network (testnet first). Your application's backend will use the proving key to generate proofs for investor commitments, which are then submitted alongside the commitment to the verifier contract.
Core Cryptographic Concepts
Learn the cryptographic primitives that enable secure, fair, and private fundraising mechanisms like those used in token sales and DAO treasury raises.
Commitment Schemes
A commitment scheme allows a user to commit to a value (e.g., a bid amount) while keeping it hidden, with the ability to later reveal it. This is foundational for private fundraising.
- Key Properties: Hiding (commitment reveals nothing about the value) and Binding (cannot change the committed value later).
- Real-world use: Used in zk-SNARKs for private transactions and blind auctions to prevent front-running.
- Common construction: Use a cryptographic hash function like SHA-256 or a Pedersen commitment in elliptic curve groups.
Zero-Knowledge Proofs (ZKPs)
ZKPs allow one party (the prover) to prove to another (the verifier) that a statement is true without revealing any information beyond the validity of the statement.
- Crucial for privacy: Enables proving you have sufficient funds for a contribution or meet KYC criteria without exposing your wallet balance or identity.
- Types: zk-SNARKs (succinct, require trusted setup), zk-STARKs (transparent, no trusted setup), and Bulletproofs (efficient for range proofs).
- Application: Aztec Network uses ZKPs for private DeFi, and Mina Protocol uses recursive zk-SNARKs for a constant-sized blockchain.
Multi-Party Computation (MPC)
MPC enables a group of parties to jointly compute a function over their private inputs without revealing those inputs to each other.
- Use case: A decentralized fundraising round where multiple investors compute the total funds raised without any single party learning individual contribution amounts.
- Threshold schemes: Often combined with secret sharing, where a secret (like a decryption key) is split among participants.
- Real protocol: The Keep Network's tBTC v2 uses MPC to manage a distributed signer for Bitcoin custody.
Verifiable Delay Functions (VDFs)
A VDF requires a prescribed number of sequential steps to compute, but the output can be verified quickly. This creates a transparent and fair timing mechanism.
- Preventing manipulation: In fundraising, a VDF can be used to generate a random beacon for a fair lottery or to enforce a mandatory "waiting period" after commitments are made, preventing last-second sniping.
- Implementation: Chia Network uses VDFs for its proof-of-space-and-time consensus. Ethereum's RANDAO is exploring VDF integration for stronger randomness.
Elliptic Curve Cryptography (ECC)
ECC provides the underlying mathematical framework for most modern cryptographic primitives used in blockchain, offering high security with smaller key sizes.
- Core operations: Digital signatures (ECDSA, EdDSA) for authorizing transactions and key agreement (ECDH) for secure channels.
- Commitment schemes: Pedersen commitments rely on the discrete log problem on elliptic curves to create perfectly hiding commitments.
- Standards: secp256k1 is used by Bitcoin and Ethereum. Ed25519 is used by Solana and other high-performance chains for faster signatures.
Step 1: Implementing the Commit Phase
The commit phase is the cryptographic cornerstone of a private fundraising round, allowing participants to submit binding pledges without revealing their contribution amount.
The commit phase establishes a secure, binding promise from participants. Using a commitment scheme, a user generates a cryptographic hash of their intended contribution amount and a secret random value, known as a nonce. This hash, called a commitment, is submitted to the smart contract. The critical property is that the commitment reveals nothing about the underlying amount, yet it is cryptographically bound to it. Once submitted, the commitment cannot be altered, ensuring the user is locked into their pledge for the reveal phase.
In practice, this is implemented using a hash function like keccak256 or sha256. A user with a contribution of amount = 1000 and a secret nonce = 0x123abc... would compute commitment = keccak256(abi.encodePacked(amount, nonce)). Only this hash is sent on-chain. The abi.encodePacked ensures deterministic encoding for the EVM. This process provides hiding (the amount is secret) and binding (the user cannot later reveal a different amount/nonce pair that hashes to the same commitment).
The smart contract must manage the commit window and store commitments. A typical commit function would accept the bytes32 commitment as a parameter, check that the current block time is within the commit period, and map the commitment to the sender's address. It's crucial to prevent replay attacks and ensure each address can only submit one commitment. This phase is purely about collecting these cryptographic promises, with no funds transferred yet.
Security considerations are paramount. The nonce must be generated securely and kept private; using block.timestamp or blockhash is insecure. Participants should use a cryptographically secure random number generator. Furthermore, the contract must be designed to prevent front-running of commitments, though the secret nonce inherently protects the contribution value itself from being copied by an adversary.
For developers, integrating this with a frontend involves generating the commitment off-chain. A common pattern uses Ethers.js or Viem: const nonce = ethers.randomBytes(32); const commitment = ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(['uint256', 'bytes32'], [amount, nonce]));. The user then signs a transaction calling commit(commitment). The amount and nonce are stored locally (e.g., in browser storage) for the subsequent reveal.
Step 2: Implementing the Reveal Phase
After the commitment period ends, participants must reveal their pledged amounts to finalize the round. This phase validates commitments and calculates the final distribution.
The reveal phase is the second critical stage where participants submit the original data that was hashed during the commitment step. For a fundraising round, this means each contributor sends their actual pledge amount and the random salt they used. The smart contract will then recompute the keccak256 hash of abi.encodePacked(msg.sender, amount, salt) and verify it matches the stored commitment from the previous phase. This proves the participant acted in good faith and locks in their contribution. Any commitment that fails this verification is invalidated, protecting the round from malicious actors who might try to change their pledge.
A common implementation involves a reveal function that accepts the pledge amount and salt as arguments. The contract logic is straightforward: it recalculates the hash and checks for a match in the commitments mapping. It's crucial to enforce that reveals can only happen within a specific time window after the commitment deadline. Here is a simplified Solidity example:
solidityfunction reveal(uint256 amount, bytes32 salt) public { require(block.timestamp > commitDeadline && block.timestamp <= revealDeadline, "Not in reveal phase"); bytes32 computedCommitment = keccak256(abi.encodePacked(msg.sender, amount, salt)); require(commitments[msg.sender] == computedCommitment, "Invalid reveal"); require(!hasRevealed[msg.sender], "Already revealed"); hasRevealed[msg.sender] = true; finalPledges[msg.sender] = amount; totalRaised += amount; }
After all valid reveals are processed, the contract enters a finalized state. The totalRaised variable reflects the sum of all successfully revealed pledges. This amount is now securely locked and ready for the project to claim, typically after a timelock or via a multisig for added security. The commit-reveal scheme successfully prevents front-running and last-minute sniping by hiding economic intent until the reveal deadline, creating a fairer fundraising environment. Projects using this pattern include early DAOs and fair launch mechanisms on networks like Ethereum and Arbitrum.
Using Pedersen Commitments for Hiding
A guide to implementing a private fundraising round where investors can commit capital without revealing their contribution amount until the round concludes.
A Pedersen commitment is a cryptographic primitive that allows a user to commit to a value (like an investment amount) without revealing it, while providing the ability to later prove the committed value. It is binding (you cannot change the committed value later) and hiding (the commitment reveals zero information about the value). For a fundraising round, this enables a trust-minimized process where the total funds raised are verifiably correct, but individual contributions remain confidential until a predefined reveal phase. This protects investor privacy and can prevent front-running or strategic bidding based on public information.
The scheme relies on elliptic curve cryptography. To commit to a secret value v, a user also selects a random blinding factor r. The commitment C is computed as C = v*G + r*H, where G and H are independent, publicly known generators on an elliptic curve (like secp256k1). The security relies on the Discrete Logarithm Problem; given C, it is computationally infeasible to determine v or r. The pair (v, r) is the opening that must be revealed later to prove the commitment was valid. In a fundraising context, v would be the pledged amount, often represented as a scalar.
To launch a round, you deploy a smart contract that accepts Pedersen commitments. Investors call a commit(bytes32 commitmentHash) function, submitting the hash C. The contract stores these commitments. Crucially, it cannot validate the amount v at this stage—it only records the public commitment. This phase continues until the fundraising deadline. A zero-knowledge proof system like Bulletproofs can be integrated to allow investors to prove their committed value lies within an acceptable range (e.g., between the minimum and maximum investment limits) without revealing the exact amount, adding an extra layer of compliance and trust.
After the commitment phase ends, a reveal period begins. Each investor must submit their secret opening (v, r) to the contract via a reveal(uint256 value, uint256 blindingFactor) function. The contract recomputes the commitment C' = v*G + r*H and verifies it matches the previously stored commitmentHash. If it matches, the pledged amount v is recorded as the investor's final contribution. Contributions that are not revealed are forfeited. The contract can then sum all revealed v values to calculate the total funds raised, which is now transparent and verifiable by all participants.
Implementing this requires careful handling of the cryptographic parameters and secure random number generation for the blinding factor r. A common vulnerability is reusing r for different commitments, which can leak information. Use a cryptographically secure random function. In Solidity, you would typically perform the elliptic curve operations off-chain in a client (using a library like elliptic in JavaScript) and only send the computed points to the chain. The contract, using precompiles like ecAdd and ecMul (or a library like EIP-1962), verifies the recomputed commitment during the reveal. This keeps gas costs manageable.
This pattern is used in privacy-focused auctions and dark pool-like mechanisms on-chain. It provides a balance between necessary transparency (verifiable total outcome) and participant privacy (hidden bids). Future enhancements could integrate zk-SNARKs to allow the contract to verify the sum of all committed values meets a funding threshold without any individual reveals, enabling a fully private conditional fundraising round. For developers, libraries such as pedersen-commitments in Rust or ffjavascript provide the necessary building blocks.
Commitment Scheme Comparison
A comparison of cryptographic commitment schemes suitable for private fundraising rounds, evaluating security, performance, and implementation complexity.
| Feature / Metric | Pedersen Commitments | zk-SNARKs (Groth16) | Merkle Tree of Commitments |
|---|---|---|---|
Cryptographic Assumption | Discrete Logarithm | Knowledge of Exponent / QAP | Cryptographic Hash (SHA-256) |
Trust Setup Required | |||
Proof Size | 64 bytes | ~200 bytes | O(log n) * 32 bytes |
Verification Gas Cost (approx.) | ~45k gas | ~250k gas | ~80k - 150k gas |
Hiding Property | Unconditionally Hiding | Computationally Hiding | Computationally Hiding |
Binding Property | Computationally Binding | Computationally Binding | Computationally Binding |
Reveal Complexity | Simple (value, blinding factor) | Complex (witness generation) | Simple (leaf value, path) |
Suitable for Batch Verification |
Common Implementation Pitfalls and Attacks
Encrypted commitment schemes like MACI are used for private voting and quadratic funding, but implementation errors can compromise privacy or funds.
Incorrect ZK-SNARK Circuit Design
Flaws in the zero-knowledge circuit logic can leak voter intent or allow invalid state transitions. Common issues include:
- Insufficient constraints that fail to enforce all business logic rules.
- Public input mismanagement that accidentally reveals a user's commitment.
- Incorrect nullifier logic, enabling double-voting or preventing legitimate vote tallying.
Always audit circuits with tools like circom's
circomspector perform a formal verification.
Private Key Leakage in Message Submission
Users encrypt commands with a shared public key. If the application frontend or SDK mishandles the ephemeral key pair, the message can be decrypted by an attacker.
- Browser extensions or malicious scripts can intercept keys before encryption.
- Insecure random number generation for ephemeral keys weakens encryption.
- Logging encryption parameters in server-side analytics. Use hardened client libraries and consider trusted execution environments for sensitive key operations.
Coordinator Centralization and Censorship
The coordinator role, which decrypts and processes votes, is a single point of failure.
- A malicious or compromised coordinator can censor votes by refusing to process specific messages.
- They could also delay the final tally indefinitely, freezing funds. Mitigations include using a coordinator committee with threshold decryption (e.g., using gnosis-safe and tss-lib) or implementing a coordinator rotation mechanism.
Time-Based Attacks and Front-Running
The timing of message submission and publication can be exploited.
- Front-running the subsidy calculation: In QF rounds, an attacker observes early votes and submits large, matching donations to skew the matching pool.
- Late publication attacks: A coordinator can see the cleartext results before publishing the proof, potentially altering behavior. Defenses include strict, immutable submission deadlines and using a commit-reveal scheme for the coordinator's result publication.
Insufficient Parameter Sizing and Overflows
Fixed-size arrays and finite field arithmetic can cause silent failures.
- Exceeding the maximum number of messages or users defined in the circuit causes the system to halt.
- Voice credit overflow: In quadratic funding, calculating
sqrt(credits)can overflow if not constrained to the correct bit-length. - Gas limits on-chain can prevent proof verification if the circuit is too large. Rigorously test with maximum load parameters and use safe math libraries.
Testing the Fundraising Contract
This guide walks through the process of launching and testing a fundraising round using encrypted commitment schemes on the blockchain.
Before launching a live fundraising round, thorough testing of the smart contract is essential. This involves deploying the contract to a test network like Sepolia or a local Hardhat node. The core functionality to test includes the initialization of the round with parameters like the funding goal, duration, and the public key for the commitment encryption. You must also verify that the contract correctly enforces the commitment phase, preventing premature contribution reveals. Use a testing framework such as Hardhat or Foundry to write comprehensive unit tests that simulate user interactions.
The encrypted commitment scheme is the security cornerstone. In testing, you simulate a user's journey: they generate a secret, hash it to create a commitment, and encrypt this commitment with the round's public key before sending it to the contract via the commit function. Your tests must validate that the contract stores only the encrypted blob and that the original secret remains undisclosed. Crucially, test the nullifier mechanism to ensure the same secret cannot be used to commit twice, preventing Sybil attacks. Mock the encryption process using a library like ethers.js to ensure end-to-end logic integrity.
After the commitment phase ends, testing shifts to the reveal phase. Write tests where users call reveal with their original secret and contribution amount. The contract must decrypt the stored commitment, hash the provided secret, and verify the match before accepting the contribution. Test edge cases: revealing with a wrong secret (should fail), revealing after the deadline (should fail), and attempting to contribute without revealing (should fail). Also, simulate the finalization process where the contract owner calls finalize to distribute funds if the goal is met or allow refunds if it isn't. Measure gas usage for these operations to estimate mainnet costs.
For integration testing, consider using a tool like Chainlink Functions or Gelato to automate the phase transitions based on block timestamps. This tests the real-world condition where an off-chain keeper triggers the startRevealPhase or finalize functions. Furthermore, implement fuzz testing with Foundry to input random, invalid data into your functions, ensuring the contract handles unexpected inputs gracefully without leaking funds or reverting in a way that locks capital. Auditing the event emissions is also critical for off-chain indexers.
Finally, before mainnet deployment, conduct a test on a public testnet with a simulated front-end. This end-to-end test should involve multiple mock participants using a wallet like MetaMask to go through the complete commit-reveal-contribute flow. Monitor for any discrepancies between the contract state and the UI, and verify that all user funds are accounted for correctly. This practical run-through catches integration bugs that isolated unit tests might miss, ensuring a secure and smooth launch for your encrypted fundraising round.
Frequently Asked Questions
Common technical questions and troubleshooting for developers implementing fundraising rounds with encrypted commitments.
An encrypted commitment scheme is a cryptographic protocol that allows a user to commit to a value (like a bid amount) without revealing it, while proving they have the funds to back it. In fundraising, this prevents front-running and information leakage during a round.
How it works:
- A user generates a cryptographic commitment (e.g., a Pedersen commitment or hash) to their bid amount and a secret random value (the "blinding factor").
- They submit this commitment on-chain, along with a zero-knowledge proof (ZKP) that the committed amount is within allowed bounds and backed by their balance.
- Later, during the reveal phase, they submit the original bid and blinding factor. The contract verifies it matches the earlier commitment.
This ensures the auction mechanism remains fair and trustless, as used by protocols like Clober and Whales Market for dark pool trading.
Resources and Further Reading
References, tools, and research papers for designing and launching a fundraising round using encrypted or cryptographic commitment schemes. These resources focus on commit-reveal flows, privacy-preserving allocations, and verifiable fairness.
Conclusion and Next Steps
This guide has walked you through the core concepts and steps for launching a secure fundraising round using encrypted commitment schemes. The next phase involves operational execution and exploring advanced applications.
You now have the foundational knowledge to implement a private fundraising round. The key steps are: deploying your CommitmentManager contract, integrating the frontend commitment interface, securely managing the SECRET_SALT for the final reveal, and executing the revealAllCommitments transaction to settle funds. Remember, the security of the entire scheme hinges on the preimage of the commitmentHash (the SECRET_SALT + investor address) remaining confidential until the reveal phase. A breach of this secret before the deadline compromises the round's privacy.
For production deployment, rigorous testing is non-negotiable. Beyond standard unit tests, conduct simulations with multiple mock participants to ensure the reveal function correctly validates hashes and distributes funds. Consider implementing a commit-reveal frontend using libraries like viem and wagmi, which provide robust hooks for managing transaction states and wallet connections. Audit trails are crucial; ensure all commitments and their corresponding on-chain reveal transactions are logged and verifiable for compliance purposes.
This primitive unlocks more than simple fundraising. You can adapt the pattern for sealed-bid auctions on-chain, where bids are hidden commitments revealed after a deadline. Another application is in governance, allowing for private voting commitments that are revealed to finalize a proposal's outcome, mitigating early voting influence. The core keccak256(abi.encodePacked(secret, data)) pattern is a versatile tool for any Ethereum application requiring a blind, binding promise that is later verifiably opened.
To dive deeper, review the official Ethereum Foundation documentation on cryptographic hashing and the Solidity by Example guide on commit-reveal schemes. Experiment with the complete example code in a forked testnet environment using Foundry or Hardhat. The next step in your learning journey could be exploring zk-SNARKs or MACI (Minimal Anti-Collusion Infrastructure) for scenarios requiring even stronger privacy guarantees and collusion resistance beyond basic commitment schemes.