Build a custom multicall
A multicall executes an ordered list of Credit Account actions in one transaction. Use it when you want to choose the individual steps instead of using a prepared operation from Manage or following a delayed operation's intent.
Calls and their targets
Each MultiCall entry contains:
| Field | Meaning |
|---|---|
target | Credit Facade for account actions, or an adapter allowed by this Credit Manager for protocol interactions |
callData | Encoded function and arguments for that target |
Use SDK helpers to encode account actions and the router to generate supported conversion paths. Encode adapter calls directly only for operations the router does not cover; do not substitute a protocol contract or token address as the target. The batch executes in order and reverts if a call or required account check fails.
Available actions
Credit Facade actions
These are the supported Credit Facade multicall entries for V3.1.
Their target is the account's Credit Facade. Token amounts use integer base
units, not human-readable decimals: 1_000_000n represents 1 token with 6
decimals. In TypeScript, use bigint for amounts, quotas, and bitmasks.
MAX_UINT256 below means 2^256 - 1.
Debt changes must respect market limits and cannot be repeated in the same block for an account. Full repayment requires all quotas to be disabled. Opening, closing, liquidation, and bot-driven batches have additional permissions; this guide uses an owner-driven update of an existing account.
These are entries inside the batch. Functions such as multicall,
openCreditAccount, and closeCreditAccount are transaction entry points,
not calls to nest inside it.
| Action | Arguments and behavior |
|---|---|
addCollateral | Transfer approved tokens from the caller into the Credit Account. The caller must approve the Credit Manager.
|
addCollateralWithPermit | Add collateral using an EIP-2612 permit. The caller signs the permit with the Credit Manager as spender and the funding amount as value.
|
increaseDebt | Borrow underlying into the Credit Account.
|
decreaseDebt | Repay using underlying already held by the Credit Account.
An amount covering the total debt requests full repayment. Use |
withdrawCollateral | Transfer collateral from the Credit Account to a recipient.
For a supported phantom token, withdrawal redeems it and sends its deposited token to the recipient. |
updateQuota | Increase, reduce, or disable a collateral token's quota.
A positive change increases quota; a negative change reduces it. Use |
setBotPermissions | Set or revoke a bot's access to the Credit Account.
Combine permission bits with bitwise OR:
A nonzero grant must exactly match the bot's |
onDemandPriceUpdates | Submit price updates. This call must be first in the batch.
Each update contains:
|
storeExpectedBalances | Record minimum balances to check after subsequent operations.
Each delta contains:
The stored floor is the balance at this call plus the delta. A positive delta requires a gain, a negative delta permits a bounded spend, and zero requires the starting balance to be preserved. For example, 100 starting units with a delta of −30 sets a floor of 70. The floor cannot be negative. Call |
compareBalances | No arguments. Check that current balances are at least the stored floors, then clear the expectations. Reverts if no expectations were stored. Outstanding expectations are checked automatically at the end of the batch. |
setFullCheckParams | Configure the final collateral check. Unavailable during closure or liquidation.
Each hint is an individual, single-bit collateral token mask from the Credit Manager. Token addresses, combined masks, and the underlying mask are not valid. For the health factor, |
Adapter actions
Adapter actions depend on the integrations enabled for the selected Credit Manager. Common categories include:
| Action | Examples |
|---|---|
| Swap | Exchange tokens through an allowed DEX adapter |
| Deposit or mint | Convert tokens into vault shares or a position token |
| Withdraw or redeem | Convert shares or position tokens back into assets |
| Add or remove liquidity | Enter or exit a liquidity pool |
| Stake or unstake | Enter or exit a supported staking position |
| Claim rewards | Collect rewards from a supported position |
| Request redemption | Start a delayed operation |
| Claim redemption | Collect proceeds through the integration's claim adapter |
| Integration-specific operations | For example, receive a Midas greenlisting or transfer a redeemer where permitted |
There is no single fixed list of adapter methods across all markets. For
supported conversions, the router assembles the adapter chain for you. Use the
selected suite's creditManager.adapters and each adapter's ABI to identify
its supported calls. A method's presence does not remove its permissions or
integration-specific requirements.
Select the account
Start with the connected sdk, owner, and position from
Manage.
import type { MultiCall } from "@gearbox-protocol/sdk/onchain"; const chainId = position.chainId; const chain = sdk.onchain.chain(chainId); const account = await chain.accounts.getCreditAccountData(position.creditAccount); if (!account) throw new Error("Credit Account not found"); const suite = chain.marketRegister.findCreditManager(account.creditManager); const facade = suite.creditFacade;
Assemble the calls
For example, repay part of a loan with wallet funds: add underlying → repay.
Here, repayAmount is a positive bigint in the pool underlying's base units.
This example uses an ordinary ERC-20 underlying; RWA markets may require
additional wrapping or factory-specific steps.
if (repayAmount <= 0n) throw new Error("Repayment must be positive"); const calls: MultiCall[] = [ ...facade.prepareAddCollateral([ { token: account.underlying, balance: repayAmount }, ], {}), facade.prepareChangeDebt(repayAmount, true), // true means decrease debt ];
Approve the funding amount to the SDK-resolved spender before execution, as described in Repay. Partial repayment must leave debt above the market minimum. These helpers encode calls; they do not calculate an affordable amount or validate the operation.
For other batches, order the steps by their dependencies:
- Claim → convert → repay → withdraw: claim before spending its proceeds.
- Quotas: include required quota updates when changing collateral; the transaction builder does not derive them for you.
- Swap limits: preserve the route's minimum outputs and balance checks.
- Final state: leave sufficient eligible collateral for the remaining debt. A claimed token may need conversion before its value counts toward HF.
Generate adapter calls with the router
The router is an onchain contract that builds a conversion path using the adapters available in the relevant Credit Manager. It returns the ordered adapter calls needed to execute that path.
Use chain.routerFor(account) to find a conversion path through the market's
supported integrations. This is a read-only quote: it returns ordered
route.calls to insert into your batch, not a router transaction to send.
As an alternative to the wallet-funded example above, convert collateral →
repay. Here, tokenIn differs from the underlying, inputBalance is its
available balance when the route executes, and swapAmount is the portion to
convert. Amounts are bigint base units; slippage is in basis points
(100 means 1%).
if (swapAmount <= 0n || swapAmount > inputBalance) { throw new Error("Invalid conversion amount"); } const route = await chain.routerFor(account).findManyToOnePath({ creditAccount: account, creditManager: suite.creditManager, expectedBalances: [{ token: tokenIn, balance: inputBalance }], leftoverBalances: [{ token: tokenIn, balance: inputBalance - swapAmount }], target: account.underlying, slippage, }); if (repayAmount <= 0n || repayAmount > route.minAmount) { throw new Error("Repayment exceeds the route's minimum output"); } const calls: MultiCall[] = [ ...route.calls, facade.prepareChangeDebt(repayAmount, true), ];
Preserve the returned sequence and its slippage and balance checks. route.amount
is the quoted output; route.minAmount is the slippage-adjusted minimum. The
example caps repayment at that minimum and must still satisfy debt, quota,
and health checks. Build and preview the complete batch below.
| Routing need | SDK method |
|---|---|
| Convert a single token's full balance | findOneTokenPath |
| Convert selected balances while retaining explicit amounts | findManyToOnePath with expectedBalances and leftoverBalances |
| Convert collateral and borrowed funds when opening | findOpenStrategyPath |
| Convert assets to underlying for closing | findBestClosePath; use the Close guide for the complete operation |
For a claim → convert batch, quote with the balances expected after the
claim, including any pre-existing input-token balance, and place the claim
before route.calls. Do not reuse a quote made before settlement. A route
only supplies the conversion leg; add the required claim, debt, quota, and
withdrawal actions yourself. If no route is available, the batch is not ready
to execute.
Build the transaction
const tx = await chain.accounts.executeCaUpdate(account, calls);
Despite its name, executeCaUpdate only builds a transaction. It prepends
required price updates and chooses the Credit Facade or applicable RWA factory
as the transaction destination. It does not sign, send, or invent missing calls.
Continue with chainId, tx, and owner at
Preview and check.
Use that page's transaction payload instructions for signing and execution.
Include a delayed claim
For a fresh claimable entry from
Monitor settlement, the
claim entry is:
const claimCall: MultiCall = { target: claimable.claimCall.to, callData: claimable.claimCall.callData, };
Place this entry before the calls that use its proceeds, replacing the
repayment example's funding step as appropriate. The claim does not execute
the recorded intent. Add your chosen conversion, repayment, quota, or withdrawal
calls so the complete batch passes the final health check.
For expected-balance checks around a claim, use
chain.accounts.assembleClaimDelayedCalls(...) with the lower-level withdrawal
data; the raw entry above does not add those checks.