Borrow
When you borrow an asset through Curvance, you're creating a debt position against your collateral in the same isolated market. The system evaluates your position before every borrow and during every liquidity check to ensure it remains healthy (above the required collateralization ratio).
The borrowing process is straightforward: you specify how much underlying you want to borrow and the recipient wallet, and the cToken contract handles the debt issuance directly. The borrowed underlying is sent to the receiver address; no cToken shares are minted to you for the debt. Debt is tracked in a separate internal mapping on the cToken. See Introduction to cTokens for the full lending-vs-debt model.
Use fresh or projected reads for borrow UX. marketManager.statusOf.staticCall(account) gives the account's current collateral, maximum debt, and current debt in USD WAD terms. The transaction remains the source of truth because the borrow path accrues interest and rechecks caps, collateral, price availability, and pool liquidity on-chain.
Before calling borrow()
borrow()A borrow reverts if any of the following hold:
Asset isn't borrowable in this market:
debtCap == 0. Check withawait marketManager.debtCaps(cUSDC) > 0n.Borrowing paused for this cToken: inspect the 3-tuple from
marketManager.actionsPaused(cUSDC).Debt cap would be exceeded:
marketOutstandingDebt()is a last-accrued storage read. For a fresher cap check, useawait cUSDC.marketOutstandingDebtUpdated.staticCall()and compare the projected market debt toawait marketManager.debtCaps(cUSDCAddress).Insufficient collateral: the protocol simulates the post-borrow position. If the account would have a liquidity deficit, the call reverts with
MarketManager__InsufficientCollateral().Below
MIN_LOAN_SIZE: each market has an immutable minimum active loan size, declared asMIN_LOAN_SIZEand configured between$10and$100in USD WAD. The runtime check compares the account's post-borrow USD-denominated debt to that floor, not the debt token's native units.You have collateral posted on this same cToken:
BorrowableCToken__CollateralPositionActivereverts ifcollateralPosted[owner] > 0on the cToken you're trying to borrow from (BorrowableCToken.sol:550). Typical flows borrow the opposite side of the isolated market (e.g., post ezETH as collateral inezETH | WETH, borrow WETH fromcWETH).The cToken does not hold enough underlying: if the pool cannot send the requested amount, the cToken reverts with
BorrowableCToken__InsufficientAssetsHeld().Amount is zero:
borrow(0, receiver)reverts withBaseCToken__ZeroAmount().A required price read fails: price-dependent checks can revert before the borrow completes.
Pre-Flight Check
This example uses ethers v6, placeholder addresses, and plain ethers.Contract instances. Source real addresses from the deployment registry for the chain you're on.
The MIN_LOAN_SIZE check above prices the projected debt into USD WAD before comparing. Comparing amountInUsdcUnits directly to MIN_LOAN_SIZE is a unit bug because USDC amounts are usually 1e6-scaled while MIN_LOAN_SIZE is 1e18-scaled USD value.
Calling borrow()
borrow()After a successful borrow:
Your account's debt in
cUSDCincreases byamountInUsdcUnits. Read fresh viaawait cUSDC.debtBalanceUpdated.staticCall(userAddress).A 20-minute
MIN_HOLD_PERIODwindow starts. During this window, repayment, redemption / withdrawal, and share transfers for that account revert withMarketManager__MinimumHoldPeriod(). Plain deposits are still allowed and do not reset the cooldown. New borrows and new collateral postings are allowed if otherwise valid, and they reset the cooldown timestamp.Debt accrues lazily. The DynamicIRM computes per-second borrow rates from utilization, and its rate adjustment interval is 10 minutes.
borrowFor (platforms borrowing on behalf of users)
borrowFor (platforms borrowing on behalf of users)Platforms that borrow on behalf of users can use borrowFor(assets, receiver, owner). The owner takes on the debt; the receiver gets the borrowed underlying. They can be different addresses.
owneris the account that takes on the debt;receiveris where the borrowed underlying lands. They can differ.Authorization switches from a caller check to
_checkDelegate(owner, msg.sender); no ERC20 allowance is involved.See Plugin & Delegation for the full delegation model.
Error Handling
Curvance uses Solidity custom errors. In ethers v6, let each relevant contract interface try to parse the revert data.
Manual selector comparison (fallback)
If you can't use an Interface (e.g., no ABI to hand), compare selectors directly. Make sure both sides are normalized:
Common error scenarios
BaseCToken__ZeroAmount()
Borrow amount is zero.
MarketManager__Paused()
Borrowing is paused for this cToken.
MarketManager__CapReached()
debtCap == 0 or projected market debt would exceed the cap.
MarketManager__InsufficientCollateral()
Borrow would push the account below its collateralization requirement.
LiquidityManager__InsufficientLoanSize()
Resulting total USD-denominated debt would be below MIN_LOAN_SIZE.
LiquidityManager__PriceError() / OracleManager__ErrorCodeFlagged()
A required price read failed or was not acceptable for the borrow check.
BorrowableCToken__CollateralPositionActive()
The owner has collateral posted on the same cToken they are trying to borrow from.
BorrowableCToken__InsufficientAssetsHeld()
The cToken pool does not hold enough underlying to send the borrow.
BorrowableCToken__DepositsNotInitialized()
The market has not initialized deposits, so borrowable liquidity cannot be calculated.
PluginDelegable__Unauthorized()
borrowFor caller is not an approved delegate of owner.
Last updated
Was this helpful?