> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bebop.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Falling back to RFQ

> Use the /quote endpoint to get swapWithFallback calldata that tries BopAMM first and settles via RFQ if the on-chain book can't fill.

`BopAmmRouter.swapWithFallback()` is BopAMM's safety net for high-value flow that can't tolerate a same-block-execution failure or insufficient on-chain liquidity. The function tries the BopAMM leg in a try/catch and falls through on **any** revert to an allowlisted fallback adapter with pre-encoded calldata. The standard fallback adapter executes against Bebop RFQ settlement.

You don't compose this calldata yourself. The BopAMM `/quote` endpoint builds the whole thing: it prepares the BopAMM leg, fetches a matching RFQ quote, and returns a ready `swapWithFallback(...)` call in the response `tx`. You sign and submit it like any other transaction. There's no separate RFQ request, no EIP-712 signature, and no calldata splicing on your side.

You burn more gas than a plain `swapWithAllowance()`, but you trade reliably even when the BopAMM leg can't land or the book is too thin.

## When to use it

* **High-value flow** where a single revert is more expensive than the gas overhead of carrying a fallback.
* **Long-tail assets** with sparse maker coverage on BopAMM where the on-chain book may not always reach `minAmountOut`.
* **Bursty traffic** where you want to avoid coordinating around builder windows for every swap.

If you're routing small, frequent flow on majors and can submit through a supported builder, plain `swapWithAllowance()` is cheaper. Use `swapWithFallback` selectively rather than as the default.

## Get the fallback calldata

Request a quote from the BopAMM `/quote` endpoint. When your partner key has the RFQ fallback feature, the response's `tx.data` is a complete `BopAmmRouter.swapWithFallback(...)` call.

```text theme={null}
GET https://api.bebop.xyz/bopamm/ethereum/v1/quote
Authorization: Bearer <your-api-key>
```

| Parameter | Notes |
| - | - |
| `sell_tokens`, `buy_tokens` | Single token addresses. Use `0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE` for native ETH at the router boundary. |
| `sell_amounts` | Integer base units (`buy_amounts` is not supported yet). |
| `taker_address` | The EOA that will send the transaction. |
| `receiver_address` | Optional. Defaults to `taker_address`. Applies to both legs (see caveats). |
| `slippage` | Optional decimal fraction (for example `0.005`). Sets the router `minAmountOut`. Defaults to `0.003`. |
| `approval_type` | Must be `Standard`. |
| `gasless` | Must be `false`. BopAMM quotes are self-executed; `gasless=true` is rejected. |
| `fee` | Optional integrator fee in bps, capped at 1,000. Charged only when the BopAMM leg succeeds. |
| `fee_recipient` | Required when `fee` is non-zero. Receives the integrator share of the router fee. |
| `fee` | Optional integrator fee in bps of gross BopAMM output, capped at 1,000 bps. See [Fees](/bopamm/guides/fees). |
| `fee_recipient` | Required when `fee` is non-zero. Receives the integrator share. |

```python theme={null}
amount_in = 1 * 10**USDC_DECIMALS  # sell 1 USDC

quote = httpx.get(
    f"{API_BASE}/quote",
    params={
        "sell_tokens": USDC,
        "buy_tokens": WETH,
        "sell_amounts": str(amount_in),
        "taker_address": account.address,
        "slippage": "0.005",       # 0.5% minAmountOut tolerance
        "approval_type": "Standard",
        "gasless": "false",        # required: BopAMM quotes are self-executed
        "fee": "25",               # optional: 0.25% on successful BopAMM output
        "fee_recipient": "0x<your-fee-recipient>"
    },
    headers={"Authorization": f"Bearer {API_KEY}"},
).json()
```

```python theme={null}
amount_in = 1 * 10**USDC_DECIMALS  # sell 1 USDC

quote = httpx.get(
    f"{API_BASE}/quote",
    params={
        "sell_tokens": USDC,
        "buy_tokens": WETH,
        "sell_amounts": str(amount_in),
        "taker_address": account.address,
        "slippage": "0.005",       # 0.5% minAmountOut tolerance
        "approval_type": "Standard",
        "gasless": "false",        # required: BopAMM quotes are self-executed
    },
    headers={"Authorization": f"Bearer {API_KEY}"},
).json()
```

The response is a standard Bebop quote. The fields that matter for the fallback path:

```json theme={null}
{
  "requestId": "e0c1f1e4-6d5f-4a9c-8f63-6f47f7c13f1a",
  "type": "121",
  "status": "Success",
  "quoteId": "121-1780382915-e0c1f1e4",
  "chainId": 1,
  "expiry": 1780382915,
  "slippage": 0.5,
  "approvalType": "Standard",
  "nativeToken": "ETH",
  "taker": "0xC205Eb18a2B0E05cdCC11961d853F36da232da9f",
  "receiver": "0xC205Eb18a2B0E05cdCC11961d853F36da232da9f",
  "settlementAddress": "0xB098881c587f623FAC85eAe60809bEE7A174CeE7",
  "approvalTarget": "0xB098881c587f623FAC85eAe60809bEE7A174CeE7",
  "requiredSignatures": [],
  "sellTokens": {
    "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48": {
      "amount": "1000000",
      "decimals": 6,
      "symbol": "USDC"
    }
  },
  "buyTokens": {
    "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2": {
      "amount": "504024636724243",
      "minimumAmount": "501504513540621",
      "decimals": 18,
      "symbol": "WETH"
    }
  },
  "tx": {
    "to": "0xB098881c587f623FAC85eAe60809bEE7A174CeE7",
    "value": "0x0",
    "data": "0x...calldata..."
  }
}
```

| Field | Description |
| - | - |
| `tx.to` | The BopAMM router. Same as `settlementAddress` and `approvalTarget`. |
| `tx.data` | The calldata for `swapWithFallback`, with the RFQ settlement embedded as adapter calldata. Send it as-is. |
| `tx.value` | `0x0`, or `amountIn` in hex when selling native ETH. |
| `requiredSignatures` | Empty. Nothing to sign off-chain - the transaction is the order. |
| `buyTokens[].amount` | Expected BopAMM output after router fees and before slippage. Expected BopAMM output after any router fee and before slippage. |
| `buyTokens[].minimumAmount` | The router `minAmountOut`, derived from your net amount and `slippage`. The router `minAmountOut`, derived from the post-fee amount and your `slippage`. |
| `expiry` | The shared deadline for both legs (see caveats). |

## Submit it

Approve the BopAMM router for `tokenIn` if you haven't already (see [Approve the BopAMM router](/bopamm/quickstart#1-approve-the-bopamm-router)), then build, sign, and submit `tx`. Unlike a plain `swapWithAllowance()`, you don't have to route this through a builder: the fallback is caught inside the same transaction, so a public-mempool submission can still settle via RFQ when the BopAMM leg can't land.

```python theme={null}
to = Web3.to_checksum_address(quote["tx"]["to"])
data = quote["tx"]["data"]
value = int(quote["tx"]["value"], 16)

estimated_gas = web3.eth.estimate_gas(
    {"from": account.address, "to": to, "data": data, "value": value}
)

tx_req = {
    "from": account.address,
    "to": to,
    "data": data,
    "value": value,
    "nonce": web3.eth.get_transaction_count(account.address),
    "gas": int(estimated_gas * 1.5),
    "gasPrice": web3.eth.gas_price,
    "chainId": web3.eth.chain_id,
}

signed_tx = web3.eth.account.sign_transaction(tx_req, private_key=PRIVATE_KEY)
tx_hash = web3.eth.send_raw_transaction(signed_tx.raw_transaction)
print(Web3.to_hex(tx_hash))
```

## What the calldata encodes

You don't assemble this yourself, but it helps to know what `tx.data` decodes to. The on-chain function is:

```solidity theme={null}
function swapWithFallback(
    address tokenIn,
    address tokenOut,
    uint256 amountIn,
    uint256 minAmountOut,
    address recipient,
    uint256 deadline,
    address adapter,
    bytes calldata adapterData,
    uint256 fee
) external payable returns (uint256 amountOut, bool usedFallback);
```

The first six parameters define the BopAMM leg. The last three carry the fallback adapter and router fee configuration.

| Parameter | Applies to | Set by the endpoint to |
| - | - | - |
| `tokenIn`, `tokenOut`, `amountIn` | BopAMM leg | Your requested pair and sell amount. Native ETH uses the sentinel address. |
| `minAmountOut` | Both legs | `buyTokens[].minimumAmount`, derived from the post-fee amount and your `slippage`. |
| `recipient` | Output delivery | Your `receiver_address`, or `taker_address` when omitted. |
| `deadline` | Both legs | The RFQ quote's expiry, so the on-chain deadline matches the fallback's. |
| `adapter` | Fallback leg | An allowlisted Bebop fallback adapter. |
| `adapterData` | Fallback leg | The signed RFQ settlement calldata. |
| `fee` | BopAMM leg | Packed from `fee` and `fee_recipient`, or `0` when omitted. |

## Fees

To have the endpoint encode a router fee, pass both `fee` and `fee_recipient` to `/quote`. The endpoint packs the fee into the returned `swapWithFallback` calldata and returns `buyTokens[].amount` / `minimumAmount` net of the BopAMM router fee.

```text theme={null}
fee=25
fee_recipient=0x<your-fee-recipient>
```

The router fee is charged only when the BopAMM leg succeeds. A configured percentage of the fee goes to the protocol, and the rest goes to `fee_recipient`. Fallback output is not charged by the BopAMM router.

## Caveats

A few BopAMM-specific behaviors to be aware of:

* **`minAmountOut` protects both paths.** On the BopAMM path it is checked after router fees. On the fallback path it is checked against fallback output.
* **Fallback output is not router-fee charged.** Any RFQ terms or fees are part of the embedded RFQ calldata.
* **One shared `deadline`.** The endpoint sets the BopAMM leg's `deadline` to the RFQ quote's expiry and reports it as the response `expiry`. Your whole transaction must land before then.
* **Native ETH is router-only.** The router maps the native ETH sentinel to WETH internally and unwraps WETH for native output. Pairs that normalize to the same token are invalid.
* **Residual sweep.** If the fallback leg consumes less than `amountIn`, any residual `tokenIn` or ETH is swept back to `msg.sender` automatically.

## Events

The events emitted depend on which leg actually settled:

| Leg taken | Event |
| - | - |
| BopAMM succeeded | Core `Swapped(sender=router, ...)`, one core `MakerFill` per filling maker, and `FeeCharged` when a non-zero direct router fee is used. |
| Fallback taken | Router `SwapFallback(user, adapter, tokenIn, tokenOut, amountIn, amountOut)`. |

Off-chain, watch for `SwapFallback` to detect when your fallback paths are firing. A spike in fallback usage on a specific asset usually means the BopAMM book is too thin for your typical size on that pair - either size down, route elsewhere, or contact the team to broaden maker coverage.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.