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/members

Active identity commitments in a credential group (revocation-aware)

GET
/api/groups/:groupId/stats

Group size and issuance/revocation counts

GET
/api/credentials/:commitment

Groups a specific identity belongs to

GET
/api/stats/global

Protocol-wide stats: active credentials, groups, submissions

POST
/api/kyc/submit

Submit a KYC request for issuer review

GET
/api/kyc/queue?status=pending

Issuer queue of pending/approved/rejected requests

POST
/api/kyc/review

Issuer action — approve (record tx) or reject

Deployed Contracts

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>

Live Examples

Need help integrating?

Open an issue on GitHub or reach out to the core team.

Open on GitHub