Home / Solutions / Governance & DAOs
Governance & DAOs

Governance data: proposals, votes, and treasury movements

Most DAOs run the same Governor contracts, so one topic0-keyed query reads every deployment at once: in one pinned week on Ethereum, a single VoteCast filter returned 281 ballots from 11 governor contracts, no per-DAO integration. SQD serves the full trail across 225+ networks: proposals, votes, delegation checkpoints, and the treasury transfers that settle them.

225+ Networks
VoteCast One filter, every fork
Checkpoints Delegation history
Genesis Full history
The problem

A governance app reads many DAOs at once, and the chain is the only source of truth

A dashboard that tracks proposals, votes, delegates, and treasuries has to read every DAO it covers back off the chain. Rebuilt the naive way, that means integrating each DAO one at a time, or leaning on a curated governance API that only covers the DAOs someone chose to add. Go direct and a single week is an eth_getLogs pagination loop against per-provider window caps, with an archive-node bill for full history. All of it is already onchain as proposal and vote events, delegation checkpoints, and the treasury transfers that settle them; the cost is in how you read it.

How it works

Most DAOs run the same Governor ABI. One topic0 watches all of them.

OpenZeppelin Governor and Compound's Governor Bravo declare the identical event: VoteCast(address voter, uint256 proposalId, uint8 support, uint256 votes, string reason). Its keccak256 hash is the log's topic0 on every deployment and every fork, so one filter on that hash, with no address list, watches all of them at once. Counted live over a pinned week of Ethereum blocks:

VoteCast emitters, blocks 25,450,000-25,500,000 ballots
281 VoteCast logs from 11 contracts, one topic0 filter, no address list, 2026-07-03 to 2026-07-10. Nouns' governor is a fork of Compound's GovernorBravoDelegator, which is exactly why the same filter catches it.

Decoded against the ABI: topic1 is the voter (0x089a…2dfd); the data words are proposalId 0x62 = 98, support 1 (for), weight 51661908035897703762 = 51.66 UNI, and a 344-byte reason string ending "a prudent step for the protocol's continued expansion." Everything a governance UI shows rides in the event.

The proposal entered the chain the same way: a ProposalCreated log at block 25,460,396 whose data field carries the proposal's full markdown text and call payload. In the pinned week the proposal drew 174 ballots (172 for, 1 against, 1 abstain, with voting still open at the window's end): 36,508,599.69 UNI of weight, 14,986,062.25 of it from a single ballot (0x1dd0…171a). Only two of the 174 attached a reason.

The same filter is expressible over eth_getLogs, but public endpoints cap the scanned range per call (50-block and 1,000-block caps, measured here), so a week is a pagination loop against error responses, and full history is an archive-node bill. The Portal takes the whole range in one filter and streams back only the blocks with matches, resuming by block cursor instead of erroring, from genesis to head. And where a curated governance API covers the DAOs someone chose to integrate, a filter keyed on the ABI covers every deployment, including the fork that launched yesterday.

One gotcha: the signature is shared, the units are not. votes is denominated per DAO: Uniswap and ENS weights arrive as 18-decimal token wei, while Nouns counts whole NFTs, one Noun one vote, weights in the same window topping out at 177. A cross-DAO tally that divides every weight by 1e18 silently zeroes out the NFT-vote DAOs. Normalize per governor's voting token, not per event.

Pinned to one block, one ballot on Uniswap proposal 98:

your terminal
curl -s -X POST https://portal.sqd.dev/datasets/ethereum-mainnet/stream \
-H 'content-type: application/json' \
-d '{
"type": "evm",
"fromBlock": 25478574, "toBlock": 25478574,
"logs": [{
"address": ["0x408ed6354d4973f66138c91495f2f2fcbd8724c3"],
"topic0": ["0xb8e138887d0aa13bab447e82de9d5c1777041ecd21ca36ba824ff1e6c07ddda4"]
}],
"fields": { "log": { "topics": true, "data": true, "transactionHash": true }, "block": { "number": true } }
}'
the response, pretty-printed
{
"header": { "number": 25478574 },
"logs": [{
"transactionHash": "0xd06e9e6ec6fe00c64aec1cafd8d941b733577471a809396e55d705cabae3ee27",
"data": "0x00000000…00000000",
"topics": [
"0xb8e138887d0aa13bab447e82de9d5c1777041ecd21ca36ba824ff1e6c07ddda4",
"0x000000000000000000000000089acc703d9fd8b14d6e03d4d38561c1d4672dfd"
]
}]
}

The data value is shown elided to its first and last bytes; in full it is 1,026 characters (the 0x prefix and 1,024 hex digits), and the curl above returns it complete.

Show the full raw row
the full response
{
"header": { "number": 25478574 },
"logs": [{
"transactionHash": "0xd06e9e6ec6fe00c64aec1cafd8d941b733577471a809396e55d705cabae3ee27",
"data": "0x00000000000000000000000000000000000000000000000000000000000000620000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000002ccf3f7a46294bd52000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001584920737570706f727420746869732070726f706f73616c2e205570646174696e6720556e697377617027732063726f73732d636861696e20676f7665726e616e636520696e66726173747275637475726520746f2063757272656e7420626573742070726163746963657320696d70726f766573206c6f6e672d7465726d2073656375726974792c206d61696e7461696e6162696c6974792c20616e64206f7065726174696f6e616c20636f6e73697374656e63792e204d6967726174696e6720617761792066726f6d2064657072656361746564206d6573736167696e672073797374656d73207768696c652073696d706c696679696e6720676f7665726e616e6365206163726f737320737570706f72746564206e6574776f726b7320697320612070727564656e74207374657020666f72207468652070726f746f636f6c277320636f6e74696e75656420657870616e73696f6e2e0000000000000000",
"topics": [
"0xb8e138887d0aa13bab447e82de9d5c1777041ecd21ca36ba824ff1e6c07ddda4",
"0x000000000000000000000000089acc703d9fd8b14d6e03d4d38561c1d4672dfd"
]
}]
}
Treasury flow

Watch the treasury address, not just the governor

A DAO treasury is an address: the timelock the governor controls, or a Safe. Every outbound token movement is a Transfer log with that address in the indexed from position, so one filter on topic1 catches all of them, in any token, with no token contract in the query. Pinned to one block on the ENS DAO treasury, wallet.ensdao.eth (0xFe89...44b7):

your terminal
curl -s -X POST https://portal.sqd.dev/datasets/ethereum-mainnet/stream \
-H 'content-type: application/json' \
-d '{
"type": "evm",
"fromBlock": 25495658, "toBlock": 25495658,
"logs": [{
"topic0": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],
"topic1": ["0x000000000000000000000000fe89cc7abb2c4183683ab71653c4cdc9b02d44b7"]
}],
"fields": { "log": { "address": true, "topics": true, "data": true, "transactionHash": true }, "block": { "number": true } }
}'
the response, pretty-printed
{
"header": { "number": 25495658 },
"logs": [
{
"transactionHash": "0x7a79d65f6a3d9aff90eda2f66999775044329c394a8c0f3380ca5b24cc3c5d79",
"address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"data": "0x000000000000000000000000000000000000000000000000000000bced8d34d8",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000fe89cc7abb2c4183683ab71653c4cdc9b02d44b7",
"0x000000000000000000000000690f0581ececcf8389c223170778cd9d029606f2"
]
},
{
"transactionHash": "0x7a79d65f6a3d9aff90eda2f66999775044329c394a8c0f3380ca5b24cc3c5d79",
"address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"data": "0x000000000000000000000000000000000000000000000000000000f7aa407f1c",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000fe89cc7abb2c4183683ab71653c4cdc9b02d44b7",
"0x000000000000000000000000690f0581ececcf8389c223170778cd9d029606f2"
]
}
]
}

Two USDC (0xA0b8...eB48) transfers to the same recipient (0x690f…06f2) in one transaction, 0x7a79…5d79 (2026-07-09): 811,439.305944 plus 1,063,713.275676, 1,875,152.581620 USDC in total. Paginating the same filter across the full pinned week returns exactly these two rows: the treasury's only outbound token transfers in the window.

The non-obvious part: this transaction carries no ProposalExecuted log. The receiving Safe executed it, closing with its own ExecutionSuccess event in the same log stream. A feed keyed only on governor events misses every treasury movement that does not ride a proposal; keying on the treasury address catches both. Native ETH leaving the treasury emits no log at all: that movement is a trace, served from the same endpoint, explained here.

Where the data stops: the Portal serves the logs, transactions, traces, and state diffs the chain records, not reconstructed contract state, so there is no voting-power-at-block field. What the chain emits is the checkpoint series: on the UNI token over blocks 25,490,000-25,500,000, 3 DelegateChanged re-delegations against 102 DelegateVotesChanged balance moves, because a checkpoint fires each time a delegate's balance changes. Fold the series from genesis and voting power at any height is your own read. Offchain ballots (Snapshot) never enter the log stream at all.

Capabilities

Built for Governance & DAOs at scale

One filter, every Governor fork

OpenZeppelin Governor and Compound's Governor Bravo emit the same VoteCast signature, so one topic0 filter watches every deployment at once: Uniswap, ENS, Nouns, and the forks nobody integrated. In a pinned week on Ethereum it returned ballots from 11 governor contracts.

Proposal lifecycle from the log stream

ProposalCreated, ProposalQueued, ProposalExecuted, and ProposalCanceled, each with its block and transaction. The created event carries the proposal's full text and call payload in its data field, so the intent and the execution are both queryable rows.

Delegation checkpoints, not balance lookups

ERC20Votes tokens emit DelegateChanged on a re-delegation and DelegateVotesChanged every time a delegate's balance moves. The Portal serves that event series from genesis; fold it per delegate and voting power at any block is your own read.

Treasury movements, keyed on the address

Filter Transfer logs by the treasury address in the indexed from position and every outbound token movement comes back, in any token, whoever initiated it, including movements that never touch a proposal. Safe executions emit ExecutionSuccess in the same stream.

Why SQD

What you would build yourself

Every proposal, vote, and treasury transfer is already a public log or trace. What a provider saves you is the reading, and it compounds with every DAO you add.

  • DAO coverage
    Yourself A curated governance API covers only the DAOs someone chose to integrate.
    With SQD One topic0 filter keyed on the shared Governor ABI returns every deployment and every fork at once.
  • Range and history
    Yourself eth_getLogs caps the scanned range per call, as small as 50 or 1,000 blocks, so a week is a pagination loop against errors and full history is an archive-node bill.
    With SQD One filter takes the whole range and streams back only matching blocks, resuming by block cursor from genesis to head.
  • Treasury movements
    Yourself A feed keyed on governor events misses every treasury movement that does not ride a proposal.
    With SQD Filtering Transfer logs by the treasury address catches every outbound token movement, including Safe executions that emit no proposal event.
  • Native ETH out
    Yourself Native ETH leaving the treasury emits no log, so a log-only feed never sees it.
    With SQD That movement is a trace, served from the same endpoint as the logs.

See this on your own contracts

Tell us the chains and contracts you care about, and we will show you the query that returns them.

Use cases

What teams build with SQD

  • Governance dashboards covering every Governor fork
  • Proposal lifecycle alerts, from created to executed
  • Delegate and voting-power tracking from checkpoint events
  • DAO treasury monitoring keyed on timelock and Safe addresses
  • Turnout and voter-overlap analytics across DAOs
  • Auditable governance archives with block-level evidence

Frequently asked questions

How do I get governance data for many DAOs at once?
OpenZeppelin Governor and Compound's Governor Bravo declare the same VoteCast event, so its keccak256 signature hash is the log's topic0 on every deployment and fork. Filter on that one hash with no address list and every governor comes back at once: over Ethereum blocks 25,450,000 to 25,500,000 (one week of July 2026), that single filter returned 281 ballots from 11 governor contracts. Add an address filter only when you want one DAO. The mechanics are covered in the EVM indexer guide.
Does SQD cover Snapshot votes?
No. Snapshot ballots are gasless signed messages stored offchain (its own docs describe the platform as "all offchain"), so they never appear in any chain's log stream, from SQD or anyone else. The Portal serves what the chain records: Governor proposal and vote events, delegation checkpoints, timelock and Safe executions, and treasury transfers. When an offchain vote leads to an onchain execution, that execution and the transfers it makes are ordinary logs and are queryable.
Can I read an address's voting power at a given block?
Not as a field. The Portal serves logs, transactions, traces, and state diffs, not reconstructed contract state, so there is no voting-power-at-block lookup. What the chain emits is the checkpoint series: ERC20Votes tokens fire DelegateChanged on an explicit re-delegation and DelegateVotesChanged every time a delegate's balance moves. On the UNI token over blocks 25,490,000 to 25,500,000 that was 3 re-delegations against 102 balance-moving checkpoints. Stream the series from genesis, fold it per delegate, and voting power at any height is your own read.
Get started

Your blockchain data infrastructure, handled.

Private Portal. Dedicated. Validated. Managed. Tell us what you're building, we'll show you what it looks like on SQD.