External Calls
Interact with external protocols (Uniswap, Curve, Yearn, etc.) from your credit account through whitelisted adapter contracts.
Why
You make external calls when:
- Swapping tokens - Trade via Uniswap, Curve, or other DEXs
- Depositing to vaults - Stake in Yearn, Lido, or yield strategies
- Managing LP positions - Add/remove liquidity on various protocols
- Executing complex strategies - Chain multiple protocol interactions
What
External calls flow through adapters:
- You encode a call targeting an adapter address
- Credit Facade routes the call to the adapter
- Adapter builds the actual calldata for the external protocol
- Adapter requests token approvals if needed
- Credit Manager executes the call from the Credit Account
- Credit Account acts as the "user" from the external protocol's perspective
- Adapter returns which tokens to enable/disable based on the operation
Key insight: The Credit Account makes the actual call, so it receives the output tokens directly. You never touch the funds - they stay in the Credit Account.
How
Step 1: Get Adapter Address
import { getContract } from 'viem'; import { creditManagerAbi } from '@gearbox-protocol/sdk'; const creditManager = getContract({ address: cmAddress, abi: creditManagerAbi, client: publicClient, }); // Get adapter for a protocol (e.g., Uniswap V3 Router) const uniswapV3Adapter = await creditManager.read.contractToAdapter([ UNISWAP_V3_ROUTER, ]); // Returns 0x0 if no adapter exists for this protocol if (uniswapV3Adapter === '0x0000000000000000000000000000000000000000') { throw new Error('No adapter for this protocol'); }
Step 2: Encode the Adapter Call
import { encodeFunctionData } from 'viem'; import { uniswapV3AdapterAbi } from '@gearbox-protocol/integrations-v3'; const swapParams = { tokenIn: usdcAddress, tokenOut: wethAddress, fee: 500, recipient: '0x0000000000000000000000000000000000000000', // Adapter overrides this deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), amountIn: 50_000n * 10n ** 6n, amountOutMinimum: 24n * 10n ** 18n, // Slippage protection sqrtPriceLimitX96: 0n, }; const calls = [ { target: uniswapV3Adapter, callData: encodeFunctionData({ abi: uniswapV3AdapterAbi, functionName: 'exactInputSingle', args: [swapParams], }), }, ];
Complete Example: Swap with Slippage Protection
import { encodeFunctionData } from 'viem'; import { GearboxSDK, createCreditAccountService, iCreditFacadeV300MulticallAbi, } from '@gearbox-protocol/sdk'; const sdk = await GearboxSDK.attach({ client, marketConfigurators: [] }); const service = createCreditAccountService(sdk, 310); const market = sdk.marketRegister.findByCreditManager(cmAddress); // Get adapter const uniswapV3Adapter = await market.creditManager.read.contractToAdapter([ UNISWAP_V3_ROUTER, ]); const calls = [ // Add collateral (SDK helper) service.prepareAddCollateral(usdcAddress, 50_000n * 10n ** 6n), // Borrow (SDK helper) service.prepareIncreaseDebt(200_000n * 10n ** 6n), // Slippage protection start { target: market.creditFacade.address, callData: encodeFunctionData({ abi: iCreditFacadeV300MulticallAbi, functionName: 'storeExpectedBalances', args: [[{ token: wethAddress, amount: 99n * 10n ** 18n }]], }), }, // Swap via adapter (manual encoding) { target: uniswapV3Adapter, callData: encodeFunctionData({ abi: uniswapV3AdapterAbi, functionName: 'exactInputSingle', args: [swapParams], }), }, // Slippage protection end { target: market.creditFacade.address, callData: encodeFunctionData({ abi: iCreditFacadeV300MulticallAbi, functionName: 'compareBalances', args: [], }), }, // Set quota for received token (SDK helper) service.prepareUpdateQuota(wethAddress, 250_000n * 10n ** 6n, 250_000n * 10n ** 6n), ]; // Approve collateral to Credit Manager await usdcContract.write.approve([market.creditManager.address, 50_000n * 10n ** 6n]); // Execute await market.creditFacade.write.openCreditAccount([ownerAddress, calls, 0n]);
Diff Functions
Many adapters have _diff variants that operate on "entire balance minus 1":
// Instead of specifying exact amount... { functionName: 'deposit', args: [exactAmount] } // Use diff to deposit all USDC (minus 1 wei) { functionName: 'depositDiff', args: [1n] }
This is useful when you don't know the exact balance after previous operations.
Gotchas
Adapter ABIs Need Separate Import
SDK exports core ABIs, but adapter ABIs often need a separate import:
// Core ABIs from SDK import { iCreditFacadeV300MulticallAbi } from '@gearbox-protocol/sdk'; // Adapter ABIs from integrations package import { uniswapV3AdapterAbi } from '@gearbox-protocol/integrations-v3';
Not All Protocols Have Adapters
An adapter must exist for each protocol you want to interact with. Check with contractToAdapter - it returns the zero address if no adapter exists.
Recipient Parameter is Overridden
Many DEX functions have a recipient parameter. Adapters override this to ensure tokens go to the Credit Account, not an arbitrary address. You can pass any value.
Always Use Slippage Protection
External calls are vulnerable to sandwich attacks. Always wrap swaps with slippage checks using storeExpectedBalances / compareBalances.
Token Enable/Disable is Automatic
After adapter calls, tokens are automatically enabled/disabled based on balance changes. You usually don't need manual enableToken/disableToken after adapter calls.
Learn More
- Slippage & Safety - Protect your swaps
- Multicalls - Combining SDK helpers with manual encoding
- Token Management - Manual token management