Разработка системы KYC/AML для криптобиржи
KYC/AML для криптобиржи — это не форма "введите паспорт". Это комплексная система, которая решает regulatory требования лицензии, управляет рисками, и при этом не убивает conversion rate. Большинство бирж теряют 40-60% пользователей на KYC воронке — это проблема архитектуры и UX, не только регуляции.
Tiered KYC структура
Стандарт индустрии — многоуровневая верификация:
Tier 0 (без KYC): только просмотр платформы. Никаких транзакций.
Tier 1 (Email + AML check): до $1,000 USDC эквивалента в месяц. Депозит и вывод только криптовалюты. Автоматический wallet screening через Chainalysis или Elliptic при каждом депозите. Время верификации: < 1 минуты.
Tier 2 (Full KYC): до $50,000/месяц. Паспорт + liveness check. Провайдер: Sumsub, Onfido, Jumio. Время: автоматически 2-5 минут, ручная проверка до 24 часов для сложных случаев. Разблокируется: фиат ввод/вывод, криптовалютный вывод без ограничений.
Tier 3 (Enhanced Due Diligence): без лимитов. Source of Funds + Source of Wealth + расширенный background check. Только для VIP клиентов, ручная обработка compliance офицером.
Архитектура KYC сервиса
// KYC State Machine
enum KYCStatus {
NOT_STARTED = 'not_started',
PENDING = 'pending',
UNDER_REVIEW = 'under_review',
APPROVED = 'approved',
REJECTED = 'rejected',
RESUBMISSION_REQUIRED = 'resubmission_required',
}
interface UserKYCProfile {
userId: string;
tier: 0 | 1 | 2 | 3;
status: KYCStatus;
applicantId?: string; // Sumsub applicant ID
approvedAt?: Date;
expiresAt?: Date; // переверификация каждые N лет
riskScore: number; // 0-100, рассчитывается при апруве
pep: boolean; // Politically Exposed Person
sanctioned: boolean;
walletRiskLevels: Map<string, WalletRisk>; // кешированные оценки кошельков
}
class KYCService {
async initiateKYC(userId: string, targetTier: number): Promise<{ accessToken: string }> {
const accessToken = await this.sumsubClient.createApplicantToken(
userId,
this.getLevelName(targetTier)
);
await this.db.updateUserKYC(userId, {
status: KYCStatus.PENDING,
initiatedAt: new Date()
});
return { accessToken };
}
async processWebhook(payload: SumsubWebhook): Promise<void> {
const { applicantId, reviewResult, type } = payload;
const userId = await this.db.getUserByApplicantId(applicantId);
if (type === 'applicantReviewed' && reviewResult.reviewAnswer === 'GREEN') {
const riskData = await this.calculateRiskScore(applicantId);
await this.db.updateUserKYC(userId, {
status: KYCStatus.APPROVED,
tier: 2,
approvedAt: new Date(),
expiresAt: new Date(Date.now() + 2 * 365 * 24 * 60 * 60 * 1000), // 2 года
riskScore: riskData.score,
pep: riskData.pep,
});
await this.notifyUser(userId, 'kyc_approved');
await this.unlockFeatures(userId, 2);
} else if (reviewResult.reviewAnswer === 'RED') {
const reason = this.mapRejectionReason(reviewResult.reviewRejectType);
await this.handleRejection(userId, reason);
}
}
private async calculateRiskScore(applicantId: string): Promise<RiskData> {
const applicantData = await this.sumsubClient.getApplicant(applicantId);
// PEP проверка
const pepResult = await this.amlProvider.checkPEP(
applicantData.info.firstName,
applicantData.info.lastName,
applicantData.info.dob,
applicantData.info.country
);
// Sanctions проверка (OFAC, EU, UN)
const sanctionsResult = await this.amlProvider.checkSanctions(applicantData.info);
// Risk scoring
let score = 0;
if (pepResult.isPEP) score += 40;
if (sanctionsResult.isSanctioned) score = 100; // автоматический reject
if (HIGH_RISK_COUNTRIES.includes(applicantData.info.country)) score += 20;
return { score, pep: pepResult.isPEP, sanctioned: sanctionsResult.isSanctioned };
}
}
On-chain AML скрининг
Каждый входящий депозит и каждый вывод проверяется через wallet screening:
class WalletScreeningService {
async screenDepositAddress(
walletAddress: string,
asset: string,
amount: number,
userId: string
): Promise<ScreeningResult> {
// Кеш: одинаковый адрес не перепроверяем каждый раз
const cached = await this.cache.get(`wallet:${walletAddress}`);
if (cached && cached.age < 3600) return cached.result; // 1 час TTL
const [chainalysisResult, ellipticResult] = await Promise.all([
this.chainalysis.getAddressRisk(walletAddress, asset),
this.elliptic.getWalletRisk(walletAddress),
]);
const riskScore = Math.max(chainalysisResult.riskScore, ellipticResult.riskScore);
const categories = [...new Set([
...chainalysisResult.categories,
...ellipticResult.categories,
])];
const result: ScreeningResult = {
allowed: riskScore < 70 && !this.hasBlockedCategory(categories),
riskScore,
categories,
requiresReview: riskScore >= 40 && riskScore < 70,
};
await this.cache.set(`wallet:${walletAddress}`, { result, age: Date.now() });
await this.logScreening(userId, walletAddress, result);
if (!result.allowed) {
await this.alertComplianceTeam(userId, walletAddress, result);
}
return result;
}
private hasBlockedCategory(categories: string[]): boolean {
const BLOCKED = ['darknet_market', 'ransomware', 'stolen_funds', 'sanctions'];
return categories.some(c => BLOCKED.includes(c));
}
}
Transaction Monitoring (TM)
Ongoing мониторинг транзакций для выявления подозрительных паттернов после верификации:
Structuring detection: множество транзакций чуть ниже reporting threshold (классический smurfing).
Velocity monitoring: резкое увеличение активности — 10x от обычного объёма за день.
Round-trip detection: средства выводятся и возвращаются через несколько хопов.
Mixing/tumbling indicators: транзакции через известные mixing сервисы (Tornado Cash и аналоги).
class TransactionMonitor {
async analyzeTransaction(tx: Transaction): Promise<AlertLevel> {
const userHistory = await this.db.getUserTxHistory(tx.userId, 30); // 30 дней
const checks = await Promise.all([
this.checkStructuring(tx, userHistory),
this.checkVelocity(tx, userHistory),
this.checkGeographicAnomalies(tx),
this.checkTimePatterns(tx, userHistory),
]);
const maxLevel = Math.max(...checks.map(c => c.level));
if (maxLevel >= AlertLevel.HIGH) {
await this.createSAR(tx, checks.filter(c => c.level >= AlertLevel.MEDIUM));
}
return maxLevel;
}
private async checkStructuring(tx: Transaction, history: Transaction[]): Promise<Check> {
const threshold = await this.getReportingThreshold(tx.currency);
// Несколько транзакций в течение 24 часов суммарно выше threshold,
// каждая по отдельности ниже
const last24h = history.filter(h =>
Date.now() - h.timestamp < 86400000 && h.amount < threshold
);
const total24h = last24h.reduce((sum, h) => sum + h.amount, 0) + tx.amount;
if (total24h >= threshold * 0.9 && last24h.length >= 3) {
return { level: AlertLevel.HIGH, reason: 'structuring_detected' };
}
return { level: AlertLevel.NONE };
}
}
SAR (Suspicious Activity Report) автоматизация
При срабатывании алертов — автоматическое формирование черновика SAR для compliance офицера:
async function generateSARDraft(
userId: string,
transactions: Transaction[],
alerts: Alert[]
): Promise<SARDocument> {
const user = await getUserKYCData(userId);
return {
reportType: 'SUSPICIOUS_ACTIVITY',
filingEntity: COMPANY_DETAILS,
subject: {
name: `${user.firstName} ${user.lastName}`,
address: user.residenceAddress,
dob: user.dateOfBirth,
idNumber: user.documentNumber,
},
suspiciousActivity: {
dateRange: { from: transactions[0].date, to: transactions[transactions.length - 1].date },
totalAmount: transactions.reduce((sum, t) => sum + t.usdValue, 0),
description: generateNarrative(alerts, transactions),
alertTypes: alerts.map(a => a.type),
},
supportingTransactions: transactions.map(formatForSAR),
};
}
Технический стек
| Компонент | Технология |
|---|---|
| KYC провайдер | Sumsub (основной) / Onfido (резерв) |
| AML on-chain | Chainalysis KYT + Elliptic |
| PEP/Sanctions | Refinitiv World-Check или ComplyAdvantage |
| Transaction monitoring | кастомный + Chainalysis Reactor |
| SAR management | кастомный модуль compliance |
| Backend | Node.js + TypeScript + PostgreSQL |
| Queue | BullMQ (Redis) для async обработки |
Сроки разработки
| Компонент | Срок |
|---|---|
| KYC tier system + Sumsub интеграция | 3-4 нед |
| AML wallet screening | 2 нед |
| Transaction monitoring engine | 3-4 нед |
| SAR management система | 2 нед |
| Compliance dashboard | 2-3 нед |
| Testing + compliance review | 2-3 нед |
Полная KYC/AML система для биржи: 3-4 месяца разработки.







