Skip to content

Commit af2b0c1

Browse files
committed
Add base case stress simulation tests (v0, PYUSD0 debt)
1 parent cf310cb commit af2b0c1

5 files changed

Lines changed: 1072 additions & 44 deletions

File tree

cadence/contracts/mocks/FlowTransactionScheduler.cdc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// This contract extends the original contract by adding a reset method,
2+
// which is useful in tests for clearing any pre-existing scheduled transactions.
3+
// https://github.com/onflow/flow-core-contracts/blob/master/contracts/FlowTransactionScheduler.cdc
14
import "FungibleToken"
25
import "FlowToken"
36
import "FlowFees"

cadence/tests/scripts/simulations/generate_fixture.py

Lines changed: 38 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ def cmd_fetch(args: argparse.Namespace) -> None:
221221
def generate_cdc(data: dict) -> str:
222222
scenario = data["scenario"]
223223
is_daily = "duration_days" in data
224+
prices = data.get("prices") or data["btc_prices"]
224225

225226
lines: list[str] = []
226227
lines.append("import Test")
@@ -293,8 +294,8 @@ def generate_cdc(data: dict) -> str:
293294

294295
# --- Price array ---
295296
lines.append(f"access(all) let {scenario}_prices: [UFix64] = [")
296-
for i, price in enumerate(data["btc_prices"]):
297-
comma = "," if i < len(data["btc_prices"]) - 1 else ""
297+
for i, price in enumerate(prices):
298+
comma = "," if i < len(prices) - 1 else ""
298299
lines.append(f" {to_ufix64(price)}{comma}")
299300
lines.append("]")
300301
lines.append("")
@@ -308,28 +309,7 @@ def generate_cdc(data: dict) -> str:
308309
lines.append("]")
309310
lines.append("")
310311

311-
# --- Agent array ---
312-
lines.append(f"access(all) let {scenario}_agents: [SimAgent] = [")
313-
for i, agent in enumerate(data["agents"]):
314-
comma = "," if i < len(data["agents"]) - 1 else ""
315-
debt = (
316-
agent["debt_per_agent"]
317-
if isinstance(agent["debt_per_agent"], (int, float))
318-
else 0
319-
)
320-
total_debt = agent.get("total_system_debt", 0)
321-
lines.append(" SimAgent(")
322-
lines.append(f" count: {agent['count']},")
323-
lines.append(f" initialHF: {to_ufix64(agent['initial_hf'])},")
324-
lines.append(f" rebalancingHF: {to_ufix64(agent['rebalancing_hf'])},")
325-
lines.append(f" targetHF: {to_ufix64(agent['target_hf'])},")
326-
lines.append(f" debtPerAgent: {to_ufix64(float(debt))},")
327-
lines.append(f" totalSystemDebt: {to_ufix64(float(total_debt))}")
328-
lines.append(f" ){comma}")
329-
lines.append("]")
330-
lines.append("")
331-
332-
# --- Pool dict ---
312+
# --- Pool dict (before agents so scenarios read nicely) ---
333313
lines.append(f"access(all) let {scenario}_pools: {{String: SimPool}} = {{")
334314
pool_items = list(data["pools"].items())
335315
for i, (name, pool) in enumerate(pool_items):
@@ -354,28 +334,42 @@ def generate_cdc(data: dict) -> str:
354334
lines.append(")")
355335
lines.append("")
356336

357-
# --- Expected outcomes ---
358-
e = data["expected"]
359-
lines.append(
360-
f"access(all) let {scenario}_expectedLiquidationCount: Int = {e['liquidation_count']}"
361-
)
362-
lines.append(
363-
f"access(all) let {scenario}_expectedAllAgentsSurvive: Bool = {'true' if e['all_agents_survive'] else 'false'}"
364-
)
365-
lines.append("")
366-
367337
# --- Duration & notes ---
368338
if is_daily:
369-
lines.append(
370-
f"access(all) let {scenario}_durationDays: Int = {data['duration_days']}"
371-
)
372-
else:
373-
lines.append(
374-
f"access(all) let {scenario}_durationMinutes: Int = {data['duration_minutes']}"
375-
)
376-
lines.append(f'access(all) let {scenario}_notes: String = "{data["notes"]}"')
339+
lines.append(f"access(all) let {scenario}_durationDays: Int = {data['duration_days']}")
340+
elif "duration_minutes" in data:
341+
lines.append(f"access(all) let {scenario}_durationMinutes: Int = {data['duration_minutes']}")
342+
if "notes" in data:
343+
lines.append(f'access(all) let {scenario}_notes: String = "{data["notes"]}"')
377344
lines.append("")
378345

346+
# --- Agents & expected outcomes (supports multi-scenario via "scenarios" key) ---
347+
def _emit_agents_and_expected(prefix, agents, expected):
348+
lines.append(f"access(all) let {prefix}_agents: [SimAgent] = [")
349+
for i, agent in enumerate(agents):
350+
comma = "," if i < len(agents) - 1 else ""
351+
debt = agent["debt_per_agent"] if isinstance(agent["debt_per_agent"], (int, float)) else 0
352+
total_debt = agent.get("total_system_debt", 0)
353+
lines.append(" SimAgent(")
354+
lines.append(f" count: {agent['count']},")
355+
lines.append(f" initialHF: {to_ufix64(agent['initial_hf'])},")
356+
lines.append(f" rebalancingHF: {to_ufix64(agent['rebalancing_hf'])},")
357+
lines.append(f" targetHF: {to_ufix64(agent['target_hf'])},")
358+
lines.append(f" debtPerAgent: {to_ufix64(float(debt))},")
359+
lines.append(f" totalSystemDebt: {to_ufix64(float(total_debt))}")
360+
lines.append(f" ){comma}")
361+
lines.append("]")
362+
lines.append("")
363+
lines.append(f"access(all) let {prefix}_expectedLiquidationCount: Int = {expected['liquidation_count']}")
364+
lines.append(f"access(all) let {prefix}_expectedAllAgentsSurvive: Bool = {'true' if expected['all_agents_survive'] else 'false'}")
365+
lines.append("")
366+
367+
if "scenarios" in data:
368+
for sub in data["scenarios"]:
369+
_emit_agents_and_expected(f"{scenario}_{sub['name']}", sub["agents"], sub["expected"])
370+
else:
371+
_emit_agents_and_expected(scenario, data["agents"], data["expected"])
372+
379373
return "\n".join(lines)
380374

381375

@@ -390,8 +384,8 @@ def cmd_generate(args: argparse.Namespace) -> None:
390384
f.write(cdc)
391385

392386
scenario = data["scenario"]
393-
n_prices = len(data["btc_prices"])
394-
print(f"Generated {args.output} ({n_prices} prices, scenario: {scenario})")
387+
prices = data.get("prices") or data["btc_prices"]
388+
print(f"Generated {args.output} ({len(prices)} prices, scenario: {scenario})")
395389

396390

397391
# ---------------------------------------------------------------------------
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
{
2+
"scenario": "simulation_ht_vs_aave",
3+
"duration_minutes": 60,
4+
"prices": [
5+
100000.00, 99551.11, 99104.23, 98659.37, 98216.49,
6+
97775.61, 97336.70, 96899.77, 96464.80, 96031.78,
7+
95600.70, 95171.56, 94744.34, 94319.04, 93895.65,
8+
93474.17, 93054.57, 92636.86, 92221.02, 91807.05,
9+
91394.93, 90984.67, 90576.25, 90169.66, 89764.90,
10+
89361.95, 88960.82, 88561.48, 88163.94, 87768.18,
11+
87374.20, 86981.98, 86591.53, 86202.83, 85815.87,
12+
85430.65, 85047.16, 84665.39, 84285.34, 83906.99,
13+
83530.34, 83155.38, 82782.10, 82410.50, 82040.57,
14+
81672.30, 81305.68, 80940.71, 80577.37, 80215.67,
15+
79855.59, 79497.12, 79140.27, 78785.02, 78431.36,
16+
78079.29, 77728.80, 77379.88, 77032.53, 76686.74,
17+
76342.50
18+
],
19+
"pools": {
20+
"pyusd0_yt": {
21+
"size": 500000,
22+
"concentration": 0.95,
23+
"fee_tier": 0.0005
24+
},
25+
"pyusd0_flow": {
26+
"size": 500000,
27+
"concentration": 0.80,
28+
"fee_tier": 0.003
29+
}
30+
},
31+
"constants": {
32+
"btc_collateral_factor": 0.75,
33+
"btc_liquidation_threshold": 0.80,
34+
"yield_apr": 0.10,
35+
"direct_mint_yt": true
36+
},
37+
"scenarios": [
38+
{
39+
"name": "aggressive_1_01",
40+
"agents": [
41+
{"count": 5, "initial_hf": 1.10, "rebalancing_hf": 1.05, "target_hf": 1.01, "debt_per_agent": 133333, "total_system_debt": 666665},
42+
{"count": 5, "initial_hf": 1.20, "rebalancing_hf": 1.05, "target_hf": 1.01, "debt_per_agent": 133333, "total_system_debt": 666665}
43+
],
44+
"expected": {"liquidation_count": 0, "all_agents_survive": true}
45+
},
46+
{
47+
"name": "moderate_1_025",
48+
"agents": [
49+
{"count": 5, "initial_hf": 1.20, "rebalancing_hf": 1.05, "target_hf": 1.025, "debt_per_agent": 133333, "total_system_debt": 666665},
50+
{"count": 5, "initial_hf": 1.40, "rebalancing_hf": 1.05, "target_hf": 1.025, "debt_per_agent": 133333, "total_system_debt": 666665}
51+
],
52+
"expected": {"liquidation_count": 0, "all_agents_survive": true}
53+
},
54+
{
55+
"name": "conservative_1_05",
56+
"agents": [
57+
{"count": 5, "initial_hf": 1.30, "rebalancing_hf": 1.10, "target_hf": 1.05, "debt_per_agent": 133333, "total_system_debt": 666665},
58+
{"count": 5, "initial_hf": 1.50, "rebalancing_hf": 1.10, "target_hf": 1.05, "debt_per_agent": 133333, "total_system_debt": 666665}
59+
],
60+
"expected": {"liquidation_count": 0, "all_agents_survive": true}
61+
},
62+
{
63+
"name": "mixed_1_075",
64+
"agents": [
65+
{"count": 5, "initial_hf": 1.10, "rebalancing_hf": 1.05, "target_hf": 1.075, "debt_per_agent": 133333, "total_system_debt": 666665},
66+
{"count": 5, "initial_hf": 1.50, "rebalancing_hf": 1.05, "target_hf": 1.075, "debt_per_agent": 133333, "total_system_debt": 666665}
67+
],
68+
"expected": {"liquidation_count": 0, "all_agents_survive": true}
69+
},
70+
{
71+
"name": "balanced_1_1",
72+
"agents": [
73+
{"count": 5, "initial_hf": 1.25, "rebalancing_hf": 1.10, "target_hf": 1.10, "debt_per_agent": 133333, "total_system_debt": 666665},
74+
{"count": 5, "initial_hf": 1.45, "rebalancing_hf": 1.10, "target_hf": 1.10, "debt_per_agent": 133333, "total_system_debt": 666665}
75+
],
76+
"expected": {"liquidation_count": 0, "all_agents_survive": true}
77+
}
78+
],
79+
"notes": "BTC $100K to $76,342.50 (-23.66%) exponential decline over 60 minutes. Source: comprehensive_ht_vs_aave_analysis.py"
80+
}

0 commit comments

Comments
 (0)