Ariadne / patterns BlocksFlare research · 2026-09-22

Behaviour alone cannot flag a sanctioned wallet.
It can rank one.

Ethereum's full history — 546 million addresses, 145 behavioural measurements each — against the 124 addresses OFAC has named. Twenty-one measurements separate the two groups at better than one-in-a-thousand odds. None of them, alone or combined, gets anywhere near a usable alert. This page shows the separation that is real, the three ways it is easy to fake, and the one number that decides what a rule engine may do with it.

The question

Does an address's behaviour betray it, with no labels and no graph?

Ariadne's risk engine already has two backbones: known labels, and propagation across the transaction graph. This is a test of the third candidate — pure behaviour. If an address's own activity pattern separates sanctioned from ordinary, that is a signal available for every address on the chain, including ones no list has ever named.

Everything below comes from one BigQuery table (address_features, 546M rows, 496 GiB, built 2026-09-19) and two public label sources. Every number on this page was produced by a query whose cost is itemised at the bottom; the SQL is in the repository.

Ground truth is 124 addresses. The OFAC feeds carry 211 label rows, but those collapse to 124 distinct addresses — and 5 of those have never sent or received a native transaction. So 119 addresses carry any behaviour at all. Every recall figure on this page uses 119 as the denominator. With 119 positives against 46 candidate measurements, this is signal ranking, not a validated detector, and it is labelled that way throughout.

Trap 1 — activity

Sanctioned addresses are busy. Almost nothing else is.

Three quarters of Ethereum is dust: 77% of all addresses have three transactions or fewer. The sanctioned set looks nothing like that — it clusters in the moderately active middle. Compare the two groups directly and every measurement "separates" them, because every measurement is partly a proxy for how busy an address is.

So no comparison on this page is made against the population. Each sanctioned address is compared only against addresses in its own activity bucket — and, from Trap 2 onward, its own era as well.

Trap 2 — age

Older addresses have had longer to accumulate everything.

Features are cumulative and the window ends 2026-09-19, so an address first seen in 2017 has had eight years to gather counterparties and ETH; one from 2023 has had three. Match only on activity and the oldest addresses look distinctive for no reason but age.

The test: rebuild every baseline matched on activity bucket and era of first activity, then see which measurements move. If separation were an age artefact, points would fall off the diagonal.

They do not. Twenty-one of forty-six measurements clear the Bonferroni threshold either way, and the four that weaken are exactly the ones that are age by another name — lifespan_days, the two interval moments, gas price. The separation is real.

What separates

Twenty-one measurements, six behavioural axes, one modest effect size.

Each bar is the probability that a randomly chosen sanctioned address scores higher on that measurement than a randomly chosen ordinary address from the same activity bucket and era. 0.50 is no separation. The strongest measurement on the chain reaches 0.69.

Twenty-one is not twenty-one findings. Six of them are the token family measured six ways. Grouped by behavioural axis there are about six independent signals: token intensity, value retained, contract-surface narrowness, counterparty inflow, working rhythm, and execution cleanliness.

Spreading the sanctioned addresses out individually shows what an effect size of 0.69 actually looks like — a shifted cloud, not two separated groups. The shaded band is the middle half of the ordinary population.

The working rhythm

The most durable signal is a timetable, not a transaction.

Sanctioned addresses transact across the clock more evenly than their activity-matched peers, avoid the UTC small hours, and — the sharpest of the three — go quieter at weekends. Shares of an address's own activity, they are immune to both the activity and the age confound by construction — and of the eleven measurements that held in every era, the rhythm ones have the narrowest spread across eras.

Trap 3 — calibration

A 90th-percentile threshold does not fire on 10% of addresses.

The obvious way to turn a measurement into a rule is "flag the top decile of its activity bucket". On this data that rule is worse than a coin flip — because most measurements are mostly ties. Three quarters of addresses have zero unique function selectors, so "at or below the 10th percentile" means "at or below zero", which fires on nearly everyone.

Fixing it means calibrating on realised population share rather than nominal percentile, and refusing to fire at all in buckets where ties make the target unreachable. That single change moves the same eight predicates from 0.9× to 8×.

For the rule engine: a percentile threshold is a promise about false-positive rate that tied distributions silently break. Thresholds must be calibrated against the realised firing rate and re-checked whenever the population shifts — and a predicate that cannot hit its target rate in a segment must be disabled there, not fired anyway.

Drift

The strongest signal is the least durable.

Splitting the sanctioned set by when each address first appeared, and scoring each group against its own era-matched baseline, separates a signal that holds from one that happened to hold once. "Holds" has to mean two things at once — an effect in every era and overall significance — because a measurement that never separated anywhere trivially never stopped separating. Eleven of forty-six clear both bars.

Token intensity — the single strongest measurement overall, at 0.69 — does not separate pre-2019 sanctioned addresses at all (0.45, i.e. nothing). It only becomes strong from 2021 onward. Incoming ETH runs the opposite way. The finding is specific and dateable: the asset mix of sanctioned activity moved from native ETH to ERC-20 tokens, and a detector fitted before that shift would have degraded straight through it.

Plotting every measurement's overall strength against its worst era makes the trade-off explicit. Points far below the diagonal are strong on average and unreliable in practice — the strongest measurements are the furthest below it.

Confirmed by a temporal holdout: predicates fitted only on pre-2021 sanctioned addresses and tested on post-2021 ones lose half their recall once age-matching is applied, and three quarters of it without. Both runs are in the table below.

The shippable rule

Five predicates that held in every era.

Keeping only measurements that are era-stable and significant, one per behavioural axis, gives five predicates — value inflow, counterparty inflow, contract surface, weekly rhythm, daily rhythm. There is no sixth axis that clears both bars: the direction signal out_share is stable across eras but only reaches z = −3.1 against a threshold of 3.27, and every other stable survivor repeats an axis already represented. Each predicate is calibrated per activity bucket and era to fire on about 10% of the population.

Read the last column, not the lift column. Fourteen-times lift sounds like a detector. Against a base rate of 2.3 in ten million, fourteen-times lift means an address the rule flags has roughly a three-in-a-million chance of being on the list. Firing at two or more predicates means flagging 7.1 million addresses to catch 22 of the 119 known ones.

Two caveats, neither of which changes the order of magnitude. The false-positive rate is an upper bound: the unlabelled population certainly contains sanctioned addresses nobody has named yet. And the predicate set was chosen for era-stability using all four eras, so these five have no out-of-time estimate at all — their 14× is in-sample by construction. The holdout above tests a different, train-only-selected set, and its held-out lift of 2.2–2.8× is the closest available upper bound on what these would do against addresses sanctioned after they were fitted.

What this means for BlocksFlare

Behaviour cannot gate an alert. It can order a queue.

The platform's rule engine is at v0 — a beat-scan over three per-row predicates, with full detection explicitly out of 1.0. This analysis says what may and may not be added to it.

FindingWhat the engine should do
Posterior after a 14× behavioural rule is ~3×10⁻⁶. Never raise an alert from behaviour alone. Behaviour is a ranking input for addresses already surfaced by a label or by graph proximity — which is the ordering docs/precheck-contract.md already committed to. That decision is now measured rather than assumed.
Eleven measurements held across every era; the strongest did not. Ship the era-stable set (five axes' worth). Keep the token-intensity signal, but treat it as dated and re-fit it on a schedule — it is the strongest and the most perishable. Budget for re-fitting, not for a fixed rule.
Percentile thresholds break on tied distributions. Calibrate every threshold on realised firing rate per segment, store the realised rate next to the threshold, and disable a predicate in any segment where it cannot hit its target rate.
Rhythm signals are confound-proof and era-stable. Weekend share, night share and hour entropy are shares of an address's own activity, so they survive both activity and age matching. Cheapest to compute, most durable, least likely to need re-fitting.
A per-address lookup costs $0.0003 on a clustered table and $0.18 on the raw one. Any per-address scoring path must read a clustered projection. Scoring a thousand addresses against the raw table costs $183; against af_compact, $0.30.
Five sanctioned addresses are missing from the ground-truth view. ariadne.labeled_addresses inner-joins labels to ariadne.addresses and silently drops 5 of 124 — they exist in address_features but not in the accounts snapshot. The view needs to surface the gap rather than swallow it.

Cost & reproducibility

The whole analysis cost $3.17.

One curated pass over the 546M-row feature table produced a clustered projection; a stratified sample of it made every subsequent query effectively free. Dry-run first, hard byte cap on every query.