UPC — Universal Private ComplianceConcepts

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

ContractProof SystemNotes
PlonkVerifierBLS12381PLONK/BLS12-381Default — 128-bit security, EIP-2537
PlonkVerifierBN254PLONK/BN254Legacy — 100-bit security
SemaphoreAdapterSemaphorePlug 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.

On this page