@intentsolutionsio/flash-loan-simulator 1.0.0 → 1.0.5

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.
@@ -3,6 +3,7 @@
3
3
  ## RPC Connection Errors
4
4
 
5
5
  ### ERR-001: RPC Connection Timeout
6
+
6
7
  ```
7
8
  Error: Request timed out after 30000ms
8
9
  ```
@@ -10,16 +11,20 @@ Error: Request timed out after 30000ms
10
11
  **Cause:** Network latency or overloaded RPC endpoint.
11
12
 
12
13
  **Solution:**
14
+
13
15
  1. Switch to a different RPC endpoint:
16
+
14
17
  ```yaml
15
18
  rpc_endpoints:
16
- ethereum: "https://rpc.ankr.com/eth" # Free, no signup
19
+ ethereum: "eth" # Free, no signup
17
20
  backup: "https://eth.llamarpc.com" # Alternative
18
21
  ```
22
+
19
23
  2. Increase timeout in settings.yaml
20
24
  3. Check network connectivity
21
25
 
22
26
  ### ERR-002: RPC Rate Limited
27
+
23
28
  ```
24
29
  Error: 429 Too Many Requests
25
30
  ```
@@ -27,11 +32,14 @@ Error: 429 Too Many Requests
27
32
  **Cause:** Free RPC tier limits exceeded.
28
33
 
29
34
  **Solution:**
35
+
30
36
  1. Add delay between requests:
37
+
31
38
  ```python
32
39
  import time
33
40
  time.sleep(0.5) # 500ms between calls
34
41
  ```
42
+
35
43
  2. Switch to a different free RPC (rotate between Ankr, Chainstack, Llamarpc)
36
44
  3. Use a private RPC for production (Alchemy, Chainstack, Infura, or QuickNode)
37
45
 
@@ -40,6 +48,7 @@ Error: 429 Too Many Requests
40
48
  ## Simulation Errors
41
49
 
42
50
  ### ERR-010: Insufficient Liquidity
51
+
43
52
  ```
44
53
  Error: Loan amount exceeds available liquidity
45
54
  Provider: Aave V3
@@ -50,11 +59,13 @@ Requested: 100,000 ETH
50
59
  **Cause:** Flash loan amount exceeds pool depth.
51
60
 
52
61
  **Solution:**
62
+
53
63
  1. Reduce loan amount
54
64
  2. Split across multiple providers
55
65
  3. Use a different asset with more liquidity
56
66
 
57
67
  ### ERR-011: Unsupported Asset
68
+
58
69
  ```
59
70
  Error: Asset 'SHIB' not supported by provider 'dYdX'
60
71
  ```
@@ -62,14 +73,18 @@ Error: Asset 'SHIB' not supported by provider 'dYdX'
62
73
  **Cause:** Provider doesn't support the requested token.
63
74
 
64
75
  **Solution:**
76
+
65
77
  1. Check provider's supported assets:
78
+
66
79
  ```bash
67
80
  python flash_simulator.py compare SHIB 1000
68
81
  ```
82
+
69
83
  2. Use a different provider (Aave V3 supports more assets)
70
84
  3. Swap to a supported intermediary token
71
85
 
72
86
  ### ERR-012: Chain Mismatch
87
+
73
88
  ```
74
89
  Error: Provider 'dYdX' not available on chain 'polygon'
75
90
  ```
@@ -77,6 +92,7 @@ Error: Provider 'dYdX' not available on chain 'polygon'
77
92
  **Cause:** Provider only operates on specific chains.
78
93
 
79
94
  **Solution:**
95
+
80
96
  1. Check provider chains:
81
97
  - dYdX: Ethereum only
82
98
  - Aave V3: Ethereum, Polygon, Arbitrum, Optimism, Avalanche
@@ -88,6 +104,7 @@ Error: Provider 'dYdX' not available on chain 'polygon'
88
104
  ## Strategy Errors
89
105
 
90
106
  ### ERR-020: No Profitable Route
107
+
91
108
  ```
92
109
  Error: All simulated routes result in loss
93
110
  Best route: -0.05 ETH (-$125.00)
@@ -96,12 +113,14 @@ Best route: -0.05 ETH (-$125.00)
96
113
  **Cause:** Current market conditions don't support the arbitrage.
97
114
 
98
115
  **Solution:**
116
+
99
117
  1. Wait for better price differences
100
118
  2. Try different token pairs
101
119
  3. Consider smaller trade sizes (less slippage)
102
120
  4. Check for stale price data
103
121
 
104
122
  ### ERR-021: Negative Profit Margin
123
+
105
124
  ```
106
125
  Warning: Strategy unprofitable after costs
107
126
  Gross profit: +0.10 ETH
@@ -112,12 +131,14 @@ Net profit: -0.05 ETH
112
131
  **Cause:** Costs (gas + fees) exceed the arbitrage opportunity.
113
132
 
114
133
  **Solution:**
134
+
115
135
  1. Use a zero-fee provider (dYdX)
116
136
  2. Wait for lower gas prices
117
137
  3. Increase trade size to improve margin ratio
118
138
  4. Find larger price discrepancies
119
139
 
120
140
  ### ERR-022: High MEV Competition
141
+
121
142
  ```
122
143
  Warning: MEV competition score 85/100
123
144
  This pair has high bot activity
@@ -126,6 +147,7 @@ This pair has high bot activity
126
147
  **Cause:** ETH/USDC and similar pairs are heavily competed.
127
148
 
128
149
  **Solution:**
150
+
129
151
  1. Use Flashbots Protect for private transactions
130
152
  2. Try less-competitive pairs
131
153
  3. Consider smaller, faster trades
@@ -136,6 +158,7 @@ This pair has high bot activity
136
158
  ## Risk Assessment Errors
137
159
 
138
160
  ### ERR-030: Critical Risk Score
161
+
139
162
  ```
140
163
  Warning: Risk score 78/100 (CRITICAL)
141
164
  Viability: NO-GO
@@ -144,14 +167,18 @@ Viability: NO-GO
144
167
  **Cause:** Multiple risk factors are elevated.
145
168
 
146
169
  **Solution:**
170
+
147
171
  1. Review individual risk factors:
172
+
148
173
  ```bash
149
174
  python flash_simulator.py arbitrage ETH USDC 100 --risk-analysis
150
175
  ```
176
+
151
177
  2. Address the highest-scoring factors
152
178
  3. Consider a different strategy or timing
153
179
 
154
180
  ### ERR-031: Protocol Risk Warning
181
+
155
182
  ```
156
183
  Warning: Using unaudited protocol 'NewDEX'
157
184
  Protocol risk score: 80/100
@@ -160,6 +187,7 @@ Protocol risk score: 80/100
160
187
  **Cause:** Less-established protocol increases smart contract risk.
161
188
 
162
189
  **Solution:**
190
+
163
191
  1. Verify protocol audits before using
164
192
  2. Stick to well-audited protocols (Aave, Uniswap, Balancer)
165
193
  3. Limit exposure to newer protocols
@@ -169,6 +197,7 @@ Protocol risk score: 80/100
169
197
  ## Gas Estimation Errors
170
198
 
171
199
  ### ERR-040: Gas Price Spike
200
+
172
201
  ```
173
202
  Warning: Current gas price 150 gwei (normal: 30 gwei)
174
203
  Strategy breakeven gas: 45 gwei
@@ -177,16 +206,20 @@ Strategy breakeven gas: 45 gwei
177
206
  **Cause:** Network congestion causing unprofitable gas costs.
178
207
 
179
208
  **Solution:**
209
+
180
210
  1. Wait for gas prices to normalize
181
211
  2. Use gas oracles to estimate future prices:
212
+
182
213
  ```python
183
214
  # Check if current gas is above breakeven
184
215
  if current_gas > breakeven_gas:
185
216
  print("Wait for lower gas prices")
186
217
  ```
218
+
187
219
  3. Set gas price limits in execution scripts
188
220
 
189
221
  ### ERR-041: Gas Estimation Failed
222
+
190
223
  ```
191
224
  Error: Could not estimate gas for transaction
192
225
  ```
@@ -194,6 +227,7 @@ Error: Could not estimate gas for transaction
194
227
  **Cause:** Transaction would revert on-chain.
195
228
 
196
229
  **Solution:**
230
+
197
231
  1. Check token approvals
198
232
  2. Verify sufficient balances
199
233
  3. Test on forked mainnet first
@@ -204,11 +238,13 @@ Error: Could not estimate gas for transaction
204
238
  ## Input Validation Errors
205
239
 
206
240
  ### ERR-050: Invalid Amount
241
+
207
242
  ```
208
243
  Error: Invalid amount 'abc' - must be numeric
209
244
  ```
210
245
 
211
246
  **Solution:**
247
+
212
248
  ```bash
213
249
  # Correct usage
214
250
  python flash_simulator.py arbitrage ETH USDC 100 # Integer
@@ -216,6 +252,7 @@ python flash_simulator.py arbitrage ETH USDC 100.5 # Decimal
216
252
  ```
217
253
 
218
254
  ### ERR-051: Invalid Token Symbol
255
+
219
256
  ```
220
257
  Error: Unknown token 'ETHEREUM' - did you mean 'ETH'?
221
258
  ```
@@ -224,11 +261,13 @@ Error: Unknown token 'ETHEREUM' - did you mean 'ETH'?
224
261
  Use standard token symbols: ETH, WETH, USDC, USDT, DAI, WBTC
225
262
 
226
263
  ### ERR-052: Invalid Provider
264
+
227
265
  ```
228
266
  Error: Unknown provider 'aavev3' - valid options: aave, dydx, balancer, uniswap
229
267
  ```
230
268
 
231
269
  **Solution:**
270
+
232
271
  ```bash
233
272
  # Use lowercase provider names
234
273
  python flash_simulator.py arbitrage ETH USDC 100 --provider aave
@@ -239,6 +278,7 @@ python flash_simulator.py arbitrage ETH USDC 100 --provider aave
239
278
  ## Output/Format Errors
240
279
 
241
280
  ### ERR-060: JSON Export Failed
281
+
242
282
  ```
243
283
  Error: Cannot serialize Decimal to JSON
244
284
  ```
@@ -246,6 +286,7 @@ Error: Cannot serialize Decimal to JSON
246
286
  **Cause:** Custom Decimal types not handled.
247
287
 
248
288
  **Solution:** The simulator handles this automatically. If using custom code:
289
+
249
290
  ```python
250
291
  from formatters import DecimalEncoder
251
292
  import json
@@ -257,7 +298,9 @@ json.dumps(data, cls=DecimalEncoder)
257
298
  ## Recovery Procedures
258
299
 
259
300
  ### Complete Reset
301
+
260
302
  If simulator is in an inconsistent state:
303
+
261
304
  ```bash
262
305
  # Clear any cached data
263
306
  rm -rf ~/.cache/flash-simulator/
@@ -267,14 +310,16 @@ pip install --upgrade web3 httpx
267
310
  ```
268
311
 
269
312
  ### Verify RPC Connectivity
313
+
270
314
  ```bash
271
315
  # Test RPC endpoint
272
316
  curl -X POST -H "Content-Type: application/json" \
273
317
  --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
274
- https://rpc.ankr.com/eth
318
+ eth
275
319
  ```
276
320
 
277
321
  ### Test Simulation Engine
322
+
278
323
  ```bash
279
324
  # Run built-in demo
280
325
  python strategy_engine.py
@@ -14,6 +14,7 @@ python flash_simulator.py arbitrage ETH USDC 100 \
14
14
  ```
15
15
 
16
16
  **Expected Output:**
17
+
17
18
  ```
18
19
  ==================== FLASH LOAN SIMULATION ====================
19
20
 
@@ -70,6 +71,7 @@ python flash_simulator.py compare ETH 100
70
71
  ```
71
72
 
72
73
  **Expected Output:**
74
+
73
75
  ```
74
76
  =================== PROVIDER COMPARISON ===================
75
77
 
@@ -97,6 +99,7 @@ python flash_simulator.py arbitrage ETH USDC 100 --full
97
99
  ```
98
100
 
99
101
  **Expected Output:**
102
+
100
103
  ```
101
104
  ==================== FLASH LOAN SIMULATION ====================
102
105
  [... strategy result ...]
@@ -174,6 +177,7 @@ python flash_simulator.py triangular ETH USDC WBTC ETH --amount 50
174
177
  ```
175
178
 
176
179
  **Expected Output:**
180
+
177
181
  ```
178
182
  ==================== FLASH LOAN SIMULATION ====================
179
183
 
@@ -240,6 +244,7 @@ python flash_simulator.py liquidation \
240
244
  ```
241
245
 
242
246
  **Expected Output:**
247
+
243
248
  ```
244
249
  ==================== FLASH LOAN SIMULATION ====================
245
250
 
@@ -296,6 +301,7 @@ python flash_simulator.py arbitrage ETH USDC 100 --full --output json > simulati
296
301
  ```
297
302
 
298
303
  **Example JSON Output:**
304
+
299
305
  ```json
300
306
  {
301
307
  "simulation": {
@@ -369,6 +375,7 @@ python flash_simulator.py arbitrage ETH USDC 100 \
369
375
  ```
370
376
 
371
377
  **Key Difference:**
378
+
372
379
  - Flash loan fee: 0.00 ETH (vs 0.09 ETH with Aave)
373
380
  - Net profit increases by ~$225
374
381
 
@@ -386,6 +393,7 @@ python flash_simulator.py arbitrage ETH USDC 100 \
386
393
  ```
387
394
 
388
395
  This adjusts:
396
+
389
397
  - All USD calculations use $3,500/ETH
390
398
  - Gas costs calculated at 50 gwei
391
399
  - Breakeven gas price recalculated
@@ -482,6 +490,7 @@ if __name__ == "__main__":
482
490
  ## Integration Patterns
483
491
 
484
492
  ### Batch Analysis
493
+
485
494
  ```bash
486
495
  # Analyze multiple pairs
487
496
  for pair in "ETH-USDC" "WBTC-ETH" "ETH-DAI"; do
@@ -491,6 +500,7 @@ done
491
500
  ```
492
501
 
493
502
  ### Pipeline Integration
503
+
494
504
  ```bash
495
505
  # Feed into analysis pipeline
496
506
  python flash_simulator.py arbitrage ETH USDC 100 --output json | \
@@ -498,6 +508,7 @@ python flash_simulator.py arbitrage ETH USDC 100 --output json | \
498
508
  ```
499
509
 
500
510
  ### Automated Monitoring
511
+
501
512
  ```bash
502
513
  # Check profitability every minute
503
514
  while true; do
@@ -523,6 +534,7 @@ All examples are for educational purposes only. Flash loan strategies involve si
523
534
  4. **Market Risk**: Prices change between simulation and execution
524
535
 
525
536
  Always:
537
+
526
538
  - Test on testnets first
527
539
  - Start with small amounts
528
540
  - Use MEV protection in production
@@ -40,6 +40,7 @@ When `--risk-analysis` is enabled, four risk factors are scored 0-100:
40
40
  - **Liquidity Risk** (0-100): Whether pool depth supports the trade size. Score > 80 means trade may move price significantly.
41
41
 
42
42
  Overall viability grades:
43
+
43
44
  - **A**: All risks low, high confidence
44
45
  - **B**: Moderate risks, proceed with caution
45
46
  - **C**: High risks, likely unprofitable
@@ -48,20 +49,25 @@ Overall viability grades:
48
49
  ## Output Modes
49
50
 
50
51
  **Quick Mode** (default):
52
+
51
53
  - Net profit/loss, provider recommendation, Go/No-Go verdict
52
54
 
53
55
  **Breakdown Mode** (`--breakdown`):
56
+
54
57
  - Step-by-step transaction flow, individual cost components, slippage estimates
55
58
 
56
59
  **Comparison Mode** (`--compare-providers`):
60
+
57
61
  - All providers ranked by net profit, fee differences, liquidity availability
58
62
 
59
63
  **Risk Analysis** (`--risk-analysis`):
64
+
60
65
  - Competition score, execution probability, protocol safety, overall viability grade
61
66
 
62
67
  ## Educational Disclaimer
63
68
 
64
69
  **FOR EDUCATIONAL PURPOSES ONLY.** Flash loan strategies involve significant risks:
70
+
65
71
  - Smart contract bugs can cause total loss of borrowed funds
66
72
  - MEV bots compete for the same opportunities with faster infrastructure
67
73
  - Gas costs can exceed profits, especially during network congestion
@@ -18,7 +18,6 @@ Usage:
18
18
  import argparse
19
19
  import sys
20
20
  from decimal import Decimal, InvalidOperation
21
- from typing import Optional
22
21
 
23
22
  from strategy_engine import (
24
23
  StrategyFactory,
@@ -77,77 +76,39 @@ EDUCATIONAL DISCLAIMER:
77
76
  subparsers = parser.add_subparsers(dest="command", help="Simulation type")
78
77
 
79
78
  # Arbitrage subcommand
80
- arb_parser = subparsers.add_parser(
81
- "arbitrage", help="Simple two-DEX arbitrage simulation"
82
- )
83
- arb_parser.add_argument(
84
- "input_token", help="Input token (e.g., ETH)"
85
- )
86
- arb_parser.add_argument(
87
- "output_token", help="Output token (e.g., USDC)"
88
- )
89
- arb_parser.add_argument(
90
- "amount", type=str, help="Loan amount"
91
- )
92
- arb_parser.add_argument(
93
- "--dex-buy", default="sushiswap", help="DEX to buy on (default: sushiswap)"
94
- )
95
- arb_parser.add_argument(
96
- "--dex-sell", default="uniswap", help="DEX to sell on (default: uniswap)"
97
- )
98
- arb_parser.add_argument(
99
- "--provider", default="aave", help="Flash loan provider (default: aave)"
100
- )
79
+ arb_parser = subparsers.add_parser("arbitrage", help="Simple two-DEX arbitrage simulation")
80
+ arb_parser.add_argument("input_token", help="Input token (e.g., ETH)")
81
+ arb_parser.add_argument("output_token", help="Output token (e.g., USDC)")
82
+ arb_parser.add_argument("amount", type=str, help="Loan amount")
83
+ arb_parser.add_argument("--dex-buy", default="sushiswap", help="DEX to buy on (default: sushiswap)")
84
+ arb_parser.add_argument("--dex-sell", default="uniswap", help="DEX to sell on (default: uniswap)")
85
+ arb_parser.add_argument("--provider", default="aave", help="Flash loan provider (default: aave)")
101
86
 
102
87
  # Triangular arbitrage subcommand
103
- tri_parser = subparsers.add_parser(
104
- "triangular", help="Multi-hop circular arbitrage simulation"
105
- )
106
- tri_parser.add_argument(
107
- "tokens", nargs="+", help="Token path (e.g., ETH USDC WBTC ETH)"
108
- )
109
- tri_parser.add_argument(
110
- "--amount", type=str, required=True, help="Loan amount"
111
- )
112
- tri_parser.add_argument(
113
- "--provider", default="aave", help="Flash loan provider (default: aave)"
114
- )
88
+ tri_parser = subparsers.add_parser("triangular", help="Multi-hop circular arbitrage simulation")
89
+ tri_parser.add_argument("tokens", nargs="+", help="Token path (e.g., ETH USDC WBTC ETH)")
90
+ tri_parser.add_argument("--amount", type=str, required=True, help="Loan amount")
91
+ tri_parser.add_argument("--provider", default="aave", help="Flash loan provider (default: aave)")
115
92
 
116
93
  # Liquidation subcommand
117
- liq_parser = subparsers.add_parser(
118
- "liquidation", help="Liquidation opportunity analysis"
119
- )
120
- liq_parser.add_argument(
121
- "--protocol", default="aave", help="Lending protocol (default: aave)"
122
- )
94
+ liq_parser = subparsers.add_parser("liquidation", help="Liquidation opportunity analysis")
95
+ liq_parser.add_argument("--protocol", default="aave", help="Lending protocol (default: aave)")
123
96
  liq_parser.add_argument(
124
97
  "--health-factor",
125
98
  type=float,
126
99
  default=0.95,
127
100
  help="Health factor threshold (default: 0.95)",
128
101
  )
129
- liq_parser.add_argument(
130
- "--collateral", default="ETH", help="Collateral asset (default: ETH)"
131
- )
132
- liq_parser.add_argument(
133
- "--debt", default="USDC", help="Debt asset (default: USDC)"
134
- )
135
- liq_parser.add_argument(
136
- "--amount", type=str, default="10", help="Debt amount to liquidate"
137
- )
138
- liq_parser.add_argument(
139
- "--provider", default="aave", help="Flash loan provider (default: aave)"
140
- )
102
+ liq_parser.add_argument("--collateral", default="ETH", help="Collateral asset (default: ETH)")
103
+ liq_parser.add_argument("--debt", default="USDC", help="Debt asset (default: USDC)")
104
+ liq_parser.add_argument("--amount", type=str, default="10", help="Debt amount to liquidate")
105
+ liq_parser.add_argument("--provider", default="aave", help="Flash loan provider (default: aave)")
141
106
 
142
107
  # Compare providers subcommand
143
- cmp_parser = subparsers.add_parser(
144
- "compare", help="Compare flash loan providers"
145
- )
108
+ cmp_parser = subparsers.add_parser("compare", help="Compare flash loan providers")
146
109
  cmp_parser.add_argument("asset", help="Asset to borrow (e.g., ETH)")
147
110
  cmp_parser.add_argument("amount", type=str, help="Amount to borrow")
148
- cmp_parser.add_argument(
149
- "--chain", default="ethereum", help="Chain (default: ethereum)"
150
- )
111
+ cmp_parser.add_argument("--chain", default="ethereum", help="Chain (default: ethereum)")
151
112
 
152
113
  # Global options
153
114
  for sub in [arb_parser, tri_parser, liq_parser]:
@@ -223,9 +184,7 @@ def run_arbitrage(args: argparse.Namespace) -> int:
223
184
  providers = None
224
185
 
225
186
  if args.full or hasattr(args, "compare_providers") and args.compare_providers:
226
- calculator = ProfitCalculator(
227
- eth_price_usd=args.eth_price, gas_price_gwei=args.gas_price
228
- )
187
+ calculator = ProfitCalculator(eth_price_usd=args.eth_price, gas_price_gwei=args.gas_price)
229
188
  breakdown = calculator.calculate_breakdown(result)
230
189
 
231
190
  if args.full or (hasattr(args, "risk_analysis") and args.risk_analysis):
@@ -234,9 +193,7 @@ def run_arbitrage(args: argparse.Namespace) -> int:
234
193
 
235
194
  if args.full or (hasattr(args, "compare_providers") and args.compare_providers):
236
195
  manager = ProviderManager()
237
- providers = manager.compare_providers(
238
- args.input_token.upper(), amount, "ethereum"
239
- )
196
+ providers = manager.compare_providers(args.input_token.upper(), amount, "ethereum")
240
197
 
241
198
  # Format output
242
199
  output_result(args, result, breakdown, assessment, providers)
@@ -276,18 +233,14 @@ def run_triangular(args: argparse.Namespace) -> int:
276
233
  providers = None
277
234
 
278
235
  if args.full:
279
- calculator = ProfitCalculator(
280
- eth_price_usd=args.eth_price, gas_price_gwei=args.gas_price
281
- )
236
+ calculator = ProfitCalculator(eth_price_usd=args.eth_price, gas_price_gwei=args.gas_price)
282
237
  breakdown = calculator.calculate_breakdown(result)
283
238
 
284
239
  assessor = RiskAssessor(eth_price_usd=args.eth_price)
285
240
  assessment = assessor.assess(result)
286
241
 
287
242
  manager = ProviderManager()
288
- providers = manager.compare_providers(
289
- args.tokens[0].upper(), amount, "ethereum"
290
- )
243
+ providers = manager.compare_providers(args.tokens[0].upper(), amount, "ethereum")
291
244
 
292
245
  if hasattr(args, "risk_analysis") and args.risk_analysis:
293
246
  assessor = RiskAssessor(eth_price_usd=args.eth_price)
@@ -330,18 +283,14 @@ def run_liquidation(args: argparse.Namespace) -> int:
330
283
  providers = None
331
284
 
332
285
  if args.full:
333
- calculator = ProfitCalculator(
334
- eth_price_usd=args.eth_price, gas_price_gwei=args.gas_price
335
- )
286
+ calculator = ProfitCalculator(eth_price_usd=args.eth_price, gas_price_gwei=args.gas_price)
336
287
  breakdown = calculator.calculate_breakdown(result)
337
288
 
338
289
  assessor = RiskAssessor(eth_price_usd=args.eth_price)
339
290
  assessment = assessor.assess(result)
340
291
 
341
292
  manager = ProviderManager()
342
- providers = manager.compare_providers(
343
- args.debt.upper(), amount, "ethereum"
344
- )
293
+ providers = manager.compare_providers(args.debt.upper(), amount, "ethereum")
345
294
 
346
295
  if hasattr(args, "risk_analysis") and args.risk_analysis:
347
296
  assessor = RiskAssessor(eth_price_usd=args.eth_price)
@@ -362,9 +311,7 @@ def run_compare(args: argparse.Namespace) -> int:
362
311
  return 1
363
312
 
364
313
  manager = ProviderManager()
365
- providers = manager.compare_providers(
366
- args.asset.upper(), amount, args.chain.lower()
367
- )
314
+ providers = manager.compare_providers(args.asset.upper(), amount, args.chain.lower())
368
315
 
369
316
  if not providers:
370
317
  print(
@@ -375,7 +322,7 @@ def run_compare(args: argparse.Namespace) -> int:
375
322
 
376
323
  # Format output
377
324
  if args.output == "json":
378
- json_fmt = JSONFormatter()
325
+ JSONFormatter()
379
326
  # Create a minimal result for JSON output
380
327
  output = {
381
328
  "comparison": {
@@ -396,6 +343,7 @@ def run_compare(args: argparse.Namespace) -> int:
396
343
  ],
397
344
  }
398
345
  import json
346
+
399
347
  print(json.dumps(output, indent=2))
400
348
  else:
401
349
  console = ConsoleFormatter()
@@ -436,11 +384,7 @@ def output_result(
436
384
 
437
385
  # Show provider comparison if calculated
438
386
  if providers:
439
- print(
440
- console.format_provider_comparison(
441
- providers, result.loan_asset, result.loan_amount
442
- )
443
- )
387
+ print(console.format_provider_comparison(providers, result.loan_asset, result.loan_amount))
444
388
 
445
389
  # Always show quick summary at end
446
390
  print(console.format_quick_summary(result, assessment))