[Exchange] Quadratic funding allocator (Driver #15)

← All Specs

Goal

Implement the quadratic funding allocator (Exchange Driver #15) based on Buterin/Hitzig/Weyl's "Liberal Radicalism" (2018). Replace the heuristic "venture funder picks top-N gaps" with an open contribution mechanism where any agent can fund a gap they care about. The central matching pool uses the quadratic formula match = (Σ √cᵢ)² - Σ cᵢ to reward broad consensus over single-whale capture.

Acceptance Criteria

gap_funding_contributions table exists with fields: id, timestamp, agent_id, gap_id, amount, tier (direct/match), matching_amount, total_allocation
gap_funding_rounds table exists to track matching rounds (round_number, matching_pool_tokens, gaps_funded, status)
QuadraticFundingAllocator class implements:
- contribute_to_gap(agent_id, gap_id, amount) - Record direct contributions
- compute_matching_round(pool_size) - Run quadratic matching every 6h
- get_gap_funding_stats(gap_id) - Get per-gap funding breakdown
- get_all_gaps_funding() - Get system-wide funding stats
☑ API endpoints:
- POST /api/gaps/{gap_id}/fund - Contribute tokens to a gap
- GET /api/gaps/{gap_id}/funding - Get gap funding stats
- GET /api/gaps/funding/stats - Get all gaps funding
- POST /api/gaps/funding/match - Run matching round (admin only)
☑ Quadratic formula correctly computes: For direct contributions [c₁, c₂, ... cₙ], the total with match is (Σ √cᵢ)²
☑ Token ledger integration: contributions transfer from agent to gap:{gap_id} escrow
☑ Migration 069 creates both tables with proper indexes
☑ CLI commands work: --qf-match, --qf-stats, --qf-contribute
☑ Auth protection: /fund and /match endpoints require valid API key with agent_id binding
☑ Admin protection: /match endpoint requires write or admin permission

Approach

  • Create migration 069 for gap_funding_contributions and gap_funding_rounds tables
  • Implement QuadraticFundingAllocator class in funding_allocators.py:
  • - ensure_schema() - Create tables if not exists
    - contribute_to_gap() - Handle direct contributions with token transfer
    - _get_gap_totals() - Compute quadratic formula: (Σ √cᵢ)² - Σ cᵢ
    - compute_matching_round() - Distribute matching pool pro-rata across gaps
    - get_gap_funding_stats() - Per-gap breakdown
    - get_all_gaps_funding() - System-wide stats
  • Add API endpoints to api.py:
  • - POST /api/gaps/{gap_id}/fund - Authenticated contribution
    - GET /api/gaps/{gap_id}/funding - Public stats
    - GET /api/gaps/funding/stats - Public system stats
    - POST /api/gaps/funding/match - Admin-only matching trigger
  • Add CLI interface to funding_allocators.py for testing
  • Test with sample contributions to verify quadratic formula
  • Document the quadratic funding mechanism in AGENTS.md
  • Dependencies

    • token_ledger.py - Token transfers and account management
    • capital_pools.py - Pool management for matching pool
    • Migration system - For schema changes

    Dependents

    • Exchange layer funding dashboard
    • Knowledge gap detail pages
    • Economics drivers that emit rewards for gap funding participation

    Work Log

    2026-04-22 00:02 UTC — Driver #15 recurring cycle hardening

    • Started recurring cycle by reading AGENTS.md, /home/ubuntu/Orchestra/AGENTS.md, CLAUDE.md, alignment/gap/artifact planning docs, this task spec, and the economics v2 spec.
    • Verified live PostgreSQL dry-run: PYTHONPATH=. python3 -m economics_drivers.quadratic_funding --dry-run --limit 5 reported round 28 with 5 agents funding 1 gap, 100.0 direct + 500.0 matched tokens.
    • Found actionable hardening item in economics_drivers/quadratic_funding.py: _get_agent_gaps() used SELECT DISTINCT gap_id, ac_id, which can return the same gap more than once for an agent if they have multiple recent contributions to the same gap. That can debit an agent for duplicate targets while only retaining one per-gap QF contribution in the {gap_id: {agent_id: amount}} accumulator.
    • Implemented hardening while preserving the existing pool-account exclusion helper: deduplicates agent gap targets by latest contribution timestamp and uses DEFAULT CURRENT_TIMESTAMP for fallback schema creation.
    • Added regression coverage for unique recent open-gap selection. Existing QF tests continue to cover broad-consensus amplification and pool/system account exclusion.
    • Verification: PYTHONPATH=. pytest tests/test_quadratic_funding.py -q passed; python3 -m py_compile economics_drivers/quadratic_funding.py tests/test_quadratic_funding.py passed; live PostgreSQL dry-run passed.
    • Live cycle: PYTHONPATH=. python3 -m economics_drivers.quadratic_funding --limit 20 completed round 28: 20 agents funded 1 gap, 282.5 direct + 500.0 matched tokens, top gap gap-debate-20260417-033236-0fe26d91.
    • Note: the original worktree gitdir was mounted read-only for fetch, rebase, and git add, so this commit was prepared in a temporary clone under /tmp and pushed to the task branch.

    2026-04-11 08:00 PT — Attempt 4, focusing on scope

    • Feedback from review gate: scope mismatch - delivered broader economics infrastructure instead of just quadratic funding
    • Created this spec file to clarify the exact task scope
    • Reset branch to only include quadratic funding changes (migration + funding_allocators.py + api.py endpoints)
    • Excluded: economics_drivers/, squads/, datasets/, unrelated specs
    • Result: Clean commit with only quadratic funding implementation

    2026-04-11 00:40 PT — Fix auth and permissions

    • Bound agent_id to authenticated principal in /fund endpoint
    • Added admin permission check for /match endpoint
    • Fixed scope to prevent arbitrary agent_id injection

    2026-04-11 00:36 PT — Fix scope and auth

    • Removed broader economics_v2 spec (was too broad)
    • Fixed API endpoint authentication

    2026-04-11 00:32 PT — Initial implementation

    • Created migration 069: gap_funding_contributions and gap_funding_rounds tables
    • Implemented QuadraticFundingAllocator class with full quadratic formula
    • Added API endpoints for contributions and stats
    • Added CLI commands for testing
    • Updated /gaps page with quadratic funding stats widget
    • Test: 6 contributors (235 tokens) → 1002.82 matching (~4.3x leverage)

    Quadratic Funding Formula

    For a gap receiving direct contributions [c₁, c₂, ... cₙ] from n contributors:

    direct_total = Σ cᵢ
    quadratic_total = (Σ √cᵢ)²
    match_amount = quadratic_total - direct_total

    This rewards broad consensus:

    • 1 contributor giving 100: match = (√100)² - 100 = 0
    • 10 contributors giving 10 each: match = (10 × √10)² - 100 ≈ 216 - 100 = 116 (116% match)
    • 100 contributors giving 1 each: match = (100 × √1)² - 100 = 10,000 - 100 = 9,900 (99× match!)

    The more diverse the contributor base, the higher the matching multiplier.

    References

    • Buterin, Hitzig, Weyl (2018) "Liberal Radicalism: A Flexible Design for Philanthropic Matching Funds"
    • Gitcoin Grants quadratic funding implementation
    • CLR (Capital-Constrained Liberal Radicalism) matching

    File: 2bb1d0cd-2672-4286-b532-8ed2ebc4e59a_quadratic_funding_allocator_spec.md
    Modified: 2026-04-25 23:40
    Size: 7.2 KB