Skip to content
Open
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
950 changes: 465 additions & 485 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"contracts/distribution/*",
"contracts/delegation/*",
"contracts/external/*",
"contracts/gauges/*",
"contracts/proposal/*",
"contracts/pre-propose/*",
"contracts/staking/*",
Expand Down Expand Up @@ -143,6 +144,9 @@ dao-voting-cw721-roles = { path = "./contracts/voting/dao-voting-cw721-roles", v
dao-voting-cw721-staked = { path = "./contracts/voting/dao-voting-cw721-staked", version = "2.8.0-alpha.2" }
dao-voting-onft-staked = { path = "./contracts/voting/dao-voting-onft-staked", version = "2.8.0-alpha.2" }
dao-voting-token-staked = { path = "./contracts/voting/dao-voting-token-staked", version = "2.8.0-alpha.2" }
gauge-adapter = { path = "./contracts/gauges/gauge-adapter", version = "2.8.0-alpha.2" }
gauge-budget-allocator = { path = "./contracts/gauges/budget-allocator", version = "2.8.0-alpha.2" }
gauge-orchestrator = { path = "./contracts/gauges/gauge", version = "2.8.0-alpha.2" }
nft-controllers = { path = "./packages/nft-controllers", version = "2.8.0-alpha.2" }

# v1 dependencies. used for state migrations.
Expand Down
60 changes: 60 additions & 0 deletions contracts/gauges/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Gauges

A gauge is a stake-weighted preference signal that periodically translates into
on-chain action. Stakers continuously express how some pool of resources should
be allocated (reward emissions, validator delegations, marketing budget, etc).
The gauge orchestrates the voting, the adapter translates the result into
`CosmosMsg`s for the DAO to execute.

Inspired by the [Curve gauge system](https://resources.curve.fi/reward-gauges/gauge-weights).
Forked from the [Wynd DAO repo](https://github.com/wynddao/wynddao) (Apache-2.0;
git history preserved per the LICENSE/NOTICE files) and modified to support any
DAO DAO voting module — cw4 membership, cw20-staked, cw721-staked, native- or
token-factory-staked.

## Two-contract design

```
┌────────────────────────┐ ┌──────────────────────┐
staking │ │ query │ │
hooks ─────▶│ gauge-orchestrator │◀───────▶│ gauge-adapter │
│ (this folder/gauge) │ msgs │ (this folder/gauge- │
│ │ │ adapter, or your │
│ │ │ own adapter) │
└────────────┬───────────┘ └──────────────────────┘
│ executes selected set
┌──────────────────────┐
│ DAO DAO core │
│ (proposal module) │
└──────────────────────┘
```

- **`gauge` (gauge-orchestrator)** — generic vote-tally + epoch dispatcher.
Holds one or many gauges, each with its own adapter. Doesn't know what an
"option" *is* — just an opaque string that the adapter validates and
interprets.
- **adapters** — pluggable. Each adapter defines what an option means (a
project address, a validator address, an AMM pool…), how to validate
user-submitted options, and how to translate a winning set into
`CosmosMsg`s. This folder ships two:
- **[`gauge-adapter`](./gauge-adapter/README.md)** — the *Marketing Gauge
Adapter*: project registry, refundable bond, proportional reward
dispatch (native or cw20).
- **[`budget-allocator`](./budget-allocator/README.md)** — a minimal
second example: admin-curated option list, no bond, native-token-only
proportional payouts. Useful as a starting point for new adapters or
as a treasury-allocator gauge in its own right.

Both contracts are wired into the DAO via the standard `dao-interface` module
plumbing — gauge-orchestrator is typically installed as a proposal module on
DAO DAO core, with the staking module's hooks routed to it.

See the individual contract READMEs for ExecuteMsg / QueryMsg semantics and
integration walk-throughs.

## Reference deployments

- [Curve](https://dao.curve.fi/gaugeweight) — the OG.
- [Wynd DAO](https://app.wynddao.com/gauges) — the immediate predecessor of this code.
5 changes: 5 additions & 0 deletions contracts/gauges/budget-allocator/.cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[alias]
wasm = "build --release --lib --target wasm32-unknown-unknown"
wasm-debug = "build --lib --target wasm32-unknown-unknown"
unit-test = "test --lib"
schema = "run --example schema"
35 changes: 35 additions & 0 deletions contracts/gauges/budget-allocator/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[package]
name = "gauge-budget-allocator"
description = "Minimal gauge adapter: distributes a fixed native-token budget proportional to gauge weights, over an admin-curated option list."
version = { workspace = true }
edition = { workspace = true }
license = { workspace = true }
repository = { workspace = true }

[lib]
crate-type = ["cdylib", "rlib"]

[features]
backtraces = ["cosmwasm-std/backtraces"]
# use library feature to disable all instantiate/execute/query exports
library = []

[dependencies]
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
cw-storage-plus = { workspace = true }
cw2 = { workspace = true }
cw-ownable = { workspace = true }
cw-utils = { workspace = true }
thiserror = { workspace = true }

# Re-use the orchestrator's adapter query/response types so this contract
# stays drop-in compatible with the orchestrator. `library` skips the
# adapter's own contract entry-points so its exports don't collide with
# ours at link time.
gauge-adapter = { workspace = true, features = ["library"] }

[dev-dependencies]
anyhow = { workspace = true }
cw-multi-test = { workspace = true }
serde = { workspace = true }
68 changes: 68 additions & 0 deletions contracts/gauges/budget-allocator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Gauge Budget Allocator

A minimal example of a [gauge adapter](../gauge-adapter/README.md) that
distributes a fixed native-token budget proportionally to gauge weights.
Compared to the [marketing-gauge adapter](../gauge-adapter/README.md), this
contract:

- has **no submission registry** — the owner curates the option list directly,
- has **no bond** — no `required_deposit` / `ReturnDeposits` flow,
- handles **native tokens only** — no cw20 path.

It exists as a working second example for the orchestrator + adapter pattern
and as a starting point for adapters that need a similar minimal shape
(treasury allocation, validator-preference signaling, AMM-incentive routing,
etc.). The contract is ~150 lines of logic — fork it freely.

## Lifecycle

1. **Instantiate.** `owner`, an initial `options` list (non-empty), and an
`epoch_budget: Coin`.
2. **Manage options.** Owner can `AddOption`, `RemoveOption`, or
`UpdateBudget` at any time.
3. **Wire into a gauge.** The DAO creates a gauge against this adapter via
`gauge-orchestrator`'s `CreateGauge`. The orchestrator pulls
`AllOptions` on attach and queries `CheckOption` when voters propose
new options via `ExecuteMsg::AddOption` upstream — those proposals
need to match the owner-curated list here.
4. **Execute.** At epoch close the orchestrator queries
`SampleGaugeMsgs { selected }`, which returns `BankMsg::Send` payouts
for each `(recipient, weight)` pair sized as
`epoch_budget.amount * weight` (floor).

## ExecuteMsg

| Variant | Auth | Notes |
|---|---|---|
| `AddOption { option }` | owner | Reject if option already in the set. |
| `RemoveOption { option }` | owner | Reject if option not in the set. |
| `UpdateBudget { epoch_budget }` | owner | Replaces the per-epoch budget. |
| `UpdateOwnership(action)` | owner / pending owner | Standard [`cw_ownable`](https://crates.io/crates/cw-ownable) two-step transfer / renounce flow. |

## QueryMsg

| Variant | Response | Purpose |
|---|---|---|
| `Config {}` | `Config { epoch_budget }` | Inspect deployed parameters. |
| `AllOptions {}` | `AllOptionsResponse` | Used by the orchestrator on attach. |
| `CheckOption { option }` | `CheckOptionResponse` | Used by the orchestrator on `AddOption`. |
| `SampleGaugeMsgs { selected }` | `SampleGaugeMsgsResponse` | Translates a selected set into payouts. |
| `Ownership {}` | `cw_ownable::Ownership<Addr>` | Current owner + any pending transfer. |

## Errors

| Variant | Trigger |
|---|---|
| `Ownership(OwnershipError)` | Caller is not the owner (or `cw-ownable` transfer-flow misuse). |
| `OptionAlreadyExists(option)` | `AddOption` with an option already in the set. |
| `OptionDoesNotExist(option)` | `RemoveOption` on an option not in the set. |
| `NoOptions` | Instantiated with an empty `options` list. |

## Funding

The contract itself does not hold the budget — the DAO does. The
orchestrator dispatches `BankMsg::Send` messages from the DAO's core
module, so the DAO must hold enough of `epoch_budget.denom` for the
selected set to actually transfer. If the DAO is underfunded the dispatch
will fail at execution time; consider integrating with a treasury balance
check or topping the DAO up regularly.
12 changes: 12 additions & 0 deletions contracts/gauges/budget-allocator/examples/schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use cosmwasm_schema::write_api;

use gauge_budget_allocator::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};

fn main() {
write_api! {
instantiate: InstantiateMsg,
execute: ExecuteMsg,
query: QueryMsg,
migrate: MigrateMsg,
}
}
Loading
Loading