LendFactory
The LendFactory is the entry point for creating new one-way lending markets. It deploys a triplet of contracts — Vault, LendController, and AMM — from blueprint implementations stored in an internal registry. Each market is fully isolated.
The factory uses Snekmate's ownable module for access control and pausable for emergency pause functionality.
LendFactory.vyExamples 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 factory; execute a write only on a fork first.
Market Creation
create
LendFactory.create(_borrowed_token: address, _collateral_token: address, _A: uint256, _fee: uint256, _loan_discount: uint256, _liquidation_discount: uint256, _price_oracle: address, _monetary_policy: address, _supply_limit: uint256) -> address[3]Deploys a new lending market by creating a Vault, LendController, and AMM from their respective blueprints. Validates all parameters, initializes the triplet, and registers the market.
| Input | Type | Description |
|---|---|---|
_borrowed_token | address | Token that lenders deposit and borrowers receive |
_collateral_token | address | Token used as collateral by borrowers |
_A | uint256 | Amplification coefficient — determines band width (~1/A) |
_fee | uint256 | Swap fee for the AMM |
_loan_discount | uint256 | Maximum discount for LTV calculation |
_liquidation_discount | uint256 | Discount at which liquidation can occur |
_price_oracle | address | Initialized price oracle contract |
_monetary_policy | address | Initialized monetary policy contract |
_supply_limit | uint256 | Maximum supply cap for the vault |
Returns: array of three addresses — [vault, controller, amm] (address[3]).
Emits: NewVault event.
<>Source code▼
Excerpt from lending/LendFactory.vy at 0d726b1:
@external
def create(
_borrowed_token: IERC20,
_collateral_token: IERC20,
_A: uint256,
_fee: uint256,
_loan_discount: uint256,
_liquidation_discount: uint256,
_price_oracle: IPriceOracle,
_monetary_policy: IMonetaryPolicy,
_supply_limit: uint256,
) -> address[3]:
"""
@notice Creation of the vault using user-supplied price oracle contract
@param _borrowed_token Token which is being borrowed
@param _collateral_token Token used for collateral
@param _A Amplification coefficient: band size is ~1//A
@param _fee Fee for swaps in AMM (for ETH markets found to be 0.6%).
Bounds are enforced by the AMM: MIN_FEE <= _fee <= min(WAD * MIN_TICKS / _A, 10%)
@param _loan_discount Maximum discount. LTV = sqrt(((A - 1) // A) ** 4) - loan_discount
@param _liquidation_discount Liquidation discount. LT = sqrt(((A - 1) // A) ** 4) - liquidation_discount
@param _price_oracle Custom price oracle contract
@param _monetary_policy Monetary policy contract to set the borrow rate
@param _supply_limit Supply cap
@return Addresses of the deployed [vault, AMM, controller] contracts
"""
pausable._require_not_paused()
assert _borrowed_token != _collateral_token, "Same token"
assert _A >= MIN_A and _A <= MAX_A, "Wrong A"
assert _liquidation_discount > 0, "liquidation discount = 0"
assert _loan_discount < WAD, "loan discount >= 100%"
assert _loan_discount > _liquidation_discount, "loan discount <= liquidation discount"
A_ratio: uint256 = 10**18 * _A // (_A - 1)
# Validate price oracle
p: uint256 = (staticcall _price_oracle.price())
assert p > 0 # dev: price oracle returned zero
# … the remaining implementation is linked above.
▶Example▼
const contract = new Contract('0x8f6B56EC5ddF1F2691a1059f1D3cd97Ac9EaB0bd', LendFactoryAbi, signer)
const tx = await contract.create(
/* _borrowed_token: address */ "0x0000000000000000000000000000000000000000",
/* _collateral_token: address */ "0x0000000000000000000000000000000000000000",
/* _A: uint256 */ 0n,
/* _fee: uint256 */ 0n,
/* _loan_discount: uint256 */ 0n,
/* _liquidation_discount: uint256 */ 0n,
/* _price_oracle: address */ "0x0000000000000000000000000000000000000000",
/* _monetary_policy: address */ "0x0000000000000000000000000000000000000000",
/* _supply_limit: uint256 */ 0n,
)
await tx.wait()
Market Registry
market_count
LendFactory.market_count() -> uint256: viewReturns the total number of lending markets created by the factory.
Returns: number of markets (uint256).
<>Source code▼
Excerpt from lending/LendFactory.vy at 0d726b1:
@external
@view
def market_count() -> uint256:
return len(self._vaults)
▶Example▼
const contract = new Contract('0x8f6B56EC5ddF1F2691a1059f1D3cd97Ac9EaB0bd', LendFactoryAbi, provider)
const result = await contract.market_count()
markets
LendFactory.markets(_n: uint256) -> Market: viewReturns the market info struct for a given market index.
| Input | Type | Description |
|---|---|---|
_n | uint256 | Market index (0-based) |
Returns: market struct containing the vault, controller, AMM, collateral-token, borrowed-token, price-oracle, and monetary-policy addresses (Market).
<>Source code▼
Excerpt from lending/LendFactory.vy at 0d726b1:
@external
@view
@reentrant
def markets(_n: uint256) -> ILendFactory.Market:
"""
@notice Get market data for market at index `_n`
@param _n Index of the market
@return Market struct containing vault, controller, amm, tokens, price oracle and monetary policy addresses
"""
vault: IVault = self._vaults[_n]
controller: IController = staticcall vault.controller()
amm: IAMM = staticcall vault.amm()
return ILendFactory.Market(
vault=vault,
controller=controller,
amm=amm,
collateral_token=staticcall vault.collateral_token(),
borrowed_token=staticcall vault.borrowed_token(),
price_oracle=staticcall amm.price_oracle_contract(),
monetary_policy=staticcall controller.monetary_policy(),
)
▶Example▼
const contract = new Contract('0x8f6B56EC5ddF1F2691a1059f1D3cd97Ac9EaB0bd', LendFactoryAbi, provider)
const result = await contract.markets(
/* _n: uint256 */ 0n,
)
coins
LendFactory.coins(_vault_id: uint256) -> IERC20[2]: viewReturns the borrowed and collateral token addresses for a given vault.
| Input | Type | Description |
|---|---|---|
_vault_id | uint256 | Vault index |
Returns: array of [borrowed_token, collateral_token] (IERC20[2]).
<>Source code▼
Excerpt from lending/LendFactory.vy at 0d726b1:
@external
@view
@reentrant
def coins(_vault_id: uint256) -> IERC20[2]:
vault: IVault = self._vaults[_vault_id]
return [staticcall vault.borrowed_token(), staticcall vault.collateral_token()]
▶Example▼
const contract = new Contract('0x8f6B56EC5ddF1F2691a1059f1D3cd97Ac9EaB0bd', LendFactoryAbi, provider)
const result = await contract.coins(
/* _vault_id: uint256 */ 0n,
)
vaults_index
LendFactory.vaults_index(_vault: IVault) -> uint256: viewReturns the index of a vault in the factory registry. Reverts if the vault is not registered.
| Input | Type | Description |
|---|---|---|
_vault | IVault | Vault address to look up |
Returns: vault index (uint256).
<>Source code▼
Excerpt from lending/LendFactory.vy at 0d726b1:
@external
@view
@reentrant
def vaults_index(_vault: IVault) -> uint256:
"""
@notice Get the index of a vault in the markets array
@param _vault Address of the vault
@return Index of the vault
"""
return self._vaults_index[_vault] - 2**128
▶Example▼
const contract = new Contract('0x8f6B56EC5ddF1F2691a1059f1D3cd97Ac9EaB0bd', LendFactoryAbi, provider)
const result = await contract.vaults_index(
/* _vault: IVault */ "0x0000000000000000000000000000000000000000",
)
Blueprints
amm_blueprint
LendFactory.amm_blueprint() -> address: viewReturns the current AMM blueprint address used for deploying new markets.
Returns: blueprint address (address).
<>Source code▼
Excerpt from lending/LendFactory.vy at 0d726b1:
@external
@view
def amm_blueprint() -> address:
"""
@notice Get the address of the AMM blueprint
@return Address of the AMM blueprint
"""
return blueprint_registry.get(AMM_BLUEPRINT_ID)
▶Example▼
const contract = new Contract('0x8f6B56EC5ddF1F2691a1059f1D3cd97Ac9EaB0bd', LendFactoryAbi, provider)
const result = await contract.amm_blueprint()
controller_blueprint
LendFactory.controller_blueprint() -> address: viewReturns the current controller blueprint address.
Returns: blueprint address (address).
<>Source code▼
Excerpt from lending/LendFactory.vy at 0d726b1:
@external
@view
def controller_blueprint() -> address:
"""
@notice Get the address of the controller blueprint
@return Address of the controller blueprint
"""
return blueprint_registry.get(CONTROLLER_BLUEPRINT_ID)
▶Example▼
const contract = new Contract('0x8f6B56EC5ddF1F2691a1059f1D3cd97Ac9EaB0bd', LendFactoryAbi, provider)
const result = await contract.controller_blueprint()
vault_blueprint
LendFactory.vault_blueprint() -> address: viewReturns the current vault blueprint address.
Returns: blueprint address (address).
<>Source code▼
Excerpt from lending/LendFactory.vy at 0d726b1:
@external
@view
def vault_blueprint() -> address:
"""
@notice Get the address of the vault blueprint
@return Address of the vault blueprint
"""
return blueprint_registry.get(VAULT_BLUEPRINT_ID)
▶Example▼
const contract = new Contract('0x8f6B56EC5ddF1F2691a1059f1D3cd97Ac9EaB0bd', LendFactoryAbi, provider)
const result = await contract.vault_blueprint()
controller_view_blueprint
LendFactory.controller_view_blueprint() -> address: viewReturns the current controller view blueprint address.
Returns: blueprint address (address).
<>Source code▼
Excerpt from lending/LendFactory.vy at 0d726b1:
@external
@view
def controller_view_blueprint() -> address:
"""
@notice Get the address of the controller view blueprint
@return Address of the controller view blueprint
"""
return blueprint_registry.get(CONTROLLER_VIEW_BLUEPRINT_ID)
▶Example▼
const contract = new Contract('0x8f6B56EC5ddF1F2691a1059f1D3cd97Ac9EaB0bd', LendFactoryAbi, provider)
const result = await contract.controller_view_blueprint()
Fee Receivers
fee_receiver
LendFactory.fee_receiver(_controller: address) -> address: viewReturns the fee receiver for a given controller. If a custom fee receiver is set, it returns that; otherwise, it returns the default fee receiver.
| Input | Type | Description |
|---|---|---|
_controller | address | Controller address |
Returns: fee receiver address (address).
<>Source code▼
Excerpt from lending/LendFactory.vy at 0d726b1:
@external
@view
def fee_receiver(_controller: address = msg.sender) -> address:
"""
@notice Get fee receiver who earns interest from admin fees
@dev This function is called by controllers without specifying the
first argument to get their fee receiver.
@param _controller Address of the controller
@return Address of the fee receiver
"""
custom_fee_receiver: address = self.fee_receivers[_controller]
return custom_fee_receiver if custom_fee_receiver != empty(address) else self.default_fee_receiver
▶Example▼
const contract = new Contract('0x8f6B56EC5ddF1F2691a1059f1D3cd97Ac9EaB0bd', LendFactoryAbi, provider)
const result = await contract.fee_receiver(
/* _controller: address */ "0x0000000000000000000000000000000000000000",
)
set_default_fee_receiver
LendFactory.set_default_fee_receiver(_fee_receiver: address)This function is only callable by the owner of the contract.
Sets the default fee receiver for all markets that don't have a custom one.
| Input | Type | Description |
|---|---|---|
_fee_receiver | address | New default fee receiver address |
Emits: SetFeeReceiver event.
<>Source code▼
Excerpt from lending/LendFactory.vy at 0d726b1:
@external
@reentrant
def set_default_fee_receiver(_fee_receiver: address):
"""
@notice Set default fee receiver who earns admin fees on
all controllers without a custom fee receiver
@param _fee_receiver Address of the receiver
"""
ownable._check_owner()
self._set_default_fee_receiver(_fee_receiver)
▶Example▼
const contract = new Contract('0x8f6B56EC5ddF1F2691a1059f1D3cd97Ac9EaB0bd', LendFactoryAbi, signer)
const tx = await contract.set_default_fee_receiver(
/* _fee_receiver: address */ "0x0000000000000000000000000000000000000000",
)
await tx.wait()
set_custom_fee_receiver
LendFactory.set_custom_fee_receiver(_controller: address, _fee_receiver: address)This function is only callable by the owner of the contract.
Sets a custom fee receiver for a specific market's controller.
| Input | Type | Description |
|---|---|---|
_controller | address | Controller address |
_fee_receiver | address | Custom fee receiver address |
Emits: CustomSetFeeReceiver event.
<>Source code▼
Excerpt from lending/LendFactory.vy at 0d726b1:
@external
@reentrant
def set_custom_fee_receiver(_controller: address, _fee_receiver: address):
"""
@notice Set fee receiver who earns admin fees for a specific controller
@dev Setting to zero address resets to default fee receiver
@param _controller Address of the controller
@param _fee_receiver Address of the receiver
"""
ownable._check_owner()
contract_info: ILendFactory.ContractInfo = self.check_contract[_controller]
assert contract_info.contract_type == ILendFactory.ContractType.CONTROLLER, "not a controller"
self.fee_receivers[_controller] = _fee_receiver
log ILendFactory.SetCustomFeeReceiver(controller=_controller, fee_receiver=_fee_receiver)
@internal
def _set_default_fee_receiver(_fee_receiver: address):
assert _fee_receiver != empty(address), "invalid receiver"
self.default_fee_receiver = _fee_receiver
log ILendFactory.SetFeeReceiver(fee_receiver=_fee_receiver)
▶Example▼
const contract = new Contract('0x8f6B56EC5ddF1F2691a1059f1D3cd97Ac9EaB0bd', LendFactoryAbi, signer)
const tx = await contract.set_custom_fee_receiver(
/* _controller: address */ "0x0000000000000000000000000000000000000000",
/* _fee_receiver: address */ "0x0000000000000000000000000000000000000000",
)
await tx.wait()
Contract Ownership
admin
LendFactory.admin() -> address: viewReturns the current owner of the factory contract.
Returns: owner address (address).
<>Source code▼
Excerpt from lending/LendFactory.vy at 0d726b1:
@external
@view
@reentrant
def admin() -> address:
"""
@notice Get the admin of the factory
@dev Called `admin` for backwards compatibility
@return Address of the factory admin
"""
return ownable.owner
▶Example▼
const contract = new Contract('0x8f6B56EC5ddF1F2691a1059f1D3cd97Ac9EaB0bd', LendFactoryAbi, provider)
const result = await contract.admin()
pause
LendFactory.pause()This function is only callable by the owner of the contract.
Pauses the factory, preventing new market creation.
<>Source code▼
Excerpt from lending/LendFactory.vy at 0d726b1:
@external
def pause():
"""
@notice Pause new market creation
"""
ownable._check_owner()
pausable._pause()
▶Example▼
const contract = new Contract('0x8f6B56EC5ddF1F2691a1059f1D3cd97Ac9EaB0bd', LendFactoryAbi, signer)
const tx = await contract.pause()
await tx.wait()
unpause
LendFactory.unpause()This function is only callable by the owner of the contract.
Unpauses the factory, re-enabling market creation.
<>Source code▼
Excerpt from lending/LendFactory.vy at 0d726b1:
@external
def unpause():
"""
@notice Unpause the factory to allow new market creation
"""
ownable._check_owner()
pausable._unpause()
▶Example▼
const contract = new Contract('0x8f6B56EC5ddF1F2691a1059f1D3cd97Ac9EaB0bd', LendFactoryAbi, signer)
const tx = await contract.unpause()
await tx.wait()
Other Methods
version
LendFactory.version() -> String[5]: viewReturns the contract version string.
Returns: version (String[5]).
<>Source code▼
Excerpt from lending/LendFactory.vy at 0d726b1:
@external
@view
def version() -> String[5]:
return c.__version__
▶Example▼
const contract = new Contract('0x8f6B56EC5ddF1F2691a1059f1D3cd97Ac9EaB0bd', LendFactoryAbi, provider)
const result = await contract.version()