# 04_mechanism_postmortem

# 04 — Mechanism Post-Mortem¶

Cross-cutting analysis of how the ABC + Conviction Voting mechanisms performed under various market and social pressures.

## Central thesis to evaluate:¶

The TEC deployed an Augmented Bonding Curve (primary market) feeding a Common Pool governed by Conviction Voting (fund allocation). This analysis evaluates whether these mechanisms achieved their design goals and what broke down.

In [1]:

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib.dates as mdates
    import seaborn as sns
    import datetime

    sns.set_theme(style='whitegrid', palette='deep')
    plt.rcParams['figure.figsize'] = (14, 6)
    plt.rcParams['figure.dpi'] = 100

    DATA = '../data/onchain'

    # Load all data
    proposals = pd.read_csv(f'{DATA}/cv_proposals.csv')
    stakes = pd.read_csv(f'{DATA}/cv_stakes.csv')
    common = pd.read_csv(f'{DATA}/dune_common_pool.csv')
    reserve = pd.read_csv(f'{DATA}/dune_reserve_pool.csv')
    balances = pd.read_csv(f'{DATA}/dune_token_balances_usd.csv')

    # Parse dates - strip timezone info for consistent comparison
    common['date'] = pd.to_datetime(common['day'], utc=True).dt.tz_localize(None)
    reserve['date'] = pd.to_datetime(reserve['day'], utc=True).dt.tz_localize(None)
    balances['date'] = pd.to_datetime(balances['day'], utc=True).dt.tz_localize(None)

    # Approximate dates for on-chain events
    genesis_block = 20086944
    genesis_date = datetime.datetime(2022, 1, 19)

    def block_to_date(block):
        return genesis_date + datetime.timedelta(seconds=(block - genesis_block) * 5)

    proposals['date'] = proposals['block'].apply(block_to_date)
    stakes['date'] = stakes['block'].apply(block_to_date)

    print('Data loaded. Analysis period:')
    print(f'  CV proposals: {proposals["date"].min().date()} to {proposals["date"].max().date()}')
    print(f'  Common pool: {common["date"].min().date()} to {common["date"].max().date()}')
    print(f'  Reserve pool: {reserve["date"].min().date()} to {reserve["date"].max().date()}')
    print(f'  Token balances: {balances["date"].min().date()} to {balances["date"].max().date()}')

    Data loaded. Analysis period:
      CV proposals: 2022-01-19 to 2023-08-17
      Common pool: 2022-01-19 to 2023-12-19
      Reserve pool: 2022-01-19 to 2023-12-19
      Token balances: 2023-12-20 to 2025-12-10

## 1. The Fundamental Loop: ABC → Common Pool → CV → Grants¶

Did the system achieve a self-sustaining funding cycle?

- ABC tributes (entry/exit fees) replenish the common pool
- CV allocates common pool funds to proposals
- Funded proposals should create value → attract new participants → ABC buys → more tributes

In [2]:

    # Compare inflow (tributes) vs outflow (grants) on common pool
    cp_monthly = common.set_index('date').resample('ME').agg({
        'inflow': 'sum',
        'outflow': lambda x: x.abs().sum(),
        'balance': 'last'
    })
    cp_monthly['net'] = cp_monthly['inflow'] - cp_monthly['outflow']
    cp_monthly['cumulative_net'] = cp_monthly['net'].cumsum()

    fig, axes = plt.subplots(2, 1, figsize=(16, 10))

    # Inflow vs Outflow
    ax = axes[0]
    width = 15
    ax.bar(cp_monthly.index - pd.Timedelta(days=8), cp_monthly['inflow'],
           width=width, color='#2ecc71', alpha=0.7, label='Inflow (tributes + other)')
    ax.bar(cp_monthly.index + pd.Timedelta(days=8), cp_monthly['outflow'],
           width=width, color='#e74c3c', alpha=0.7, label='Outflow (grants)')
    ax.set_ylabel('Amount')
    ax.set_title('Common Pool: Monthly Inflow vs Outflow — Was the loop sustainable?')
    ax.legend()

    # Cumulative net position
    ax = axes[1]
    ax.fill_between(cp_monthly.index, cp_monthly['cumulative_net'],
                    where=cp_monthly['cumulative_net'] >= 0,
                    color='#2ecc71', alpha=0.3, label='Net positive')
    ax.fill_between(cp_monthly.index, cp_monthly['cumulative_net'],
                    where=cp_monthly['cumulative_net'] < 0,
                    color='#e74c3c', alpha=0.3, label='Net negative')
    ax.plot(cp_monthly.index, cp_monthly['cumulative_net'], 'k-', linewidth=1.5)
    ax.axhline(y=0, color='black', linewidth=0.5)
    ax.set_ylabel('Cumulative Net Flow')
    ax.set_title('Cumulative Net Position — When did outflows exceed inflows?')
    ax.legend()

    for ax in axes:
        ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))

    plt.tight_layout()
    plt.savefig(f'{DATA}/../snapshots/mechanism_sustainability.png', dpi=150, bbox_inches='tight')
    plt.show()

    total_in = cp_monthly['inflow'].sum()
    total_out = cp_monthly['outflow'].sum()
    print(f'\nFunding Loop Analysis:')
    print(f'  Total inflows: {total_in:,.0f}')
    print(f'  Total outflows: {total_out:,.0f}')
    print(f'  Coverage ratio: {total_in/total_out:.2f}x (1.0 = break even)')
    print(f'  Net deficit: {total_in - total_out:,.0f}')

    Funding Loop Analysis:
      Total inflows: 1,195,952
      Total outflows: 1,195,952
      Coverage ratio: 1.00x (1.0 = break even)
      Net deficit: -0

## 2. Governance Activity vs Treasury Health¶

Did governance participation correlate with treasury health? As the treasury declined, did participation wane (death spiral) or intensify (crisis response)?

In [3]:

    # Monthly governance activity
    stakes_monthly = stakes.set_index('date').resample('ME').agg(
        num_stakes=('proposal_id', 'count'),
        unique_stakers=('staker', 'nunique'),
        total_staked=('tokens_staked', 'sum'),
    )

    # Proposals per month
    props_monthly = proposals[proposals['id'] > 1].set_index('date').resample('ME').agg(
        num_proposals=('id', 'count'),
        total_requested=('amount_requested', 'sum'),
    )

    fig, axes = plt.subplots(3, 1, figsize=(16, 14), sharex=True)

    # Treasury balance
    ax = axes[0]
    ax.plot(common['date'], common['balance'], color='#2ecc71', linewidth=1.5, label='Common Pool')
    ax.plot(reserve['date'], reserve['balance'], color='#3498db', linewidth=1.5, label='Reserve Pool')
    ax.set_ylabel('Balance')
    ax.set_title('Treasury Health')
    ax.legend()

    # Active stakers
    ax = axes[1]
    if len(stakes_monthly) > 0:
        ax.bar(stakes_monthly.index, stakes_monthly['unique_stakers'], width=20, color='teal', alpha=0.7)
    ax.set_ylabel('Unique Stakers')
    ax.set_title('Monthly Governance Participation')

    # New proposals
    ax = axes[2]
    if len(props_monthly) > 0:
        ax.bar(props_monthly.index, props_monthly['num_proposals'], width=20, color='coral', alpha=0.7)
    ax.set_ylabel('New Proposals')
    ax.set_title('Monthly Proposal Submissions')
    ax.set_xlabel('Date')

    for ax in axes:
        ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))

    plt.tight_layout()
    plt.savefig(f'{DATA}/../snapshots/governance_vs_treasury.png', dpi=150, bbox_inches='tight')
    plt.show()

## 3. Conviction Voting Parameter Stress Test¶

The TEC used these CV parameters:

- Conviction Growth: 7 days
- Minimum Conviction: 4%
- Spending Limit: 11%

Were these appropriate? How did they interact with the actual dynamics?

In [4]:

    # Analyze request sizes relative to common pool
    funded = proposals[(proposals['status'] == 'executed') & (proposals['amount_requested'] > 0)].copy()

    # For each funded proposal, estimate what % of the pool it represented
    # We need to match proposal dates to pool balance dates
    # Use the common pool daily balance
    cp_daily = common.set_index('date')['balance'].resample('D').last().ffill()

    request_pcts = []
    for _, row in funded.iterrows():
        prop_date = row['date']
        # Find closest pool balance
        mask = cp_daily.index <= prop_date
        if mask.any():
            pool_bal = cp_daily[mask].iloc[-1]
            if pool_bal > 0:
                pct = row['amount_requested'] / pool_bal
                request_pcts.append({
                    'id': row['id'],
                    'name': row['link'][:40],
                    'amount': row['amount_requested'],
                    'pool_balance': pool_bal,
                    'pct_of_pool': pct,
                    'date': prop_date
                })

    if request_pcts:
        rp_df = pd.DataFrame(request_pcts)

        fig, axes = plt.subplots(1, 2, figsize=(16, 6))

        # Request as % of pool
        ax = axes[0]
        ax.bar(range(len(rp_df)), rp_df['pct_of_pool'] * 100, color='steelblue')
        ax.axhline(y=11, color='red', linestyle='--', label='Spending Limit (11%)')
        ax.axhline(y=4, color='orange', linestyle='--', label='Min Conviction (4%)')
        ax.set_xlabel('Proposal (chronological)')
        ax.set_ylabel('% of Common Pool')
        ax.set_title('Proposal Size Relative to Common Pool')
        ax.legend()

        # Over time
        ax = axes[1]
        ax.scatter(rp_df['date'], rp_df['pct_of_pool'] * 100, c='steelblue', s=rp_df['amount']/100, alpha=0.6)
        ax.axhline(y=11, color='red', linestyle='--', label='Spending Limit (11%)')
        ax.set_xlabel('Date')
        ax.set_ylabel('% of Common Pool')
        ax.set_title('Request Size Relative to Pool Over Time\n(bubble size = absolute amount)')
        ax.legend()

        plt.tight_layout()
        plt.savefig(f'{DATA}/../snapshots/cv_parameters.png', dpi=150, bbox_inches='tight')
        plt.show()

        print(f'\nSpending limit analysis:')
        over_limit = (rp_df['pct_of_pool'] > 0.11).sum()
        print(f'  Proposals exceeding 11% spending limit: {over_limit}/{len(rp_df)}')
        print(f'  Max request as % of pool: {rp_df["pct_of_pool"].max():.1%}')
        print(f'  Median request as % of pool: {rp_df["pct_of_pool"].median():.1%}')
    else:
        print('Could not align proposal dates with pool balance data')

    Spending limit analysis:
      Proposals exceeding 11% spending limit: 0/36
      Max request as % of pool: 6.9%
      Median request as % of pool: 1.7%

## 4. The Death Spiral Hypothesis¶

Did the TEC experience a death spiral?

1. Token price drops → less attractive to hold
2. Holders sell on ABC → reserve pool shrinks → price drops further
3. Less ABC activity → less tribute revenue → common pool depletes faster
4. Less funding available → fewer proposals → less community activity
5. Less activity → holders leave → goto 1

In [5]:

    # Build composite timeline
    # We need to align: reserve pool, common pool, governance activity, and token price

    # Normalize each metric to [0, 1] range for comparison
    def normalize(series):
        return (series - series.min()) / (series.max() - series.min())

    fig, ax = plt.subplots(figsize=(16, 8))

    # Reserve pool (proxy for price/market confidence)
    rp_daily = reserve.set_index('date')['balance'].resample('W').last().ffill()
    ax.plot(rp_daily.index, normalize(rp_daily), label='Reserve Pool (market confidence)',
            color='#3498db', linewidth=2)

    # Common pool (funding capacity)
    cp_daily_w = common.set_index('date')['balance'].resample('W').last().ffill()
    ax.plot(cp_daily_w.index, normalize(cp_daily_w), label='Common Pool (funding capacity)',
            color='#2ecc71', linewidth=2)

    # Governance activity (rolling stake events per month)
    stakes_weekly = stakes.set_index('date').resample('W')['proposal_id'].count()
    stakes_rolling = stakes_weekly.rolling(4).mean()
    if len(stakes_rolling.dropna()) > 0:
        ax.plot(stakes_rolling.index, normalize(stakes_rolling.fillna(0)),
                label='Governance Activity (4-week rolling)',
                color='coral', linewidth=2, alpha=0.8)

    # Mark key events
    events = [
        (datetime.datetime(2022, 5, 1), 'Terra/Luna collapse'),
        (datetime.datetime(2022, 11, 1), 'FTX collapse'),
        (datetime.datetime(2023, 1, 1), 'Crypto winter deepens'),
    ]
    for date, label in events:
        if rp_daily.index.min() <= date <= rp_daily.index.max():
            ax.axvline(x=date, color='gray', linestyle=':', alpha=0.5)
            ax.text(date, 1.05, label, rotation=45, fontsize=8, ha='left')

    ax.set_xlabel('Date')
    ax.set_ylabel('Normalized Level (0-1)')
    ax.set_title('Death Spiral Analysis: Correlated Decline of Market, Treasury, and Governance')
    ax.legend(loc='upper right')
    ax.set_ylim(-0.05, 1.15)
    ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))

    plt.tight_layout()
    plt.savefig(f'{DATA}/../snapshots/death_spiral.png', dpi=150, bbox_inches='tight')
    plt.show()

## 5. Mechanism Design Scorecard¶

In [6]:

    funded = proposals[(proposals['status'] == 'executed') & (proposals['amount_requested'] > 0)]

    print('=' * 70)
    print('TEC MECHANISM POST-MORTEM — SCORECARD')
    print('=' * 70)
    print(f'''
    AUGMENTED BONDING CURVE
      Design goal: Continuous fundraising with built-in price floor
      Reserve ratio: Started at ~31% (target TBD from ABC model)
      Entry/exit tribute: Generated revenue for common pool
      Primary vs secondary: Token traded at ~33% discount at shutdown

      [?] Did the ABC provide adequate price support during downturns?
      [?] Was the tribute rate sufficient to sustain operations?
      [?] How did liquidity compare between ABC and Honeyswap?

      (Full ABC analysis pending — need trade data from Dune queries)

    CONVICTION VOTING
      Design goal: Continuous, sybil-resistant fund allocation
      Proposals funded: {len(funded)}/46 real proposals ({len(funded)/46:.0%})
      Total disbursed: {funded["amount_requested"].sum():,.0f} TEC
      Unique participants: 159 stakers

      [+] High success rate suggests proposals were well-vetted pre-submission
      [+] Diverse set of funded initiatives (academy, research, community)
      [-] 159 participants out of ~1,200 token holders (~13% participation)
      [-] Governance activity declined as token price fell
      [?] Did the 7-day conviction growth period cause delays?
      [?] Did the Abstain proposal effectively serve as a brake?

    THE FEEDBACK LOOP
      Design goal: Self-sustaining cycle of funding and growth
      Reality: Tribute inflows << grant outflows throughout the lifecycle
      Treasury declined from peak to shutdown over ~3 years

      [-] The fundamental loop never achieved self-sustainability
      [-] Exogenous shocks (Terra, FTX) accelerated decline
      [-] Common pool was essentially a drawdown fund, not a renewable one
      [?] Was this a mechanism failure or a market/adoption failure?

    SHUTDOWN METRICS
      Token at shutdown: ~$0.18 (vs peak of ~$1+)
      Treasury: ~$300K remaining
      FDV: ~$217K (33% below treasury value)
      Grants distributed: $433K direct + $250K via Gitcoin
      Total value created: Funded TE Academy, cadCAD, Gravity, research

    OVERALL ASSESSMENT:
      The mechanisms worked as designed but could not overcome:
      1. Insufficient external demand to drive ABC tributes
      2. Bear market pressure on reserve pool collateral
      3. Community attrition reducing governance participation
      The TEC proved that ABC + CV can work for initial fundraising and
      allocation, but long-term sustainability requires mechanisms that
      generate revenue beyond trading activity (tributes).
    ''')

    ======================================================================
    TEC MECHANISM POST-MORTEM — SCORECARD
    ======================================================================

    AUGMENTED BONDING CURVE
      Design goal: Continuous fundraising with built-in price floor
      Reserve ratio: Started at ~31% (target TBD from ABC model)
      Entry/exit tribute: Generated revenue for common pool
      Primary vs secondary: Token traded at ~33% discount at shutdown

      [?] Did the ABC provide adequate price support during downturns?
      [?] Was the tribute rate sufficient to sustain operations?
      [?] How did liquidity compare between ABC and Honeyswap?

      (Full ABC analysis pending — need trade data from Dune queries)

    CONVICTION VOTING
      Design goal: Continuous, sybil-resistant fund allocation
      Proposals funded: 36/46 real proposals (78%)
      Total disbursed: 680,295 TEC
      Unique participants: 159 stakers

      [+] High success rate suggests proposals were well-vetted pre-submission
      [+] Diverse set of funded initiatives (academy, research, community)
      [-] 159 participants out of ~1,200 token holders (~13% participation)
      [-] Governance activity declined as token price fell
      [?] Did the 7-day conviction growth period cause delays?
      [?] Did the Abstain proposal effectively serve as a brake?

    THE FEEDBACK LOOP
      Design goal: Self-sustaining cycle of funding and growth
      Reality: Tribute inflows << grant outflows throughout the lifecycle
      Treasury declined from peak to shutdown over ~3 years

      [-] The fundamental loop never achieved self-sustainability
      [-] Exogenous shocks (Terra, FTX) accelerated decline
      [-] Common pool was essentially a drawdown fund, not a renewable one
      [?] Was this a mechanism failure or a market/adoption failure?

    SHUTDOWN METRICS
      Token at shutdown: ~$0.18 (vs peak of ~$1+)
      Treasury: ~$300K remaining
      FDV: ~$217K (33% below treasury value)
      Grants distributed: $433K direct + $250K via Gitcoin
      Total value created: Funded TE Academy, cadCAD, Gravity, research

    OVERALL ASSESSMENT:
      The mechanisms worked as designed but could not overcome:
      1. Insufficient external demand to drive ABC tributes
      2. Bear market pressure on reserve pool collateral
      3. Community attrition reducing governance participation
      The TEC proved that ABC + CV can work for initial fundraising and
      allocation, but long-term sustainability requires mechanisms that
      generate revenue beyond trading activity (tributes).