> For the complete documentation index, see [llms.txt](https://docs.convexfinance.com/convexfinanceintegration/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.convexfinance.com/convexfinanceintegration/on-chain-voting.md).

# On-Chain Voting

Convex has deployed a dedicated on-chain voting platform that lets **vlCVX holders and delegates** participate in governance across multiple external protocols: **Curve**, **f(x)**, **Frax**, and **Resupply**. Convex aggregates sentiment from **vlCVX** holders/delegates on-chain. Where executors are registered, the platform then executes consolidated votes on the destination protocol's governance contracts (Curve DAO, Curve gauge, f(x) gauge, Resupply DAO); for protocols without a registered executor, votes finalize at the `DaoVotePlatform` level. The same infrastructure is the foundation for moving Convex's own administrative control toward token-weighted on-chain governance.

GitHub: <https://github.com/convex-eth/voting>

### VotingRegistry

The routed platform components are discoverable through the `VotingRegistry` (mainnet `0x6C3a56ea7c2DA4ee2876DbDa437173B28f001c34`) via a two-key lookup:

```solidity
function getAddress(string platform, uint8 type) external view returns (address)
```

Keys are **case-sensitive** (e.g. `"CURVE"`, `"FX"`, `"FRAX"`, `"RESUPPLY"`, `"CONVEX"`). The registry itself and the legacy `VoteDelegateExtension` have no registry route.

#### Type constants

| Constant         | Value | What it points to                                  |
| ---------------- | ----- | -------------------------------------------------- |
| `VOTE_DAO`       | 0     | `DaoVotePlatform` for the platform                 |
| `VOTE_GAUGE`     | 1     | `GaugeVotePlatform` for the platform               |
| `GAUGE_REGISTRY` | 2     | Gauge registry contract (Curve and F(x) only)      |
| `DAO_EXECUTOR`   | 3     | DAO vote executor (Curve, Resupply only)           |
| `GAUGE_EXECUTOR` | 4     | Gauge vote executor (Curve, F(x) only)             |
| `DAO_PROPOSER`   | 5     | DAO proposal creator                               |
| `GAUGE_PROPOSER` | 6     | Gauge proposal creator (Curve, F(x) only)          |
| `DAO_METADATA`   | 7     | Proposal metadata contract (FX, FRAX, CONVEX live) |
| `OTHER_TYPE`     | 8     | reserved/generic                                   |

Additional shared registrations: `"DELEGATION"` + `VOTE_DAO` (0) → **Delegation (DAO)**, `"DELEGATION"` + `VOTE_GAUGE` (1) → **Delegation (Gauge)**, `"SURROGATE"` + `VOTE_DAO`/`VOTE_GAUGE` → **SurrogateRegistry**, `"ConvexCore"` + `VOTE_DAO` → **ConvexCore**, `"OWNER"` + `VOTE_DAO` → **ConvexCore** (alias), `"HELPER"` + `VOTE_GAUGE` → **GaugeVoteHelper**.

#### What each platform registers

| Type                 | CURVE              | FX                 | FRAX               | RESUPPLY             | CONVEX             |
| -------------------- | ------------------ | ------------------ | ------------------ | -------------------- | ------------------ |
| `VOTE_DAO` (0)       | DaoVotePlatform    | DaoVotePlatform    | DaoVotePlatform    | DaoVotePlatform      | DaoVotePlatform    |
| `VOTE_GAUGE` (1)     | GaugeVotePlatform  | GaugeVotePlatform  | —                  | —                    | —                  |
| `GAUGE_REGISTRY` (2) | CurveGaugeRegistry | FxGaugeRegistry    | —                  | —                    | —                  |
| `DAO_EXECUTOR` (3)   | CurveVoteExecutor  | —                  | —                  | ResupplyVoteExecutor | —                  |
| `GAUGE_EXECUTOR` (4) | CurveGaugeExecutor | FxGaugeExecutor    | —                  | —                    | —                  |
| `DAO_PROPOSER` (5)   | CurveDaoProposer   | GenericDaoProposer | GenericDaoProposer | ResupplyDaoProposer  | GenericDaoProposer |
| `GAUGE_PROPOSER` (6) | GaugeProposer      | GaugeProposer      | —                  | —                    | —                  |

Note: f(x) Protocol, Frax and Convex Finance have **no DAO executor** registration — votes on those platforms finalize at the `DaoVotePlatform` level and are not executed into an external contract.

### Voting weight model

Voting weight is derived **directly from vlCVX lock state** — no off-chain merkle proofs.

* `baseWeight` = `vlCVX.balanceAtEpochOf(proposalEpoch, user)` — the user's raw **vlCVX** balance at the proposal's reference epoch.
* Add/subtract delegated weight via `adjustedWeight` (a **signed** value — it can go negative when a direct delegator reclaims weight from a delegate).
* Effective voting weight = `baseWeight + adjustedWeight`.
* Delegation weights are stored truncated to `1e17` units in the Delegation contract, so sub-0.1-**CVX** precision is lost.

Proposals are anchored to a **vlCVX epoch**. Gauge proposals record `epoch = vlCVX.epochCount() - 2` (current epoch); DAO proposals may walk backward to an epoch beginning at/before `_startTime`. The epoch reference is fixed per proposal, but each account's balance is read **lazily when that account votes or re-votes**, not copied at proposal creation.

### Delegation

The [Delegation.sol](https://github.com/convex-eth/voting/blob/main/src/Delegation.sol) contract lets a vlCVX holder delegate their weight to **one** address (depth 1 — chains are not followed):

* `setDelegate(_delegate)` — changes take effect from the **next** epoch; `address(0)` removes delegation.
* `sync(_user)` / `syncAtEpoch(_user, _epoch)` — propagate a user's changed vlCVX balance into the delegate's accumulated weight. Because vlCVX balances change over time, delegators and delegates should `sync()` periodically.
* `balanceOf(_delegate)` / `balanceAtEpochOf(_epoch, _delegate)` — a delegate's total accumulated voting weight.
* **Expired locks**: if a delegator's vlCVX locks have expired, `sync()` reverts — the user must `processExpiredLocks()` / relock first.

Delegation powers both DAO and gauge voting (two separate Delegation instances registered under `"DELEGATION"`). On the voting platform, `lossless`-style weight checks: a first vote syncs if cached delegation weight differs from the Delegation contract; re-votes use deltas.

### Surrogates

[SurrogateRegistry.sol](https://github.com/convex-eth/voting/blob/main/src/SurrogateRegistry.sol) lets an account register a **surrogate** address that may vote on its behalf:

* `setSurrogate(_surrogate)` — caller registers another address as its surrogate.
* `isSurrogate(_surrogate, _account)` — check helper.
* Voting contracts accept votes from an account **or** its registered surrogate.
* **Priority rule**: a direct vote from the account itself overrides a surrogate vote and permanently blocks later surrogate updates for that proposal. There is no signature/EIP-1271 vote path on-chain — the caller must be the account or its registered surrogate.

### DaoVotePlatform (yes/no voting)

`createProposal(startTime, endTime, voteType, proposalId)` (operator-only). Duration must be 1–6 days. **Concurrent DAO proposals are supported** — a new proposal does not wait for the previous one's finalization (that restriction is a gauge-voting rule).

* `vote(proposalId, account, yesWeight, noWeight)` — weights in basis points, must sum to 10000 (e.g. `(10000,0)` = yes, `(6000,4000)` = 60/40 split).
* `voteType` ∈ {0 = Ownership, 1 = Parameter} — metadata for off-chain policy, and used by the **Curve vote executor** to select ownership vs parameter voting.
* `getYes/getNo/voteTotals`, `getVote`, `isFinalized`, `forceEndProposal`.
* Proposal IDs are external references (snapshot hash, forum ID) recorded on-chain.

A user can re-vote/change their vote while the proposal is active; weight changes propagate to totals on each vote.

### GaugeVotePlatform (gauge weight voting)

`createProposal(startTime, endTime)` (operator-only). Same epoch anchoring and 1–6 day duration. Voters allocate their weight across **multiple gauges**:

* `vote(account, gauges[], weights[])` — weights are **parts per million of 1,000,000**, must be nonzero, and must **sum exactly to 1,000,000** for every vote (`max_weight` on the platform); gauges validated against the platform's `GAUGE_REGISTRY` (Curve or f(x)).
* `gaugeTotal(proposalId, gauge)` / `getGaugeCount` / `getGaugeEntry` — read totals per gauge.
* `forceEndProposal()`, `isFinalized`, `setOvertimeAccount(account, active)` — marks **equalizer accounts** that may vote/re-vote during the 10-minute overtime after a gauge proposal's normal end.
* A new gauge proposal cannot begin until the previous one's `endTime + overtime` has passed.

### Executors, proposers & metadata

* **Proposers** create/mirror the internal proposal **before** voting and prepare the external reference; they do not draft the final execution payload. `CurveDaoProposer`, `ResupplyDaoProposer`, and `GaugeProposer` (the latter used for both Curve and F(x) gauge routes) entry points are **permissionless** subject to their external/epoch checks; only `GenericDaoProposer` is operator-gated.
* **Executors** act only after the required on-chain checks:
  * `CurveVoteExecutor` / `ResupplyVoteExecutor` enforce a **quorum** (`quorumBps = 1500`, i.e. 15% minimum participation) — quorum is enforced at the executor, not at the `DaoVotePlatform`.
  * `CurveGaugeExecutor` / `FxGaugeExecutor` submit gauge weight votes to the respective gauge controllers.
* **ProposalMetadata** contracts store operator-written title/description/reference URL keyed by internal proposal ID. Executors do **not** read metadata and there is no enforced payload-hash linkage — treat metadata as documentation, not as verified execution evidence.

### ConvexCore (registry owner / governance alias)

[ConvexCore.sol](https://github.com/convex-eth/voting/blob/main/src/ConvexCore.sol) (mainnet `0xCC07e8BA6bc8aeb18C4AE110C3Da9c7Dce4A3e74`) is the registry owner (`"OWNER"` registry key). It is a **one-of-N operator dispatcher**: any of its operators can `execute(target, data)` arbitrary calls and manage the operator set (`setOperator`). It is **not** currently token-governed — verify `operatorCount()`/`operatorList` on-chain before assuming its trust model. Its role is to be the point of control that governance-aligned operators act through on the way to fuller on-chain ownership.

### Integrating with the voting platform

* **Reading voting power**: `vlCVX.balanceAtEpochOf(epoch, account)` for raw weight; use the `Delegation` contract for aggregated/delegated weight; ask the user to register a surrogate if your service submits votes on their behalf.
* **Submitting votes as a service**: register as the user's surrogate via `SurrogateRegistry`, then call `vote(...)` on the relevant Dao/GaugeVotePlatform with the account as `_account`. Votes from unregistered addresses are rejected unless they equal `_account`.
* **Reading results**: `getYes/getNo/voteTotals` or `gaugeTotal` per proposal; listen to vote events for real-time updates.
* **Proposing**: `createProposal` on the vote platforms is operator-only — external integrations can **not** create proposals there. The permissionless entry points are the **proposer contracts** (e.g. `CurveDaoProposer`, `GaugeProposer` — used for both Curve and F(x) gauge routes, and `ResupplyDaoProposer`), which mirror external proposals into the platform subject to their own epoch/external checks and do not require operator coordination. Only the generic DAO proposer routes are operator-gated.

### Deployed addresses

| Component                      | Address                                      |
| ------------------------------ | -------------------------------------------- |
| VotingRegistry                 | `0x6C3a56ea7c2DA4ee2876DbDa437173B28f001c34` |
| ConvexCore                     | `0xCC07e8BA6bc8aeb18C4AE110C3Da9c7Dce4A3e74` |
| SurrogateRegistry              | `0x8E4828a8C69A837F95Caa0D5e18fa09Ded12F73f` |
| DaoDelegation                  | `0x22697721EC1C14e42305e91BF02672932E8a7B09` |
| GaugeDelegation                | `0xb8270eef1319173dE9f5033FED442F638ff1607d` |
| Curve DAO voting               | `0xE645F3d7b04BFFD67F5Ad4457c08E0C8FE1ddB89` |
| Curve gauge voting             | `0x64D9B5AC386B70af9EDCD20A58cE9262D2EAC278` |
| F(x) DAO voting                | `0x51e08c14991387C2F5ad2E374eBc9B144FFC1E13` |
| F(x) gauge voting              | `0xDcEa673B021f1f431E7D0Ec70a63bF8DcB6d44E6` |
| Frax DAO voting                | `0xC4df4415120C1305b7dd998a881e809fA6695f03` |
| Resupply DAO voting            | `0xb463Cf769577aa062Cb72f5e1f8276eDF28914f3` |
| Convex DAO voting              | `0x26674330B9696E052D70b4F971E46425761daCc2` |
| VoteDelegateExtension (legacy) | `0x5349ffba494aC3c888ffa16fD438F44B8c67fB07` |

The canonical addresses and upgrade/replacement records live in the `voting` repo's `deployment/` directory (`mainnet.json`, plus per-change safe transaction files). Verify live registrations with `VotingRegistry.getAddress(...)` before publishing.
