# Vote composition at source

A tally says how much weight voted for, against and abstained. It does not say how many
addresses carried that weight. This note describes the second read, publishes a worked
example, and states the limit.

## Why it is a separate read

"The proposal passed with 1.69M tokens in favour" and "1.69M in favour from 63 addresses,
the largest five holding 51.3% of the votes cast" are different facts, and only the second
one measures the distribution of voting weight across the addresses that exercised the
right. It does not establish the number of beneficial holders. Both are true of the same proposal. Where a standard asks whether authority can
be recalled and whether the voting mechanism resists concentration (GSA-2 and GSA-7), the
composition is the relevant measurement — so the feed publishes it, dated, with its inputs.

## How the read works, and why it needs its own route

The obvious route is `eth_getLogs` against the governor contract. On the public JSON-RPC
endpoints reachable from our host it does not work: the block range is capped at 50 blocks
on some, and others refuse the request outright. Per-voter composition is usually reported as
unavailable for that reason.

The read instead uses a decoded, paginated per-address log endpoint, which returns the
governor's own `VoteCast` events with voter, support, weight and timestamp already decoded.
Paging stops once a page reaches blocks older than the proposal's snapshot block, so the walk
costs a few requests rather than a walk through years of history.

## The check that makes it publishable

The tool sums the weights in the `VoteCast` logs and compares the sums against the
governor's own `proposalVotes(uint256)` tuple. All three support categories must agree:
For, Against and Abstain. If any differs by one wei, the run fails and nothing ships.
The record publishes `cross_check.agreement` and each category's delta.

We first fetch one canonical Ethereum block. Every `eth_call` uses its hash with EIP-1898
`requireCanonical: true`; the log walk excludes later blocks. The record publishes
`read_block`, `read_block_hash` and the block's `read_timestamp_utc`. This prevents a moving
head from mixing one block's tally with another block's votes. `quorum` takes the proposal
snapshot as its function argument but is evaluated at the pinned read block.

Every figure is reproducible without any tool of ours. Against any public Ethereum JSON-RPC
endpoint, on the governor address named in the record:

- `0x544ffc9c` — `proposalVotes(uint256)`: the tally, returned in the order
  `(against, for, abstain)`
- `0x3e4f49e6` — `state(uint256)`: 1 is Active, 5 is Queued, 7 is Executed
- `0x2d63f693` — `proposalSnapshot(uint256)`: the timepoint, which bounds the log walk
- `0xf8ce560a` — `quorum(uint256)`, called with the snapshot **block**, not the proposal id;
  the proposal id reverts because the argument is a timepoint
- the decoded logs at `read_method.log_source`

A browser `User-Agent` is required by the endpoints; a default python or curl agent is
answered with 403.

## Worked example — ENS, "Activate ENSv2", read 2026-10-25

- 63 `VoteCast` events, 63 distinct voting addresses.
- For 1,689,763.327061786872049809 ENS · Against 24.944871129841 ENS · Abstain 0.
- Sum of log weights against the governor's own tally: difference 0 wei.
- Quorum 1,000,000 ENS (1% of supply), snapshot block 26,221,945, deadline block 26,267,763.
- Concentration of the votes cast: largest address 11.84%, largest five 51.31%, largest ten
  76.42%, HHI 0.0725.
- The most recent cast at the read: one address, 60,000 ENS, For, 2026-10-24 16:57:59Z,
  block 26,263,851.
- Proposal state at the read: Active. No outcome is asserted here; the deadline block had not
  been mined when the record was written.

The same read has been taken at three dates since the vote opened, and the difference between
them is itself a fact: 1,629,763.3270617868 For on 2026-10-24, 1,689,763.3270617868 For on
2026-10-25 — a single 60,000 ENS cast in the final hours. Reported tallies elsewhere have run
well behind the chain at each of those dates.

## Closed-ballot update — verified 2026-10-26

**Verified:** at block 26,273,746, hash
`0xa0ba37a9ae6485fdc39f3ca145c5bfddd9a9a6375214799b67b5e94ad12b467d`,
timestamp 2026-10-26 02:02:47 UTC, the governor reported **Queued (5)**. All three
support tallies reconciled with the logs at zero wei difference. The 63 addresses and
concentration figures above are unchanged from the October 25 read.

The deadline block was mined at **2026-10-25 06:01:35 UTC**. `ProposalQueued` landed
in the next block at **06:01:47 UTC**, with ETA **2026-10-27 06:01:47 UTC**, exactly
two days later. The record's optional `proposalEta(uint256)` view supplies that ETA;
an unsupported view stays null, with a stated reason.

**Limit:** Queued is not executed. ETA is an earliest scheduled execution time, not proof
of execution. The voted payload covers stage 2 of five proposal stages. Operator actions
and a later cleanup vote have separate evidence requirements and remain unresolved in
our record. We did not verify those actions in this update; no absence is asserted.
The [ENS feed record](../data/determinations/ens.json) carries both the composition and
these execution dependencies. Its GSA verdicts are unchanged.

## Executed — verified 2026-10-28

**Verified:** `ProposalExecuted` for this proposal landed at block **26,282,117**
(hash `0x9f9f5966…9d00`) at **2026-10-27 06:01:59 UTC**, twelve seconds after the queued
ETA — the next slot. At the 2026-10-28 read (block 26,288,101) the governor reports
**state 7 (Executed)** and `proposalEta(uint256)` returns 0.

An event is not the same thing as an effect, so the record now also runs the seven
post-execution checks the proposal's own text told readers to run, expanded to fourteen
call-level reads. All fourteen pass:

- `BaseRegistrarImplementation.owner()` is `0x56c447f3…cb68` (`ETHRenewerV1`).
- `controllers()` is true for `ETHRenewerV1` and the `Graveyard`, and false for all five
  controllers the payload removed — including the `NameWrapper`, so v1 registration is closed.
- `ENSRegistry.resolver(namehash("eth"))` is `0x3071ad64…68a4` (`ENSV2Resolver`).
- `PublicResolverSet.includes()` is true for both v1 public resolvers; the HCA factory
  approves the standalone implementation; both reverse-registrar adapters are controllers.
- The Universal Resolver proxy's EIP-1967 implementation slot points at
  `0xcffd2ae6…022b`, the managed intermediate proxy.

**What this does and does not establish.** Execution is established by state, not by the
event alone: every state the fifteen calls were supposed to leave behind is the state the
chain holds. It does not establish that ENS Labs performed the operator stages (final sync,
v2 registration enablement, role handover) or that the Security Council has cut resolution
over to v2 — those remain unresolved in our record, and their absence is not asserted.

## A second live example — Compound proposal 617

One record is an anecdote. The same read on a second governor, with different parameters,
makes the measure comparable. Compound 617 ("Withdraw reserves from cUSDEv3 on Mantle"),
executed 2026-10-26, was read at block 26,288,115:

| | ENS "Activate ENSv2" | Compound 617 (cUSDEv3 reserves) |
|---|---|---|
| Voting addresses | 63 | 15 |
| For / Against / Abstain | 1,689,763.33 / 24.94 / 0 | 594,117.18 / 0 / 0 |
| Largest address | 11.84% of votes cast | 13.58% |
| Largest five | 51.31% | 53.97% |
| Largest ten | 76.42% | 99.998% |
| HHI | 0.0725 | 0.1024 |
| Reconciliation | 0 wei difference | 0 wei difference |

Two records are not a survey, and nothing here is a statement about how these DAOs are
governed in general: one is a constitutional vote with heavy participation, the other a
routine treasury operation in which fifteen addresses carried a near-unanimous Yes. What
does carry across is an arithmetic point that applies to both quorums. ENS's quorum of
1,000,000 ENS was 86.7% carried by the largest five addresses alone; Compound's quorum of
400,000 COMP, 80.2%. In neither case did clearing the bar require a broad base of holders,
which is why the count of addresses belongs beside the tally rather than under it.

## Limits, stated

- Composition is per voting address, not per beneficial owner. One address can be a
  multi-signature wallet, a custodial omnibus, or a delegate voting on behalf of many
  tokenholders. A small count of addresses is not by itself a small count of holders, and this
  record does not claim that it is.
- Shares are of the votes actually cast in that proposal, not of token supply or delegated
  supply. Abstaining voters and their weights are included. Addresses that did not vote
  are excluded. We aggregate weights by voting address before computing concentration.
- Vote reasons are recorded on chain as strings. They are not read, quoted or interpreted here.
- Blocks continue to be produced while the proposal is open, so a composition taken before the
  deadline is a snapshot of a moving count. The record states the read timestamp and the
  proposal state at that moment.
- No address is attributed to any person or organisation. The attribution would be off-chain
  and is deliberately left out.

## What it is not

A composition read is a descriptive statement about what the governor contract recorded. It is
not a rating, not investment advice, not a recommendation on any proposal, and not a statement
about how any holder should vote or should have voted. It is published in the freshness layer
as a dated watch event with impact `may-affect`. Like every watch event, it moves no GSA cell:
verdicts change only through the frozen methodology at a scheduled re-check.

---

<!-- seo:begin -->
<link rel="canonical" href="https://probity-institute.pages.dev/docs/vote-composition.md">
<meta property="og:type" content="article">
<meta property="og:site_name" content="Probity Institute">
<meta property="og:title" content="Vote composition at source — Probity Institute">
<meta property="og:description" content="A tally says how much weight voted for, against and abstained. It does not say how many addresses carried that weight.">
<meta property="og:url" content="https://probity-institute.pages.dev/docs/vote-composition.md">
<meta property="og:locale" content="en_GB">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Vote composition at source — Probity Institute">
<meta name="twitter:description" content="A tally says how much weight voted for, against and abstained. It does not say how many addresses carried that weight.">
<!-- seo:end -->
