Docs
Docs/Minimax Algorithm

Minimax Algorithm

How the minimax search algorithm from game theory applies to cross-chain routing — and why it produces better outcomes than expected-value optimization.

The Chess Connection

In chess, the minimax algorithm evaluates every possible move by assuming the opponent plays optimally. It picks the move that maximizes your minimum guaranteed score — the best you can do even when the opponent plays perfectly.

Cross-chain routing is the same problem in a different domain. The "opponent" is not a person — it's the combined adversarial force of market conditions, MEV bots, bridge congestion, and gas spikes. These forces don't coordinate, but they all work against the user's outcome. Minimax treats them as a single adversary that plays optimally against each route.

ChessMNMX Routing
Board positionsCandidate routes
Possible movesPossible paths (bridge + DEX combinations)
Opponent's best responseWorst-case market conditions
Position evaluationRoute scoring (fees + slippage + risk)
Minimax scoreGuaranteed minimum output
Alpha-beta pruningEarly elimination of dominated routes
Evaluation depthNumber of adversarial factors modeled
Time controlQuote timeout budget

Formal Definition

The minimax value of a route R is defined as:

text
1minimax(R) = max_R [ min_A [ score(R, A) ] ]
2
3Where:
4 R = set of candidate routes
5 A = set of adversarial conditions
6 score(R, A) = net output of route R under adversarial conditions A
7
8The optimal route R* satisfies:
9 R* = argmax_R [ min_A [ score(R, A) ] ]
10
11In plain English:
12 "Pick the route whose worst case is better than every other route's worst case."

The Algorithm

MNMX applies a modified minimax with two levels:

text
1Level 0 (Maximizer — your routes):
2 For each candidate route, compute the worst-case output.
3 Pick the route with the HIGHEST worst-case output.
4
5Level 1 (Minimizer — adversarial conditions):
6 For each route, model all adversarial factors:
7 - Slippage spike (2x quoted)
8 - Gas price surge (1.5x current)
9 - Bridge delay (3x median)
10 - MEV extraction (0.3% of value)
11 - Price movement (0.5% adverse)
12 Apply all simultaneously → worst-case output for this route.
typescript
1function minimax(routes: Route[]): Route {
2 let bestRoute: Route | null = null;
3 let bestMinimaxScore = -Infinity;
4 let alpha = -Infinity; // Alpha-beta pruning bound
5
6 for (const route of routes) {
7 // Minimizer: compute worst-case output for this route
8 const worstCase = evaluateWorstCase(route);
9
10 // Alpha-beta pruning: skip if this route can't beat current best
11 if (worstCase.lowerBound < alpha) {
12 continue; // Prune — this route's best possible worst-case is below alpha
13 }
14
15 // Maximizer: pick route with best worst-case
16 if (worstCase.score > bestMinimaxScore) {
17 bestMinimaxScore = worstCase.score;
18 bestRoute = route;
19 alpha = bestMinimaxScore; // Raise pruning threshold
20 }
21 }
22
23 return bestRoute;
24}
25
26function evaluateWorstCase(route: Route): Evaluation {
27 let output = route.quotedOutput;
28
29 // Apply all adversarial conditions simultaneously
30 output -= route.slippage * 2.0; // 2x slippage
31 output -= route.gasCost * 1.5; // 1.5x gas
32 output -= route.value * 0.003; // MEV extraction
33 output -= route.value * 0.005; // Adverse price movement
34 output -= route.bridgeRiskPenalty; // Bridge-specific risk
35
36 // Compute lower bound for early pruning
37 // (assumes best possible outcome for remaining adversarial factors)
38 const lowerBound = output;
39
40 return { score: output, lowerBound, route };
41}

Worked Example With Real Numbers

Let's trace through a complete minimax evaluation for a real transfer: 10 ETH on Ethereum to SOL on Solana. Current market price: 1 ETH = 14.3 SOL.

text
1Input: 10 ETH ($32,500 at $3,250/ETH)
2Target: SOL on Solana ($225/SOL)
3Ideal output (zero-fee): 144.44 SOL
4
5═══════════════════════════════════════════════════════════════════
6ROUTE A: Wormhole Direct
7 ETH(Ethereum) → Wormhole → ETH(Solana) → Jupiter(ETH→SOL)
8═══════════════════════════════════════════════════════════════════
9
10Quoted output breakdown:
11 Bridge fee: 0.04 ETH ($130) → 143.87 SOL equivalent
12 Jupiter swap: 0.18% price impact → -0.259 SOL
13 Ethereum gas: $12.50 (21,000 gas @ 45 gwei)
14 Solana gas: $0.02 (5,000 compute units)
15 ─────────────────────────────────────────────
16 Quoted output: 143.55 SOL
17
18Worst-case modeling:
19 Slippage (2x): -0.259 SOL additional → 143.29 SOL
20 Gas surge (1.5x): -$6.26 (additional gas) → 143.26 SOL
21 MEV (0.3%): -0.430 SOL → 142.83 SOL
22 Price move (0.5%): -0.714 SOL → 142.12 SOL
23 Bridge risk: -0.143 SOL (99.2% success rate) → 141.98 SOL
24 ─────────────────────────────────────────────
25 MINIMAX SCORE: 141.98 SOL
26
27═══════════════════════════════════════════════════════════════════
28ROUTE B: deBridge DLN Direct
29 ETH(Ethereum) → deBridge DLN → ETH(Solana) → Jupiter(ETH→SOL)
30═══════════════════════════════════════════════════════════════════
31
32Quoted output breakdown:
33 Bridge fee: 0.02 ETH ($65) → 144.15 SOL equivalent
34 Jupiter swap: 0.18% price impact → -0.259 SOL
35 Ethereum gas: $14.00 (complex calldata)
36 Solana gas: $0.02
37 ─────────────────────────────────────────────
38 Quoted output: 143.83 SOL
39
40Worst-case modeling:
41 Slippage (2x): -0.259 SOL → 143.57 SOL
42 Gas surge (1.5x): -$7.00 → 143.54 SOL
43 MEV (0.3%): -0.431 SOL → 143.11 SOL
44 Price move (0.5%): -0.716 SOL → 142.39 SOL
45 Bridge risk: -0.072 SOL (99.6% success) → 142.32 SOL
46 ─────────────────────────────────────────────
47 MINIMAX SCORE: 142.32 SOL
48
49═══════════════════════════════════════════════════════════════════
50ROUTE C: Stablecoin Multi-Hop
51 ETH(Ethereum) → Uniswap(ETH→USDC) → deBridge(USDC) →
52 Jupiter(USDC→SOL) on Solana
53═══════════════════════════════════════════════════════════════════
54
55Quoted output breakdown:
56 Uniswap swap: 0.05% price impact on ETH/USDC → 32,483.75 USDC
57 Bridge fee: $3.50 (USDC bridges cheaply) → 32,480.25 USDC
58 Jupiter swap: 0.08% price impact on USDC/SOL → 144.24 SOL
59 Ethereum gas: $15.00 (swap + bridge initiate)
60 Solana gas: $0.02
61 ─────────────────────────────────────────────
62 Quoted output: 144.17 SOL
63
64Worst-case modeling:
65 Slippage (2x): -0.094 SOL (low impact pairs) → 144.08 SOL
66 Gas surge (1.5x): -$7.51 → 144.04 SOL
67 MEV (0.3%): -0.432 SOL → 143.61 SOL
68 Price move (0.5%): -0.720 SOL → 142.89 SOL
69 Bridge risk: -0.072 SOL (deBridge, 99.6%) → 142.82 SOL
70 ─────────────────────────────────────────────
71 MINIMAX SCORE: 142.82 SOL ← HIGHEST
72
73═══════════════════════════════════════════════════════════════════
74ROUTE D: Multi-Hop via Arbitrum
75 ETH(Ethereum) → LayerZero(ETH→Arbitrum) → Uniswap(ETH→USDC) →
76 Wormhole(USDC→Solana) → Jupiter(USDC→SOL)
77═══════════════════════════════════════════════════════════════════
78
79Quoted output breakdown:
80 LayerZero bridge: 0.01 ETH ($32.50)
81 Arbitrum Uniswap: 0.04% price impact
82 Wormhole bridge: $2.00 (USDC)
83 Jupiter swap: 0.08% price impact
84 Ethereum gas: $12.00
85 Arbitrum gas: $0.35
86 Solana gas: $0.02
87 ─────────────────────────────────────────────
88 Quoted output: 143.92 SOL
89
90Worst-case modeling:
91 Slippage (2x): -0.086 SOL → 143.83 SOL
92 Gas surge (1.5x): -$6.19 (3 chains!) → 143.80 SOL
93 MEV (0.3%): -0.431 SOL → 143.37 SOL
94 Price move (0.5%): -1.434 SOL (TWO bridges!) → 141.94 SOL
95 Bridge risk: -0.288 SOL (2 bridges compound) → 141.65 SOL
96 ─────────────────────────────────────────────
97 MINIMAX SCORE: 141.65 SOL
98
99═══════════════════════════════════════════════════════════════════
100RESULT: Route C wins with minimax score 142.82 SOL
101═══════════════════════════════════════════════════════════════════
102
103Ranking by minimax score:
104 1. Route C (stablecoin multi-hop): 142.82 SOL ← SELECTED
105 2. Route B (deBridge direct): 142.32 SOL
106 3. Route A (Wormhole direct): 141.98 SOL
107 4. Route D (multi-hop via Arbitrum): 141.65 SOL
108
109Note: Route D had the highest QUOTED output (143.92) but the
110LOWEST minimax score (141.65) due to compounding risk from
111two bridge hops. This is exactly the kind of trap that expected-
112value optimization falls into.

Minimax vs Expected Value

Most routers use expected value optimization — they estimate the most likely outcome for each route and pick the highest. Here's why minimax is better for cross-chain:

The Probability Problem

Expected value requires knowing the probability of each outcome. In cross-chain, these probabilities are fundamentally unknowable:

  • Bridge delays are driven by validator set behavior, network congestion, and consensus mechanics that vary unpredictably
  • MEV extraction depends on the current mempool state and the sophistication of searchers watching your transaction
  • Liquidity depth can change in the milliseconds between quote and execution as other users trade
  • Gas prices spike non-linearly during high-demand events (token launches, liquidation cascades)

Expected value routers must assign probabilities to these events. If those probabilities are wrong (and they always are), the "optimal" route is suboptimal. Minimax sidesteps this entirely by not using probabilities at all.

Concrete Example

text
1Transfer: 1 ETH → SOL
2
3Route A (Wormhole direct):
4 Best case: 14.5 SOL (probability: 60%)
5 Worst case: 11.2 SOL (probability: 40%)
6 Expected value: 14.5 x 0.6 + 11.2 x 0.4 = 13.18 SOL
7 Minimax score: 11.2 SOL
8
9Route B (Multi-hop via Arbitrum):
10 Best case: 14.2 SOL (probability: 70%)
11 Worst case: 13.8 SOL (probability: 30%)
12 Expected value: 14.2 x 0.7 + 13.8 x 0.3 = 14.08 SOL
13 Minimax score: 13.8 SOL
14
15Expected value picks: Route B (14.08 > 13.18) ← correct here
16Minimax picks: Route B (13.8 > 11.2) ← also correct, same answer
17
18But what if probabilities are wrong?
text
1What if actual worst-case probability was 70%, not 40%?
2
3Route A actual: 14.5 x 0.3 + 11.2 x 0.7 = 12.19 SOL ← much worse
4Route B actual: 14.2 x 0.3 + 13.8 x 0.7 = 13.92 SOL ← barely changed
5
6Minimax doesn't need to know probabilities.
7It just guarantees Route B gives you AT LEAST 13.8 SOL.
8Period.

In cross-chain, probability estimates are unreliable — bridge delays are unpredictable, MEV is adversarial, liquidity conditions change between quote and execution. Minimax doesn't need probability estimates. It gives you the best guaranteed floor.

Why Variance Matters More Than Mean

Consider a user bridging $50,000 USDC from Ethereum to Solana. Two routes are available:

text
1Route X: Expected output $49,850 | Worst case: $49,200 | Variance: $650
2Route Y: Expected output $49,900 | Worst case: $48,500 | Variance: $1,400
3
4Expected value picks Route Y (saves $50 on average).
5But Route Y can cost $700 MORE in the worst case.
6
7For a $50,000 transfer, the user cares far more about avoiding
8a $1,500 loss than capturing an extra $50 on average.
9
10Minimax picks Route X: guaranteed to lose at most $800.
11Route Y could lose up to $1,500.
12
13The rational choice is Route X unless you're making thousands
14of identical transfers and can afford to average out.

Alpha-Beta Pruning

When evaluating many candidate paths, alpha-beta pruning eliminates routes that cannot possibly beat the current best. This is the same optimization used in chess engines to search deeper within the same time budget.

text
1Routes to evaluate: [A, B, C, D, E, F, G, H, I, J, K, L]
2Alpha (best minimax found so far): -Infinity
3
4Evaluate A:
5 Quoted output: 143.55 SOL
6 Apply adversarial conditions...
7 Minimax score: 141.98 SOL
8 Alpha = 141.98
9
10Evaluate B:
11 Quoted output: 143.83 SOL
12 Apply adversarial conditions...
13 Minimax score: 142.32 SOL
14 Alpha = 142.32 (new best)
15
16Evaluate C:
17 Quoted output: 144.17 SOL
18 Apply adversarial conditions...
19 Minimax score: 142.82 SOL
20 Alpha = 142.82 (new best)
21
22Evaluate D:
23 Quoted output: 143.92 SOL
24 After slippage + gas penalties: 143.80 SOL
25 After MEV penalty: 143.37 SOL
26 After price movement: 141.94 SOL ← already below alpha (142.82)!
27 PRUNE — no need to compute bridge risk. This route cannot beat C.
28
29Evaluate E:
30 Quoted output: 142.10 SOL ← below alpha before ANY penalties
31 PRUNE immediately — quoted output is already below best minimax.
32
33Evaluate F:
34 Quoted output: 143.50 SOL
35 After first penalty: 143.20 SOL
36 After second penalty: 142.65 SOL ← below alpha
37 PRUNE — cannot beat C.
38
39...continue...
40
41Result: Only 5 of 12 routes fully evaluated. Same result, 58% less work.

The efficiency of alpha-beta pruning depends on the order in which routes are evaluated. MNMX sorts routes by estimated quality before evaluation, which means the best route is likely evaluated early, setting a high alpha threshold that prunes more aggressively. In practice, this reduces the number of full evaluations by 40-60%.

Pruning Implementation

typescript
1class MinimaxEvaluator {
2 evaluate(routes: Route[], config: AdversarialConfig): Route {
3 // Pre-sort by estimated quality (heuristic: lower fees + higher liquidity)
4 const sorted = routes.sort((a, b) =>
5 this.estimateQuality(b) - this.estimateQuality(a)
6 );
7
8 let bestRoute: Route | null = null;
9 let alpha = -Infinity;
10 let evaluationCount = 0;
11 let pruneCount = 0;
12
13 for (const route of sorted) {
14 // Quick check: can this route's quoted output even beat alpha?
15 if (route.quotedOutput < alpha) {
16 pruneCount++;
17 continue; // Quoted output already below best worst-case
18 }
19
20 // Incremental evaluation with early termination
21 const result = this.evaluateWithPruning(route, config, alpha);
22 evaluationCount++;
23
24 if (result.pruned) {
25 pruneCount++;
26 continue;
27 }
28
29 if (result.score > alpha) {
30 alpha = result.score;
31 bestRoute = route;
32 }
33 }
34
35 console.log(
36 `Evaluated ${evaluationCount}/${routes.length} routes, pruned ${pruneCount}`
37 );
38
39 return bestRoute!;
40 }
41
42 private evaluateWithPruning(
43 route: Route,
44 config: AdversarialConfig,
45 alpha: number
46 ): { score: number; pruned: boolean } {
47 let output = route.quotedOutput;
48
49 // Apply penalties one at a time, checking alpha after each
50 const penalties = [
51 () => route.slippage * config.slippageMultiplier,
52 () => route.gasCost * config.gasMultiplier - route.gasCost,
53 () => route.value * config.mevExtraction,
54 () => route.value * config.priceMovement * route.bridgeCount,
55 () => this.calculateBridgeRisk(route),
56 ];
57
58 for (const penalty of penalties) {
59 output -= penalty();
60 if (output < alpha) {
61 return { score: output, pruned: true };
62 }
63 }
64
65 return { score: output, pruned: false };
66 }
67}

Route Scoring Function

Each route is scored across five dimensions. The combined score represents the net output in destination tokens:

DimensionWeightCalculationDescription
Total fees0.25gas_usd + bridge_fees_usd + swap_fees_usdSum of gas, bridge, and swap fees across all hops
Slippage impact0.25Sum of price_impact per pool * amountPrice impact from swaps and low bridge liquidity
Transfer speed0.15Sum of p90_confirmation_time per hopExpected total time from start to completion
Bridge reliability0.20Product of success_rates * tvl_score * audit_scoreHistorical uptime, TVL, security audit score
MEV exposure0.15unprotected_swap_value * historical_extraction_rateRisk of value extraction during multi-hop execution

The weighted score is normalized to a 0-1 scale where higher is better:

typescript
1function computeWeightedScore(
2 route: Route,
3 state: PathState,
4 weights: ScoringWeights
5): number {
6 // Normalize each dimension to 0-1 scale
7 const feeScore = 1 - (state.totalFeesUSD / state.transferValueUSD);
8 const slippageScore = 1 - state.totalSlippage;
9 const speedScore = Math.max(0, 1 - (state.totalTime / 1200)); // 20 min max
10 const reliabilityScore = state.bridgeHealth.reduce(
11 (acc, h) => acc * h.recentSuccessRate, 1.0
12 );
13 const mevScore = 1 - state.mevExposure;
14
15 return (
16 weights.fees * feeScore +
17 weights.slippage * slippageScore +
18 weights.speed * speedScore +
19 weights.reliability * reliabilityScore +
20 weights.mevExposure * mevScore
21 );
22}

Adversarial Factor Calibration

The adversarial multipliers are not arbitrary — they are calibrated from historical execution data. The table below shows how each factor was derived:

FactorDefaultDerivationLast Calibration
Slippage multiplier2.0xp99 of actual/quoted slippage ratio across 50K+ swapsRolling 30-day window
Gas multiplier1.5xp95 of gas price ratio (execution vs quote time) across 100K+ txsRolling 7-day window
Bridge delay3.0xp95 of actual/median confirmation time per bridgeRolling 7-day window
MEV extraction0.3%Mean extracted value on unprotected swaps >$1K, measured via sandwich detectionRolling 30-day window
Price movement0.5%p95 adverse price change of major pairs over median bridge confirmation timeRolling 7-day window

User Strategy Profiles

Users can adjust the scoring weights to match their priorities. Each strategy shifts the minimax evaluation toward different trade-offs:

ProfileFeesSlippageSpeedReliabilityMEV
Minimax (default)0.250.250.150.200.15
Cheapest0.450.300.050.100.10
Fastest0.100.150.500.150.10
Safest0.100.150.100.400.25
typescript
1// Strategy comparison for 10 ETH → SOL transfer:
2
3// Minimax (default): Picks Route C (stablecoin multi-hop)
4// Guaranteed minimum: 142.82 SOL
5// Rationale: Best worst-case across all dimensions
6
7// Cheapest: Picks Route B (deBridge direct)
8// Expected fees: $79.02 (lowest)
9// Trade-off: Slightly worse worst-case (142.32 SOL)
10
11// Fastest: Picks Route B (deBridge DLN)
12// Expected time: 90 seconds (DLN is fastest)
13// Trade-off: Higher fees than stablecoin route
14
15// Safest: Picks Route C (stablecoin multi-hop)
16// Bridge reliability: 99.6% (deBridge) + deep USDC liquidity
17// Trade-off: Slightly slower than DLN direct

Multi-Bridge Route Evaluation

Routes that use multiple bridges have compounding risk. The minimax evaluator handles this by multiplying bridge risk factors rather than adding them:

text
1Single-bridge route:
2 Bridge risk = 1 - success_rate = 1 - 0.996 = 0.004 (0.4%)
3
4Two-bridge route:
5 Combined risk = 1 - (success_rate_A * success_rate_B)
6 = 1 - (0.996 * 0.992) = 1 - 0.988 = 0.012 (1.2%)
7
8 Not 0.4% + 0.8% = 1.2% (additive would be coincidentally similar here)
9 But for non-independent events, the correlation matters.
10
11Three-bridge route:
12 Combined risk = 1 - (0.996 * 0.992 * 0.985) = 1 - 0.973 = 0.027 (2.7%)
13
14This is why the minimax evaluator penalizes multi-hop routes more
15heavily — each additional bridge multiplies the failure probability,
16and the worst-case model amplifies this by applying the delay
17multiplier to EACH bridge independently.
18
19Price movement risk also compounds:
20 1 bridge: 0.5% adverse movement during 1 transit
21 2 bridges: 0.5% * 2 = 1.0% adverse movement during 2 transits
22 3 bridges: 0.5% * 3 = 1.5% adverse movement during 3 transits

When Minimax Is Not Optimal

Minimax is not always the best strategy. It has known limitations that users should understand:

Conservative Bias

Conservative bias: Minimax inherently favors reliable paths over potentially more profitable but riskier ones. For users making many small transfers where individual outcomes don't matter much, the "cheapest" strategy may produce better aggregate results. Minimax is designed for users who care about the outcome of each individual transfer.

Over-Pessimism on Stable Routes

For stablecoin-to-stablecoin transfers (e.g., USDC on Ethereum to USDC on Solana), the price movement penalty is overly conservative because the price of USDC/USDC is pegged. The engine partially compensates for this by reducing the price movement multiplier for stablecoin pairs, but some conservatism remains.

State Staleness

State staleness: Route conditions can change between quote and execution. The engine re-validates conditions before executing each step and will abort if conditions degrade beyond tolerance. The worst-case model accounts for this with the adversarial multipliers, but extreme market events (flash crashes, protocol exploits) can exceed even worst-case assumptions.

Algorithm Comparison

PropertyMinimax (MNMX)Expected ValueGreedy (lowest fee)
Requires probability estimatesNoYesNo
Guarantees minimum outputYesNoNo
Optimal for single transferYes (risk-averse)On averageNo
Optimal for many transfersConservativeYes (if probabilities correct)No
Handles adversarial conditionsBy designOnly if modeledIgnores them
Computation costO(n) with pruningO(n)O(n)
Implementation complexityModerateHigh (needs probability model)Low