Bridge Quote
[Alpha Preview] Get a cross-chain bridge quote with a ready-to-sign deposit transaction. EVM, Solana, and HyperLiquid.
GET /api/2/bridge/quote returns a ready-to-sign deposit transaction plus a
intentId you’ll use to poll status. The Mobula solver detects the deposit
(flash blocks on Base, gRPC on Solana) and fills on the destination chain.
Typical end-to-end latency: ~500 ms for Base ↔ Solana, ~1–3 s elsewhere.
Query parameters
| Name | Required | Notes |
|---|---|---|
apiKey | yes | Your Mobula API key, passed in the query string (?apiKey=…). /quote reads it from the query, not an Authorization header — omitting it returns { "error": "Missing required parameter: apiKey" }. |
originChainId | yes | One of the supported chain IDs (see Bridge Routes). |
destinationChainId | yes | Same set. When equal to originChainId, the endpoint short-circuits to the Swap API — see Same-chain quotes. |
walletAddress | yes | Destination recipient. Format-validated against the destination chain: EVM regex for evm:* and hl:mainnet, Base58 for solana:solana. |
amount | yes (cross-chain) | Decimal human units ("0.05", not wei). Must be finite, positive, ≤ 1e15. Not required when source and destination are the same chain. |
originToken | no | Omit, or pass 0x0000…0000 / 0xeeee…eeee for the native token. EVM addresses are checksummed server-side. |
destinationToken | no | Same rules. When omitted on a Solana destination, the API substitutes wSOL (So11111111111111111111111111111111111111112). |
senderAddress | conditional | Required for Solana SPL bridges (the controller needs it to build the ATA + SPL transfer + memo). For HL origin, this is the HL spot wallet that will sign — it must equal the EIP-712 signer (see Signed-quote flow). Optional for EVM origins (defaults to walletAddress). |
slippage | no | Percent. Default 1, valid range 0 to 50. |
signature | conditional | EIP-712 signature over the typed-data payload returned by the unsigned call. Required for evm:* and hl:mainnet origins to commit the prediction. Omit on the first call to preview the quote + receive the typedData to sign. See Signed-quote flow. |
intentId | conditional | Echo back the intentId returned by the unsigned call when submitting signature. Format xxxxxxx-xxxxxxx-xxx (lowercase hex). |
deadline | conditional | Echo back the deadline returned by the unsigned call. Unix seconds. Server rejects expired deadlines. |
minAmountOut | conditional | Echo back the minAmountOut (raw destination-token units) from the typedData. The signed value is authoritative — the server reconstructs the typedData from this exact value before verifying the signature. |
{ "error": "..." } with HTTP 200 (or HTTP 400
for invalid signatures) — always check the error key before reading data.
Response
intentIdis the user-facing handle, formatxxxxxxx-xxxxxxx-xxx(lowercase hex). Pass it toGET /status/:idor/status/:id/wait. There is also an on-chainbytes32intent ID emitted byMobulaBridgeon EVM deposits — both resolve in/status/:id, so use whichever you have. You must echo this exactintentIdback via theintentIdquery param when submitting the signature — the signed payload binds to it.deadlineis Unix seconds. The server rejects signed calls past this point.typedDatais the EIP-712 structured-data payload your wallet should sign (see Signed-quote flow for the full schema).signatureRequired: trueforevm:*andhl:mainnetorigins.falseforsolana:solana(the depositor-signed memo replaces the signature).prediction.persistedindicates whether the server wrote the prediction row.falseon the unsigned preview call;trueafter a successful signed call.stepsis present only when the deposit needs more than one transaction. Today that means EVM ERC-20 origins:[approve, bridgeToken | swapAndBridge]. Native EVM bridges and Solana/HL bridges omitsteps.recommendedSlippageis the slippage % we suggest signing with — the route’s implied spread ((1 − outUsd/inUsd)×100) plus a0.5%drift buffer, rounded up to0.1, floored at1, and capped at50(nullwhen USD prices are unavailable). The solver refunds any fill below the signedminAmountOut(failure codeslippage), so a tolerance under this value is a near-guaranteed refund. If yourslippageis below it, re-quote at the recommendation before signing.feesare real, deducted amounts — there is no placeholder.bridgeFeeUsdis the Mobula protocol fee (bridgeFeeBps, currently0);destFillGasUsdis what the solver pays to fill on the destination chain;originRefundGasUsdis a refund-gas reserve charged on every quote so a refund never puts the solver in the red.gasFeeUsd = destFillGasUsd + originRefundGasUsdandtotalFeeUsdis the sum.estimatedAmountOut/estimatedAmountOutUsdare already net of all of it; the user additionally pays only origin-chain gas to broadcast the deposit.maxTradeUsdis$10,000. Amounts above that return"Amount $X exceeds maximum trade of $10000".
deposit shapes
The shape depends on originChainId. Sign and broadcast whichever one is
present.
EVM origin (deposit.evm)
- Native ETH/BNB/POL — single
bridge()call onMobulaBridge.valueis the raw amount in wei. Nosteps. - Direct-bridge tokens —
approvestep toMobulaBridge, thenbridgeToken().valueis"0". The set is USDC on all four chains, plus Base’s bridged USDbC and USDT on Arbitrum and Polygon (BSC is USDC-only). - Any other ERC-20 —
approvestep toSwapBridgeHelper, thenswapAndBridge()— atomic swap to native + bridge in one TX. The embedded swap calldata is validated server-side to start with theMobulaRouter.executeRouteselector (0xa564dfa4); if it doesn’t, the quote returns"Swap quote failed: invalid calldata selector".
MobulaBridge (MobulaBridgeV2, deployed 2026-06-04) is the same proxy address
on every EVM chain:
| Chain | Bridge contract |
|---|---|
evm:8453 (Base) | 0xa834E70303322D86E5DaE95ee47E9c6a073d9812 |
evm:56 (BSC) | 0xa834E70303322D86E5DaE95ee47E9c6a073d9812 |
evm:42161 (Arbitrum) | 0xa834E70303322D86E5DaE95ee47E9c6a073d9812 |
evm:137 (Polygon) | 0xa834E70303322D86E5DaE95ee47E9c6a073d9812 |
SwapBridgeHelper (the
spender named in the approve step) — always approve the spender the step
specifies, not a hardcoded address.
Approval handling: approvalAmount is always MAX_UINT256, so a single
approve per (token, spender) is enough forever. Skip the approve step only
if the current on-chain allowance already covers amount.
Solana origin (deposit.solana)
Two shapes depending on token:
- Native SOL —
{ to, amount, memo }. Build aSystemProgram.transferforamountlamports toto(the solver address), then add a memo instruction (programMemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr) whose data is thememostring. The memo is a JSON blob the Solana listener parses to recoverintentId,destinationChainId,recipient,destinationToken, andminAmountOut. - SPL —
{ type: "spl-transfer", serializedTx }. The controller has already built the full versioned transaction (SPL transfer + ATA creation if missing + memo). JustVersionedTransaction.deserialize, sign, and send.
HyperLiquid origin (deposit.hl)
spotSend action to the solver L1 address using your HL signer.
Same-chain quotes
WhenoriginChainId === destinationChainId, the controller short-circuits
into a swap-wrapper. The response is the raw Swap API response, not the
bridge shape above (no intentId, no deposit.evm/solana/hl). Approval
amounts in the response are overridden to MAX_UINT256 server-side.
Branch on the response: data.deposit present → bridge flow; otherwise →
swap flow.
Signed-quote flow
Forevm:* and hl:mainnet origins the destination token (and, for HL, the
destination address) is not committed on chain — it lives in a server-side
prediction row keyed on the depositor’s address. Without binding that row to
the depositor’s key, anyone who knows the address can overwrite the row and
redirect funds to a different token. To close that hole, /quote enforces an
EIP-712 signature flow.
The flow is two API calls plus one wallet signature:
- Preview call —
GET /quote?...with nosignature. Server returns the quote, the deposit calldata, and atypedDatapayload. No prediction row is written. - Sign — your wallet signs
typedData(e.g.eth_signTypedData_v4for injected wallets,walletClient.signTypedDatawith viem). The signer’s address must equaltypedData.message.sender. - Commit call —
GET /quote?...&signature=<sig>&intentId=<id>&deadline=<ts>&minAmountOut=<raw>with the same input params as call 1. Server reconstructs the typedData from the query params, recovers the signer, and writes the prediction row (with the signature persisted) if everything lines up. Returns the same response shape withprediction.persisted: true. - Broadcast — submit
deposit.evm(ordeposit.hl) on chain.
EIP-712 schema
EIP712Domain type from types — inject it client-side
(name:string, version:string, chainId:uint256) before signing if your signer
needs it.
HyperLiquid specifics. HL signs all actions under Ethereum mainnet, so for
hl:mainnet origins typedData.domain.chainId is 1 (not 999) — switch the
wallet to chain 1 before signing. An HL bridge then needs two signatures:
(1) this EIP-712 BridgeIntent confirm, then (2) the HL spotSend transfer to
the solver.
Failure modes on the commit call
400 Invalid bridge intent signature: ...— signature did not recover to the depositor address. Re-checktypedData.message.senderand the signer.400 Signature deadline has expired or is invalid—deadlineis pastnow. Re-quote.400 Signed-mode quote requires intentId, deadline, minAmountOut, and signature— one of the four signed-mode params is missing.
Side effects
/quote upserts a row into misc.bridge_intent_predictions keyed by
(sender, originChainId, destinationChainId) only when called with a valid
signature (Solana origin: also writes on every call, since the memo carries
the binding). The destination-chain listener reads the row back to resolve
destinationToken, recipient, and slippage when the deposit lands. The
solver rejects deposits whose prediction row has signature IS NULL for
EVM/HL origins.
Example
EVM origin (signed-quote flow, Base → BSC, 100 USDC → USDT):/status/:id/wait with
quote.intentId.Query Parameters
Origin chain ID (e.g., "evm:8453", "solana:solana", "hl:mainnet")
Destination chain ID (e.g., "evm:8453", "solana:solana", "hl:mainnet")
Human-readable amount of origin token to bridge (e.g., "0.1")
Recipient wallet address on the destination chain (EVM hex, Solana base58, or HL hex).
Origin token contract/mint address. Omit or pass the zero address for the native token.
Destination token contract/mint address. Omit or pass the zero address for the native token.
Slippage tolerance in percent (0-50, default: 1).
Origin-chain sender address. Required when bridging an SPL token from Solana (the wallet signing the swap+deposit TX).
Response
Bridge quote response. Either data is populated with quote + deposit instructions, or error describes why the quote could not be built.