feat: who the wormhole exits pay, and how much of it is the protocol itself
deploy / build (push) Successful in 7m37s
deploy / deploy-web (push) Successful in 5s
deploy / deploy-api (push) Successful in 16s

The premise was that pools are the best surface for observing wormhole
transfers, because payouts must leave a pattern in the batches. The pattern
is there. Most of it is not payouts.

Two things break the obvious reading. Batch *size* is aggregation: the pallet
documents a public batch as one segment per inner private batch and a segment
as one client, so a 120-output batch is an aggregator bundling unrelated
people. And the most recurrent recipient is the aggregator itself — one
address is credited in 91.5% of every public batch anyone submitted and in
none of the private ones, which is the rebate minted to the proof's
`aggregator_address`. QUANPOOL's mining address appears in 501 batches for
7.54 QTC total, 0.015 each: the fee share paid to whoever included the batch.

Classified, three addresses take 76% of everything the exits pay. A cohort
table that did not say so would be describing aggregator plumbing and calling
it an economy, so the share is the section's eyebrow and its first sentence.

What survives the subtraction is the finding worth shipping: among genuine
recipients, 35 addresses paid twenty or more times take two thirds of the
remaining value at twice everyone else's average credit. That is a
recurring-payment shape and the page says it is no more than that — the chain
records no sender, so a regular payee is as plausibly an exchange deposit or
one person on a schedule as a pool meeting its obligations.

Batch size is not rendered anywhere, on purpose.

Closes #20

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-14 11:49:48 +03:00
co-authored by Claude Opus 5
parent 4d9d41888a
commit f81ebd4746
9 changed files with 471 additions and 3 deletions
@@ -0,0 +1,41 @@
{
"db_name": "PostgreSQL",
"query": "\n with ev as (\n select e.height, e.extrinsic_index,\n e.fields->>'to' as recipient,\n (e.fields->>'amount')::numeric as amount\n from chain_event e\n join chain_extrinsic x\n on x.chain = e.chain\n and x.height = e.height\n and x.extrinsic_index = e.extrinsic_index\n where e.chain = $1\n and e.pallet = 'Wormhole'\n and e.variant = 'NativeTransferred'\n and x.pallet = 'Wormhole'\n and x.call = 'verify_public_batch'\n ),\n batches as (\n select count(distinct (height, extrinsic_index))::numeric as n from ev\n ),\n per as (\n select recipient,\n count(distinct (height, extrinsic_index)) as credits,\n sum(amount) as amount\n from ev group by 1\n ),\n miners as (\n select distinct fields->>'miner' as address\n from chain_event\n where chain = $1\n and pallet = 'MiningRewards'\n and variant = 'MinerRewarded'\n ),\n classed as (\n select p.credits, p.amount,\n case\n when (select n from batches) > 0\n and p.credits::float8 / (select n from batches)::float8 > $2\n then 'infrastructure'\n when p.recipient in (select address from miners)\n then 'miner_fee'\n when p.credits = 1 then 'paid_once'\n when p.credits between 2 and 4 then 'paid_a_few'\n when p.credits between 5 and 19 then 'paid_often'\n else 'paid_regularly'\n end as kind\n from per p\n )\n select kind as \"kind!\",\n count(*) as \"addresses!\",\n sum(credits)::bigint as \"credits!\",\n coalesce(sum(amount), 0) as \"amount!\"\n from classed\n group by 1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "kind!",
"type_info": "Text"
},
{
"ordinal": 1,
"name": "addresses!",
"type_info": "Int8"
},
{
"ordinal": 2,
"name": "credits!",
"type_info": "Int8"
},
{
"ordinal": 3,
"name": "amount!",
"type_info": "Numeric"
}
],
"parameters": {
"Left": [
"Text",
"Float8"
]
},
"nullable": [
null,
null,
null,
null
]
},
"hash": "5dfe48cf0681d8946c77c2d6904d8b5590c86b3cb8f9809fb3eec479150534be"
}