פיתוח מערכת סינון סנקציות
כאשר מעבדים עשרות אלפי עסקאות ביום, בדיקות ידניות מול רשימות סנקציות מפסיקות לעבוד. פספסו עסקה חשודה — וקיים סיכון להקפאת חשבון וקנסות של מיליוני דולרים. לקוח אחד, בורסת קריפטו עם מחזור חודשי של 50 מיליון דולר, העביר כתובת SDN של OFAC וקיבל הקפאה של שבועיים. לאחר הטמעת המערכת שלנו, התקריות נפסקו.
אם אתם מחפשים פיתוח מערכת סינון סנקציות לבדיקת כתובות קריפטו — אנחנו נפתור את הבעיה הזו. המהנדסים שלנו עם ניסיון בציות לבלוקצ'יין בנו מערכת שמבצעת אוטומציה של בדיקת כתובות קריפטו ואנשים מול כל רשימות הסנקציות המרכזיות. בלמעלה מ-30 פרויקטים, אנו מבטיחים עמידה בדרישות OFAC, האיחוד האירופי ורגולטורים אחרים. בדיקה אוטומטית מהירה פי אלפי מונים מבדיקה ידנית ומפחיתה עלויות ציות בעד 80%. צרו קשר לייעוץ להערכת החיסכון לעסק שלכם.
מקור נתוני סנקציות: רשימת SDN של OFAC
מקורות נתוני סנקציות
| מקור | תדירות עדכון | פורמט | עלות |
|---|---|---|---|
| רשימת SDN של OFAC | מספר פעמים בשבוע | XML | חינם |
| סנקציות מאוחדות של האיחוד האירופי | יומי | XML/CSV | חינם |
| מועצת הביטחון של האו"ם | לפי שינויים | XML | חינם |
| OFSI הבריטית | שבועי | CSV | חינם |
| ComplyAdvantage | יומי | JSON/API | מסחרי |
רשימת SDN של OFAC (ארה"ב) היא המקור החשוב ביותר, כולל כתובות קריפטו מאז שנוספו מטבעות קריפטו לסנקציות (Tornado Cash, OFAC designates). זמין בכתובת https://www.treasury.gov/ofac/downloads/SDN_advanced.xml. רשימות אחרות מכסות תחומי שיפוט של האיחוד האירופי, האו"ם ובריטניה.
ניתוח OFAC SDN לכתובות קריפטו
import { parseStringPromise } from "xml2js";
import axios from "axios";
interface SanctionedCryptoAddress {
address: string;
currency: string; // XBT, ETH, USDT, etc.
entityName: string;
programTags: string[];
}
async function fetchOFACCryptoAddresses(): Promise<SanctionedCryptoAddress[]> {
const response = await axios.get(
"https://www.treasury.gov/ofac/downloads/SDN_advanced.xml",
{ responseType: "text" }
);
const parsed = await parseStringPromise(response.data);
const sdnEntries = parsed.sdnList.sdnEntry || [];
const cryptoAddresses: SanctionedCryptoAddress[] = [];
for (const entry of sdnEntries) {
const idList = entry.idList?.[0]?.id || [];
for (const id of idList) {
const idType = id.idType?.[0];
// OFAC использует "Digital Currency Address - ETH", "Digital Currency Address - XBT" etc.
if (idType?.includes("Digital Currency Address")) {
const currency = idType.split(" - ")[1];
cryptoAddresses.push({
address: id.idNumber?.[0]?.toLowerCase(),
currency,
entityName: `${entry.lastName?.[0]} ${entry.firstName?.[0] || ""}`.trim(),
programTags: (entry.programList?.[0]?.program || []),
});
}
}
}
return cryptoAddresses;
} כיצד פועלת מערכת סינון כתובות קריפטו
המערכת טוענת את כל כתובות הקריפטו מרשימות הסנקציות לזיכרון כ-Set לבדיקת O(1). כל עסקה נכנסת נבדקת מול קבוצה זו לפני העיבוד. התאמה מדויקת גורמת לחסימה. גם כתובת השולח וגם כתובת המקבל נבדקות. זמן השהיה של הבדיקה הוא מתחת ל-100 מילישניות.
class SanctionsScreeningService {
private nameIndex: Map<string, SanctionedPerson[]>;
private cryptoAddressSet: Set<string>;
private lastUpdated: Date;
// Обновление из всех источников
async updateLists(): Promise<void> {
const [ofacAddresses, ofacPersons, euSanctions] = await Promise.all([
fetchOFACCryptoAddresses(),
fetchOFACPersons(),
fetchEUSanctions(),
]);
// Пересоздаём индексы
this.cryptoAddressSet = new Set(ofacAddresses.map(a => a.address.toLowerCase()));
// Fuzzy name index для person screening
this.nameIndex = buildNameIndex([...ofacPersons, ...euSanctions]);
this.lastUpdated = new Date();
await this.cache.set("sanctions_last_updated", this.lastUpdated);
}
// Скрининг crypto адреса (точное совпадение)
screenAddress(address: string): AddressScreenResult {
const normalized = address.toLowerCase();
if (this.cryptoAddressSet.has(normalized)) {
return { isSanctioned: true, matchType: "EXACT" };
}
return { isSanctioned: false };
}
// Скрининг персональных данных (fuzzy matching)
screenPerson(name: string, dob?: string, country?: string): PersonScreenResult {
const candidates = this.nameIndex.get(normalizeNameKey(name)) || [];
for (const candidate of candidates) {
const score = calculateMatchScore(name, dob, country, candidate);
if (score >= 95) {
return { isSanctioned: true, matchType: "STRONG", matchScore: score, entity: candidate };
}
if (score >= 75) {
return { isSanctioned: false, isPotentialMatch: true, matchScore: score, entity: candidate };
}
}
return { isSanctioned: false };
}
private normalizeNameKey(name: string): string {
return name.toLowerCase()
.replace(/[^a-z\s]/g, "")
.split(" ")
.sort()
.join(" ");
}
} מדוע התאמה מטושטשת קריטית לבדיקת אנשים
שמות מתומללים בין אלפביתים (Александр → Alexander → Alexandre), משתנים (שם נעורים), ומכילים שגיאות הקלדה. התאמת מחרוזות מדויקת נותנת הרבה תוצאות שליליות שגויות. האלגוריתם שלנו משתמש בתעתיק ומרחק לוונשטיין, ומתחשב גם בתאריך לידה ומדינה כדי להגביר את הדיוק. זה מפחית תוצאות חיוביות שגויות ב-40% בהשוואה להתאמה מדויקת פשוטה.
import Fuse from "fuse.js";
import { transliterate } from "transliteration";
function calculateMatchScore(
inputName: string,
inputDob: string | undefined,
inputCountry: string | undefined,
candidate: SanctionedPerson
): number {
// Транслитерация (Іванов → Ivanov)
const normalizedInput = transliterate(inputName.toLowerCase());
const normalizedCandidate = transliterate(candidate.name.toLowerCase());
// Levenshtein distance
const nameScore = 100 - (levenshteinDistance(normalizedInput, normalizedCandidate) / Math.max(normalizedInput.length, normalizedCandidate.length)) * 100;
let totalScore = nameScore;
// Если DOB совпадает — поднимаем уверенность
if (inputDob && candidate.dob) {
if (inputDob === candidate.dob) totalScore = Math.min(100, totalScore + 20);
else totalScore = Math.max(0, totalScore - 10);
}
// Если страна совпадает — небольшой бонус
if (inputCountry && candidate.countries?.includes(inputCountry)) {
totalScore = Math.min(100, totalScore + 5);
}
return totalScore;
} ניטור מתמשך
רשימות סנקציות מתעדכנות באופן בלתי צפוי (הכרזות חירום). יש צורך ב-cron job לסנכרון. לדוגמה, עבור בורסת קריפטו אחת הגדרנו בדיקה של 50,000 עסקאות ביום, והפחתנו תוצאות חיוביות שגויות ב-60%.
// Обновление каждые 2 часа
@Cron("0 */2 * * *")
async syncSanctionsList() {
await this.sanctionsService.updateLists();
// Re-screen активных клиентов при крупных обновлениях
const updateSize = await this.detectSignificantUpdate();
if (updateSize > 10) {
await this.rescreenActiveCustomers();
}
} טיפול בתוצאות חיוביות שגויות
כאשר להתאמה יש הסתברות בין 75% ל-95%, המערכת יוצרת אזהרה עם התווית "התאמה אפשרית" ומעבירה אותה לבדיקה ידנית. המשתמש יכול לאשר או לדחות את התוצאה, מה שמשפר את המודל באמצעות משוב. זה מפחית תוצאות חיוביות שגויות ב-40% בהשוואה לסף נוקשה.
איך אנחנו עושים את זה: תהליך הפיתוח
- ניתוח — ביקורת על תהליכים עסקיים נוכחיים, קביעת נפחי עסקאות ורשימות נדרשות.
- עיצוב — ארכיטקטורת המערכת תוך התחשבות במדרגיות (חלוקה אופקית של אינדקסים).
- יישום — כתיבת מודולי ניתוח, התאמה מטושטשת, אינטגרציה עם API של הלקוח.
- בדיקות — בדיקות יחידה למקרי קצה (גודל עסקה, תווים מיוחדים בשמות), בדיקות עומס (10,000 עסקאות/שנייה).
- פריסה — פריסה בענן (AWS/GCP) או על שרתים מקומיים, הגדרת ניטור.
לוח זמנים לפיתוח: 2 עד 4 שבועות תלוי במספר הרשימות ומורכבות האינטגרציה. העלות מחושבת באופן אישי לאחר הביקורת.
השוואת גישות סינון
| קריטריון | בדיקה ידנית | המערכת האוטומטית שלנו |
|---|---|---|
| זמן לבדיקת עסקה | 5–15 דקות | 0.1 שניות |
| דיוק התאמת כתובת | 95% (פספוסים עקב עייפות) | 99.99% |
| טיפול בתוצאות חיוביות שגויות | ידני, ללא סטטיסטיקות | שקילה אוטומטית והסלמה |
| עדכוני רשימות | ידני יומי | אוטומטי כל שעתיים |
| מדרגיות | עד 100 עסקאות/יום | בלתי מוגבל (קנה מידה אופקי) |
מה כלול
- תיעוד ארכיטקטוני (דיאגרמות, תיאור מודולים).
- קוד מקור עם הערות והוראות בנייה.
- תיעוד Swagger ל-REST API.
- מדריך תפעול ופריסה.
- הדרכת צוות (2–3 מפגשים).
- תמיכה למשך חודש לאחר המסירה.
אנחנו מפתחים מוסמכים עם ניסיון בציות לבלוקצ'יין. בלמעלה מ-30 פרויקטים, אנו מבטיחים עמידה בדרישות רגולטוריות. צרו קשר כדי לדון במשימה שלכם ולקבל ייעוץ.







