> For the complete documentation index, see [llms.txt](https://docs.usefarlight.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.usefarlight.com/protocol/offers-and-matching.md).

# Signed offers and the originate call

The fields of a signed LendOffer, how offers are stored, cancelled and partly filled, how the relayer builds a fill, and the checks originate() runs before any money moves.

Farlight separates the search for a price from the movement of money. Price discovery happens off-chain, in signed messages that cost nothing to create and nothing to withdraw. Settlement happens on-chain, in one transaction that verifies every message it depends on before a single token is spent.

Blend, NFTfi and Seaport-style exchanges rest on the same pattern. The lender gets the equivalent of a limit-order book, and nobody pays gas to place or pull an order.

## The offer structure

An offer is EIP-712 typed data signed under the domain name `Farlight`, version `1`, chain ID `4663`. A lender signs a `LendOffer`; a borrower signs a `BorrowRequest`. These are the fields:

```solidity
struct LendOffer {
    address maker;           // the signer: a lender or a borrower
    uint8   side;            // 0 = lend, 1 = borrow
    address collateralToken; // a specific token, or a tier sentinel for standing offers
    address loanToken;       // USDG
    uint256 principalMin;    // smallest fill the maker will take
    uint256 principalMax;    // largest fill; after a partial fill, the capacity left
    uint16  aprBps;          // fixed APR in basis points
    uint16  maxLtvBps;       // the maker's own LTV ceiling; never above the tier max
    uint32  termSeconds;     // loan term the maker will accept
    uint40  expiry;          // timestamp after which the offer is dead
    uint256 nonce;           // slot in the maker's cancellation bitmap
    bytes32 salt;            // guarantees a unique hash
    bytes32 requestId;       // zero for standing offers, request hash for targeted ones
    uint8   flags;           // selfLiquidate, noClosedMarketLiquidation, parkIdle
}
```

### Targeted and standing offers

* A **targeted offer** puts the hash of a particular borrow request in `requestId`. It can fill that request and no other.
* A **standing offer** sets `requestId` to zero and aims `collateralToken` at a token or a tier sentinel. It behaves like a resting limit order: any request that satisfies its terms may draw on it until `principalMax` is exhausted.

Depth in the book comes from standing offers. A professional lender who leaves one standing offer per Tier A token will be matched with one borrower after another without signing anything further.

### What the flags do

| Flag                        | Effect                                                                                                                                                                  |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `selfLiquidate`             | On liquidation the lender receives collateral in kind at the oracle price instead of USDG proceeds. For lenders who would rather hold the stock.                        |
| `noClosedMarketLiquidation` | The lender waives liquidation while the underlying equity market is closed, accepting gap risk instead of selling into a weekend book.                                  |
| `parkIdle`                  | Unmatched capital sits in the whitelisted Morpho USDG vault and is withdrawn at the moment of origination. See [Idle capital between fills](/protocol/idle-capital.md). |

## Signature schemes

* **ECDSA** for externally owned accounts.
* **EIP-1271** for smart accounts. This is a requirement rather than a convenience. Robinhood Chain ships ERC-4337 entry points and EIP-7702, so a large share of wallets will be contracts.

## Cancelling offers and partial fills

* Each maker has a **nonce bitmap**. Cancelling a single offer flips one bit; cancelling a range flips a whole word. Both are inexpensive on-chain calls that take effect immediately.
* **Partial fills** are tracked against the offer hash. A standing offer with a `principalMax` of 50,000 USDG that fills 10,000 today keeps 40,000 of capacity until it expires or is cancelled.
* The `expiry` field lets an offer lapse by itself, with no transaction needed.

## What the relayer does

Unconsumed offers live in an off-chain relayer, a stateless service with REST and WebSocket endpoints. It

* stores signed offers and borrow requests,
* validates each against chain state (balances, allowances, eligibility, nonces, expiry),
* serves views filtered by token, tier, term, APR and LTV,
* proposes a matching set of offers for any request.

The relayer holds no privilege. Anyone can operate one, and since offers are plain signed messages every relayer sees the same book. The indexer keeps a mirror, so the book survives the loss of any single relayer. [Off-chain services](/architecture/off-chain-services.md) describes the relayer and indexer in detail.

## Building a fill

Matching is deterministic and runs off-chain. For a given borrow request the relayer retains the offers that

1. agree on `side`, `loanToken` and `termSeconds`,
2. cover the request's `collateralToken`, either directly or through its tier,
3. have an `aprBps` at or under the borrower's ceiling,
4. have a `maxLtvBps` at or above the request's LTV,
5. remain valid: not expired, not cancelled, with capacity left.

It sorts the survivors by APR, lowest first, and takes capacity down the list until the principal is covered. The borrower sees the proposed syndicate, the blended APR and the terms of each slice before anything is sent.

## The checks inside originate

`originate(request, offers[], signatures[])` performs the steps below in order and reverts if any one of them fails:

1. Rebuild the request hash and check the fill deadline is still ahead. This deadline is the request's launch window; once it closes, no set of offers can fill the request.
2. For each offer, recover the signer (ECDSA or EIP-1271), check the nonce bit is clear, check the offer has not expired, check capacity remains, and check the term and tokens match.
3. Query the access registry for the borrower and every lender.
4. Query the price gate for the collateral price, session status and staleness, applying the closed-market haircut where it applies.
5. Compare the resulting LTV against the tier ceiling and against each lender's `maxLtvBps`.
6. Confirm the per-token exposure cap is not exceeded.
7. Transfer the collateral from the borrower into escrow.
8. For each lender, pull the principal (directly from the wallet, or from the idle-capital vault when `parkIdle` is set) and forward it to the borrower.
9. Mint one slice token per slice, recording principal, APR, start time and loan ID.
10. Collect the origination fee.
11. Emit `LoanOriginated` with the full slice list.

Batch signature verification is the costliest part of the call. A Rust verifier on Stylus is on the roadmap to reduce it; [Smart contracts](/architecture/smart-contracts.md) has the current picture.

## Worked example

A borrower requests 20,000 USDG against 250 NVDA Stock Tokens for 30 days at a ceiling of 9.00% APR. With NVDA at 176.40 the collateral is worth 44,100 USDG, giving a request LTV of 45.4%, under the 55% Tier A ceiling.

The relayer finds three standing offers:

| Lender        | Available   | APR   | LTV ceiling |
| ------------- | ----------- | ----- | ----------- |
| 0x71c3...9e4a | 5,000 USDG  | 8.50% | 50%         |
| 0xb02f...17d3 | 10,000 USDG | 8.90% | 55%         |
| 0x4e88...c0b1 | 5,000 USDG  | 9.00% | 50%         |

The blended APR across the three is 8.825%. The borrower submits one `originate` call and receives 20,000 USDG less the 0.25% origination fee. Three slice tokens are minted. The health factor at origination is `44,100 × 0.70 / 20,000 = 1.54`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.usefarlight.com/protocol/offers-and-matching.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
