> For the complete documentation index, see [llms.txt](https://socious.gitbook.io/midnight-smart-contract-library/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://socious.gitbook.io/midnight-smart-contract-library/crowdfunding/crowdfunding-smart-contract.md).

# Crowdfunding Smart Contract

## Tutorial: Decentralized Crowdfunding

Code: <https://github.com/socious-io/midnight-fund/>

{% embed url="<https://youtu.be/BvtIi6_vG2c>" %}

### 1. Overview

The **Midnight Crowdfunding** contract allows users to create fundraising campaigns with built-in accountability and privacy. Unlike traditional crowdfunding platforms, this contract:

* **Protects Contributor Privacy:** Uses Zero-Knowledge (ZK) proofs to shield the identity of contributors while still allowing them to prove their contribution for refunds.
* **Enforces Fair Fees:** Automatically manages a 5% protocol fee that must be processed before the project owner can withdraw funds.
* **Isolates Funds:** Each project’s funds are tracked separately using the `protocolTvl` map, preventing cross-contamination between campaigns.

### 2. Roles

* **Project Owner:** The creator of the campaign. They hold the secret key required to manage the project and withdraw funds.
* **Contributor:** A user who donates funds (`CoinInfo`) to a project. Their participation is recorded in a private Merkle Tree.
* **Protocol Admin:** The entity receiving the platform fees (configured via `feeAddress`).

### 3. The Fundraising Lifecycle

#### Step 1: Creating a Project

To start a campaign, the owner calls the `createProject` circuit. This generates a unique "Owner Hash" derived from their local secret key, allowing them to manage the project anonymously if desired.

**Function:** `createProject(...)`**Parameters:**

* `_projectId`: Unique 32-byte identifier.
* `_contributionGoal`: The target amount (Uint128).
* `_duration`: How long the campaign lasts.
* `_coinType`: The specific currency/token accepted (Color).
* `_title` / `_desc`: Opaque strings describing the project.

**Code Example:**

```
await contract.createProject(
  projectId,
  100000n, // Goal
  86400n,  // Duration (e.g., 1 day)
  coinType,
  "My Privacy App",
  "Building a ZK-dApp..."
);
```

#### Step 2: Contributing to a Project

Contributors send funds to the contract. The contract merges these coins into the project's TVL and adds the contributor's commitment to a Merkle Tree.

**Function:** `contributeProject(coin, _projectId)`

* **Validation:** The contract checks if the project is active, not expired, and if the goal hasn't been reached.
* **Privacy:** The `Contributor` struct is hashed and stored in the `contributors` ledger. The raw data (who sent what) is **not** stored publicly in cleartext on the project list, preserving donor privacy.

#### Step 3: Managing the Project (Owner Only)

The owner can update details or cancel the project using their secret key to prove ownership.

* **`updateProject(...)`**: Change title, description, or goal.
* **`cancelProject(...)`**: Can only be called if `raised == 0`.
* **`endProject(...)`**: Manually closes the project (requires `raised == 0` in current logic, or used to stop accepting funds).

#### Step 4: Withdrawing Funds (The 5% Fee Rule)

The contract enforces a strict order of operations for withdrawals. The project owner **must** trigger the fee payment before they can claim the remaining funds.

**A. Pay Protocol FeeFunction:** `withdrawProjectFee(_projectId, _feeAmount)`

* **Constraint:** `_feeAmount` must be exactly 5% of the total raised.
* **Logic:** Transfers the fee to the `feeAddress` hardcoded/set in the constructor. Marks the project as `feeWithdrawnProjects`.

**B. Withdraw Raised CapitalFunction:** `withdrawProjectFunds(_projectId, _feeAmount)`

* **Pre-requisite:** The fee must have already been withdrawn.
* **Outcome:** The remaining 95% is transferred to the `projectPayoutAddresses` (or the owner's key if not set). The project status changes to `withdrawn`.

#### Step 5: Refunds

If a project is active but the user wants to pull out (and the logic permits, e.g., before the goal is met or if the project fails), they can request a refund.

**Function:** `requestRefund(...)`**Privacy Feature:** Because the contributor list is private (Merkle Tree), the user must provide a **Zero-Knowledge Witness** (`findContributor`) to prove they deposited funds without revealing their entire transaction history.

```
// The witness generates a Merkle Path proving the commitment exists
const path = disclose(findContributor(contributorsCommit));
assert(contributors.checkRoot(path), "Invalid contributor");
```

### 4. Technical Constraints & Tips

* **Fee Logic:** The contract strictly enforces `feeAmount * 20 <= totalAmount`. If you try to withdraw a fee smaller or larger than the 5% calculation, the transaction will fail.
* **Coin Merging:** The contract uses `mergeCoinImmediate` to combine incoming contributions into a single UTXO per project. This simplifies state management but requires `protocolTvl` to be carefully synchronized.
* **Deadlines:** The `confirmProjectExpiration` witness checks the timestamp. Ensure your client (frontend) provides an accurate `currentTimestamp` witness.
