Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.nexroute.io/llms.txt

Use this file to discover all available pages before exploring further.

In this shape, nexroute returns the backrun as (target, callData). You compose that call into your own contract (a multicall, a post-hook on your existing router, a custom wrapper) so it executes atomically after your swap in the same transaction. Use this shape when you operate your own router or smart account and want full control over how the user transaction is constructed.

When to choose this shape

  • You operate your own router or smart-account stack.
  • You want full control over transaction construction (token routing, hooks, custom logic).
  • Your contract has a way to execute a (target, callData) after the main swap (multicall, post-hook, custom path).

Flow

  1. Get aggregator swap calldata in your normal flow.
  2. Ask nexroute for a backrun via the backrun API. The response includes a backrun call descriptor.
  3. Construct the user transaction so the backrun call executes atomically after the swap.
  4. Submit through your normal channel.

Atomicity requirements

The backrun must execute in the same transaction as the user’s swap, after the swap completes. Examples:
  • Multicall: bundle the swap and the backrun in a single multicall: multicall([swapCall, backrunCall]).
  • Router with post-hook: pass (target, callData) as the post-hook argument so your router invokes it after the swap.
  • Custom wrapper: include the backrun call inline in your wrapper’s execute path.
The backrun call must NOT be allowed to revert the parent transaction. Wrap it in a try/catch (or perform a low-level call whose return value is ignored) so a failed backrun does not roll back the user’s swap.

Funds and approvals

The backrun is self-contained. It does not consume the user’s input or the swap’s output, and it does not require any additional approvals from the user beyond what your existing router already needs.

What you receive from nexroute

{
    "target": "0x...",
    "callData": "0x..."
}
FieldDescription
targetthe address your transaction must call atomically after the swap; null when no arbitrage opportunity is found
callDatathe calldata to send to target; null when no arbitrage opportunity is found
When both fields are null, no arbitrage opportunity was found and there is no backrun to include. Submit the user’s swap on its own.

What you build

The exact composition depends on your stack. Two illustrative patterns: Multicall composition
function multicall(Call[] calldata calls) external {
    for (uint256 i = 0; i < calls.length; i++) {
        Call memory c = calls[i];
        if (c.allowFailure) {
            c.target.call(c.callData); // ignore failure
        } else {
            (bool ok, bytes memory ret) = c.target.call(c.callData);
            if (!ok) { /* revert with ret */ }
        }
    }
}

// off-chain construction:
multicall([
    { target: aggregator, callData: swapData,    allowFailure: false },
    { target: nexroute,   callData: backrunData, allowFailure: true  },
])
Router post-hook
function swapWithHook(
    bytes calldata swapData,
    address hookTarget,
    bytes calldata hookData
) external {
    aggregator.call(swapData); // user swap, reverts on failure
    if (hookTarget != address(0)) {
        hookTarget.call(hookData); // best-effort, ignore failure
    }
}

Failure semantics

  • Backrun failure: best-effort, ignored. The user’s swap still completes.
  • If your composition does check the backrun’s return value and reverts on failure, a stale quote can fail the user’s transaction. Always treat the backrun call as non-blocking.

Submission

Same as the wrapper-contract shape: any RPC, paired with a private relay when mempool exposure is a concern.