All pages
Powered by GitBook
1 of 3

Loading...

Loading...

Loading...

Deleveraging

Understanding Deleveraging

Deleveraging is the process of reducing your position's leverage by withdrawing collateral and repaying debt. This can be done in two ways:

  1. Partial deleveraging: Reduce leverage while maintaining an active position.

  2. Full deleveraging: Completely unwind the position by repaying all debt.

Preparing for Deleveraging

Before deleveraging, you need to:

  1. Understand your current position: Review your collateral amount, debt amount, and health factor.

  2. Set an appropriate slippage tolerance: Typically 0.5-2% depending on asset volatility.

  3. Calculate the optimal deleveraging amounts: Determine how much collateral to withdraw and debt to repay.

Constructing the DeleverageAction struct

Deleveraging requires constructing a DeleverageAction struct that specifies how to unwind your position:

Executing the Deleverage Operation

With the DeleverageAction struct prepared, execute the deleverage operation:

Important Considerations

  1. Swap Data Configuration:

    1. The swapData array can contain multiple swaps for complex routes.

    2. Each swap must specify the correct input/output tokens and amounts.

    3. Slippage tolerance should be set appropriately for each swap.

  • Amount Calculations:

    1. collateralAmount: The amount of position tokens to withdraw.

    2. repayAmount: The amount of debt to repay in the borrowed asset.

    3. Ensure these amounts are properly calculated to maintain desired position size.

  • Protocol-Specific Features:

    1. Some protocols may require additional data in the auxData field.

    2. Check protocol documentation for specific requirements.

    3. Consider using protocol-specific optimizations when available.

  • // Define how much collateral to withdraw
    const collateralAmount = ethers.utils.parseUnits('300', 18);
    const repayAmount = ethers.utils.parseUnits('200', 18); // Amount of debt to repay
    
    // Create swap data to convert collateral to borrow asset
    // Note: This is an array of swaps, allowing for multi-hop routes
    const swapData = [{
      target: '0x1111111254EEB25477B68fb85Ed929f73A960582', // 1inch router
      inputToken: underlyingAsset,
      outputToken: borrowUnderlying,
      inputAmount: ethers.utils.parseUnits('250', 18), // Amount needed to convert to repay debt
      call: '0x...', // Encoded swap call data
    }];
    
    // Construct deleverage struct
    const deleverageAction = {
      cToken: COLLATERAL_CTOKEN,
      collateralAssets: collateralAmount,
      borrowedToken: BORROWED_CTOKEN,
      swapData: swapData,
      repayAmount: repayAmount,
      auxData: '0x' // Optional for specialized protocols
    };
    // Set slippage tolerance (1%)
    const slippage = ethers.utils.parseUnits('0.01', 18);
    
    // Execute the deleverage transaction
    const tx = await positionManagement.deleverage(
      deleverageData, 
      slippage,
      2000000
    );
    
    const receipt = await tx.wait();
    console.log(`Position deleveraged! Tx hash: ${receipt.transactionHash}`);

    Leveraging

    1. Initial Deposit and Leverage

    The most common approach is to deposit and leverage in a single transaction. This is ideal for users who want to create a leveraged position from scratch.

    Preparing for Leverage

    Before leveraging, you need to:

    1. Understand your target position: Determine the collateral asset, borrow asset, and desired leverage ratio.

    2. Set an appropriate slippage tolerance: Typically 0.5-2% depending on asset volatility.

    3. Approve the necessary contracts: Your collateral token must approve the Position Management contract.

    Here's how to prepare the deposit and leverage transaction:

    Constructing the LeverageAction

    The key to a successful leverage operation is properly constructing the LeverageStruct:

    Executing the Leverage Operation

    With the LeverageStruct prepared, execute the leverage operation:

    2. Leveraging an Existing Position

    If you already have collateral deposited, you can increase your leverage without an additional deposit:

    Important Considerations

    1. Position Health and Risk Management:

      1. Monitor your position's health factor before and after leveraging.

      2. Consider the maximum leverage ratio allowed by the protocol.

      3. Be aware of liquidation risks when increasing leverage.

    Leverage

    Leveraging in Curvance allows users to amplify their position by borrowing assets against their collateral and reinvesting them. This creates a more capital-efficient position with greater exposure to underlying assets. Curvance's leveraging system is built on its powerful Position Management framework, which simplifies complex DeFi operations into single, atomic transactions. This eliminates the manual loop of borrowing, swapping, and depositing that would otherwise be required to build leveraged positions.

    For those that want to dive deep into the inner workings of our leverage system, check out our Position Management page here:

    Calculate the minimum collateral required to maintain a safe position.

  • Swap Data Configuration:

    1. The swapData must accurately reflect the desired swap route.

    2. Ensure the inputAmount matches the borrowAmount in the leverage struct.

    3. Set appropriate slippage tolerance for the swap (typically 0.3-1% for stable pairs, 1-3% for volatile pairs).

  • Amount Calculations:

    1. Ensure these amounts are properly calculated to achieve desired leverage ratio.

    2. Consider protocol fees when calculating amounts.

  • Protocol-Specific Features:

    1. Some protocols may require additional data in the auxData field.

    2. Check protocol documentation for specific requirements.

  • View functions inside ProtocolReader provide handy data needed to calculate proper swap and leverage amounts.

    Core Structures for Leveraging

    To understand leveraging in Curvance, you need to be familiar with two key data structures that control the leverage and deleverage operations:

    LeverageAction

    DeleverageAction

    SwapperLib.Swap

    Both structures use the SwapperLib.Swap structure to handle token swaps:

    Important Note: LeverageStruct takes a single Swap while DeleverageStruct takes an array of Swap operations, allowing for multi-hop swaps when deleveraging.

    Pre, Post, and Auxiliary Swap Operations

    Pre-Swap when Leveraging

    When leveraging, a swap is executed to obtain the collateral asset using the borrowed asset. You must provide the appropriate calldata hex string in the Swap.call struct member. Additionally, ensure the swap's receiver is set to the corresponding PositionManager contract.

    Post-Swap when Deleveraging

    During deleveraging, a swap is performed to exchange the collateral asset for the borrowed asset. You need to supply the correct calldata hex string in the Swap.call struct member. Also, make sure the swap's receiver is configured as the relevant PositionManager contract.

    Auxiliary Swap for Complex Assets

    When leveraging or deleveraging exotic assets, you may need to include auxiliary data to initiate the position. For instance, to enter a Pendle PT position, you must provide swap data to exchange the borrowed asset for Pendle PT via Pendle’s internal exchange.

    Below is a code snippet demonstrating how to prepare this data:

    Setting Up Your Environment

    Before interacting with Curvance, set up your development environment:

    Monitoring Position Health

    Maintaining a healthy leverage ratio is crucial to avoid liquidation. Regularly check your position's health by using getPositionHealth() in the ProtocolReader contract. The function returns the healthiness of an account's position.

    Use zero addresses and amounts to simply check an existing position.

    If the value is greater than WAD (1e18), the position is healthy.

    If the value is less than WAD (1e18), the position is unhealthy/liquidatable.

    Position Management
    // Approve the position management contract to spend your tokens if depositing and leveraging in one transaction
    const underlyingContract = new ethers.Contract(
      underlyingAsset,
      UNDERLYING_ABI,
      signer
    );
    const depositAmount = ethers.utils.parseUnits('1000', 18); // Adjust amount and decimals
    await underlyingContract.approve(POSITION_MANAGEMENT, depositAmount);
    // Create the swap data structure
    // This example uses 1inch swap data, but any DEX can be used
    const swapData = {
      target: '0x1111111254EEB25477B68fb85Ed929f73A960582', // 1inch router
      inputToken: COLLATERAL_ASSET_UNDERLYING,
      outputToken: BORROWED_ASSET_ADDRESS,
      inputAmount: ethers.utils.parseUnits('500', 18), // Amount to borrow and swap
      call: '0x...', // Encoded swap call data from 1inch API
    };
    
    // Construct the LeverageAction struct
    const leverageAction = {
      borrowableCToken: BORROWABLECTOKEN,
      borrowAssets: ethers.utils.parseUnits('500', 18),
      cToken: CTOKEN,
      expectedShares: ethers.utils.parseUnits('0.25', 18), // expected amount of collateral cTokens
      swapData: swapData,
      auxData: '0x' // Optional auxiliary data for specialized protocols
    };
    // Set slippage tolerance (1%)
    const slippage = ethers.utils.parseUnits('0.01', 18);
    
    // Execute deposit and leverage in one transaction
    const tx = await positionManagement.depositAndLeverage(
      depositAmount,
      leverageData,
      slippage,
      2000000
    );
    
    const receipt = await tx.wait();
    console.log(`Leveraged position created! Tx hash: ${receipt.transactionHash}`);
    const tx = await positionManagement.leverage(
      leverageData, // Same structure as above
      slippage,
      2000000
    );
    
    const receipt = await tx.wait();
    console.log(`Position further leveraged! Tx hash: ${receipt.transactionHash}`);
    struct LeverageAction {
        IBorrowableCToken borrowToken;   // The cToken you want to borrow from.
        uint256 borrowAssets;            // Amount of underlying tokens to borrow.
        ICToken cToken;                  // cToken that will receive the swapped assets.
        SwapperLib.Swap swapData;        // Instructions for swapping borrowed tokens.
        bytes auxData;                   // Optional protocol-specific data.
    }
    struct DeleverageAction {
        ICToken cToken;                  // The cToken you're unwinding
        uint256 collateralAmount;        // Amount of cTokens to redeem
        IBorrowableCToken borrowToken;   // The cToken debt to repay
        SwapperLib.Swap[] swapData;      // Array of swaps to execute
        uint256 repayAmount;             // Amount of debt to repay
        bytes auxData;                   // Optional protocol-specific data
    }
    struct Swap {
        address target;        // Swap router address
        address inputToken;    // Token being swapped from
        address outputToken;   // Token being swapped to
        uint256 inputAmount;   // Amount to swap
        bytes call;            // Encoded swap call data
    }
    const PendleData = {
        approx: {
            guessMin: ethers.utils.parseUnits("9.9", 18),
            guessMax: ethers.utils.parseUnits("10.0", 18),
            guessOffchain: ethers.utils.parseUnits("1.0", 18),
            maxIteration: 30,
            eps: ethers.utils.parseUnits("0.001", 18)
        },
        input: {
            tokenIn: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC address
            netTokenIn: ethers.utils.parseUnits(estimatedUniswapOutputAmount.toString(), 18),
            tokenMintSy: "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84", // STETH address
            pendleSwap: "0x1e8b6Ac39f8A33f46a6Eb2D1aCD1047B99180AD1", // PENDLE_SWAP address
            swapData: {
                swapType: 1, // KYBERSWAP enum value
                extRouter: "0x6131B5fae19EA4f9D964eAc0408E4408b66337b5",
                extCalldata: "0xe21fd0e9...", // The calldata hex string
                needScale: false
            }
        }
    };
    
    // Create the leverage data object
    const leverageAction = {
        borrowToken: cDAIAddress,
        borrowAmount: ethers.utils.parseUnits(amountForLeverage.toString(), 18),
        positionToken: cPendlePTAddress,
        auxData: null,
        swapData: swapData
    };
    
    // Encode the data
    leverageAction .auxData = ethers.utils.defaultAbiCoder.encode(
        [
            "tuple(tuple(uint256 guessMin, uint256 guessMax, uint256 guessOffchain, uint256 maxIteration, uint256 eps) approx, tuple(address tokenIn, uint256 netTokenIn, address tokenMintSy, address pendleSwap, tuple(uint8 swapType, address extRouter, bytes extCalldata, bool needScale) swapData) input)",
            "address",
            "uint256"
        ],
        [
            [
                [
                    PendleData.approx.guessMin,
                    PendleData.approx.guessMax,
                    PendleData.approx.guessOffchain,
                    PendleData.approx.maxIteration,
                    PendleData.approx.eps
                ],
                [
                    PendleData.input.tokenIn,
                    PendleData.input.netTokenIn,
                    PendleData.input.tokenMintSy,
                    PendleData.input.pendleSwap,
                    [
                        PendleData.input.swapData.swapType,
                        PendleData.input.swapData.extRouter,
                        PendleData.input.swapData.extCalldata,
                        PendleData.input.swapData.needScale
                    ]
                ]
            ],
            lpStethAddress,
            1
        ]
    );
    const { ethers } = require('ethers');
    
    // Initialize provider and signer
    const provider = new ethers.providers.JsonRpcProvider('YOUR_RPC_URL');
    const signer = new ethers.Wallet('YOUR_PRIVATE_KEY', provider);
    
    // Contract addresses
    const MARKET_MANAGER = '0x...';
    const POSITION_MANAGEMENT = '0x...';
    const C_TOKEN_COLLATERAL = '0x...'; // Your collateral token
    const C_TOKEN_DEBT = '0x...'; // Your borrow token
    
    // Initialize contracts
    const marketManager = new ethers.Contract(
      MARKET_MANAGER,
      positionManagerAbi,
      provider
    );
    
    const positionManagement = new ethers.Contract(
      POSITION_MANAGEMENT,
      [
       'function depositAndLeverage(uint256, tuple(address,uint256,address,tuple(address,address,address,uint256,bytes),bytes), uint256)',
        'function leverage(tuple(address,uint256,address,tuple(address,address,address,uint256,bytes),bytes), uint256)',
        'function deleverage(tuple(address,uint256,address,tuple[](address,address,address,uint256,bytes),uint256,bytes), uint256)'
      ],
      signer
    );
    // Create ProtocolReader contract instance
      const protocolReader = new ethers.Contract(
        protocolReaderAddress,
        protocolReaderAbi,
        provider
      );
    
      async function checkPositionHealth() {
        try {
          const [positionHealth, errorCodeHit] = await
      protocolReader.getPositionHealth(
            "MARKET_MANAGER_ADDRESS",        // IMarketManager mm
            userAddress,                     // address account
            ethers.constants.AddressZero,    // address cToken
            ethers.constants.AddressZero,    // address borrowableCToken
            false,                           // bool isDeposit
            0,                               // uint256 collateralAssets
            false,                           // bool isRepayment
            0,                               // uint256 debtAssets
            0                                // uint256 bufferTime
          );
    
          console.log("Position Health:", positionHealth.toString());
          console.log("Error Code Hit:", errorCodeHit);
    
          return { positionHealth, errorCodeHit };
        } catch (error) {
          console.error("Error calling getPositionHealth:", error);
          throw error;
        }
      }