Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/dao-space.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ on:
pull_request:
paths:
- 'docs/dao/**'
- 'scripts/check-dao-space.js'
- 'scripts/check-dao-space*.js'
- 'scripts/fixtures/dao-space.json'
- '.github/workflows/dao-space.yml'
schedule:
# Once a week, Monday 06:00 UTC
Expand All @@ -22,5 +23,8 @@ jobs:
with:
node-version: '^24.18.0'

- name: Test the settings checker
run: node --test scripts/check-dao-space.test.js

- name: Compare the pages with the space
run: node scripts/check-dao-space.js
3 changes: 2 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ node ./libs/link-validator.js http://localhost:8082 ./docs/.vitepress/dist/

Hosts that block automated requests are listed in [`libs/link-validator-ignore.json`](./libs/link-validator-ignore.json).

The DAO pages repeat what the `api3.eth` space on Snapshot is set to: the contracts, methods and market its voting power strategy reads, and the proposal threshold, voting type, delay, period and quorum. [`scripts/check-dao-space.js`](./scripts/check-dao-space.js) reads the space from the Snapshot hub and fails when the pages differ from it. The [workflow](./.github/workflows/dao-space.yml) runs it on pull requests that touch `docs/dao` and once a week, so a settings change on Snapshot shows up as a failing run. Reproduce it locally with:
The DAO pages describe the `api3.eth` space on Snapshot. [`scripts/fixtures/dao-space.json`](./scripts/fixtures/dao-space.json) records its documented configuration, including the complete voting power strategy parameters, privacy, eligibility and multisigs. [`scripts/check-dao-space.js`](./scripts/check-dao-space.js) compares the hub's settings with that configuration and checks the contracts, reads and settings named on the pages. Update the configuration and the pages together when an approved governance change takes effect, never just to make the check pass. The [workflow](./.github/workflows/dao-space.yml) tests the checker and runs the comparison on relevant pull requests and once a week. Reproduce it locally with:

```sh
pnpm test
node scripts/check-dao-space.js
```

Expand Down
6 changes: 4 additions & 2 deletions docs/dao/api3-token.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ API3 is the governance token of the API3 DAO, an ERC-20 token on Ethereum mainne
## Supply

The contract minted 100,000,000 API3 when it was deployed.
Minting is restricted to addresses its owner marks as minters, and the only minter it ever had was the DAO pool, which minted the staking rewards of the legacy DAO.
Minting is restricted to addresses its owner marks as minters.
The DAO pool minted the staking rewards of the legacy DAO.

The migration to Snapshot closed the supply.
The pool's minter status was revoked, a final mint to the DAO's treasury multisig brought the supply to 200,000,000 API3, and the contract's ownership was transferred to the dead address, so no address can mint and none can be made a minter.
Expand All @@ -31,7 +32,8 @@ Nothing can mint it back, so the supply can only fall from here.
## Governance

Each API3 in your wallet, in the DAO pool, scheduled for unstaking or posted as collateral in the Kabu API3/USDC market on Morpho is one vote in the `api3.eth` space (see [Voting power](/dao/voting-power)).
API3 anywhere else, on an exchange, in a liquidity pool or bridged to another chain, does not vote.
Exchange balances, liquidity pool tokens and tokens bridged to another chain do not count as your voting power.
Other contracts can explicitly delegate their own API3 token balance on Snapshot (see [Voting power](/dao/voting-power#what-counts)).
Posting a proposal takes 500,000 API3 of voting power (see [Posting a proposal](/dao/voting#posting-a-proposal)).

## Staking
Expand Down
16 changes: 16 additions & 0 deletions docs/dao/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,22 @@ For quick reference, you can copy-paste [`llms-full.txt`](https://docs.api3.org/
- [Voting](/dao/voting) is a gasless signature on snapshot.box, For or Against, during the seven days a proposal is open.
Anyone with 500,000 API3 of voting power can [post a proposal](/dao/voting#posting-a-proposal), and it passes with at least 10,000,000 API3 in favour and more votes for than against.
- [Independent verification](/dao/verification) recomputes a result from the chain before the API3 multisigs execute it.
See [Snapshot assumptions](/dao/snapshot-assumptions) for the remaining trust in vote collection and the space's settings.

## Governing proposal

The primary DAO migration proposal in the [legacy DAO dashboard](https://api3.eth.limo/#/history) sets out the governance rules, mandate and charter.
These pages explain how to participate under those rules.

## DAO multisigs

| Multisig | Role |
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| [Vault](https://etherscan.io/address/0x8e03609ed680B237C7b6f8020472CA0687308b24) | Controls `api3.eth` and its Snapshot settings. Posts proposals and votes with the pool stake delegated to it. |
| [Agent](https://etherscan.io/address/0xBBE0dE9757F93e3306aDBfeBE906AB285EDD13DA) | Executes operational proposals. Its own voting power is delegated to a dead address. |
| [Treasury](https://etherscan.io/address/0xb7fa2Cb302C5dC127e61F27b92beC411cbf0AE4A) | Holds the DAO's treasury API3. Its own voting power is delegated to a dead address, and it does not post proposals. |

Changes to the Snapshot settings and other rules of governance follow the [rule-change execution period](/dao/voting#execution).

## Legacy DAO

Expand Down
4 changes: 4 additions & 0 deletions docs/dao/sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ module.exports = [
text: 'Independent verification',
link: '/dao/verification',
},
{
text: 'Snapshot assumptions',
link: '/dao/snapshot-assumptions',
},
];
52 changes: 52 additions & 0 deletions docs/dao/snapshot-assumptions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
title: Snapshot assumptions
pageHeader: DAO
outline: deep
---

<PageHeader/>

# Snapshot assumptions

Snapshot voting is off-chain.
The [independent verification](/dao/verification) recomputes voting power and the tally, while vote collection still relies on Snapshot.

## Vote collection

The Snapshot sequencer receives signed votes and decides whether they arrived before the deadline.
The hub supplies the list of accepted votes.
A signature proves who signed a vote and what they signed, including its timestamp, but does not independently prove when Snapshot received it.

Snapshot cannot forge a voter's signature.
It could omit a signed vote or accept one after the deadline with a backdated signed timestamp.
Recomputing the final tally alone cannot rule those cases out.
Voters should check that their vote appears in the proposal's vote list.

The verification tool archives the vote list on each run and compares it with the previous archive.
Running it at least daily while voting is open, and again at the close, helps detect votes that were inserted, removed or changed.
Those archives improve visibility into changes, but do not remove the trust in Snapshot's receipt and inclusion of votes.

## Proposal timing

Snapshot sets a proposal's creation time when the sequencer accepts it.
If its signed start time is in the past, the sequencer moves the start forward and keeps the full configured voting period.
For `api3.eth`, voting starts without a delay and runs for seven days.
Time spent collecting Safe confirmations does not shorten that period.

The snapshot block is still the block chosen when preparing the proposal.
It can become too old while confirmations are collected, so a proposal must also meet the DAO's [snapshot block window](/dao/voting#snapshot-block).

## Voting power and settings

The verifier reads balances and contract state from an Ethereum archive RPC at the proposal's snapshot block, and delegations from the delegate registry's events.
It does not use Snapshot's score API or delegation subgraph for that recomputation.
The RPC must serve the correct Ethereum history.

The recomputation uses the strategies recorded on the proposal.
Agreement with those strategies does not itself establish that a strategy or settings change was authorized.
The API3 multisigs also check that the proposal uses the DAO's approved governance rules before executing it.

## Execution

A passed Snapshot vote does not automatically move funds or change contracts.
The [DAO multisigs](/dao/#dao-multisigs) execute the verified result under the [governing proposal](/dao/#governing-proposal), including the execution period for a change to the rules of governance.
15 changes: 8 additions & 7 deletions docs/dao/verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ outline: deep

# Independent verification

The DAO relies on Snapshot to run its votes.
The space settings, the strategies that compute voting power and the hub that collects and tallies the votes are Snapshot's, and the DAO trusts them to work as described.
It verifies them all the same: before executing a proposal, the API3 multisigs recompute its result from the chain with the [Snapshot voting verifier](https://github.com/api3dao/snapshot-voting-verifier), never taking a result off a website.
The tool is open source, anyone can run it on any proposal of the space, and its README says how.
Before executing a proposal, the API3 multisigs recompute its result from the chain with the [Snapshot voting verifier](https://github.com/api3dao/snapshot-voting-verifier), never taking a result off a website.
The tool's source is public, anyone can run it on any proposal of the space, and its README says how.
The remaining trust in Snapshot's vote collection and settings is described under [Snapshot assumptions](/dao/snapshot-assumptions).

The DAO's rules also go further than Snapshot's settings can express, such as counting only the votes in favour toward the quorum and the window for the snapshot block.
The verification enforces those too, so a proposal that Snapshot shows as passed can still fail a check and not be executed.
Expand All @@ -20,13 +19,15 @@ The verification enforces those too, so a proposal that Snapshot shows as passed

Given a proposal, the tool reads every vote the Snapshot hub lists and establishes, independently of Snapshot's own computation, that:

- Every vote was signed by the voter it is listed under, names one of the proposal's choices and was cast while the vote was open.
- Every voter's power is what the [voting power](/dao/voting-power) rules give them at the proposal's block, delegations included.
- Every listed vote was signed by the voter it is listed under, names one of the proposal's choices and has a signed timestamp within the voting window.
- Every listed voter's power matches the proposal's recorded strategies at the proposal's block, delegations included (see [Voting power](/dao/voting-power)).
- The tally follows from those powers.
- The proposal met the posting rules: the proposer's 500,000 API3 at the proposal's block, a snapshot block and a signed timestamp within their windows (see [Snapshot block](/dao/voting#snapshot-block)), and seven days of voting with no delay.
- The proposal met the posting rules: the proposer's 500,000 API3 at the proposal's block, a snapshot block within its window (see [Snapshot block](/dao/voting#snapshot-block)), and seven days of voting with no delay.

It then judges the result on what it recomputed: at least 10,000,000 API3 in favour and more votes for than against (see [Passing](/dao/voting#passing)).
The run ends by saying whether the result is verified, and lists every problem it found.
`VERIFIED` means the listed votes and tally match the recomputation.
A verified result can still be a rejected proposal, so execution also requires `Outcome: passed`.
A proposal that fails a check is not executed.

The tool does not judge whether a proposal is a change of the rules of governance.
Expand Down
49 changes: 33 additions & 16 deletions docs/dao/voting-power.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Snapshot shows voting power under the symbol vpAPI3.

## What counts

At the block a proposal is measured at, your voting power is the sum of:
At the block a proposal is measured at, your base voting power is the sum of:

- API3 in your wallet.
- API3 staked in the DAO pool, locked rewards included.
Expand All @@ -24,9 +24,14 @@ At the block a proposal is measured at, your voting power is the sum of:
- API3 scheduled for unstaking, during the waiting period.
- API3 posted as collateral in the Kabu API3/USDC market on Morpho.

Nothing else counts: API3 on an exchange, in a liquidity pool, lent out, bridged to another chain or held in any other contract.
API3 held for you by an exchange or custodian does not count as yours.
Neither do liquidity pool tokens, lending receipts or tokens bridged to another chain.

API3 held by other contracts counts for you only if the contract explicitly delegates its own API3 token balance to you on Snapshot.
Owning a share of a contract does not give you voting power over its underlying assets.

[Snapshot delegation](#snapshot-delegation) applies on top of this sum: a delegation gives all of it to the delegate.
Power delegated to you on Snapshot is added separately and is not delegated onward.

## When it is measured

Expand Down Expand Up @@ -59,26 +64,36 @@ There are two kinds, and they behave differently.

| | Pool delegation | Snapshot delegation |
| -------------------------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------ |
| Made in | The legacy DAO dashboard | The [Delegates](https://snapshot.box/#/s:api3.eth/delegates) page of the space |
| What is delegated | Your stake in the pool | Everything that counts for you, stake delegated to you in the pool included |
| Made in | The pool contract | The [Delegates](https://snapshot.box/#/s:api3.eth/delegates) page of the space |
| What is delegated | Your stake in the pool | Your base voting power, stake delegated to you in the pool included |
| Follows a delegate who delegates on Snapshot | Yes | No |
| Changing it | A transaction, then a week before the next change | A transaction |

### Pool delegation

Pool delegation is made in the legacy DAO dashboard and carries over for the stake only.
Existing pool delegations carry over for the stake only.
If you delegated your stake in the pool, it keeps voting with your delegate.
Your wallet balance, deposits, unstaking amount and collateral still vote with you.

The pool refuses a delegation change within a week of the last one.
After delegating, you wait a week before you can undelegate or change the delegate, and after undelegating, a week before you can delegate again.

The dashboard dates from the legacy DAO, and [its documentation](https://dao-docs.api3.org/members/voting.html) describes delegating in it.
The legacy dashboard is retained for unstaking and withdrawing.
To inspect or change a pool delegation, open the [pool contract on Etherscan](https://etherscan.io/address/0x6dd655f10d4b9E242aE186D9050B68F725c76d76#code):

1. Under **Read Contract**, call `userDelegate` with your wallet address to see its current delegate.
The zero address means no pool delegation.
2. Under **Write Contract**, connect that wallet and call `undelegateVotingPower` to clear the delegation, or `delegateVotingPower` with the new delegate's address to change it.
These are transactions and cost gas.

The one-week cooldown applies to either change.
The pool refuses a new delegate who is already delegating inside the pool.

### Snapshot delegation

Snapshot delegation gives your delegate everything that counts for you, stake that others delegated to you in the pool included.
You then vote with zero.
Snapshot delegation gives your delegate your base voting power, stake that others delegated to you in the pool included.
You can still vote with power delegated to you on Snapshot, because Snapshot delegation does not chain.
If you have no incoming Snapshot delegations, your voting power is zero.
It is recorded in Snapshot's delegate registry on Ethereum mainnet, so setting, changing or clearing it is a transaction.

A delegation can be set under `api3.eth` or under all spaces.
Expand All @@ -96,19 +111,21 @@ To delegate, open the [Delegates](https://snapshot.box/#/s:api3.eth/delegates) p
- A pool delegation to someone who has since delegated inside the pool counts for nobody.
The pool gives a delegating user no voting power at all, received delegations included, and does not pass them on.
Re-delegate or undelegate in the pool to fix it.
- Voting yourself after delegating does nothing.
Your own power is zero while the delegation stands.
Clear it before the proposal's block to vote yourself.
- Voting yourself after delegating does not take your base voting power back from your delegate.
You can still use any incoming Snapshot delegations.
To vote with your own holdings again, clear the applicable space and all-spaces delegations before the proposal's block.
- Changing or clearing a delegation counts from the next proposal's block.
A proposal already posted keeps the delegations as they were at its block.
- To put everything with one delegate, delegate on Snapshot.
Pool delegation alone leaves the rest of your holdings with you.
- To put your own holdings with one delegate, clear any outgoing pool delegation first, then delegate on Snapshot.
While a pool delegation stands, that stake follows the pool delegate's Snapshot delegation, not yours.
Incoming Snapshot delegations stay with you and cannot be passed on.

## Other cases

- API3 held for you by an exchange or a custodian is in their wallet, not yours, and does not vote as yours.
- A Safe or another contract wallet has voting power like any wallet and votes by signing through the Safe app (see [Voting](/dao/voting)).
- A Safe can vote directly through the Safe app (see [Voting](/dao/voting)).
Other contract wallets need a compatible signing method or must explicitly delegate their own API3 balance.
- Collateral on Morpho counts for the owner of the position, in the Kabu API3/USDC market only.
Borrowing against it changes nothing.
- An API3 multisig that pool delegations name votes with the stake delegated to it.
- The API3 multisig that holds the DAO's own API3 has its voting power delegated to a dead address and does not vote.
- The Vault votes with the pool stake delegated to it.
The Agent and Treasury delegate their own voting power to a dead address and do not vote (see [DAO multisigs](/dao/#dao-multisigs)).
Loading
Loading