# LombardFi in a Nutshell

Fixed Terms. All the Coins.

LombardFi is a permissionless, reputation-based liquidity protocol facilitating fixed-term borrowing of all the assets on the Ethereum network. The main actors are:

Borrowers, who can request any ERC20 token, pledge a collateral of size and type they feel comfortable with, select maturity of the debt facility, and offer a fixed rate in return. All done in a transparent and isolated manner, ultimately using one of protocol’s user-friendly interfaces. Lenders can assess the creditworthiness of the borrowers and enter the pool that offers the yield, collateralization, and duration that satisfy their risk tolerance.

LombardFi aims to bridge the gap between institutional investors, deploying sophisticated trading infrastructure and strategies on one hand, and the passive crypto investors, on another.

Ultimately, the lenders will be able to capture the rates available on the private crypto lending markets and centralized trading venues without the need to constantly monitor and rebalance their positions. Borrowers, on another hand, will be able to secure inventory in a predictable and capital-efficient manner.


# Filing the Gap

Product Market Fit

Decentralized finance (DeFi) creates an environment for material interest rate discrepancies across different protocols and networks. For extended periods of time, a given asset could be borrowed at substantially lower rates than those offered to depositors by other projects. These arbitrage opportunities are caused by short-term supply/demand imbalances, economic incentives (liquidity mining programs), and/or different levels of technical/operational risks associated with the products.

These abnormalities gave rise to [yEarn Finance](https://yearn.finance/vaults) and a series of “leveraged yield farming protocols”, deploying borrow/lend marketplaces (e.g. [Alpha Homora](https://homora-v2.alphaventuredao.io/) and Alpaca Finance), whose value proposition is to capture the best rates available in a systematic manner.

As the DeFi ecosystem matures and the protocols with a competitive advantage expand their market share – namely by developing economies of scale and nurturing loyal communities – the rates across the board are steadily converging, thus pushing investors into more complex strategies in their pursuit for yield.

Still, there is a mismatch between the yield generation opportunities available across centralized trading venues and their DeFi counterparties. This divergence stems from, among other things, differences in the use cases, duration, and structure of the respective products:

* The **funding rates** on the derivatives markets are quite volatile and move from positive to negative territories depending on the risk appetite of the market participants.&#x20;
* **CeFi lending platforms** offer stable rates for a predefined set of assets but generally lack collateralization and transparency.&#x20;
* **Market makers** are looking for inventory to perform their day-to-day business operations. Typically, they need a broad range of inventory, try to avoid price exposure, and are sensitive about the capital efficiency (levels of collateralization) of the credit lines secured. Institutional investors source various tokens to execute their directional views on the market.

As a result, the market players described above usually source liquidity twofold – either by tapping existing DeFi protocols or by borrowing in a peer-to-peer fashion via centralized over-the-counter (OTC) desks, acting as intermediaries between borrowers and lenders.

Touching on the centralized options, the OTC desks have a range of weak points:

* **Come with counterparty risk and lack of transparency**: Exposure to black box companies, typically running a proprietary trading desk on the side. The result is a cross-industry leverage and multiple rehypothecations of the same assets. It takes one rogue participant to pull back the industry as a whole (e.g [the Three Arrows Capital fiasco](https://decrypt.co/105416/bankrupt-three-arrows-capital-owes-3-5b-to-creditors-including-2-3b-to-genesis) is not possible on the chain).
* **Have limited availability**: Servicing high net worth investors and institutions only, following a lengthy onboarding process. The typical DeFi user with $10,000 in total assets under management is generally not welcomed by the large OTC desks.
* **Are time-inefficient and quoting based on existing relationships**: The negotiations involving rate and collateralization happen in a chaotic manner over private Telegram groups. The quotes given are typically influenced by historical business relationships and opportunity costs.
* **Involve margin calls and liquidation risks**: The market is not regulated. Each deal comes with lengthy paperwork that is hardly useful when it comes to legal actions during court hearings over disputes.


# DeFi Lending Landscape

There are two predominant lending-borrowing primitives on the non-custodial scene - anonymous overcollateralized platforms like [Aave](https://aave.com/) and [Compound](https://compound.finance/), and reputation-based uncollateralized platforms like [Maple](https://maple.finance/) and [TrueFi](https://truefi.io/).

The overcollateralized model comes with a number of shortfalls:

* **Lack of brand equity**: Borrowers are indifferent from one another, prohibiting large institutions from leveraging their brands and balance sheets for better credit facility terms.&#x20;
* **Capital inefficiency**: Combining overcollateralization with risk of liquidation substantially increases market participants’ effective borrowing rates due to the cost of collateral and buffer allocated to the borrowing facility.&#x20;
* **Interest rate spikes**: The interest rates on these platforms are usually a function of the protocol’s utilization. The utilization, however, is outside of borrowers’ control, implying a further possibility of forced repayment.&#x20;
* **Proneness to oracle attacks**: Most DeFi lending protocols are cross-collateral. It takes one faulty asset to drain the whole system down (for reference, check [Mango Markets](https://rekt.news/mango-markets-rekt/) and [CreamFinance](https://rekt.news/cream-rekt-2/)).&#x20;
* **A limited number of assets available for borrowing/lending**: Partially due to the oracle and cross-collateral problem discussed above, DeFi protocols are reluctant to onboard new or small-to-mid-cap assets as borrowing/collateral options.

The reputation-based uncollateralized platforms also have limitations:&#x20;

* **No collateral**: With few exceptions, the platforms are offering institutional borrowing facilities without requiring any collateral being pledged, resulting in 100% downside for the depositors.&#x20;
* **Limited inventory available**: Most reputation-based platforms are only offering loans in stablecoins. At the same time, the highest rates achievable for market-neutral strategies are typically on altcoins (due to limited supply and structural inefficiencies).&#x20;
* **Risk consolidation and discretionary decisions**: As a rule of thumb, the platforms are bundling several borrowers into a single pool. Regardless of the balance sheet strength or strategies deployed, the participants are offered the same terms. Furthermore, the decision of who is allowed to participate in the protocol/pools is outside of lenders’ discretion.

Last but not least, the architecture of the dominant primitives is timeless, meaning depositors and borrowers can provide, pull and repay the liquidity whenever they want. As a result, the protocols are severely underutilized at most of the time, making them even more capital inefficient.

And here comes LombardFi:

<figure><img src="/files/ZCuuu1XLmpVNY5Si5iWe" alt=""><figcaption></figcaption></figure>

We are a permissionless public good, allowing borrowers of any kind to request a loan by opening an isolated pool. The terms are fixed, but parameters are flexible:

* Cautious about capital efficiency? Ask for an undercollateralized loan, backed by inventory that is hard to deploy.&#x20;
* Sensitive on rates? Offer overcollateralization with blue chip assets.&#x20;
* Having clarity over the duration of the opportunity presented? Set the maturity matching the strategy time horizon and lock the rates today.

Lenders can assess the opportunities available on LombardFi and select the one suiting their risk appetite. LombardFi is also integrated with real-time credit scoring agent [Credora](https://credora.io/) and real-time auditor [Armanino](https://www.armaninollp.com/) to help lenders with their decisions.


# LombardFi: A Reputation-based Liquidity Protocol

Fixed terms. All the coins.

LombardFi is a permissionless public good under the [Apache-2.0 licensing framework](https://www.apache.org/licenses/LICENSE-2.0). It allows [borrowers](/protocol-design/borrowers) of any kind to request a loan by opening an isolated pool with predefined parameters ([Term Sheet](/protocol-design/term-sheet)).

It aims to be an on-chain over-the-counter (OTC) service that provides a bridge between large institutional players and the general investment public and is governed by the laws of blockchain smart contracts (available in our public repository).

Its genesis product is a lending desk, where participants can enter into peer-to-peer lending/borrowing contracts with predefined terms, including assets borrowed, collateral pledged, loan duration, and rates paid for the debt facility. Ultimately, the product gives DeFi users exposure to the rates available on centralized trading platforms and private capital markets, where well-known borrowers (Nexo, GSR, Wintermute) can leverage their brand and balance sheet positions to get better terms for their debt facilities.

LombardFi has two types of users: [*Borrowers*](/protocol-design/borrowers) and [*Lenders*](/protocol-design/lenders).


# Borrowers

The targeted audience includes institutional borrowers and companies looking for a specific inventory for a predefined period of time (fixed-term maturity). When posting their request, they open a pool with committed fixed rate due, a collateral type (one or a mixture of the tokens available on the Ethereum networks), and collateralization ratio (loan-to-value from 0 to infinity).

The pools are smart contracts which aim to:

* Custody the collateral pledged by the *Borrower,*
* Gather the *Lenders*’ funds, and
* Govern the borrowing, [repayment](/protocol-design/repayments-and-withdrawals), and [default](/protocol-design/defaults) processes.

The isolated pool architecture allows lenders to do proper due diligence on the counterparty they are dealing with and put their capital into the pool, offering what they consider to represent the best risk/return profile.

The Protocol avoids discretionary decisions by electing pool delegators to onboard the borrowing institutions and assess the strength of their businesses or balance sheets. Instead, to help lenders assess the risk, LombardFi is integrated with real-time credit scoring agent [Credora](https://credora.io/) and real-time auditor [Armanino](https://www.armaninollp.com/).

By deploying this logic, LombardFi avoids the convergence towards a mix of the riskiest borrowers as the Protocol matures/expands. The design delivers different terms that could be applied to the counterparties depending on the riskiness of their business model and their balance sheet price exposure. This way, the interest rates are not syndicated (all borrowers get the same terms). There are also no discretionary decisions made by a risk committee to determine who the borrowers within a certain pool should be. Additionally, by not sticking to the timeless standard of the alternative solutions, LombardFi is able to deploy 100% of its capital for the duration of the agreement.

The collateralization of the loans is fully at the borrowers’ discretion – could be un-, under-, or over-collateralized, allowing the large borrowers to leverage their brand equity to access liquidity in a capital efficient manner. Alternatively, over-collateralized deals could be offered to achieve better rates or offering yield-generating/idle assets as collateral.

To improve borrower liquidity and predict the amount of collateral required during the loan term, the protocol does not include liquidations or margin calls by design. LombardFi believes the targeted borrowers will avoid reputational risk at any cost. At the same time, the material improvement in their inventory management should result in a substantial competitive advantage for the Protocol when compared to over-collateralized solutions. Last but not least, the improved capital efficiency should translate into higher returns on capital the institutional investors are generating, ultimately passing part of the additional yields generated to Lenders.


# Lenders

The *Lenders* are crypto/DeFi native users and institutions, who are looking for a set in stone solution allowing them to capture а part of the interest the market-neutral strategies are generating on centralized exchanges and private capital markets without the complexity of running the infrastructure to execute the trades or having access to an institutional-grade lending desk.

* **Single-borrower pool**: You decide who to lend to. No more consolidated risk among dozens of borrowers. You are lending to a single entity, executing a clear investment strategy. Not a number of lenders, most of whom you have never head of.
* **Fixed terms**: Fixed maturity that matches your investment horizon. Predictable, fixed in-kind yield locked today. Fixed collateral - both in terms of type and coverage levels.&#x20;
* **Risk management**: Full transparency. Independent credit ratings. Permissionless collateral custody and transfer.&#x20;
* **Yield on every asset**: Stables and blue chips are widely available. On top of them, LombardFi improve markets’ capital efficiency by making mid-, smallcap or obscured assets productive again.

**How does it work?**

<figure><img src="/files/rHmFPseVceISYz4pNe94" alt=""><figcaption></figcaption></figure>

The key parameters of each loan requested are defined in the so-called “[Term Sheet](/protocol-design/term-sheet)” that is presented to the Lenders once the pool is created and the bonding period begins.


# Term Sheet

In order to request a loan, the [Borrowers](/protocol-design/borrowers) submit a *Term Sheet*, outlining the parameters of the deal proposed. It could be done either via LombardFi’s [authorized interface](https://lombard.fi/), or with a direct smart contract call. The key variables include:

* The assets requested and pledged as collateral. The collateral may be a single asset or a mixed portfolio with predefined components. Upon its release, LombardFi will support ETH and all ERC20 tokens, with a wider variety to be added not long after.
* Loan origination date and duration / maturity date of the pool.
* Minimum and maximum supply of the requested token to initiate the pool. The Borrower self-imposes the size of the credit facility requested. If the minimum threshold is not reached by the origination date, the loan request is voided. In this case, Lenders can claim their tokens back.
* The annualized interest rate offered for the duration of the loan. Fixed, paid in-kind, and deposited upon a loan’s initiation.
* The collateralization level of the debt facility upon origination.

<figure><img src="/files/T1X7d8zGRZCnR8FbBLSS" alt=""><figcaption><p>An example of the term sheet form</p></figcaption></figure>

Using the authorized LombardFi interface, the term sheet is a no-code tool for borrowers of any kind to create pools with custom parameters. Once the request is submitted, the parameters are pushed onto the blockchain and available for deposits. The verified Borrowers are also presented on the authorized interface. This is when the bonding period begins.


# Loan Origination

Once the pool is created, lenders are given a period of time to commit inventory until the loan origination date comes, or the supply capacity is reached.

<figure><img src="/files/Joqcg4be0sP5cA308EMB" alt=""><figcaption><p>Pool bonding period</p></figcaption></figure>

The pool’s origination date is the moment when the loan gets underwritten. Past this point, the borrower can supply collateral (one or multiple predefined assets) and withdraw the pooled amount of the requested token in an atomic fashion.


# Repayments and Withdrawals

Before the loan matures, the Lender should return the assets borrowed and withdraw her/his collateral in an atomic transaction.

Once the maturity has been reached and funds have been deposited into the contract, the Lenders can withdraw their principal and interest payments from the pool.


# Defaults

To improve borrower liquidity and predict the amount of inventory required during the loan term, no liquidations or margin calls are included in the Protocol’s design. LombardFi believes the borrowers targeted will avoid reputational risk at all costs while the material improvement in the flexibility of their inventory management should result in substantial competitive advantage for LombardFi when compared to other solutions on the market. Last but not least, the improved capital efficiency should result in higher returns on capital generated by the institutional borrowers, ultimately passing part of the additional yields to the Lenders.

In case the borrower does not meet their obligation to repay the loan on the maturity date, the borrower defaults and lenders take control of the collateral pledged. The collateral is distributed on a pro-rata basis between the debt holders, depending on their share of the pool.

In case of a default, the event is officially announced across LombardFi’s communication channels and the borrower is prohibited from being listed on the [authorized interface](https://lombard.fi) of the Protocol.


# How To Deposit Funds


# How to Request a Loan


# Project Roadmap

## **23Q1**

* LombardFi is deployed on Ethereum Mainnet with first verified borrowers being Nexo, Wintermute and SCP among others. Focus on adoption and commercialization.

## **23Q2**

* Development of analyticals tools: official DuneAnalytics dashboard.
* Product integrations with leading non-custodial wallets, yield and traffic aggregators.
* Integrating insurance protection from potential principal loss in case of a default. We are in active conversations with several parties interested in acting as a safeguard for our users.
* As opportunities on other blockchains emerge, we will be looking for alternative networks to deploy our codebase. The EVM-compatible chains are a priority.
* We will start our [path to decentralization](/project-information/path-to-decentralization) by voting on the issuance of $LMD token.

## **23Q3-4**

* LombardFi aims to introduce government bond yields into the protocol. The team is looking for counterparties with bank and custody licenses to facilitate the deal, where LombardFi will be tokenizing the securities held. By doing so, we will bring real yields to the DeFi space, effectively allowing all kinds of users to purchase treasury bills without friction. *Although we are in active discussions with possible counterparties, the service is pushed to the bottom of our priority list due to regulatory uncertainty and the constantly evolving landscape*.&#x20;
* With the rise of digital identity projects, LombardFi will expand the list of targeted borrowers from well-known crypto institutions to natural persons with on-chain reputation.&#x20;
* The team is looking to expand the scope of the borrowers sourcing funds from the Protocol by onboarding traditional financial institutions and businesses onto the platform.&#x20;
* Founded as a peer-to-peer lending platform, LombardFi’s goal is to ultimately cover all services offered by a full-scale OTC desk including bilateral futures/forward and option contracts.


# Value Capturing

The value capturing of LombardFi stems from loan origination fees. At the genesis of the protocol, the standard rate is set to 5% of the interest paid by the Borrower.

The fees will be accumulated in an address controlled by the development team. The funds generated may be used for product development, marketing, other corporate purposes, or distributions.


# Path to Decentralization

Developed by an anonymous team of contributors, LombardFi aims to represent a set of permissionless public goods offered to the blockchain community, far and wide.

Following a successful deployment and initial adoption of the OTC lending desk, the team is committed to form a decentralized organization (DAO), with the first step being the issuance of a governance and treasury token ($LMB) that will be distributed to the participating contributors, active community members and core team of the Protocol.

The details of the token launch will be disclosed via a Snapshot proposal and voted by LombardFi’s active community.


# System Overview

## Introduction

LombardFi is a permissionless DeFi protocol that provides functionality for custom, permissionless reputation-based undercollateralized loans. The smart contracts are meant to allow anyone to become a borrower, whereas external teams are encouraged to develop custom frontends that can filter borrowers, provide reputation-based metadata and verify identities.

## Smart Contract Diagram

<figure><img src="/files/LJGLufhX4pNk65TUkqjK" alt=""><figcaption><p>High-level diagram of the Contracts, Roles and Interactions.</p></figcaption></figure>

## Roles

There are 3 types of actors in the LombardFi protocol: pool borrower, pool lender and governance. The LombardFi protocol is designed in such a way that every action can only be performed by a single role. There are two exceptions to this rule: deploying a pool and repaying debt, which can be performed by anyone. The rationale behind not restricting debt repayment to the borrower is outlined below.

### Borrower

The borrower role is pool-scoped: when we speak of *the borrower* in the context of the protocol, we mean a specific pool's borrower. A user becomes the borrower when they create a pool through the factory. The borrower can perform the following actions in the protocol:

**`Router.sol`**

* Borrow from their pool
* Repay the borrow from the pool
* Withdraw leftover rewards from their pool

**`Pool.sol`**

* Set a whitelisted lender for their pool

### Lender

The lender role is pool-scoped: when we speak of *a lender* in the context of the protocol, we mean a specific pool's lender. A user becomes a lender in a pool when they deposit assets.

LombardFi pools are public by default, thus every address except the borrower can be a lender. The borrower can optionally take their pool private by whitelisting a certain address to be the sole lender in their pool. The lender can perform the following actions in the protocol:

**`Router.sol`**

* Deposit into a pool
* Redeem their deposit with interest

### Governor

The governor role is protocol-scoped: when we speak of *the governor* the context of the protocol, we mean the protocol-wide governing role. By default the governor is the contract deployer.

LombardFi contracts use [OpenZeppelin's Ownable module](https://docs.openzeppelin.com/contracts/4.x/access-control#ownership-and-ownable)[ ](https://docs.openzeppelin.com/contracts/4.x/access-control#ownership-and-ownable)in the Router, PoolFactory and OracleManager. Therefore each contract has an `owner` state variable set initially to the deployer.&#x20;

The `onlyOwner` modifier provided by `Ownable` is used to restrict access to administrative functions.

**`Router.sol`**

* Set the PoolFactory address
* Set the OracleManager address
* Set the treasury address
* Pause the contract
* Unpause the contract

**`PoolFactory.sol`**

* Set the maximum number of collateral address
* Set the protocol-wide origination fee

**`OracleManager.sol`**

* Set the oracle implementations

### Lender

The lender role is pool-scoped: when we speak of *a lender* in the context of the protocol, we mean a specific pool's lender. A user becomes a lender in a pool when they deposit assets.

LombardFi pools are public by default, thus every address except the borrower can be a lender. The borrower can optionally take their pool private by whitelisting a certain address to be the sole lender in their pool. The lender can perform the following actions in the protocol:

* Deposit into a pool
* Redeem their deposit with interest

## Architecture

The `PoolFactory` is the contract that handles the creation of pools. When the contract is constructed, the immutable `Pool` implementation contract is created. Every pool is a clone of this contract. The `PoolFactory` also acts as a registry for deployed pools.

The `Router` is the entry point for all interactions with deployed pools.

The `OracleManager` is an oracle aggregator that can get prices from multiple oracle adapters. Every adapter must conform to the `IBasePriceOracle` interface. The oracle manager's strategy is not to combine prices but to have multiple oracle implementations in order of robustness. For example, first it tries Chainlink, then UniV3. Only Chainlink is supported at this moment with the Uniswap V3 TWAP oracle in development.

The `Treasury` is the address where the origination fee is forwarded to. This is intended to become a DAO treasury.

## Pool

The core functionality of the Lombard protocol is the pool. Anyone can become a borrower by deploying a `Pool` via `PoolFactory.createPool()`.&#x20;

### **Term Sheet**

Each pool has a set of parameters, collectively called a term sheet due to their equivalence with term sheets in traditional finance. Part of the term sheet is set by the borrower at pool creation, whereas the rest is set by the protocol. These parameters will remain immutable throughout the lifecycle of the Pool with the exception of the whitelisted lender addressed later.

* `lentAsset`, the address of an ERC20 token that the borrower wants to borrow. Lender will deposit and earn on this asset.
* `collateralAssets`, an array of addresses corresponding to the ERC20 tokens that the borrower can supply as collateral at the time of borrow.
* `minSupply`, the minimum amount of the lent asset that must be achieved in order to activate the pool. If this amount is achieved, the borrower can borrow the collected funds. At maturity the lenders can redeem their notional with yield.&#x20;
* `maxSupply`, the maximum amount of the lent asset that the pool can accept.
* `startsAt`, the timestamp of the block in which the pool was created.
* `activeAt`, the timestamp after which the pool becomes active.
* `maturesAt`, the timestamp after which the pool matures.
* `coupon`, the yield paid by the borrower to the lenders for the duration in which the pool is active. The coupon is different from the APR.
* `ltv`, the loan-to-value ratio which says the minimum value of the collateral that must be supplied by the borrower for the pool.
* `originationFee`, A pool has an origination fee, deducted from the borrowed amount at the point of borrow, and deposited into the protocol treasury.
* `whitelistedLender`, set to the zero address by default, which means that pool is public by default. The borrower can choose to whitelist a certain address as a lender at any time, mimicking a private OTC deal.

Parameters in the term sheet are chosen by the borrower at pool creation with the exception of the origination fee (defined at protocol level by the governance), the start timestamp which is automatically set to the block timestamp at pool creation and the whitelisted lender which can be changed by the borrower at any time.

### **Timestamps and Pool Lifecycle**

There are 3 timestamps in a pool: `startsAt`, `activeAt` and `maturesAt` that collectively delineate 3 pool periods: the bonding, active and mature periods.

<figure><img src="/files/qyXexejsYLXd4MsXmxMj" alt=""><figcaption><p><strong>The lifecycle diagram of a pool</strong></p></figcaption></figure>

#### Bonding period

The start timestamp is set to the time at which the pool is created. Up until the active timestamp the pool is said to be *bonding*. During the bonding period lenders can deposit the lent asset.

#### Active period

Between the active timestamp and the maturity timestamp the pool is said to be *active*. Once a pool is active lenders can no longer deposit.

* If the minimum supply is achieved the borrower can borrow all collected funds (all at once, partial borrows are not allowed). They must repay the loan by the end of the active period.
* If the minimum supply is not achieved the borrower cannot borrow. Lenders can instead redeem their deposits without the coupon. The borrowers can withdraw their upfront.

#### Mature period

After the maturity timestamp the pool is said to be *mature*.

* If all debt was repaid lenders can redeem their deposit + the coupon on their deposit.
* If debt was partially repaid lenders can redeem a pro-rata distribution on the partial repayment + the coupon on their deposit + a pro-rata distribution of the collateral.
* If debt was not repaid lenders can redeem the coupon on their deposit + a pro-rata distribution of the collateral.

## **Particularities of the Protocol**

Lenders that default are not punished by the system. Instead, the reputation problem is to be solved on the user interface level, where any number of on-chain or off-chain reputation/curation systems can be used.

Anyone can repay the borrower's loan on behalf of the borrower. This is done for the greater flexibility of institutional borrowers, which will usually deploy the borrowed capital on centralized exchanges, and would prefer to repay with a hot wallet.

When creating a pool the borrower must supply the coupon for the maximum supply upfront. For example if the maximum is 1M DAI and the coupon is 6% then they must supply 60000 DAI in order to create the pool. This is done to guarantee yield to the lenders and to encourage the creation of high-quality pools by reputable actors. If the maximum supply is not achieved borrowers can withdraw the coupon for the unfilled amount.

When borrowing the collected funds the borrower must supply collateral of any subset of the collateral assets whose value satisfies the loan-to-value ratio.

Pools are OpenZeppelin Clones of an immutable pool implementation created in the constructor of `PoolFactory`.

Native Ether is not accepted. All assets must be ERC20 tokens.

Tokens with more than 18 decimals are not accepted due to concerns about mathematical precision and token sanity.

## Typical Flow

* Boris spots a profitable market-neutral opportunity for USDC on a centralized exchange. He wants to borrow USDC and provide collateral with WETH and/or WBTC. He wants a minimum of 1M DAI and a maximum of 20M DAI. He chooses a 500% LTV, and gives a 1.5% coupon for a 3-month term, amounting to a 6% APR. Deposits are open for 2 weeks.
* Boris creates a pool by calling `PoolFactory.createPool()` with the desired termsheet. To do that he has to supply 0.3M DAI as an upfront (1.5% \* 20M DAI).
* Lena spots the pool and deposits 3M DAI by calling `Router.deposit()`.
* The initial 2 weeks are over. The pool is now active.
* Boris borrows all 3M DAI by calling `Router.borrow()`. To do that he has to supply collateral of value equal to 20% of the borrowed amount (0.6M DAI) to achieve a 500% LTV. He decides to give 10% of that in WETH and 90% of that in WBTC as he believes that WBTC is overvalued and WETH is undervalued.
* Boris withdraws the extra coupon for the unfilled 17M DAI by calling `Router.withdrawLeftovers()`. This amounts to 45K DAI.
* Boris deploys the funds off-chain. After 2 1/2 months the off-chain opportunity has dried up.
* Boris repays the borrowed 3M in time by calling `Router.repay()`. He gets back all collateral.
* The pool matures.
* Lena redeems her deposit (3M) + coupon (0.3M) by calling `Router.redeem()`.

## Tests

The codebase has unit tests for all contracts for 100% branch and line coverage, as well as end-to-end tests for scenarios and integration.

## Deployment

There is a number of deployment scripts under `scripts/` that allow for mainnet and testnet deployments.


# Pool Factory

The PoolFactory contract acts as a deploye and registry for new Pool contracts. It also provides certain functions for changing the properties of the deployed pools, such as the origination fee.

## Overview

The `PoolFactory` contract is a factory contract that deploys `Pool` contracts. It is owned by a single address, which can be set using the `Ownable` contract. The contract uses `Clones` from OpenZeppelin to clone the `Pool` contract when a new pool is created. The `PoolFactory` contract uses `ReentrancyGuard` to prevent reentrancy attacks. The contract has several public variables, including `router`, `pid`, `maxNumberOfCollateralAssets`, and `originationFee`. The `router` variable holds the address of the Router contract, while `pid` holds the number of pools that have been created. The `maxNumberOfCollateralAssets` variable holds the maximum number of unique collateral assets per pool, and `originationFee` holds the origination fee in wad (1e18 = 100%) for the pools that the contract deploys. The `PoolFactory` contract has a public function called `createPool` that deploys a new `Pool` contract with the specified parameters.

## Creating a pool

The process of creating a pool in the contract involves several steps. First, the `createPool` function is called, providing it with the necessary parameters such as the address of the lending and collateral assets, the coupon yield, the loan-to-value ratio, and the timestamps for when the pool becomes active and matures.

Next, the function performs several checks to ensure that the supplied parameters are valid. This includes checking that the provided addresses are not null, that the loan-to-value ratio and coupon yield are within acceptable ranges, and that the pool will become active and mature at the specified times.

Once the parameters have been validated, the contract uses the OpenZeppelin Clones library to clone the implementation contract for the pool, which is stored in the `poolImplementation` variable. This creates a new instance of the `Pool` contract with the supplied parameters.

The newly created pool is then added to the `pidToPoolAddress` mapping, which maps pool IDs to their corresponding addresses. This allows the contract to easily track and retrieve the address of any created pool by its ID.

Finally, the function returns the address of the newly created pool.

## Helper functions

The contract uses a number of helper functions to assist with the main functionality of deploying new pool contracts. These helper functions include:

* `getAllPools` - this function returns an array of all the pool contracts that have been deployed using this contract.
* `getAllPoolsSlice` - this function allows the caller to retrieve a slice of the array of all pool contracts, starting at a specified index and returning a specified number of contracts.
* `_executeTransferFromWithBalanceChecks` - this is an internal function used by the contract to transfer ERC20 tokens from one address to another, while returning the received amount. It is used to make sure the lent asset is not a fee-on-transfer token.
* `setMaxNumberOfCollateralAssets` - this function allows the owner of the contract to set the maximum number of unique collateral assets that can be included in a pool contract.
* `setOriginationFee` - this function allows the owner of the contract to set the origination fee for pool contracts. This is the fee that borrowers must pay when creating a new loan.


# Pool

The Pool contract is a core element of the LombardFi protocol that contains assets and governs their accounting logic.

## Overview

This smart contract is a "pool" contract that allows lenders to deposit a specific type of ERC20 token and borrowers to borrow the deposited tokens with collateral. The pool has a defined loan-to-value ratio, coupon yield, and origination fee. The contract also includes functionality for borrowers to withdraw redundant coupons if the maximum capacity of the pool is not reached and for lenders to withdraw their deposited assets after the pool matures. The contract is written in Solidity and uses the OpenZeppelin framework for its ERC20 functionality.

## Lenders and borrowers

The borrower is the party that takes a loan from the lenders on a specific predefined loan-to-value ratio against collateral. The lender is the party that provides the loan to the borrower. In the smart contract, the borrower is defined by the "borrower" address, and the lenders are defined by the "whitelistedLender" address (if the pool is open to everyone, the lender address is set to zero). The lender deposits the "lentAsset" ERC20 token, which the borrower borrows. The borrower must repay the loan before the pool matures, or the pool will default.

## Lifecycle of a pool

The lifecycle of a pool in Lombard starts with its creation by a borrower who wants to take out a loan. The borrower specifies the terms of the loan, including the type of asset that will be borrowed, the collateral assets that will be used to secure the loan, the loan-to-value ratio, the origination fee, the minimum and maximum amounts of the borrowed asset, and the duration of the loan.

Once the pool is created, lenders can start depositing the specified borrowed asset into the pool. The pool remains open for deposits until the predetermined "startsAt" timestamp, at which point the pool becomes active and borrowers can start taking out loans. The borrower must provide collateral assets that are equal in value to the borrowed amount multiplied by the loan-to-value ratio specified in the pool's terms.

Once the pool is active, borrowers can take out loans and lenders can earn yield on their deposited assets. The yield is determined by the "coupon" rate specified in the pool's terms and accrues until the "maturesAt" timestamp, at which point the pool reaches maturity.

After the pool matures, lenders can withdraw their deposited assets and any accrued yield. If the borrower has not repaid their loan by this point, the pool defaults and the collateral assets are liquidated to repay the lenders.

Additionally, if the minimum amount of the borrowed asset specified in the pool's terms is not reached, borrowers are not able to take out loans and lenders can withdraw their deposited assets without accruing yield. The borrower can also withdraw any unused coupon in this case.

## Risks for lenders

The risks for lenders when interacting with this contract include the potential for the borrower to default on their loan, which would result in a loss of their deposited funds. Additionally, the loan-to-value ratio (LTV) of the pool is fixed and if the value of the collateral decreases, the lender may be at risk of losing their deposited funds if the collateral is liquidated to repay the loan. There is also a potential risk of the pool not reaching the minimum amount of lentAsset required to activate borrowing functionality, in which case the lenders would not be able to earn yield on their deposited funds.

## Depositing assets

The deposit function allows lenders to deposit a specified amount of the "lentAsset" ERC20 token into the pool. The function checks that the caller is a whitelisted lender (if the pool has a whitelisted lender) and that the pool is still in the deposit phase (before the "activeAt" timestamp). If these conditions are met, the function transfers the specified amount of "lentAsset" from the caller to the pool contract.

## Redeeming the deposit

A lender uses the `redeem` function in the Pool smart contract to withdraw their deposited `lentAsset` from the pool. This can only be done after the pool has matured, as specified by the `maturesAt` timestamp. If the pool was successful the lender will also receive accrued yield.

## Default logic

The \_default function is used by lenders to trigger the default functionality of the pool contract. This occurs when the borrower fails to repay their loan before the pool matures. When this happens, the lenders can call the \_default function to seize the collateral that the borrower has posted. This collateral is then distributed to the lenders in proportion to their contribution to the pool. This allows the lenders to recoup their losses and ensures that the terms of the loan are upheld.

## Borrowing

To borrow from the pool, a borrower must also provide enough collateral according to the requirements specified in the pool contract. Once these requirements are met, the borrower can call the `borrow` function on the pool contract with the desired amount of the loan and the corresponding collateral. The contract will then transfer the borrowed funds to the borrower's address and update the borrower's loan balance.

## Repaying a loan

To repay a loan, the borrower can call the `repay` function on the pool contract with the amount they wish to repay. The contract will then transfer the repaid funds to the pool and return all or part of the previously pledged collateral to the borrower.


# Router

The Router contract is an entry point for interacting with the LombardFi system, with much of the verification logic done in the contract to compress the size of the Pool contract.

## Overview

The Router contract is an entry point for interacting with the LombardFi system. It is a pausable, ownable, and reentrancy-guarded contract that implements the IRouter interface. It uses the SafeERC20 library and imports several other contracts, including the Ownable, Pausable, and ReentrancyGuard contracts from the openzeppelin library. The Router contract has methods for setting the addresses of the PoolFactory, OracleManager, and treasury, as well as methods for pausing and unpausing the contract. It also has methods for creating pools, adding collateral to pools, and redeeming collateral from pools.

## Deposit

The deposit function allows users to deposit funds into a pool. The function takes in the pool address, the amount to deposit, and the ERC20 token to deposit as parameters. The function first checks that the contract is not paused and that the pool address and token address are not the zero address. The function then uses the SafeERC20 contract to transfer the specified amount of the specified token to the pool.

## Borrow

The borrow function allows users to borrow funds from a pool. The function takes in the pool address, the amount to borrow, and the ERC20 token to borrow as parameters. The function first checks that the contract is not paused and that the pool address and token address are not the zero address. The function then calls the borrow function on the specified pool contract, passing in the amount and token to borrow.

## Repay

The repay function allows users to repay a loan from a pool. The function takes in the pool address, the amount to repay, and the ERC20 token to repay with as parameters. The function first checks that the contract is not paused and that the pool address and token address are not the zero address. The function then calls the repay function on the specified pool contract, passing in the amount and token to repay with.

## Redeem

The `redeem` function allows a user to redeem their assets from a pool in the LombardFi system. It requires the caller to provide the ID of the pool they want to redeem from, the amount they want to redeem, and the amount of fees they want to pay. The function verifies that the caller is a member of the specified pool, and that the amount they want to redeem is not greater than their share of the pool's assets. If the verification is successful, the function calls the `redeem` function of the pool contract and transfers the redeemed assets to the caller.

## Withdraw leftovers

The `withdrawLeftovers` function allows a user to withdraw any assets that remain in a pool after all other members have redeemed their shares. It requires the caller to provide the ID of the pool they want to withdraw from. The function verifies that the caller is the owner of the pool, and that the pool is not currently paused. If the verification is successful, the function calls the `withdrawLeftovers` function of the pool contract and transfers the remaining assets to the caller.

## Helper functions

* `_getPool` is a helper function that retrieves the pool contract instance associated with a given pool ID. This is used internally by other functions to access the functions and variables of the pool contract.
* `_verifyCallerIsNotBorrower` is a helper function that checks if the caller is the borrower of a given pool. This is used to ensure that the caller is not the borrower before allowing them to execute certain functions.
* `_assetsAreValidPoolCollateral` is a helper function that checks if the assets being added to a pool as collateral are valid. This is used to ensure that only valid assets are added as collateral to a pool.
* `getBorrowingPower` is a helper function that calculates the borrowing power of a given pool. This is used to determine the maximum amount that can be borrowed from the pool.


# Oracle Manager

The OracleManager contract aggregates answers by oracle systems such as Chainlink. Fetching a price involves querying oracles one-by-one until a satisfactory answer is obtained.

## Overview

The OracleManager contract is a smart contract that is used to store and manage various oracle implementations. The contract uses the `IBasePriceOracle` interface to define the oracle implementations that it supports. It also uses the `Ownable` contract to restrict access to certain functions to the contract owner.

The contract has an array of oracle implementations called `oracles`, and a variable called `numOracles` which holds the number of active oracle implementations. It also has a variable called `quoteAsset` which holds the address of the ERC20 token in which the price is quoted.

The contract has a constructor which takes the address of the quote asset as a parameter and sets the `quoteAsset` variable to this value. It also has a `getPrice` function which takes the address of the asset to be quoted as a parameter, and returns the price of the asset in terms of the base asset. This function iterates through the `oracles` array, and calls the `IBasePriceOracle.getPrice` function on each oracle implementation if it supports the asset. If no oracle supports the asset, the function reverts.

The contract also has a `setOracles` function which allows the contract owner to set the oracle implementations in the contract. This function takes an array of `IBasePriceOracle` implementations as a parameter, and first zeroes out the `oracles` array in storage. It then copies the elements from the `_oracles` array into the `oracles` array, and updates the `numOracles` variable. This function can only be called by the contract owner.

## Getting the price

The getPrice function is a public view function in the OracleManager contract that allows external contracts to query the price of an asset. It accepts a single argument, the address of the asset to be quoted, and returns the price of the asset in wad.

The function first checks if the asset is the same as the quote asset specified in the contract constructor. If this is the case, it returns a price of 1e18 (1 with 18 zeros after it) as the price of the asset in wad.

If the asset is not the same as the quote asset, the function iterates through the array of oracle implementations stored in the contract in order of priority. For each oracle, it checks if the oracle supports the asset by calling the supportsAsset function on the oracle. If the oracle supports the asset, it calls the getPrice function on the oracle to get the price of the asset in wad. If the oracle successfully returns the price, the function returns it.

If none of the oracles in the contract support the asset, the function reverts with the error message "OracleManager::not supported".

The getPrice function is a view function, so it does not modify the contract state and can be called by external contracts without any gas costs.

## Setting the oracles

To set the oracles, the contract first zeroes out the `oracles` array in storage. It then copies elements from the `_oracles` array passed as a parameter and updates the `numOracles` variable to reflect the number of oracles that have been set. This can only be done by the owner of the contract.

The contract requires that the number of oracles is between 1 and 10, inclusive. If the number of oracles is outside of this range, the transaction will revert with an error message.

Once the oracles have been set, external contracts can call the `getPrice` function to get the price of an asset. The `getPrice` function iterates through the `oracles` array in order and calls the `IBasePriceOracle.getPrice()` function on each oracle if the oracle supports the asset. If no oracle supports the asset, the transaction will revert with an error message.


# Chainlink Oracle Adapter

The ChainlinkOracleAdapter is a contract that uses Chainlink to retrieve price data for various assets. It supports all X / ETH feeds as well as WBTC and WETH.

## Overview

The ChainlinkOracleAdapter contract is an implementation of the IBasePriceOracle interface and it uses Chainlink's feed registry to retrieve prices of assets. The contract takes in the address of the feed registry and the addresses of wrapped native assets (e.g. WETH and WBTC) as input in its constructor.

## Getting the price

The contract has a `getPrice` function that retrieves the price of a particular asset. It first checks if the asset is supported by calling the `supportsAsset` function. It then retrieves the feed for the asset from the feed registry and checks if the price update is within the allowed staleness period (i.e. 24 hours). If the price update is within the allowed staleness period, it retrieves the latest price from the feed and returns it. If the price update is outside the allowed staleness period, it throws an error.

## Supported assets

The contract has a `supportsAsset` function that checks if a particular asset is supported by the oracle. It first checks if the quote asset is either WETH or ETH and not disabled. It then checks if the base asset is equal to WBTC and converts it to BTC if it is. It then retrieves the feed for the base asset and quote asset from the feed registry and checks if the feed exists. If the feed exists, it returns true, indicating that the asset is supported by the oracle.

The contract also has a `setAssetStatus` function that enables or disables a particular asset. This function can only be called by the contract owner and it checks if the asset is supported by calling the `supportsAsset` function. If the asset is supported, it updates the disabled assets mapping with the new status of the asset.


# Contract Reference

<table><thead><tr><th width="241">Contract</th><th>Deployment Address</th><th width="144">Network</th></tr></thead><tbody><tr><td>Router</td><td><code>0x0F063E57fB54EaBC88BDb2cc6edeD3f935c59b03</code></td><td><a href="https://sepolia.etherscan.io/address/0x0F063E57fB54EaBC88BDb2cc6edeD3f935c59b03#code">Sepolia</a></td></tr><tr><td>PoolFactory</td><td><code>0xedAf63bEe391d8Cff4DCD59BC95561A30A4Bb7Ea</code></td><td><a href="https://sepolia.etherscan.io/address/0xedAf63bEe391d8Cff4DCD59BC95561A30A4Bb7Ea#code">Sepolia</a></td></tr><tr><td>OracleManager</td><td><code>0x34656E99368e26Ce752De2f0c975Ad8712C17EcA</code></td><td><a href="https://sepolia.etherscan.io/address/0x34656E99368e26Ce752De2f0c975Ad8712C17EcA#code">Sepolia</a></td></tr><tr><td>ChainlinkOracleAdapter</td><td><code>0x28f670F1D70A9bD3C530bfF604036fa3C9De1E0E</code></td><td><a href="https://sepolia.etherscan.io/address/0x28f670F1D70A9bD3C530bfF604036fa3C9De1E0E#code">Sepolia</a></td></tr></tbody></table>


# Router.sol

### Router

The router is the entry point for interacting with the LombardFi system. All of the transfers from user to pool are executed by the router. *Much of the verification logic is done in the router to compress the size of the Pool contract.*

#### poolFactory

```solidity
contract IPoolFactory poolFactory
```

Address of the PoolFactory.

#### oracleManager

```solidity
contract IOracleManager oracleManager
```

Address of the OracleManager.

#### treasury

```solidity
address treasury
```

Address of the protocol treasury.

*Receives the origination fee at pool creation.*

#### nonZero

```solidity
modifier nonZero(uint256 amt)
```

Verify that an integer is greater than 0.

*Throws an error if the uint256 is equal to 0*

**Parameters**

| Name | Type    | Description           |
| ---- | ------- | --------------------- |
| amt  | uint256 | The integer to check. |

#### nonZeroAddress

```solidity
modifier nonZeroAddress(address _address)
```

Verify that an address is not the zero address.

*Throws an error if the address is the zero address.*

**Parameters**

| Name      | Type    | Description           |
| --------- | ------- | --------------------- |
| \_address | address | The address to check. |

#### setFactory

```solidity
function setFactory(contract IPoolFactory _poolFactory) external
```

Set the PoolFactory implementation address.&#x20;

Callable only by the owner.

*Throws an error if the supplied address is the zero address.*

**Parameters**

| Name          | Type                  | Description             |
| ------------- | --------------------- | ----------------------- |
| \_poolFactory | contract IPoolFactory | The new implementation. |

#### setOracleManager

```solidity
function setOracleManager(contract IOracleManager _oracleManager) external
```

Set the OracleManager implementation address.&#x20;

Callable only by the owner.

*Throws an error if the address is the zero address.*

**Parameters**

| Name            | Type                    | Description             |
| --------------- | ----------------------- | ----------------------- |
| \_oracleManager | contract IOracleManager | The new implementation. |

#### setTreasury

```solidity
function setTreasury(address _treasury) external
```

Set the treasury address. Callable only by the owner.

*Throws an error if the address is the zero address.*

**Parameters**

| Name       | Type    | Description        |
| ---------- | ------- | ------------------ |
| \_treasury | address | The new recipient. |

#### pause

```solidity
function pause() external
```

Pause the contract. Callable only by the owner.

#### unpause

```solidity
function unpause() external
```

Unpause the contract. Callable only by the owner.

*The contract must be paused to unpause it.*

#### deposit

```solidity
function deposit(uint256 _pid, uint256 _amt) external
```

Deposit into a pool.

*The contract must not be paused. `_amt` must be nonzero.*&#x20;

*A pool with the `_pid` must exist.*&#x20;

*Caller must not be the pool's borrower.*&#x20;

*Caller must be the whitelisted lender if the pool has one.*&#x20;

*Caller must have approved this contract to spend `_amt` of the pool's lent asset.*&#x20;

*The pool must not be active or mature.*&#x20;

*The pool must have sufficient open capacity for `_amt`.*

**Parameters**

| Name  | Type    | Description                                     |
| ----- | ------- | ----------------------------------------------- |
| \_pid | uint256 | The id of the pool to deposit in.               |
| \_amt | uint256 | The amount of the pool's lent asset to deposit. |

#### borrow

```solidity
function borrow(uint256 _pid, address[] _collateralAssets, uint256[] _amts) external
```

Borrow the available amount of lent asset from a pool. Transfers the pool's lent asset from pool to borrower. Transfers collateral from borrower to pool.

*The contract must not be paused.*&#x20;

*A pool with the `_pid` must exist.*&#x20;

*Caller must be the pool's borrower.*&#x20;

*Lengths of `_collateralAssets` and `amts` must match.*&#x20;

*`_collateralAssets` must be the pool's collateral assets or a subset.*&#x20;

*The pool must be active.*&#x20;

*The pool's minimum deposit must have been reached.*&#x20;

*The loan amount and value of the supplied collateral must satisfy the loan-to-value ratio.*

**Parameters**

| Name               | Type       | Description                                  |
| ------------------ | ---------- | -------------------------------------------- |
| \_pid              | uint256    | The id of the pool to borrow from.           |
| \_collateralAssets | address\[] | The assets to deposit as collateral          |
| \_amts             | uint256\[] | The amounts corresponding to the collaterals |

#### repay

```solidity
function repay(uint256 _pid, uint256 _amt) external
```

Repay a part of the loan. Transfers the pool's lent asset from borrower to pool. Transfers collateral from pool to borrower.

*A pool with the `_pid` must exist. `_amt` must be nonzero.*&#x20;

*The contract must not be paused.*&#x20;

*Pool must be active if the router is under normal operation.*&#x20;

*Caller must be the pool's borrower.*&#x20;

*The pool must be active.*

**Parameters**

| Name  | Type    | Description                                   |
| ----- | ------- | --------------------------------------------- |
| \_pid | uint256 | The id of the pool to repay in.               |
| \_amt | uint256 | The amount of the pool's lent asset to repay. |

#### redeem

```solidity
function redeem(uint256 _pid) external
```

Redeem notional and yield from a mature pool or redeem notional from an unsuccessful pool. Transfers the pool's lent asset from pool to caller. Transfers collateral from caller to pool.

*The contract must not be paused.*&#x20;

*A pool with the `_pid` must exist.*&#x20;

*Caller must not be the pool's borrower.*&#x20;

*The pool must be mature or active with less deposits than the minimum.*&#x20;

*Caller must have made a deposit.*&#x20;

*Caller can redeem only once per pool.*

**Parameters**

| Name  | Type    | Description                        |
| ----- | ------- | ---------------------------------- |
| \_pid | uint256 | The id of the pool to redeem from. |

#### withdrawLeftovers

```solidity
function withdrawLeftovers(uint256 _pid) external
```

Withdraw redundant yield from a pool. Transfers a part of the upfront for the unrealised size back to the borrower.

*The contract must not be paused.*&#x20;

*A pool with the `_pid` must exist.*&#x20;

*Caller must be the pool's borrower.*&#x20;

*Borrower can withdraw only once.*&#x20;

*The pool must be active.*

**Parameters**

| Name  | Type    | Description                                    |
| ----- | ------- | ---------------------------------------------- |
| \_pid | uint256 | The id of the pool to withdraw leftovers from. |

#### getBorrowingPower

```solidity
function getBorrowingPower(address[] _collateralAssets, uint256[] _amts) public view returns (uint256 _borrowingPower)
```

Utility function that returns the value of collateral. Prices are fetched from the OracleManager.

*Also used for off-chain data retrieval.*

**Parameters**

| Name               | Type       | Description                                         |
| ------------------ | ---------- | --------------------------------------------------- |
| \_collateralAssets | address\[] | Array of collateral assets.                         |
| \_amts             | uint256\[] | The amounts corresponding to the collateral assets. |

**Return Values**

| Name             | Type    | Description                        |
| ---------------- | ------- | ---------------------------------- |
| \_borrowingPower | uint256 | The total value of the collateral. |

#### \_assetsAreValidPoolCollateral

```solidity
function _assetsAreValidPoolCollateral(contract IPool _pool, address[] _assets) private view returns (bool)
```

Utility function that checks whether an array of addresses match pool collateral. They must be a subset.

**Parameters**

| Name     | Type           | Description                                                   |
| -------- | -------------- | ------------------------------------------------------------- |
| \_pool   | contract IPool | The pool to check the assets against.                         |
| \_assets | address\[]     | The array of ERC20 token addresses to check against the pool. |

**Return Values**

| Name | Type | Description                                                   |
| ---- | ---- | ------------------------------------------------------------- |
| \[0] | bool | Whether the given assets are valid subset of pool collateral. |

#### \_verifyCallerIsNotBorrower

```solidity
function _verifyCallerIsNotBorrower(contract IPool _pool) private view
```

Private function that verifies that the caller is not the pool's borrower.

**Parameters**

| Name   | Type           | Description        |
| ------ | -------------- | ------------------ |
| \_pool | contract IPool | The pool contract. |

#### \_getPool

```solidity
function _getPool(uint256 _pid) private view returns (contract IPool _pool)
```

Private function that gets a pool address from a pool id.

*Throws an error if a pool with the `_pid` does not exist.*

**Parameters**

| Name  | Type    | Description                               |
| ----- | ------- | ----------------------------------------- |
| \_pid | uint256 | The id of the pool to get the address of. |

**Return Values**

| Name   | Type           | Description        |
| ------ | -------------- | ------------------ |
| \_pool | contract IPool | The pool contract. |


# Pool.sol

### Pool

Pools are OpenZeppelin Clones of an immutable pool implementation. The pool contract resembles an OTC term sheet with a single borrower and one or more lenders. The borrower takes a loan from the lenders on a specific predefined loan-to-value ratio against collateral.&#x20;

#### borrower

```solidity
address borrower
```

Address of the borrower.

#### whitelistedLender

```solidity
address whitelistedLender
```

Address of the whitelisted lender.

*Zero address if the pool is open to everyone.*

#### lentAsset

```solidity
address lentAsset
```

The ERC20 token that lenders deposit and the borrower borrows.

#### collateralAssets

```solidity
address[] collateralAssets
```

The ERC20 tokens that can be used as collateral.

#### startsAt

```solidity
uint32 startsAt
```

The timestamp at which the pool was deployed. Lenders can deposit `lentAsset` until the pool becomes active.

*Set to block.timestamp during pool initialization.*

#### activeAt

```solidity
uint32 activeAt
```

The timestamp after which deposits close and borrowers can borrow the deposits. If the minimum is not reached, borrowers cannot borrow and lenders can withdraw their deposits without accruing yield.

#### maturesAt

```solidity
uint32 maturesAt
```

The timestamp at which the Pool matures. Lenders can withdraw their deposits after maturity.

#### coupon

```solidity
uint96 coupon
```

The yield for the pool during the term. The term is the duration at which the pool is active. Borrower must pay this

*Denominated in WAD. A value of 1e17 means the coupon is 10%. The APR can be calculated by multiplying this value by the number of seconds in a year and dividing it by `maturesAt-activeAt`.*

#### ltv

```solidity
uint96 ltv
```

The loan-to-value ratio of the pool. When borrowing the borrower must supply collateral such that the value of the loan = ltv \* value of the collateral.

*Denonimated in WAD. A value of 2e18 means the LTV is 200%. The collateralization ratio is the inverse of the LTV. If the LTV is 200% then the CR is 50%.*

#### originationFee

```solidity
uint96 originationFee
```

The origination fee charged to the protocol treasury. When borrowing, the origination fee is deducted from the borrowed amount and transferred to the protocol treasury.

*Denominated in WAD. A value of 3e15 means the origination fee is 0.3%. The Treasury address is kept in the `Router` contract.*

#### leftoversWithdrawn

```solidity
bool leftoversWithdrawn
```

A boolean that stores whether the borrower has withdrawn the redundant coupon for the pool. Borrowers are required to supply the coupon for the maximum capacity of the pool at pool creation. If the maximum capacity is not reached and the pool is active or mature, the coupon for the unfilled capacity can be withdrawn by the borrower.

#### minSupply

```solidity
uint256 minSupply
```

The minimum amount of `lentAsset` that lenders must deposit to activate borrowing functionality.

*If at least `minSupply` of `lentAsset` is deposited, borrowers can borrow all or a part of deposits. Borrowers must repay their loan before maturity or the pool will default.*

#### maxSupply

```solidity
uint256 maxSupply
```

The maximum amount of `lentAsset` that lenders can deposit. Borrowers must pay the coupon upfront based on this amount, so in practice this cannot be unbounded. This measure also keeps away pool spam.

#### supply

```solidity
uint256 supply
```

The pool supply reached. This is used for a checkpoint to calculate the amount of extra coupon to withdraw.

*When lenders deposit, the amount is added to `supply`. However when lenders redeem, the amount is NOT subtracted from `supply`. Therefore this variable can be interpreted as the supply reached. This is done so that the borrower can withdraw the extra coupon at any time.*

#### borrowed

```solidity
uint256 borrowed
```

The amount of `lentAsset` borrowed by the borrower. Borrowers must pay the coupon upfront based on this amount, so in practice this cannot be unbounded. This is also a measure to keep away pool spam.

#### notionals

```solidity
mapping(address => uint256) notionals
```

Stores a lender's notional (total deposited amount), used to calculate rewards.

#### collateralReserves

```solidity
mapping(address => uint256) collateralReserves
```

Collateral amounts supplied by the borrower.

#### router

```solidity
address router
```

Address of the Router contract.

#### onlyRouter

```solidity
modifier onlyRouter()
```

Verify that the caller is the router.

*Throws an error otherwise.*

#### constructor

```solidity
constructor(address _router) public
```

#### initialize

```solidity
function initialize(address _borrower, address _lentAsset, address[] _collateralAssets, uint96 _coupon, uint96 _ltv, uint96 _originationFee, uint32 _activeAt, uint32 _maturesAt, uint256 _minSupply, uint256 _maxSupply, address _whitelistedLender) external
```

Initialize the pool variables.

*Called by the Router when the clone is created.*

**Parameters**

| Name                | Type       | Description                                           |
| ------------------- | ---------- | ----------------------------------------------------- |
| \_borrower          | address    | The address of the borrower.                          |
| \_lentAsset         | address    | The address of the lent asset.                        |
| \_collateralAssets  | address\[] | The addresses of the collateral assets.               |
| \_coupon            | uint96     | The yield generated by lenders in WAD.                |
| \_ltv               | uint96     | The loan-to-value ratio for the borrower.             |
| \_originationFee    | uint96     | The fee charged by the protocol given by the .        |
| \_activeAt          | uint32     | When deposits end and borrowing is allowed.           |
| \_maturesAt         | uint32     | When the pool is over.                                |
| \_minSupply         | uint256    | Minimum supply of the lent asset to enable borrowing. |
| \_maxSupply         | uint256    | Maximum allowed supply of the lent asset.             |
| \_whitelistedLender | address    |                                                       |

#### deposit

```solidity
function deposit(address _src, uint256 _amt) external
```

Performs accounting when an asset is deposited. Called when the lender deposits.

*Can be called only by the Router.*

**Parameters**

| Name  | Type    | Description                       |
| ----- | ------- | --------------------------------- |
| \_src | address | The lender address.               |
| \_amt | uint256 | The amount of `_asset` deposited. |

#### supplyCollateral

```solidity
function supplyCollateral(address _asset, uint256 _amt) external
```

Performs accounting when the borrower supplies collateral.

*Can be called only by the Router.*

**Parameters**

| Name    | Type    | Description                         |
| ------- | ------- | ----------------------------------- |
| \_asset | address | The address of the asset deposited. |
| \_amt   | uint256 | The amount of `_asset` deposited.   |

#### borrow

```solidity
function borrow(address _lentAsset, uint256 _amt) external
```

Performs accounting and transfers on borrow. Called when the borrower borrows.

*Can be called only by the Router.*

**Parameters**

| Name        | Type    | Description                                         |
| ----------- | ------- | --------------------------------------------------- |
| \_lentAsset | address | The address of `lentAsset` supplied for efficiency. |
| \_amt       | uint256 | The amount of `lentAsset` to borrow.                |

#### repay

```solidity
function repay(uint256 _amt) external
```

Performs accounting and transfers back collateral on repay. Called when the borrower repays their loan.

*Can be called only by the Router.*

**Parameters**

| Name  | Type    | Description                         |
| ----- | ------- | ----------------------------------- |
| \_amt | uint256 | The amount of `lentAsset` to repay. |

#### redeem

```solidity
function redeem(address _src) external
```

Performs accounting and returns deposit to the lender. Called when the lender redeems their deposit. Partial redeems are not allowed.

*Can be called only by the Router.*

**Parameters**

| Name  | Type    | Description                |
| ----- | ------- | -------------------------- |
| \_src | address | The address of the lender. |

#### \_default

```solidity
function _default(address _src) external
```

Performs accounting and returns some lent asset and collateral to the lender. Called when the lender redeems their deposit and the borrower has not repaid all debt before maturity.

*Can be called only by the Router.*

**Parameters**

| Name  | Type    | Description                |
| ----- | ------- | -------------------------- |
| \_src | address | The address of the lender. |

#### withdrawLeftovers

```solidity
function withdrawLeftovers(uint256 _amt) external
```

Performs accounting when the borrower withdraws redundant rewards.

*Can be called only by the Router.*

**Parameters**

| Name  | Type    | Description                        |
| ----- | ------- | ---------------------------------- |
| \_amt | uint256 | The amount of rewards to withdraw. |

#### \_transfer

```solidity
function _transfer(address _asset, address _dst, uint256 _amt) private
```

Transfers an ERC20 token from the pool.

*Uses the OpenZeppelin's SafeERC20 library.*

**Parameters**

| Name    | Type    | Description                           |
| ------- | ------- | ------------------------------------- |
| \_asset | address | The address of the token to transfer. |
| \_dst   | address | The recipient of the transfer.        |
| \_amt   | uint256 | The amount to transfer.               |

#### getCollateralAssets

```solidity
function getCollateralAssets() external view returns (address[])
```

Retrieves the array of collateral assets.

*Used for off-chain data retrieval.*

**Return Values**

| Name | Type       | Description                           |
| ---- | ---------- | ------------------------------------- |
| \[0] | address\[] | `collateralAssets` as a memory array. |


# PoolFactory.sol

### PoolFactory

The PoolFactory deploys Pool with the help of OpenZeppelin Clones. Each pool is a representation of an immutable pool implementation.

#### router

```solidity
address router
```

Address of Router contract.

#### pid

```solidity
uint256 pid
```

The number of created pools.

#### maxNumberOfCollateralAssets

```solidity
uint256 maxNumberOfCollateralAssets
```

Maximum unique collateral assets per pool.

*Can be set by the owner in `setMaxNumberOfCollateralAssets`.*

#### originationFee

```solidity
uint96 originationFee
```

Origination fee in WAD (1e18 = 100%)

*Denominated in WAD. A value of 0.01e18 means the origination fee is 1%. Supplied to pool clones by copying this variable, not by the deployer. Can be set by the owner in `setOriginationFee`.*

#### pidToPoolAddress

```solidity
mapping(uint256 => address) pidToPoolAddress
```

Maps pool id to its address.

#### MAX\_ORIGINATION\_FEE

```solidity
uint96 MAX_ORIGINATION_FEE
```

Maximum configurable origination fee.

*Set to 10%.*

#### poolImplementation

```solidity
address poolImplementation
```

Address of the pool implementation contract.

\_This implementation is cloned when a new pool is deployed in `createPool`.

Set in the constructor. See `Pool`.\_

#### constructor

```solidity
constructor(address _router) public
```

#### createPool

```solidity
function createPool(address _lentAsset, address[] _collateralAssets, uint96 _coupon, uint96 _ltv, uint32 _activeAt, uint32 _maturesAt, uint256 _minSupply, uint256 _maxSupply, address _whitelistedLender) external returns (address pool)
```

Deploys a new pool. Parameters must pass certain sanity checks.

*Uses OpenZeppelin Clones to clone the pool implementation. Throws if the supplied parameters are invalid. See `Pool` for detailed descriptions of the parameters.*

**Parameters**

| Name                | Type       | Description                                                                     |
| ------------------- | ---------- | ------------------------------------------------------------------------------- |
| \_lentAsset         | address    | The ERC20 token that lenders deposit and the borrower borrows.                  |
| \_collateralAssets  | address\[] | The ERC20 tokens that can be used as collateral by the borrower.                |
| \_coupon            | uint96     | The yield for the duration of the term in WAD.                                  |
| \_ltv               | uint96     | The loan-to-value ratio in WAD that must be achieved when borrowing.            |
| \_activeAt          | uint32     | The timestamp after which deposits close and borrowers can borrow the deposits. |
| \_maturesAt         | uint32     | The timestamp after which lenders can withdraw their deposits with yield.       |
| \_minSupply         | uint256    | The minimum supplied lent asset to activate the pool.                           |
| \_maxSupply         | uint256    | The deposit cap of the pool.                                                    |
| \_whitelistedLender | address    | The whitelisted lender for the pool. 0 address if the pool is public.           |

**Return Values**

| Name | Type    | Description                       |
| ---- | ------- | --------------------------------- |
| pool | address | The address of the deployed pool. |

#### setMaxNumberOfCollateralAssets

```solidity
function setMaxNumberOfCollateralAssets(uint256 _maxNumberOfCollateralAssets) external
```

Sets the maximum number of collateral assets allowed in a pool. Must be greater than 0.

*Can be called only by the owner.*

**Parameters**

| Name                          | Type    | Description                                  |
| ----------------------------- | ------- | -------------------------------------------- |
| \_maxNumberOfCollateralAssets | uint256 | The new maximum number of collateral assets. |

#### setOriginationFee

```solidity
function setOriginationFee(uint96 _originationFee) external
```

Sets the origination fee. Cannot be greater than the maximum.

*Can be called only by the owner.*

**Parameters**

| Name             | Type   | Description                     |
| ---------------- | ------ | ------------------------------- |
| \_originationFee | uint96 | The new origination fee in WAD. |

#### getAllPools

```solidity
function getAllPools() external view returns (address[])
```

Retrieves all pools.

*Used for off-chain data retrieval. May run out of gas if `pid` is too large. Use `getAllPoolsSlice` in that case.*

**Return Values**

| Name | Type       | Description                 |
| ---- | ---------- | --------------------------- |
| \[0] | address\[] | an array of pool addresses. |

#### getAllPoolsSlice

```solidity
function getAllPoolsSlice(uint256 _from, uint256 _to) external view returns (address[])
```

Retrieves a slice of pools.

*Used for off-chain data retrieval.*

**Parameters**

| Name   | Type    | Description                       |
| ------ | ------- | --------------------------------- |
| \_from | uint256 | The starting pool id (inclusive). |
| \_to   | uint256 | The ending pool id (exclusive).   |

**Return Values**

| Name | Type       | Description                 |
| ---- | ---------- | --------------------------- |
| \[0] | address\[] | an array of pool addresses. |

#### \_assetsAreValid

```solidity
function _assetsAreValid(address[] _collateralAssets, address _lentAsset) private view returns (bool)
```

Verifies that pool assets are valid. They must be unique, nonzero and have 18 or less decimals.

*The decimal check also checks (loosely) that the assets conform to the ERC20 standard. May throw if the address does not have a decimals function.*

**Parameters**

| Name               | Type       | Description                                                 |
| ------------------ | ---------- | ----------------------------------------------------------- |
| \_collateralAssets | address\[] | An array of ERC20 token addresses to be used as collateral. |
| \_lentAsset        | address    | The address of the ERC20 token which is lent.               |

**Return Values**

| Name | Type | Description                                               |
| ---- | ---- | --------------------------------------------------------- |
| \[0] | bool | true if the assets are valid or false if the checks fail. |

#### \_executeTransferFromWithBalanceChecks

```solidity
function _executeTransferFromWithBalanceChecks(contract IERC20 _asset, address _from, address _to, uint256 _amt) private returns (uint256)
```

Private function that transfers specific amount of `_asset` and performs checks before and after the execution of the transfer.

**Parameters**

| Name    | Type            | Description                        |
| ------- | --------------- | ---------------------------------- |
| \_asset | contract IERC20 | The address of the token transfer. |
| \_from  | address         | The address of the sender.         |
| \_to    | address         | The address of the recipient.      |
| \_amt   | uint256         | The amount to be transferred.      |

**Return Values**

| Name | Type    | Description                                                                    |
| ---- | ------- | ------------------------------------------------------------------------------ |
| \[0] | uint256 | uint256 The value extracted from the difference between pre and post transfer. |


# OracleManager.sol

### OracleManager

The Oracle Manager stores oracle implementations. *External contracts can call `getPrice` to get the price of an asset.*

#### oracles

```solidity
contract IBasePriceOracle[] oracles
```

Active oracle implementations.

*Stored in priority order.*

#### numOracles

```solidity
uint256 numOracles
```

The number of active oracle implementations.

#### quoteAsset

```solidity
address quoteAsset
```

The ERC20 in which the price is quoted.

#### MIN\_SUPPORTED\_ORACLES

```solidity
uint256 MIN_SUPPORTED_ORACLES
```

The minimum number of supported oracles.

#### MAX\_SUPPORTED\_ORACLES

```solidity
uint256 MAX_SUPPORTED_ORACLES
```

The maximum number of supported oracles.

#### constructor

```solidity
constructor(address _quoteAsset) public
```

Sets the ERC20 in which the price is quoted.

**Parameters**

| Name         | Type    | Description                     |
| ------------ | ------- | ------------------------------- |
| \_quoteAsset | address | the address of the quote token. |

#### getPrice

```solidity
function getPrice(address _asset) external view returns (uint256)
```

Returns the price of the asset quoted in terms of the base asset.

*Iterates through `oracles` in order, and calls `IBasePriceOracle.getPrice()` if the oracle supports the asset. Reverts if no oracle supports by asset.*

**Parameters**

| Name    | Type    | Description                        |
| ------- | ------- | ---------------------------------- |
| \_asset | address | the address of the asset to quote. |

**Return Values**

| Name | Type    | Description                    |
| ---- | ------- | ------------------------------ |
| \[0] | uint256 | The price of the asset in WAD. |

#### setOracles

```solidity
function setOracles(contract IBasePriceOracle[] _oracles) external
```

Sets the oracle implementations.

*First it zeroes out the `oracles` array in storage. It copies elements from `_oracles` then updates `numOracles`. 10 >= number of oracles > 0. Can be called only by the owner.*

**Parameters**

| Name      | Type                         | Description                                     |
| --------- | ---------------------------- | ----------------------------------------------- |
| \_oracles | contract IBasePriceOracle\[] | an array of `IBasePriceOracle` implementations. |

#### getOracles

```solidity
function getOracles() external view returns (contract IBasePriceOracle[] _oracles)
```

Retrieves the oracle implementations.

*Used for off-chain data retrieval.*

**Return Values**

| Name      | Type                         | Description                                                   |
| --------- | ---------------------------- | ------------------------------------------------------------- |
| \_oracles | contract IBasePriceOracle\[] | The current active oracle implementations in iteration order. |


# ChainlinkOracleAdapter.sol

### ChainlinkOracleAdapter

Adapted from the official implementation.&#x20;

#### feedRegistry

```solidity
contract IFeedRegistry feedRegistry
```

The feed registry

*Chainlink feed registry contract. See <https://docs.chain.link/docs/data-feeds/feed-registry/>*

#### MAX\_ACCEPTABLE\_STALENESS

```solidity
uint256 MAX_ACCEPTABLE_STALENESS
```

The maximum time allowed after the price update has happened. If the price was updated in more seconds than the maximum time, `getPrice` function returns zero.

#### weth

```solidity
address weth
```

The address of the wrapped native asset.

#### wbtc

```solidity
address wbtc
```

The address of the wrapped asset.

#### disabledAssets

```solidity
mapping(address => bool) disabledAssets
```

Assets which are temporarily stopped.

#### constructor

```solidity
constructor(address _feedRegistry, address _weth, address _wbtc) public
```

Constructor that sets the Chainlink feed registry and wrapped native contract.

**Parameters**

| Name           | Type    | Description                                        |
| -------------- | ------- | -------------------------------------------------- |
| \_feedRegistry | address | The Chainlink Feed Registry implementation.        |
| \_weth         | address | The address of the wrapped native asset e.g. WETH. |
| \_wbtc         | address | The address of the wrapped native asset e.g. WBTC. |

#### setAssetStatus

```solidity
function setAssetStatus(address baseAsset, bool isEnabled) external
```

Function that disables and enables particular assets.

**Parameters**

| Name      | Type    | Description                               |
| --------- | ------- | ----------------------------------------- |
| baseAsset | address | The asset which will be disabled/enabled. |
| isEnabled | bool    | False if the asset should be disabled.    |

#### supportsAsset

```solidity
function supportsAsset(address _baseAsset, address _quoteAsset) external view returns (bool)
```

Checks if a token is supported.

*Only the native token is supported as a quote.*

**Parameters**

| Name         | Type    | Description                          |
| ------------ | ------- | ------------------------------------ |
| \_baseAsset  | address | The asset for whose price is needed. |
| \_quoteAsset | address | The price denomination               |

**Return Values**

| Name | Type | Description                             |
| ---- | ---- | --------------------------------------- |
| \[0] | bool | Whether the oracle supports this asset. |

#### getPrice

```solidity
function getPrice(address _baseAsset, address _quoteAsset) external view returns (bool, uint256)
```

Returns the price of an asset denominated in the base asset.

*Does not throw an error on failure but returns success = false.*

**Return Values**

| Name | Type    | Description                                        |
| ---- | ------- | -------------------------------------------------- |
| \[0] | bool    | whether the call succeeded and the returned price. |
| \[1] | uint256 |                                                    |


# Audits

Lombard is currently undergoing a formal audit by Hacken and Nexo Audits. Prior to the submission for audits, the code was reviewed by RugDoc.


