@intentsolutionsio/flash-loan-simulator 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +22 -0
- package/LICENSE +21 -0
- package/README.md +411 -0
- package/agents/flashloan-agent.md +261 -0
- package/package.json +43 -0
- package/skills/simulating-flash-loans/ARD.md +454 -0
- package/skills/simulating-flash-loans/PRD.md +239 -0
- package/skills/simulating-flash-loans/SKILL.md +109 -0
- package/skills/simulating-flash-loans/config/settings.yaml +241 -0
- package/skills/simulating-flash-loans/references/errors.md +297 -0
- package/skills/simulating-flash-loans/references/examples.md +532 -0
- package/skills/simulating-flash-loans/references/implementation.md +73 -0
- package/skills/simulating-flash-loans/scripts/flash_simulator.py +492 -0
- package/skills/simulating-flash-loans/scripts/formatters.py +512 -0
- package/skills/simulating-flash-loans/scripts/profit_calculator.py +315 -0
- package/skills/simulating-flash-loans/scripts/protocol_adapters.py +416 -0
- package/skills/simulating-flash-loans/scripts/risk_assessor.py +439 -0
- package/skills/simulating-flash-loans/scripts/strategy_engine.py +596 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Flash loan profitability calculator.
|
|
4
|
+
|
|
5
|
+
Calculates net profit after all costs including:
|
|
6
|
+
- Flash loan fees
|
|
7
|
+
- Gas costs
|
|
8
|
+
- DEX fees (implicit in swaps)
|
|
9
|
+
- Slippage
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from decimal import Decimal
|
|
14
|
+
from typing import List, Optional
|
|
15
|
+
|
|
16
|
+
from strategy_engine import StrategyResult, TransactionStep
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class ProfitBreakdown:
|
|
21
|
+
"""Detailed profit breakdown."""
|
|
22
|
+
|
|
23
|
+
gross_revenue: Decimal
|
|
24
|
+
flash_loan_fee: Decimal
|
|
25
|
+
gas_cost_eth: Decimal
|
|
26
|
+
gas_cost_usd: Decimal
|
|
27
|
+
dex_fees: Decimal
|
|
28
|
+
slippage_cost: Decimal
|
|
29
|
+
total_costs: Decimal
|
|
30
|
+
net_profit: Decimal
|
|
31
|
+
net_profit_usd: Decimal
|
|
32
|
+
roi_percent: float
|
|
33
|
+
breakeven_gas_price: float # Max gas price for profitability
|
|
34
|
+
is_profitable: bool
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class GasEstimate:
|
|
39
|
+
"""Gas estimation for a transaction."""
|
|
40
|
+
|
|
41
|
+
gas_units: int
|
|
42
|
+
gas_price_gwei: float
|
|
43
|
+
cost_eth: Decimal
|
|
44
|
+
cost_usd: Decimal
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ProfitCalculator:
|
|
48
|
+
"""
|
|
49
|
+
Calculates detailed profitability for flash loan strategies.
|
|
50
|
+
|
|
51
|
+
Accounts for all costs and provides breakeven analysis.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
eth_price_usd: float = 2500.0,
|
|
57
|
+
gas_price_gwei: float = 30.0,
|
|
58
|
+
):
|
|
59
|
+
"""
|
|
60
|
+
Initialize calculator.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
eth_price_usd: Current ETH price
|
|
64
|
+
gas_price_gwei: Current gas price
|
|
65
|
+
"""
|
|
66
|
+
self.eth_price_usd = Decimal(str(eth_price_usd))
|
|
67
|
+
self.gas_price_gwei = gas_price_gwei
|
|
68
|
+
|
|
69
|
+
def calculate_breakdown(
|
|
70
|
+
self,
|
|
71
|
+
result: StrategyResult,
|
|
72
|
+
slippage_pct: float = 0.5,
|
|
73
|
+
) -> ProfitBreakdown:
|
|
74
|
+
"""
|
|
75
|
+
Calculate detailed profit breakdown from strategy result.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
result: Strategy simulation result
|
|
79
|
+
slippage_pct: Expected slippage percentage
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Detailed profit breakdown
|
|
83
|
+
"""
|
|
84
|
+
# Calculate gross revenue (profit before any costs)
|
|
85
|
+
gross_revenue = result.gross_profit + result.loan_fee + result.gas_cost_eth
|
|
86
|
+
|
|
87
|
+
# Flash loan fee
|
|
88
|
+
flash_loan_fee = result.loan_fee
|
|
89
|
+
|
|
90
|
+
# Gas costs
|
|
91
|
+
gas_cost_eth = result.gas_cost_eth
|
|
92
|
+
gas_cost_usd = result.gas_cost_usd
|
|
93
|
+
|
|
94
|
+
# DEX fees (usually included in price, but track separately)
|
|
95
|
+
# Estimate ~0.3% per swap
|
|
96
|
+
num_swaps = sum(1 for s in result.steps if s.action == "swap")
|
|
97
|
+
dex_fee_rate = Decimal("0.003") # 0.3%
|
|
98
|
+
dex_fees = result.loan_amount * dex_fee_rate * num_swaps
|
|
99
|
+
|
|
100
|
+
# Slippage cost
|
|
101
|
+
slippage_rate = Decimal(str(slippage_pct / 100))
|
|
102
|
+
slippage_cost = result.loan_amount * slippage_rate
|
|
103
|
+
|
|
104
|
+
# Total costs
|
|
105
|
+
total_costs = flash_loan_fee + gas_cost_eth + slippage_cost
|
|
106
|
+
# Note: DEX fees already reflected in swap outputs
|
|
107
|
+
|
|
108
|
+
# Net profit
|
|
109
|
+
net_profit = result.net_profit
|
|
110
|
+
net_profit_usd = result.net_profit_usd
|
|
111
|
+
|
|
112
|
+
# ROI
|
|
113
|
+
roi = result.roi_percent
|
|
114
|
+
|
|
115
|
+
# Breakeven gas price
|
|
116
|
+
# At what gas price does profit = 0?
|
|
117
|
+
profit_before_gas = gross_revenue - flash_loan_fee - slippage_cost
|
|
118
|
+
total_gas_units = sum(s.gas_estimate for s in result.steps)
|
|
119
|
+
|
|
120
|
+
if total_gas_units > 0 and profit_before_gas > 0:
|
|
121
|
+
# profit_before_gas = gas_units * gas_price * eth_price
|
|
122
|
+
# gas_price = profit_before_gas / (gas_units * eth_price)
|
|
123
|
+
breakeven_gas = float(
|
|
124
|
+
profit_before_gas * Decimal("1e9") / (total_gas_units * self.eth_price_usd)
|
|
125
|
+
)
|
|
126
|
+
else:
|
|
127
|
+
breakeven_gas = 0.0
|
|
128
|
+
|
|
129
|
+
return ProfitBreakdown(
|
|
130
|
+
gross_revenue=gross_revenue,
|
|
131
|
+
flash_loan_fee=flash_loan_fee,
|
|
132
|
+
gas_cost_eth=gas_cost_eth,
|
|
133
|
+
gas_cost_usd=gas_cost_usd,
|
|
134
|
+
dex_fees=dex_fees,
|
|
135
|
+
slippage_cost=slippage_cost,
|
|
136
|
+
total_costs=total_costs,
|
|
137
|
+
net_profit=net_profit,
|
|
138
|
+
net_profit_usd=net_profit_usd,
|
|
139
|
+
roi_percent=roi,
|
|
140
|
+
breakeven_gas_price=breakeven_gas,
|
|
141
|
+
is_profitable=net_profit > 0,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
def estimate_gas(self, steps: List[TransactionStep]) -> GasEstimate:
|
|
145
|
+
"""
|
|
146
|
+
Estimate total gas cost for transaction steps.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
steps: List of transaction steps
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Gas estimate with cost
|
|
153
|
+
"""
|
|
154
|
+
total_units = sum(s.gas_estimate for s in steps)
|
|
155
|
+
|
|
156
|
+
cost_eth = Decimal(total_units * self.gas_price_gwei) / Decimal("1e9")
|
|
157
|
+
cost_usd = cost_eth * self.eth_price_usd
|
|
158
|
+
|
|
159
|
+
return GasEstimate(
|
|
160
|
+
gas_units=total_units,
|
|
161
|
+
gas_price_gwei=self.gas_price_gwei,
|
|
162
|
+
cost_eth=cost_eth,
|
|
163
|
+
cost_usd=cost_usd,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
def calculate_minimum_profit(
|
|
167
|
+
self,
|
|
168
|
+
loan_amount: Decimal,
|
|
169
|
+
loan_fee_rate: Decimal,
|
|
170
|
+
gas_units: int,
|
|
171
|
+
num_swaps: int = 2,
|
|
172
|
+
) -> Decimal:
|
|
173
|
+
"""
|
|
174
|
+
Calculate minimum profit needed to break even.
|
|
175
|
+
|
|
176
|
+
Useful for determining if an opportunity is worth pursuing.
|
|
177
|
+
"""
|
|
178
|
+
# Flash loan fee
|
|
179
|
+
loan_fee = loan_amount * loan_fee_rate
|
|
180
|
+
|
|
181
|
+
# Gas cost
|
|
182
|
+
gas_cost = Decimal(gas_units * self.gas_price_gwei) / Decimal("1e9")
|
|
183
|
+
|
|
184
|
+
# Minimum slippage assumption (0.5%)
|
|
185
|
+
slippage = loan_amount * Decimal("0.005")
|
|
186
|
+
|
|
187
|
+
return loan_fee + gas_cost + slippage
|
|
188
|
+
|
|
189
|
+
def compare_providers(
|
|
190
|
+
self,
|
|
191
|
+
loan_amount: Decimal,
|
|
192
|
+
asset: str,
|
|
193
|
+
gas_units: int,
|
|
194
|
+
providers: dict,
|
|
195
|
+
) -> List[dict]:
|
|
196
|
+
"""
|
|
197
|
+
Compare profitability across flash loan providers.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
loan_amount: Amount to borrow
|
|
201
|
+
asset: Asset to borrow
|
|
202
|
+
gas_units: Base gas units (before provider overhead)
|
|
203
|
+
providers: Dict of provider name -> fee rate
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
List of provider comparisons sorted by net cost
|
|
207
|
+
"""
|
|
208
|
+
results = []
|
|
209
|
+
|
|
210
|
+
for name, fee_rate in providers.items():
|
|
211
|
+
# Calculate fee
|
|
212
|
+
fee = loan_amount * Decimal(str(fee_rate))
|
|
213
|
+
|
|
214
|
+
# Estimate gas (with provider overhead)
|
|
215
|
+
overhead = {
|
|
216
|
+
"aave": 100000,
|
|
217
|
+
"dydx": 150000,
|
|
218
|
+
"balancer": 80000,
|
|
219
|
+
}.get(name.lower(), 100000)
|
|
220
|
+
|
|
221
|
+
total_gas = gas_units + overhead
|
|
222
|
+
gas_cost = Decimal(total_gas * self.gas_price_gwei) / Decimal("1e9")
|
|
223
|
+
|
|
224
|
+
total_cost = fee + gas_cost
|
|
225
|
+
|
|
226
|
+
results.append({
|
|
227
|
+
"provider": name,
|
|
228
|
+
"fee_rate": float(fee_rate) * 100,
|
|
229
|
+
"fee_amount": float(fee),
|
|
230
|
+
"gas_overhead": overhead,
|
|
231
|
+
"gas_cost_eth": float(gas_cost),
|
|
232
|
+
"total_cost_eth": float(total_cost),
|
|
233
|
+
"total_cost_usd": float(total_cost * self.eth_price_usd),
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
# Sort by total cost
|
|
237
|
+
results.sort(key=lambda x: x["total_cost_eth"])
|
|
238
|
+
|
|
239
|
+
return results
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def demo():
|
|
243
|
+
"""Demonstrate profit calculator."""
|
|
244
|
+
from strategy_engine import StrategyFactory, StrategyType, ArbitrageParams
|
|
245
|
+
|
|
246
|
+
# Run a simulation first
|
|
247
|
+
factory = StrategyFactory()
|
|
248
|
+
strategy = factory.create(StrategyType.SIMPLE_ARBITRAGE)
|
|
249
|
+
|
|
250
|
+
params = ArbitrageParams(
|
|
251
|
+
input_token="ETH",
|
|
252
|
+
output_token="USDC",
|
|
253
|
+
amount=Decimal("100"),
|
|
254
|
+
dex_buy="sushiswap",
|
|
255
|
+
dex_sell="uniswap",
|
|
256
|
+
provider="aave",
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
result = strategy.simulate(params)
|
|
260
|
+
|
|
261
|
+
# Calculate detailed breakdown
|
|
262
|
+
calculator = ProfitCalculator(eth_price_usd=2500.0, gas_price_gwei=30.0)
|
|
263
|
+
breakdown = calculator.calculate_breakdown(result)
|
|
264
|
+
|
|
265
|
+
print("=" * 60)
|
|
266
|
+
print("PROFIT BREAKDOWN")
|
|
267
|
+
print("=" * 60)
|
|
268
|
+
|
|
269
|
+
print(f"\nGross Revenue: {breakdown.gross_revenue:.6f} ETH")
|
|
270
|
+
print(f"\nCosts:")
|
|
271
|
+
print(f" Flash Loan Fee: -{breakdown.flash_loan_fee:.6f} ETH")
|
|
272
|
+
print(f" Gas Cost: -{breakdown.gas_cost_eth:.6f} ETH (${breakdown.gas_cost_usd:.2f})")
|
|
273
|
+
print(f" Est. Slippage: -{breakdown.slippage_cost:.6f} ETH")
|
|
274
|
+
print(f" ────────────────────────────────")
|
|
275
|
+
print(f" Total Costs: -{breakdown.total_costs:.6f} ETH")
|
|
276
|
+
|
|
277
|
+
print(f"\nNet Profit: {breakdown.net_profit:.6f} ETH (${breakdown.net_profit_usd:.2f})")
|
|
278
|
+
print(f"ROI: {breakdown.roi_percent:.4f}%")
|
|
279
|
+
print(f"Breakeven Gas: {breakdown.breakeven_gas_price:.1f} gwei")
|
|
280
|
+
print(f"\nProfitable: {'YES ✓' if breakdown.is_profitable else 'NO ✗'}")
|
|
281
|
+
|
|
282
|
+
# Compare providers
|
|
283
|
+
print("\n" + "=" * 60)
|
|
284
|
+
print("PROVIDER COMPARISON")
|
|
285
|
+
print("=" * 60)
|
|
286
|
+
|
|
287
|
+
providers = {
|
|
288
|
+
"Aave V3": 0.0009,
|
|
289
|
+
"dYdX": 0.0,
|
|
290
|
+
"Balancer": 0.0001,
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
comparisons = calculator.compare_providers(
|
|
294
|
+
loan_amount=Decimal("100"),
|
|
295
|
+
asset="ETH",
|
|
296
|
+
gas_units=300000,
|
|
297
|
+
providers=providers,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
print(f"\nFor 100 ETH flash loan:")
|
|
301
|
+
print(f"{'Provider':<12} {'Fee %':<8} {'Fee ETH':<12} {'Gas ETH':<12} {'Total':<12}")
|
|
302
|
+
print("-" * 56)
|
|
303
|
+
|
|
304
|
+
for comp in comparisons:
|
|
305
|
+
print(
|
|
306
|
+
f"{comp['provider']:<12} "
|
|
307
|
+
f"{comp['fee_rate']:.2f}%{'':<4} "
|
|
308
|
+
f"{comp['fee_amount']:.4f}{'':<6} "
|
|
309
|
+
f"{comp['gas_cost_eth']:.4f}{'':<6} "
|
|
310
|
+
f"{comp['total_cost_eth']:.4f}"
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
if __name__ == "__main__":
|
|
315
|
+
demo()
|