🌻Auto-Leverage and Capital-Efficiency in the Borrowing Module

Tutorial to take advantage of Angle Borrowing Module capital-efficiency features

Angle Borrowing Module was designed in a way to maximize capital-efficiency of stakeholders when interacting with the protocol. This takes place at different levels in the protocol and opens the gate to several different operations:

  • One-transaction leverage: if in a single transaction you deposit collateral and borrow stablecoins against it, the protocol leaves you the opportunity to swap your borrowed stablecoins against collateral. You can technically use this to increase your exposure to an asset in just one transaction.

  • Liquidations without capital: during a liquidation, you have the opportunity to swap the discounted collateral obtained in stablecoins in order to repay the debt of the liquidated vault. This makes it possible to participate in liquidations without any capital commitment.

  • Capital-efficient debt repayment: if in a single transaction you want to repay stablecoins and get collateral back from your vault, you can swap a portion (or all) of the collateral you should obtain to stablecoins to repay your debt. This means that you do not actually have to bring the stablecoins back in the first place.

In this guide, we intend to explain how developers should prepare such transactions to maximize the capital and gas efficiency of their interactions with the Angle Protocol Borrowing Module.

These capital-efficiency features are optional and reserved for advanced users or for users interacting with some verified user interface (like Angle Labs app). They require deploying and relying on some specific contracts with the right interfaces to perform the swaps at the right time and for the right amounts.

All of this logic takes place in the VaultManager contract for which you can check the docs here.

Overview and Interfaces

There are two main functions in the VaultManager contract in which this kind of feature can be used: the angle() function and the liquidate() function.

    function angle(
        ActionType[] memory actions,
        bytes[] memory datas,
        address from,
        address to,
        address who,
        bytes memory repayData
    ) public payable returns (PaymentData memory paymentData);

    function liquidate(
        uint256[] memory vaultIDs,
        uint256[] memory amounts,
        address from,
        address to,
        address who,
        bytes memory data
    ) public returns (LiquidatorData memory liqData)

Both of these functions contain a who and a data parameter. This is what allows stakeholders of the protocol to specify a contract (who) to perform swaps if needed. The data parameter can be used by the who contract to know which route it should use to do the swap.

Specifically, the VaultManager contract expects that the who contract conforms to the ISwapper interface. This means that they must implement the swap function below:

    function swap(
        IERC20 inToken,
        IERC20 outToken,
        address outTokenRecipient,
        uint256 outTokenOwed,
        uint256 inTokenObtained,
        bytes calldata data
    ) external;

This function when called on a ISwapper contract lets this contract know that it got inTokenObtained of inToken and that it should swap these inToken to outToken. The execution of the transaction fails in the VaultManager if the ISwapper contract fails make sure that at the end of the call the outTokenRecipient is not at least outTokenOwed.

Angle Core Team has made an open-source implementation of a ISwapper contract available here. Docs on this example contract is available here.

Auto-Leverage

Auto-leverage is the operation which allows people to get a an exposure to an amount of collateral greater than what they initially brought. Assume I bring 0.5 ETH, then by using the auto-leverage feature of Angle Protocol, I can for instance get an exposure to 1.5 ETH.

How to do this in practice?

Let's say you have an empty vault (with no collateral and no stablecoin) in a VaultManager with ETH as a collat. If you want to get to get exposure to 1.5 ETH from 0.5 ETH, you need to call the angle() function of the contract and perform the following actions (in the right order):

  • addCollateral: 1.5 ETH

  • borrow: 1 ETH worth of stablecoins

The angle() function is coded in a way that in this case stablecoins are sent before collateral is given. As such, if you specify in your call to the angle() a who contract along with data needed for this contract to know how to process the swap, then the operation will succeed even if the msg.sender just has 0.5 ETH in your balance.

In this situation, the to address specified in the transaction should either be the who contract, or it should have approved the who contract for the collateral (for the who contract to fetch stablecoins from this address).

The flow of operations during the transaction is as follows:

  • The VaultManager sends 1 ETH worth of stablecoins to the who contract (which should in many cases and depending on the implementation be the to contract)

  • Assuming no slippage, stablecoins are swapped for 1 ETH and sent to the msg.sender address (your address). The who contract could for instance perform 1Inch or UniswapV3 swaps.

  • The VaultManager fetches the 1.5 ETH from the msg.sender address and concludes the transaction

After this, you own a vault with an amount of collateral of 1.5 ETH, and a debt worth 1 ETH. Beware though of liquidation risks with this auto-leverage feature: the higher the leverage you take the bigger the risk.

Liquidations and Debt Repayment

Liquidations and debt repayment correspond to the same situation in which the protocol expects to receive stablecoins and gives collateral in exchange for this.

Assume a liquidator comes to liquidate 1 ETH worth of debt in a vault and gets a 10% discount on the liquidated collateral: then this liquidator is expected to receive 1.1 ETH from the protocol and to repay 1 ETH worth of stablecoins to the protocol.

In the liquidate function, to perform such swaps, you should specify a who contract which supports the ISwapper interface, depending on the implementation of the who contract, the to address should be the who address. The data parameter given should help the who contract perform the swap.

Flow of operations is then as follows:

  • The VaultManager contract sends 1.1 ETH to the to contract

  • The who contract swaps a portion (or all) these 1.1 ETH for 1 ETH worth of stablecoins

  • The VaultManager burns the 1 ETH worth of stablecoins from the from address given in the function. For this to work, the from address should have by the way given approval to the msg.sender for the stablecoin.

Last updated