Opens your running SynapCores (Anti-money-laundering layering detection will be staged for a preview — nothing runs until you click Run). No instance yet? Install free in ~30s.
Share
Objective
Layering is the AML stage where dirty money is moved through a long chain of accounts to obscure
its origin. Compliance teams need to find chains longer than three hops where the value preserved
end-to-end is high. The graph view turns this into a single MATCH that traces money flow across
banks, jurisdictions, and shell companies in one shot.
// Look for chains of length >3 where every hop is over $50k AND >85% of the value
// is preserved end-to-end (a hallmark of pass-through layering).
MATCH path = (src:Account)-[t:TRANSFER*4..6]->(dst:Account)
WHERE ALL(r IN t WHERE r.amount > 50000)
WITH src, dst, path, t,
head(t).amount AS source_amount,
last(t).amount AS final_amount
WHERE toFloat(final_amount) / source_amount > 0.85
WITH src, dst, path,
[n IN nodes(path) | n.id] AS account_chain,
[n IN nodes(path) | n.country] AS country_chain,
length(path) AS hop_count,
reduce(s = 0, r IN relationships(path) | s + r.amount) AS total_volume
RETURN account_chain, country_chain, hop_count, total_volume
ORDER BY hop_count DESC, total_volume DESC;
What's happening
TRANSFER*4..6 only matches chains of at least four hops — exactly where layering lives. Direct
transfers and 2-hop "round-trip" patterns are ignored.
head(t) and last(t) access the first and last edges of the path to compute value retention,
the strongest layering signal: if 90% of $250k arrives at the destination, it likely passed
through unchanged.
country_chain reveals jurisdiction-hopping (US → KY → LV → BG → IT here) — auditors immediately
see the geographic footprint.
In SQL this is essentially impossible without an iterative loop in application code — recursive
CTEs that emit path arrays are a Postgres-specific extension and still require complex window
arithmetic for retention scoring.
Run this incrementally on every new transfer to alert the FIU within seconds, not after a batch.
Try this next
MATCH (a:Account)-[t:TRANSFER]->(b:Account)
WHERE a.country <> b.country AND t.amount > 100000
RETURN a.country AS from_country, b.country AS to_country,
count(t) AS transfers, sum(t.amount) AS volume
ORDER BY volume DESC;
MATCH path = (a:Account)-[:TRANSFER*3..]->(z:Account)
WHERE NOT (z)-[:TRANSFER]->()
RETURN a.holder AS originator, z.holder AS terminus, length(path) AS hops
ORDER BY hops DESC LIMIT 5;
MATCH (a:Account)-[t:TRANSFER]->(b:Account)
WITH a, count(t) AS out_degree
WHERE out_degree >= 3
RETURN a.id, a.holder, a.country, out_degree
ORDER BY out_degree DESC;