Catching Fake Crypto Volume with a Graph: Building a Wash-Trade Investigator on FalkorDB
Built for Graph Hacks: Context for AI Agents (WeMakeDevs x FalkorDB), Track 1: Agents That Act on Connected Data. Code: https://github.com/bibliophile17/Wash-Trade-Investigator-graph-agent-on-FalkorDB The prob
Built for Graph Hacks: Context for AI Agents (WeMakeDevs x FalkorDB), Track 1: Agents That Act on Connected Data.
Code: https://github.com/bibliophile17/Wash-Trade-Investigator-graph-agent-on-FalkorDB
The problem: fake volume that looks real
Wash trading is when a group of wallets trade a token back and forth among themselves so it looks popular. High volume attracts real buyers, and the people behind the ring cash out.
The hard part is that every single trade looks normal. A price, an amount, two wallets. If you look at trades as rows in a table, nothing stands out.
The fraud is in the shape: money moving A -> B -> C -> A, again and again, between wallets that were all funded by the same source. That is a pattern of relationships, and that is what graphs are good at.
What I built
Wash-Trade Investigator is an agent that investigates a token and does three things:
- Decides whether the token's volume is real, using graph queries as its tools.
-
Acts: flags the suspicious wallets and writes a
Casenode linked to them in the graph. - Explains: draws the exact subgraph of wallets, trades and funders behind the decision.
The graph model is small:
(:Wallet)-[:TRADED {token, amount, ts}]->(:Wallet)
(:Wallet)-[:FUNDED_BY]->(:Funder)
(:Case)-[:FLAGGED]->(:Wallet)
My seed script creates 300 normal wallets with random trades, plus two planted wash-trading rings. The ring membership is never stored in the graph, so the agent has to discover it.
How the agent investigates
Each tool is one Cypher query. First it looks for wallets trading in circles:
MATCH (a:Wallet)-[x:TRADED]->(b:Wallet)-[y:TRADED]->(a)
WHERE x.token = $t AND y.token = $t AND a.id < b.id
RETURN a.id, b.id, count(x) AS legs
A single round trip can happen by chance, so I only treat pairs with repeated round trips as signals, and I also look for three-wallet cycles. Wallets connected by these cycles are merged into clusters.
For each cluster the agent then asks:
- How much of the token's volume stays inside the cluster? In the ring it found, 97.5%.
- Are the trade sizes suspiciously uniform? Trade sizes varied by only 5.6%.
- Do the wallets share a funder? All six were funded by the same wallet.
- Who is the hub? FalkorDB's PageRank picked one wallet out of the ring.
If the evidence adds up, it flags the cluster. In my test it flagged exactly the six planted wallets. It also found a second cluster of three wallets with circular trades, but only 50% internal volume, widely varying trade sizes and no shared funder, so it correctly left them alone. That second case matters: an agent that flags everything isn't useful.
Two modes
The agent runs in two modes with the same tools. In LLM mode (with an API key) a model chooses which tools to call. Without a key, an offline mode runs a fixed playbook with a transparent scoring rule. I made the offline mode so the project works for anyone who clones it, with nothing but a FalkorDB instance.
What I learned
Graphs make the question easy to ask. "Find wallets that trade in circles and share a funder" is a few lines of Cypher. In SQL it would be a pile of self-joins.
Let the graph do the evidence. Because the agent works from query results, every decision comes with the nodes and edges behind it. The explanation is not an extra step, it is the output of the investigation.
Write the action back to the graph. Creating a Case node linked to the flagged wallets means the decision becomes part of the data, ready for later investigations to build on.
Setup friction is real. I don't have Docker on my laptop, so FalkorDB Cloud's free tier saved me. I also lost time because I pasted the host and port together and got a confusing host:port:port connection error. After that I made my connection code strip a port from the host. Small lesson: validate your own config inputs.
Build with ground truth. Planting the rings myself meant I could check the agent's answer instead of guessing whether it worked.
What's next
- Real chain data instead of synthetic trades, and timing-based signals
- Tuned scoring thresholds
- Exposing the same graph tools through an MCP server so any agent client can use them
- Streaming the agent's tool calls live in the UI
Thanks to WeMakeDevs and FalkorDB for the hackathon. If you want to try it, the README has setup steps for both FalkorDB Cloud and Docker.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.