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:
storeExpectedBalances- Record expected minimum balances BEFORE operationscompareBalances- Verify actual balances meet expectations AFTER operations
If the final balance is less than expected, the entire multicall reverts.
Basic Usage
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:
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
storeExpectedBalancesimmediately before swaps, andcompareBalancesimmediately after. Internal operations likeaddCollateralbetween them can affect balances unexpectedly. compareBalanceswithoutstoreExpectedBalancesreverts - 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
amountinBalanceDeltais the change you expect, not the resulting balance. - Cannot reuse stored balances - After
compareBalances, stored expectations are cleared. CallstoreExpectedBalancesagain 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
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:
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
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:
- Collateral hints - Token masks to prioritize during the check
- Min health factor - Minimum acceptable HF (in basis points, 10000 = 1.0)
Basic Usage
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:
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 -
setFullCheckParamsonly 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
- External Calls - Adapter calls that need slippage protection
- Multicalls - Building and executing multicalls
- Withdrawing Collateral - May need reserve feed updates