Skip to Content
DocumentContractsIdentityGate
Protocol reference

Multi-CA aggregator that fronts the IIdentityRegistry interface for the protocol.

IdentityGate is the contract every flow consults to check whether a given address is identity-verified. It’s a thin aggregator — verification logic itself lives in one or more underlying IIdentityRegistry instances (one per CA / attestation issuer); the gate combines their results so callers (CommitmentPool, RelayerRegistry) only need to know about a single address.

Conceptual overview: see Architecture / Identity gate. This page is the on-chain reference.

Storage

IIdentityRegistry[] public registries; // ordered list of CAs mapping(address => bool) public registryExists; // dedupe set uint256 public constant MAX_REGISTRIES = 10;

The gate itself has no per-user storage — every read fans out to the registered CAs.

External entry points

Registry management (owner-only)

function addRegistry(address _registry) external; function removeRegistry(address _registry) external; function getRegistryCount() external view returns (uint256); function getRegistries() external view returns (address[] memory);

removeRegistry keeps at least one entry (NoRegistries revert if you’d empty the gate); add/remove cycles use a swap-and-pop on the registries array.

IIdentityRegistry implementation

function isVerified(address user) external view returns (bool); function verifiedUntil(address user) external view returns (uint64); function paused() external view returns (bool);
MethodBehaviour
isVerified(user)true if any registered CA returns true. Reverts in any one CA are caught and skipped — one misbehaving registry can’t DoS the whole gate.
verifiedUntil(user)Latest expiry across all CAs (0 = unverified everywhere).
paused()Conservative: returns true if any one CA is paused. Doesn’t actually block isVerified (other CAs can still pass).

Deployment pattern

zkScatter’s reference deployment uses two separate gate instances:

InstanceWired intoWhat it gates
User IdentityGateCommitmentPool (deposit + withdraw)End-user deposits and withdrawals
Relayer IdentityGateRelayerRegistry.identityRegistryRelayer registration

Each gate can hold its own list of CAs — e.g. relayer registration might require institutional KYC while user deposits accept retail attestations.

Events

event RegistryAdded(address indexed registry); event RegistryRemoved(address indexed registry);

Errors

ErrorWhen
RegistryAddressZeroaddRegistry(address(0)) or constructor with zero
RegistryAlreadyAddedAdding the same registry twice
RegistryNotFoundremoveRegistry for an address that isn’t registered
NoRegistriesremoveRegistry would empty the list
TooManyRegistriesMore than MAX_REGISTRIES (10)
RenounceOwnershipDisabledrenounceOwnership() is bricked to prevent lockout

Where the underlying CAs come from

The actual zk-X509 verification logic lives in the IIdentityRegistry instances the gate aggregates over — zkX509IdentityRegistry, sanctions wrappers, manual allowlists, etc. Each must implement:

interface IIdentityRegistry { function isVerified(address user) external view returns (bool); function verifiedUntil(address user) external view returns (uint64); function paused() external view returns (bool); }

The gate doesn’t dictate which scheme they use; a CA could be a zk-X509 verifier, a curated manual allowlist, or a sanctions denial layer (note that “denial layers” are typically wired elsewhere — CommitmentPool.sanctionsList is its own slot).

SDK status

The @zkscatter/sdk/identity module — client-side zk-X509 proof generation and attest() calldata — ships in Phase 7. Phase 0–3 of the SDK don’t expose identity primitives; the gate is wired into the contracts but apps expect the user to have already attested via a separate flow.

Address by network

NetworkConfig.contracts.identityGate. ABI ships as IDENTITY_GATE_ABI / IDENTITY_GATE_IFACE.

Last updated on