Policy Composer
Compliance Policy Composer
Pick the compliance rules your dApp needs. We'll generate the Solidity contract, React component, and test file. Paste into your project and you're done.
Start from a preset
or build a custom policy below ↓Policy
Unique per-action namespace for sybil resistance.
Required credentials
Allowed jurisdictions
Selective disclosure — prove membership in the set without revealing which one.
Policy expression
KYCSolidity contract
Paste into your project — enforces the policy on every call.
MyAppGated.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {ISemaphore} from "@semaphore-protocol/contracts/interfaces/ISemaphore.sol";
interface IHSKPassport {
function verifyCredential(uint256 groupId, ISemaphore.SemaphoreProof calldata proof) external view returns (bool);
}
contract MyAppGated {
IHSKPassport public constant PASSPORT = IHSKPassport(0x7d2E692A08f2fb0724238396e0436106b4FbD792);
error NotKYCVerified();
error ProofNotCallerBound();
/// Gate entry point. Enforces the composed policy.
function gatedAction(ISemaphore.SemaphoreProof calldata proof) external {
// 1. Caller-bound proof — prevents front-running.
if (proof.message != uint256(uint160(msg.sender))) revert ProofNotCallerBound();
// 2. KYC verification required.
if (!PASSPORT.verifyCredential(25, proof)) revert NotKYCVerified();
// Policy satisfied — run your action below.
_onApproved(msg.sender);
}
function _onApproved(address user) internal virtual {
// Override: mint, borrow, airdrop, etc.
}
}React frontend
Drop into any page — handles proof generation + submission.
MyAppAction.tsx
"use client";
import { HSKPassportGate } from "hsk-passport-sdk/react";
export function MyAppAction() {
return (
<HSKPassportGate
policy={{
kyc: true,
accredited: false,
jurisdictions: [],
scope: "my-dapp:mint",
}}
onProof={async (proof) => {
// Call your gated contract
const tx = await contract.gatedAction(proof);
await tx.wait();
}}
>
<button className="btn-primary">Execute MyApp Action</button>
</HSKPassportGate>
);
}Hardhat test
Starter test — verifies the policy rejects attackers and accepts valid users.
MyAppGated.test.ts
import { expect } from "chai";
import { ethers } from "hardhat";
import { buildProof } from "hsk-passport-sdk/test";
describe("MyAppGated", () => {
it("rejects unapproved users", async () => {
const [, attacker] = await ethers.getSigners();
const fakeProof = await buildProof({ identity: attacker, message: attacker.address });
await expect(gated.connect(attacker).gatedAction(fakeProof))
.to.be.revertedWithCustomError(gated, "NotKYCVerified");
});
it("accepts users with the full policy", async () => {
const proof = await buildProof({
identity: alice,
message: alice.address,
scope: "my-dapp:mint",
groups: [25],
});
await expect(gated.connect(alice).gatedAction(proof)).to.not.be.reverted;
});
});How it works: Your contract calls into the deployed
HSKPassport verifier at 0x7d2E692A08f2fb0724238396e0436106b4FbD792. Users generate zero-knowledge proofs in-browser that satisfy your policy without revealing their identity. Each proof is caller-bound to prevent front-running and scoped per-action for sybil resistance.