Revocation Epochs & Freshness
Fairway separates long-term KYC from short-term sanctions monitoring. KYC proofs include the time of verification; protocols enforce their own acceptance windows and checking latest sanctions epoch.
Purpose
Compliance freshness has two layers:
KYC Verification Timestamp → When was the user last verified?
Recorded once in proof metadata.
Protocols decide how old is “too old” (jurisdiction-specific).
Sanctions & Risk Epochs → Rolling counters that track dynamic changes.
Incremented whenever sanctions lists or risk profiles update.
Only addresses affected by a change are updated.
This layered approach avoids forcing users to re-KYC daily, while ensuring protocols can enforce up-to-date sanctions and AML checks.
Why It Matters
KYC validity → EU vs. US vs. emerging market rules differ (6 months, 1 year, etc.).
Sanctions screening → must be near real-time (lists update daily/weekly).
Risk monitoring → risk scores can change without invalidating full KYC.
Without this separation, you’d either:
Burden users with unnecessary re-checks, or
Accept stale compliance states.
How It Works
Cardano (Merkle UTXO)
The Compliance UTXO datum includes:
{
"merkle_root": "0xabc123...",
"kyc_verified_at": "2025-03-15T10:00:00Z",
"sanctions_epoch": 452,
"risk_profile_score": 17,
"midnight_ref": "0xdeadbeef#0",
"fairway_sig": "sig(...)"
}
kyc_verified_at
= fixed timestamp of when KYC was performed.sanctions_epoch
= current counter (increments on list/risk changes).risk_profile_score
= optional numeric risk measure (low=0, high=100).
EVM (EAS Attestation)
EAS attestations include the same fields:
{
"subject": "0xUserWallet",
"policyId": "POOL-A.KYCv2.EU",
"kyc_verified_at": "2025-03-15T10:00:00Z",
"sanctions_epoch": 452,
"risk_profile_score": 17,
"commitmentRoot": "0xabc123...",
"epoch": 452
}
Protocols pass
expectedEpochRoot
into the PolicyEngine for freshness.Off-chain rules define how old a
kyc_verified_at
can be.
Midnight (Proof Ledger)
ZK-proofs generated by the Cloud Agent are posted to Midnight with:
KYC verification timestamp
Current sanctions epoch
Serves as the immutable audit trail for regulators.
Diagram
flowchart TD
U[User completes KYC once] --> V[Vault stores PII + timestamp]
V --> A[Fairway Agent generates proof]
A --> M[Midnight Ledger (proof + kyc_verified_at + sanctions_epoch)]
M --> C[Cardano Merkle UTXO {root, timestamp, epoch, sig}]
M --> E[EVM Attestation {policyId, timestamp, epoch}]
S[Sanctions feed update] --> F[Cloud Agent updates sanctions_epoch and only impacted leaves]
F --> C
F --> E
Enforcement Logic
Check KYC freshness
Compare
kyc_verified_at
against protocol’s policy.Example: “only accept KYC within last 6 months.”
Check sanctions epoch
Ensure user’s proof includes the latest sanctions_epoch.
If out of date → reject.
Check risk score (optional)
Compare against configured thresholds.
Cardano (Aiken snippet)
let ok_kyc = ctx.tx.now - utxo.datum.kyc_verified_at <= rule.max_age
let ok_epoch = utxo.datum.sanctions_epoch == registry.current_epoch
EVM (Solidity snippet)
require(
block.timestamp - kycVerifiedAt <= maxAge,
"KYC_DENIED:STALE_KYC"
);
require(
latestEpochRoot == expectedEpochRoot,
"KYC_DENIED:STALE_SANCTIONS"
);
Benefits
Jurisdiction-aware → protocols choose their own KYC freshness policy.
User-friendly → users don’t re-KYC for every sanctions update.
Efficient → only affected addresses updated when sanctions lists change.
Audit-ready → Midnight provides historical proof trail.
Compliance Alignment
FATF Recommendation 10 (ongoing due diligence) → sanctions epoch updates.
AML Directives → periodic KYC refresh enforced per institution.
GDPR → only timestamps & hashed commitments on-chain.
Regulatory audits → auditors can query
kyc_verified_at
+epoch
state at time of tx.
Next Steps
See Build on Cardano → validator checks timestamps + epoch.
See Build on EVM → attestation freshness.
Review Trust Registries → how issuer keys evolve with epochs.
Last updated
Was this helpful?