Deleveraging is the process of reducing your position's leverage by withdrawing collateral and repaying debt. This can be done in two ways:
Partial deleveraging: Reduce leverage while maintaining an active position.
Full deleveraging: Completely unwind the position by repaying all debt.
Before deleveraging, you need to:
Understand your current position: Review your collateral amount, debt amount, and health factor.
Set an appropriate slippage tolerance: Typically 0.5-2% depending on asset volatility.
Calculate the optimal deleveraging amounts: Determine how much collateral to withdraw and debt to repay.
Deleveraging requires constructing a DeleverageAction struct that specifies how to unwind your position:
With the DeleverageAction struct prepared, execute the deleverage operation:
Swap Data Configuration:
The swapData array can contain multiple swaps for complex routes.
Each swap must specify the correct input/output tokens and amounts.
Slippage tolerance should be set appropriately for each swap.
Amount Calculations:
collateralAmount: The amount of position tokens to withdraw.
repayAmount: The amount of debt to repay in the borrowed asset.
Ensure these amounts are properly calculated to maintain desired position size.
Protocol-Specific Features:
Some protocols may require additional data in the auxData field.
Check protocol documentation for specific requirements.
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}`);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:
Understand your target position: Determine the collateral asset, borrow asset, and desired leverage ratio.
Set an appropriate slippage tolerance: Typically 0.5-2% depending on asset volatility.
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:
If you already have collateral deposited, you can increase your leverage without an additional deposit:
Position Health and Risk Management:
Monitor your position's health factor before and after leveraging.
Consider the maximum leverage ratio allowed by the protocol.
Be aware of liquidation risks when increasing 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.
Calculate the minimum collateral required to maintain a safe position.
Swap Data Configuration:
The swapData must accurately reflect the desired swap route.
Ensure the inputAmount matches the borrowAmount in the leverage struct.
Set appropriate slippage tolerance for the swap (typically 0.3-1% for stable pairs, 1-3% for volatile pairs).
Amount Calculations:
Ensure these amounts are properly calculated to achieve desired leverage ratio.
Consider protocol fees when calculating amounts.
Protocol-Specific Features:
Some protocols may require additional data in the auxData field.
Check protocol documentation for specific requirements.
To understand leveraging in Curvance, you need to be familiar with two key data structures that control the leverage and deleverage operations:
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-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:
Before interacting with Curvance, set up your development environment:
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.
// 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;
}
}