Integrate Cleave

On-chain split routing across concentrated liquidity pools. No API keys. No off-chain infrastructure. One contract call.

> Why Integrate

Transparent Execution

Every swap emits a SwapExecuted event with gross output, single-DEX baseline, and net savings. Your users can verify every trade.

One Contract Call

No API keys, no off-chain dependencies. Import the interface, call swap(), done. Works from any smart contract or frontend.

Aligned Incentives

15% of the improvement Cleave finds. If there's no improvement, the fee is zero. Cleave only earns when your users get better prices.

> Frontend Integration — Getting a Quote

Call quoteSwap() from your frontend using viem, ethers.js, or any EVM library. It's a free view call — no gas, no state changes.

Step 1 — Call quoteSwap

Pass the ERC-20 addresses (use WETH/WBNB for native tokens) and the raw input amount in the token's smallest unit.

// viem example
import { createPublicClient, http } from "viem";
import { mainnet } from "viem/chains";

const ROUTER = "0x6f1f8b1276bbcc6b79d5b65b5aff4e80267a0b19";
const WETH   = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
const USDC   = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";

const client = createPublicClient({
  chain: mainnet,
  transport: http(),
});

const [
  amountOut,            // Net output after protocol fee
  grossTotalOutput,     // Total output before fee
  singleDexBestOutput,  // Best single-pool output (benchmark)
  protocolFee,          // Fee charged (15% of savings, 0 if no savings)
  allocations,          // Input amounts routed to each pool
  pools,                // Pool addresses used in the route
] = await client.readContract({
  address: ROUTER,
  abi: CLEAVE_ROUTER_ABI,
  functionName: "quoteSwap",
  args: [WETH, USDC, 1000000000000000000n], // 1 ETH
});

Step 2 — Parse the Quote

The six return values tell you everything about the optimal route.

FieldDescription
amountOutWhat the user receives (after fee). Use this for display and slippage.
grossTotalOutputTotal output before fee. grossTotalOutput - protocolFee = amountOut.
singleDexBestOutputBest single-pool output. Shows how much the split saved. Pass to swapWithRoute.
protocolFee15% of (grossTotal - singleBest). Zero when no savings found.
allocations[]Raw input amounts split across pools (not percentages). Sum = amountIn.
pools[]Pool addresses matching each allocation. Pass directly to swapWithRoute.

Step 3 — Execute the Swap

Use the quote result to build a swapWithRoute() call. This skips on-chain pool discovery and saves ~90% gas vs swap().

import { parseEther } from "viem";

const amountIn = parseEther("1");       // 1 ETH
const slippageBps = 50n;                // 0.5%
const minAmountOut = amountOut * (10000n - slippageBps) / 10000n;
const deadline = BigInt(Math.floor(Date.now() / 1000) + 1200); // 20 min

// Build zeroForOne: true when tokenIn address < tokenOut address
const zeroForOne = pools.map(() => WETH.toLowerCase() < USDC.toLowerCase());

const hash = await walletClient.writeContract({
  address: ROUTER,
  abi: CLEAVE_ROUTER_ABI,
  functionName: "swapWithRoute",
  args: [
    {
      tokenIn:      WETH,
      tokenOut:     USDC,
      amountIn:     amountIn,
      minAmountOut: minAmountOut,
      deadline:     deadline,
      recipient:    userAddress,
    },
    {
      pools:               pools,
      zeroForOne:           zeroForOne,
      allocations:          allocations,
      singleDexBestOutput:  singleDexBestOutput,
    },
  ],
  value: amountIn,  // Send ETH with tx (only for native token input)
});

Native Token Handling

  • >Use the WETH/WBNB address as tokenIn — not the native token address
  • >Send the native token amount as msg.value — the contract wraps it internally
  • >No ERC-20 approval needed for native token swaps
  • >If tokenOut is WETH, the contract unwraps and sends native ETH to the recipient

ERC-20 Token Swaps

  • >Approve the router before swapping: token.approve(ROUTER, amountIn)
  • >Do not send msg.value — omit the value field
  • >Or use Permit2 for gasless signature-based approvals

> Deployed Addresses

Router Contracts

Permit2 (All Chains)

0x000000000022D473030F116dDEE9F6B43aC78BA3

> Fee Model

Protocol Fee15% of savings
When No SavingsZero fee

Cleave charges 15% of the additional output it finds for you. If split routing doesn't beat the best single pool, the fee is zero. You never pay for a worse outcome.

>Contract Details — Solidity Examples

Integration Methods

MethodUse Case
quoteSwap()Get optimal route and expected output (free view call)
swap()Simple integration with on-chain pool discovery
swapWithPermit2()Gasless approvals via Uniswap Permit2 signatures
swapWithRoute()Execute with a pre-computed route, skip on-chain discovery
swapWithRouteAndPermit2()Pre-computed route with gasless approvals

Quick Start — Basic Swap

The simplest integration — approve, quote, and swap in one transaction.

ICleaveRouter router = ICleaveRouter(ROUTER_ADDRESS);

// Approve the router to spend your tokens
IERC20(tokenIn).approve(address(router), amountIn);

// Get a quote (free view call)
(uint256 amountOut, , , , , ) = router.quoteSwap(
    tokenIn, tokenOut, amountIn
);

// Execute with slippage protection
uint256 received = router.swap(SwapParams({
    tokenIn:      tokenIn,
    tokenOut:     tokenOut,
    amountIn:     amountIn,
    minAmountOut: amountOut * 995 / 1000,  // 0.5% slippage
    deadline:     block.timestamp + 1200,
    recipient:    msg.sender
}));

Advanced — Off-Chain Quote, On-Chain Execution

Call quoteSwap() off-chain to compute the optimal split, then execute with swapWithRoute() — skipping on-chain pool discovery.

// 1. Quote off-chain (free view call)
(uint256 amountOut, , uint256 singleBest, ,
 uint256[] memory allocs, address[] memory pools)
    = router.quoteSwap(tokenIn, tokenOut, amountIn);

// 2. Build the pre-computed route
bool[] memory zeroForOne = new bool[](pools.length);
for (uint256 i; i < pools.length; i++) {
    zeroForOne[i] = tokenIn < tokenOut;
}

PrecomputedRoute memory route = PrecomputedRoute({
    pools:                pools,
    zeroForOne:           zeroForOne,
    allocations:          allocs,
    singleDexBestOutput:  singleBest
});

// 3. Execute with the pre-computed route
uint256 received = router.swapWithRoute(
    SwapParams({
        tokenIn:      tokenIn,
        tokenOut:     tokenOut,
        amountIn:     amountIn,
        minAmountOut: amountOut * 995 / 1000,
        deadline:     block.timestamp + 1200,
        recipient:    msg.sender
    }),
    route
);

Advanced — Gasless Approvals with Permit2

Use Uniswap's Permit2 standard for one-time approvals and gasless signature-based swaps on every subsequent trade.

// One-time: approve Permit2 to spend your token
IERC20(tokenIn).approve(PERMIT2_ADDRESS, type(uint256).max);

// Per-swap: sign a permit off-chain, submit with swap
router.swapWithPermit2(
    SwapParams({
        tokenIn:      tokenIn,
        tokenOut:     tokenOut,
        amountIn:     amountIn,
        minAmountOut: minOut,
        deadline:     deadline,
        recipient:    msg.sender
    }),
    Permit2SignatureTransferData({
        permit:    permitData,
        signature: signature
    })
);

Reading the Quote (Solidity)

The quoteSwap function returns detailed routing information for display or decision-making.

(
    uint256 amountOut,          // Net output after fee
    uint256 grossTotalOutput,   // Total before fee
    uint256 singleDexBestOut,   // Best single pool output
    uint256 protocolFee,        // Fee charged (15% of savings)
    uint256[] allocations,      // Amount routed to each pool
    address[] pools             // Pool addresses used
) = router.quoteSwap(tokenIn, tokenOut, amountIn);

// If amountOut > singleDexBestOut, split routing saved money
// protocolFee = 0 when no savings found