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?
- Token price drops → less attractive to hold
- Holders sell on ABC → reserve pool shrinks → price drops further
- Less ABC activity → less tribute revenue → common pool depletes faster
- Less funding available → fewer proposals → less community activity
- 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).