Skip to content
Draft
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
53 changes: 53 additions & 0 deletions .agent/AGENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Commonwealth Protocol — Agent Orientation

Commonwealth is a yield-bearing LP token protocol. Users buy tokens with USDC, provide liquidity, and earn yield from vault rehypothecation (5% APY). All vault yield is shared proportionally among liquidity providers — including yield from non-LPing users' USDC. This "common yield" is the core value proposition. We are validating the math in Python before writing Solidity.

**Start here, then read [CONTEXT.md](./CONTEXT.md) for operational details.**

---

## Reading Order

| # | File | When to read | What you learn |
|---|------|-------------|----------------|
| 1 | **[CONTEXT.md](./CONTEXT.md)** | Always | How to run, code locations, current problems, file map |
| 2 | **[MISSION.md](./MISSION.md)** | For design decisions | Value proposition, yield design, why buy_usdc_yield to LPs is intentional |
| 3 | **[math/FINDINGS.md](./math/FINDINGS.md)** | For analysis context | Root causes of vault residuals, mathematical proofs, known issues |
| 4 | **[math/PLAN.md](./math/PLAN.md)** | For implementation work | Exact code changes, execution order, success criteria |
| 5 | **[math/VALUES.md](./math/VALUES.md)** | For reference data | Manual calculations, scenario traces, actual vs expected numbers |
| 6 | **[../sim/MATH.md](../sim/MATH.md)** | For protocol math | All formulas, curve integrals, price multiplier mechanism |
| 7 | **[../sim/MODELS.md](../sim/MODELS.md)** | For model matrix | Codename convention, archived models, tradeoffs |
| 8 | **[../sim/TEST.md](../sim/TEST.md)** | For test env specifics | Virtual reserves, exposure factor, test-only mechanics |
| 9 | **[GUIDELINES.md](./GUIDELINES.md)** | For coding standards | Code style, principles, testing philosophy |
| 10 | **[workflows/ULTRAWORK.md](./workflows/ULTRAWORK.md)** | When user says "ultrawork" | Maximum precision mode — certainty protocol, zero-compromise delivery |

---

## Glossary

| Term | Definition |
|------|-----------|
| **Commonwealth** | Internal name for this protocol |
| **Bonding Curve** | Pricing function: determines token price from supply/reserves |
| **Vault** | External yield protocol (Spark/Sky/Aave) where all USDC earns 5% APY |
| **Rehypothecation** | Deploying user-deposited USDC into yield vaults |
| **LP** | Liquidity Provider — deposits tokens + USDC pair to earn yield |
| **Minting/Burning** | Creating/destroying tokens on buy/sell |
| **Token Inflation** | Minting new tokens for LPs at configurable APY |
| **Common Yield** | All vault yield shared among LPs — the core value proposition |
| **buy_usdc** | Aggregate USDC from token purchases (feeds into price) |
| **lp_usdc** | Aggregate USDC from LP deposits (does NOT feed into price in active models) |
| **effective_usdc** | `buy_usdc * (vault_balance / total_principal)` — yield-adjusted pricing input |
| **Price Multiplier** | `effective_usdc / buy_usdc` — how yield scales integral curve prices |
| **Fair Share Cap** | Limits withdrawals to proportional vault share (prevents bank runs) |
| **CYN/EYN/SYN/LYN** | Active models: [C]onstant/[E]xp/[S]igmoid/[L]og + [Y]ield->Price + [N]o LP->Price |

---

## Working Rules

1. **This is a testbed.** Validate math first. Get the math right before Solidity.
2. **Keep it simple.** Complexity should come from economic mechanics, not scaffolding.
3. **Track what matters.** Every model reports: total yield, yield per user, profit/loss per user, vault residual.
4. **Dual goal.** Attractive to users (everyone earns) AND sustainable for the protocol.
5. **Common good.** Models that structurally disadvantage late entrants must be identified and avoided.
126 changes: 126 additions & 0 deletions .agent/CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Operational Context

## How to Run

```bash
# Run comparison table (all 4 active models × all scenarios)
./run_sim.sh

# Run a specific model (all scenarios, verbose)
./run_sim.sh CYN

# Run specific scenario flag
./run_sim.sh --whale CYN
./run_sim.sh --bank
./run_sim.sh --multi CYN,EYN

# Run full test suite (220 tests, 7 modules)
python3 -m sim.test.run_all
```

## File Structure

```
sim/
core.py # ALL protocol logic: LP class, Vault, curves, buy/sell/LP operations (807 lines)
run_model.py # CLI entry point: argparse, comparison table, scenario dispatch (394 lines)
formatter.py # Output formatting: Formatter class, verbosity levels, ASCII art (326 lines)
scenarios/ # 8 scenario files: single_user, multi_user, bank_run, whale, hold, late, partial_lp, real_life
test/ # 7 test modules: conservation, invariants, yield_accounting, stress, curves, scenarios, coverage_gaps
MATH.md # Mathematical reference (formulas, curves, price mechanics)
MODELS.md # Model matrix, codenames, archived models
TEST.md # Test environment mechanics (virtual reserves, exposure factor)

.agent/
AGENT.md # Agent orientation (entry point, reading order, glossary)
CONTEXT.md # This file — operational guide, code locations, current state
MISSION.md # Design principles, yield philosophy, "common yield" rationale
GUIDELINES.md # Coding standards (typing, comments, testing, benchmarking)
math/FINDINGS.md # Root cause analysis, mathematical proofs, parameter sensitivity
math/PLAN.md # Implementation plan: FIX 1-4 (DONE) + Phase 5 (NEXT)
math/VALUES.md # Manual calculations, scenario traces, actual results
```

## Key Code Locations (`sim/core.py`)

| Component | Lines | What it does |
|-----------|-------|-------------|
| Constants & CurveConfig | 15-65 | CAP, EXPOSURE_FACTOR, VIRTUAL_LIMIT, VAULT_APY, `CurveConfig` dataclass, `EXP_CFG`/`SIG_CFG`/`LOG_CFG` instances |
| Model registry | 85-111 | `ModelConfig`, `MODELS` dict (16 combos), `ACTIVE_MODELS` = CYN, EYN, SYN, LYN |
| Curve integrals | 233-311 | `_exp_integral`, `_sig_integral`, `_log_integral`, `_bisect_tokens_for_cost` |
| `LP.__init__` | 326-357 | State: buy_usdc, lp_usdc, minted, k, user tracking dicts |
| `_get_effective_usdc()` | 361-377 | `buy_usdc * (vault / total_principal)` — yield inflates pricing input |
| `_get_price_multiplier()` | 379-387 | `effective_usdc / buy_usdc` — scales integral curve **buy** prices |
| `_get_sell_multiplier()` | 389-405 | FIX 4: `(buy_usdc + lp_usdc) / buy_usdc` — principal-only, no yield inflation |
| Virtual reserves (CYN) | 408-441 | `get_exposure`, `get_virtual_liquidity`, `_get_token/usdc_reserve` |
| `price` property | 448-467 | CP: `usdc_reserve / token_reserve`. Integral: `base_price(s) * multiplier` |
| Fair share cap | 473-489 | `_apply_fair_share_cap`, `_get_fair_share_scaling` — prevents vault drain |
| `buy()` | 516-553 | USDC → tokens. CP: k-invariant swap. Integral: bisect for token count |
| `sell()` | 555-625 | Tokens → USDC. Integral curves use `_get_sell_multiplier()` (FIX 4) |
| `add_liquidity()` | 627-641 | Deposits tokens + USDC pair into vault |
| `remove_liquidity()` | 645-694 | LP withdrawal: principal + yield (LP USDC + buy USDC) + token inflation |

## Current State

### All Residuals Eliminated

**ALL 4 models × 6 scenarios = 24 combinations show 0 vault residual.**

| Model | Vault Residual | Fix Applied |
|-------|:---:|-------------|
| **CYN** | **0** | FIX 1: removed `_update_k()` from LP ops (k was inflated 5.79×) |
| **EYN** | **0** | FIX 4: `_get_sell_multiplier()` (principal-only, no yield in sell) |
| **SYN** | **0** | None needed — sigmoid ceiling makes integral linear → perfect symmetry |
| **LYN** | **0** | FIX 4: same as EYN (log gentleness dampened it to 33 USDC, now 0) |

### Applied Fixes (all verified, 220/220 tests pass)

| Fix | What it does | Impact |
|-----|-------------|--------|
| **FIX 1** | Remove `_update_k()` calls from `add/remove_liquidity()` | CYN: 20k → 0 |
| **FIX 2** | `raw_out = max(D(0), raw_out)` after CP sell calc | Safety: no negative USDC |
| **FIX 3** | `TOKEN_INFLATION_FACTOR` constant (default 1.0) | Enables inflation isolation |
| **FIX 4** | `_get_sell_multiplier()` for integral curve sells | EYN: 7k → 0, LYN: 33 → 0 |

### Recent Structural Changes

| Change | What |
|--------|------|
| **T2** | `CurveConfig` frozen dataclass groups `base_price`, `k`, `max_price`, `midpoint`. Instances: `EXP_CFG`, `SIG_CFG`, `LOG_CFG`. Backward-compatible aliases kept |
| **T6** | `Vault.balance_usd` field removed. `balance_of()` returns `D(0)` when no snapshot exists |
| **T1** | `Color` class unified in `core.py`, imported by `formatter.py` (no more duplicate) |
| **T5** | ANSI regex pre-compiled as `_ANSI_RE` in `formatter.py` |

### FIX 4 Mechanism (Critical Design Change)

**Problem**: Buy divides by multiplier, sell multiplied by multiplier. After compounding, multiplier includes yield inflation → sell returns excess USDC → vault residual.

**Fix**: New `_get_sell_multiplier()` returns `(buy_usdc + lp_usdc) / buy_usdc` (or `1` when `lp_impacts_price=False`). This is the **principal-only** ratio — no vault yield. Sell is now symmetric with buy. Yield flows exclusively through `remove_liquidity()`.

**Design implication**: Users who sell tokens get only curve-based pricing (no yield). To capture yield, users must add liquidity first, then `remove_liquidity()`. This is the intended protocol incentive.

### Test Suite

220 tests across 7 modules:

| Module | Tests | What |
|--------|:---:|------|
| test_conservation | 4×4=16 | System USDC conservation across scenarios |
| test_invariants | 7×4=28 | buy_usdc tracking, sell proportion, LP math |
| test_yield_accounting | 3×4=12 | LP yield channels, duration scaling |
| test_stress | 9×4=36 | Atomic vault/LP accounting, multi-user invariants |
| test_curves | 9×4=36 | Integral math, overflow guards, bisect precision |
| test_scenarios | 13×4=52 | End-to-end scenario validation |
| test_coverage_gaps | 10×4=40 | Edge cases: yield flags, parametrized APY, fair share |

### Next Steps

See [math/PLAN.md](./math/PLAN.md) — **Phase 5** (6 sub-phases):
- A: Code cleanup (dead code, unused imports, typing, duplicates)
- B: FIX 4 toggle (`--fix` / `--no-fix` CLI flag + tokenomics analysis)
- C: Architecture (curve dispatch strategy, comments)
- D: Tests TG1-TG7 (FIX 4 regression, sigmoid edges, multi-LP, etc.)
- E: New features (quadratic/polynomial curves, reverse whale, time-weighted scenario)
- F: Math report (MA1-MA6 detailed analysis for specialist review)

For root cause analysis, see [math/FINDINGS.md](./math/FINDINGS.md). For yield design rationale, see [MISSION.md](./MISSION.md).
83 changes: 83 additions & 0 deletions .agent/GUIDELINES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Project Guidelines

These guidelines define the standards for code quality, style, and philosophy for this project.

1. **Clean & Readable Code**
- Write clean, concise, and readable code.
- Code should be self-explanatory where possible.

2. **Consistency**
- Look for existing patterns and principles.
- Maintain consistent ordering, rules, and naming conventions throughout the codebase.

3. **Comments & Cleanup**
- Cleanup files from unnecessary comments.
- Comments should be helpful and increase readability, not just describe obvious code.
- Remove unused imports.
- Ensure files are up to date with documentation.

4. **Structure & Visuals**
- Favour comment blocks / ASCII-art style banners to group code blocks into sections.
- This improves readability and navigation.

5. **Rich Output**
- Rich pretty-printing is important.
- Fit output into verbosity levels:
- `-v`: (Default) Necessary printing.
- `-vv`: Extended, enriches info for debugging.
- `-vvv`: Maximum information.

6. **Typing**
- Use strong typing.
- Ensure `pylance` and `pyright` linters/type-checkers run flawlessly.

7. **Math & Precision**
- Favour `Decimal` over `float` or `integer` where clean and sensible.
- Treat math like an art.

8. **Root Cause Fixes**
- Always aim to fix the real issue.
- Avoid shortcuts, dirty solutions, or hiding/clamping errors.

9. **Tests & Quality**
- Treat code, math, and comments like an art.
- Treat tests as the preference to ensure this art runs flawlessly.
- Extend tests often: add unit tests or new tests based on findings/bugs.
- Run tests to confirm modifications at the end.

10. **Benchmarking**
- Treat running scenarios as benchmarks.
- Aim for the best numbers possible and improve them.
- Recommend and consult on ideas to improve scenarios, models, and outputs.

11. **Documentation Consistency**
- Documentation (e.g., `CONTEXT.md`) MUST be accurate.
- If you find a command or instruction that doesn't work, fix the documentation immediately.
- Don't leave broken instructions for others to stumble over.

12. **Honest Critique Phase**
- After completing a task, launch a "subagent" (or rigorous self-review) to critique the work.
- Check for: regressions, unclean code, potential improvements.
- **Ask critical questions**:
- "If I removed X, what mechanism now ensures Y works?"
- "Who else relied on this code/state?"
- "What happens if external state (e.g., yield, time) changes?"
- Be honest: identify if a solution is "dirty" or "clamped" vs "real fix".
- Include this critique in your final summary.

13. **Unexpected Issues -> Return to Planning**
- When finding something unexpected, difficult, or a bug:
- ALWAYS go back to the PLANNING phase.
- Work with the user to explore, structure, and formalize a well-thought plan.
- Do not just "patch" it on the fly.

14. **Mandatory Review Workflow**
- All plans, code reviews, and summaries MUST be presented to the user for interactive review.
- **Environment-aware tooling**:
- **opencode** + Plannotator installed: Use Plannotator (`/submit-plan`, `/plannotator-review`, `/plannotator-annotate`).
- **Antigravity / Cursor IDE**: Use the built-in plan verification and review tools provided by the environment.
- **Plans**: Always verify the plan with the user at the beginning. Wait for explicit approval before proceeding.
- **Code reviews**: After each milestone, review code changes in detail with the user. Walk through what changed and why.
- **Summaries**: Always present summaries to the user at the end of work.
- **Confirmation required**: Always require explicit confirmation from the user before continuing. Do not assume approval — wait for a response.
- Never skip this step. Silent completion without review is not acceptable.
73 changes: 73 additions & 0 deletions .agent/MISSION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Commonwealth Protocol — Design Principles

## Core Value Proposition: The Common Yield

> **All USDC deposited by all users generates yield. That yield is shared proportionally among liquidity providers.**

- When a user **buys tokens**, their USDC goes to the vault and earns yield
- When a user **provides liquidity**, their LP USDC also goes to the vault
- **LPs receive yield from ALL vault USDC** — including USDC from users who did NOT LP
- Non-LP users effectively donate their buy_usdc yield to the LP collective
- This creates the incentive: **LP and you share in everyone's yield**

---

## Protocol Goals (Priority Order)

1. **Mathematical correctness** — vault residual must be ~0 when all users exit. Validate in Python before Solidity.
2. **Incentivize buy + hold + LP** — LPs earn the most; buyers contribute to the commons; holders support price.
3. **Sustainability & fairness** — late entrants not structurally disadvantaged; fewest users lose money.
4. **Attractive returns** — real yield from vault rehypothecation (Spark/Sky/Aave, ~5% APY).

---

## Yield Design

### Three Channels for LPs

| Channel | Source | Mechanism |
|---------|--------|-----------|
| **LP USDC yield** | Yield on USDC provided as liquidity | Direct withdrawal in `remove_liquidity()` |
| **Buy USDC yield** | Yield on USDC used to buy tokens | Direct withdrawal + price appreciation |
| **Token inflation** | New tokens minted proportional to LP tokens | Configurable rate (can be 0%) |

### Who Gets What

| User Type | LP USDC Yield | Own Buy Yield | Others' Buy Yield | Token Inflation |
|-----------|:---:|:---:|:---:|:---:|
| Buyer only (no LP) | - | Via price only | - | - |
| Buyer + LP | Direct | Direct + price | Proportional share | Yes |

### Key Design Decision

**The buy_usdc_yield going to LPs in `remove_liquidity()` is INTENTIONAL.**

```python
# core.py:612-613 — THIS IS CORRECT, NOT A BUG
buy_usdc_yield_full = buy_usdc_principal * (delta - D(1))
total_usdc_full = usd_amount_full + buy_usdc_yield_full
```

This is the mechanism for distributing the "common yield." Without it, LPs only earn on their own USDC — there's no incentive to LP beyond individual yield.

---

## Model Architecture

Four bonding curves, all sharing the same invariants:

| Invariant | Value | Why |
|-----------|-------|-----|
| Yield -> Price | Yes | Vault growth inflates token price via `effective_usdc` |
| LP -> Price | No | Adding/removing liquidity is price-neutral |
| Token Inflation | Yes | LPs earn minted tokens (configurable rate) |
| Buy/Sell -> Price | Yes | Core price discovery mechanism |

Active models: **CYN** (Constant Product), **EYN** (Exponential), **SYN** (Sigmoid), **LYN** (Logarithmic). See [../sim/MODELS.md](../sim/MODELS.md).

### Two Yield Channels Operate Simultaneously

1. **Price appreciation**: `effective_usdc = buy_usdc * (vault / total_principal)` inflates the curve
2. **Direct LP withdrawal**: `remove_liquidity()` pays LPs yield as USDC

In single-user scenarios, these cancel out perfectly (mathematically proven — see [math/FINDINGS.md](./math/FINDINGS.md)). In multi-user scenarios, the bonding curve must be symmetric for conservation to hold. After FIX 1 (CYN k-inflation) and FIX 4 (principal-only sell multiplier), **all four models achieve 0 residual across all scenarios**.
Loading