Skip to main content

LendController

The LendController is the primary user-facing contract for each lending market. Borrowers interact with it to create loans, manage collateral, borrow more, repay, and get liquidated. It wraps the core controller.vy module — which contains all shared loan logic — and extends it with lending-specific features: borrow caps, vault token transfers, and available balance tracking.

Each lending market gets its own LendController instance, deployed by the LendFactory.

LendController.vy

The source code for the LendController.vy contract can be found on GitHub. The core module is controller.vy. The contract is written in Vyper version 0.4.3.

Deployment addresses will be added once contracts are finalized.

Examples use Ethers v6 with an ABI generated from the pinned source linked in each panel. Replace every zero address and amount placeholder. Read-only calls use the verified Ethereum v2 reference market; execute a write only on a fork first.

info

The LendController exports most of its functions from the controller.vy module. Functions documented below include both module-exported functions and lending-specific extensions. The module pattern means the core loan logic is shared with MintController (used for crvUSD mint markets).


Loan Management

create_loan

LendController.create_loan(_collateral: uint256, _debt: uint256, _N: uint256, _for: address = msg.sender, _callbacker: address = empty(address), _calldata: Bytes[CALLDATA_MAX_SIZE] = b"")

Creates a new loan by depositing collateral and borrowing tokens. The collateral is distributed across _N bands in the AMM.

InputTypeDescription
_collateraluint256Amount of collateral to deposit
_debtuint256Amount of borrowed tokens to receive
_Nuint256Number of bands to distribute collateral across (4–50)
_foraddressAddress that owns the new loan
_callbackeraddressOptional callback contract for leveraged operations
_calldataBytes[CALLDATA_MAX_SIZE]Data passed to the callback contract

Emits: Borrow event.

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
def create_loan(
_collateral: uint256,
_debt: uint256,
_N: uint256,
_for: address = msg.sender,
_callbacker: address = empty(address),
_calldata: Bytes[CALLDATA_MAX_SIZE] = b"",
):
"""
@notice Create loan but pass borrowed tokens to a callback first so that it can build leverage
@param _collateral Amount of collateral to use
@param _debt Borrowed asset debt to take
@param _N Number of bands to deposit into (to do autoliquidation-deliquidation),
can be from MIN_TICKS to MAX_TICKS
@param _for Address to create the loan for
@param _callbacker Address of the callback contract
@param _calldata Any data for callbacker
"""
assert self._has_approval(_for)

more_collateral: uint256 = 0
if _callbacker != empty(address):
tkn.transfer(BORROWED_TOKEN, _callbacker, _debt)
# If there is any unused debt, callbacker can send it to the user
cb: IController.CallbackData = self._execute_callback(
_callbacker,
CALLBACK_DEPOSIT,
_for,
0,
_collateral,
_debt,
_calldata,
)
assert cb.borrowed == 0 # dev: Not available
more_collateral = cb.collateral

total_collateral: uint256 = _collateral + more_collateral
# … the remaining implementation is linked above.
Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, signer)
const tx = await contract.create_loan(
/* _collateral: uint256 */ 0n,
/* _debt: uint256 */ 0n,
/* _N: uint256 */ 0n,
/* _for: address */ "0x0000000000000000000000000000000000000000",
/* _callbacker: address */ "0x0000000000000000000000000000000000000000",
/* _calldata: Bytes[CALLDATA_MAX_SIZE] */ 0n,
)
await tx.wait()

borrow_more

LendController.borrow_more(_collateral: uint256, _debt: uint256, _for: address = msg.sender, _callbacker: address = empty(address), _calldata: Bytes[CALLDATA_MAX_SIZE] = b"")

Adds additional collateral and/or borrows more tokens against an existing loan.

InputTypeDescription
_collateraluint256Additional collateral to deposit (can be 0)
_debtuint256Additional debt to take on (can be 0)
_foraddressAddress that owns the loan
_callbackeraddressOptional callback contract for leveraged operations
_calldataBytes[CALLDATA_MAX_SIZE]Data passed to the callback contract

Emits: Borrow event.

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
def borrow_more(
_collateral: uint256,
_debt: uint256,
_for: address = msg.sender,
_callbacker: address = empty(address),
_calldata: Bytes[CALLDATA_MAX_SIZE] = b"",
):
"""
@notice Borrow more borrowed tokens while adding more collateral using a callback (to leverage more)
@param _collateral Amount of collateral to add
@param _debt Amount of borrowed asset debt to take
@param _for Address to borrow for
@param _callbacker Address of the callback contract
@param _calldata Any data for callbacker
"""
if _debt == 0:
return
assert self._has_approval(_for)

more_collateral: uint256 = 0
if _callbacker != empty(address):
tkn.transfer(BORROWED_TOKEN, _callbacker, _debt)
# If there is any unused debt, callbacker can send it to the user
cb: IController.CallbackData = self._execute_callback(
_callbacker,
CALLBACK_DEPOSIT,
_for,
0,
_collateral,
_debt,
_calldata,
)
assert cb.borrowed == 0 # dev: Not available
more_collateral = cb.collateral

rate_mul: uint256 = self._add_collateral_borrow(
_collateral + more_collateral, _debt, _for, False
# … the remaining implementation is linked above.
Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, signer)
const tx = await contract.borrow_more(
/* _collateral: uint256 */ 0n,
/* _debt: uint256 */ 0n,
/* _for: address */ "0x0000000000000000000000000000000000000000",
/* _callbacker: address */ "0x0000000000000000000000000000000000000000",
/* _calldata: Bytes[CALLDATA_MAX_SIZE] */ 0n,
)
await tx.wait()

add_collateral

LendController.add_collateral(_collateral: uint256, _for: address = msg.sender)

Adds collateral to an existing loan without borrowing more. The borrower must call it directly or authorize the caller.

InputTypeDescription
_collateraluint256Amount of collateral to add
_foraddressAddress of the borrower to add collateral for

Emits: Borrow event.

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
def add_collateral(_collateral: uint256, _for: address = msg.sender):
"""
@notice Add extra collateral to avoid bad liquidations
@param _collateral Amount of collateral to add
@param _for Address to add collateral for
"""
if _collateral == 0:
return
assert self._has_approval(_for)
self._add_collateral_borrow(_collateral, 0, _for, False)
tkn.transfer_from(COLLATERAL_TOKEN, msg.sender, AMM.address, _collateral)
self._save_rate()


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, signer)
const tx = await contract.add_collateral(
/* _collateral: uint256 */ 0n,
/* _for: address */ "0x0000000000000000000000000000000000000000",
)
await tx.wait()

remove_collateral

LendController.remove_collateral(_collateral: uint256, _for: address = msg.sender)

Removes collateral from an existing loan. Reverts if the resulting health would be too low.

InputTypeDescription
_collateraluint256Amount of collateral to remove
_foraddressAddress that owns the loan; the caller must be authorized

Emits: RemoveCollateral event.

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
def remove_collateral(_collateral: uint256, _for: address = msg.sender):
"""
@notice Remove some collateral without repaying the debt
@param _collateral Amount of collateral to remove
@param _for Address to remove collateral for
"""
if _collateral == 0:
return
assert self._has_approval(_for)
self._add_collateral_borrow(_collateral, 0, _for, True)
tkn.transfer_from(COLLATERAL_TOKEN, AMM.address, _for, _collateral)
self._save_rate()


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, signer)
const tx = await contract.remove_collateral(
/* _collateral: uint256 */ 0n,
/* _for: address */ "0x0000000000000000000000000000000000000000",
)
await tx.wait()

repay

LendController.repay(_wallet_d_debt: uint256, _for: address = msg.sender, _max_active_band: int256 = max_value(int256), _callbacker: address = empty(address), _calldata: Bytes[CALLDATA_MAX_SIZE] = b"", _shrink: bool = False)

Repays debt for a loan. If _wallet_d_debt exceeds the current debt, only the outstanding debt is repaid. The borrower must call it directly or authorize the caller.

InputTypeDescription
_wallet_d_debtuint256Amount of debt to repay from the wallet (use max_value(uint256) to repay all)
_foraddressAddress of the borrower to repay for
_max_active_bandint256Maximum active band accepted for the repayment
_callbackeraddressOptional callback contract
_calldataBytes[CALLDATA_MAX_SIZE]Data passed to the callback contract
_shrinkboolWhether to shrink a soft-liquidated position

Emits: Repay event.

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
def repay(
_wallet_d_debt: uint256,
_for: address = msg.sender,
_max_active_band: int256 = max_value(int256),
_callbacker: address = empty(address),
_calldata: Bytes[CALLDATA_MAX_SIZE] = b"",
_shrink: bool = False
):
"""
@notice Repay debt (partially or fully)
@param _wallet_d_debt The amount of debt to repay from user's wallet.
If it's max_value(uint256) or just higher than the current debt - will do full repayment.
@param _for The user to repay the debt for
@param _max_active_band Don't allow active band to be higher than this (to prevent front-running the repay)
@param _callbacker Address of the callback contract
@param _calldata Any data for callbacker
@param _shrink Whether shrink soft-liquidated part of the position or not
"""
assert self._has_approval(_for)
debt: uint256 = 0
rate_mul: uint256 = 0
debt, rate_mul = self._debt(_for)
self._check_loan_exists(debt)
xy: uint256[2] = staticcall AMM.get_sum_xy(_for)

cb: IController.CallbackData = empty(IController.CallbackData)
if _callbacker != empty(address):
xy = extcall AMM.withdraw(_for, WAD)
tkn.transfer_from(COLLATERAL_TOKEN, AMM.address, _callbacker, xy[1])
cb = self._execute_callback(
_callbacker, CALLBACK_REPAY, _for, xy[0], xy[1], debt, _calldata
)

d_debt: uint256 = min(min(_wallet_d_debt, debt) + xy[0] + cb.borrowed, debt)
assert d_debt > 0 # dev: no coins to repay

if d_debt == debt:
# … the remaining implementation is linked above.
Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, signer)
const tx = await contract.repay(
/* _wallet_d_debt: uint256 */ 0n,
/* _for: address */ "0x0000000000000000000000000000000000000000",
/* _max_active_band: int256 */ 0n,
/* _callbacker: address */ "0x0000000000000000000000000000000000000000",
/* _calldata: Bytes[CALLDATA_MAX_SIZE] */ 0n,
/* _shrink: bool */ false,
)
await tx.wait()

liquidate

LendController.liquidate(_user: address, _min_x: uint256, _frac: uint256 = 10**18, _callbacker: address = empty(address), _calldata: Bytes[CALLDATA_MAX_SIZE] = b"")

Liquidates an unhealthy loan (health < 0). The caller repays the debt and receives the remaining collateral. Supports partial liquidation if the position is partially in soft liquidation.

InputTypeDescription
_useraddressAddress of the borrower to liquidate
_min_xuint256Minimum amount of collateral to receive (slippage protection)
_fracuint256Fraction of the position to liquidate, where 10**18 is 100%
_callbackeraddressOptional callback contract
_calldataBytes[CALLDATA_MAX_SIZE]Data passed to the callback contract

Emits: Liquidate event.

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
def liquidate(
_user: address,
_min_x: uint256,
_frac: uint256 = 10**18,
_callbacker: address = empty(address),
_calldata: Bytes[CALLDATA_MAX_SIZE] = b"",
):
"""
@notice Perform a bad liquidation (or self-liquidation) of user if health is not good
@param _user Address of the user to liquidate
@param _min_x Minimal amount of borrowed asset to receive (to avoid liquidators being sandwiched)
@param _frac Fraction to liquidate; 100% = 10**18
@param _callbacker Address of the callback contract
@param _calldata Any data for callbacker
"""
approval: bool = self._has_approval(_user)
liquidation_discount: uint256 = self.liquidation_discounts[_user]
debt: uint256 = 0
rate_mul: uint256 = 0
debt, rate_mul = self._debt(_user)

health_before: int256 = self._health(_user, debt, True, liquidation_discount)
health_limit: uint256 = 0
if not approval:
assert health_before < 0, "Not enough rekt"
health_limit = liquidation_discount

final_debt: uint256 = debt
assert _frac <= WAD, "frac>100%"
debt = unsafe_div(debt * _frac + (WAD - 1), WAD)
assert debt > 0
final_debt = unsafe_sub(final_debt, debt)

# If liquidating entire debt, ensure full collateral withdrawal
f_remove: uint256 = self._get_f_remove(_frac, health_limit)
if final_debt == 0:
f_remove = WAD
# … the remaining implementation is linked above.
Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, signer)
const tx = await contract.liquidate(
/* _user: address */ "0x0000000000000000000000000000000000000000",
/* _min_x: uint256 */ 0n,
/* _frac: uint256 */ 0n,
/* _callbacker: address */ "0x0000000000000000000000000000000000000000",
/* _calldata: Bytes[CALLDATA_MAX_SIZE] */ 0n,
)
await tx.wait()

Loan Info

debt

LendController.debt(_user: address) -> uint256: view

Returns the current debt of a user, including accrued interest.

InputTypeDescription
_useraddressBorrower address

Returns: current debt (uint256).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
def debt(_user: address) -> uint256:
"""
@notice Get the value of debt without changing the state
@param _user User address
@return Value of debt
"""
return self._debt(_user)[0]


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.debt(
/* _user: address */ "0x0000000000000000000000000000000000000000",
)

health

LendController.health(_user: address, _full: bool = False) -> int256: view

Returns the health of a user's loan. Health > 0 means the loan is safe; health < 0 means it can be liquidated. If _full is true, the calculation also accounts for an oracle-price difference above the user's highest band when the position is not in soft liquidation. If false, it calculates health as for a user in soft-liquidation mode.

InputTypeDescription
_useraddressBorrower address
_fullboolIf true, use full (pessimistic) health calculation

Returns: health value (int256).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
def health(_user: address, _full: bool = False) -> int256:
"""
@notice Returns position health normalized to 1e18 for the user.
Liquidation starts when < 0, however devaluation of collateral doesn't cause liquidation
@param _user Address of the user
@param _full If True, use the full health calculation including bad debt discounts
@return Health value normalized to 1e18
"""
return self._health(
_user, self._debt(_user)[0], _full, self.liquidation_discounts[_user]
)


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.health(
/* _user: address */ "0x0000000000000000000000000000000000000000",
/* _full: bool */ false,
)

loan_exists

LendController.loan_exists(_user: address) -> bool: view

Returns whether a loan exists for the given user.

InputTypeDescription
_useraddressBorrower address

Returns: whether the loan exists (bool).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
def loan_exists(_user: address) -> bool:
"""
@notice Check whether there is a loan of `user` in existence
@param _user Address of the user to check
@return True if the user has an active loan, False otherwise
"""
return self.loan[_user].initial_debt > 0


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.loan_exists(
/* _user: address */ "0x0000000000000000000000000000000000000000",
)

user_state

LendController.user_state(_user: address) -> uint256[4]: view

Returns the full state of a user's loan: [collateral, borrowed, debt, N].

InputTypeDescription
_useraddressBorrower address

Returns: [collateral, borrowed, debt, N] (uint256[4]).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
def user_state(_user: address) -> uint256[4]:
"""
@notice Return the user state in one call
@param _user User to return the state for
@return (collateral, borrowed, debt, N)
"""
return staticcall self._view.user_state(_user)


@internal
def _configure(
_loan_discount: uint256,
_liquidation_discount: uint256,
_monetary_policy: IMonetaryPolicy,
_view_blueprint: address,
_fee: uint256,
_price_oracle: IPriceOracle,
_liquidity_mining_callback: ILMCallback,
):
if _loan_discount != SKIP_CONFIG_UINT256 and _liquidation_discount != SKIP_CONFIG_UINT256:
self.loan_discount = _loan_discount
self.liquidation_discount = _liquidation_discount
if _monetary_policy.address != SKIP_CONFIG_ADDRESS:
self._monetary_policy = _monetary_policy
if _view_blueprint != SKIP_CONFIG_ADDRESS:
view: address = create_from_blueprint(
_view_blueprint,
self,
SQRT_BAND_RATIO,
LOGN_A_RATIO,
AMM,
A,
COLLATERAL_TOKEN,
COLLATERAL_PRECISION,
BORROWED_TOKEN,
BORROWED_PRECISION,
# … the remaining implementation is linked above.
Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.user_state(
/* _user: address */ "0x0000000000000000000000000000000000000000",
)

user_prices

LendController.user_prices(_user: address) -> uint256[2]: view

Returns the upper and lower price bounds of a user's collateral bands in the AMM.

InputTypeDescription
_useraddressBorrower address

Returns: [price_upper, price_lower] (uint256[2]).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
def user_prices(_user: address) -> uint256[2]: # Upper, lower
"""
@notice Lowest price of the lower band and highest price of the upper band the user has deposit in the AMM
@param _user User address
@return (upper_price, lower_price)
"""
return staticcall self._view.user_prices(_user)


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.user_prices(
/* _user: address */ "0x0000000000000000000000000000000000000000",
)

users_to_liquidate

LendController.users_to_liquidate(_from: uint256 = 0, _limit: uint256 = 0) -> IController.Position[]: view

Returns a list of user positions that can be liquidated, paginated.

InputTypeDescription
_fromuint256Starting index
_limituint256Maximum number of positions to return

Returns: array of liquidatable positions (IController.Position[]).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
def users_to_liquidate(
_from: uint256 = 0, _limit: uint256 = 0
) -> DynArray[IController.Position, 1000]:
"""
@notice Returns a dynamic array of users who can be "hard-liquidated".
This method is designed for convenience of liquidation bots.
@param _from Loan index to start iteration from
@param _limit Number of loans to look over
@return Dynamic array with detailed info about positions of users
"""
return staticcall self._view.users_to_liquidate(_from, _limit)


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.users_to_liquidate(
/* _from: uint256 */ 0n,
/* _limit: uint256 */ 0n,
)

tokens_to_liquidate

LendController.tokens_to_liquidate(_user: address, _frac: uint256 = 10**18) -> uint256: view

Returns the amount of borrowed tokens needed to fully liquidate a user's position.

InputTypeDescription
_useraddressBorrower address
_fracuint256Fraction to liquidate, where 10**18 is 100%

Returns: tokens needed for liquidation (uint256).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
def tokens_to_liquidate(_user: address, _frac: uint256 = WAD) -> uint256:
"""
@notice Calculate the amount of borrowed asset to have in liquidator's wallet to liquidate a user
@param _user Address of the user to liquidate
@param _frac Fraction to liquidate; 100% = 10**18
@return The amount of borrowed asset needed
"""
assert _frac <= WAD, "frac>100%"
health_limit: uint256 = 0
if not self._has_approval(_user):
health_limit = self.liquidation_discounts[_user]
borrowed: uint256 = unsafe_div(
(staticcall AMM.get_sum_xy(_user))[0]
* self._get_f_remove(_frac, health_limit),
WAD,
)
debt: uint256 = unsafe_div(self._debt(_user)[0] * _frac + (WAD - 1), WAD)

return crv_math.sub_or_zero(debt, borrowed)


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.tokens_to_liquidate(
/* _user: address */ "0x0000000000000000000000000000000000000000",
/* _frac: uint256 */ 0n,
)

total_debt

LendController.total_debt() -> uint256: view

Returns the total outstanding debt across all loans in this market, including accrued interest.

Returns: total debt (uint256).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
@reentrant
def total_debt() -> uint256:
"""
@notice Total debt of this controller
@dev Marked as reentrant because used by monetary policy
@return Total outstanding debt with accrued interest
"""
return self._get_total_debt()


@internal
@pure
def _get_y_effective(
_collateral: uint256,
_N: uint256,
_discount: uint256,
_SQRT_BAND_RATIO: uint256,
_A: uint256,
) -> uint256:
"""
@notice Intermediary method which calculates y_effective defined as x_effective / p_base,
however discounted by loan_discount.
x_effective is an amount which can be obtained from collateral when liquidating
@param _collateral Amount of collateral with 18 decimals to get the value for
@param _N Number of bands the deposit is made into
@param _discount Loan discount at 1e18 base (e.g. 1e18 == 100%)
@param _SQRT_BAND_RATIO Square root of the band ratio sqrt((A-1)/A) at 1e18 base
@param _A Band width factor
@return y_effective
"""
# x_effective = sum_{i=0..N-1}(y / N * p(n_{n1+i})) =
# = y / N * p_oracle_up(n1) * sqrt((A - 1) / A) * sum_{0..N-1}(((A-1) / A)**k)
# === d_y_effective * p_oracle_up(n1) * sum(...) === y_effective * p_oracle_up(n1)
# d_y_effective = y / N / sqrt(A / (A - 1))
# d_y_effective: uint256 = collateral * unsafe_sub(10**18, discount) / (SQRT_BAND_RATIO * N)
# Make some extra discount to always deposit lower when we have DEAD_SHARES rounding
# … the remaining implementation is linked above.
Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.total_debt()

n_loans

LendController.n_loans() -> uint256: view

Returns the total number of active loans.

Returns: number of loans (uint256).

<>Source code

The implementation is in lending/LendController.vy at 0d726b1. This public ABI entry is compiler-generated or inherited; inspect the linked contract source and interfaces for its implementation.

Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, signer)
const tx = await contract.n_loans()
await tx.wait()

loans

LendController.loans(_index: uint256) -> address: view

Returns the borrower address at a given loan index.

InputTypeDescription
_indexuint256Loan index

Returns: borrower address (address).

<>Source code

The implementation is in lending/LendController.vy at 0d726b1. This public ABI entry is compiler-generated or inherited; inspect the linked contract source and interfaces for its implementation.

Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, signer)
const tx = await contract.loans(
/* _index: uint256 */ 0n,
)
await tx.wait()

loan_ix

LendController.loan_ix(_user: address) -> uint256: view

Returns the index of a user's loan in the loans array.

InputTypeDescription
_useraddressBorrower address

Returns: loan index (uint256).

<>Source code

The implementation is in lending/LendController.vy at 0d726b1. This public ABI entry is compiler-generated or inherited; inspect the linked contract source and interfaces for its implementation.

Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, signer)
const tx = await contract.loan_ix(
/* _user: address */ "0x0000000000000000000000000000000000000000",
)
await tx.wait()

Health Previews

These functions allow UIs to show the health impact of operations before the user submits a transaction.

create_loan_health_preview

LendController.create_loan_health_preview(_collateral: uint256, _debt: uint256, _N: uint256, _for: address, _full: bool) -> int256: view

Previews the health of a loan that would result from calling create_loan.

InputTypeDescription
_collateraluint256Collateral amount
_debtuint256Debt amount
_Nuint256Number of bands
_foraddressBorrower address
_fullboolFull (pessimistic) health

Returns: predicted health (int256).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
def create_loan_health_preview(
_collateral: uint256,
_debt: uint256,
_N: uint256,
_for: address,
_full: bool,
) -> int256:
"""
@notice Calculates health after calling create_loan with the same params
@param _collateral Amount of collateral to use (from wallet + callback).
Note: the collateral amount coming from the callback should be included.
@param _debt Borrowed asset debt to take
@param _N Number of bands to deposit into (to do autoliquidation-deliquidation),
can be from MIN_TICKS to MAX_TICKS
@param _for Address of the user the loan is being created for
@param _full Whether it's a 'full' health or not
@return Signed health value
"""
return staticcall self._view.create_loan_health_preview(
_collateral, _debt, _N, _for, _full
)


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.create_loan_health_preview(
/* _collateral: uint256 */ 0n,
/* _debt: uint256 */ 0n,
/* _N: uint256 */ 0n,
/* _for: address */ "0x0000000000000000000000000000000000000000",
/* _full: bool */ false,
)

borrow_more_health_preview

LendController.borrow_more_health_preview(_collateral: uint256, _debt: uint256, _for: address, _full: bool) -> int256: view

Previews the health after borrowing more.

InputTypeDescription
_collateraluint256Additional collateral
_debtuint256Additional debt
_foraddressBorrower address
_fullboolFull (pessimistic) health

Returns: predicted health (int256).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
def borrow_more_health_preview(
_collateral: uint256,
_debt: uint256,
_for: address,
_full: bool,
) -> int256:
"""
@notice Calculates health after calling borrow_more with the same params
@param _collateral Amount of collateral to add (from wallet + callback).
Note: the collateral amount coming from the callback should be included.
@param _debt Amount of borrowed asset debt to take
@param _for Address to borrow for
@param _full Whether it's a 'full' health or not
@return Signed health value
"""
return staticcall self._view.borrow_more_health_preview(
_collateral, _debt, _for, _full
)


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.borrow_more_health_preview(
/* _collateral: uint256 */ 0n,
/* _debt: uint256 */ 0n,
/* _for: address */ "0x0000000000000000000000000000000000000000",
/* _full: bool */ false,
)

add_collateral_health_preview

LendController.add_collateral_health_preview(_collateral: uint256, _for: address, _full: bool) -> int256: view

Previews the health after adding collateral.

InputTypeDescription
_collateraluint256Collateral to add
_foraddressBorrower address
_fullboolFull (pessimistic) health

Returns: predicted health (int256).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
def add_collateral_health_preview(
_collateral: uint256,
_for: address,
_full: bool,
) -> int256:
"""
@notice Calculates health after calling add_collateral with the same args
@param _collateral Amount of collateral to add
@param _for The user to add collateral for
@param _full Whether it's a 'full' health or not
@return Signed health value
"""
return staticcall self._view.add_collateral_health_preview(_collateral, _for, _full)


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.add_collateral_health_preview(
/* _collateral: uint256 */ 0n,
/* _for: address */ "0x0000000000000000000000000000000000000000",
/* _full: bool */ false,
)

remove_collateral_health_preview

LendController.remove_collateral_health_preview(_collateral: uint256, _for: address, _full: bool) -> int256: view

Previews the health after removing collateral.

InputTypeDescription
_collateraluint256Collateral to remove
_foraddressBorrower address
_fullboolFull (pessimistic) health

Returns: predicted health (int256).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
def remove_collateral_health_preview(
_collateral: uint256,
_for: address,
_full: bool,
) -> int256:
"""
@notice Calculates health after calling remove_collateral with the same args
@param _collateral Amount of collateral to remove
@param _for Address to remove collateral from
@param _full Whether it's a 'full' health or not
@return Signed health value
"""
return staticcall self._view.remove_collateral_health_preview(
_collateral, _for, _full
)


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.remove_collateral_health_preview(
/* _collateral: uint256 */ 0n,
/* _for: address */ "0x0000000000000000000000000000000000000000",
/* _full: bool */ false,
)

repay_health_preview

LendController.repay_health_preview(_d_collateral: uint256, _d_debt: uint256, _for: address, _shrink: bool, _full: bool) -> int256: view

Previews the health after repaying debt.

InputTypeDescription
_d_collateraluint256Collateral amount removed through the callback
_d_debtuint256Debt to repay
_foraddressBorrower address
_shrinkboolWhether the repayment shrinks a soft-liquidated position
_fullboolFull (pessimistic) health

Returns: predicted health (int256).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
def repay_health_preview(
_d_collateral: uint256,
_d_debt: uint256,
_for: address,
_shrink: bool,
_full: bool,
) -> int256:
"""
@notice Calculates health after calling repay with the same params
@dev Works only for partial repay, reverts for full repay
@param _d_collateral Amount of collateral to remove (goes to callback)
@param _d_debt The amount of debt to repay (from wallet + callback).
Note: the borrowed amount coming from the callback should be included.
@param _for The user to repay the debt for
@param _shrink Whether shrink soft-liquidated part of the position or not
@param _full Whether it's a 'full' health or not
@return Signed health value
"""
return staticcall self._view.repay_health_preview(
_d_collateral, _d_debt, _for, _shrink, _full
)


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.repay_health_preview(
/* _d_collateral: uint256 */ 0n,
/* _d_debt: uint256 */ 0n,
/* _for: address */ "0x0000000000000000000000000000000000000000",
/* _shrink: bool */ false,
/* _full: bool */ false,
)

liquidate_health_preview

LendController.liquidate_health_preview(_user: address, _caller: address, _frac: uint256, _full: bool) -> int256: view

Previews what a user's health would be after liquidation.

InputTypeDescription
_useraddressBorrower address
_calleraddressAddress that would submit the liquidation
_fracuint256Fraction of the position to liquidate, where 10**18 is 100%
_fullboolFull (pessimistic) health

Returns: predicted health (int256).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
def liquidate_health_preview(
_user: address,
_caller: address,
_frac: uint256,
_full: bool,
) -> int256:
"""
@notice Calculates health after calling liquidate with the same args
@param _user User to liquidate
@param _caller Address from which liquidate tx is going to be sent.
Depending on this address liquidation_discount will be changed or not.
@param _frac Fraction to liquidate; 100% = 10**18
@param _full Whether it's a 'full' health or not
@return Signed health value
"""
return staticcall self._view.liquidate_health_preview(
_user, _caller, _frac, _full
)


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.liquidate_health_preview(
/* _user: address */ "0x0000000000000000000000000000000000000000",
/* _caller: address */ "0x0000000000000000000000000000000000000000",
/* _frac: uint256 */ 0n,
/* _full: bool */ false,
)

Lending-Specific

These functions are unique to LendController and not part of the shared controller.vy module.

available_balance

LendController.available_balance() -> uint256: view

Returns the amount of borrowed tokens currently available for new loans — i.e., tracked lender deposits minus amounts lent out. Direct token donations are not included in this accounting.

Returns: available balance (uint256).

<>Source code

Excerpt from lending/LendController.vy at 0d726b1:

@external
@view
@reentrant
def available_balance() -> uint256:
return self._available_balance


# https://github.com/vyperlang/vyper/issues/4721
Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.available_balance()

borrow_cap

LendController.borrow_cap() -> uint256: view

Returns the maximum total debt allowed for this market. It is initialized to 0, which prevents new borrowing; the LendConfigurator must raise it above 0 (for example, through a DAO action) before borrowing can be enabled.

Returns: borrow cap (uint256).

<>Source code

The implementation is in lending/LendController.vy at 0d726b1. This public ABI entry is compiler-generated or inherited; inspect the linked contract source and interfaces for its implementation.

Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, signer)
const tx = await contract.borrow_cap()
await tx.wait()

vault

LendController.vault() -> address: view

Returns the address of the associated ERC4626 vault.

Returns: vault address (address).

<>Source code

Excerpt from lending/LendController.vy at 0d726b1:

@external
@view
@reentrant
def vault() -> address:
"""
@notice Address of the vault
@return Address of the vault contract
"""
return VAULT.address


@deploy
def __init__(
_vault: IVault,
_amm: IAMM,
_borrowed_token: IERC20,
_collateral_token: IERC20,
_monetary_policy: IMonetaryPolicy,
_loan_discount: uint256,
_liquidation_discount: uint256,
_view_blueprint: address,
_configurator: core.IConfigurator,
):
"""
@notice Lend Controller constructor
@param _vault Address of the vault this controller belongs to
@param _amm AMM address (already deployed from blueprint)
@param _borrowed_token Token which is being borrowed
@param _collateral_token Token to use for collateral
@param _monetary_policy Address of monetary policy
@param _loan_discount Discount of the maximum loan size compare to get_x_down() value
@param _liquidation_discount Discount of the maximum loan size compare to
get_x_down() for "bad liquidation" purposes
@param _view_blueprint Address of the controller view blueprint
@param _configurator Address of the configurator contract
"""
VAULT = _vault

# … the remaining implementation is linked above.
Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.vault()

configure_lend

LendController.configure_lend(_borrow_cap: uint256, _admin_percentage: uint256)
Guarded Method

This function is only callable by the LendConfigurator contract. Governance normally invokes it through the configurator's administrative methods.

Configures the market's borrow cap and admin-fee share. Either parameter can be left unchanged by passing the protocol's SKIP_CONFIG_UINT256 sentinel.

InputTypeDescription
_borrow_capuint256New maximum total debt (0 disables new borrowing)
_admin_percentageuint256New admin-fee share, scaled so that 10**18 is 100%

Updates the supplied configuration values.

<>Source code

Excerpt from lending/LendController.vy at 0d726b1:

@external
def configure_lend(_borrow_cap: uint256, _admin_percentage: uint256):
assert msg.sender == core.CONFIGURATOR.address, "Only configurator"
if _borrow_cap != core.SKIP_CONFIG_UINT256:
self.borrow_cap = _borrow_cap
if _admin_percentage != core.SKIP_CONFIG_UINT256:
rate_mul: uint256 = staticcall core.AMM.get_rate_mul()
core._update_total_debt(0, rate_mul, False)
core.admin_percentage = _admin_percentage


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, signer)
const tx = await contract.configure_lend(
/* _borrow_cap: uint256 */ 0n,
/* _admin_percentage: uint256 */ 0n,
)
await tx.wait()

Calculations

max_borrowable

LendController.max_borrowable(_d_collateral: uint256, _N: uint256, _user: address = empty(address)) -> uint256: view

Returns the maximum amount that can be borrowed given a collateral amount and number of bands, respecting the borrow cap and available balance.

InputTypeDescription
_d_collateraluint256Collateral amount
_Nuint256Number of bands
_useraddressBorrower address (for existing loan context)

Returns: max borrowable amount (uint256).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
def max_borrowable(
_d_collateral: uint256, _N: uint256, _user: address = empty(address)
) -> uint256:
"""
@notice Calculation of maximum which can be borrowed.
Returns the maximum additional amount to be borrowed in case the user has a position.
@param _d_collateral Collateral amount against which to borrow
(additional amount if the user has a position)
@param _N Number of bands to have the deposit into (to be ignored if the user has a position)
@param _user User to calculate the value for
(can be zero address for create_loan if the user doesn't have extra_health)
@return Maximum (additional) amount of borrowed asset to borrow
"""
return staticcall self._view.max_borrowable(_d_collateral, _N, _user)


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.max_borrowable(
/* _d_collateral: uint256 */ 0n,
/* _N: uint256 */ 0n,
/* _user: address */ "0x0000000000000000000000000000000000000000",
)

min_collateral

LendController.min_collateral(_debt: uint256, _N: uint256, _user: address = empty(address)) -> uint256: view

Returns the minimum collateral required for a given debt amount and number of bands.

InputTypeDescription
_debtuint256Desired debt amount
_Nuint256Number of bands
_useraddressUser address, needed only when the user has nonzero extra health

Returns: minimum collateral required (uint256).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
def min_collateral(
_debt: uint256, _N: uint256, _user: address = empty(address)
) -> uint256:
"""
@notice Calculation of minimum collaterl amount required for given debt amount
@param _debt The amount of debt for which calculation should be done
@param _N number of bands to have the deposit into
@param _user User to calculate the value for (only necessary for nonzero extra_health)
@return Minimum amount of collateral asset to provide
"""
return staticcall self._view.min_collateral(_debt, _N, _user)


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.min_collateral(
/* _debt: uint256 */ 0n,
/* _N: uint256 */ 0n,
/* _user: address */ "0x0000000000000000000000000000000000000000",
)

calculate_debt_n1

LendController.calculate_debt_n1(_collateral: uint256, _debt: uint256, _N: uint256, _user: address = empty(address)) -> int256: view

Calculates the upper band number (n1) for a loan with the given parameters. Used to determine where collateral will be placed in the AMM.

InputTypeDescription
_collateraluint256Collateral amount
_debtuint256Debt amount
_Nuint256Number of bands
_useraddressUser address, needed only when the user has nonzero extra health

Returns: upper band number (int256).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
def calculate_debt_n1(
_collateral: uint256,
_debt: uint256,
_N: uint256,
_user: address = empty(address),
) -> int256:
"""
@notice Calculate the upper band number for the deposit to sit in to support
the given debt. Reverts if requested debt is too high.
@param _collateral Amount of collateral (at its native precision)
@param _debt Amount of requested debt
@param _N Number of bands to deposit into
@param _user User to calculate n1 for (only necessary for nonzero extra_health)
@return Upper band n1 (n1 <= n2) to deposit into. Signed integer
"""
assert _N > MIN_TICKS_UINT - 1 # dev: Need more ticks
assert _N < MAX_TICKS_UINT + 1 # dev: Need less ticks
return self._calculate_debt_n1(_collateral, _debt, _N, _user)


@internal
@view
def _calculate_debt_n1(
_collateral: uint256,
_debt: uint256,
_N: uint256,
_user: address,
) -> int256:
assert _debt > 0 # dev: No loan
n0: int256 = staticcall AMM.active_band()
p_base: uint256 = staticcall AMM.p_oracle_up(n0)

# x_effective = y / N * p_oracle_up(n1) * sqrt((A - 1) / A) * sum_{0..N-1}(((A-1) / A)**k)
# === d_y_effective * p_oracle_up(n1) * sum(...) === y_effective * p_oracle_up(n1)
# d_y_effective = y / N / sqrt(A / (A - 1))
y_effective: uint256 = self._get_y_effective(
# … the remaining implementation is linked above.
Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.calculate_debt_n1(
/* _collateral: uint256 */ 0n,
/* _debt: uint256 */ 0n,
/* _N: uint256 */ 0n,
/* _user: address */ "0x0000000000000000000000000000000000000000",
)

Configuration

configure

LendController.configure(_loan_discount: uint256, _liquidation_discount: uint256, _monetary_policy: address, _view_blueprint: address, _fee: uint256, _price_oracle: address, _liquidity_mining_callback: address)
Guarded Method

This function is only callable by the LendConfigurator contract.

Updates shared-controller settings. Governance normally invokes it through the LendConfigurator rather than calling the controller directly. Parameters that should remain unchanged use the protocol's SKIP_CONFIG_UINT256 or SKIP_CONFIG_ADDRESS sentinel, as applicable.

InputTypeDescription
_loan_discountuint256Loan discount for LTV calculations
_liquidation_discountuint256Discount used in liquidation calculations
_monetary_policyaddressMonetary policy contract
_view_blueprintaddressController-view blueprint
_feeuint256Base AMM fee
_price_oracleaddressPrice oracle contract
_liquidity_mining_callbackaddressLiquidity-mining callback contract

Updates the supplied configuration values.

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
def configure(
_loan_discount: uint256,
_liquidation_discount: uint256,
_monetary_policy: IMonetaryPolicy,
_view_blueprint: address,
_fee: uint256,
_price_oracle: IPriceOracle,
_liquidity_mining_callback: ILMCallback,
):
assert msg.sender == CONFIGURATOR.address # dev: Only configurator
self._configure(
_loan_discount,
_liquidation_discount,
_monetary_policy,
_view_blueprint,
_fee,
_price_oracle,
_liquidity_mining_callback,
)



@internal
@view
def _admin_fees() -> uint256:
return self._stored_admin_fees + self._preview_total_debt(staticcall AMM.get_rate_mul(), self._total_debt)[1]


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.configure(
/* _loan_discount: uint256 */ 0n,
/* _liquidation_discount: uint256 */ 0n,
/* _monetary_policy: address */ "0x0000000000000000000000000000000000000000",
/* _view_blueprint: address */ "0x0000000000000000000000000000000000000000",
/* _fee: uint256 */ 0n,
/* _price_oracle: address */ "0x0000000000000000000000000000000000000000",
/* _liquidity_mining_callback: address */ "0x0000000000000000000000000000000000000000",
)

Other Methods

amm

LendController.amm() -> address: view

Returns the AMM (LLAMMA) address for this market.

Returns: AMM address (address).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
@reentrant
def amm() -> IAMM:
"""
@notice Address of the AMM
@return Address of the AMM contract
"""
return AMM


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.amm()

monetary_policy

LendController.monetary_policy() -> address: view

Returns the current monetary policy address.

Returns: monetary policy address (address).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
@reentrant
def monetary_policy() -> IMonetaryPolicy:
"""
@notice Address of the monetary policy
@return Address of the monetary policy contract
"""
return self._monetary_policy


approval: public(HashMap[address, HashMap[address, bool]])
extra_health: public(reentrant(HashMap[address, uint256]))

loan: HashMap[address, IController.Loan]
liquidation_discounts: public(HashMap[address, uint256])
_total_debt: IController.Loan

# Enumerate existing loans
loans: public(address[2**64 - 1])
# Position of the loan in the list
loan_ix: public(HashMap[address, uint256])
# Number of nonzero loans
n_loans: public(uint256)


# cumulative amount of borrowed assets ever lent
lent: uint256
# cumulative amount of borrowed assets ever repaid
repaid: uint256
# cumulative amount of borrowed assets ever collected as admin fees
collected: uint256


# Admin fees yet to be collected. Goes to zero when collected.
_stored_admin_fees: uint256
admin_percentage: public(uint256)

# … the remaining implementation is linked above.
Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.monetary_policy()

liquidation_discount

LendController.liquidation_discount() -> uint256: view

Returns the current liquidation discount.

Returns: liquidation discount (uint256).

<>Source code

The implementation is in lending/LendController.vy at 0d726b1. This public ABI entry is compiler-generated or inherited; inspect the linked contract source and interfaces for its implementation.

Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, signer)
const tx = await contract.liquidation_discount()
await tx.wait()

loan_discount

LendController.loan_discount() -> uint256: view

Returns the current loan discount.

Returns: loan discount (uint256).

<>Source code

The implementation is in lending/LendController.vy at 0d726b1. This public ABI entry is compiler-generated or inherited; inspect the linked contract source and interfaces for its implementation.

Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, signer)
const tx = await contract.loan_discount()
await tx.wait()

admin_fees

LendController.admin_fees() -> uint256: view

Returns the amount of uncollected admin fees.

Returns: admin fees (uint256).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
@reentrant
def admin_fees() -> uint256:
"""
@notice Pending admin fees which can be claimed if the controller has enough balance
@return Amount of pending admin fees in borrowed token units
"""
return self._admin_fees()


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.admin_fees()

admin_percentage

LendController.admin_percentage() -> uint256: view

Returns the current admin fee percentage, scaled so that 10**18 is 100%.

Returns: admin percentage (uint256).

<>Source code

The implementation is in lending/LendController.vy at 0d726b1. This public ABI entry is compiler-generated or inherited; inspect the linked contract source and interfaces for its implementation.

Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, signer)
const tx = await contract.admin_percentage()
await tx.wait()

collect_fees

LendController.collect_fees()

Collects accumulated admin fees and sends them to the fee receiver set in the factory.

Emits: CollectFees event.

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
def collect_fees() -> uint256:
"""
@notice Collect the fees charged as interest.
@return Amount of fees collected and transferred to the fee receiver
"""
rate_mul: uint256 = staticcall AMM.get_rate_mul()
self._update_total_debt(0, rate_mul, False)

# self._stored_admin_fees == self.admin_fees() after _update_total_debt
pending_admin_fees: uint256 = self._stored_admin_fees
self.collected += pending_admin_fees
self._stored_admin_fees = 0

extcall VIRTUAL.on_borrowed_token_transfer_out(pending_admin_fees)
tkn.transfer(BORROWED_TOKEN, staticcall FACTORY.fee_receiver(), pending_admin_fees)

self._save_rate()
log IController.CollectFees(amount=pending_admin_fees)

return pending_admin_fees


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, signer)
const tx = await contract.collect_fees()
await tx.wait()

save_rate

LendController.save_rate()

Updates the stored interest rate from the monetary policy. This is called automatically during loan operations but can also be called externally to keep the rate fresh.

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
def save_rate():
"""
@notice Save current rate
"""
self._save_rate()


@internal
@view
def _debt(_user: address) -> (uint256, uint256):
"""
@notice Get the value of debt and rate_mul without changing the state
@param _user User address
@return (debt, rate_mul)
"""
rate_mul: uint256 = staticcall AMM.get_rate_mul()
loan: IController.Loan = self.loan[_user]
if loan.initial_debt == 0:
return (0, rate_mul)
else:
# Let user repay 1 smallest decimal more so that the system doesn't lose on precision
# Use ceil div
debt: uint256 = math._ceil_div(loan.initial_debt * rate_mul, loan.rate_mul)
return (debt, rate_mul)


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.save_rate()

version

LendController.version() -> String[10]: view

Returns the contract version string (e.g., "2.0.0-lend").

Returns: version string (String[10]).

<>Source code

Excerpt from lending/LendController.vy at 0d726b1:

@external
@view
@reentrant
def version() -> String[10]:
"""
@notice Version of this controller
@return Version string of the form "<core_version>-lend"
"""
return concat(core.version, "-lend")


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.version()

factory

LendController.factory() -> address: view

Returns the factory that deployed this controller.

Returns: factory address (address).

<>Source code

Excerpt from Controller.vy at 0d726b1:

@external
@view
@reentrant
def factory() -> IFactory:
"""
@notice Address of the factory
@return Address of the factory contract
"""
return FACTORY


Example
const contract = new Contract('0xc77d97cf01737eb7ace46cab7cd9f60ec51a40c0', LendControllerAbi, provider)
const result = await contract.factory()