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 server 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). |
feePayerAddress | no | Solana origin only: the Solana address that pays the deposit transaction’s fee (set as the built transaction’s fee payer). Defaults to senderAddress. Must be a valid Solana address or the quote returns "feePayerAddress must be a Solana address". |
slippage | no | Percent. Default 1, valid range 0 to 50. |
destinationType | no | HyperLiquid destinations only: which venue the funds land in — spot (default) or perps. Ignored on non-HL destinations. Invalid values return "Invalid destinationType: must be 'spot' or 'perps'". |
originType | no | HyperLiquid origins only: which venue the deposit is pulled from — spot (default) or perps. Ignored on non-HL origins. Invalid values return "Invalid originType: must be 'spot' or 'perps'". |
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 has committed your signed intent.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, currently5);destFillGasUsdis what the solver pays to fill on the destination chain;gasFeeUsdequalsdestFillGasUsd.destActivationCostUsdappears only when the destination needs a one-off account-creation cost (e.g. Solana ATA rent for a first-time recipient) — it’s omitted otherwise.totalFeeUsdis the sum of all present fee fields.estimatedAmountOut/estimatedAmountOutUsdare already net of all of it; the user additionally pays only origin-chain gas to broadcast the deposit.destinationType/originTypeare echoed back only for HyperLiquid routes (spotorperps), reflecting the venue the funds land in / are pulled from.maxTradeUsdis$400. Amounts above that return"Amount $X exceeds maximum trade of $400".
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 solver parses to recoverintentId,destinationChainId,recipient,destinationToken, andminAmountOut. - SPL —
{ type: "spl-transfer", serializedTx }. The server has already built the full versioned transaction (SPL transfer + ATA creation if missing + memo). JustVersionedTransaction.deserialize, sign, and send.
HyperLiquid origin (deposit.hl)
Same-chain quotes
WhenoriginChainId === destinationChainId, the server 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 — the server holds it against
the depositor’s address until the deposit lands. Without binding that to the
depositor’s key, anyone who knows the address could overwrite it 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. Nothing is committed yet. - 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 commits your signed intent 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 usdSend 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 commits your signed intent — bound to (sender, originChainId, destinationChainId) — only when called with a valid signature (Solana
origin: committed on every call, since the memo carries the binding). The
server uses it to resolve destinationToken, recipient, and slippage when
the deposit lands. For EVM/HL origins a deposit with no committed signature is
rejected (and refunded) — so always complete the commit call before
broadcasting.
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")
evm:8453, evm:56, evm:42161, evm:137, solana:solana, hl:mainnet Destination chain ID (e.g., "evm:8453", "solana:solana", "hl:mainnet")
evm:8453, evm:56, evm:42161, evm:137, 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).
Solana origin only. Address that pays the deposit transaction fee (set as the built transaction's fee payer). Defaults to senderAddress.
HyperLiquid destinations only. Which venue the funds land in. Default "spot". Ignored on non-HL destinations.
spot, perps HyperLiquid origins only. Which venue the deposit is pulled from. Default "spot". Ignored on non-HL origins.
spot, perps Your Mobula API key, passed in the query string. /quote reads it from the query, not an Authorization header.
EIP-712 signature over the typedData returned by the unsigned call. Required for evm:* and hl:mainnet origins to commit the prediction. Omit on the first (preview) call.
Echo back the intentId returned by the unsigned call when submitting signature (signed-quote flow).
Echo back the deadline (Unix seconds) returned by the unsigned call. Server rejects expired deadlines.
Echo back the minAmountOut (raw destination-token units) from the typedData. The signed value is authoritative.
Response
Bridge quote response. Either data is populated with quote + deposit instructions, or error describes why the quote could not be built.