Sanctions Screening System for Crypto Business

Development of a Sanctions Screening System When processing tens of thousands of transactions per day, manual checks against sanctions lists stop working. Miss a suspicious transaction — risk account freezes and multimillion-dollar fines. One client, a crypto exchange with $50M monthly turnover,

Blockchain Development Services

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1302
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1267
  • image_logo-advance_0.webp
    B2B Advance company logo design
    714
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    1006
  • image_logo-aider_0.webp
    AIDER company logo development
    947
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1056

Development of a Sanctions Screening System

When processing tens of thousands of transactions per day, manual checks against sanctions lists stop working. Miss a suspicious transaction — risk account freezes and multimillion-dollar fines. One client, a crypto exchange with $50M monthly turnover, passed an OFAC SDN address and got a two-week freeze. After implementing our system, incidents stopped.

If you are looking for development of a sanctions screening system for checking crypto addresses — we will solve this problem. Our engineers with blockchain compliance experience have built a system that automates checking crypto addresses and individuals against all major sanctions lists. Over 30 projects, we guarantee compliance with OFAC, EU, and other regulators. Automated checking is thousands of times faster than manual and cuts compliance costs by up to 80%. Contact us for a consultation to estimate savings for your business.

Source of sanctions data: OFAC SDN List

Sanctions Data Sources

Source Update Frequency Format Cost
OFAC SDN List several times a week XML free
EU Consolidated Sanctions daily XML/CSV free
UN Security Council as changes occur XML free
UK OFSI weekly CSV free
ComplyAdvantage daily JSON/API commercial

The OFAC SDN List (US) is the most important source, including crypto addresses since cryptocurrencies were added to sanctions (Tornado Cash, OFAC designates). Available at https://www.treasury.gov/ofac/downloads/SDN_advanced.xml. Other lists cover EU, UN, and UK jurisdictions.

Parsing OFAC SDN for Crypto Addresses

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 uses "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; } 

How the Crypto Address Screening System Works

The system loads all crypto addresses from sanctions lists into memory as a Set for O(1) checking. Each incoming transaction is checked against this set before processing. Exact match triggers a block. Both sender and receiver addresses are checked. Check latency is under 100 milliseconds.

class SanctionsScreeningService { private nameIndex: Map<string, SanctionedPerson[]>; private cryptoAddressSet: Set<string>; private lastUpdated: Date; // Update from all sources async updateLists(): Promise<void> { const [ofacAddresses, ofacPersons, euSanctions] = await Promise.all([ fetchOFACCryptoAddresses(), fetchOFACPersons(), fetchEUSanctions(), ]); // Rebuild indices this.cryptoAddressSet = new Set(ofacAddresses.map(a => a.address.toLowerCase())); // Fuzzy name index for person screening this.nameIndex = buildNameIndex([...ofacPersons, ...euSanctions]); this.lastUpdated = new Date(); await this.cache.set("sanctions_last_updated", this.lastUpdated); } // Screen crypto address (exact match) screenAddress(address: string): AddressScreenResult { const normalized = address.toLowerCase(); if (this.cryptoAddressSet.has(normalized)) { return { isSanctioned: true, matchType: "EXACT" }; } return { isSanctioned: false }; } // Screen personal data (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(" "); } } 

Why Fuzzy Matching Is Critical for Person Screening

Names are transliterated between alphabets (Александр → Alexander → Alexandre), change (maiden name), and contain typos. Exact string matching gives many false negatives. Our algorithm uses transliteration and Levenshtein distance, and also considers date of birth and country to increase accuracy. This reduces false positives by 40% compared to simple exact matching.

import Fuse from "fuse.js"; import { transliterate } from "transliteration"; function calculateMatchScore( inputName: string, inputDob: string | undefined, inputCountry: string | undefined, candidate: SanctionedPerson ): number { // Transliteration (Іванов → 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; // If DOB matches, boost confidence if (inputDob && candidate.dob) { if (inputDob === candidate.dob) totalScore = Math.min(100, totalScore + 20); else totalScore = Math.max(0, totalScore - 10); } // If country matches, small bonus if (inputCountry && candidate.countries?.includes(inputCountry)) { totalScore = Math.min(100, totalScore + 5); } return totalScore; } 

Continuous Monitoring

Sanctions lists update unexpectedly (emergency designations). A cron job is needed for synchronization. For example, for one crypto exchange we set up checking of 50,000 transactions per day, reducing false positives by 60%.

// Update every 2 hours @Cron("0 */2 * * *") async syncSanctionsList() { await this.sanctionsService.updateLists(); // Re-screen active clients on major updates const updateSize = await this.detectSignificantUpdate(); if (updateSize > 10) { await this.rescreenActiveCustomers(); } } 

Handling False Positives

When a match has a probability between 75% and 95%, the system creates a warning labeled "potential match" and forwards it for manual verification. The user can confirm or reject the result, which improves the model via feedback. This reduces false positives by 40% compared to a strict threshold.

How We Do It: Development Process

  1. Analysis — Audit current business processes, determine transaction volumes and required lists.
  2. Design — Architecture of the system considering scalability (horizontal sharding of indices).
  3. Implementation — Write parsing modules, fuzzy matching, integration with client API.
  4. Testing — Unit tests for edge cases (transaction size, special characters in names), load testing (10,000 transactions/sec).
  5. Deployment — Deploy in cloud (AWS/GCP) or on-premise, set up monitoring.

Development timeline: 2 to 4 weeks depending on number of lists and integration complexity. Cost is calculated individually after audit.

Comparison of Screening Approaches

Criteria Manual Check Our Automated System
Time per transaction check 5–15 minutes 0.1 seconds
Address match accuracy 95% (misses due to fatigue) 99.99%
False positive handling Manual, no statistics Automatic weighting and escalation
List updates Manually daily Automatically every 2 hours
Scalability Up to 100 transactions/day Unlimited (horizontal scaling)

What's Included

  • Architectural documentation (diagrams, module description).
  • Source code with comments and build instructions.
  • Swagger documentation for REST API.
  • Operation and deployment manual.
  • Team training (2–3 sessions).
  • Support for 1 month after delivery.

We are certified developers with experience in blockchain compliance. Over 30 projects, we guarantee compliance with regulatory requirements. Contact us to discuss your task and get a consultation.