Deleverage / Fold

Overview

Curvance PositionManagers are router-style contracts that wrap multi-step leverage and deleverage actions inside a single isolated market. For deleveraging, the PositionManager withdraws collateral-side assets, runs the manager-specific exit or swap path, repays debt, and returns defined leftover balances while the cTokens and MarketManager enforce market listing, PositionManager authorization, repayment rules, redemption rules, account liquidity, and cooldown checks.

Key Components

The deleveraging flow involves:

  • BasePositionManager: shared deleverage and deleverageFor entrypoints, callback handling, delegation checks, reentrancy protection, protocol fee handling, and pre/post account-value slippage checks.

  • Concrete PositionManagers: asset-specific conversion logic for simple ERC20 routes, vault routes, Pendle routes, Velodrome routes, and Aerodrome routes.

  • MarketManager: isolated-market listing, PositionManager authorization, account-status checks, repayment review, redemption review, pause state, caps, and cooldown enforcement.

  • cTokens and BorrowableCToken: the collateral-side cToken executes withdrawByPositionManager, and the debt-side borrowable cToken receives repayment through repayFor. Live cTokens are not separate "collateral cToken" and "borrowable cToken" contract types; borrowability is a per-market configuration controlled by debtCaps(cToken).

Deleverage Process Flow

The position unwinding flow follows these key steps:

Data Flow

  1. Initiation: The position unwinding flow starts when the user calls deleverage(action, slippage) for their own account, or an approved delegate calls deleverageFor(action, account, slippage). The action argument is a DeleverageAction containing:

    1. cToken: the cToken to withdraw from.

    2. collateralAssets: the amount of that cToken's underlying asset to redeem, not cToken shares.

    3. borrowableCToken: the debt-side cToken to repay.

    4. repayAssets: the minimum debt-asset balance the PositionManager must hold after the route.

    5. swapActions: manager-specific swap instructions.

    6. auxData: manager-specific auxiliary data.

  2. Position Token Withdrawal:

    The PositionManager validates the action and calls action.cToken.withdrawByPositionManager(action.collateralAssets, account, action). The cToken accrues, computes the shares needed with previewWithdraw logic, withdraws the underlying asset to the PositionManager, and then calls onRedeem(...) so the PositionManager can run the deleverage callback.

  3. Collateral Conversion:

    Collateral conversion is implemented by the concrete PositionManager's _swapCollateralAssetToDebtAsset(action) hook. The exact route is manager-specific. SimplePositionManager requires exactly one collateral-asset-to-debt-asset swap. Vault managers may redeem vault shares first and can skip the external swap when the vault output already matches the debt asset. Pendle and Velodrome or Aerodrome managers exit their protocol position first, then optionally run one or more swaps whose endpoints must match the manager's expected output and final debt asset.

  4. Debt Repayment:

    Debt repayment happens in the base deleverage callback after the manager-specific route finishes. The PositionManager checks that it holds at least action.repayAssets of the debt asset. It then reads the owner's fresh debt with debtBalanceUpdated(owner) and repays min(assetsHeld, currentDebt) through borrowableCToken.repayFor(repayAssets, owner).

    repayAssets is a minimum debt-asset output floor for the deleverage route. It is not always the exact final repayment amount, and setting it to zero reverts in the PositionManager.

  5. Asset Return:

    After repayment, the base callback returns the balances it explicitly sweeps:

    • remaining debt asset;

    • remaining withdrawn collateral asset;

    • remaining balances of each swapActions[i].outputToken.

    Do not describe this as a blanket refund of every possible intermediate token. PositionManagers are router-style contracts and should not be treated as user-specific custody for arbitrary stranded balances.

State Transitions

During deleverage, validation happens across the PositionManager, the collateral cToken, the debt cToken, and the MarketManager:

  • deleverageFor checks delegation on the PositionManager contract.

  • _deleverage rejects repayAssets == 0 and requires action.cToken to be listed in the connected MarketManager.

  • withdrawByPositionManager requires the caller to be an enabled PositionManager, accrues the cToken, computes the shares needed for collateralAssets, withdraws the underlying to the PositionManager, and runs the onRedeem callback.

  • onRedeem requires the debt-side borrowableCToken to be listed and verifies that the callback token and amount match the action.

  • repayFor accrues debt, checks the repayment through MarketManager.canRepayWithReview(...), and can revert during the hold period or if a partial repay would leave debt below MIN_LOAN_SIZE.

  • The final redemption review runs through MarketManager.canRedeemWithCollateralRemoval(...), which enforces redeem pause state, transfer eligibility, and account liquidity for any posted collateral that is removed.

Protocol-Specific Implementations

Curvance supports deleverage through concrete PositionManager implementations:

  • SimplePositionManager: generic non-native ERC20 paths using a single swap.

  • SingleSidedVaultPositionManager: ERC4626-style vault positions that use the simple deleverage path.

  • DualSidedVaultPositionManager: vault positions that redeem vault shares before optionally swapping into the debt asset.

  • NativeVaultPositionManager: native-token vault positions that inherit the simple deleverage path.

  • PendlePTPositionManager: Pendle principal-token positions.

Each implementation provides specialized logic for handling the unique characteristics of its respective protocol when exiting positions.

Access Control and Delegation

PositionManager deleverage operations can be initiated by:

  • the position owner through deleverage(action, slippage);

  • a delegate through deleverageFor(action, account, slippage), if the account approved that delegate on the specific PositionManager contract with setDelegateApproval(delegate, true).

Slippage Protection

Deleverage has several slippage-related controls:

  • the top-level slippage parameter on deleverage and deleverageFor, checked by BasePositionManager.checkSlippage against the account's collateral-minus-debt value before and after the action;

  • each SwapperLib.Swap.slippage, checked by _swapSafe using oracle-priced input and output value;

  • external swap calldata minimum-output checks, enforced through the approved calldata checker for the swap target;

  • manager-specific floors, such as repayAssets, Pendle minimum-output fields, or Velodrome PT exit and route constraints.

The top-level checkSlippage modifier is a sanity check, not a complete replacement for route quoting and swap-level limits.

Example Flow

A typical simple ERC20 deleverage flow:

  1. The user has a WETH collateral position and USDC debt in the same isolated market.

  2. The user calls deleverage(action, slippage), where action.cToken is the WETH cToken and action.borrowableCToken is the USDC cToken.

  3. The PositionManager calls withdrawByPositionManager(action.collateralAssets, owner, action) on the WETH cToken.

  4. The WETH cToken withdraws WETH underlying to the PositionManager and calls onRedeem.

  5. The concrete PositionManager route swaps WETH to USDC according to swapActions.

  6. The base callback checks that the PositionManager holds at least action.repayAssets USDC, reads fresh debt, and calls repayFor(...) on the USDC cToken.

  7. Remaining swept balances are returned to the owner.

  8. Swap-target and debt-cToken allowances opened by the flow are removed when the relevant helper reaches its cleanup path.

The flow reduces collateral and debt in one transaction while still relying on cToken and MarketManager checks for repayment, redemption, liquidity, pause state, cooldowns, and route slippage.

User Interaction Functions

deleverage()

Description: Deleverages an active Curvance position for msg.sender by redeeming collateral-side assets, converting them through the concrete PositionManager route, repaying debt, and checking pre/post account value through checkSlippage.

Contract: BasePositionManager, inherited by the concrete PositionManager used for the market.

Function signature:

Type
Name
Description

DeleverageAction

action

Struct containing the collateral cToken, collateral underlying amount, debt cToken, minimum debt-asset output floor, swap route, and manager-specific auxiliary data.

uint256

slippage

Top-level PositionManager slippage tolerance in WAD, checked against account value before and after the action.

Events:


deleverageFor()

Description: Deleverages an active Curvance position for account through PositionManager delegation. The account must approve the caller on the specific PositionManager contract with setDelegateApproval(delegate, true).

Contract: BasePositionManager, inherited by the concrete PositionManager used for the market.

Function signature:

Type
Name
Description

DeleverageAction

action

Structure containing deleverage operation details including position token, collateral amount, borrow token, swap data, repay amount, and auxiliary data.

address

account

The account to deleverage an active Curvance position for.

uint256

slippage

Slippage accepted by the user for the deleverage action, in WAD (1e18).

Events:

Last updated

Was this helpful?