MetaDAO: how futarchy on Solana actually works (three programs, two markets, one TWAP)
MetaDAO runs Solana governance through prediction markets. Three programs, conditional tokens, dual AMMs, TWAP settlement — the technical reference.
Most DAOs vote with tokens. MetaDAO bets with them. It's the only production-grade implementation of futarchy — Robin Hanson's "vote on values, bet on beliefs" mechanism — and it runs on Solana as three cooperating open-source programs.
Per proposal: spin up two conditional markets, let traders price the protocol's expected metric under each outcome, take the time-weighted average, and execute whichever side's market is priced higher. The proposal's executable Solana instruction fires automatically. No token-weighted vote, no quorum gymnastics.
This is the technical reference for how that's wired.
The three programs
MetaDAO is metaDAOproject/programs — Anchor-style Rust programs:
- autocrat — orchestrates futarchy. Anyone creates a proposal containing an executable SVM instruction. Autocrat owns the lifecycle: spin up conditional vaults & markets, monitor, finalise or revert.
- conditional_vault — mints the conditional tokens (pass and fail) against a deposit of the underlying asset. Owns settlement: redeem to the winning side after autocrat reports which side won.
- amm — a constant-product AMM (Uniswap-V2-shape) with a hardened on-chain TWAP oracle. Two AMM instances per proposal — one for the pass market, one for the fail market.
The conditional vault
Per proposal, autocrat creates two vaults: one for the governance token (META) and one for the quote token (USDC). Each vault accepts deposits and mints two conditional tokens 1:1:1:
deposit 10 USDC →
+ 10 USDC-on-pass (redeemable for USDC iff vault finalises)
+ 10 USDC-on-fail (redeemable for USDC iff vault reverts)Both tokens are real SPL mints. Both are fully transferable. One of them will eventually be worth the underlying; the other will be worth zero.
// Sketch of the deposit instruction's effects, per metaDAOproject/programs
pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
// 1. Pull underlying into the vault's escrow ATA
token::transfer(ctx.accounts.transfer_underlying_to_vault(), amount)?;
// 2. Mint amount of pass-conditional + amount of fail-conditional
// to the user, both authorised by the vault PDA
token::mint_to(ctx.accounts.mint_pass_conditional(), amount)?;
token::mint_to(ctx.accounts.mint_fail_conditional(), amount)?;
Ok(())
}The two markets
Once both vaults exist, autocrat opens two AMM pools — one per conditional outcome:
- Pass market: META-on-pass / USDC-on-pass
- Fail market: META-on-fail / USDC-on-fail
Traders take a position by depositing into the vault to mint the conditional pair, then trading one leg on the relevant market. Example flow for a trader who believes META is worth $112 if the proposal passes and $105 if it fails:
1. Deposit 1,000 USDC into the USDC vault
→ 1,000 USDC-on-pass + 1,000 USDC-on-fail
2. Deposit 10 META into the META vault
→ 10 META-on-pass + 10 META-on-fail
3. On the pass-market AMM:
→ swap USDC-on-pass for META-on-pass at the current $112 price
4. On the fail-market AMM:
→ swap META-on-fail for USDC-on-fail at the current $105 price
5. Wait for proposal finalisationAfter finalisation, the conditional tokens of the winning side redeem 1:1 for the underlying. The losing side's tokens evaporate. The trader's realised PnL is the spread between their conditional fills and the eventual outcome.
The TWAP oracle
The AMM exposes a time-weighted average price oracle modelled after Uniswap V2's, with extra manipulation guards (the original V2 oracle was famously cheap to skew on low-liquidity pairs). On each swap the AMM accumulates price × Δtime; the TWAP for a window is the difference of two accumulators divided by the window duration.
// Approximate read pattern (off-chain client)
const cumulNow = await readAmmAccumulator(passMarket) // accumulator + ts
const cumulStart = await readAmmAccumulator(passMarket, atSlot) // at proposal open
const twapPass = (cumulNow.value - cumulStart.value) / (cumulNow.ts - cumulStart.ts)
const twapFail = /* same on the fail market */
// Whichever is higher wins:
const passes = twapPass > twapFailAutocrat reads both TWAPs on finalisation, sets the winning vault's settlement authority to Finalised and the loser's to Reverted. The conditional tokens of each vault now have a fixed payoff.
Proposals and execution
A proposal is a Solana instruction wrapped in metadata. The instruction is anything you could put in a regular Solana transaction — a treasury transfer, a program upgrade, a fee parameter change, a vote in another DAO. Autocrat stores it; the market decides whether it runs.
// Conceptual shape (the real type comes from @metadaoproject/futarchy-sdk)
type Proposal = {
number: u64
descriptionUrl: string // IPFS, Arweave, or HTTPS markdown
instruction: {
programId: Pubkey
accounts: { pubkey: Pubkey; isSigner: boolean; isWritable: boolean }[]
data: Uint8Array
}
// Created at the same time, owned by the proposal:
passMarket: Pubkey // AMM
failMarket: Pubkey // AMM
baseConditional: { passMint: Pubkey, failMint: Pubkey, vault: Pubkey }
quoteConditional: { passMint: Pubkey, failMint: Pubkey, vault: Pubkey }
slotEnacted: Option<Slot>
}When the proposal's market window expires and pass wins, anyone can call autocrat's execute to invoke the wrapped instruction with autocrat as the signer authority. The DAO's decision is also the on-chain action.
SDK usage
Most app developers don't talk to the programs directly — they use the TypeScript SDK at metaDAOproject/futarchy-sdk:
pnpm install @metadaoproject/futarchy-sdkimport { FutarchyClient } from "@metadaoproject/futarchy-sdk"
import { Connection } from "@solana/web3.js"
const conn = new Connection("https://api.mainnet-beta.solana.com")
const client = new FutarchyClient(conn, wallet)
// List active proposals for a DAO:
const proposals = await client.daos.fetchProposals(daoAddress, { status: "pending" })
// Read live TWAPs for a proposal:
const twap = await client.proposals.fetchTwap(proposalAddress)
console.log("pass twap:", twap.pass, "fail twap:", twap.fail)
// Mint conditional tokens by depositing into a vault:
await client.vaults.deposit(quoteVault, BigInt(1_000_000_000)) // 1,000 USDC (6 decimals)Local development
Spinning up the full futarchy stack against a localsolana-test-validator is fiddly because three programs need to be present with matching IDs. The community helper jshiohaha/local-futarchy scripts the setup — clones autocrat, conditional_vault, and amm at the right versions and loads them into a fresh validator.
Why builders should care
Three concrete reasons MetaDAO's primitives matter beyond the governance use case:
- Conditional tokens are a general primitive. Any time you need to express "A pays out iff X, B pays out iff not-X", the conditional vault is the right tool. Useful for insurance, derivatives, options, settlement disputes.
- The TWAP AMM is reusable. The hardened Uniswap-V2-shape TWAP oracle is a clean dependency for any Solana protocol that needs manipulation-resistant price feeds without an off-chain oracle.
- Token launches as decisions. Several Solana projects in 2024-2026 launched via MetaDAO — letting the market decide whether to issue the token, rather than the team announcing it. A pattern that turns "should we launch?" into a tradeable question.
What it doesn't do
- It doesn't enforce a one-tx execution. Autocrat's wrapped instruction is one Solana instruction. For multi-step changes you need a proxy program or a wrapper contract that the single instruction calls into via CPI.
- It doesn't fix uninformed markets. If nobody trades, the TWAP is meaningless and the proposal's outcome reflects no real information. Liquidity bootstrapping per proposal is a non-trivial UX problem MetaDAO has spent real effort on.
- It doesn't generalise to off-chain decisions. The framework decides "should this on-chain instruction execute?" — not "should we hire this person?". Off-chain decisions still need traditional voting layered on top.
References
- metaDAOproject/programs — autocrat, conditional_vault, amm
- metaDAOproject/futarchy-sdk — the TypeScript client
- MetaDAO docs — Program architecture — canonical reference
- jshiohaha/local-futarchy — local validator setup script
Futarchy is one of the oldest unexplored mechanism-design ideas in crypto. MetaDAO is the first time it's actually shipped at production scale on a real chain. The conditional-vault primitive and the hardened TWAP AMM are both worth reading even if you never intend to run a proposal through them.