AttestationHub & IAttestationVerifier
On-chain registry of ASPs and the IAttestationVerifier interface for plugging in any attestation backend.
AttestationHub & IAttestationVerifier
AttestationHub
AttestationHub is the on-chain registry of all registered ASPs for a given chain. It serves as a discovery mechanism — like Uniswap's token list, but for compliance providers.
interface IAttestationHub {
struct ASPInfo {
address verifier; // IAttestationVerifier implementation
string name;
string description;
string url; // Off-chain API endpoint
bool active;
}
function register(ASPInfo calldata info) external returns (uint256 aspId);
function getASP(uint256 aspId) external view returns (ASPInfo memory);
function publishRoot(uint256 aspId, bytes32 root) external;
function getLatestRoot(uint256 aspId) external view returns (bytes32);
function getRootHistory(uint256 aspId) external view returns (bytes32[] memory);
}Root History
When an ASP publishes a new Merkle root, the hub stores the last N roots (configurable per ASP). Users can prove membership against any root in the history window, providing resilience against timing issues.
Root TTL
Each root has a time-to-live (default: 24 hours). Proofs generated against expired roots are rejected by the verifier. ASPs should publish roots frequently enough that users don't encounter expired roots during normal operation.
IAttestationVerifier
The pluggable interface that any attestation backend implements:
interface IAttestationVerifier {
/// @notice Verify an attestation proof
/// @param proof Backend-specific proof bytes
/// @param inputs Public inputs (root, nullifier, etc.)
/// @return valid true if the proof is valid
function verify(
bytes calldata proof,
uint256[] calldata inputs
) external view returns (bool valid);
/// @notice Human-readable name
function name() external view returns (string memory);
}Built-in Implementations
| Contract | Proof System | Notes |
|---|---|---|
PlonkVerifierBLS12381 | PLONK/BLS12-381 | Default — 128-bit security, EIP-2537 |
PlonkVerifierBN254 | PLONK/BN254 | Legacy — 100-bit security |
SemaphoreAdapter | Semaphore | Plug in existing Semaphore groups |
Custom Verifier Example
contract WorldIDAdapter is IAttestationVerifier {
IWorldID public immutable worldId;
function verify(
bytes calldata proof,
uint256[] calldata inputs
) external view override returns (bool) {
(uint256 root, uint256 nullifier, uint256 signal) =
abi.decode(inputs, (uint256, uint256, uint256));
worldId.verifyProof(root, 1, signal, nullifier, proof);
return true; // reverts on failure
}
function name() external pure override returns (string memory) {
return "WorldID Adapter";
}
}Protocol Integration
Protocols verify compliance by calling the AttestationHub:
contract PrivatePool {
IAttestationHub public immutable hub;
function withdraw(
uint256 amount,
bytes calldata proof,
uint256 aspId
) external {
ASPInfo memory asp = hub.getASP(aspId);
bytes32 currentRoot = hub.getLatestRoot(aspId);
uint256[] memory inputs = new uint256[](2);
inputs[0] = uint256(currentRoot);
inputs[1] = computeNullifier(msg.sender);
require(
IAttestationVerifier(asp.verifier).verify(proof, inputs),
"Invalid compliance proof"
);
// ... withdrawal logic
}
}The AttestationHub is deployed per-chain. UPC SDK's deployments/ folder contains the canonical addresses for each supported network.