Build gates for humans,
not identity databases.
Integrate a minimal soulbound humanity credential, or require a narrowly scoped age, nationality, or sanctions Boolean. All public artifacts use pinned EIP-712 domains and contract-enforced bindings.
Trust model
Self verifies passport facts. The issuer evaluates the private held credential and signs one Boolean attestation.
Only the reviewed Base Sepolia contracts are accepted. Mainnet, custom v2 provers, and fabricated credentials fail closed.
Canonical v1 descriptors
Consumers require the hash of an exact canonical string. No spaces or aliases are accepted.
age>=180xe3e8342a70f40c3ef2dacba55a24b87789c9ddaf64d9d329e304d6478e856e96age>=210xf46616a186a0a92a2ad1b0eb2ce5f36921c3801e79eb7f5d4bb4e1ed514de170nationality=ARG0x8c8663bf87a0b160b7d8d5bf5e44cdafd54e219d069061ff778b2a9483798d44sanctions-clear0xd414ccab0db9191e8802a047b1c0f135d0d054ed112952854cd54456966c47daNationality uses ISO 3166-1 alpha-3 uppercase codes: nationality=ARG, nationality=USA.
Request a v1 attestation
The holder calls the API from the verification app. Before signing, the server verifies the private credential signature and freshness, the subject's live SBT ownership, both contract issuer addresses, and the exact configured chain/verifier pair.
import { predicateContext } from "@ubi2/sdk";
const response = await fetch("https://proofofhumanity.org/api/predicate", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
credential, // private, holder-controlled v1 credential
credentialSig,
predicate: "age>=18",
consumer: APP_OR_CONTRACT_ADDRESS,
context: predicateContext("community:season-1"),
subject: connectedWallet,
nonce: cryptoNonce.toString(),
verifier: PREDICATE_VERIFIER_ADDRESS,
chainId: 84532,
}),
});
const { attestation, signature } = await response.json();Read-only verification
import { checkPredicateArtifact } from "@ubi2/sdk";
const result = await checkPredicateArtifact({
artifact: { chainId, verifier, attestation, signature },
rpcUrl,
presenter: connectedWallet,
consumer: APP_OR_CONTRACT_ADDRESS,
});
if (!result) throw new Error("predicate not satisfied");Consume v1 atomically on-chain
A state-changing consumer must call consume itself. The verifier requiresatt.consumer == msg.sender, checks subject and freshness, and marks the(subject, consumer, context, nonce) replay key as spent.
interface IPredicateVerifier {
struct PredicateAttestation {
address consumer;
bytes32 context;
bytes32 predicate;
bool result;
address subject;
uint32 epoch;
uint256 nonce;
}
function consume(
PredicateAttestation calldata att,
bytes calldata signature,
address presenter
) external returns (bool result);
}
bytes32 constant AGE_18 = keccak256("age>=18");
function join(
IPredicateVerifier.PredicateAttestation calldata att,
bytes calldata signature
) external {
require(att.consumer == address(this), "wrong consumer");
require(att.context == keccak256("community:season-1"), "wrong context");
require(att.predicate == AGE_18 && att.result, "18+ required");
require(verifier.consume(att, signature, msg.sender), "not eligible");
_addMember(msg.sender);
}Release network
Quick Launch exposes only Base Sepolia and only when both contract addresses are configured.
Security checklist
- Require the exact descriptor hash,
result == true, consumer, context, and subject your action expects. - Use
consumeinside state-changing flows; usecheckonly when replay is harmless. - Never accept a chain ID or verifier address that is not in your own allowlist.
- Keep the issuer key server-side in an HSM or managed signer; never expose it as
NEXT_PUBLIC_*. - Keep owner and issuer roles distinct: owner is the governance multisig; issuer is the narrow operational signer.
- Do not log held credentials, Self proof payloads, passport attributes, or issuer secrets.
- Reject every non-Base-Sepolia chain and require
PredicateVerifier.prover() == address(0).
Release boundary
This release is Base Sepolia only. Mainnet is intentionally unavailable, the predicate prover must stay unset, and a real Self callback-to-mint-to-predicate journey must be observed before launch readiness.