Merge branch 'development_builders' of https://github.com/freeflowuniverse/herolib into development_builders
This commit is contained in:
48
examples/threefold/incatokens/main.v
Normal file
48
examples/threefold/incatokens/main.v
Normal file
@@ -0,0 +1,48 @@
|
||||
import freeflowuniverse.herolib.threefold.incatokens
|
||||
|
||||
fn main() {
|
||||
// Create simulation
|
||||
mut sim := incatokens.simulation_new(
|
||||
name: 'inca_tge_v1'
|
||||
total_supply: 10_000_000_000
|
||||
)!
|
||||
|
||||
// Initialize with default rounds
|
||||
sim.init_default_rounds()!
|
||||
|
||||
// Define scenarios
|
||||
scenarios := {
|
||||
'Low': {
|
||||
'demands': [10_000_000.0, 10_000_000.0, 0.0],
|
||||
'amm_trades': [0.0, 0.0, 0.0]
|
||||
},
|
||||
'Mid': {
|
||||
'demands': [20_000_000.0, 20_000_000.0, 0.0],
|
||||
'amm_trades': [0.0, 0.0, 0.0]
|
||||
},
|
||||
'High': {
|
||||
'demands': [50_000_000.0, 100_000_000.0, 0.0],
|
||||
'amm_trades': [0.0, 0.0, 0.0]
|
||||
}
|
||||
}
|
||||
|
||||
// Run all scenarios
|
||||
for name, params in scenarios {
|
||||
sim.run_scenario(name, params['demands'], params['amm_trades'])!
|
||||
}
|
||||
|
||||
// Create vesting schedules
|
||||
sim.create_vesting_schedules()!
|
||||
|
||||
// Generate charts
|
||||
price_chart := sim.generate_price_chart()!
|
||||
market_cap_chart := sim.generate_market_cap_chart()!
|
||||
vesting_chart := sim.generate_vesting_chart()!
|
||||
|
||||
// Export results
|
||||
sim.export_report('simulation_report.md')!
|
||||
sim.export_csv('prices', 'price_data.csv')!
|
||||
sim.export_csv('vesting', 'vesting_schedule.csv')!
|
||||
|
||||
println('Simulation complete!')
|
||||
}
|
||||
38
lib/threefold/incatokens/amm.v
Normal file
38
lib/threefold/incatokens/amm.v
Normal file
@@ -0,0 +1,38 @@
|
||||
module incatokens
|
||||
|
||||
import math
|
||||
|
||||
pub struct AMMPool {
|
||||
pub mut:
|
||||
tokens f64
|
||||
usdc f64
|
||||
k f64 // constant product
|
||||
}
|
||||
|
||||
pub fn (mut pool AMMPool) add_liquidity(tokens f64, usdc f64) {
|
||||
pool.tokens += tokens
|
||||
pool.usdc += usdc
|
||||
pool.k = pool.tokens * pool.usdc
|
||||
}
|
||||
|
||||
pub fn (mut pool AMMPool) trade(usdc_amount f64) ! {
|
||||
if pool.tokens <= 0 || pool.usdc <= 0 {
|
||||
return error('AMM pool is empty')
|
||||
}
|
||||
|
||||
pool.usdc += usdc_amount
|
||||
|
||||
if pool.usdc <= 0 {
|
||||
return error('Insufficient USDC in pool')
|
||||
}
|
||||
|
||||
// Maintain constant product
|
||||
pool.tokens = pool.k / pool.usdc
|
||||
}
|
||||
|
||||
pub fn (pool AMMPool) get_price() f64 {
|
||||
if pool.tokens <= 0 {
|
||||
return 0
|
||||
}
|
||||
return pool.usdc / pool.tokens
|
||||
}
|
||||
54
lib/threefold/incatokens/auction.v
Normal file
54
lib/threefold/incatokens/auction.v
Normal file
@@ -0,0 +1,54 @@
|
||||
module incatokens
|
||||
|
||||
import math
|
||||
pub struct AuctionResult {
|
||||
pub mut:
|
||||
tokens_sold f64
|
||||
clearing_price f64
|
||||
usd_raised f64
|
||||
fully_subscribed bool
|
||||
}
|
||||
|
||||
pub fn simulate_auction(config AuctionConfig) !AuctionResult {
|
||||
demand := config.demand
|
||||
min_price := config.min_price
|
||||
token_supply := config.token_supply
|
||||
|
||||
if token_supply <= 0 {
|
||||
return AuctionResult{
|
||||
tokens_sold: 0
|
||||
clearing_price: 0
|
||||
usd_raised: 0
|
||||
fully_subscribed: true
|
||||
}
|
||||
}
|
||||
|
||||
implied_avg := demand / token_supply
|
||||
|
||||
if implied_avg < min_price {
|
||||
// Partial fill at floor price
|
||||
tokens_sold := math.min(demand / min_price, token_supply)
|
||||
return AuctionResult{
|
||||
tokens_sold: tokens_sold
|
||||
clearing_price: min_price
|
||||
usd_raised: tokens_sold * min_price
|
||||
fully_subscribed: tokens_sold >= token_supply
|
||||
}
|
||||
}
|
||||
|
||||
// Full subscription at market clearing price
|
||||
return AuctionResult{
|
||||
tokens_sold: token_supply
|
||||
clearing_price: implied_avg
|
||||
usd_raised: demand
|
||||
fully_subscribed: true
|
||||
}
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct AuctionConfig {
|
||||
pub mut:
|
||||
demand f64
|
||||
min_price f64
|
||||
token_supply f64
|
||||
}
|
||||
67
lib/threefold/incatokens/charts.v
Normal file
67
lib/threefold/incatokens/charts.v
Normal file
@@ -0,0 +1,67 @@
|
||||
module incatokens
|
||||
|
||||
import freeflowuniverse.herolib.biz.spreadsheet
|
||||
import freeflowuniverse.herolib.web.echarts
|
||||
|
||||
// Generate price evolution chart
|
||||
pub fn (sim Simulation) generate_price_chart() !echarts.EChartsOption {
|
||||
mut rownames := []string{}
|
||||
for name, _ in sim.scenarios {
|
||||
rownames << 'scenario_${name}_price'
|
||||
}
|
||||
|
||||
return sim.price_sheet.line_chart(
|
||||
rowname: rownames.join(',')
|
||||
period_type: .month
|
||||
title: 'INCA Token Price Evolution'
|
||||
title_sub: 'Price paths across different scenarios'
|
||||
unit: .normal
|
||||
)!
|
||||
}
|
||||
|
||||
// Generate market cap chart
|
||||
pub fn (sim Simulation) generate_market_cap_chart() !echarts.EChartsOption {
|
||||
// Create market cap rows from price rows
|
||||
mut mc_sheet := spreadsheet.sheet_new(
|
||||
name: '${sim.name}_market_cap'
|
||||
nrcol: sim.price_sheet.nrcol
|
||||
curr: sim.currency
|
||||
)!
|
||||
|
||||
for name, scenario in sim.scenarios {
|
||||
mut mc_row := mc_sheet.row_new(
|
||||
name: 'scenario_${name}_mc'
|
||||
tags: 'scenario:${name} type:market_cap'
|
||||
descr: 'Market cap for ${name} scenario'
|
||||
)!
|
||||
|
||||
price_row := sim.price_sheet.row_get('scenario_${name}_price')!
|
||||
for i, cell in price_row.cells {
|
||||
mc_row.cells[i].val = cell.val * sim.total_supply
|
||||
}
|
||||
}
|
||||
|
||||
return mc_sheet.bar_chart(
|
||||
namefilter: mc_sheet.rows.keys()
|
||||
period_type: .quarter
|
||||
title: 'INCA Market Capitalization'
|
||||
title_sub: 'Market cap evolution by quarter'
|
||||
unit: .million
|
||||
)!
|
||||
}
|
||||
|
||||
// Generate vesting schedule chart
|
||||
pub fn (mut sim Simulation) generate_vesting_chart() !echarts.EChartsOption {
|
||||
if isnil(sim.vesting_sheet) {
|
||||
sim.create_vesting_schedules()!
|
||||
}
|
||||
|
||||
return sim.vesting_sheet.line_chart(
|
||||
includefilter: ['type:vesting']
|
||||
excludefilter: ['type:total_vesting']
|
||||
period_type: .quarter
|
||||
title: 'Token Vesting Schedule'
|
||||
title_sub: 'Cumulative tokens unlocked over time'
|
||||
unit: .million
|
||||
)!
|
||||
}
|
||||
95
lib/threefold/incatokens/export.v
Normal file
95
lib/threefold/incatokens/export.v
Normal file
@@ -0,0 +1,95 @@
|
||||
module incatokens
|
||||
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.data.markdown
|
||||
import os
|
||||
|
||||
// // Export simulation results to markdown report
|
||||
// pub fn (sim &Simulation) export_report(path string) ! {
|
||||
// content := sim.render_report()!
|
||||
|
||||
// // Write to file
|
||||
// mut p := pathlib.get_file(
|
||||
// path: path
|
||||
// create: true
|
||||
// delete: true
|
||||
// )!
|
||||
// p.write(content)!
|
||||
// }
|
||||
|
||||
// Export data to CSV
|
||||
pub fn (sim Simulation) export_csv(sheet_name string, path string) ! {
|
||||
mut sheet := match sheet_name {
|
||||
'prices' { sim.price_sheet }
|
||||
'tokens' { sim.token_sheet }
|
||||
'investments' { sim.investment_sheet }
|
||||
'vesting' { sim.vesting_sheet }
|
||||
else { return error('Unknown sheet: ${sheet_name}') }
|
||||
}
|
||||
|
||||
sheet.export_csv(
|
||||
path: path
|
||||
separator: ','
|
||||
include_empty: false
|
||||
)!
|
||||
}
|
||||
|
||||
pub fn (sim Simulation) generate_distribution_section() !string {
|
||||
mut lines := []string{}
|
||||
|
||||
lines << '- **Total supply:** ${sim.total_supply} INCA'
|
||||
lines << '- **Public (TGE):** ${(sim.public_pct * 100).str()}% (No lockup)'
|
||||
lines << '- **Team:** ${(sim.team_pct * 100).str()}% (${sim.team_vesting.cliff_months}mo cliff, ${sim.team_vesting.vesting_months}mo vest)'
|
||||
lines << '- **Treasury:** ${(sim.treasury_pct * 100).str()}% (${sim.treasury_vesting.cliff_months}mo cliff, ${sim.treasury_vesting.vesting_months}mo vest)'
|
||||
lines << '- **Investors:** ${(sim.investor_pct * 100).str()}%'
|
||||
lines << ''
|
||||
lines << '### Investor Rounds & Vesting'
|
||||
lines << '| Round | Allocation | Price (USD) | Vesting Schedule |'
|
||||
lines << '|---|---|---|---|'
|
||||
|
||||
for round in sim.investor_rounds {
|
||||
lines << '| **${round.name}** | ${(round.allocation_pct * 100).str()}% | \$${round.price} | ${round.vesting.cliff_months}mo cliff, ${round.vesting.vesting_months}mo linear vest |'
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
pub fn (sim Simulation) generate_scenario_section(scenario &Scenario) !string {
|
||||
mut lines := []string{}
|
||||
|
||||
lines << '### ${scenario.name} Scenario'
|
||||
lines << '**Parameters:**'
|
||||
lines << '- **Auction Demand:** \$${scenario.demands.map(it.str()).join(', ')}'
|
||||
lines << '- **AMM Net Trade:** \$${scenario.amm_trades.map(it.str()).join(', ')}'
|
||||
lines << ''
|
||||
lines << '**Results:**'
|
||||
lines << '| Treasury Raised | Final Price | ${sim.investor_rounds.map("ROI " + it.name).join(" | ")} |'
|
||||
lines << '|:---|:---|${sim.investor_rounds.map(":---").join("|")}|'
|
||||
|
||||
mut row := ['\$${(scenario.final_metrics.treasury_total / 1000000).str()}M', '\$${scenario.final_metrics.final_price}']
|
||||
for round in sim.investor_rounds {
|
||||
roi := scenario.final_metrics.investor_roi[round.name] or { 0.0 }
|
||||
row << '${roi}x'
|
||||
}
|
||||
lines << '| ${row.join(' | ')} |'
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
pub fn (sim Simulation) generate_financial_summary() !string {
|
||||
mut lines := []string{}
|
||||
|
||||
lines << '### Funds Raised for INCA COOP'
|
||||
lines << '| Round | USD Raised |'
|
||||
lines << '|---|---|'
|
||||
|
||||
mut total_raised := 0.0
|
||||
for round in sim.investor_rounds {
|
||||
raised := round.allocation_pct * sim.total_supply * round.price
|
||||
total_raised += raised
|
||||
lines << '| **${round.name}** | \$${raised} |'
|
||||
}
|
||||
lines << '| **Total** | **\$${total_raised}** |'
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
68
lib/threefold/incatokens/factory.v
Normal file
68
lib/threefold/incatokens/factory.v
Normal file
@@ -0,0 +1,68 @@
|
||||
module incatokens
|
||||
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.biz.spreadsheet
|
||||
|
||||
__global (
|
||||
simulations map[string]&Simulation
|
||||
)
|
||||
|
||||
@[params]
|
||||
pub struct SimulationNewArgs {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
total_supply f64 = 10_000_000_000
|
||||
public_pct f64 = 0.50
|
||||
team_pct f64 = 0.15
|
||||
treasury_pct f64 = 0.15
|
||||
investor_pct f64 = 0.20
|
||||
nrcol int = 60 // 60 months default
|
||||
currency string = 'USD'
|
||||
}
|
||||
|
||||
pub fn simulation_new(args SimulationNewArgs) !&Simulation {
|
||||
name := texttools.name_fix(args.name)
|
||||
|
||||
// Initialize spreadsheets for tracking
|
||||
price_sheet := spreadsheet.sheet_new(
|
||||
name: '${name}_prices'
|
||||
nrcol: args.nrcol
|
||||
curr: args.currency
|
||||
)!
|
||||
|
||||
token_sheet := spreadsheet.sheet_new(
|
||||
name: '${name}_tokens'
|
||||
nrcol: args.nrcol
|
||||
curr: args.currency
|
||||
)!
|
||||
|
||||
investment_sheet := spreadsheet.sheet_new(
|
||||
name: '${name}_investments'
|
||||
nrcol: args.nrcol
|
||||
curr: args.currency
|
||||
)!
|
||||
|
||||
mut sim := &Simulation{
|
||||
name: name
|
||||
total_supply: args.total_supply
|
||||
public_pct: args.public_pct
|
||||
team_pct: args.team_pct
|
||||
treasury_pct: args.treasury_pct
|
||||
investor_pct: args.investor_pct
|
||||
currency: args.currency
|
||||
price_sheet: price_sheet
|
||||
token_sheet: token_sheet
|
||||
investment_sheet: investment_sheet
|
||||
vesting_sheet: unsafe { nil }
|
||||
}
|
||||
|
||||
simulations[name] = sim
|
||||
return sim
|
||||
}
|
||||
|
||||
pub fn simulation_get(name string) !&Simulation {
|
||||
name_fixed := texttools.name_fix(name)
|
||||
return simulations[name_fixed] or {
|
||||
return error('Simulation "${name_fixed}" not found')
|
||||
}
|
||||
}
|
||||
81
lib/threefold/incatokens/incatokens_test.v
Normal file
81
lib/threefold/incatokens/incatokens_test.v
Normal file
@@ -0,0 +1,81 @@
|
||||
module incatokens
|
||||
|
||||
fn test_simulation_creation() {
|
||||
mut sim := simulation_new(name: 'test_sim')!
|
||||
sim.init_default_rounds()!
|
||||
|
||||
assert sim.name == 'test_sim'
|
||||
assert sim.total_supply == 10_000_000_000
|
||||
assert sim.investor_rounds.len == 3
|
||||
}
|
||||
|
||||
fn test_scenario_execution() {
|
||||
mut sim := simulation_new(name: 'test_sim')!
|
||||
sim.init_default_rounds()!
|
||||
|
||||
// Run low scenario
|
||||
low_scenario := sim.run_scenario(
|
||||
'Low',
|
||||
[10_000_000.0, 10_000_000.0, 0.0],
|
||||
[0.0, 0.0, 0.0]
|
||||
)!
|
||||
|
||||
assert low_scenario.name == 'Low'
|
||||
assert low_scenario.final_metrics.treasury_total > 0
|
||||
assert low_scenario.final_metrics.final_price > 0
|
||||
|
||||
// Check ROI is positive for all rounds
|
||||
for round in sim.investor_rounds {
|
||||
roi := low_scenario.final_metrics.investor_roi[round.name] or { 0.0 }
|
||||
assert roi > 0 // Should have positive return
|
||||
}
|
||||
}
|
||||
|
||||
fn test_vesting_schedules() {
|
||||
mut sim := simulation_new(name: 'test_sim')!
|
||||
sim.init_default_rounds()!
|
||||
sim.create_vesting_schedules()!
|
||||
|
||||
// Check team vesting
|
||||
team_row := sim.vesting_sheet.row_get('team_vesting')!
|
||||
|
||||
// Before cliff (month 11), should be 0
|
||||
assert team_row.cells[11].val == 0
|
||||
|
||||
// After cliff starts (month 12), should have tokens
|
||||
assert team_row.cells[12].val > 0
|
||||
|
||||
// After full vesting (month 48), should have all tokens
|
||||
total_team_tokens := sim.total_supply * sim.team_pct
|
||||
assert team_row.cells[48].val == total_team_tokens
|
||||
}
|
||||
|
||||
fn test_export_functionality() {
|
||||
mut sim := simulation_new(name: 'test_export')!
|
||||
sim.init_default_rounds()!
|
||||
|
||||
// Run scenarios
|
||||
sim.run_scenario('Low', [10_000_000.0, 10_000_000.0, 0.0], [0.0, 0.0, 0.0])!
|
||||
sim.run_scenario('High', [50_000_000.0, 100_000_000.0, 0.0], [0.0, 0.0, 0.0])!
|
||||
|
||||
// Test CSV export
|
||||
sim.export_csv('prices', '/tmp/test_prices.csv')!
|
||||
|
||||
// Test report generation
|
||||
// sim.export_report('/tmp/test_report.md')!
|
||||
}
|
||||
// fn test_template_rendering() {
|
||||
// mut sim := simulation_new(name: 'test_template')!
|
||||
// sim.init_default_rounds()!
|
||||
|
||||
// // Run a scenario
|
||||
// sim.run_scenario('Low', [10_000_000.0, 10_000_000.0, 0.0], [0.0, 0.0, 0.0])!
|
||||
|
||||
// // Render the report
|
||||
// report := sim.render_report()!
|
||||
|
||||
// // Assert that the report is not empty and contains some expected text
|
||||
// assert report.len > 0
|
||||
// assert report.contains('# INCA TGE Simulation Report')
|
||||
// assert report.contains('## 1) Token Distribution & Vesting')
|
||||
// }
|
||||
93
lib/threefold/incatokens/model.v
Normal file
93
lib/threefold/incatokens/model.v
Normal file
@@ -0,0 +1,93 @@
|
||||
module incatokens
|
||||
|
||||
import freeflowuniverse.herolib.biz.spreadsheet
|
||||
import freeflowuniverse.herolib.data.ourtime
|
||||
|
||||
pub struct VestingSchedule {
|
||||
pub mut:
|
||||
cliff_months int
|
||||
vesting_months int
|
||||
}
|
||||
|
||||
pub struct InvestorRound {
|
||||
pub mut:
|
||||
name string
|
||||
allocation_pct f64
|
||||
price f64
|
||||
vesting VestingSchedule
|
||||
}
|
||||
|
||||
pub enum EpochType {
|
||||
auction_only
|
||||
hybrid
|
||||
amm_only
|
||||
}
|
||||
|
||||
pub struct Epoch {
|
||||
pub mut:
|
||||
index int
|
||||
type_ EpochType
|
||||
start_month int
|
||||
end_month int
|
||||
auction_share f64
|
||||
amm_share f64
|
||||
tokens_allocated f64
|
||||
tokens_spillover f64
|
||||
auction_demand f64
|
||||
amm_net_trade f64
|
||||
final_price f64
|
||||
treasury_raised f64
|
||||
}
|
||||
|
||||
pub struct Scenario {
|
||||
pub mut:
|
||||
name string
|
||||
demands []f64
|
||||
amm_trades []f64
|
||||
epochs []Epoch
|
||||
final_metrics ScenarioMetrics
|
||||
}
|
||||
|
||||
pub struct ScenarioMetrics {
|
||||
pub mut:
|
||||
treasury_total f64
|
||||
final_price f64
|
||||
investor_roi map[string]f64
|
||||
market_cap_final f64
|
||||
circulating_supply_final f64
|
||||
}
|
||||
|
||||
@[heap]
|
||||
pub struct Simulation {
|
||||
pub mut:
|
||||
name string
|
||||
|
||||
// Token distribution
|
||||
total_supply f64
|
||||
public_pct f64
|
||||
team_pct f64
|
||||
treasury_pct f64
|
||||
investor_pct f64
|
||||
|
||||
// Configuration
|
||||
currency string
|
||||
epoch1_floor_uplift f64 = 1.20
|
||||
epochn_floor_uplift f64 = 1.20
|
||||
amm_liquidity_depth_factor f64 = 2.0
|
||||
|
||||
// Investor rounds
|
||||
investor_rounds []InvestorRound
|
||||
|
||||
// Vesting schedules
|
||||
team_vesting VestingSchedule
|
||||
treasury_vesting VestingSchedule
|
||||
|
||||
// Tracking sheets
|
||||
price_sheet &spreadsheet.Sheet
|
||||
token_sheet &spreadsheet.Sheet
|
||||
investment_sheet &spreadsheet.Sheet
|
||||
vesting_sheet &spreadsheet.Sheet
|
||||
|
||||
// Scenarios
|
||||
scenarios map[string]&Scenario
|
||||
}
|
||||
175
lib/threefold/incatokens/simulation.v
Normal file
175
lib/threefold/incatokens/simulation.v
Normal file
@@ -0,0 +1,175 @@
|
||||
module incatokens
|
||||
|
||||
import freeflowuniverse.herolib.biz.spreadsheet
|
||||
|
||||
// Initialize default investor rounds
|
||||
pub fn (mut sim Simulation) init_default_rounds() ! {
|
||||
sim.investor_rounds = [
|
||||
InvestorRound{
|
||||
name: 'R1'
|
||||
allocation_pct: 0.05
|
||||
price: 0.005
|
||||
vesting: VestingSchedule{cliff_months: 6, vesting_months: 24}
|
||||
},
|
||||
InvestorRound{
|
||||
name: 'R2'
|
||||
allocation_pct: 0.075
|
||||
price: 0.009
|
||||
vesting: VestingSchedule{cliff_months: 6, vesting_months: 24}
|
||||
},
|
||||
InvestorRound{
|
||||
name: 'R3'
|
||||
allocation_pct: 0.075
|
||||
price: 0.0106
|
||||
vesting: VestingSchedule{cliff_months: 6, vesting_months: 24}
|
||||
}
|
||||
]
|
||||
|
||||
sim.team_vesting = VestingSchedule{cliff_months: 12, vesting_months: 36}
|
||||
sim.treasury_vesting = VestingSchedule{cliff_months: 12, vesting_months: 48}
|
||||
}
|
||||
|
||||
// Run a scenario with given demands and AMM trades
|
||||
pub fn (mut sim Simulation) run_scenario(name string, demands []f64, amm_trades []f64) !&Scenario {
|
||||
mut scenario := &Scenario{
|
||||
name: name
|
||||
demands: demands
|
||||
amm_trades: amm_trades
|
||||
}
|
||||
|
||||
// Initialize epochs
|
||||
scenario.epochs = [
|
||||
Epoch{index: 0, type_: .auction_only, start_month: 0, end_month: 3, auction_share: 1.0, amm_share: 0.0},
|
||||
Epoch{index: 1, type_: .hybrid, start_month: 3, end_month: 6, auction_share: 0.5, amm_share: 0.5},
|
||||
Epoch{index: 2, type_: .amm_only, start_month: 6, end_month: 12, auction_share: 0.0, amm_share: 1.0}
|
||||
]
|
||||
|
||||
// Track in spreadsheet
|
||||
mut price_row := sim.price_sheet.row_new(
|
||||
name: 'scenario_${name}_price'
|
||||
tags: 'scenario:${name} type:price'
|
||||
descr: 'Token price evolution for ${name} scenario'
|
||||
)!
|
||||
|
||||
mut treasury_row := sim.investment_sheet.row_new(
|
||||
name: 'scenario_${name}_treasury'
|
||||
tags: 'scenario:${name} type:treasury'
|
||||
descr: 'Treasury raised for ${name} scenario'
|
||||
aggregatetype: .sum
|
||||
)!
|
||||
|
||||
// Calculate public tokens per epoch
|
||||
total_public := sim.total_supply * sim.public_pct
|
||||
tokens_per_epoch := total_public / 3.0
|
||||
|
||||
mut last_auction_price := sim.get_last_investor_price()
|
||||
mut spillover := 0.0
|
||||
mut treasury_total := 0.0
|
||||
mut amm_pool := AMMPool{}
|
||||
|
||||
for mut epoch in scenario.epochs {
|
||||
epoch.tokens_allocated = tokens_per_epoch + spillover
|
||||
epoch.auction_demand = demands[epoch.index]
|
||||
epoch.amm_net_trade = amm_trades[epoch.index]
|
||||
|
||||
// Run auction if applicable
|
||||
if epoch.auction_share > 0 {
|
||||
auction_tokens := epoch.tokens_allocated * epoch.auction_share
|
||||
floor_price := sim.calculate_floor_price(epoch.index, last_auction_price)
|
||||
|
||||
auction_result := simulate_auction(
|
||||
demand: epoch.auction_demand
|
||||
min_price: floor_price
|
||||
token_supply: auction_tokens
|
||||
)!
|
||||
|
||||
epoch.treasury_raised = auction_result.usd_raised
|
||||
treasury_total += auction_result.usd_raised
|
||||
last_auction_price = auction_result.clearing_price
|
||||
epoch.final_price = auction_result.clearing_price
|
||||
spillover = auction_tokens - auction_result.tokens_sold
|
||||
|
||||
// Record in spreadsheet
|
||||
treasury_row.cells[epoch.start_month].val = auction_result.usd_raised
|
||||
}
|
||||
|
||||
// Handle AMM if applicable
|
||||
if epoch.amm_share > 0 {
|
||||
amm_tokens := epoch.tokens_allocated * epoch.amm_share + spillover
|
||||
spillover = 0
|
||||
|
||||
// Seed AMM pool
|
||||
amm_usdc_to_add := sim.amm_liquidity_depth_factor * epoch.treasury_raised
|
||||
amm_pool.add_liquidity(amm_tokens, amm_usdc_to_add)
|
||||
|
||||
// Simulate trading
|
||||
if epoch.amm_net_trade != 0 {
|
||||
amm_pool.trade(epoch.amm_net_trade)!
|
||||
}
|
||||
|
||||
epoch.final_price = amm_pool.get_price()
|
||||
}
|
||||
|
||||
// Record price in spreadsheet
|
||||
for month in epoch.start_month .. epoch.end_month {
|
||||
price_row.cells[month].val = epoch.final_price
|
||||
}
|
||||
|
||||
epoch.tokens_spillover = spillover
|
||||
}
|
||||
|
||||
// Calculate final metrics
|
||||
scenario.final_metrics = sim.calculate_metrics(scenario, treasury_total)!
|
||||
|
||||
sim.scenarios[name] = scenario
|
||||
return scenario
|
||||
}
|
||||
|
||||
// Calculate metrics for scenario
|
||||
fn (sim Simulation) calculate_metrics(scenario &Scenario, treasury_total f64) !ScenarioMetrics {
|
||||
final_price := scenario.epochs.last().final_price
|
||||
|
||||
mut investor_roi := map[string]f64{}
|
||||
for round in sim.investor_rounds {
|
||||
investor_roi[round.name] = final_price / round.price
|
||||
}
|
||||
|
||||
return ScenarioMetrics{
|
||||
treasury_total: treasury_total
|
||||
final_price: final_price
|
||||
investor_roi: investor_roi
|
||||
market_cap_final: final_price * sim.total_supply
|
||||
circulating_supply_final: sim.calculate_circulating_supply(12) // at month 12
|
||||
}
|
||||
}
|
||||
|
||||
fn (sim Simulation) get_last_investor_price() f64 {
|
||||
mut max_price := 0.0
|
||||
for round in sim.investor_rounds {
|
||||
if round.price > max_price {
|
||||
max_price = round.price
|
||||
}
|
||||
}
|
||||
return max_price
|
||||
}
|
||||
|
||||
fn (sim Simulation) calculate_floor_price(epoch_idx int, last_auction_price f64) f64 {
|
||||
last_investor_price := sim.get_last_investor_price()
|
||||
|
||||
if epoch_idx == 0 {
|
||||
return last_investor_price * sim.epoch1_floor_uplift
|
||||
}
|
||||
return last_auction_price * sim.epochn_floor_uplift
|
||||
}
|
||||
|
||||
fn (sim Simulation) calculate_circulating_supply(month int) f64 {
|
||||
// Base circulation (non-public allocations)
|
||||
investor_tokens := sim.investor_pct * sim.total_supply
|
||||
team_tokens := sim.team_pct * sim.total_supply
|
||||
treasury_tokens := sim.treasury_pct * sim.total_supply
|
||||
|
||||
// For simplicity, assume all public tokens are circulating after TGE
|
||||
public_tokens := sim.public_pct * sim.total_supply
|
||||
|
||||
return investor_tokens + team_tokens + treasury_tokens + public_tokens
|
||||
}
|
||||
71
lib/threefold/incatokens/vesting.v
Normal file
71
lib/threefold/incatokens/vesting.v
Normal file
@@ -0,0 +1,71 @@
|
||||
module incatokens
|
||||
|
||||
import freeflowuniverse.herolib.biz.spreadsheet
|
||||
|
||||
// Create vesting schedule in spreadsheet
|
||||
pub fn (mut sim Simulation) create_vesting_schedules() ! {
|
||||
// Create vesting sheet
|
||||
mut vesting_sheet := spreadsheet.sheet_new(
|
||||
name: '${sim.name}_vesting'
|
||||
nrcol: 60 // 60 months
|
||||
curr: sim.currency
|
||||
)!
|
||||
|
||||
// Team vesting
|
||||
team_tokens := sim.total_supply * sim.team_pct
|
||||
mut team_row := vesting_sheet.row_new(
|
||||
name: 'team_vesting'
|
||||
tags: 'category:team type:vesting'
|
||||
descr: 'Team token vesting schedule'
|
||||
)!
|
||||
sim.apply_vesting_schedule(mut team_row, team_tokens, sim.team_vesting)!
|
||||
|
||||
// Treasury vesting
|
||||
treasury_tokens := sim.total_supply * sim.treasury_pct
|
||||
mut treasury_row := vesting_sheet.row_new(
|
||||
name: 'treasury_vesting'
|
||||
tags: 'category:treasury type:vesting'
|
||||
descr: 'Treasury token vesting schedule'
|
||||
)!
|
||||
sim.apply_vesting_schedule(mut treasury_row, treasury_tokens, sim.treasury_vesting)!
|
||||
|
||||
// Investor rounds vesting
|
||||
for round in sim.investor_rounds {
|
||||
round_tokens := sim.total_supply * round.allocation_pct
|
||||
mut round_row := vesting_sheet.row_new(
|
||||
name: '${round.name}_vesting'
|
||||
tags: 'category:investor round:${round.name} type:vesting'
|
||||
descr: '${round.name} investor vesting schedule'
|
||||
)!
|
||||
sim.apply_vesting_schedule(mut round_row, round_tokens, round.vesting)!
|
||||
}
|
||||
|
||||
// Create total unlocked row
|
||||
mut total_row := vesting_sheet.group2row(
|
||||
name: 'total_unlocked'
|
||||
include: ['type:vesting']
|
||||
tags: 'summary type:total_vesting'
|
||||
descr: 'Total tokens unlocked over time'
|
||||
aggregatetype: .sum
|
||||
)!
|
||||
|
||||
sim.vesting_sheet = vesting_sheet
|
||||
}
|
||||
|
||||
fn (sim Simulation) apply_vesting_schedule(mut row spreadsheet.Row, total_tokens f64, schedule VestingSchedule) ! {
|
||||
monthly_unlock := total_tokens / f64(schedule.vesting_months)
|
||||
|
||||
for month in 0 .. row.sheet.nrcol {
|
||||
if month < schedule.cliff_months {
|
||||
// Before cliff, no tokens unlocked
|
||||
row.cells[month].val = 0
|
||||
} else if month < schedule.cliff_months + schedule.vesting_months {
|
||||
// During vesting period
|
||||
months_vested := month - schedule.cliff_months + 1
|
||||
row.cells[month].val = monthly_unlock * f64(months_vested)
|
||||
} else {
|
||||
// After vesting complete
|
||||
row.cells[month].val = total_tokens
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user