Skip to content

Latest commit

 

History

75 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

ZK Coinbase Attestation Screenshot

ZK Coinbase Attestation

A zero-knowledge proof system to privately prove your Coinbase KYC status using Noir.


🔍 What It Does

ZK Coinbase Attestation allows users to prove they've completed Coinbase KYC without revealing their wallet address or onchain history.

This is useful for:

  • Accessing compliant DeFi products.
  • Proving personhood privately.
  • Gaining exclusive perks while preserving privacy.

🧪 How It Works

We leverage onchain attestations made by Coinbase using the Ethereum Attestation Service (EAS).

Instead of storage proofs, we verify that:

  1. A privileged Coinbase account called attestAccount(userAddress)
  2. The transaction was signed correctly by Coinbase
  3. The user owns the wallet that was attested

All this happens in a Noir circuit with sub-4 second proving time.


🔧 Circuit Logic (Noir)

mod utils;
use utils::{concat_prefix_and_digest, extract_address_from_calldata};
use dep::ecrecover::ecrecover;
use dep::keccak256::keccak256;
use std::hash::poseidon;
use std::ecdsa_secp256k1::verify_signature;

global ETH_PREFIX_BYTES: [u8; 28] = [
    25, 69, 116, 104, 101, 114, 101, 117, 109, 32, 83, 105, 103, 110, 101, 100, 32, 77, 101, 115,
    115, 97, 103, 101, 58, 10, 51, 50,
];

// Unconstrained function to compute the hash that the user signs.
// This combines nonce and timestamp with Poseidon, adds the Ethereum prefix, and hashes with Keccak.
unconstrained fn compute_user_hash(nonce_hash: Field, timestamp_hash: Field) -> [u8; 32] {
    let combined_hash = poseidon::bn254::hash_2([nonce_hash, timestamp_hash]);
    let message = concat_prefix_and_digest(ETH_PREFIX_BYTES, combined_hash.to_be_bytes());
    keccak256(message, 60)
}

// This is the main circuit logic for verifying a Coinbase-style ZK attestation.
// It checks that both the attester (e.g., Coinbase) and the user have signed expected messages using ECDSA.
fn main(
    attester_pub_key_x: [u8; 32],
    attester_pub_key_y: [u8; 32],
    attester_signature: [u8; 64],
    hashed_attestation_tx: pub [u8; 32],
    user_pub_key_x: [u8; 32],
    user_pub_key_y: [u8; 32],
    user_signature: [u8; 64],
    nonce_hash: pub Field,
    timestamp_hash: pub Field,
    tx_calldata: [u8; 36],
) {
    // Verify the signature from the attester's public key over the attestation hash.
    // This proves that the attestation was genuinely signed by the trusted attester (e.g., Coinbase).
    let attesterSignatureIsValid = verify_signature(
        attester_pub_key_x,
        attester_pub_key_y,
        attester_signature,
        hashed_attestation_tx,
    );
    assert(attesterSignatureIsValid);

    // Validate selector manually instead of using return-based function
    assert(tx_calldata[0] == 0x56);
    assert(tx_calldata[1] == 0xfe);
    assert(tx_calldata[2] == 0xed);
    assert(tx_calldata[3] == 0x5e);

    // Safety: The `compute_user_hash` function is unconstrained and does not perform any unsafe memory operations.
    // Secure verification using ECDSA
    let signed_user_hash = unsafe { compute_user_hash(nonce_hash, timestamp_hash) };

    // Recover the user's Ethereum address from their public key and signature over their identity hash.
    // This shows that the user claims ownership of a particular address (e.g., for KYC or eligibility proofs).
    let user_addr = ecrecover(
        user_pub_key_x,
        user_pub_key_y,
        user_signature,
        signed_user_hash,
    );

    // Extract address from calldata safely using the utility function
    let extracted_addr = extract_address_from_calldata(tx_calldata);

    // Ensure the recovered user address matches the expected address.
    // This binds the user's signature to their identity in the attestation process.
    assert(user_addr == extracted_addr);
}

🖥️ Web UI Flow

  • Connect your wallet
  • Fetch your Coinbase attestation via EAS
  • Locate the attestation transaction
  • Generate a fresh zkProof
  • Use the proof in any integrated dApp

🧱 Tech Stack

  • Frontend: Vite, Wagmi, Tailwind
  • ZK Circuit: Noir
  • Onchain Data: Base Explorer

🚀 Getting Started

git clone https://github.com/lucholeonel/zk-coinbase-attestation-project
cd zk-coinbase-attestation-project

# Frontend
cd frontend
cp .env.example .env
yarn install
yarn dev

⚠️ You'll need an API key from BaseScan to query transaction data. Add it to your .env file as:

VITE_PUBLIC_BASE_API_KEY=your_api_key_here

Visit http://localhost:5174 and click "Fetch Attestation Transaction".


📁 Folder Structure

  • /frontend: Web interface for proof generation
  • /frontend/circuit: Noir circuit logic

📚 Key References


🤝 Credits

Built with ❤️ for the Elite NoirHacker Grant.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages