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.
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
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.
}DeleverageAction
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
}SwapperLib.Swap
Both structures use the SwapperLib.Swap structure to handle token swaps:
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
}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:
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
]
);Setting Up Your Environment
Before interacting with Curvance, set up your development environment:
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
);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.
// 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;
}
}
Last updated