> ## Documentation Index
> Fetch the complete documentation index at: https://docs.keystoneos.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# ComplianceRegistry

> On-chain compliance attestations gating settlement execution.

The ComplianceRegistry stores compliance attestations on-chain, authorized by an **M-of-N threshold of independent attester signatures** (EIP-712). The KeystoneSettlement contract consults this registry as its **execution gate**: every party hash bound at registration must be attested Pass before the contract pays out. Screening and attestation are the only steps that require KeyStone's off-chain involvement - everything else is handled autonomously by the contract.

## How it works

1. Off-chain compliance screening runs per party via LSEG World-Check (entity) and CipherOwl (wallet)
2. On a passing result, an EIP-712 `Attestation` message is signed by M of the N configured attester keys
3. The signed bundle is submitted on-chain: `attest(attestation, signatures)`. The submitter is not trusted - the signatures authorize, so no single key (and no submitter) can forge a Pass
4. At the last deposit (or a later `execute()` call), the KeystoneSettlement contract calls `areAllPartiesCleared(settlementId, partyHashes)` with the hashes bound at registration
5. If every bound hash has a passing attestation, the settlement executes; otherwise it stays registered until attestation lands

```mermaid theme={null}
%%{init: {'theme': 'base', 'themeVariables': {'actorBkg': '#1a3a35', 'actorBorder': '#2DD4A8', 'actorTextColor': '#e0f2ef', 'actorLineColor': '#2DD4A8', 'signalColor': '#2DD4A8', 'signalTextColor': '#e0f2ef', 'noteBkgColor': '#162e2a', 'noteTextColor': '#e0f2ef', 'noteBorderColor': '#1AAF8B', 'activationBkgColor': '#1a3a35', 'activationBorderColor': '#2DD4A8', 'labelBoxBkgColor': '#1a3a35', 'labelBoxBorderColor': '#2DD4A8', 'labelTextColor': '#e0f2ef', 'loopTextColor': '#2DD4A8'}}}%%
sequenceDiagram
    participant Oracle as Compliance Oracle
    participant LSEG as LSEG World-Check
    participant CO as CipherOwl
    participant CR as ComplianceRegistry
    participant K as KeystoneSettlement

    Oracle->>LSEG: Screen entity
    LSEG-->>Oracle: PASS
    Oracle->>CO: Screen wallet
    CO-->>Oracle: PASS

    Oracle->>Oracle: sign EIP-712 attestation with M of N attester keys
    Oracle->>CR: attest(attestation, signatures)

    Note over K: last deposit lands (or execute())
    K->>CR: areAllPartiesCleared(settlementId, partyHashes)
    CR-->>K: true
    K->>K: execute - atomic payout
```

## Functions

### attest()

Submit an M-of-N signed compliance attestation for a party in a settlement.

```solidity theme={null}
struct Attestation {
    bytes32 settlementId;   // UUID converted to bytes32
    bytes32 partyHash;      // keccak256(abi.encode(partyId_bytes32, walletAddress))
    uint8 status;           // must be Pass (1) - see below
    bytes32 referenceHash;  // keccak256 of the compliance provider's reference ID
    uint64 deadline;        // signature expiry (unix seconds)
}

function attest(Attestation calldata a, bytes[] calldata signatures) external whenNotPaused
```

The registry verifies the bundle before writing anything:

* **Pass only.** Only `Pass` attestations exist on-chain - a failed or flagged screening is never written to the registry; the gate simply never clears for that party. Any other status reverts `InvalidComplianceStatus`.
* **Deadline.** A bundle submitted after `deadline` reverts `AttestationExpired`, so a leaked old signature cannot be replayed indefinitely.
* **M-of-N signatures.** Each signature must recover to a configured attester, signers must be in strictly ascending address order (rejects duplicates), and at least the threshold M must be present - otherwise `SignersNotSortedOrDuplicate`, `UnknownAttester`, or `ThresholdNotMet`.
* **Sequence.** Re-attesting the same party increments a per-record `sequence` counter, so downstream systems can distinguish the first attestation from a re-screen.

<Warning>
  No personal data is stored on-chain. The `partyHash` is a one-way hash that cannot be reversed to identify the party. The `referenceHash` is a hash of the provider's reference ID, not the ID itself.
</Warning>

### isCleared()

Check if a specific party has a passing attestation.

```solidity theme={null}
function isCleared(
    bytes32 settlementId,
    bytes32 partyHash
) external view returns (bool)
```

### areAllPartiesCleared()

Check if every supplied party hash has a passing attestation for the settlement. This is the function the KeystoneSettlement contract calls as its execution gate, passing the party hashes bound at registration (the gate can never pass vacuously - registration rejects empty hash lists).

```solidity theme={null}
function areAllPartiesCleared(
    bytes32 settlementId,
    bytes32[] calldata partyHashes
) external view returns (bool)
```

## Party hash computation

Party hashes are computed as `keccak256(abi.encode(partyId_bytes32, walletAddress))`. The same inputs MUST be used at registration (KeystoneSettlement partyHashes) and at attestation, or the gate never clears:

| Check type              | partyId                          | walletAddress                  |
| ----------------------- | -------------------------------- | ------------------------------ |
| Entity-only (no wallet) | Settlement party UUID as bytes32 | Zero address (`0x0000...0000`) |
| Wallet-bound            | Settlement party UUID as bytes32 | The party's wallet address     |

This allows separate attestations for entity-level and wallet-level screening while maintaining a consistent hashing scheme.

## Status mapping

Off-chain, KeyStone tracks three screening outcomes per party: `PASS`, `FLAGGED`, and `FAIL`. On-chain, only one of them ever becomes a record:

| Screening outcome | On-chain effect                                                                         |
| ----------------- | --------------------------------------------------------------------------------------- |
| PASS              | `ComplianceStatus.Pass` (1) attested via M-of-N bundle - the gate clears for this party |
| FLAGGED           | Nothing written - the gate stays closed while the flag is resolved off-chain            |
| FAIL              | Nothing written - the gate stays closed permanently for this settlement                 |

Deposits are still accepted while the gate is closed - only execution is blocked. The `ComplianceStatus` enum retains `Flagged` (2) and `Fail` (3) values for ABI compatibility, but `attest` rejects them.

## Immutable M-of-N threshold

The attester set and the threshold are **fixed at deployment and immutable**: the contract has no function to add or remove an attester or to change the threshold. Changing the set means deploying a fresh registry, deploying KeystoneSettlement against it, and repointing the backend - a visible, coordinated, multi-step operation that cannot happen in a single transaction. There is no on-chain path to forge a Pass.

The pause authority (`PAUSER_ROLE`, with `DEFAULT_ADMIN_ROLE` existing only to rotate it) is **halt-only**: it can freeze new attestations in an emergency but cannot forge, alter, or delete one.

The current testnet deployments run a **2-of-3** attester set operated by KeyStone as an interim arrangement; the design goal is independent attester operators, at which point no single organization can clear the gate alone.

## Events

| Event                                                                          | When                                                     |
| ------------------------------------------------------------------------------ | -------------------------------------------------------- |
| `ComplianceAttested(settlementId, partyHash, status, referenceHash, sequence)` | Each successful `attest` (status is always Pass)         |
| `AttesterAdded(attester)`                                                      | Constructor only - one per attester in the immutable set |
| `ThresholdSet(threshold)`                                                      | Constructor only                                         |

## What KeyStone stores

KeyStone never stores raw KYC/AML data. The compliance flow stores:

| Layer                         | What is stored                                                                     |
| ----------------------------- | ---------------------------------------------------------------------------------- |
| Off-chain (KeyStone DB)       | Compliance status (pass/fail/flagged) + reference ID pointing to provider's record |
| On-chain (ComplianceRegistry) | Hashed party identifier + status enum + hashed reference ID                        |

The compliance provider (LSEG, CipherOwl) remains the source of truth for the actual screening data. KeyStone only records the outcome.
