GM Lending Pool
The core lending contract stores user balances, user debt, price oracles, asset data, and fee data; calculates interest and health factors; and is the executor of all deposits, borrows, repays, withdrawals, leverage position, and liquidations. The pool contract is deployed by the Lending Pool Factory and inherits from Solmate's Auth contract. The protocol and asset mechanisms are set according to the following functions:
constructor
At deployment, the constructor sets the deployer (msg.sender
of the lendingPoolFactory.deployLendingPool()
call) as the owner and as the Authority (both hereafter referred to as Admin). It then stores its own pool name
, which is provided by the Lending Pool Factory. Lastly, the msg.sender
is stored as the gloopStakersWallet
.
Admin Functions
setGloopStakersWallet
Sets or updates the gloopStakersWallet
address, which will receive a portion of the platform fees to be distributed to future Gloop stakers. This can only be changed by the Admin. Upon success, the call emits an GloopStakerswalletUpdated
event with the caller's address and the new wallet address as parameters.
Parameters:
Name | Type | Description |
---|---|---|
| address | The address of the new Gloop Staker rewards wallet |
setAddressStore
Sets or updates the Address Store contract. The Address Store can only be changed by the Admin. Upon success, the call emits an AddressStoreUpdated
event with the caller's address and the new contract as parameters.
Parameters:
Name | Type | Description |
---|---|---|
| AddressStore | The new AddressStore instance |
setOracle
Sets or updates the GM Lending Oracle contract. The Oracle can only be updated by the Admin. Upon success, the call emits an OracleUpdated
event with the caller's address and the new oracle contract as parameters.
Parameters:
Name | Type | Description |
---|---|---|
| PriceOracle | The Lending Oracle contract instance |
setInterestRateModel
Sets a new or updates the existing interest rate model for a specific asset. Models can only be set or updated by the Admin. Upon success, the call emits an InterestRateModelUpdated
event with the caller's address, asset, and model contract. See the Interest Rate Model section for details.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The asset to assign the model to |
| InterestRateModel | The interest model that the asset will use. |
configureAsset
Configures/adds USDC or a GM token for the Lending Pool, so that users are able to utilize and transfer the tokens. Tokens can only be added or configured by the Admin. If the asset is new to the pool, the vault, configuration struct, and decimals are set for the asset in three separate mappings. The vault contract is the ERC-4626 Vault that has been deployed for the particular GM token. (See GM Vaults section for more.) The decimals are obtained by calling decimals()
on the ERC-20 token.
The configuration struct contains the lending and borrowing factor for each asset as shown here:
If the asset is not new to the pool, the call updates the lending and borrow factors, and, if the updateVault
boolean is marked as true, this function will assign the asset to a new vault/address. Note that this does not move any tokens to the new vault. Each vault has a migrate()
function that moves the tokens.
Once the mappings are successfully updated, an AssetConfigured
or AssetConfigurationUpdated
event is emitted with caller's address, asset, vault contract, and configuration.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The asset to be configured |
| ERC4626 | The tokenized standard type of the asset's vault |
| Configuration | A struct that contains an asset's lend and borrow factors |
| boolean | Updates the asset's vault if true |
setGmTokens
Stores an array of GM Token addresses in the gmTokensAddresses
state array. The array can only be updated by the Admin. Upon success, the call emits a NewGMTokens
event with the caller's address and the new address array as parameters.
Parameters:
Name | Type | Description |
---|---|---|
| address[] memory | An array of GM tokens |
addNewGmToken
Adds a GMX Market (GM) Token to the gmTokensAddresses
state array. Tokens can only be added by the Admin. Upon success, the call emits a NewGMToken
event with the caller's address and the new GM Token address as parameters.
Parameters:
Name | Type | Description |
---|---|---|
| address | The address of the GMX Market Token |
setGMPointsContract
Sets the GM Points contract for the pool. The contract can only be set by the Admin. Upon success, the call emits an GMPointsContractUpdated
event with the caller's address, and the new contract address. See the GM Points section for details.
Parameters:
Name | Type | Description |
---|---|---|
| address | The address of the GM Points contract |
updateLiquidationBonus
Sets or updates the liquidationBonus
, which can only be done by the Admin. Upon success, the call emits a LiquidationBonusUpdated
event with the caller's address and the new liquidation bonus multiplier as parameters.
Parameters:
Name | Type | Description |
---|---|---|
| uint256 | The bonus multiplier for the liquidator |
updateMaxHealthFactor
Sets or updates the MaxHealthFactor
, which can only be done by the Admin. Upon success, the call emits a MaxHealthFactorUpdated
event with the caller's address and the new maximum health factor as parameters.
Parameters:
Name | Type | Description |
---|---|---|
| uint256 | The max Health Factor that forms a limit on how much a liquidator can repay for the borrower. |
setGloopStakersYield
Sets or updates the gloopStakersYieldRatio
. Can only be called by the Admin. The ratio cannot be greater than 25%. The ratio represents the portion of the platform fees to be distributed to Gloop stakers. Upon success, the call emits an GloopStakersYieldUpdated
event with the caller's address and the new ratio as parameters.
Parameters:
Name | Type | Description |
---|---|---|
| uint256 | The new Gloop Staker yield (percentage of lending platform fees) |
withdrawReserves (to be completed)
Core External/Public Functions
deposit
This function is the main and first point of entry to the protocol. Depositing is the same as "lending" an asset to the pool. At present, the only assets allowed for depositing are GM tokens and USDC.
When a user deposits, since the protocol uses ERC-4626 tokenized vaults, the amount of tokens to transfer are converted into "shares
" using the internalBalanceExchangeRate()
. The internalBalances
and totalInternalBalances
mappings of the asset are then increased by shares
amount for the user and the protocol. The token amount
is then transferred briefly to the pool contract, which immediately transfers them to the asset's ERC-4626 vault. If the asset is a GM token, then it is automatically enabled as collateral for the user. If the asset is USDC, it is not. (Currently, USDC is solely used for liquidity provision.) Upon successful transfer, the Deposit
event is emitted, including msg.sender
's address, the asset
, and the amount
.
If block.timestamp
is not past the gmPointsContract.pointsEndTime()
(e.g. the Points program is still going on), then the caller's points are updated via updatePoints()
in the GM Points contract. See GM Points for more on the point system.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The asset to deposit |
| uint256 | The amount of tokens to deposit |
withdraw
Transfers an amount
of tokens to the caller. This function is essentially a public wrapper of the internal function _withdraw()
. For convenience, the withdraw logic will be explained here.
_withdraw()
:
Withdraws an amount
of the caller's tokens from the pool. The asset
can be GM or USDC tokens. As during depositing, the amount of tokens to transfer are converted into "shares
" using the internalBalanceExchangeRate()
. If the shares requested are less than the user's balance or zero, the call with revert. Similar to disableAsset()
, if the withdrawal request would bring the user's health factor below 1, the call with revert. If the health factor remains above 1, then the internalBalances
and totalInternalBalances
mappings of the asset are decreased by shares
amount for the user and the protocol. The token amount
is then transferred out of the asset's vault to the pool contract, which immediately transfers them to the user. Upon successful transfer, the Withdraw
event is emitted, including msg.sender
's address, the asset
, and the amount
.
If block.timestamp
is not past the gmPointsContract.pointsEndTime()
(e.g. the Points program is still going on), then the caller's points are updated via updatePoints()
in the GM Points contract. See GM Points for more on the point system.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The asset to withdraw |
| uint256 | The amount of tokens to withdraw |
borrow
Transfers an amount of borrowed USDC to the caller. This function is essentially a public wrapper of the internal function internalBorrow()
. For convenience, the borrow logic will be explained here.
internalBorrow()
:
Once a user has deposited GM tokens as enabled collateral, the user is able to borrow USDC. (More assets may be available to borrow in the future.) If the user has an existing loan, the unaccrued interest will be calculated for and added to the position via accrueInterest()
. The user's health factor is then calculated based on the requested borrow (see calculateHealthFactor()
for details). If the health factor remains above 1, then the user may borrow. If not, the function will revert.
Similar to depositing, the requested amount
of tokens is converted to debtUnits
by the internalDebtExchangeRate()
. The debtUnits
are then added to the internalDebt
and totalInternalDebt
mappings for the user and the protocol, respectively. The token amount is also added to the cachedTotalBorrows
mapping for the borrowed asset (USDC).
The tokens are now finally transferred from USDC's vault to the pool contract, which immediately transfers them to the caller/borrower. Upon successful transfer, the Borrow
event is emitted, including msg.sender
's address, the asset
, and the amount
.
If block.timestamp
is not past the gmPointsContract.pointsEndTime()
(e.g. the Points program is still going on), then the caller's points are updated via updatePoints()
in the GM Points contract. See GM Points for more on the point system.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The asset to borrow (for now, this can only be USDC) |
| uint256 | The amount of USDC to borrow |
repay
Transfers an amount of USDC from the caller to the protocol as a repayment of debt. This function is essentially a public wrapper of the internal function _repay()
. For convenience, the repay logic will be explained here.
_repay()
:
Similar to borrowing, the repayment amount
of tokens is converted to debtUnits
by the internalDebtExchangeRate()
, with the function reverting if the debtUnits
are greater than the internalDebt
of the user. The debtUnits
are then subtracted from the internalDebt
and totalInternalDebt
mappings for the user and the protocol, respectively.
Once a user has deposited GM tokens as enabled collateral, the user is able to borrow USDC. (More assets may be available to borrow in the future.) If the user has an existing loan, then interest will be calculated for their position via accrueInterest()
. The token amount
is also subtracted from the cachedTotalBorrows
mapping for the borrowed asset (USDC).
The tokens are now finally transferred from the user to the pool contract, which immediately transfers them to the USDC vault. Upon successful transfer, the Repay
event is emitted, including the msg.sender
's address, the asset
, and the amount
.
If block.timestamp
is not past the gmPointsContract.pointsEndTime()
(e.g. the Points program is still going on), then the caller's points are updated via updatePoints()
in the GM Points contract. See GM Points for more on the point system.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The asset to repay (for now, this can only be USDC) |
| uint256 | The amount of USDC to repay |
liquidateUser
To be Completed
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The borrowed asset to be repaid |
| address | The address with poor health factor to liquidate |
| uint256 | The amount the liquidator needs to repay to bring the borrower's position back into good health. |
Ancillary Functions
isValidGmToken
Searches the gmTokensAddresses
array to verify if the input asset is a valid GM token and returns true if so. This check ensures that only GM tokens can be deposited as collateral in the protocol.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The token to validate |
Returns:
Name | Type | Description |
---|---|---|
| bool | If true, the asset is a valid GM token. |
enableAsset
Enables an asset as a user's collateral. The asset cannot be USDC. The asset is pushed to the userCollateral
and enabledCollateral
mappings. An AssetEnabled
event is then emitted with the caller's address and asset
as event parameters.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The token to enable |
disableAsset
Disables an asset as a user's collateral. The asset must be an enabled GM token and cannot be USDC. If the asset is the user's sole collateral, then the user must not have an active borrow against it. If the user isn't borrowing or employing leverage, then the collateral will be disabled.
If the user has multiple assets as active collateral, the value of the asset to be disabled is subtracted from the total value of the user's assets. If the theoretical health factor based on the new collateral value remains above 1e18
, then the function will proceed. If not, the function reverts with an error.
The asset is disabled by deleting it from the userCollateral
mapping and switching the enabledCollateral[msg.sender]
boolean to false. An AssetDisabled
event is emitted with the msg.sender
's address and the asset
now disabled.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The token to disable |
userLiquidatable
Returns true if canBorrow(borrowedAsset, user, 0)
returns false, which means the user's health factor is below 1e18
. If false, the user cannot be liquidated at the time of the call.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The asset currently borrowed |
| address | The address to check |
Returns:
Name | Type | Description |
---|---|---|
unnamed | bool | If true, the user can be liquidated. If false, user cannot. |
canBorrow
Returns true if calculateHealthFactor()
is greater than or equal to 1e18
. If true, the user can borrow the input amount
.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The asset requested to borrow |
| address | The address to calculate the borrow balance for |
| uint256 | The amount requested to borrow |
Returns:
Name | Type | Description |
---|---|---|
unnamed | bool | If true, the user can borrow. If false, user cannot. |
_seizeCollateral
To be completed
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The asset requested to borrow |
| address | The address to calculate the borrow balance for |
| uint256 | The amount requested to borrow |
getUserCollateralValue
Calculates and sums the value of each asset the borrower currently has enabled as collateral.
Parameters:
Name | Type | Description |
---|---|---|
| address | The address the collateral belongs to |
Returns:
Name | Type | Description |
---|---|---|
unnamed | uint256 | The total collateral value of the user/borrower |
calculateHealthFactor
Returns the new health factor after a requested borrow by the user
. The calculation occurs in a few steps:
First, the collateral values for each of the user's enabled assets are multiplied by each asset's
lendingFactor
, then summed together to get themaximumBorrowable
value.The
hypotheticalBorrowBalance
is determined by adding the requested borrow amount to the currentborrowBalance
, if any.The
borrowBalance
value is then determined by multiplying thehypotheticalBorrowBalance
by the current price of the asset.If the asset to be borrowed has a
borrowFactor
, it is multiplied by themaximumBorrowable
value to obtain theactualBorrowable
value.Dividing the
actualBorrowable
value by theborrowBalance
results in the Health Factor. If the Health Factor is above1e18
, e.g. ifactualBorrowable
is greater than or equal to the futureborrowBalance
, then the user can borrow the amount requested.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The asset requested to borrow |
| address | The address to calculate the borrow balance for |
| uint256 | The amount requested to borrow |
Returns:
Name | Type | Description |
---|---|---|
unnamed | uint256 | The new Health Factor after the requested borrow |
maxBorrowableValue
Calculates and returns the maximumBorrowableValue
for the caller. This function loops through the userCollateral
array and sums the products of the collateral value and the lendingFactor
for each asset. If the asset to be borrowed has a borrowFactor
, it is multiplied by the maximumBorrowableValue
to result in a lesser borrowable value. However, since USDC has a borrowFactor
of 1, so currently this step has no effect on the answer. The resulting maximumBorrowableValue
is the theoretical maximum value allowed to borrow in relation to the user's current collateral.
Returns:
Name | Type | Description |
---|---|---|
| uint256 | The maximum value the caller can borrow |
getCollateral
Retrieves the user's ERC20[]
array of collateral assets from the userCollateral
mapping.
Parameters:
Name | Type | Description |
---|---|---|
| address | The owner of the collateral |
Returns:
Name | Type | Description |
---|---|---|
unnamed | ERC20[] | An |
Accounting Logic Functions
totalUnderlying
Returns the total amount of underlying tokens both held by and owed to the pool. It does so by adding the totalBorrows()
to the availableLiquidity()
of the asset.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The underlying asset |
Returns:
Name | Type | Description |
---|---|---|
unnamed | uint256 | The total amount of tokens held by and owed to the pool |
availableLiquidity
Returns the total amount of underlying tokens held in the token's vault for the Lending pool. After calling balanceOf
on the asset's vault, the shares are converted to the returned token amount by calling convertToAssets()
on the vault.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The underlying asset |
Returns:
Name | Type | Description |
---|---|---|
unnamed | uint256 | The total amount of tokens held in the asset's vault. |
balanceOf
Calculates the token balance of the user by multiplying the user's internal balance units by the internalBalanceExchangeRate()
.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The underlying asset |
| address | The address to get the token balance of |
Returns:
Name | Type | Description |
---|---|---|
unnamed | uint256 | The token balance of the user |
internalBalanceExchangeRate
Calculates the exchange rate between underlying tokens and internal balance units. By dividing the totalUnderlying()
shares by the totalInternalBalance
units for the asset, this function converts ERC4626 shares into tokens. If the totalInternalBalance
is zero, then the exchange rate is 1.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The underlying asset |
Returns:
Name | Type | Description |
---|---|---|
unnamed | uint256 | The exchange rate |
borrowBalance
Calculates and returns the borrow balance of an address by multiplying the user's internal debt units by the internalDebtExchangeRate()
.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The borrowed asset |
| address | The address to calculate the borrow balance for |
Returns:
Name | Type | Description |
---|---|---|
unnamed | uint256 | The borrow balance of the user |
internalDebtExchangeRate
Calculates the exchange rate between underlying tokens and internal debt units. By dividing the totalBorrows()
shares by the totalInternalDebt
units for the asset, this function converts ERC4626 shares into tokens. If the totalInternalDebt
is zero, then the exchange rate is 1.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The underlying asset |
Returns:
Name | Type | Description |
---|---|---|
unnamed | uint256 | The exchange rate |
totalBorrows
Calculates and returns the total amount of underlying tokens being loaned out to borrowers. To calculate the accrued interest on the total underlying tokens and cachedTotalBorrows
for the asset are passed as arguments into the getBorrowRate()
function called on the Interest Rate Model set for the asset (see Interest Rate Model section for details). Using the time or blockDelta
since the lastInterestAccrual
, we can calculate the interestAccumulator
:
Multiplying cachedtotalBorrows
by the interestAccumulator
gives us the total borrows with the interest accrual.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The underlying asset |
Returns:
Name | Type | Description |
---|---|---|
unnamed | uint256 | The total amount of borrowed tokens |
accrueInterest
Deriving the totalInterestAccumulated
from totalBorrows()
, this function calculates the gloopStakersYield
and passes it into _updateStakersYield()
. It then adds the interest accumulates (less the staker yield) to the cachedTotalBorrows
internal mapping for the given asset. The current block.number
is then stored in lastInterestAccrual
for the asset.
Parameters:
Name | Type | Description |
---|---|---|
| ERC20 | The underlying asset (only USDC for now) |
_updateStakersYield
Converts the incoming _stakersYield
interest amount to shares via the internalBalanceExchangeRate
, These shares are added both to the internalBalances
for the gloopStakersWallet and the totalInternalBalances
for the asset.
Parameters:
Name | Type | Description |
---|---|---|
| uint256 | The interest accumulated from lending fees that goes to Gloop stakers. |
_updateTotalReserves (To Be Completed)
Last updated