Docs
Docs/Python SDK

Python SDK

Cross-chain route discovery, simulation, and analysis in Python.

Installation

Requires Python 3.10+.

bash
1git clone https://github.com/MNMX-labs/mnmx.git
2cd mnmx/sdk/python
3pip install -e .

This installs the mnmx package in development mode, so changes to the source are immediately reflected. Dependencies include httpx for async HTTP, pydanticfor data validation, and numpy for Monte Carlo simulations.

Verify Installation

bash
1# Verify the installation
2python -c "import mnmx; print(mnmx.__version__)"
3# 0.4.2
4
5# Run the test suite
6cd mnmx/sdk/python
7python -m pytest tests/ -v

Configuration

Set your RPC endpoints via environment variables or pass them directly to the router:

bash
1# Environment variables
2export ETH_RPC_URL="https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"
3export SOL_RPC_URL="https://mainnet.helius-rpc.com/?api-key=YOUR_KEY"
4export ARB_RPC_URL="https://arb-mainnet.g.alchemy.com/v2/YOUR_KEY"
python
1# Or configure programmatically
2from mnmx import MnmxRouter
3
4router = MnmxRouter(
5 strategy="minimax",
6 chains={
7 "ethereum": {"rpc": "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"},
8 "solana": {"rpc": "https://mainnet.helius-rpc.com/?api-key=YOUR_KEY"},
9 "arbitrum": {"rpc": "https://arb-mainnet.g.alchemy.com/v2/YOUR_KEY"},
10 },
11)

Basic Usage

python
1from mnmx import MnmxRouter
2
3router = MnmxRouter(strategy="minimax")
4
5route = router.find_route(
6 from_chain="ethereum",
7 from_token="ETH",
8 amount="1.0",
9 to_chain="solana",
10 to_token="SOL",
11)
12
13print(f"Path: {' → '.join(route.path)}")
14print(f"Expected output: {route.expected_output} SOL")
15print(f"Guaranteed minimum: {route.guaranteed_minimum} SOL")
16print(f"Total fees: {route.total_fees}")
17print(f"Estimated time: {route.estimated_time}s")
18print(f"Minimax score: {route.minimax_score:.4f}")
19print(f"Strategy: {route.strategy}")
20
21# Access individual hops
22for i, hop in enumerate(route.hops):
23 print(f" Hop {i+1}: {hop.provider} ({hop.type})")
24 print(f" {hop.from_chain}:{hop.from_token}{hop.to_chain}:{hop.to_token}")
25 print(f" Fee: {hop.fee} | Time: {hop.estimated_time}s")

Compare All Routes

python
1routes = router.find_all_routes(
2 from_chain="ethereum",
3 from_token="ETH",
4 amount="1.0",
5 to_chain="solana",
6 to_token="SOL",
7)
8
9print(f"Found {len(routes)} viable routes\n")
10
11for route in routes:
12 print(f"{' → '.join(route.path)}")
13 print(f" Expected: {route.expected_output} SOL")
14 print(f" Minimum: {route.guaranteed_minimum} SOL")
15 print(f" Fees: ${route.total_fees_usd}")
16 print(f" Time: {route.estimated_time}s")
17 print(f" Score: {route.minimax_score:.4f}")
18 print()
19
20# Sort by different criteria
21cheapest = sorted(routes, key=lambda r: float(r.total_fees_usd))[0]
22fastest = sorted(routes, key=lambda r: r.estimated_time)[0]
23safest = sorted(routes, key=lambda r: float(r.guaranteed_minimum), reverse=True)[0]
24
25print(f"Cheapest route: {' → '.join(cheapest.path)} (${cheapest.total_fees_usd})")
26print(f"Fastest route: {' → '.join(fastest.path)} ({fastest.estimated_time}s)")
27print(f"Safest route: {' → '.join(safest.path)} (min: {safest.guaranteed_minimum} SOL)")

Route Options

Fine-tune route discovery with additional parameters:

python
1# Exclude specific bridges
2route = router.find_route(
3 from_chain="ethereum",
4 from_token="ETH",
5 amount="1.0",
6 to_chain="solana",
7 to_token="SOL",
8 exclude_bridges=["allbridge"],
9)
10
11# Set maximum transfer time
12route = router.find_route(
13 from_chain="ethereum",
14 from_token="USDC",
15 amount="5000",
16 to_chain="solana",
17 to_token="USDC",
18 deadline=300, # Max 5 minutes
19)
20
21# Set minimum output threshold
22route = router.find_route(
23 from_chain="ethereum",
24 from_token="ETH",
25 amount="1.0",
26 to_chain="solana",
27 to_token="SOL",
28 min_output="13.5", # Reject routes with guaranteed minimum below 13.5 SOL
29)
30
31# Prefer a specific bridge (still evaluated by minimax)
32route = router.find_route(
33 from_chain="ethereum",
34 from_token="USDC",
35 amount="10000",
36 to_chain="arbitrum",
37 to_token="USDC",
38 preferred_bridge="debridge",
39)

Strategy Comparison

Compare how different strategies evaluate the same transfer:

python
1from mnmx import MnmxRouter
2
3strategies = ["minimax", "cheapest", "fastest", "safest"]
4
5for strategy in strategies:
6 router = MnmxRouter(strategy=strategy)
7 route = router.find_route(
8 from_chain="ethereum",
9 from_token="ETH",
10 amount="5.0",
11 to_chain="solana",
12 to_token="SOL",
13 )
14 print(f"Strategy: {strategy}")
15 print(f" Route: {' → '.join(route.path)}")
16 print(f" Expected: {route.expected_output} SOL")
17 print(f" Guaranteed: {route.guaranteed_minimum} SOL")
18 print(f" Fees: ${route.total_fees_usd}")
19 print(f" Time: {route.estimated_time}s")
20 print()
21
22# Example output:
23# Strategy: minimax
24# Route: uniswap-v3 → debridge → jupiter
25# Expected: 71.42 SOL
26# Guaranteed: 69.25 SOL
27# Fees: $18.50
28# Time: 110s
29#
30# Strategy: cheapest
31# Route: uniswap-v3 → allbridge → jupiter
32# Expected: 71.28 SOL
33# Guaranteed: 68.10 SOL
34# Fees: $12.80
35# Time: 300s
36#
37# Strategy: fastest
38# Route: debridge-dln → jupiter
39# Expected: 71.15 SOL
40# Guaranteed: 68.50 SOL
41# Fees: $22.00
42# Time: 65s
43#
44# Strategy: safest
45# Route: uniswap-v3 → wormhole → jupiter
46# Expected: 70.95 SOL
47# Guaranteed: 69.80 SOL
48# Fees: $25.00
49# Time: 780s

Route Simulation

Simulate routes under various market conditions without executing:

python
1from mnmx import RouteSimulator
2
3sim = RouteSimulator()
4
5# Simulate with default adversarial conditions
6result = sim.simulate(route)
7print(f"Default simulation: {result.output} SOL")
8print(f"Fees: {result.total_fees}")
9print(f"Time: {result.total_time}s")
10
11# Simulate with custom adversarial conditions
12result = sim.simulate(route, conditions={
13 "slippage_multiplier": 2.0, # 2x quoted slippage
14 "gas_multiplier": 1.5, # 1.5x current gas
15 "bridge_delay_multiplier": 3.0, # 3x median delay
16 "mev_extraction": 0.003, # 0.3% MEV
17 "price_movement": 0.005, # 0.5% adverse price change
18})
19print(f"Adversarial simulation: {result.output} SOL")
20
21# Simulate extreme conditions
22result = sim.simulate(route, conditions={
23 "slippage_multiplier": 5.0, # 5x slippage (extreme)
24 "gas_multiplier": 3.0, # 3x gas (network congestion event)
25 "bridge_delay_multiplier": 10.0,# 10x delay (bridge degradation)
26 "mev_extraction": 0.01, # 1% MEV (sandwich attack)
27 "price_movement": 0.02, # 2% price move (volatile market)
28})
29print(f"Extreme simulation: {result.output} SOL")
30
31# Compare simulation output against quoted values
32print(f"\nQuoted output: {route.expected_output} SOL")
33print(f"Guaranteed minimum: {route.guaranteed_minimum} SOL")
34print(f"Default sim: {result_default.output} SOL")
35print(f"Adversarial sim: {result_adversarial.output} SOL")
36print(f"Extreme sim: {result_extreme.output} SOL")

Scenario-Based Simulation

python
1# Simulate specific real-world scenarios
2scenarios = {
3 "normal": {
4 "slippage_multiplier": 1.0,
5 "gas_multiplier": 1.0,
6 "bridge_delay_multiplier": 1.0,
7 "mev_extraction": 0.001,
8 "price_movement": 0.001,
9 },
10 "nft_mint_gas_spike": {
11 "slippage_multiplier": 1.2,
12 "gas_multiplier": 4.0, # Gas spikes during popular mints
13 "bridge_delay_multiplier": 1.5,
14 "mev_extraction": 0.002,
15 "price_movement": 0.003,
16 },
17 "market_crash": {
18 "slippage_multiplier": 3.0, # Liquidity dries up
19 "gas_multiplier": 2.5,
20 "bridge_delay_multiplier": 2.0,
21 "mev_extraction": 0.005,
22 "price_movement": 0.05, # 5% adverse price movement
23 },
24 "bridge_congestion": {
25 "slippage_multiplier": 1.5,
26 "gas_multiplier": 1.2,
27 "bridge_delay_multiplier": 8.0, # Severe bridge delays
28 "mev_extraction": 0.002,
29 "price_movement": 0.01,
30 },
31}
32
33for name, conditions in scenarios.items():
34 result = sim.simulate(route, conditions=conditions)
35 print(f"{name:25s}{result.output:>8s} SOL (time: {result.total_time:>5}s)")

Monte Carlo Analysis

Run thousands of simulations with randomized conditions to understand the outcome distribution:

python
1mc = sim.monte_carlo(
2 route=route,
3 iterations=10_000,
4 seed=42,
5)
6
7print(f"Mean output: {mc.mean_output:.4f} SOL")
8print(f"Median output: {mc.median_output:.4f} SOL")
9print(f"Std deviation: {mc.std_output:.4f} SOL")
10print(f"5th percentile: {mc.percentile_5:.4f} SOL")
11print(f"25th percentile: {mc.percentile_25:.4f} SOL")
12print(f"75th percentile: {mc.percentile_75:.4f} SOL")
13print(f"95th percentile: {mc.percentile_95:.4f} SOL")
14print(f"Worst observed: {mc.min_output:.4f} SOL")
15print(f"Best observed: {mc.max_output:.4f} SOL")
16print(f"Guaranteed min: {route.guaranteed_minimum} SOL")
17print(f"Below guarantee: {mc.below_guarantee_pct:.2f}%")
18
19# Distribution histogram (text-based)
20mc.print_histogram(bins=20)
21
22# Example output:
23# 11.20 - 11.50 | ## (0.3%)
24# 11.50 - 11.80 | ### (0.5%)
25# 11.80 - 12.10 | ##### (1.2%)
26# 12.10 - 12.40 | ######## (2.1%)
27# 12.40 - 12.70 | ########### (3.8%)
28# 12.70 - 13.00 | ################ (5.5%)
29# 13.00 - 13.30 | ##################### (7.2%)
30# 13.30 - 13.60 | ############################ (9.8%)
31# 13.60 - 13.90 | ################################(12.1%) ← guaranteed minimum
32# 13.90 - 14.20 | ################################(14.5%)
33# 14.20 - 14.50 | ################################(15.2%)
34# 14.50 - 14.80 | ############################ (11.8%)
35# 14.80 - 15.10 | ##################### (8.2%)
36# ...

Monte Carlo Comparison of Routes

python
1# Compare multiple routes using Monte Carlo
2routes = router.find_all_routes(
3 from_chain="ethereum",
4 from_token="ETH",
5 amount="5.0",
6 to_chain="solana",
7 to_token="SOL",
8)
9
10print(f"{'Route':<40} {'Mean':>8} {'P5':>8} {'P95':>8} {'Min':>8} {'Guarantee':>10}")
11print("-" * 90)
12
13for route in routes[:5]: # Top 5 routes
14 mc = sim.monte_carlo(route=route, iterations=5_000, seed=42)
15 path = ' → '.join(route.path)[:38]
16 print(
17 f"{path:<40} "
18 f"{mc.mean_output:>8.2f} "
19 f"{mc.percentile_5:>8.2f} "
20 f"{mc.percentile_95:>8.2f} "
21 f"{mc.min_output:>8.2f} "
22 f"{route.guaranteed_minimum:>10}"
23 )
24
25# Example output:
26# Route Mean P5 P95 Min Guarantee
27# ------------------------------------------------------------------------------------------
28# uniswap-v3 → debridge → jupiter 71.28 69.15 72.80 67.50 69.25
29# wormhole → jupiter 71.05 67.80 73.20 64.20 68.10
30# uniswap-v3 → allbridge → jupiter 70.95 68.50 72.40 66.80 68.50
31# layerzero → jupiter 70.80 67.20 73.10 63.50 67.80
32# uniswap-v3 → wormhole → orca 70.60 68.90 72.10 67.20 69.80

Batch Route Analysis

Compare strategies across multiple token pairs:

python
1from mnmx import BatchAnalyzer
2
3analyzer = BatchAnalyzer(router)
4
5pairs = [
6 ("ethereum", "ETH", "solana", "SOL", "1.0"),
7 ("ethereum", "USDC", "solana", "USDC", "5000"),
8 ("ethereum", "USDC", "arbitrum", "USDC", "10000"),
9 ("solana", "SOL", "base", "ETH", "10.0"),
10 ("arbitrum", "ETH", "polygon", "MATIC","0.5"),
11 ("ethereum", "WBTC", "avalanche","AVAX", "0.1"),
12]
13
14report = analyzer.compare_strategies(
15 pairs=pairs,
16 strategies=["minimax", "cheapest", "fastest", "safest"],
17)
18
19# Print summary table
20print(report.summary())
21
22# Detailed per-pair analysis
23for pair_result in report.results:
24 print(f"\n{pair_result.from_chain}:{pair_result.from_token} → "
25 f"{pair_result.to_chain}:{pair_result.to_token}")
26 for strategy, route in pair_result.routes.items():
27 print(f" {strategy:10s}: {route.expected_output:>10s} "
28 f"(min: {route.guaranteed_minimum:>10s}, "
29 f"fees: ${route.total_fees_usd:>6s}, "
30 f"time: {route.estimated_time:>4}s)")
31
32# Export results
33report.to_csv("route_comparison.csv")
34report.to_json("route_comparison.json")

Historical Route Analysis

python
1from mnmx import HistoricalAnalyzer
2
3analyzer = HistoricalAnalyzer(router)
4
5# Analyze how routes would have performed over the past 24 hours
6# by replaying historical state snapshots
7analysis = analyzer.backtest(
8 from_chain="ethereum",
9 from_token="ETH",
10 amount="1.0",
11 to_chain="solana",
12 to_token="SOL",
13 period="24h",
14 interval="1h", # Sample every hour
15 strategies=["minimax", "cheapest"],
16)
17
18print(f"Samples: {analysis.sample_count}")
19print(f"\nMinimax strategy:")
20print(f" Avg output: {analysis.minimax.avg_output:.4f} SOL")
21print(f" Worst output: {analysis.minimax.min_output:.4f} SOL")
22print(f" Best output: {analysis.minimax.max_output:.4f} SOL")
23print(f" Avg fees: ${analysis.minimax.avg_fees:.2f}")
24print(f"\nCheapest strategy:")
25print(f" Avg output: {analysis.cheapest.avg_output:.4f} SOL")
26print(f" Worst output: {analysis.cheapest.min_output:.4f} SOL")
27print(f" Best output: {analysis.cheapest.max_output:.4f} SOL")
28print(f" Avg fees: ${analysis.cheapest.avg_fees:.2f}")
29
30# Minimax advantage: how often does minimax produce a better
31# worst-case outcome than cheapest?
32print(f"\nMinimax had better worst-case: "
33 f"{analysis.minimax_advantage_pct:.1f}% of samples")

Bridge Health Monitoring

python
1from mnmx import BridgeMonitor
2
3monitor = BridgeMonitor(router)
4
5# Get current health status of all bridges
6health = monitor.get_all_health()
7
8for bridge, status in health.items():
9 print(f"{bridge}:")
10 print(f" Online: {status.online}")
11 print(f" Congestion: {status.congestion}")
12 print(f" Success rate: {status.success_rate:.1%}")
13 print(f" Median time: {status.median_confirm_time}s")
14 print(f" Pending: {status.pending_transfers}")
15 print()
16
17# Get liquidity info for a specific bridge and token
18liquidity = monitor.get_liquidity("allbridge", "solana", "USDC")
19print(f"Allbridge USDC on Solana:")
20print(f" Available: ${liquidity.available}")
21print(f" Total: ${liquidity.total}")
22print(f" Util: {liquidity.utilization:.1f}%")

Async Usage

The Python SDK supports async operations for non-blocking integration:

python
1import asyncio
2from mnmx import AsyncMnmxRouter
3
4async def main():
5 router = AsyncMnmxRouter(strategy="minimax")
6
7 # Find routes for multiple pairs concurrently
8 tasks = [
9 router.find_route(
10 from_chain="ethereum", from_token="ETH", amount="1.0",
11 to_chain="solana", to_token="SOL",
12 ),
13 router.find_route(
14 from_chain="ethereum", from_token="USDC", amount="5000",
15 to_chain="arbitrum", to_token="USDC",
16 ),
17 router.find_route(
18 from_chain="solana", from_token="SOL", amount="10.0",
19 to_chain="base", to_token="ETH",
20 ),
21 ]
22
23 routes = await asyncio.gather(*tasks)
24
25 for route in routes:
26 print(f"{' → '.join(route.path)}: {route.expected_output}")
27
28asyncio.run(main())

CLI

The Python SDK includes a command-line interface for quick lookups and scripting:

bash
1# Find optimal route
2mnmx route --from ethereum:ETH:1.0 --to solana:SOL
3
4# Compare all routes
5mnmx route --from ethereum:ETH:1.0 --to solana:SOL --all
6
7# Use a specific strategy
8mnmx route --from ethereum:ETH:1.0 --to solana:SOL --strategy cheapest
9
10# Monte Carlo simulation
11mnmx simulate --from ethereum:ETH:1.0 --to solana:SOL --iterations 10000
12
13# Compare strategies
14mnmx compare --from ethereum:ETH:1.0 --to solana:SOL
15
16# Check bridge health
17mnmx health
18mnmx health --bridge wormhole
19
20# Check liquidity
21mnmx liquidity --bridge allbridge --chain solana --token USDC
22
23# Output as JSON (for scripting)
24mnmx route --from ethereum:ETH:1.0 --to solana:SOL --json
25
26# Verbose output with debug logging
27mnmx route --from ethereum:ETH:1.0 --to solana:SOL -v

CLI Output Examples

text
1$ mnmx route --from ethereum:ETH:1.0 --to solana:SOL
2
3MNMX Route Finder
4═══════════════════════════════════════════════════
5
6Input: 1.0 ETH on Ethereum
7Output: SOL on Solana
8Strategy: minimax
9
10Optimal Route:
11 Step 1: Swap ETH → USDC on Uniswap V3 (Ethereum)
12 Step 2: Bridge USDC via deBridge DLN (Ethereum → Solana)
13 Step 3: Swap USDC → SOL on Jupiter (Solana)
14
15Expected output: 14.32 SOL
16Guaranteed minimum: 13.85 SOL
17Total fees: $6.09
18Estimated time: ~2 minutes
19Minimax score: 0.8470
20
21$ mnmx route --from ethereum:ETH:1.0 --to solana:SOL --all
22
23Found 8 viable routes:
24
25# Route Expected Minimum Fees Time Score
26─────────────────────────────────────────────────────────────────────────────────────
271 uniswap → debridge → jupiter 14.32 13.85 $6.09 110s 0.847
282 uniswap → wormhole → jupiter 14.28 13.71 $8.50 300s 0.821
293 debridge-dln → jupiter 14.25 13.52 $9.20 65s 0.815
304 uniswap → allbridge → jupiter 14.18 13.48 $7.80 240s 0.809
315 wormhole → jupiter 14.15 13.22 $13.2 780s 0.792
326 layerzero → jupiter 14.10 13.10 $5.50 420s 0.788
337 uniswap → wormhole → arb → debridge 14.05 12.85 $15.0 600s 0.772
348 allbridge → orca 13.95 12.90 $8.20 360s 0.764

Error Handling

ExceptionWhenRecovery
MnmxErrorBase class for all SDK errorsCatch-all for unexpected errors
NoRouteFoundErrorNo viable path existsTry different token pair, increase max_hops, or check bridge health
InsufficientLiquidityErrorBridge liquidity below transfer amountReduce amount or try a different bridge
SimulationErrorInvalid simulation parametersCheck condition values are positive numbers
TimeoutErrorRoute discovery exceeded time limitIncrease timeout or reduce max_hops
RpcConnectionErrorCannot connect to RPC endpointCheck RPC URL and network connectivity
ConfigurationErrorInvalid configuration parametersCheck parameter types and ranges
python
1from mnmx.exceptions import (
2 MnmxError,
3 NoRouteFoundError,
4 InsufficientLiquidityError,
5 TimeoutError,
6 RpcConnectionError,
7)
8
9try:
10 route = router.find_route(
11 from_chain="ethereum", from_token="ETH", amount="1.0",
12 to_chain="solana", to_token="SOL",
13 )
14except NoRouteFoundError as e:
15 print(f"No route available: {e.reason}")
16 print(f"Tried bridges: {e.bridges_checked}")
17 print(f"Suggestion: {e.suggestion}")
18
19except InsufficientLiquidityError as e:
20 print(f"Liquidity too low on {e.bridge}")
21 print(f"Available: {e.available}, Required: {e.required}")
22
23except TimeoutError as e:
24 print(f"Route discovery timed out after {e.elapsed}ms")
25 print(f"Try: router = MnmxRouter(timeout=60000)")
26
27except RpcConnectionError as e:
28 print(f"Cannot connect to {e.chain} RPC: {e.url}")
29 print(f"Error: {e.message}")
30
31except MnmxError as e:
32 print(f"Unexpected error: {e}")

Data Types Reference

python
1from dataclasses import dataclass
2from typing import Optional
3
4@dataclass
5class Route:
6 path: list[str] # Provider names in order
7 hops: list[RouteHop] # Detailed hop information
8 expected_output: str # Best-case output
9 guaranteed_minimum: str # Minimax worst-case output
10 total_fees: str # Total fees in source token
11 total_fees_usd: str # Total fees in USD
12 estimated_time: int # Seconds
13 minimax_score: float # 0.0 - 1.0
14 strategy: str # Strategy used
15 quoted_at: int # Timestamp (ms)
16 expires_at: int # Timestamp (ms)
17
18@dataclass
19class RouteHop:
20 type: str # 'swap' | 'bridge'
21 from_chain: str
22 from_token: str
23 from_amount: str
24 to_chain: str
25 to_token: str
26 to_amount: str
27 provider: str # DEX or bridge name
28 fee: str
29 fee_usd: str
30 estimated_time: int # Seconds
31 price_impact: float # 0.01 = 1%
32
33@dataclass
34class MonteCarloResult:
35 iterations: int
36 mean_output: float
37 median_output: float
38 std_output: float
39 min_output: float
40 max_output: float
41 percentile_5: float
42 percentile_25: float
43 percentile_75: float
44 percentile_95: float
45 below_guarantee_pct: float # % of iterations below guaranteed min
46 distribution: list[float] # Raw output values
47
48 def print_histogram(self, bins: int = 20) -> None: ...
49 def to_csv(self, path: str) -> None: ...
50 def to_json(self, path: str) -> None: ...
51
52@dataclass
53class SimulationResult:
54 output: str # Simulated output amount
55 total_fees: str # Simulated total fees
56 total_time: int # Simulated total time (seconds)
57 per_hop: list[HopSimResult] # Per-hop breakdown
58 conditions_applied: dict # Conditions used