|=[ HOW "PRIVATE" IS STARKNET'S BUILT-IN PRIVACY? :: METHODOLOGY ]=

METHODOLOGY.

How every number in How "private" is Starknet's built-in privacy, really? was produced: the dataset, the link engine, the entropy maths, and the honest negatives.

DATA · 20.04.2026 → 18.08.2026 · STARKNET MAINNET

THE DATASET

Every interaction with the STRK20 pool contract emits a public event. I re-indexed all of them from raw JSON-RPC (starknet_getEvents, 19 checkpointed 250k-block chunks) against the publicnode archive node, with transaction metadata from batched starknet_getTransactionByHash calls. Funding histories came from the public Starkscan REST API, and the Ethereum side of the StarkGate bridges was crawled over paid dRPC (eth_getLogs, topics only). The window covers deployment on 20.04.2026 (block 8,978,970) to 18.08.2026 (block ~13,478,212): 113,022 events in total, decoded into typed tables:

  • 15,763 Deposit legs (794 of them fee legs)
  • 38,476 Withdrawal legs (28,378 of them fee legs)
  • 24,499 nullifiers consumed and 28,280 encrypted notes created
  • 2,378 open-note events (1,189 plaintext note pairs)
  • 2,420 viewing-key registrations, one per account, immutable
  • 1,152 pool-to-external calls and 54 admin events
  • 28,843 unique transactions carrying pool events

With fee legs filtered out, the user population is 14,969 deposits from 1,997 accounts and 10,098 withdrawal legs to 1,616 recipients, moving 38.2M STRK, 603k USDC, 6.77 strkBTC, and 95 ETH plus a long tail. The protocol fee is flat and height-dependent: 4 STRK for most of the pool's life, raised to 6 STRK at block 12,806,094 (5ᵗʰ August), paid as real same-token withdrawal legs to the avnu forwarder; 46.1% of fee legs are not STRK, so any heuristic that treats the fee as a STRK-only constant misclassifies half of them.

Two structural facts shape the whole analysis. The pool is small and young: 2,420 registered accounts, 423 of which never deposited. And a third of the traffic is one farm regime: the 22ⁿᵈ June – 1ˢᵗ July incentive burst (plus a May pre-launch wave) is scripted mass-onboarding, with 620 accounts (31% of depositors) tagged farm or scripted. Farm traffic churns itself and is trivially linkable; organic traffic is not. One contract upgrade (block 11,632,886, 9ᵗʰ July) splits the dataset into two eras, and since that upgrade every deposit must carry a screening attestation from a commercial AML provider (Elliptic, per StarkWare's docs), fetched seconds before the transaction lands.

The hypothesis: the public record alone is enough to link a large share of withdrawals back to their deposits. The observer sees everything onchain and nothing offchain: no IPs, no exchange ledgers, no relayer logs. A link made from the public record is a link anyone can make. Each heuristic is ranked on yield, confidence (certain versus probabilistic), and null calibration against amount-shuffled controls.

THE ANALYSIS

01 Conservation amount-tagging.

The contract enforces, per transaction and per token: deposits plus consumed notes equal withdrawals (including fee legs) plus created notes. Evaluated over the public legs, two shapes collapse to certainty: a transaction with zero consumed and one created note pins that note's amount, and one consumed with zero created pins the spent nullifier's. Yield: 13,784 of 28,280 notes (48.7%) and 4,560 of 24,499 nullifiers (18.6%) exactly tagged. Matching exact-spent against exact-created with a unique live candidate gives 2,679 certain creation-to-spend links, 98.1% of them self-links with a median note lifetime of 98 seconds. Null calibration: zero balance violations across 28,825 transactions. This heuristic also certifies change-freeness, which is what makes the amount heuristics below certain rather than statistical.

02 The withdraw-max peel.

A withdraw-max transaction spends one note with no change, so the note's value is V = W + f: W alone matches nothing in the deposit history, but V is computable from public data and matches the funding deposit exactly. 1,426 single-leg withdrawal transactions have a V-match; 353 have a unique candidate, null-calibrated at 3% false positives or less.

03 The reshield remainder.

A three-event arithmetic identity: an account deposits D1, later withdraws W paying fee f, and reshields the remainder as D2 = D1 − W − f, all in the clear. 3,365 matches across 808 withdrawal transactions and 295 accounts, against zero false matches in 9,302 shuffled controls. Median D1-to-D2 lag is 1.9 days. The strongest account-level certain link in the dataset.

04 Exact-amount single candidate.

Set-of-1 enumeration: 518 withdrawal legs have exactly one amount-feasible prior deposit in the pool's entire history (including 29 funded through open notes). Certain as enumeration; the funding claim assumes exact-amount funding.

05 Address reuse and recipient structure.

8,377 withdrawal legs (83.0%) pay an address that is itself a pool depositor with a prior deposit: the recipient's identity is public before any heuristic runs. 53 accounts made their first deposit minutes after receiving a pool payout, 68 legs pay the outer transaction sender's own wallet, and 13 co-spend pairs (two or more recipients in one transaction) are same-initiator payment links.

06 The funding graph and entity resolution.

Full inbound funding histories for every depositor: 185,621 edges at 99.95% coverage. 38.3% of depositors were first-funded by another depositor, and one mega-farm funder seeded 524 accounts with identical 5 STRK plus dust packages in a single week. Union-find over common-control evidence collapses the 1,997 naive depositors into 1,474 effective entities on the strict tier; the looser extended tier merges into a 1,408-account supercluster held together by peel edges, reported separately and never as certain. The tagged farm cohort (mega-farm, cadence bots, strict clusters of five or more) is 620 accounts.

07 Bridge anchoring and L1 identity.

Deposit events on the L1 StarkGate bridges deterministically bind an L1 sender to an L2 privacy account: 1,326 L1 events for 287 depositors, yielding 1,027 fully anchored bridge rows covering 279 depositors, some with multi-year pre-pool L1 history. A 2023 Etherscan label dump matched zero of the 333 unique L1 senders; a fresh label set (86,924 mainnet accounts) matched two. A bounded one-hop scan (who funded each L1 sender?) found 38 senders funded directly from CEX-labeled hot wallets. Net: 159 of the 279 anchored depositors (57%) gain an L1 identity hint, 36 of them CEX-grade. One-hop labels are behavioral hints, not certain identity.

08 The note-graph solver.

48.4% of legs (4,883) have no single-deposit candidate at all: they are funded by change notes and merges. The solver propagates the exact conservation tags through the note graph to a fixpoint, then walks each blind leg's consumed notes back to their deposit-bearing origin transactions. The honest finding is that propagation converges after one round and no split back-tagging materializes at all: a note's creation and spend are cryptographically unlinkable, so conservation can only solve a transaction whose public legs already pin the values. That boundary is exactly where the protocol's cryptography holds. The walk still attributes 315 of the 4,883 blind legs (6.5%), 29 of them certain-tier, lifting the certain union from 4,051 to 4,080 legs (40.1% to 40.4%) and legs with any attributed candidate set from 4,501 to 4,816 (44.6% to 47.7%).

09 Timing kernels.

Deposit-to-withdrawal gaps of certain pairs fit a two-component mixture of exponentials: globally a fast component with an 80-second half-life (weight 0.55, the farm churning itself) and a slow component of 2.8 days. The implied sub-minute enrichment is 55× over shuffled controls; an independent measurement with a different pair definition got ~25×, the same order. Refit per regime: the burst cohort runs at 71 seconds / 1.5 days with a median certain gap of 153 seconds (n=3,110); the organic cohort at 9.3 minutes / 9.4 days with a median of 1.6 days (n=439). Recomputing all posteriors with per-regime kernels leaves the global median leak unchanged (10.90 to 10.88 bits) but drops the organic cohort's median from 8.34 to 7.81 bits: the global kernel had been over-pricing fast coincidences for organic users.

What didn't leak.
  • The cryptography: zero duplicate salts, nullifiers, or ciphertexts across all ten crypto fields (111k+ felts); every public key on-curve; no RNG bias; the paper's reverted-transaction slot-reuse leak is unobservable because all 28,843 pool transactions succeeded.
  • Circadian timing: mean contribution 0.05 bits per leg, and the +12-hour phase-shifted control scores as well as the true kernel. Noise at this sample size; kept in the model, regularized, effectively null.
  • Sender clustering: 95 of 157 sender wallets each serve 117–179 depositors near-uniformly, so clustering on senders merges 1,954 of 1,997 depositors into one useless blob. Senders are wallet-backend relayers; they tag wallet software, not users.
  • Protocol timing: the fastest fund-to-deposit decile is 83 seconds, above the operational minimum; no block-quantization signature.

The headline numbers, certain meaning deterministic from public data:

MetricValue
User withdrawal legs10,098
Certain (link-engine union)4,051 · 40.1%
Certain (+ note-graph solver)4,080 · 40.4%
High confidence (top candidate ≥ 0.9)195 · 1.9%
Medium confidence (0.5–0.9)255 · 2.5%
Any attribution ≥ 0.5 (+ note-graph sets)4,816 · 47.7%
Naive anonymity set, median3,578 deposits (11.8 bits)
Median leak per leg10.9 bits (effective set: 1.0 candidate)
Depositor entities1,997 naive → 1,474 strict
Live unspent notes at head4,970 (the anonymity ceiling)
Burst cohort, certainly linked48.7% of 7,292 legs
Organic cohort, certainly linked17.9% of 2,806 legs · median leak 8.3 bits
Post-upgrade era, certainly linked12.8% of 1,325 legs

THE MATHS

Leaks are measured in bits: each bit halves the crowd you could be hiding in. The naive prior for a withdrawal leg is uniform over same-token deposits older than the withdrawal: median 3,578 candidates, 11.8 bits. The candidate filter keeps deposits that are amount-feasible (D == W, or D == W + f for the withdraw-max crowd) and removes deposits whose exact-created note is certainly spent before the withdrawal.

Soft signals enter as likelihood ratios calibrated against five amount-shuffle nulls (~75.8k chance pairs each): the timing likelihood ratio clipped to [1/200, 200], the circadian log-likelihood clipped to ±2 bits, the consolidation score capped at 500 (10,664 observed matches against 18 in the null), and a farm base-rate prior of 0.43× for farm accounts and 1.16× otherwise (farm accounts supply 22.5% of candidate pairs but only 9.7% of certainly linked deposits, because their round, duplicated amounts generate coincidence candidates). The posterior is proportional to prior × timing × circadian × consolidation, normalized per leg; certain links override it with a point mass.

Measured per heuristic (leave-one-out, probabilistic legs): the amount filter alone is worth 7.31 bits, timing adds 0.98, and everything else combined adds about 0.05. The anonymity-set distribution collapses accordingly: naive median 11.8 bits (p90 12.8) to an honest feasible-and-unspent median of 2.8 bits (p90 6.7) to a post-heuristic median of 2.15 bits for probabilistic legs and 0 for certain legs. Combined with the certain mass: median leak 10.9 bits per leg, a median effective anonymity set of 1.0 candidate.

Two ceilings sit above all of this. Effective entities: 1,474, not 1,997. And live unspent notes: 4,970 at the time of writing, the entire anonymity ceiling across every token, amount, and user. That figure is a correction to the study's own earlier count of 3,781, which counted only encrypted-note creations: its cumulative live curve dips to −25 in early May, physically impossible. Adding the 1,189 plaintext open notes keeps the curve non-negative at every point, which validates 4,970 as the definition. The pool ran its first two months at a plateau of 1,370 live notes, peaked at 5,034 on 1ˢᵗ July during the burst, and has drained since; 16.9% of all notes ever created are still unspent.