For builders
Developer Portal
Add privacy-preserving ZK credential verification to your dApp in under 10 minutes.
1
Inherit
Inherit from HSKPassportVerifier in your contract
2
Gate
Add modifier to functions that need KYC
3
Ship
Users generate proofs via SDK, submit to your contract
Smart Contract Integration
// 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;
constructor(address _passport) {
passport = IHSKPassport(_passport);
}
function restrictedAction(
ISemaphore.SemaphoreProof calldata proof
) external {
// Bind proof to caller to prevent front-running
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
}
}Security tip: Always bind proofs to msg.sender to prevent front-running attacks. The messagefield of the proof must equal the caller's address cast to uint256.
Frontend — TypeScript SDK
Install the SDK (or copy from /sdk):
npm install hsk-passport-sdk ethers @semaphore-protocol/identity @semaphore-protocol/group @semaphore-protocol/proof
import { HSKPassport } from "hsk-passport-sdk";
import { BrowserProvider } from "ethers";
// Connect
const provider = new BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const passport = HSKPassport.connect("hashkey-testnet", signer);
// Create identity from wallet signature
const sig = await signer.signMessage("HSK Passport: Generate identity");
const identity = passport.createIdentity(sig);
// Check status
const creds = await passport.getCredentials(identity);
console.log(creds); // [{groupName, hasCredential, ...}]
// Generate ZK proof (bound to caller)
const callerAddress = await signer.getAddress();
const proof = await passport.generateProof(
identity,
15, // KYC_VERIFIED group
"my-action-scope", // scope for nullifier
BigInt(callerAddress) // message bound to caller
);
// Verify on-chain (read-only)
const valid = await passport.verifyProof(15, proof);Indexer REST API
Query group members and KYC state without running an Ethereum node. Base URL: https://hskpassport.gudman.xyz/api
GET
/api/groups/:groupId/membersActive identity commitments in a credential group (revocation-aware)
GET
/api/groups/:groupId/statsGroup size and issuance/revocation counts
GET
/api/credentials/:commitmentGroups a specific identity belongs to
GET
/api/stats/globalProtocol-wide stats: active credentials, groups, submissions
POST
/api/kyc/submitSubmit a KYC request for issuer review
GET
/api/kyc/queue?status=pendingIssuer queue of pending/approved/rejected requests
POST
/api/kyc/reviewIssuer action — approve (record tx) or reject
Deployed Contracts
HashKey Chain Testnet (chain ID 133)
- HSKPassport
- 0x7d2E692A08f2fb0724238396e0436106b4FbD792
- CredentialRegistry
- 0x20265dAe4711B3CeF88D7078bf1290f815279De1
- Timelock (48h)
- 0xb07Bc78559CbDe44c047b1dC3028d13c4f863D8A
- HashKeyDIDBridge
- 0xF072D06adcA2B6d5941bde6cc87f41feC5F5Ea7a
- HashKeyKYCImporter
- 0x5431ae6D2f5c3Ad3373B7B4DD4066000D681f5B8
- GatedRWA (hSILVER)
- 0xb6955cb3e442c4222fFc3b92c322851109d0b9c9
- KYCGatedAirdrop (hPILOT)
- 0x71c96016CBCAeE7B2Edc8b40Fec45de1d16Fb4b8
- KYCGatedLending
- 0x37179886986bd35a4d580f157f55f249c43A0BFD
- JurisdictionGatedPool
- 0x305f5F0b44d541785305DaDb372f118A9284Ce4D
- FreshnessRegistry (v6)
- 0xd251ecAD1a863299BAD2E25B93377B736a753938
- FreshnessVerifier (v6)
- 0x59A03fF053464150b066e78d22AEc2F69D081394
- HSKPassportFreshness (v6)
- 0xFF790dE1537a84220cD12ef648650034D4725fBb
mainnetHashKey Chain (chain ID 177)
- IssuerRegistry
- 0xf109cBe3D8d54D77C85ECF1367Cfcd6f075868e9
25
KYC Verified
26
Accredited Investor
27
HK Resident
28
SG Resident
29
AE Resident
React Component
import { HSKPassportGate } from "hsk-passport-sdk/react";
<HSKPassportGate
groupId={25}
scope="mint-silver-token"
identitySecret={walletSignature}
signer={signer}
onVerified={async (proof) => {
// User is KYC-verified — mint the token
await myContract.kycMint(proof);
}}
onError={(err) => toast.error(err.message)}
>
Verify KYC & Mint
</HSKPassportGate>