DocumentationOpen App

Slippage & Safety

Control slippage, update on-demand price feeds, and optimize collateral checks. These safety mechanisms protect your multicalls from unexpected losses.

Slippage Protection

Why

You need slippage protection when performing swaps or multi-step operations. Without it, sandwich bots can exploit unprotected transactions and you may receive significantly fewer tokens than expected.

How It Works

Gearbox uses a two-step pattern:

  1. storeExpectedBalances - Record expected minimum balances BEFORE operations
  2. compareBalances - Verify actual balances meet expectations AFTER operations

If the final balance is less than expected, the entire multicall reverts.

Basic Usage

TypeScript
import { encodeFunctionData } from 'viem'; import { iCreditFacadeV300MulticallAbi } from '@gearbox-protocol/sdk'; // Calculate minimum expected output with 0.5% slippage tolerance const expectedOutput = 25n * 10n ** 18n; // 25 WETH const slippageBps = 50n; // 0.5% const minExpected = expectedOutput - (expectedOutput * slippageBps / 10000n); const calls = [ // 1. Store expected balance BEFORE swap { target: creditFacadeAddress, callData: encodeFunctionData({ abi: iCreditFacadeV300MulticallAbi, functionName: 'storeExpectedBalances', args: [[{ token: wethAddress, amount: minExpected }]], }), }, // 2. Perform the swap { target: uniswapV3Adapter, callData: encodeFunctionData({ abi: uniswapV3AdapterAbi, functionName: 'exactInputSingle', args: [swapParams], }), }, // 3. Verify slippage AFTER swap { target: creditFacadeAddress, callData: encodeFunctionData({ abi: iCreditFacadeV300MulticallAbi, functionName: 'compareBalances', args: [], }), }, ];

Multiple Tokens

Check slippage on multiple output tokens in a single store/compare pair:

TypeScript
const calls = [ // Store expectations for both tokens { target: creditFacadeAddress, callData: encodeFunctionData({ abi: iCreditFacadeV300MulticallAbi, functionName: 'storeExpectedBalances', args: [[ { token: wethAddress, amount: minWethExpected }, { token: usdcAddress, amount: minUsdcExpected }, ]], }), }, // Multiple swaps... { /* swap 1 */ }, { /* swap 2 */ }, // Single compare covers all stored expectations { target: creditFacadeAddress, callData: encodeFunctionData({ abi: iCreditFacadeV300MulticallAbi, functionName: 'compareBalances', args: [], }), }, ];

Slippage Gotchas

  • Keep checks close to operations - Place storeExpectedBalances immediately before swaps, and compareBalances immediately after. Internal operations like addCollateral between them can affect balances unexpectedly.
  • compareBalances without storeExpectedBalances reverts - You must always call store first.
  • Auto-compare at multicall end - If you forget compareBalances, the check happens automatically, but explicit is better.
  • Amount is a delta, not final balance - The amount in BalanceDelta is the change you expect, not the resulting balance.
  • Cannot reuse stored balances - After compareBalances, stored expectations are cleared. Call storeExpectedBalances again for subsequent swaps.

On-Demand Price Updates

Why

Some tokens use pull-based oracles (Pyth, Redstone) that don't update automatically. You must push fresh price data before operations that depend on accurate pricing.

How It Works

onDemandPriceUpdate pushes oracle data to the price feed. You obtain signed price data from the oracle provider off-chain, then include the update as the FIRST call in your multicall.

Critical rule: All price updates must be at the beginning of the calls array. Any onDemandPriceUpdate after another call type will revert.

Basic Usage

TypeScript
import { encodeFunctionData } from 'viem'; import { iCreditFacadeV300MulticallAbi } from '@gearbox-protocol/sdk'; // Get price data from oracle provider (e.g., Pyth) const priceData = await pythClient.getPriceUpdateData([feedId]); const calls = [ // Price update MUST be first { target: creditFacadeAddress, callData: encodeFunctionData({ abi: iCreditFacadeV300MulticallAbi, functionName: 'onDemandPriceUpdate', args: [ tokenAddress, // Token to update price for false, // reserve: false = main feed, true = reserve feed priceData, // Signed price data from oracle ], }), }, // Now other operations service.prepareAddCollateral(usdcAddress, amount), service.prepareIncreaseDebt(debtAmount), ];

Reserve Feed Updates (For Withdrawals)

Withdrawals trigger safe pricing, which uses both main and reserve feeds:

TypeScript
const calls = [ // Main feed update { target: creditFacadeAddress, callData: encodeFunctionData({ abi: iCreditFacadeV300MulticallAbi, functionName: 'onDemandPriceUpdate', args: [tokenAddress, false, mainPriceData], // reserve = false }), }, // Reserve feed update { target: creditFacadeAddress, callData: encodeFunctionData({ abi: iCreditFacadeV300MulticallAbi, functionName: 'onDemandPriceUpdate', args: [tokenAddress, true, reservePriceData], // reserve = true }), }, // Now the withdrawal will work with safe pricing service.prepareWithdrawCollateral(otherToken, amount, recipient), ];

Detecting Which Feeds Need Updates

TypeScript
import { priceFeedCompressorAbi } from '@gearbox-protocol/sdk'; const feedInfo = await client.readContract({ address: priceFeedCompressorAddress, abi: priceFeedCompressorAbi, functionName: 'getUpdatablePriceFeeds', args: [creditManagerAddress], }); // feedInfo contains tokens that need on-demand updates const tokensNeedingUpdate = feedInfo.filter(f => f.needsUpdate);

Price Update Gotchas

  • Price updates MUST be first - Any price update after a non-price-update call reverts.
  • Fresh data required - Price data has a short validity window (usually a few minutes). Generate it right before the transaction.
  • Not all tokens need updates - Only Pyth and Redstone feeds need on-demand updates. Chainlink feeds update automatically.
  • Disabled tokens don't need updates - If a token will be disabled by the end of the multicall, skip its price update.

Collateral Check Parameters

Why

You set collateral check params to optimize gas or enforce a minimum health factor above 1.0. The collateral check iterates through enabled tokens, summing value until it exceeds debt. Hints tell it which tokens to check first, potentially skipping expensive oracle calls.

How It Works

setFullCheckParams configures two things:

  1. Collateral hints - Token masks to prioritize during the check
  2. Min health factor - Minimum acceptable HF (in basis points, 10000 = 1.0)

Basic Usage

TypeScript
import { encodeFunctionData } from 'viem'; import { iCreditFacadeV300MulticallAbi, creditManagerAbi } from '@gearbox-protocol/sdk'; import { getContract } from 'viem'; const creditManager = getContract({ address: cmAddress, abi: creditManagerAbi, client: publicClient, }); // Get token masks (each token has a unique bitmask) const usdcMask = await creditManager.read.getTokenMaskOrRevert([usdcAddress]); const wethMask = await creditManager.read.getTokenMaskOrRevert([wethAddress]); const calls = [ // Set hints at the start of multicall { target: creditFacadeAddress, callData: encodeFunctionData({ abi: iCreditFacadeV300MulticallAbi, functionName: 'setFullCheckParams', args: [ [usdcMask, wethMask], // Check these tokens first 10000, // minHealthFactor: 1.0 (10000 bps) ], }), }, // Rest of your multicall service.prepareAddCollateral(usdcAddress, amount), ];

Setting Higher Min Health Factor

Require the account to maintain at least 1.2 HF:

TypeScript
const MIN_HF_120 = 12000; // 1.2 in basis points const calls = [ { target: creditFacadeAddress, callData: encodeFunctionData({ abi: iCreditFacadeV300MulticallAbi, functionName: 'setFullCheckParams', args: [ [], // No hints MIN_HF_120, ], }), }, // Operations... ];

Check Params Gotchas

  • Use masks, not addresses - The hints array takes token masks from getTokenMaskOrRevert, not token addresses.
  • Hints are optimization, not guarantee - All enabled tokens are still validated; hints just change the evaluation order.
  • Min health factor must be >= 10000 - You cannot set a health factor below 1.0.
  • Params reset after multicall - setFullCheckParams only affects the current multicall's final check. The next multicall uses defaults.
  • Small accounts don't benefit - For accounts with fewer than 5 enabled tokens, hints add overhead without meaningful gas savings.
  • Order matters - Put your highest-value collateral first in the hints array for the best gas savings.

Learn More