Integration Guide
Add ZK credential verification to your HashKey Chain dApp in minutes.
Quick Start
Gate any function in your smart contract behind a KYC credential check:
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.23;
import {ISemaphore} from "@semaphore-protocol/contracts/interfaces/ISemaphore.sol";
interface IHSKPassport {
function verifyCredential(
uint256 groupId,
ISemaphore.SemaphoreProof calldata proof
) external view returns (bool);
}
contract MyDApp {
IHSKPassport public passport;
uint256 public constant KYC_GROUP = 25; // KYC_VERIFIED
constructor(address _passport) {
passport = IHSKPassport(_passport);
}
function kycGatedFunction(
ISemaphore.SemaphoreProof calldata proof
) external {
// REQUIRED: bind proof to caller to prevent front-running attacks
require(
proof.message == uint256(uint160(msg.sender)),
"proof must be bound to caller"
);
require(
passport.verifyCredential(KYC_GROUP, proof),
"KYC proof required"
);
// Your logic here
}
}Frontend: Generate Proofs
import { Identity } from "@semaphore-protocol/identity";
import { Group } from "@semaphore-protocol/group";
import { generateProof } from "@semaphore-protocol/proof";
// 1. Create identity from wallet signature
const signature = await signer.signMessage(
"HSK Passport: Generate my Semaphore identity"
);
const identity = new Identity(signature);
// 2. Reconstruct the group (from on-chain events or indexer)
const group = new Group();
for (const member of groupMembers) {
group.addMember(member);
}
// 3. Generate ZK proof — MUST bind proof to caller (prevents front-running)
const callerAddress = await signer.getAddress();
const proof = await generateProof(
identity,
group,
BigInt(callerAddress), // message = caller — REQUIRED to prevent front-running
"mint-silver-v1" // scope — unique per action for sybil resistance
);
// 4. Submit to your contract (which must also verify proof.message == msg.sender)
const tx = await myDApp.kycGatedFunction(proof);Contract Addresses (Testnet)
Credential Groups
| Group ID | Name | Description |
|---|---|---|
| 25 | KYC_VERIFIED | User has passed standard KYC verification |
| 26 | ACCREDITED_INVESTOR | User is an accredited/professional investor |
| 27 | HK_RESIDENT | User is a Hong Kong resident |
| 28 | SG_RESIDENT | User is a Singapore resident |
| 29 | AE_RESIDENT | User is a UAE resident |
How It Works
Semaphore v4
HSK Passport is built on Semaphore v4 by the Privacy & Scaling Explorations team (Ethereum Foundation). Semaphore uses Groth16 zero-knowledge proofs to enable anonymous group membership verification.
Identity Commitments
Each user generates a Semaphore identity (EdDSA keypair) from their wallet signature. The identity commitment (a hash of the public key) is added to a credential group's on-chain Merkle tree by an issuer.
Zero-Knowledge Proofs
When a user needs to prove a credential, they generate a Groth16 proof in their browser (via WASM). The proof demonstrates: "I know a private key whose commitment is a leaf in this Merkle tree" — without revealing which leaf.
On-Chain Verification
The smart contract verifies the proof using the bn128/alt_bn128 elliptic curve precompiles (ecAdd, ecMul, ecPairing) available on HashKey Chain. Verification costs ~241,000 gas.