Skip to main content
Single endpoint, same shape as EVM / TON. The Solana-specific calldata sits under data.solana. See the Swap Quoting page for the full input parameters.

Native SOL and WSOL

So11111111111111111111111111111111111111111
Use this tokenIn / tokenOut value when the user is paying with, or receiving, native SOL from their wallet. Wrapped SOL is the SPL mint So11111111111111111111111111111111111111112. Use the WSOL mint only when the user is intentionally swapping an existing WSOL token account.

Request

GET /api/2/swap/quoting?chainId=solana:solana
  &tokenIn=So11111111111111111111111111111111111111111
  &tokenOut=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
  &amount=1
  &walletAddress=8ZmF…
  &slippage=1
  &prioritizationFeeLamports=auto
  &jitoTipLamports=10000
  &feePercentage=0.5
  &feeWallet=8ZmF…
ParamRequiredNotes
chainIdsolana:solana
tokenIn / tokenOutToken mint address. Native SOL sentinel = So111…111; WSOL SPL mint = So111…112.
amount or amountRawHuman-readable ("1.5") or raw ("1500000" for 6-decimal USDC)
walletAddressUser’s Solana base58 pubkey
slippage% value (0-100) or auto. Defaults to auto.
prioritizationFeeLamportsJupiter-compatible priority fee budget: auto, fixed lamports, or {"priorityLevelWithMaxLamports":{"priorityLevel":"medium" | "high" | "veryHigh","maxLamports":1000000,"global":false}}
dynamicComputeUnitLimittrue by default; dynamically sizes the Solana compute limit from the built swap instructions
jitoTipLamportsAdd a Jito tip transfer to one of the official tip accounts
multiLandertrue returns N candidates over a durable nonce — see Multi-lander
landerTipLamportsPer-lander tip when multiLander=true
payerAddressFee abstraction. This wallet pays transaction fees, priority fees, Jito tips, and ATA rent. It must sign when different from walletAddress. The swap funds still come from walletAddress.
closeAuthorityOptional close authority for non-WSOL ATAs Mobula creates during the swap when payerAddress is used. Usually set it to the central payer wallet to reclaim rent later.
destinationWalletSolana-only tokenOut recipient. Defaults to walletAddress. Do not combine with finalRecipientWallet.
swapRecipientAddressSolana-only router-enforced final recipient. MobulaRouter transfers the exact final swap output to this wallet after swap execution and fee/slippage checks. Do not combine with destinationWallet. Legacy alias: finalRecipientWallet.
feePercentageCaller referral fee 0-99% (taken from input or output SOL)
feeWalletRequired when feePercentage > 0 (and for minFeesNative / feeToken)
minFeesNativeMinimum referral fee in SOL, e.g. 0.05. Floors the referral fee — max(amountIn × feePercentage/100, minFeesNative) — enforced on-chain by MobulaRouter when the fee asset is native SOL.
feeTokenMint of a token to charge a flat minimum fee in (with minFeesTokenRaw). Transferred to feeWallet via a dedicated instruction, independent of the route.
minFeesTokenRawRaw amount (smallest unit) of feeToken to charge. Tx reverts if the user’s balance is insufficient.
onlyRoutersComma-list of jupiter,naos,kyberswap,lifi
excludedProtocols / onlyProtocolsDEX-level filter (e.g. raydium,orca,pump-amm)
Use payerAddress when a central wallet should pay Solana fees / ATA rent for the user. Add closeAuthority when that central wallet should be able to close non-WSOL ATAs created by Mobula during the swap and recover rent later.
GET /api/2/swap/quoting?chainId=solana:solana
  &tokenIn=So11111111111111111111111111111111111111111
  &tokenOut=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
  &amount=0.01
  &walletAddress=USER_WALLET
  &payerAddress=CENTRAL_FEE_PAYER_WALLET
  &closeAuthority=CENTRAL_FEE_PAYER_WALLET
  &swapRecipientAddress=FINAL_OUTPUT_WALLET
  &slippage=auto
  &dynamicComputeUnitLimit=true
  • walletAddress owns the input funds and signs for the swap.
  • payerAddress pays network fees / rent and must also sign when different from walletAddress.
  • closeAuthority only has an effect when payerAddress is provided. It applies to non-WSOL ATAs Mobula creates and leaves open after execution.
  • swapRecipientAddress receives the final output amount. It does not need to sign.
  • swapRecipientAddress keeps walletAddress as the swap taker/authority, then MobulaRouter transfers the final output after fee and slippage checks.
  • swapRecipientAddress requires a Mobula on-chain router route. If the selected route cannot use the router, the quote returns an error.

Response — data.solana

{
  "data": {
    "amountOutTokens": "245.123",
    "amountInUSD": 200.45,
    "amountOutUSD": 199.87,
    "slippagePercentage": 1,
    "marketImpactPercentage": 0.04,
    "poolFeesPercentage": 0.25,
    "tokenIn":  { "address": "So111…111", "symbol": "SOL",  "decimals": 9 },
    "tokenOut": { "address": "EPjF…",     "symbol": "USDC", "decimals": 6 },
    "requestId": "f8b2…",
    "details": {
      "route": {
        "hops": [
          { "poolAddress": "5Q5…", "exchange": "Raydium", "poolType": "CLMM", "feeBps": 25 }
        ],
        "totalFeePercentage": 0.25,
        "aggregator": "jupiter"
      }
    },
    "fee": { "amount": "0.001", "percentage": 0.5, "wallet": "8ZmF…", "deductedFrom": "input" },
    "solana": {
      "transaction": {
        "serialized": "base64-encoded-VersionedTransaction-bytes",
        "variant": "versioned"
      },
      "lastValidBlockHeight": 269450123
    },
    "evm": null,
    "ton": null
  }
}

data.solana fields

FieldTypeDescription
transaction.serializedbase64Full Solana transaction. Deserialize with VersionedTransaction.deserialize (or Transaction.from for legacy).
transaction.variant'versioned' | 'legacy'Discriminator — almost always 'versioned' in practice.
lastValidBlockHeightnumberBlockhash expiry — your client should broadcast before the chain ticks past this.

Multi-lander

Set multiLander=true to receive N candidate transactions sharing a durable nonce. Race them across Jito / Nozomi / 0slot for fastest landing — only one can commit.
{
  "data": {
    "candidates": [
      { "lander": "jito",     "serialized": "base64…", "tipAccount": "Cw8C…", "tipLamports": 1000 },
      { "lander": "nozomi",   "serialized": "base64…", "tipAccount": "TpdX…", "tipLamports": 1000 },
      { "lander": "zeroslot", "serialized": "base64…", "tipAccount": "6fQa…", "tipLamports": 1000 }
    ],
    "nonceAccount": "Hgs…",
    "nonceAuthority": "BvW…",
    "solana": null,
    "evm": null,
    "ton": null
  }
}
Sign every candidate, POST them all to /swap/send in batch mode (candidates: [{lander, signedTransaction}, …]).

Signing

import { VersionedTransaction } from '@solana/web3.js';

const tx = VersionedTransaction.deserialize(
  Buffer.from(quote.data.solana.transaction.serialized, 'base64'),
);
// The user wallet signs because it owns the input funds.
let signed = await wallet.signTransaction(tx);

// If the quote used payerAddress and it differs from walletAddress,
// the payer must sign too because it pays tx fees / ATA rent.
if (centralFeePayerKeypair) {
  // With a local Keypair:
  signed.sign([centralFeePayerKeypair]);
}

// Or with a wallet adapter / remote signer:
// signed = await centralFeePayerWallet.signTransaction(signed);

const signedBase64 = Buffer.from(signed.serialize()).toString('base64');

await fetch('/api/2/swap/send', {
  method: 'POST',
  body: JSON.stringify({ chainId: 'solana:solana', signedTransaction: signedBase64 }),
});

Supported aggregators

Jupiter, KyberSwap, NAOS, Li.Fi — pick one or several via onlyRouters. Per-DEX filtering (Raydium, Orca, Meteora DLMM/Damm, Pump AMM, Whirlpool, …) via onlyProtocols / excludedProtocols.

Limits

  • Blockhash expirylastValidBlockHeight is ~150 slots (60s). Re-quote if the user takes longer than that to sign.
  • Priority budget — use prioritizationFeeLamports with dynamicComputeUnitLimit=true so the CU price is derived from the final compute limit. When bundling with Jito, set jitoTipLamports separately.
  • Address forms — Solana addresses are case-sensitive base58. Don’t lowercase.