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.
| Field | Description |
|---|---|
| amountOut | What the user receives (after fee). Use this for display and slippage. |
| grossTotalOutput | Total output before fee. grossTotalOutput - protocolFee = amountOut. |
| singleDexBestOutput | Best single-pool output. Shows how much the split saved. Pass to swapWithRoute. |
| protocolFee | 15% 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
tokenOutis 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 thevaluefield - >Or use Permit2 for gasless signature-based approvals
> Deployed Addresses
Router Contracts
Permit2 (All Chains)
> Fee Model
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
| Method | Use 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