🏦
VaultManager - Borrowing AgTokens
Angle's Borrowing Module User-Facing Contract
- Contract Name:
VaultManager
VaultManager.json
34KB
Code
VaultManager ABI
The
VaultManager
contract is the main contract of Angle Borrowing Module. It is one of the only user-facing contracts. This contract allows people to deposit collateral and open up loans of a given AgToken. It handles all the loan logic (fees and interest rate) as well as the liquidation logic.There is one
VaultManager
contract per collateral type for a given stablecoin. You may have for instance two different VaultManager
contract for ETH as a collateral, with for instance two different set of parameters.This contract is also a standard NFT contract on the
ERC721MetadataUpgradeable
standard. Vaults (name of the tokens in this contract) are non fungible, but they can be transferred from one address to another, you can also approve another address to interact with you vault or simply check the number of vaults you own.Implements
IERC721MetadataUpgradeable
, IVaultManager
, ReentrancyGuardUpgradeable
and Initializable
. This contract is upgradeable. This contract implements a version of ERC721Permit
which allows to grant allowance to an address for an ensemble of vautlts through a permit signature.treasury
: Reference to theTreasury
contract associated to thisVaultManager
collateral
: Reference to the collateral handled by thisVaultManager
stablecoin
: Stablecoin handled by this contract. AnotherVaultManager
contract could have the same rights as thisVaultManager
on the stablecoin contractoracle
: Oracle contract to get access to the price of the collateral with respect to the stablecoinveBoostProxy
: Reference to the contract which computes adjusted veANGLE balances for liquidators boosts
dust
: Minimum amount of debt a vault can have_dustCollateral
: Minimum amount of collateral (in stablecoin value) that can be left in a vault during a liquidation where the health factor function is decreasingdebtCeiling
: Maximum amount of stablecoins that can be issued with this contractxLiquidationBoost
,yLiquidationBoost
: Threshold veANGLE balance values and value of the boost for liquidators at these threshold values: the length of these arrays should be 2collateralFactor
: Encodes the maximum ratio stablecoin/collateral a vault can have before being liquidated. It's what determines the minimum collateral ratio of a positiontargetHealthFactor
: Maximum Health factor at which a vault can end up after a liquidation (unless it's fully liquidated)borrowFee
: Upfront fee taken when borrowing stablecoinsrepayFee
: Fee taken when repaying a stablecoin debtinterestRate
: Per second interest taken to borrowers taking agToken loansliquidationSurcharge
: Fee taken by the protocol during a liquidation. Technically, this value is not the fee per se, it's 1 - fee. For instance for a 2% fee,liquidationSurcharge
should be 98%.maxLiquidationDiscount
: Maximum discount given to liquidatorswhitelistingActivated
: Whether whitelisting is required to own a vault or notpaused
: Whether the contract is paused or not
lastInterestAccumulatorUpdated
: Timestamp at which theinterestAccumulator
was updatedinterestAccumulator
: Keeps track of the interest that should accrue to the protocol. The stored value is not necessarily the true value: this one is recomputed every time an action takes place within the protocol.totalNormalizedDebt
: Total normalized amount of stablecoins borrowedsurplus
: Surplus accumulated by the contract: surplus is always in stablecoins, and is then reset when the value is communicated to the treasury contractbadDebt
: Bad debt made from liquidated vaults which ended up having no collateral and a positive amount of stablecoins
vaultData
: Maps avaultID
to its data (namely collateral amount and normalized debt)isWhitelisted
: Maps an address to whether it's whitelisted and can open or own a vault
This contract has all the mappings and data associated to ERC721 Contracts. Most of them are internal variables which are not accessible externally (like
_vaultApprovals
, _owners
, _balances
or _operatorApprovals
). The only publicly available is the vaultIDCount
which is a counter to generate a unique vaultID
for each vault: vaultID
acts as tokenID
in basic ERC721 contracts.There is also a
_nonces
mapping to used to check permit signatures to the contract.Like in all contracts of this Borrowing Module, restricted functions are checked upon calling the
Treasury
contract which then calls its associated CoreBorrow
contract.All standard EIP721 methods are implemented, such as
balanceOf()
, ownerOf()
, approve()
, transferFrom
, etc.The implementation of these methods is derived from OpenZeppelin's library.
A
getControlledVaults
function was also added. It allows to check all the vaults controlled by an address. This is an expensive function that should never be called on-chain as it iterates over all vaultIDs. It is here to reduce dependency on an external graph to link a vaultID ID to its owner.Beyond these base methods, the contract includes a permit method to grant (or revoke) approval to an address for all the associated vaults of the contract.
function permit(
address owner,
address spender,
bool approved,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external
Parameters:
owner
: Address which signed the messagespender
: Address to grant or revoke allowance toapproved
: Whether to grant or revoke allowancedeadline
: Timestamp till which the signature is validv, r, s
: Signature data
Examples of how to sign can be found in the open-source
borrow-contracts
repo.Everything in the
VaultManager
contract was designed to be composable in a single transaction: this can be done with the angle()
functions.There are two different versions of the function, performing in the end the same actions.
function angle(
ActionType[] memory actions,
bytes[] memory datas,
address from,
address to,
address who,
bytes memory repayData
) external payable returns (PaymentData memory paymentData);
And:
function angle(
ActionType[] memory actions,
bytes[] memory datas,
address from,
address to
) external payable returns (PaymentData memory paymentData);
These functions allow composability between calls to the different entry points of this module. Any user calling this function can perform any of the allowed actions in the order of their choice.
This function is optimized to reduce gas cost due to payment from or to the user and that expensive calls or computations (like
oracleValue
) are done only onceParameters:
actions
: Set of actions to perform. Each action corresponds to a different integer.datas
: Array of data to be decoded for each action. Each item in this array is the abi-encoded version of different parameters for each version: parameters can include like thevaultID
or thestablecoinAmount
to borrow for instance. Each action expects particular data to be given (more detail below). Thedatas
array should have the same length as theactions
array.from
: Address from which stablecoins will be taken if one action includes burning stablecoins. This address should either be themsg.sender
or be approved by the latterto
: Address to which stablecoins and/or collateral will be sent in case of
The following parameters are optional and can be avoided if you are using the second version of the function. For about how to use these parameters correctly, you can look at this guide we have built on auto-leverage and capital-efficiency within Angle Borrowing Module.
who
: Address of the contract to handle in case of repayment of stablecoins from received collateralrepayData
: Data to pass to the repayment contract in case of
Return Value:
paymentData
: Struct containing the final transfers executed. This struct contains four different fields (named from the protocol's perspective):stablecoinAmountToGive
: Stablecoin amount the contract has given to theto
addressstablecoinAmountToReceive
: Stablecoin amount taken from thefrom
address to be burnt by the contractcollateralAmountToGive
: Collateral amount the contract has given to theto
addresscollateralAmountToReceive
: Collateral amount taken from themsg.sender
address Any contract interfacing with thevaultManager
Below, we detail the list of different actions, their corresponding integers and the data that should be passed for actions to be correctly processed. Note that for all actions requiring a
vaultID
, specifiying a vaultID = 0
will automatically process the last vault created.This action enables vault creation within the protocol. It just creates the structure without any collateral or stablecoins with it. It should be combined with other actions to borrow (like an
addCollateral
and a borrow
action to get stablecoins).Type of the parameter:
address
: address for which the vault should be created
This action enables vault closing within the protocol. An address closing a vault is expected to reimburse its full debt, and gets in exchange the value of the collateral in it. The protocol is coded so that vault closing can be done capital-efficiently. For more details about this, you can look at this guide.
Only the address that owns the vault or addresses that are approved for it can close a vault.
Type of the parameter:
uint256
: ID of the vault to close
Adds collateral in an existing vault. This function can be done in conjunction with a borrow action to collateralize a vault and get stablecoins in just a single transaction.
Any address can add collateral on any vault.
Type and order of the parameters:
- 1.
uint256
: ID of the vault to add collateral to - 2.
uint256
: Amount of collateral to add
Removes collateral from an existing vault. Only addresses that are approved for the concerned vault can perform this action. A solvency check is performed after the operation meaning that removing collateral cannot place an address in a liquidation situation.
Type and order of the parameters:
- 1.
uint256
: ID of the vault to remove collateral from - 2.
uint256
: Amount of collateral to remove
Mints stablecoins from a collateralized vault. A solvency check is performed after the operation meaning that you may be limited by the amount of borrowed stablecoins. This function takes into account borrowing fees. Only addresses that are approved for the concerned vault can perform this action.
If a vault has no stablecoins borrowed, then the first borrow action should make sure to borrow more than the
dust
of the vaultManager
.Type and order of the parameters:
- 1.
uint256
: ID of the vault for which stablecoins should be borrowed - 2.
uint256
: Amount of stablecoins to borrow. If there are borrowing fees, the amount of stablecoins actually going to theto
address
Repays a portion of the debt of a vault. Any address can repay debt on behalf of any vault. During debt repayment, you can specify a stablecoin amount that is voluntarily too high with respect to the debt of the vault: it will repay all the debt remaining. Action will revert though if the amount of debt repaid makes the debt of the vault smaller than the dust.
Type and order of the parameters:
- 1.
uint256
: ID of the vault for which debt should be repaid - 2.
uint256
: Amount of stablecoins to repay. Setting an amount higher than the debt of the vault will simply reimburse all the outstanding debt in the vault. In this case, stablecoin transfer from thefrom
address will just be equal to the value of the debt, meaning if you usetype(uint256).max
, you will just be charged the value of the debt
Gets debt in a vault from another vault potentially in another
VaultManager
contract. Only approved addresses by the source vault owner can perform this action, however any vault from any VaultManager
contract of the same stablecoin can see its debt reduced by this means. A solvency check is performed after the debt increase in the source vaultID
.Type and order of the parameters:
- 1.
uint256
: ID of the vault in this contract for which debt should be increased - 2.
address
: Address of theVaultManager
contract where the vault from which debt should be taken is: it can well be the sameVaultManager
contract. If a wrongVaultManager
address is specified (like a contract with specifically the right interface), then this function may just increase the debt in a vault without reducing the debt in another one. - 3.
uint256
: ID of the vault from which debt should be taken. This operation will technically just reduce the debt of this vault. If there are different minting fees in both contracts, then this action will make sure that everything is as if for the borrowers they had been the same - 4.
uint256
: Amount of stablecoins to increase the debt of
Action to approve the
VaultManager
for the collateral (if the specific collateral used supports this).Type and order of the parameters:
- 1.
address
: Owner address which has signed the permit and wants to approve the `VaultManager`` - 2.
uint256
: Amount of collateral to approve - 3.
uint256
: Deadline parameter for the permit - 4.
uint256
:v
parameter for the permit. Thisuint256
is casted touint8
in the contract - 5.
bytes32
:r
parameter - 6.
bytes32
:s
parameter
There are some wrappers for actions above and other external interaction methods.
function createVault(address toVault) external returns (uint256);
It creates a vault that is going to be owned by
toVault
and returns the ID of this vault. function getDebtOut(
uint256 vaultID,
uint256 stablecoinAmount,
uint256 senderBorrowFee
) external;
This is the function that is called by other
VaultManager
contracts if these contracts were requested a getDebtIn
action with debt from a vault in the contract. Hence only VaultManager
of the same treasury contracts can call it. This function ultimately reduces the debt of vaultID
by stablecoinAmount
.The sender of this action specifies the
senderBorrowFee
to make sure that if the senderBorrowFee
is greater than the borrow fee in the contract, then borrow fees are still paid on the debt transferred.Liquidation plays an essential role in Angle Borrowing Module and liquidations of the vaults of a
VaultManager
contract are handled directly in the same contract. There are two external access functions for liquidators to liquidate. function liquidate(
uint256[] memory vaultIDs,
uint256[] memory amounts,
address from,
address to,
address who,
bytes memory data
) public returns (LiquidatorData memory liqData)
And:
function liquidate(
uint256[] memory vaultIDs,
uint256[] memory amounts,
address from,
address to
) public returns (LiquidatorData memory liqData)
The first function is a more advanced function you can use for capital-efficient liquidations, the second one is a wrapper built on top of the first.
For a complete guide on how to use these functions and how to perform liquidations in Angle Borrowing Module, you can look at this docs.
These functions can be used to liquidate an ensemble of vaults specified by their vault IDs. They revert if one of the ID concerns a vault that does not exist or that should be liquidated. It is recommended to call the
checkLiquidation
function for each vault to liquidate before calling for a liquidation.Parameters:
vaultIDs
: List of the vaults to liquidateamounts
: Array of amount of stablecoins to bring for the liquidation of each vault. If an amount is too big, it is going to be rounded down by the contract to the max possible amount to repay for an unhealthy vaultfrom
: Address from which stablecoins for the liquidations will be takneto
: Address to which collateral will be sentwho
: This is an optional argument (could be the zero address), and can be used in case a specific contract should handle the repaymentdata
: Optional again, it can help thewho
contract to know which swap to proceed
Return Values:
liqOpp
: This is a struct with several info on the liquidations that were made by this function. Note that these returned values are denominated from a protocol perspective.stablecoinAmountToReceive
: This is the amount of stablecoins the liquidator has given for the liquidationcollateralAmountToGive
: Current amount of collateral obtained from the protocol from the contractbadDebtFromLiquidation
: Bad debt accrued across the liquidation process in theVaultManager
oracleValue
: Oracle value at which the liquidation took placenewInterestAccumulator
: Value of theinterestAccumulator
at the time of the call (this is a value used to track vaults outstanding debt)
function checkLiquidation(uint256 vaultID, address liquidator)
external
view
returns (LiquidationOpportunity memory liqOpp);
As mentionned above, this function checks whether a given vault is liquidable and if yes gives information regarding its liquidation. It is recommended to call it before asking for the liquidation of a given vault.
The address of the
liquidator
is optional here: it is here to compute the boost on the discount a liquidator could get.The returned value is a struct called
liqOpp
. It contains data about how much stablecoins can be repaid from the liquidation, what discount the liquidator would get, what collateral amount the liquidator could at most get. For more details on the output of this function, you can check the guide here.There are two other functions to check the debt of a vault and the total debt accross all vaults at the
VaultManager
level. These functions are: getVaultDebt(uint256 vaultID)
and getTotalDebt()
.function accrueInterestToTreasury() external returns (uint256 surplusValue, uint256 badDebtValue)
Accrues interest accumulated across all vaults to the surplus and sends the surplus to the treasury: this is the function called by the
Treasury
contract to pull surplus and bad debt from the contract. The role of the Treasury
contract is then to pool all the surplus and bad debt it got from all VaultManager
to handle governance distribution.Surplus is technically minted upon calling this function. In the
VaultManager
contract, calling this contract resets the surplus
and badDebt
value in storage to zero.Besides the function above which can only be called by the
Treasury
contract, the contract has several restricted functions used by governance (or the guardian) to set parameters:setUint64
: to change parameters stored asuint64
setDebtCeiling
: to change the debt ceiling of the `VaultManager``setLiquidationBoostParameters
: sets the parameters for the liquidation booster which encodes the slope of the discount for liquidatorstoggleWhitelist
: to toggle permission for vault ownership by any account or to allow/disallow an address in case whitelisting is activated. If this is activated, vaults that existed prior to that will not be transferrable to addresses which are not whitelisted, and new vaults will only be openable by whitelisted addressessetOracle
: to set the oracle contractsetTreasury
: to change the reference to theTreasury
contract (this function should never be used in practice)togglePause
: to pause the contractsetBaseURI
: to change the ERC721 Metadata URI