Настройка соответствия FATF Travel Rule
FATF Рекомендація 16 (Travel Rule) вимагає, щоб при переводі віртуальних активів між VASPs передавалася інформація про відправника та одержувача. Це найбільш технічно складна вимога в пакеті FATF R15-16: немає єдиного протоколу, кілька конкуруючих рішень, і проблема "sunrise issue" коли один VASP compliant, а інший — ні.
Що вимагає Travel Rule
Originator інформація (відправник):
- Ім'я
- Номер рахунку (blockchain адреса або account ID)
- Фізична адреса / дата народження / номер національного ID (одне з)
Beneficiary інформація (одержувач):
- Ім'я
- Номер рахунку
Поріг: FATF рекомендує $1,000/€1,000. Реальність: різні юрисдикції встановили різні пороги. EU TFR: €0 (всі переводи). США FinCEN: $3,000. Більшість інших: $1,000.
Архітектурні рішення
Провайдери Travel Rule messaging
Не кожен VASP будує власну інфраструктуру — є спеціалізовані messaging networks:
Notabene: найбільший охват (500+ VASP members), SSI-based (Self-Sovereign Identity), RESTful API.
Sygna Bridge: сильна в Азії (BiTSO, OKX, Huobi використовують), добре для Asian market compliance.
Veriscope (Shyft Network): blockchain-based Travel Rule, decentralized.
TRP (Travel Rule Protocol, SWIFT): для крупних банків з крипто arms.
OpenVASP: відкритий протокол, peer-to-peer, не потребує hub. Менше adoption, але технічно цікавий.
Інтеграція Notabene
import { Notabene } from "@notabene/javascript-sdk";
const notabene = new Notabene({
audience: "https://api.notabene.id",
clientId: NOTABENE_CLIENT_ID,
clientSecret: NOTABENE_CLIENT_SECRET,
vaspDID: MY_VASP_DID,
});
// Створення вихідного Travel Rule перекладу
async function createTravelRuleTransfer(withdrawal: Withdrawal): Promise<string> {
const transfer = await notabene.transfers.create({
transactionAsset: withdrawal.asset,
transactionAmount: withdrawal.amount.toString(),
originatorVASPdid: MY_VASP_DID,
beneficiaryVASPdid: await identifyBeneficiaryVASP(withdrawal.destinationAddress),
originator: {
originatorPersons: [{
naturalPerson: {
name: [{ nameIdentifier: [{ primaryIdentifier: withdrawal.userLastName,
secondaryIdentifier: withdrawal.userFirstName }] }],
},
geographicAddress: [{ streetName: withdrawal.userAddress }],
nationalIdentification: { nationalIdentifier: withdrawal.userIdNumber },
}],
accountNumber: [MY_VASP_ADDRESS_MAPPING[withdrawal.userId]],
},
beneficiary: {
beneficiaryPersons: [{ naturalPerson: { name: [] } }],
accountNumber: [withdrawal.destinationAddress],
},
transactionBlockchainInfo: {
origin: withdrawal.fromAddress,
destination: withdrawal.destinationAddress,
},
});
return transfer.id;
}
Ідентифікація VASP за адресою
Ключове завдання: визначити, чи належить адреса призначення іншому VASP (hosted wallet) чи це unhosted wallet.
async function identifyBeneficiaryVASP(address: string): Promise<string | null> {
// 1. Notabene VASP lookup (база даних VASP адрес)
const vaspLookup = await notabene.addresses.lookup({ address });
if (vaspLookup.vasp) return vaspLookup.vasp.did;
// 2. Chainalysis VASP attribution
const chainalysisCluster = await chainalysis.getCluster(address);
if (chainalysisCluster?.type === "exchange" || chainalysisCluster?.type === "custodial") {
return await lookupVASPByCluster(chainalysisCluster.name);
}
// 3. Якщо не визначено — unhosted wallet
return null;
}
Unhosted Wallet Policy
FATF допускає спрощений підхід для переводів на unhosted wallets (особисті гаманці користувачів). Але багато регуляторів (ЕС, Швейцарія) вимагають додаткові заходи:
async function handleUnhostedWalletWithdrawal(
userId: string,
destinationAddress: string,
amount: number
): Promise<void> {
const usdAmount = await convertToUSD(amount);
if (usdAmount >= UNHOSTED_WALLET_VERIFICATION_THRESHOLD) {
// Вимагаємо proof of wallet ownership
const ownershipProof = await requestWalletOwnershipProof(userId, destinationAddress);
if (!ownershipProof.verified) {
throw new Error("Wallet ownership verification failed");
}
// Записуємо в travel rule файл (без відправки — нема receiving VASP)
await db.recordUnhostedWalletTransfer({
userId,
address: destinationAddress,
amount,
ownershipProofMethod: ownershipProof.method,
verifiedAt: new Date(),
});
}
await executeWithdrawal(userId, destinationAddress, amount);
}
// Верифікація володіння гаманцем — message підписання
async function requestWalletOwnershipProof(
userId: string,
address: string
): Promise<OwnershipProof> {
const challenge = crypto.randomBytes(32).toString("hex");
// Зберігаємо challenge, чекаємо підпису від користувача
await db.storeWalletChallenge(userId, address, challenge);
// Користувач підписує challenge своїм гаманцем
// Верифікація відбувається в іншому endpoint
return { pending: true, challenge };
}
Sunrise Issue
"Sunrise issue" — ситуація коли receiving VASP не підтримує Travel Rule. Варіанти:
- Не відправляти доки нема підтвердження — строго compliant, погана UX.
- Best efforts — відправляємо Travel Rule дані якщо можемо, логуємо спроби. Прийнято більшістю регуляторів як temporary measure.
- Delay + retry — тримаємо транзакцію в pending, повторюємо запити до receiving VASP при інтервалах.
Рекомендація: best efforts підхід з повним логуванням всіх спроб та відповідей.
Технічний стек
| Компонент | Рішення |
|---|---|
| Travel Rule messaging | Notabene SDK |
| VASP identification | Notabene + Chainalysis |
| Wallet ownership proof | EIP-191 message signing |
| Records storage | PostgreSQL + шифрування |
| Compliance dashboard | React admin panel |
Настройка FATF Travel Rule compliance з Notabene інтеграцією, unhosted wallet policy та compliance dashboard: 3-5 тижнів.







