KYC Attestation
EAS-based on-chain KYC verification with single-attester policy.
The attestation system provides on-chain KYC verification using the Ethereum Attestation Service (EAS). A single authorized attester creates KYC attestations for verified users. Hook contracts query attestation status to enforce compliance at transfer time.
Architecture
Attestation Lifecycle
Create
The authorized attester calls EAS to create a KYC attestation. EAS invokes the RaycashKycAttesterResolver.onAttest callback, which:
- Verifies the caller is the authorized
attester - Checks no active attestation exists for this recipient (one attestation per user)
- Stores
attestations[recipient] = uid
Verify
Any contract can call hasValidKycAttestation(address) which checks three conditions:
kycApproved == truein the attestation data- Attestation has not expired
- Attestation has not been revoked
Revoke
The authorized attester revokes an attestation via EAS. After revocation, hasValidKycAttestation returns false for that address. The user can no longer finalize wraps or transfer — but can still unwrap (exit always callable).
Re-Attest
After revocation, the attester can create a new attestation for the same address. The resolver allows this because the previous attestation is no longer active (revoked).
RaycashKycAttesterResolver
The resolver is an EAS SchemaResolver that enforces single-attestation-per-recipient KYC policy.
Schema
The on-chain schema string is "bool kycApproved". The RaycashKycSchema library handles encoding/decoding:
struct KycData {
bool kycApproved;
}Rules
- Only the authorized
attestercan create or revoke attestations - At most one active (non-revoked) attestation per recipient
- Duplicate attestation for an already-active recipient reverts with
ActiveAttestationExists
hasValidKycAttestation
function hasValidKycAttestation(address recipient) public view returns (bool)Returns false when:
- No attestation recorded for the recipient
kycApproved == falsein attestation data- Attestation has expired (
expirationTime > 0 && block.timestamp >= expirationTime; anexpirationTimeof 0 means no expiry) - Attestation has been revoked (
revocationTime > 0)
Connection to Hooks
The RaycashKycHook calls hasValidKycAttestation as a transfer hook:
- Wrap finalization — Recipient must have valid KYC (checked via transfer hook on mint)
- Transfer — Both sender and receiver must have valid KYC (sender check skipped on mint)
KYC revocation does not block unwraps — the exit-always-callable invariant takes precedence over compliance enforcement.
Deployment Order
- Deploy
SchemaRegistry - Deploy
EASwith SchemaRegistry address - Deploy
RaycashKycSchemalibrary - Deploy
RaycashKycAttesterResolverwith EAS address and attester address (requires library linking) - Register the KYC schema:
schemaRegistry.register("bool kycApproved", resolverAddress, true)
Was this page helpful?