Refactor lockdrop calculations to a module
This commit is contained in:
parent
779b091ccd
commit
48bb2f7b83
@ -424,7 +424,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"execution_count": null,
|
||||
"id": "26573d6b",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@ -489,7 +489,7 @@
|
||||
"# Load events from watcher file\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"watcher_events_path = os.path.join(os.getenv('GENERATED_DIR', './generated'), 'watcher-events.json')\n",
|
||||
"watcher_events_path = os.path.join(os.getenv('GENERATED_DIR', 'generated'), 'watcher-events.json')\n",
|
||||
"events = load_watcher_events(watcher_events_path)\n",
|
||||
"lock_stats = analyze_lockdrop_events(events)\n",
|
||||
"\n",
|
||||
|
||||
608
lockdrop-calculations.ipynb
Normal file
608
lockdrop-calculations.ipynb
Normal file
@ -0,0 +1,608 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "experimental-header",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Z Token Lockdrop Distribution\n",
|
||||
"\n",
|
||||
"This notebook allows you to experiment with different participation distributions to see how they affect token allocations, bonus pools, and the penalty system.\n",
|
||||
"\n",
|
||||
"**Features:**\n",
|
||||
"- Adjust star and galaxy participation numbers by lock duration\n",
|
||||
"- Real-time calculation of allocations and bonus pools\n",
|
||||
"- Visualization of results"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"id": "setup-imports",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Import shared calculation module\n",
|
||||
"from lockdrop_calculations import (\n",
|
||||
" configure_matplotlib, print_constants_summary, calculate_dynamic_allocations,\n",
|
||||
" calculate_bonus_pools, calculate_final_allocations, generate_test_output,\n",
|
||||
" print_analysis_tables, create_visualization, PENALTY_RATES, NUM_STARS, NUM_GALAXIES\n",
|
||||
")\n",
|
||||
"import json\n",
|
||||
"from decimal import Decimal\n",
|
||||
"import pandas as pd\n",
|
||||
"import ipywidgets as widgets\n",
|
||||
"from IPython.display import display, clear_output\n",
|
||||
"\n",
|
||||
"# Configure matplotlib\n",
|
||||
"configure_matplotlib()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"id": "show-constants",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"================================================================================\n",
|
||||
"📊 $Z LOCKDROP DISTRIBUTION - CORE CONSTANTS\n",
|
||||
"================================================================================\n",
|
||||
"\n",
|
||||
"🔒 LOCKDROP ALLOCATION\n",
|
||||
" Parameter Value\n",
|
||||
"Total Supply (1 $Z per Urbit ID) 4,294,967,296\n",
|
||||
" Lockdrop Allocation % 30.0%\n",
|
||||
" Lockdrop Allocation ($Z) 1,288,490,188.8\n",
|
||||
"\n",
|
||||
"⭐ URBIT POINTS DISTRIBUTION\n",
|
||||
"Point Type Count Allocation %\n",
|
||||
" Galaxies 256 0.39%\n",
|
||||
" Stars 65,280 99.61%\n",
|
||||
" Planets 4,294,901,760 0%\n",
|
||||
"\n",
|
||||
"⏱️ LOCKDROP PARAMETERS\n",
|
||||
" Parameter Value\n",
|
||||
" Block Duration 2 seconds\n",
|
||||
"Max Point Lock Duration (5 yrs) 157,788,000 seconds\n",
|
||||
" Total Blocks 78,894,000\n",
|
||||
" Star Allocation % 0.996093\n",
|
||||
" Galaxy Allocation % 0.003906\n",
|
||||
"\n",
|
||||
"================================================================================\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Display core constants\n",
|
||||
"print_constants_summary()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "input-section-header",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🎛️ Experimental Parameters\n",
|
||||
"\n",
|
||||
"Adjust the participation numbers below to experiment with different scenarios. The default values represent a balanced distribution scenario."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"id": "input-parameters",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"🎯 PARTICIPATION INPUT CONTROLS\n",
|
||||
"==================================================\n",
|
||||
"\n",
|
||||
"⭐ STAR PARTICIPATION\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "79244d07df4e4ee6923dda69c1febc2e",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
"VBox(children=(IntText(value=8000, description='1 Year:', style=DescriptionStyle(description_width='80px')), I…"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"🌌 GALAXY PARTICIPATION\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "4071b25411c44a67b26f5924a2507c0c",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
"VBox(children=(IntText(value=40, description='1 Year:', style=DescriptionStyle(description_width='80px')), Int…"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "1c785fe3828d4d5eb62a9f35755f29f8",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
"Button(button_style='success', description='🔄 Calculate Allocations', layout=Layout(margin='20px 0', width='20…"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"🌌 CALCULATIONS\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "bc3937e163a34ff58f215f94a36aee3e",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
"Output()"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Define reasonable default values\n",
|
||||
"DEFAULT_PARTICIPANTS = {\n",
|
||||
" 'stars_1_years': 8000,\n",
|
||||
" 'stars_2_years': 8000,\n",
|
||||
" 'stars_3_years': 8000,\n",
|
||||
" 'stars_4_years': 8000,\n",
|
||||
" 'stars_5_years': 8000,\n",
|
||||
" 'galaxies_1_years': 40,\n",
|
||||
" 'galaxies_2_years': 40,\n",
|
||||
" 'galaxies_3_years': 40,\n",
|
||||
" 'galaxies_4_years': 40,\n",
|
||||
" 'galaxies_5_years': 40\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"# Create input widgets\n",
|
||||
"print(\"🎯 PARTICIPATION INPUT CONTROLS\")\n",
|
||||
"print(\"=\"*50)\n",
|
||||
"\n",
|
||||
"# Star participation widgets\n",
|
||||
"print(\"\\n⭐ STAR PARTICIPATION\")\n",
|
||||
"stars_1_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['stars_1_years'], description='1 Year:', style={'description_width': '80px'})\n",
|
||||
"stars_2_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['stars_2_years'], description='2 Years:', style={'description_width': '80px'})\n",
|
||||
"stars_3_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['stars_3_years'], description='3 Years:', style={'description_width': '80px'})\n",
|
||||
"stars_4_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['stars_4_years'], description='4 Years:', style={'description_width': '80px'})\n",
|
||||
"stars_5_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['stars_5_years'], description='5 Years:', style={'description_width': '80px'})\n",
|
||||
"\n",
|
||||
"star_controls = widgets.VBox([stars_1_years, stars_2_years, stars_3_years, stars_4_years, stars_5_years])\n",
|
||||
"display(star_controls)\n",
|
||||
"\n",
|
||||
"print(\"\\n🌌 GALAXY PARTICIPATION\")\n",
|
||||
"galaxies_1_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['galaxies_1_years'], description='1 Year:', style={'description_width': '80px'})\n",
|
||||
"galaxies_2_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['galaxies_2_years'], description='2 Years:', style={'description_width': '80px'})\n",
|
||||
"galaxies_3_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['galaxies_3_years'], description='3 Years:', style={'description_width': '80px'})\n",
|
||||
"galaxies_4_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['galaxies_4_years'], description='4 Years:', style={'description_width': '80px'})\n",
|
||||
"galaxies_5_years = widgets.IntText(value=DEFAULT_PARTICIPANTS['galaxies_5_years'], description='5 Years:', style={'description_width': '80px'})\n",
|
||||
"\n",
|
||||
"galaxy_controls = widgets.VBox([galaxies_1_years, galaxies_2_years, galaxies_3_years, galaxies_4_years, galaxies_5_years])\n",
|
||||
"display(galaxy_controls)\n",
|
||||
"\n",
|
||||
"# Calculate button\n",
|
||||
"calculate_button = widgets.Button(description='🔄 Calculate Allocations', button_style='success', layout={'width': '200px', 'margin': '20px 0'})\n",
|
||||
"display(calculate_button)\n",
|
||||
"\n",
|
||||
"# Output area\n",
|
||||
"print(\"\\n🌌 CALCULATIONS\")\n",
|
||||
"output_area = widgets.Output()\n",
|
||||
"display(output_area)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"id": "calculation-function",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def calculate_and_display(button=None):\n",
|
||||
" \"\"\"Calculate allocations and display results based on input parameters.\"\"\"\n",
|
||||
" with output_area:\n",
|
||||
" clear_output(wait=True)\n",
|
||||
"\n",
|
||||
" # Get current values from widgets\n",
|
||||
" participation_counts = {\n",
|
||||
" 'stars_1_years': stars_1_years.value,\n",
|
||||
" 'stars_2_years': stars_2_years.value,\n",
|
||||
" 'stars_3_years': stars_3_years.value,\n",
|
||||
" 'stars_4_years': stars_4_years.value,\n",
|
||||
" 'stars_5_years': stars_5_years.value,\n",
|
||||
" 'galaxies_1_years': galaxies_1_years.value,\n",
|
||||
" 'galaxies_2_years': galaxies_2_years.value,\n",
|
||||
" 'galaxies_3_years': galaxies_3_years.value,\n",
|
||||
" 'galaxies_4_years': galaxies_4_years.value,\n",
|
||||
" 'galaxies_5_years': galaxies_5_years.value\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" # Validate inputs\n",
|
||||
" total_stars = sum(participation_counts[key] for key in participation_counts if 'stars' in key)\n",
|
||||
" total_galaxies = sum(participation_counts[key] for key in participation_counts if 'galaxies' in key)\n",
|
||||
"\n",
|
||||
" if total_stars > NUM_STARS:\n",
|
||||
" print(f\"⚠️ ERROR: Total stars ({total_stars:,}) exceeds maximum available ({NUM_STARS:,})\")\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" if total_galaxies > NUM_GALAXIES:\n",
|
||||
" print(f\"⚠️ ERROR: Total galaxies ({total_galaxies:,}) exceeds maximum available ({NUM_GALAXIES:,})\")\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" if total_stars == 0 and total_galaxies == 0:\n",
|
||||
" print(\"⚠️ ERROR: Must have at least some participants\")\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" try:\n",
|
||||
" # Perform calculations\n",
|
||||
" print(\"🔄 Calculating allocations...\\n\")\n",
|
||||
"\n",
|
||||
" allocation_data = calculate_dynamic_allocations(participation_counts)\n",
|
||||
" bonus_data = calculate_bonus_pools(allocation_data)\n",
|
||||
" final_data = calculate_final_allocations(allocation_data, bonus_data)\n",
|
||||
"\n",
|
||||
" # Display results\n",
|
||||
" print_analysis_tables(allocation_data, bonus_data, final_data)\n",
|
||||
"\n",
|
||||
" # Show penalty rates for reference\n",
|
||||
" print(\"\\n📋 PENALTY SYSTEM REFERENCE\")\n",
|
||||
" penalty_df = pd.DataFrame({\n",
|
||||
" 'Lock Period': ['5 Years', '4 Years', '3 Years', '2 Years', '1 Year'],\n",
|
||||
" 'Penalty Rate': [f\"{PENALTY_RATES[year]:.1%}\" for year in [5, 4, 3, 2, 1]],\n",
|
||||
" 'Description': ['No penalty + Bonus', '20% penalty', '40% penalty', '60% penalty', '80% penalty']\n",
|
||||
" })\n",
|
||||
" print(penalty_df.to_string(index=False))\n",
|
||||
"\n",
|
||||
" # Create visualization\n",
|
||||
" print(\"\\n📊 Generating visualization...\")\n",
|
||||
" create_visualization(allocation_data, final_data)\n",
|
||||
"\n",
|
||||
" # Calculate participation insights\n",
|
||||
" star_participation_rate = Decimal(total_stars) / NUM_STARS\n",
|
||||
" galaxy_participation_rate = Decimal(total_galaxies) / NUM_GALAXIES\n",
|
||||
"\n",
|
||||
" print(\"\\n\" + \"=\"*80)\n",
|
||||
" print(\"🎯 EXPERIMENT INSIGHTS\")\n",
|
||||
" print(\"=\"*80)\n",
|
||||
"\n",
|
||||
" insights_df = pd.DataFrame({\n",
|
||||
" 'Metric': [\n",
|
||||
" 'Total Participants',\n",
|
||||
" 'Star Participation Rate',\n",
|
||||
" 'Galaxy Participation Rate',\n",
|
||||
" '5-Year Star Bonus',\n",
|
||||
" '5-Year Galaxy Bonus',\n",
|
||||
" 'Highest Individual Allocation',\n",
|
||||
" 'Lowest Individual Allocation'\n",
|
||||
" ],\n",
|
||||
" 'Value': [\n",
|
||||
" f\"{total_stars + total_galaxies:,}\",\n",
|
||||
" f\"{star_participation_rate:.1%}\",\n",
|
||||
" f\"{galaxy_participation_rate:.1%}\",\n",
|
||||
" f\"{float(bonus_data['bonus_per_star_5_years']):,.2f} $Z\" if participation_counts['stars_5_years'] > 0 else \"N/A (no 5Y stars)\",\n",
|
||||
" f\"{float(bonus_data['bonus_per_galaxy_5_years']):,.2f} $Z\" if participation_counts['galaxies_5_years'] > 0 else \"N/A (no 5Y galaxies)\",\n",
|
||||
" f\"{max(float(final_data['final_star_allocations'][5]), float(final_data['final_galaxy_allocations'][5])):,.2f} $Z\",\n",
|
||||
" f\"{min(float(final_data['final_star_allocations'][1]), float(final_data['final_galaxy_allocations'][1])):,.2f} $Z\"\n",
|
||||
" ]\n",
|
||||
" })\n",
|
||||
" print(insights_df.to_string(index=False))\n",
|
||||
" print(\"\\n\" + \"=\"*80)\n",
|
||||
"\n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"❌ Error during calculation: {str(e)}\")\n",
|
||||
" import traceback\n",
|
||||
" traceback.print_exc()\n",
|
||||
"\n",
|
||||
"# Connect button to calculation function\n",
|
||||
"calculate_button.on_click(calculate_and_display)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "preset-scenarios-header",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🎮 Preset Scenarios\n",
|
||||
"\n",
|
||||
"Try these interesting scenarios by running the code below:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 23,
|
||||
"id": "preset-scenarios",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"📋 AVAILABLE PRESET SCENARIOS:\n",
|
||||
"==================================================\n",
|
||||
"\n",
|
||||
"• balanced: Balanced distribution across all lock periods\n",
|
||||
"\n",
|
||||
"• five_year_focused: Most participants choose 5-year lock (maximum bonus scenario)\n",
|
||||
"\n",
|
||||
"• short_term_focused: Most participants choose shorter locks (high penalty scenario)\n",
|
||||
"\n",
|
||||
"• polarized: Split between 1-year and 5-year locks (maximum bonus per 5Y participant)\n",
|
||||
"\n",
|
||||
"• low_participation: Low overall participation scenario\n",
|
||||
"\n",
|
||||
"🎯 To load a scenario, call: load_scenario('scenario_name')\n",
|
||||
"\n",
|
||||
"Example: load_scenario('five_year_focused')\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Define preset scenarios\n",
|
||||
"SCENARIOS = {\n",
|
||||
" 'balanced': {\n",
|
||||
" 'description': 'Balanced distribution across all lock periods',\n",
|
||||
" 'params': {\n",
|
||||
" 'stars_1_years': 8000, 'stars_2_years': 8000, 'stars_3_years': 8000, 'stars_4_years': 8000, 'stars_5_years': 8000,\n",
|
||||
" 'galaxies_1_years': 40, 'galaxies_2_years': 40, 'galaxies_3_years': 40, 'galaxies_4_years': 40, 'galaxies_5_years': 40\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" 'five_year_focused': {\n",
|
||||
" 'description': 'Most participants choose 5-year lock (maximum bonus scenario)',\n",
|
||||
" 'params': {\n",
|
||||
" 'stars_1_years': 2000, 'stars_2_years': 2000, 'stars_3_years': 3000, 'stars_4_years': 5000, 'stars_5_years': 28000,\n",
|
||||
" 'galaxies_1_years': 10, 'galaxies_2_years': 10, 'galaxies_3_years': 20, 'galaxies_4_years': 30, 'galaxies_5_years': 130\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" 'short_term_focused': {\n",
|
||||
" 'description': 'Most participants choose shorter locks (high penalty scenario)',\n",
|
||||
" 'params': {\n",
|
||||
" 'stars_1_years': 20000, 'stars_2_years': 15000, 'stars_3_years': 4000, 'stars_4_years': 800, 'stars_5_years': 200,\n",
|
||||
" 'galaxies_1_years': 100, 'galaxies_2_years': 80, 'galaxies_3_years': 15, 'galaxies_4_years': 3, 'galaxies_5_years': 2\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" 'polarized': {\n",
|
||||
" 'description': 'Split between 1-year and 5-year locks (maximum bonus per 5Y participant)',\n",
|
||||
" 'params': {\n",
|
||||
" 'stars_1_years': 25000, 'stars_2_years': 0, 'stars_3_years': 0, 'stars_4_years': 0, 'stars_5_years': 15000,\n",
|
||||
" 'galaxies_1_years': 150, 'galaxies_2_years': 0, 'galaxies_3_years': 0, 'galaxies_4_years': 0, 'galaxies_5_years': 50\n",
|
||||
" }\n",
|
||||
" },\n",
|
||||
" 'low_participation': {\n",
|
||||
" 'description': 'Low overall participation scenario',\n",
|
||||
" 'params': {\n",
|
||||
" 'stars_1_years': 1000, 'stars_2_years': 800, 'stars_3_years': 600, 'stars_4_years': 400, 'stars_5_years': 200,\n",
|
||||
" 'galaxies_1_years': 8, 'galaxies_2_years': 6, 'galaxies_3_years': 4, 'galaxies_4_years': 2, 'galaxies_5_years': 5\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"def load_scenario(scenario_name):\n",
|
||||
" \"\"\"Load a preset scenario into the input widgets.\"\"\"\n",
|
||||
" if scenario_name not in SCENARIOS:\n",
|
||||
" print(f\"❌ Unknown scenario: {scenario_name}\")\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" scenario = SCENARIOS[scenario_name]\n",
|
||||
" params = scenario['params']\n",
|
||||
"\n",
|
||||
" # Update widget values\n",
|
||||
" stars_1_years.value = params['stars_1_years']\n",
|
||||
" stars_2_years.value = params['stars_2_years']\n",
|
||||
" stars_3_years.value = params['stars_3_years']\n",
|
||||
" stars_4_years.value = params['stars_4_years']\n",
|
||||
" stars_5_years.value = params['stars_5_years']\n",
|
||||
" galaxies_1_years.value = params['galaxies_1_years']\n",
|
||||
" galaxies_2_years.value = params['galaxies_2_years']\n",
|
||||
" galaxies_3_years.value = params['galaxies_3_years']\n",
|
||||
" galaxies_4_years.value = params['galaxies_4_years']\n",
|
||||
" galaxies_5_years.value = params['galaxies_5_years']\n",
|
||||
"\n",
|
||||
" print(f\"✅ Loaded scenario: {scenario_name}\")\n",
|
||||
" print(f\"📝 Description: {scenario['description']}\")\n",
|
||||
" print(\"\\n🔄 Click 'Calculate Allocations' button above to see results!\")\n",
|
||||
"\n",
|
||||
"# Display available scenarios\n",
|
||||
"print(\"📋 AVAILABLE PRESET SCENARIOS:\")\n",
|
||||
"print(\"=\"*50)\n",
|
||||
"for name, scenario in SCENARIOS.items():\n",
|
||||
" print(f\"\\n• {name}: {scenario['description']}\")\n",
|
||||
"\n",
|
||||
"print(\"\\n🎯 To load a scenario, call: load_scenario('scenario_name')\")\n",
|
||||
"print(\"\\nExample: load_scenario('five_year_focused')\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 24,
|
||||
"id": "load-scenario-example",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"✅ Loaded scenario: five_year_focused\n",
|
||||
"📝 Description: Most participants choose 5-year lock (maximum bonus scenario)\n",
|
||||
"\n",
|
||||
"🔄 Click 'Calculate Allocations' button above to see results!\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Load the balanced scenario as default\n",
|
||||
"load_scenario('five_year_focused')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "export-section",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 💾 Export Results\n",
|
||||
"\n",
|
||||
"Export your experimental results for use with the test suite:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 25,
|
||||
"id": "export-results",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"💾 Export your current scenario results:\n",
|
||||
" export_current_scenario('my_experiment.json')\n",
|
||||
"\n",
|
||||
"📋 Or use default filename:\n",
|
||||
" export_current_scenario()\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def export_current_scenario(filename='lockdrop_allocations_experiment.json'):\n",
|
||||
" \"\"\"Export current scenario results to JSON file.\"\"\"\n",
|
||||
" # Get current values from widgets\n",
|
||||
" participation_counts = {\n",
|
||||
" 'stars_1_years': stars_1_years.value,\n",
|
||||
" 'stars_2_years': stars_2_years.value,\n",
|
||||
" 'stars_3_years': stars_3_years.value,\n",
|
||||
" 'stars_4_years': stars_4_years.value,\n",
|
||||
" 'stars_5_years': stars_5_years.value,\n",
|
||||
" 'galaxies_1_years': galaxies_1_years.value,\n",
|
||||
" 'galaxies_2_years': galaxies_2_years.value,\n",
|
||||
" 'galaxies_3_years': galaxies_3_years.value,\n",
|
||||
" 'galaxies_4_years': galaxies_4_years.value,\n",
|
||||
" 'galaxies_5_years': galaxies_5_years.value\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" try:\n",
|
||||
" # Calculate allocations\n",
|
||||
" allocation_data = calculate_dynamic_allocations(participation_counts)\n",
|
||||
" bonus_data = calculate_bonus_pools(allocation_data)\n",
|
||||
" final_data = calculate_final_allocations(allocation_data, bonus_data)\n",
|
||||
"\n",
|
||||
" # Generate test output\n",
|
||||
" test_output = generate_test_output(final_data)\n",
|
||||
"\n",
|
||||
" # Add metadata\n",
|
||||
" export_data = {\n",
|
||||
" 'metadata': {\n",
|
||||
" 'source': 'lockdrop-experiment.ipynb',\n",
|
||||
" 'scenario': 'custom',\n",
|
||||
" 'participation_counts': participation_counts\n",
|
||||
" },\n",
|
||||
" 'allocations': test_output\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" # Save to file\n",
|
||||
" with open(filename, 'w') as f:\n",
|
||||
" json.dump(export_data, f, indent=2)\n",
|
||||
"\n",
|
||||
" print(f\"✅ Results exported to: {filename}\")\n",
|
||||
" print(f\"📊 Total participants: {sum(participation_counts.values()):,}\")\n",
|
||||
"\n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"❌ Export failed: {str(e)}\")\n",
|
||||
"\n",
|
||||
"# Usage example\n",
|
||||
"print(\"💾 Export your current scenario results:\")\n",
|
||||
"print(\" export_current_scenario('my_experiment.json')\")\n",
|
||||
"print(\"\\n📋 Or use default filename:\")\n",
|
||||
"print(\" export_current_scenario()\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "tips-section",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 💡 Experimentation Tips\n",
|
||||
"\n",
|
||||
"### Interesting Things to Try:\n",
|
||||
"\n",
|
||||
"1. **Bonus Pool Impact**: Set most participants to 1-2 years and a few to 5 years to see massive bonuses\n",
|
||||
"2. **Participation Rate Effects**: Try very low participation vs. high participation scenarios\n",
|
||||
"3. **Star vs Galaxy Balance**: Experiment with different star/galaxy ratios\n",
|
||||
"4. **Edge Cases**: What happens with only 5-year participants? Only 1-year?\n",
|
||||
"5. **Realistic Scenarios**: Model expected real-world behavior patterns\n",
|
||||
"\n",
|
||||
"### Key Metrics to Watch:\n",
|
||||
"\n",
|
||||
"- **5-Year Bonus Multiplier**: How much extra do 5-year participants get?\n",
|
||||
"- **Participation Rates**: What percentage of each point type participates?\n",
|
||||
"- **Penalty Pool Size**: How much bonus is generated from penalties?\n",
|
||||
"- **Allocation Spread**: Difference between highest and lowest allocations\n",
|
||||
"\n",
|
||||
"### Understanding the Math:\n",
|
||||
"\n",
|
||||
"- Penalties from shorter lock periods create bonus pools\n",
|
||||
"- All bonus pools go to 5-year participants\n",
|
||||
"- More penalty = more bonus for 5-year participants\n",
|
||||
"- Lower participation = higher individual allocations"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
500
lockdrop_calculations.py
Normal file
500
lockdrop_calculations.py
Normal file
@ -0,0 +1,500 @@
|
||||
"""
|
||||
Shared lockdrop calculation functions and utilities.
|
||||
|
||||
This module contains the reference calculation logic for lockdrop token distribution,
|
||||
penalty systems, bonus pools, and final allocations. It can be used by both
|
||||
the simulation notebook and the experimental notebook.
|
||||
"""
|
||||
|
||||
from decimal import Decimal, ROUND_DOWN, getcontext
|
||||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
import seaborn as sns
|
||||
import json
|
||||
import os
|
||||
from collections import defaultdict
|
||||
|
||||
# Configure decimal precision
|
||||
getcontext().prec = 28
|
||||
getcontext().rounding = ROUND_DOWN
|
||||
|
||||
# Constants
|
||||
TOKEN_PRECISION = Decimal('0.00000001') # 8 decimal precision
|
||||
TOTAL_SUPPLY = 4294967296 # 1 $Z per Urbit ID
|
||||
LOCKDROP_ALLOCATION_PERCENT = Decimal('0.3')
|
||||
LOCKDROP_ALLOCATION = TOTAL_SUPPLY * LOCKDROP_ALLOCATION_PERCENT
|
||||
|
||||
# Points
|
||||
NUM_GALAXIES = pow(2, 8)
|
||||
NUM_STARS = pow(2, 16) - pow(2, 8)
|
||||
NUM_PLANETS = pow(2, 32) - pow(2, 16)
|
||||
|
||||
# Allocation distribution
|
||||
STAR_ALLOCATION_PERCENT = Decimal(NUM_STARS / pow(2, 16))
|
||||
GALAXY_ALLOCATION_PERCENT = 1 - STAR_ALLOCATION_PERCENT
|
||||
|
||||
# Lockdrop duration (5 years including 1 leap year)
|
||||
LOCKDROP_DURATION_SECONDS = 5 * 365.25 * 24 * 60 * 60
|
||||
BLOCK_DURATION_SECONDS = 2
|
||||
LOCKDROP_DURATION_BLOCKS = int(LOCKDROP_DURATION_SECONDS / BLOCK_DURATION_SECONDS)
|
||||
|
||||
# Penalty rates
|
||||
PENALTY_RATES = {
|
||||
5: Decimal('0'),
|
||||
4: Decimal('0.2'),
|
||||
3: Decimal('0.4'),
|
||||
2: Decimal('0.6'),
|
||||
1: Decimal('0.8')
|
||||
}
|
||||
|
||||
|
||||
def configure_matplotlib():
|
||||
"""Configure matplotlib settings for consistent plots."""
|
||||
plt.style.use('seaborn-v0_8')
|
||||
sns.set_palette("husl")
|
||||
|
||||
plt.rcParams['figure.figsize'] = (12, 8)
|
||||
plt.rcParams['font.size'] = 11
|
||||
plt.rcParams['axes.titlesize'] = 14
|
||||
plt.rcParams['axes.labelsize'] = 12
|
||||
plt.rcParams['xtick.labelsize'] = 10
|
||||
plt.rcParams['ytick.labelsize'] = 10
|
||||
plt.rcParams['legend.fontsize'] = 10
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
|
||||
try:
|
||||
plt.rcParams['font.family'] = 'DejaVu Sans'
|
||||
except:
|
||||
plt.rcParams['font.family'] = 'sans-serif'
|
||||
plt.rcParams['font.sans-serif'] = ['DejaVu Sans', 'Liberation Sans', 'Arial', 'Helvetica']
|
||||
|
||||
|
||||
def print_constants_summary():
|
||||
"""Display core constants as formatted tables."""
|
||||
print("=" * 80)
|
||||
print("📊 $Z LOCKDROP DISTRIBUTION - CORE CONSTANTS")
|
||||
print("=" * 80)
|
||||
|
||||
# Lockdrop Allocation Table
|
||||
lockdrop_df = pd.DataFrame({
|
||||
'Parameter': ['Total Supply (1 $Z per Urbit ID)', 'Lockdrop Allocation %', 'Lockdrop Allocation ($Z)'],
|
||||
'Value': [f"{TOTAL_SUPPLY:,}", f"{LOCKDROP_ALLOCATION_PERCENT:.1%}", f"{LOCKDROP_ALLOCATION:,.1f}"]
|
||||
})
|
||||
print("\n🔒 LOCKDROP ALLOCATION")
|
||||
print(lockdrop_df.to_string(index=False))
|
||||
|
||||
# Points Distribution Table
|
||||
points_df = pd.DataFrame({
|
||||
'Point Type': ['Galaxies', 'Stars', 'Planets'],
|
||||
'Count': [f"{NUM_GALAXIES:,}", f"{NUM_STARS:,}", f"{NUM_PLANETS:,}"],
|
||||
'Allocation %': ['0.39%', '99.61%', '0%']
|
||||
})
|
||||
print("\n⭐ URBIT POINTS DISTRIBUTION")
|
||||
print(points_df.to_string(index=False))
|
||||
|
||||
# Lockdrop Parameters Table
|
||||
params_df = pd.DataFrame({
|
||||
'Parameter': ['Block Duration', 'Max Point Lock Duration (5 yrs)', 'Total Blocks', 'Star Allocation %', 'Galaxy Allocation %'],
|
||||
'Value': [f"{BLOCK_DURATION_SECONDS} seconds", f"{LOCKDROP_DURATION_SECONDS:,.0f} seconds",
|
||||
f"{LOCKDROP_DURATION_BLOCKS:,}", f"{STAR_ALLOCATION_PERCENT:.6f}", f"{GALAXY_ALLOCATION_PERCENT:.6f}"]
|
||||
})
|
||||
print("\n⏱️ LOCKDROP PARAMETERS")
|
||||
print(params_df.to_string(index=False))
|
||||
print("\n" + "="*80)
|
||||
|
||||
|
||||
def calculate_base_allocations():
|
||||
"""Calculate base allocation amounts and quanta."""
|
||||
lockdrop_allocation_stars = LOCKDROP_ALLOCATION * STAR_ALLOCATION_PERCENT
|
||||
lockdrop_allocation_galaxies = LOCKDROP_ALLOCATION * GALAXY_ALLOCATION_PERCENT
|
||||
|
||||
assert (lockdrop_allocation_stars + lockdrop_allocation_galaxies) == LOCKDROP_ALLOCATION, "point allocation doesn't add up"
|
||||
|
||||
# Max allocation per point (theoretical)
|
||||
max_allocation_per_star = lockdrop_allocation_stars / NUM_STARS
|
||||
max_allocation_per_galaxy = lockdrop_allocation_galaxies / NUM_GALAXIES
|
||||
|
||||
# Quanta calculation
|
||||
z_available_per_star_per_block = max_allocation_per_star / LOCKDROP_DURATION_BLOCKS
|
||||
z_available_per_galaxy_per_block = max_allocation_per_galaxy / LOCKDROP_DURATION_BLOCKS
|
||||
|
||||
# Round down to 6 decimals
|
||||
adjusted_z_per_star_per_block = z_available_per_star_per_block.quantize(Decimal('0.000001'), rounding=ROUND_DOWN)
|
||||
adjusted_z_per_galaxy_per_block = z_available_per_galaxy_per_block.quantize(Decimal('0.000001'), rounding=ROUND_DOWN)
|
||||
|
||||
# Adjusted max allocation
|
||||
adjusted_max_allocation_per_star = adjusted_z_per_star_per_block * LOCKDROP_DURATION_BLOCKS
|
||||
adjusted_max_allocation_per_galaxy = adjusted_z_per_galaxy_per_block * LOCKDROP_DURATION_BLOCKS
|
||||
|
||||
return {
|
||||
'lockdrop_allocation_stars': lockdrop_allocation_stars,
|
||||
'lockdrop_allocation_galaxies': lockdrop_allocation_galaxies,
|
||||
'max_allocation_per_star': max_allocation_per_star,
|
||||
'max_allocation_per_galaxy': max_allocation_per_galaxy,
|
||||
'adjusted_max_allocation_per_star': adjusted_max_allocation_per_star,
|
||||
'adjusted_max_allocation_per_galaxy': adjusted_max_allocation_per_galaxy,
|
||||
'adjusted_z_per_star_per_block': adjusted_z_per_star_per_block,
|
||||
'adjusted_z_per_galaxy_per_block': adjusted_z_per_galaxy_per_block
|
||||
}
|
||||
|
||||
|
||||
def calculate_dynamic_allocations(participation_counts):
|
||||
"""
|
||||
Calculate allocations based on actual participation counts.
|
||||
|
||||
Args:
|
||||
participation_counts: Dict with keys like 'stars_1_years', 'stars_2_years', etc.
|
||||
"""
|
||||
# Extract participation data
|
||||
stars_counts = {year: Decimal(participation_counts[f'stars_{year}_years']) for year in range(1, 6)}
|
||||
galaxies_counts = {year: Decimal(participation_counts[f'galaxies_{year}_years']) for year in range(1, 6)}
|
||||
|
||||
total_stars_locked = sum(stars_counts.values())
|
||||
total_galaxies_locked = sum(galaxies_counts.values())
|
||||
|
||||
lockdrop_allocation_stars = LOCKDROP_ALLOCATION * STAR_ALLOCATION_PERCENT
|
||||
lockdrop_allocation_galaxies = LOCKDROP_ALLOCATION * GALAXY_ALLOCATION_PERCENT
|
||||
|
||||
# Dynamic allocations based on actual participation
|
||||
max_allocation_per_star = lockdrop_allocation_stars / total_stars_locked if total_stars_locked > 0 else Decimal('0')
|
||||
max_allocation_per_galaxy = lockdrop_allocation_galaxies / total_galaxies_locked if total_galaxies_locked > 0 else Decimal('0')
|
||||
|
||||
# Quanta calculation
|
||||
z_available_per_star_per_block = max_allocation_per_star / LOCKDROP_DURATION_BLOCKS
|
||||
z_available_per_galaxy_per_block = max_allocation_per_galaxy / LOCKDROP_DURATION_BLOCKS
|
||||
|
||||
# Round down to 6 decimals
|
||||
adjusted_z_per_star_per_block = z_available_per_star_per_block.quantize(Decimal('0.000001'), rounding=ROUND_DOWN)
|
||||
adjusted_z_per_galaxy_per_block = z_available_per_galaxy_per_block.quantize(Decimal('0.000001'), rounding=ROUND_DOWN)
|
||||
|
||||
# Adjusted max allocation
|
||||
adjusted_max_allocation_per_star = adjusted_z_per_star_per_block * LOCKDROP_DURATION_BLOCKS
|
||||
adjusted_max_allocation_per_galaxy = adjusted_z_per_galaxy_per_block * LOCKDROP_DURATION_BLOCKS
|
||||
|
||||
# Rounding errors
|
||||
rounding_error_per_star = max_allocation_per_star - adjusted_max_allocation_per_star
|
||||
rounding_error_per_galaxy = max_allocation_per_galaxy - adjusted_max_allocation_per_galaxy
|
||||
|
||||
total_rounding_error_stars = lockdrop_allocation_stars - (adjusted_max_allocation_per_star * total_stars_locked)
|
||||
total_rounding_error_galaxies = lockdrop_allocation_galaxies - (adjusted_max_allocation_per_galaxy * total_galaxies_locked)
|
||||
|
||||
return {
|
||||
'stars_counts': stars_counts,
|
||||
'galaxies_counts': galaxies_counts,
|
||||
'total_stars_locked': total_stars_locked,
|
||||
'total_galaxies_locked': total_galaxies_locked,
|
||||
'adjusted_max_allocation_per_star': adjusted_max_allocation_per_star,
|
||||
'adjusted_max_allocation_per_galaxy': adjusted_max_allocation_per_galaxy,
|
||||
'total_rounding_error_stars': total_rounding_error_stars,
|
||||
'total_rounding_error_galaxies': total_rounding_error_galaxies,
|
||||
'adjusted_z_per_star_per_block': adjusted_z_per_star_per_block,
|
||||
'adjusted_z_per_galaxy_per_block': adjusted_z_per_galaxy_per_block
|
||||
}
|
||||
|
||||
|
||||
def calculate_bonus_pools(allocation_data):
|
||||
"""Calculate bonus pools from penalties and rounding errors."""
|
||||
stars_counts = allocation_data['stars_counts']
|
||||
galaxies_counts = allocation_data['galaxies_counts']
|
||||
adjusted_max_allocation_per_star = allocation_data['adjusted_max_allocation_per_star']
|
||||
adjusted_max_allocation_per_galaxy = allocation_data['adjusted_max_allocation_per_galaxy']
|
||||
total_rounding_error_stars = allocation_data['total_rounding_error_stars']
|
||||
total_rounding_error_galaxies = allocation_data['total_rounding_error_galaxies']
|
||||
|
||||
# Calculate penalty pools
|
||||
star_penalty_pool = Decimal('0')
|
||||
for years in [1, 2, 3, 4]:
|
||||
penalty = PENALTY_RATES[years]
|
||||
star_penalty_pool += adjusted_max_allocation_per_star * stars_counts[years] * penalty
|
||||
|
||||
galaxy_penalty_pool = Decimal('0')
|
||||
for years in [1, 2, 3, 4]:
|
||||
penalty = PENALTY_RATES[years]
|
||||
galaxy_penalty_pool += adjusted_max_allocation_per_galaxy * galaxies_counts[years] * penalty
|
||||
|
||||
star_penalty_pool = star_penalty_pool.quantize(TOKEN_PRECISION, rounding=ROUND_DOWN)
|
||||
galaxy_penalty_pool = galaxy_penalty_pool.quantize(TOKEN_PRECISION, rounding=ROUND_DOWN)
|
||||
|
||||
# Add rounding errors to bonus pools
|
||||
star_bonus_pool_total = star_penalty_pool + total_rounding_error_stars
|
||||
galaxy_bonus_pool_total = galaxy_penalty_pool + total_rounding_error_galaxies
|
||||
|
||||
# Calculate bonus per 5-year participant
|
||||
stars_5_years = stars_counts[5]
|
||||
galaxies_5_years = galaxies_counts[5]
|
||||
|
||||
bonus_per_star_5_years = star_bonus_pool_total / stars_5_years if stars_5_years > 0 else Decimal('0')
|
||||
bonus_per_galaxy_5_years = galaxy_bonus_pool_total / galaxies_5_years if galaxies_5_years > 0 else Decimal('0')
|
||||
|
||||
return {
|
||||
'star_penalty_pool': star_penalty_pool,
|
||||
'galaxy_penalty_pool': galaxy_penalty_pool,
|
||||
'star_bonus_pool_total': star_bonus_pool_total,
|
||||
'galaxy_bonus_pool_total': galaxy_bonus_pool_total,
|
||||
'bonus_per_star_5_years': bonus_per_star_5_years,
|
||||
'bonus_per_galaxy_5_years': bonus_per_galaxy_5_years
|
||||
}
|
||||
|
||||
|
||||
def calculate_final_allocations(allocation_data, bonus_data):
|
||||
"""Calculate final allocations for all lock periods."""
|
||||
adjusted_max_allocation_per_star = allocation_data['adjusted_max_allocation_per_star']
|
||||
adjusted_max_allocation_per_galaxy = allocation_data['adjusted_max_allocation_per_galaxy']
|
||||
bonus_per_star_5_years = bonus_data['bonus_per_star_5_years']
|
||||
bonus_per_galaxy_5_years = bonus_data['bonus_per_galaxy_5_years']
|
||||
stars_counts = allocation_data['stars_counts']
|
||||
galaxies_counts = allocation_data['galaxies_counts']
|
||||
|
||||
# Calculate final allocations
|
||||
final_star_allocations = {}
|
||||
final_galaxy_allocations = {}
|
||||
|
||||
for years in range(1, 6):
|
||||
penalty = PENALTY_RATES[years]
|
||||
|
||||
if years == 5:
|
||||
# 5-year participants get base + bonus
|
||||
final_star_allocations[years] = (adjusted_max_allocation_per_star + bonus_per_star_5_years).quantize(TOKEN_PRECISION, rounding=ROUND_DOWN)
|
||||
final_galaxy_allocations[years] = (adjusted_max_allocation_per_galaxy + bonus_per_galaxy_5_years).quantize(TOKEN_PRECISION, rounding=ROUND_DOWN)
|
||||
else:
|
||||
# Other years get penalized amounts
|
||||
final_star_allocations[years] = (adjusted_max_allocation_per_star * (1 - penalty)).quantize(TOKEN_PRECISION, rounding=ROUND_DOWN)
|
||||
final_galaxy_allocations[years] = (adjusted_max_allocation_per_galaxy * (1 - penalty)).quantize(TOKEN_PRECISION, rounding=ROUND_DOWN)
|
||||
|
||||
# Calculate Z per block for each lock period
|
||||
star_z_per_block = {}
|
||||
galaxy_z_per_block = {}
|
||||
|
||||
for years in range(1, 6):
|
||||
lock_duration_fraction = Decimal(years) / Decimal('5') # Fraction of full 5-year period
|
||||
effective_blocks = LOCKDROP_DURATION_BLOCKS * lock_duration_fraction
|
||||
|
||||
star_z_per_block[years] = (final_star_allocations[years] / effective_blocks).quantize(TOKEN_PRECISION, rounding=ROUND_DOWN)
|
||||
galaxy_z_per_block[years] = (final_galaxy_allocations[years] / effective_blocks).quantize(TOKEN_PRECISION, rounding=ROUND_DOWN)
|
||||
|
||||
# Calculate total allocations for verification
|
||||
total_stars_allocation = sum(final_star_allocations[year] * stars_counts[year] for year in range(1, 6))
|
||||
total_galaxies_allocation = sum(final_galaxy_allocations[year] * galaxies_counts[year] for year in range(1, 6))
|
||||
|
||||
return {
|
||||
'final_star_allocations': final_star_allocations,
|
||||
'final_galaxy_allocations': final_galaxy_allocations,
|
||||
'star_z_per_block': star_z_per_block,
|
||||
'galaxy_z_per_block': galaxy_z_per_block,
|
||||
'total_stars_allocation': total_stars_allocation,
|
||||
'total_galaxies_allocation': total_galaxies_allocation
|
||||
}
|
||||
|
||||
|
||||
def generate_test_output(final_data):
|
||||
"""Generate JSON output for test validation."""
|
||||
final_star_allocations = final_data['final_star_allocations']
|
||||
final_galaxy_allocations = final_data['final_galaxy_allocations']
|
||||
total_allocation = final_data['total_stars_allocation'] + final_data['total_galaxies_allocation']
|
||||
|
||||
# Convert to $sZ units (multiply by 10^8)
|
||||
output = {
|
||||
"stars": {
|
||||
f"{year}_years": int(final_star_allocations[year] * Decimal('1e8'))
|
||||
for year in range(1, 6)
|
||||
},
|
||||
"galaxies": {
|
||||
f"{year}_years": int(final_galaxy_allocations[year] * Decimal('1e8'))
|
||||
for year in range(1, 6)
|
||||
},
|
||||
"total": int(total_allocation * Decimal('1e8'))
|
||||
}
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def print_analysis_tables(allocation_data, bonus_data, final_data):
|
||||
"""Print comprehensive analysis tables."""
|
||||
stars_counts = allocation_data['stars_counts']
|
||||
galaxies_counts = allocation_data['galaxies_counts']
|
||||
total_stars_locked = allocation_data['total_stars_locked']
|
||||
total_galaxies_locked = allocation_data['total_galaxies_locked']
|
||||
final_star_allocations = final_data['final_star_allocations']
|
||||
final_galaxy_allocations = final_data['final_galaxy_allocations']
|
||||
star_z_per_block = final_data['star_z_per_block']
|
||||
galaxy_z_per_block = final_data['galaxy_z_per_block']
|
||||
|
||||
print("=" * 80)
|
||||
print("📈 DYNAMIC LOCKDROP ANALYSIS")
|
||||
print("=" * 80)
|
||||
|
||||
# Participation rates
|
||||
star_participation_rate = total_stars_locked / NUM_STARS
|
||||
galaxy_participation_rate = total_galaxies_locked / NUM_GALAXIES
|
||||
|
||||
print(f"\n📊 PARTICIPATION RATES")
|
||||
print(f" Stars: {star_participation_rate:.1%} ({total_stars_locked:,}/{NUM_STARS:,})")
|
||||
print(f" Galaxies: {galaxy_participation_rate:.1%} ({total_galaxies_locked:,}/{NUM_GALAXIES:,})")
|
||||
|
||||
# Final allocations table
|
||||
print("\n⭐ FINAL STAR ALLOCATIONS")
|
||||
star_final_df = pd.DataFrame({
|
||||
'Lock Period': ['5 Years', '4 Years', '3 Years', '2 Years', '1 Year'],
|
||||
'Penalty': [f"{PENALTY_RATES[year]:.1%}" for year in [5, 4, 3, 2, 1]],
|
||||
'Final Allocation ($Z)': [f"{final_star_allocations[year]:.12f}" for year in [5, 4, 3, 2, 1]],
|
||||
'Z per Block': [f"{star_z_per_block[year]:.12f}" for year in [5, 4, 3, 2, 1]],
|
||||
'Participants': [f"{stars_counts[year]:,}" for year in [5, 4, 3, 2, 1]]
|
||||
})
|
||||
print(star_final_df.to_string(index=False))
|
||||
|
||||
print("\n🌌 FINAL GALAXY ALLOCATIONS")
|
||||
galaxy_final_df = pd.DataFrame({
|
||||
'Lock Period': ['5 Years', '4 Years', '3 Years', '2 Years', '1 Year'],
|
||||
'Penalty': [f"{PENALTY_RATES[year]:.1%}" for year in [5, 4, 3, 2, 1]],
|
||||
'Final Allocation ($Z)': [f"{final_galaxy_allocations[year]:.12f}" for year in [5, 4, 3, 2, 1]],
|
||||
'Z per Block': [f"{galaxy_z_per_block[year]:.12f}" for year in [5, 4, 3, 2, 1]],
|
||||
'Participants': [f"{galaxies_counts[year]:,}" for year in [5, 4, 3, 2, 1]]
|
||||
})
|
||||
print(galaxy_final_df.to_string(index=False))
|
||||
|
||||
# Bonus pool summary
|
||||
print("\n🎁 BONUS POOL SUMMARY")
|
||||
bonus_summary_df = pd.DataFrame({
|
||||
'Component': ['Star Bonus Pool', 'Galaxy Bonus Pool', 'Total Bonus Distributed'],
|
||||
'Value': [
|
||||
f"{bonus_data['star_bonus_pool_total']:,.6f} $Z",
|
||||
f"{bonus_data['galaxy_bonus_pool_total']:,.6f} $Z",
|
||||
f"{bonus_data['star_bonus_pool_total'] + bonus_data['galaxy_bonus_pool_total']:,.6f} $Z"
|
||||
]
|
||||
})
|
||||
print(bonus_summary_df.to_string(index=False))
|
||||
|
||||
print("\n" + "="*80)
|
||||
|
||||
|
||||
def create_visualization(allocation_data, final_data):
|
||||
"""Create comprehensive visualization plots."""
|
||||
stars_counts = allocation_data['stars_counts']
|
||||
galaxies_counts = allocation_data['galaxies_counts']
|
||||
final_star_allocations = final_data['final_star_allocations']
|
||||
final_galaxy_allocations = final_data['final_galaxy_allocations']
|
||||
|
||||
fig = plt.figure(figsize=(20, 10))
|
||||
gs = fig.add_gridspec(2, 3, hspace=0.35, wspace=0.35)
|
||||
|
||||
fig.suptitle('Lockdrop Analysis', fontsize=18, fontweight='bold', y=0.98)
|
||||
|
||||
# 1. Star Participation Distribution
|
||||
ax1 = fig.add_subplot(gs[0, 0])
|
||||
star_years = [1, 2, 3, 4, 5]
|
||||
star_counts = [stars_counts[year] for year in star_years]
|
||||
|
||||
bars1 = ax1.bar(range(len(star_years)), star_counts,
|
||||
color=['#ff7f0e', '#ffbb78', '#2ca02c', '#98df8a', '#d62728'], alpha=0.8)
|
||||
ax1.set_xlabel('Lock Period (Years)')
|
||||
ax1.set_ylabel('Number of Stars')
|
||||
ax1.set_title('Star Participation by Lock Period', fontweight='bold')
|
||||
ax1.set_xticks(range(len(star_years)))
|
||||
ax1.set_xticklabels(star_years)
|
||||
|
||||
for i, count in enumerate(star_counts):
|
||||
height = bars1[i].get_height()
|
||||
ax1.text(bars1[i].get_x() + bars1[i].get_width()/2., height + height*0.01,
|
||||
f'{count:,}', ha='center', va='bottom', fontweight='bold', fontsize=9)
|
||||
|
||||
# 2. Galaxy Participation Distribution
|
||||
ax2 = fig.add_subplot(gs[0, 1])
|
||||
galaxy_counts = [galaxies_counts[year] for year in star_years]
|
||||
|
||||
bars2 = ax2.bar(range(len(star_years)), galaxy_counts,
|
||||
color=['#9467bd', '#c5b0d5', '#8c564b', '#c49c94', '#e377c2'], alpha=0.8)
|
||||
ax2.set_xlabel('Lock Period (Years)')
|
||||
ax2.set_ylabel('Number of Galaxies')
|
||||
ax2.set_title('Galaxy Participation by Lock Period', fontweight='bold')
|
||||
ax2.set_xticks(range(len(star_years)))
|
||||
ax2.set_xticklabels(star_years)
|
||||
|
||||
for i, count in enumerate(galaxy_counts):
|
||||
height = bars2[i].get_height()
|
||||
ax2.text(bars2[i].get_x() + bars2[i].get_width()/2., height + height*0.01,
|
||||
f'{count:,}', ha='center', va='bottom', fontweight='bold', fontsize=9)
|
||||
|
||||
# 3. Star Allocations
|
||||
ax3 = fig.add_subplot(gs[0, 2])
|
||||
star_alloc_values = [float(final_star_allocations[year]) for year in star_years]
|
||||
|
||||
bars3 = ax3.bar(range(len(star_years)), star_alloc_values,
|
||||
color=['#ff7f0e', '#ffbb78', '#2ca02c', '#98df8a', '#d62728'], alpha=0.8)
|
||||
ax3.set_xlabel('Lock Period (Years)')
|
||||
ax3.set_ylabel('Allocation per Star ($Z)')
|
||||
ax3.set_title('Star Allocations by Lock Period', fontweight='bold')
|
||||
ax3.set_xticks(range(len(star_years)))
|
||||
ax3.set_xticklabels(star_years)
|
||||
|
||||
for i, allocation in enumerate(star_alloc_values):
|
||||
height = bars3[i].get_height()
|
||||
ax3.text(bars3[i].get_x() + bars3[i].get_width()/2., height + height*0.01,
|
||||
f'{allocation:,.0f}', ha='center', va='bottom', fontweight='bold', fontsize=9)
|
||||
|
||||
# 4. Galaxy Allocations
|
||||
ax4 = fig.add_subplot(gs[1, 0])
|
||||
galaxy_alloc_values = [float(final_galaxy_allocations[year]) for year in star_years]
|
||||
|
||||
bars4 = ax4.bar(range(len(star_years)), galaxy_alloc_values,
|
||||
color=['#9467bd', '#c5b0d5', '#8c564b', '#c49c94', '#e377c2'], alpha=0.8)
|
||||
ax4.set_xlabel('Lock Period (Years)')
|
||||
ax4.set_ylabel('Allocation per Galaxy ($Z)')
|
||||
ax4.set_title('Galaxy Allocations by Lock Period', fontweight='bold')
|
||||
ax4.set_xticks(range(len(star_years)))
|
||||
ax4.set_xticklabels(star_years)
|
||||
|
||||
for i, allocation in enumerate(galaxy_alloc_values):
|
||||
height = bars4[i].get_height()
|
||||
ax4.text(bars4[i].get_x() + bars4[i].get_width()/2., height + height*0.01,
|
||||
f'{allocation:,.0f}', ha='center', va='bottom', fontweight='bold', fontsize=9)
|
||||
|
||||
# Hide remaining subplots
|
||||
ax5 = fig.add_subplot(gs[1, 1])
|
||||
ax5.axis('off')
|
||||
ax6 = fig.add_subplot(gs[1, 2])
|
||||
ax6.axis('off')
|
||||
|
||||
plt.subplots_adjust(bottom=0.08, top=0.88, left=0.05, right=0.98)
|
||||
plt.show()
|
||||
|
||||
|
||||
def load_watcher_events_data(generated_dir='generated'):
|
||||
"""Load participation data from watcher events file."""
|
||||
import urbitob
|
||||
|
||||
watcher_events_path = os.path.join(generated_dir, 'watcher-events.json')
|
||||
|
||||
try:
|
||||
with open(watcher_events_path, 'r') as f:
|
||||
data = json.load(f)
|
||||
events = data['data']['eventsInRange']
|
||||
|
||||
# Analyze events
|
||||
lock_duration_counts = {
|
||||
'star': defaultdict(int),
|
||||
'galaxy': defaultdict(int)
|
||||
}
|
||||
|
||||
for event_data in events:
|
||||
if event_data['event']['__typename'] == 'PointLockedEvent':
|
||||
point = event_data['event']['point']
|
||||
lock_period = event_data['event']['lock_period']
|
||||
|
||||
point_num = urbitob.patp_to_num(point)
|
||||
point_type = "galaxy" if point_num < NUM_GALAXIES else "star"
|
||||
lock_duration_counts[point_type][lock_period] += 1
|
||||
|
||||
# Extract counts
|
||||
result = {}
|
||||
for years in [1, 2, 3, 4, 5]:
|
||||
result[f'stars_{years}_years'] = lock_duration_counts['star'][years]
|
||||
result[f'galaxies_{years}_years'] = lock_duration_counts['galaxy'][years]
|
||||
|
||||
return result, True
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f"⚠️ Watcher events file not found at {watcher_events_path}")
|
||||
return None, False
|
||||
except Exception as e:
|
||||
print(f"⚠️ Error loading watcher events: {e}")
|
||||
return None, False
|
||||
@ -6,3 +6,4 @@ numpy
|
||||
urbitob
|
||||
tabulate
|
||||
requests
|
||||
ipywidgets
|
||||
|
||||
Loading…
Reference in New Issue
Block a user