Configurator
The Configurator is the permissioned administrative entry point for LlamaLend v2 markets. It changes parameters across a market's controller, AMM, and view contracts; lenders, borrowers, and liquidators do not interact with it in normal operation.
Each Configurator has a default_admin. The default administrator can assign a custom administrator for an individual controller. A market-setting call then accepts either the default administrator or that controller's custom administrator; changing the default administrator or custom assignments remains restricted to the default administrator.
Configurator.vyThe implementation is Configurator.vy at 0d726b1, written in Vyper 0.4.3.
The deployment registry lists the Configurator at 0x6065858d0eF0AA240DFdf6f1A0B2ae34B41f49bC on Ethereum and 0xd36c590531cAF5F620C57Faf5827Ce8E7f6E5Bec on Optimism. A read-only Ethereum RPC check confirmed deployed code and a nonzero default_admin.
Examples use Ethers v6 with an ABI generated from the pinned source. They are administrator transaction templates: replace every placeholder and execute them on a fork before any governance or multisig proposal.
import { Contract } from 'ethers'
const CONFIGURATOR = '0x6065858d0eF0AA240DFdf6f1A0B2ae34B41f49bC'
const configurator = new Contract(CONFIGURATOR, ConfiguratorAbi, signer)
Administration
set_custom_admin
Configurator.set_custom_admin(_controller: address, _admin: address)Sets the administrator for one controller. Passing the zero address removes that controller-specific assignment, so future market-setting calls fall back to default_admin.
| Input | Type | Description |
|---|---|---|
_controller | address | Controller whose authorization is being configured |
_admin | address | New per-controller administrator, or zero address to reset |
Emits: SetCustomAdmin.
<>Source code▼
Excerpt from Configurator.vy at 0d726b1:
@external
def set_custom_admin(_controller: IController, _admin: address):
self._check_admin()
self.admins[_controller] = _admin
log IConfigurator.SetCustomAdmin(controller=_controller.address, admin=_admin)
▶Example▼
const tx = await configurator.set_custom_admin(controller, marketAdmin)
await tx.wait()
set_owner
Configurator.set_owner(_new_default_admin: address)Changes the default administrator. This does not overwrite existing per-controller custom administrator assignments.
| Input | Type | Description |
|---|---|---|
_new_default_admin | address | New default administrator |
Emits: SetDefaultAdmin.
<>Source code▼
@external
def set_owner(_new_default_admin: address):
self._check_admin()
self.default_admin = _new_default_admin
log IConfigurator.SetDefaultAdmin(new_default_admin=_new_default_admin)
▶Example▼
const tx = await configurator.set_owner(newDefaultAdmin)
await tx.wait()
default_admin and admins
Configurator.default_admin() -> address: viewReturns the Configurator's default administrator. Configurator.admins(_controller) -> address: view returns the custom administrator stored for a controller, or the zero address when none is set.
<>Source code▼
default_admin: public(address)
admins: public(HashMap[IController, address])
▶Example▼
const [defaultAdmin, customAdmin] = await Promise.all([
configurator.default_admin(),
configurator.admins(controller),
])
Controller configuration
The following calls require the default administrator or the controller's custom administrator.
set_borrowing_discounts
Configurator.set_borrowing_discounts(_controller: address, _loan_discount: uint256, _liquidation_discount: uint256)Sets the loan and liquidation discounts used by the controller. Both values use 10**18 = 100%; the liquidation discount must be positive and strictly less than the loan discount, which must be less than 100%.
<>Source code▼
@external
def set_borrowing_discounts(
_controller: IController, _loan_discount: uint256, _liquidation_discount: uint256
):
self._check_authorized(_controller)
assert _liquidation_discount > 0
assert _loan_discount < WAD and _loan_discount > _liquidation_discount
extcall _controller.configure(
_loan_discount, _liquidation_discount, IMonetaryPolicy(SKIP_CONFIG_ADDRESS),
SKIP_CONFIG_ADDRESS, SKIP_CONFIG_UINT256, IPriceOracle(SKIP_CONFIG_ADDRESS),
ILMCallback(SKIP_CONFIG_ADDRESS),
)
▶Example▼
const tx = await configurator.set_borrowing_discounts(
controller,
loanDiscount,
liquidationDiscount,
)
await tx.wait()
set_monetary_policy
Configurator.set_monetary_policy(_controller: address, _monetary_policy: address)Updates a controller's monetary-policy contract, then saves the controller rate.
<>Source code▼
@external
def set_monetary_policy(_controller: IController, _monetary_policy: IMonetaryPolicy):
self._check_authorized(_controller)
assert _monetary_policy.address != SKIP_CONFIG_ADDRESS
extcall _controller.configure(
SKIP_CONFIG_UINT256, SKIP_CONFIG_UINT256, _monetary_policy,
SKIP_CONFIG_ADDRESS, SKIP_CONFIG_UINT256, IPriceOracle(SKIP_CONFIG_ADDRESS),
ILMCallback(SKIP_CONFIG_ADDRESS),
)
extcall _controller.save_rate()
▶Example▼
const tx = await configurator.set_monetary_policy(controller, monetaryPolicy)
await tx.wait()
set_view
Configurator.set_view(_controller: address, _view_blueprint: address)Deploys and configures a new controller-view instance from the supplied blueprint. The blueprint must be nonzero and cannot be the configuration sentinel address.
<>Source code▼
@external
def set_view(_controller: IController, _view_blueprint: address):
self._check_authorized(_controller)
assert _view_blueprint != empty(address)
extcall _controller.configure(
SKIP_CONFIG_UINT256, SKIP_CONFIG_UINT256, IMonetaryPolicy(SKIP_CONFIG_ADDRESS),
_view_blueprint, SKIP_CONFIG_UINT256, IPriceOracle(SKIP_CONFIG_ADDRESS),
ILMCallback(SKIP_CONFIG_ADDRESS),
)
▶Example▼
const tx = await configurator.set_view(controller, controllerViewBlueprint)
await tx.wait()
Lending-market configuration
set_borrow_cap
Configurator.set_borrow_cap(_controller: address, _borrow_cap: uint256)Sets a lending controller's borrow cap in units of its borrowed token. A new lending market starts with a borrow cap of zero, so borrowing remains disabled until an authorized administrator raises it.
<>Source code▼
@external
def set_borrow_cap(_controller: ILendController, _borrow_cap: uint256):
self._check_authorized(IController(_controller.address))
assert _borrow_cap != SKIP_CONFIG_UINT256
extcall _controller.configure_lend(_borrow_cap, SKIP_CONFIG_UINT256)
▶Example▼
const tx = await configurator.set_borrow_cap(lendController, borrowCap)
await tx.wait()
set_admin_percentage
Configurator.set_admin_percentage(_controller: address, _admin_percentage: uint256)Sets the share of interest assigned to the administrator. _admin_percentage uses 10**18 = 100% and cannot exceed 100%.
<>Source code▼
@external
def set_admin_percentage(_controller: ILendController, _admin_percentage: uint256):
self._check_authorized(IController(_controller.address))
assert _admin_percentage <= WAD
extcall _controller.configure_lend(SKIP_CONFIG_UINT256, _admin_percentage)
▶Example▼
const tx = await configurator.set_admin_percentage(lendController, adminPercentage)
await tx.wait()
AMM configuration
set_price_oracle
Configurator.set_price_oracle(_controller: address, _price_oracle: address, _max_deviation: uint256)Updates the AMM price oracle after validating it. _max_deviation is WAD-scaled and may be at most 50%; max_value(uint256) explicitly skips the deviation check. The Configurator calls price_w() and price() on the new oracle before applying the change.
<>Source code▼
@external
def set_price_oracle(
_controller: IController, _price_oracle: IPriceOracle, _max_deviation: uint256
):
self._check_authorized(_controller)
assert _max_deviation <= MAX_ORACLE_PRICE_DEVIATION or _max_deviation == max_value(uint256)
extcall _price_oracle.price_w()
new_price: uint256 = staticcall _price_oracle.price()
# The remaining implementation compares the existing oracle before configuration.
▶Example▼
const tx = await configurator.set_price_oracle(controller, priceOracle, maxDeviation)
await tx.wait()
set_callback
Configurator.set_callback(_controller: address, _cb: address)Sets the AMM liquidity-mining callback. Pass the zero address to remove the callback; the configuration sentinel address is rejected.
<>Source code▼
@external
def set_callback(_controller: IController, _cb: ILMCallback):
self._check_authorized(_controller)
assert _cb.address != SKIP_CONFIG_ADDRESS
extcall _controller.configure(
SKIP_CONFIG_UINT256, SKIP_CONFIG_UINT256, IMonetaryPolicy(SKIP_CONFIG_ADDRESS),
SKIP_CONFIG_ADDRESS, SKIP_CONFIG_UINT256, IPriceOracle(SKIP_CONFIG_ADDRESS), _cb,
)
▶Example▼
const tx = await configurator.set_callback(controller, liquidityMiningCallback)
await tx.wait()
set_amm_fee
Configurator.set_amm_fee(_controller: address, _fee: uint256)Sets the AMM fee through the controller configuration path. The fee is WAD-scaled and the controller/AMM configuration enforces its permitted range.
<>Source code▼
@external
def set_amm_fee(_controller: IController, _fee: uint256):
self._check_authorized(_controller)
assert _fee != SKIP_CONFIG_UINT256
extcall _controller.configure(
SKIP_CONFIG_UINT256, SKIP_CONFIG_UINT256, IMonetaryPolicy(SKIP_CONFIG_ADDRESS),
SKIP_CONFIG_ADDRESS, _fee, IPriceOracle(SKIP_CONFIG_ADDRESS), ILMCallback(SKIP_CONFIG_ADDRESS),
)
▶Example▼
const tx = await configurator.set_amm_fee(controller, ammFee)
await tx.wait()