Інтеграція Chainalysis для моніторингу транзакцій
Chainalysis KYT (Know Your Transaction) — галузевий стандарт моніторингу крипто-транзакцій. Ключевої переваги: Chainalysis будує граф всієї історії блокчейна, не просто перевіряє пряму транзакцію — відслідковує кошти через кілька стрибків (непрямий вплив).
Архітектура KYT API
Chainalysis KYT v2 API працює за моделлю: реєструємо користувачів і переводи, отримуємо дані ризику. Два режими: синхронний (негайна відповідь) та асинхронний (webhook зворотний виклик для складних випадків).
class ChainalysisKYTService {
private readonly API_BASE = "https://api.chainalysis.com/api/kyt/v2";
// Реєстрація нового гаманця користувача
async registerUser(userId: string, asset: string): Promise<void> {
await this.apiCall("POST", "/users", { userId, asset });
}
// Скринінг вхідного перекладу (депозит)
async registerReceivedTransfer(params: {
network: string;
asset: string;
transferReference: string; // txHash для on-chain, або зовнішній ID
userId: string;
outputAddress: string;
assetAmount: number;
timestamp: string; // ISO 8601
}): Promise<ReceivedTransferResponse> {
return this.apiCall("POST", "/transfers/received", {
network: params.network,
asset: params.asset,
transferReference: params.transferReference,
userId: params.userId,
outputAddress: params.outputAddress,
assetAmount: params.assetAmount,
timestamp: params.timestamp,
});
}
// Скринінг вихідного перекладу (виведення)
async registerSentTransfer(params: {
network: string;
asset: string;
transferReference: string;
userId: string;
outputAddress: string;
assetAmount: number;
}): Promise<SentTransferResponse> {
return this.apiCall("POST", "/transfers/sent", params);
}
// Отримання сповіщень для перекладу
async getTransferAlerts(externalId: string): Promise<Alert[]> {
const response = await this.apiCall("GET", `/transfers/${externalId}/alerts`);
return response.alerts;
}
private async apiCall(method: string, path: string, body?: any): Promise<any> {
const response = await fetch(`${this.API_BASE}${path}`, {
method,
headers: {
"Token": this.apiKey,
"Content-Type": "application/json",
"Accept": "application/json",
},
body: body ? JSON.stringify(body) : undefined,
});
if (!response.ok) {
const error = await response.json();
throw new ChainalysisError(response.status, error.message);
}
return response.json();
}
}
Обробка відповіді на ризик
interface KYTRiskResponse {
externalId: string;
asset: string;
network: string;
transferReference: string;
updatedAt: string;
status: "APPROVED" | "BLOCKED" | "IN_REVIEW";
cluster: {
name: string;
category: string; // "darknet_market", "exchange", "sanctions" та ін.
} | null;
riskScore: number; // 0-100
}
async function handleDepositScreening(deposit: Deposit): Promise<void> {
const response = await chainalysis.registerReceivedTransfer({
network: deposit.blockchain,
asset: deposit.token,
transferReference: deposit.txHash,
userId: deposit.userId,
outputAddress: deposit.toAddress,
assetAmount: deposit.amount,
timestamp: deposit.timestamp.toISOString(),
});
// Чекаємо обробки (зазвичай 1-30 секунд)
const riskData = await pollForResult(response.externalId);
if (riskData.status === "BLOCKED" || riskData.riskScore >= 70) {
await db.freezeDeposit(deposit.id, riskData.riskScore, riskData.cluster?.category);
await alertComplianceTeam({
depositId: deposit.id,
userId: deposit.userId,
riskScore: riskData.riskScore,
category: riskData.cluster?.category,
externalId: response.externalId,
});
return;
}
if (riskData.status === "IN_REVIEW" || riskData.riskScore >= 40) {
await db.holdForManualReview(deposit.id, riskData.riskScore);
await createComplianceTask(deposit, riskData);
return;
}
await db.approveDeposit(deposit.id);
await creditUserBalance(deposit);
}
Webhook для асинхронних результатів
Chainalysis надсилає webhook при завершенні аналізу:
app.post("/webhooks/chainalysis", async (req, res) => {
const { externalId, asset, updatedAt, status, riskScore, cluster, alerts } = req.body;
// Знаходимо pending транзакцію
const deposit = await db.findDepositByExternalId(externalId);
if (!deposit) return res.status(404).send();
if (status === "BLOCKED" || riskScore >= 70) {
await db.freezeDeposit(deposit.id, riskScore, cluster?.category);
await alertCompliance(deposit, { riskScore, cluster, alerts });
} else if (status === "IN_REVIEW") {
await createManualReviewTask(deposit, { riskScore, alerts });
} else {
await approveDeposit(deposit.id);
}
res.status(200).send();
});
Категорії сповіщень та правила обробки
| Категорія | Дія |
|---|---|
| sanctions | Блокування + негайне сповіщення compliance |
| darknet_market | Блокування + SAR проект |
| ransomware | Блокування + SAR проект |
| stolen_funds | Блокування |
| mixing / tumbling | Перегляд + посилений моніторинг |
| p2p_exchange | Перегляд (залежить від обсягу) |
| gambling | Перегляд (залежить від ліцензії) |
| exchange | Зазвичай пропустити |
Інтеграція Chainalysis KYT для моніторингу депозитів та виведень з обробкою webhook і compliance alerting — 1-2 тижні розробки.







