zkDonation Platform

This topic is to discuss the proposal submitted by @highonrice.
Please see below for the details of the proposal and discussion.

5th February, 2025
Current status: Funded
Funding Note: This proposal is approved for funding. This aligns with the new focus areas (provable web2 data/zkTLS use cases) where we want the applications to be built.

27th January, 2025
Current Status: Under Consideration.
Opened for community discussion on: 27th January, 2025.

2 Likes

Project Background

zkDonation is a zero-knowledge application (zkApp) built on the Mina Protocol that enhances privacy and transparency in charitable giving. By integrating APIs from major donation platforms (e.g., Every.org), zkDonation allows users to make donations to verified charities and generate cryptographic proofs of their donations without exposing personal or transactional details.

The platform ensures that donations are verifiable on-chain while maintaining donor privacy. It targets individuals, corporations, and organizations prioritizing privacy and transparency in their philanthropic activities.

Proposal Overview

Problem

While many Web3 users make donations to charities, there is no way to link verified donations to their on-chain identity. Also, traditional online donation services often compromise privacy and are centralized. Tax authorities require proof of donation for deductions, which typically involves sharing receipts and personal details. A blockchain-native donation verification solution is needed to:

  • Preserve user privacy.
  • Build trustless verification mechanisms.

Solution

zkDonation combines zero-knowledge technology with blockchain to:

  1. Allow users to donate via verified APIs, generating zk proofs that validate their contributions without exposing sensitive data.
  2. Enable on-chain verification of these zk proofs for transparency.

Impact

  • Adoption: zkDonation protects privacy while enhancing the donation experience, encouraging new users to engage with the Mina ecosystem for philanthropic activities.
  • Tools: Provides developers with reusable zk circuits for privacy-preserving applications by making the codebase open source.
  • Novel Applications: Introduces a novel use case for ZKPs in charity and altruism, expanding Mina’s utility beyond traditional DeFi and identity applications.

Audience

  • Individual Donors: Privacy-conscious users who want to make verifiable contributions.
  • Corporations: Companies seeking to demonstrate social responsibility transparently.
  • Developers: Those interested in integrating zkDonation’s circuits and APIs into their own applications.
  • Charities: Organizations looking for innovative ways to attract global, privacy-conscious donors.

Architecture & Design

Detailed Design/Architecture

zkDonation Architecture

It operates with four main components:

  • zkApp(UI + Smart Contract): A static webpage that lets the user search various charities, make donations to the charities and create the zk proof of the donation made.
  • API Server: A backend server that integrates Every.org’s Partner Webhook, retrieving the successful donation information made via zkApp. This webhook request from Every.org will then be saved in its database.
  • ZKON zkOracle: A zkTLS-based service that provides tamper-proof data, makes API requests on frontend applications’ behalf, and returns the API response with proof of provenance and proof of integrity. The zkDonation zkApp will call the service to get the user’s donation data.
  • Donation Service API: A 3rd-party charity API service that sends the donation data to the API server when a donation is made with the partner donate link.

A typical user journey is as follows:

  1. The user visits the zkDonation website and connects his/her Mina wallet.
  2. The user searches for a charity to make donations.
  3. When decided, the user asks the API server for a donate link.
  4. The API server generates a donate link to the corresponding charity with a unique webhook_token.
  5. The user donates via the custom donate link with the unique webhook_token.
  6. When the donation is successfully made, Donation Service sends a webhook request to the API server, containing the donation information. At this point, the API server verifies the webhook request is coming from the Every.org domain.
  7. If the webhook request is coming from Every.org and the data structure is identical to the API documentation of Every.org, the API server saves the donation information in its database.
  8. After waiting a few seconds, the user requests to get the donation data to ZKON zkOracle.
  9. The zkOracle then makes the API request on the user’s behalf.
  10. The API server searches for the matching donation information, and when found, it returns the donation information to the zkOracle.
  11. The zkOracle verifies the provenance and the integrity of the data, and when both are verified, it returns the donation data along with the proofs to the user.
  12. The zkApp then generates the zk proof of the donation data and verifies it. Upon successful verification, the zkApp saves the user’s public key and the proof in its IndexedMerkle tree, and the zkApp account emits an event containing the donation information and the associated user wallet address.
  13. When the user wants to use the proof for verification(e.g., accessing an external service), the IndexedMerkelMap is queried to confirm that the public key matches the stored proof. In this way, only the proof owner can successfully use the proof for verification, ensuring any malicious attempts are blocked on the contract level.

Vision

In the long term, zkDonation aims to become the standard for privacy-preserving donation systems, integrating with more donation APIs, and supporting global charities. It seeks to inspire similar zk applications across various sectors.

Existing Work

N/A - This is a new project

Budget & Milestones

Deliverables

  • Functional zkDonation MVP:
    • Integration with a 3rd party Donation Service API and zkOracle.
    • zk proof generation and on-chain verification.
    • User-friendly frontend for managing donations and proofs.
  • Documentation:
    • Technical documentation for zk circuits and API usage.

Project Timeline

  • 3 months
    • Phase 1: Research and Design (2 weeks)
      • Finalize system architecture.
      • UI design for the production app.
      • Prototype smart contract logic.
    • Phase 2: Development (2 Months)
      • Integrate Donation Service API
      • Integrate zkOracle
      • Implement zkDonation API server
      • Implement the frontend interface for searching charities, making donations, and proof generation.
      • Develop zkApp smart contract for proof creation and verification.
      • Integrate zkApp with zkOracle and the API server.
    • Phase 3: Testing and Deployment (2 weeks)
      • Conduct end-to-end testing on devnet.
      • Deploy to Mina

Budget Requested

  • 17,500 MINA

Budget Breakdown

  • UI Design: 1,000 MINA
  • Software Development: 16,500 MINA
    • 2 developers working 20 hours per week for 12 weeks, assuming approximately 35 MINA per hour rate(2 X 20 X 12 X 35 = 16,800, but just rounding down to 16,500)
  • The budget will cover any other costs incurred.

Wallet Address

B62qrFsS1pLuqXvSSKReij7RKE6K5b2RLbztm2MoYyv7B62aNU5Xzw2

Team Info

Proposer: Seoho Yoon

  • Proposer Github: highonrice (highonrice) · GitHub
  • Proposer Experience
    • A graduate of Mina Protocol Onboarding Module
    • Full-stack software developer with 8+ years of experience(3 years of Web3 experience)

Team Member: Seungmin Jeon

  • Team Member Github: sm-stack (Harry | Seungmin Jeon) · GitHub
  • Team Member Experience
    • Blockchain Engineer contributing to the validator system with ZK fault proof on Kroma
    • 2 years of smart contract & core engineering experience

Risks & Mitigations

API Dependence

  • Risk: Reliance on third-party donation APIs for data and processing could lead to service disruption or compromised delivery.
  • Mitigation: Integrate multiple APIs to diversify dependency.

Developer Timeline

  • Risk: IndexedMerkleMap is currently experimental, and any changes made to the namespace function could impact the developer timeline.
  • Mitigation: Actively monitor the latest updates to IndexedMerkleMap and the o1js SDK by maintaining close communication with Mina’s developer community and regularly reviewing documentation or release notes. Also, modular code practices should be implemented to isolate dependencies on IndexedMerkleMap, making it easier to adapt to changes without overhauling the entire system.
3 Likes

Posting this on behalf of @andrewferrone.

Is it possible to add a use case to the platform? Maybe a simple leaderboard where you climb the charts by registering your donations? I’d love to see some idea for attracting users once the platform is there.

1 Like

Hey @highonrice and team! Juanjo from ZKON here.

First of all, I think this is a great idea and a nice use case for ZK privacy, congrats!

But I have a few questions about the implementation itself:

The API server, the one who receives the donation info via the webhook, will be maintained by you guys? And this will be the one running the ZKON SDK?

Also, about step 8 and 9, I’ve been checking the every.org API docs, but I don’t see an endpoint to get the information back using the chargeId field. Do you know if it exists? Or is there any other endpoint that we can use to retrieve the information from every.org and compare it to the webhook data? If that exists, then we can notarize the request to this endpoint using our SDK and generate a Zk proof of this.

1 Like

@andrewferrone

Hi, thanks for suggesting a good idea! I can add a leaderboard once the basic features are all implemented. Maybe users can get badges based on their donation amount by leveraging MinaNFT.

Hi, thanks for the questions.

For the first question, yes the API server will be maintained ourselves and it will run the ZKON SDK.

For the second, maybe it is better to use Pledge.to API rather than Every.org API. If possible, could you look at Pledge API? I guess Pledge has better API than Every

Hi,

Yep, I think Pledge is more feasible, since they also have the webhook and an endpoint for getting the donations, and you can filter by id.

Awesome.

Great. Then I will use Pledge.to for this zkApp!

This proposal is approved for funding. This aligns with the new focus areas (provable web2 data/zkTLS use cases) where we want the applications to be built.

Looking forward to the update. Thanks!

2 Likes

Hi all, sharing the latest progress on zkDonation.

Over the past two weeks, we’ve laid the groundwork for zkDonation like UI design and prototyping smart contracts.

Here’s what we’ve been up to:

1. UI Design (Work in Progress)

The UI is shaping up to be intuitive and accessible, ensuring donors can navigate the platform with ease. We’re finishing the design and expect to complete this phase by the end of next week. Regardless of the completion of the design, we will start building the app.

2. Prototyped Smart Contract Logic

We’ve also made headway on the smart contract side of the project. Please refer to the code here. Our team has developed a prototype for the core smart contract logic that will power zkDonation. The code implements a zk donation system where donors can contribute amounts privately. It verifies donations without revealing sensitive details like the donation amount. The system uses a Merkle tree to track commitments (hashed values representing donation states) for each donor. Please note that this is a very primitive version of the work we will make, and we will keep progressing with the codes along the way.

export let ZkonProof_ = ZkProgram.Proof(mockZkProgram);
export class ZkonProof extends ZkonProof_ { }

class DonationPublicInput extends Struct({
  donor: PublicKey,
  oldCommitment: Field,
  newCommitment: Field,
  zkOnProof: ZkonProof,
}) { }

const DonationProgram = ZkProgram({
  name: "DonationProgram",
  publicInput: DonationPublicInput,
  methods: {
    proveDonation: {
      privateInputs: [Field, Field, Field, Field, Field], // oldTotalAmount, oldRandomness, newDonationAmount, newTotalAmount, newRandomness
      method: async (
        input: DonationPublicInput,
        oldTotalAmount: Field,
        oldRandomness: Field,
        newDonationAmount: Field,
        newTotalAmount: Field,
        newRandomness: Field
      ) => {
        // Verify the old commitment
        const computedOldCommitment = Poseidon.hash([oldTotalAmount, oldRandomness]);
        computedOldCommitment.assertEquals(input.oldCommitment);

        // Verify the new total amount
        newTotalAmount.assertEquals(oldTotalAmount.add(newDonationAmount));

        // Verify the new commitment
        const computedNewCommitment = Poseidon.hash([newTotalAmount, newRandomness]);
        computedNewCommitment.assertEquals(input.newCommitment);

        // Ensure the new donation amount is positive
        newDonationAmount.assertGreaterThan(Field(0));

        // Verify the ZKON proof
        await input.zkOnProof.verify();
      },
    },
  },
});

class DonationProof extends Proof<DonationPublicInput, Void> {
  static program = DonationProgram;
  static publicInputType = DonationPublicInput;
  static publicOutputType = Void;
}

class DonationEvent extends Struct({
  donor: PublicKey,
}) { }

export class DonationZkApp extends SmartContract {
  // TODO: this should be extended to incorporate various users and donations
  @state(Field) treeRoot = State<Field>();

  init() {
    this.treeRoot.set(Field(0));
  }

  @method async donate(proof: DonationProof, witness: MerkleMapWitness) {
    // Verify the ZK proof
    proof.verify();

    const input = proof.publicInput;
    input.donor.assertEquals(this.sender.getUnconstrained());

    const userKey = Poseidon.hash(input.donor.toFields());

    const currentRoot = this.treeRoot.get();
    this.treeRoot.get().assertEquals(currentRoot);

    // Use the witness to verify the old commitment and compute the new root with the new commitment
    const [computedRoot, key] = witness.computeRootAndKey(input.oldCommitment);
    computedRoot.assertEquals(currentRoot);
    key.assertEquals(userKey);

    const [newRoot, _] = witness.computeRootAndKey(input.newCommitment);
    this.treeRoot.set(newRoot);

    // Event emission
    this.emitEvent("donation", new DonationEvent({ donor: input.donor }));
  }
}

Next Steps:

  1. Complete UI Design
  2. Implement IndexedMerkle tree to incorporate various users and donations
  3. Perform PoC with ZKON
  4. Develop frontend

Happy to receive feedback from the community.

1 Like