RaycashDocs
Protocol

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:

  1. Verifies the caller is the authorized attester
  2. Checks no active attestation exists for this recipient (one attestation per user)
  3. Stores attestations[recipient] = uid

Verify

Any contract can call hasValidKycAttestation(address) which checks three conditions:

  1. kycApproved == true in the attestation data
  2. Attestation has not expired
  3. 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 attester can 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 == false in attestation data
  • Attestation has expired (expirationTime > 0 && block.timestamp >= expirationTime; an expirationTime of 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

  1. Deploy SchemaRegistry
  2. Deploy EAS with SchemaRegistry address
  3. Deploy RaycashKycSchema library
  4. Deploy RaycashKycAttesterResolver with EAS address and attester address (requires library linking)
  5. Register the KYC schema: schemaRegistry.register("bool kycApproved", resolverAddress, true)

Was this page helpful?

On this page