Интеграция AML-скрининга (Chainalysis, Elliptic, Crystal)
AML скрининг криптовалютных транзакций — обязательный элемент compliance для любой платформы, работающей с криптой. Задача: при каждом депозите или выводе проверить, не связан ли адрес с санкционными субъектами, даркнет маркетами, ворованными средствами или другими high-risk активностями.
Chainalysis KYT (Know Your Transaction)
Chainalysis — лидер рынка, используется большинством крупных бирж и регуляторами для forensics. API делится на несколько продуктов: KYT для transaction monitoring, Reactor для investigation, Kryptos для entity data.
KYT API интеграция
class ChainalysisClient {
private readonly baseURL = "https://api.chainalysis.com";
async registerAddress(address: string, asset: "USDT" | "ETH" | "BTC" | string): Promise<void> {
await this.post("/api/kyt/v2/users", {
userId: address,
asset,
});
}
async screenTransfer(params: {
asset: string;
network: string;
transferReference: string; // txHash или address
direction: "received" | "sent";
userId: string;
outputAddress?: string;
value?: string;
assetAmount?: number;
timestamp?: string;
}): Promise<TransferRisk> {
const response = await this.post("/api/kyt/v2/transfers", params);
return {
externalId: response.externalId,
riskScore: response.riskScore,
cluster: response.cluster,
status: response.status, // "APPROVED" | "BLOCKED" | "IN_REVIEW"
};
}
// Получение детальных alerts по переводу
async getTransferAlerts(externalId: string): Promise<Alert[]> {
const response = await this.get(`/api/kyt/v2/transfers/${externalId}/alerts`);
return response.alerts;
}
private async post(path: string, body: any): Promise<any> {
const response = await fetch(`${this.baseURL}${path}`, {
method: "POST",
headers: {
"Token": this.apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
return response.json();
}
}
Risk Score категории
| Score | Категория | Автоматическое действие |
|---|---|---|
| 0-39 | LOW | Пропустить |
| 40-69 | MEDIUM | Флаг для review |
| 70-100 | HIGH | Заблокировать |
| N/A | SEVERE | Заблокировать + SAR |
Категории которые дают автоматическую блокировку независимо от score:
-
darknet_market -
ransomware -
stolen_funds -
sanctions -
terrorist_financing
Обработка депозита
async function processDeposit(deposit: Deposit): Promise<DepositResult> {
// Регистрируем адрес если новый
await chainalysis.registerAddress(deposit.fromAddress, deposit.asset);
// Скринируем транзакцию
const risk = await chainalysis.screenTransfer({
asset: deposit.asset,
network: deposit.network,
transferReference: deposit.txHash,
direction: "received",
userId: deposit.userId,
value: deposit.usdValue.toString(),
assetAmount: deposit.amount,
});
if (risk.status === "BLOCKED") {
await freezeDeposit(deposit.id);
await notifyCompliance(deposit, risk);
return { status: "blocked", reason: risk.cluster?.category };
}
if (risk.status === "IN_REVIEW") {
await holdForReview(deposit.id);
await createComplianceTask(deposit, risk);
return { status: "pending_review" };
}
await creditUserAccount(deposit);
return { status: "approved" };
}
Elliptic Lens / Navigator
Elliptic — конкурент Chainalysis с похожим функционалом. Сильнее в DeFi screening и cross-asset трассировке.
class EllipticClient {
async getWalletRisk(address: string, asset: string): Promise<EllipticRisk> {
const response = await this.post("/v2/wallet/synchronous", {
subject: {
asset,
type: "address",
hash: address,
},
type: "wallet_exposure",
customer_reference: address,
});
return {
riskScore: response.risk_score, // 0-10 (у Elliptic другая шкала)
exposures: response.exposures, // разбивка по категориям
clusters: response.entities,
};
}
async getTransactionRisk(txHash: string, asset: string): Promise<EllipticRisk> {
return this.post("/v2/txs/synchronous", {
subject: { asset, type: "transaction", hash: txHash },
type: "indirect_exposure",
});
}
}
Elliptic score от 0 до 10 — нужна нормализация для единой risk логики если используете оба провайдера.
Crystal Blockchain (для Восточной Европы)
Crystal — европейский игрок, часто предпочтителен для проектов из СНГ/ЕС из-за цен и поддержки.
const crystalResponse = await axios.post(
"https://aml.crystalblockchain.com/api/v1/risks/check",
{
address,
currency: asset,
},
{
headers: { "X-Auth-Apikey": CRYSTAL_API_KEY },
}
);
// crystal возвращает: risk_score (0-100), signals (список сигналов)
Dual-provider стратегия
Для production: два провайдера снижают риск ложноотрицательных результатов. Логика: BLOCK если хотя бы один блокирует, REVIEW если хотя бы один флагирует.
async function dualProviderScreen(address: string, txHash: string): Promise<RiskDecision> {
const [chainalysisResult, ellipticResult] = await Promise.allSettled([
chainalysis.screenTransfer({ transferReference: txHash, ... }),
elliptic.getWalletRisk(address, asset),
]);
const c = chainalysisResult.status === "fulfilled" ? chainalysisResult.value : null;
const e = ellipticResult.status === "fulfilled" ? ellipticResult.value : null;
// Если один из провайдеров недоступен — используем другой
if (!c && !e) throw new Error("Both AML providers unavailable");
const maxScore = Math.max(
c?.riskScore ?? 0,
e ? e.riskScore * 10 : 0, // нормализация Elliptic 0-10 → 0-100
);
if (maxScore >= 70 || c?.status === "BLOCKED") return { decision: "BLOCK", score: maxScore };
if (maxScore >= 40) return { decision: "REVIEW", score: maxScore };
return { decision: "ALLOW", score: maxScore };
}
Интеграция одного AML провайдера (API + логика блокировки + compliance dashboard) — 1-2 недели. Dual-provider система с автоматическим failover — 2-3 недели.







