Shot analysis detects a technique, and a technique can be changed. Nobody can collude without moving value in one direction, repeatedly — that is not a tell, it is the definition. This runs against the double-entry ledger you already keep, needs no shot logs, and works from the first frame played.
And one signal a custodial platform cannot have. Your payouts land at addresses you derived, on a public chain. If two accounts’ payout outputs are later spent as inputs to the same transaction, they were signed by the same party. Common-input-ownership is the oldest tool in chain analysis and non-custody is precisely what makes it available to you. It is the difference between a statistical shape and evidence.
An adjacency matrix rather than a node graph, because a farm has a shape you can see: a bright column. Rows lose, columns win, intensity is LTC.
Net flow alone flags every good player. What separates a farm is where the money comes from, whether anything ever goes back, and whether the feeders do anything else at all.
Run over the payout addresses you already derived, against transactions already public. No cooperation from anyone required.
| Case | Signal | Verdict |
|---|---|---|
| Farm hub | Large net in, few sources, feeders play nobody else, nothing flows back | caught |
| Genuinely strong player | Large net in, but from thirty partners, and they lose too | not flagged |
| Balanced ring of three | No net movement at all — flow is blind to it. Zero reciprocity still shows | partly |
| Two accounts, one signer | Payout outputs spent in one transaction | caught on chain |
| One honest user, two addresses | Consolidates their own outputs only | not a link |
| Single large dump, never repeated | One frame, no pattern | only if on chain |
Against the litoshi ledger in the Durable Object. This is a weekend, not a project, and it runs before you have a single shot log stored.
-- net flow and concentration per account pair, over a rolling window SELECT loser AS src, winner AS dst, COUNT(*) AS frames, SUM(stake) * (1 - :rake) AS moved, MIN(settled_at), MAX(settled_at) FROM frames WHERE settled_at > :since GROUP BY loser, winner; -- then, per account: net, inHHI, reciprocity, feederFocus -- all of it derivable from that one grouped result, in memory. -- Payout addresses you already have from m/84'/2'/0'/0/i.
Litecoin-specific, and it will matter. MWEB peg-ins hide the amount and the address entirely. An account that settles into MWEB is invisible to the chain check, and a colluder who reads one paragraph of documentation will do exactly that. Treat the chain signal as a bonus that catches the careless, never as the backbone. The ledger-side analysis has to stand on its own.
You are about to do chain analysis on your own users, and you market non-custody. Those two things are in tension and the tension is real. Clustering a user’s addresses is not a privacy violation you can do quietly and describe as non-custodial later. Put it in the terms in plain words before anyone deposits: which addresses you observe, why, how long you keep the linkage, and what a flag does. Disclosed, it is diligence. Undisclosed and later discovered, it is the story that ends the platform.
Flag, hold, review. Same rule as before, and it bites harder here because the chain signal feels like proof. It is a heuristic: coinjoin breaks it on purpose, an exchange’s hot wallet will co-spend thousands of unrelated outputs, and a shared family device is not a crime. A high score means a human looks, and nothing else happens automatically.
| Reciprocity | Per counterparty, the smaller flow over the larger. A rivalry tends toward one; a farm sits at zero. It is the single most useful column here |
| Feeder focus | Of everything a feeder ever lost, how much went to this one account. Near one means the feeder exists only to lose to the hub |
| Concentration | Herfindahl over the sources of an account’s winnings. A strong player scores near zero because they beat everyone |
| Fusion | Flow carries 0.55, chain linkage 0.45. Deliberately, a chain link alone tops out below the action band — shared signer is not the same as collusion |
| Where it runs | Batch, off the settlement path. It must never be able to delay a frame finishing |
| Thresholds | Fitted against simulated populations. Re-fit on real traffic before they gate anything |