Solari Systems — Smart Contract Security
EXPLOIT ANALYSIS $2.26M DRAINED $1.84M RESCUED

FoomCash Exploit: How a Skipped Groth16 Setup Ceremony Enabled $2.26M in ZK Proof Forgery

March 11, 2026 • 8 min read • Solari Systems Research

On February 26, 2026, an attacker drained $2.26 million from FoomCash — a ZK-proof-powered privacy lottery protocol — by forging zkSNARK proofs that the on-chain verifier accepted as valid. The root cause was devastating in its simplicity: the team skipped a single CLI step during the Groth16 trusted setup ceremony, leaving the verifier's gamma2 and delta2 parameters set to the same default value. Security firm Decurity and white-hat hacker Duha raced to rescue $1.84 million (81%) before the attacker could extract it all.

Key Takeaway: When gamma2 equals delta2 in a Groth16 verifier, the pairing equation degenerates. The verifier cannot distinguish between valid and forged proofs — it effectively checks "1 = 1" for every submission. One skipped CLI command made the entire privacy system worthless from day one.

What Is the Groth16 Trusted Setup Ceremony?

Groth16 is the most widely deployed zkSNARK proving system, used by protocols like Tornado Cash, Zcash, and dozens of privacy applications. It produces compact, constant-size proofs that verify in milliseconds. But it requires a trusted setup ceremony — a two-phase initialization process that generates the cryptographic parameters the verifier uses to check proofs.

Phase 1: Powers of Tau (Universal)

This phase generates universal parameters shared across all circuits. It is a community ceremony where multiple participants contribute randomness. As long as at least one participant destroys their secret, the ceremony is secure. This phase is well-understood, widely available, and typically uses pre-computed outputs from ceremonies like Hermez or Zcash.

Phase 2: Circuit-Specific Contribution (Critical)

This phase generates parameters unique to the specific circuit being deployed. It randomizes two critical values: gamma and delta. These parameters ensure that each proof is bound to a specific witness (the secret knowledge being proved). Without this step, gamma and delta remain set to the BN254 G2 generator — the default placeholder value in snarkjs.

The Fatal Error: FoomCash completed Phase 1 but never executed Phase 2. In the snarkjs workflow, this means the command snarkjs zkey contribute was never run. The deployed verifier contract shipped with gamma2 == delta2 == G2 generator. The protocol was broken from the moment it went live.

How gamma2 == delta2 Breaks Everything

The Groth16 verification equation is a bilinear pairing check:

// Groth16 Verification Equation
e(A, B) == e(alpha1, beta2) * e(vk_x, gamma2) * e(C, delta2)

// Where:
// A, B, C = proof elements submitted by the prover
// alpha1, beta2 = fixed setup parameters
// vk_x = linear combination of public inputs and verification key
// gamma2, delta2 = MUST be independent random values

When gamma2 == delta2, the two right-hand pairing terms collapse:

// BROKEN: When gamma2 == delta2, the equation simplifies
e(vk_x, gamma2) * e(C, delta2)
= e(vk_x, gamma2) * e(C, gamma2)    // delta2 replaced with gamma2
= e(vk_x + C, gamma2)               // bilinear pairing property

// An attacker simply sets C = -vk_x to cancel both terms
// Then sets A = alpha1, B = beta2 to cancel the left side
// Result: e(alpha1, beta2) == e(alpha1, beta2) * e(0, gamma2)
//       = e(alpha1, beta2) * 1
//       = e(alpha1, beta2)  ... ALWAYS TRUE

The attacker does not need a valid witness. They do not need to know any secret. They compute C = -vk_x using publicly available verification key constants and submit a forged proof that passes verification for any arbitrary nullifierHash.

The Attack Flow

1
Reconnaissance: Attacker reads the on-chain verifier contract and identifies that gamma2 == delta2 (both equal to the BN254 G2 generator). This same flaw had been publicly disclosed in the Veil Cash post-mortem two days earlier.
2
Proof Forgery: Using elliptic curve arithmetic, the attacker computes forged proof elements (A, B, C) that satisfy the degenerate verification equation for any chosen nullifierHash — no deposit history required.
3
Base Chain Drain: A single malicious transaction on Base drains ~$427K worth of FOOM tokens from the lottery contract at block 42,650,623.
4
Ethereum Attack Begins: The attacker targets the Ethereum contract holding ~$1.84M in liquidity.
5
White-Hat Intervention: Decurity and white-hat hacker Duha independently identify the flaw, front-run the attacker on Ethereum, and secure $1.84M before it can be extracted maliciously.
6
Funds Returned: Decurity returns the rescued funds to the FoomCash protocol. Total net loss reduced to ~$420K.

The Vulnerable Verifier Contract

The on-chain verifier deployed by FoomCash contained no validation that the trusted setup was properly completed:

// VULNERABLE - Deployed with gamma2 == delta2 (both G2 generator)
contract Verifier {
    // Setup parameters baked into the contract
    G1Point alpha1;
    G2Point beta2;
    G2Point gamma2;  // G2 generator (Phase 2 never randomized this)
    G2Point delta2;  // G2 generator (identical to gamma2)

    function verifyProof(
        uint[2] memory a,
        uint[2][2] memory b,
        uint[2] memory c,
        uint[] memory input
    ) public view returns (bool) {
        // Pairing check passes for ANY forged proof
        // when gamma2 == delta2
        return pairing(a, b, alpha1, beta2, vk_x, gamma2, c, delta2);
    }
}

The Safe Pattern: Constructor Validation

// SAFE - Validate setup parameters at deployment
contract Verifier {
    constructor(
        G1Point memory _alpha1,
        G2Point memory _beta2,
        G2Point memory _gamma2,
        G2Point memory _delta2,
        G1Point[] memory _ic
    ) {
        // CRITICAL: Reject deployments where gamma2 == delta2
        require(
            _gamma2.X[0] != _delta2.X[0] ||
            _gamma2.X[1] != _delta2.X[1] ||
            _gamma2.Y[0] != _delta2.Y[0] ||
            _gamma2.Y[1] != _delta2.Y[1],
            "INVALID_SETUP: gamma2 == delta2"
        );

        alpha1 = _alpha1;
        beta2 = _beta2;
        gamma2 = _gamma2;
        delta2 = _delta2;
        ic = _ic;
    }
}

The Attack in Numbers

MetricValue
Total drained$2.26 million (24.28 trillion FOOM tokens)
White-hat recovery$1.84 million (81%)
Net protocol loss~$420,000
Chains affectedEthereum + Base
Base loss (malicious)~$427,000
Ethereum (rescued)~$1.84 million
Bounty to Duha$320,000
Security fee to Decurity$100,000
Root causeSkipped snarkjs Phase 2 trusted setup
Time from Veil Cash disclosure to copycat~48 hours

The Copycat Problem: Veil Cash to FoomCash in 48 Hours

Two days before the FoomCash exploit, the same gamma2 == delta2 flaw was discovered and publicly disclosed in Veil Cash, a smaller protocol on Base that lost only ~2.9 ETH. The Veil Cash post-mortem explained the exact vulnerability in detail, including the mathematical proof of how to forge verification.

Within 48 hours, someone applied the identical technique to FoomCash's larger liquidity pools ($8M+ TVL). BlockSec Phalcon flagged the attack as a direct copycat. This timeline — public disclosure to copycat exploitation in under two days — represents a critical risk window for any ZK protocol sharing the same vulnerability class.

Lesson for the Industry: When a ZK vulnerability is publicly disclosed, every protocol using the same proving system must be checked immediately. The 48-hour copycat window is now an established pattern. Disclosure without coordinated patching is effectively handing attackers a blueprint.

Why This Keeps Happening

ZK proof system deployment failures persist because of a fundamental mismatch between the cryptographic complexity of these systems and the operational practices of teams deploying them:

How to Protect Your Protocol

1. Validate Setup Parameters at Deployment

Add a constructor check that gamma2 != delta2. This single require statement would have prevented the entire FoomCash exploit. It costs minimal gas at deployment and provides permanent protection.

2. Complete the Full Trusted Setup Ceremony

For Groth16 circuits, the full snarkjs workflow is: snarkjs powersoftau (Phase 1) followed by snarkjs zkey new, snarkjs zkey contribute (Phase 2), and snarkjs zkey verify. Never skip the verification step. Document every phase with cryptographic hashes of intermediate outputs.

3. Add Withdrawal Rate Limiting

Even with a valid setup, implement circuit breakers: maximum withdrawals per block, per-address cooldowns, and anomalous-outflow detection. These defense-in-depth measures buy time for white-hat intervention.

4. Audit the Cryptographic Setup, Not Just the Code

Smart contract audits must include verification of trusted setup parameters. Check that gamma2 and delta2 are distinct, that verification keys match the deployed circuit, and that Phase 2 contributions are properly recorded.

5. Continuous Monitoring

Deploy real-time monitoring for unusual withdrawal patterns. FoomCash's team had been absent for three months. Active monitoring by Decurity and BlockSec Phalcon is what enabled the $1.84M rescue.

Audit Your ZK Protocol

Our security team reviews smart contracts, verifier configurations, and cryptographic setup parameters. We check for gamma2/delta2 mismatches, incomplete ceremonies, and 20+ other vulnerability patterns.

Free Vulnerability Scan Request Full Audit

Free scan requires no signup. Full audits include trusted setup verification.

On-Chain Evidence

ComponentAddress / Hash
Victim Contract (Ethereum)0x239af915abcd0a5dcb8566e863088423831951f8
Victim Contract (Base)0xdb203504ba1fea79164af3ceffba88c59ee8aafd
Broken Verifier (Ethereum)0xc043865fb4D542E2bc5ed5Ed9A2F0939965671A6
Broken Verifier (Base)0x02c30D32A92a3C338bc43b78933D293dED4f68C6
Attacker0x73f55A95D6959D95B3f3f11dDd268ec502dAB1Ea
Exploit Contract0x005299b37703511b35d851e17dd8d4615e8a2c9b
Attack Tx (Ethereum)0xce20448233f5ea6b6d7209cc40b4dc27b65e07728f2cbbfeb29fc0814e275e48
Attack Tx (Base)0xa88317a105155b464118431ce1073d272d8b43e87aba528a24b62075e48d929d
White-Hat Rescuewhitehat-rescue.eth (Decurity)

Timeline

Further Reading