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

# Intents: signing, matching and origination

In Ethosis, finding a price and moving money are two different activities. Prices are discovered off-chain, through signed messages that are free to create and free to withdraw. Money moves on-chain, in one transaction that checks each message it relies on before spending anything.

The same design underpins Blend, NFTfi and Seaport-style exchanges. Lenders get a limit-order book, but nobody pays gas to post or pull an order.

## Anatomy of an offer

Every offer is EIP-712 typed data signed under the domain `Ethosis`, version `1`, chain ID `4663`. Lenders sign a `LendOffer`; borrowers sign a `BorrowRequest`. The fields are:

```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
}
```

### Two kinds of offer

* A **targeted offer** carries the hash of one borrow request in `requestId` and can fill nothing else.
* A **standing offer** leaves `requestId` at zero and points `collateralToken` at a token or a tier sentinel. It works like a resting limit order: any request whose terms satisfy it can draw on it until `principalMax` is used up.

Standing offers are the source of depth. A professional lender who posts one standing offer per Tier A token will be matched against borrower after borrower over time without signing again.

### Flag semantics

| Flag                        | What it changes                                                                                                                                                      |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `selfLiquidate`             | If the loan is liquidated, the lender takes collateral in kind at the oracle price rather than USDG proceeds. Useful for lenders who want the stock.                 |
| `noClosedMarketLiquidation` | The lender gives up liquidation while the underlying equity market is closed, taking on gap risk rather than selling into a weekend book.                            |
| `parkIdle`                  | Capital not yet matched is placed in the whitelisted Morpho USDG vault and pulled out at the moment of origination. See [Parked capital](/protocol/idle-capital.md). |

## Accepted signatures

* **ECDSA** from externally owned accounts.
* **EIP-1271** from smart accounts. Support here is required, not a nicety: Robinhood Chain ships ERC-4337 entry points and EIP-7702, so a large proportion of wallets will be contracts.

## Withdrawing offers and filling them partially

* Every maker owns a **nonce bitmap**. Cancelling one offer flips a bit; cancelling a range flips a whole word. Either way it is a cheap on-chain call that takes effect at once.
* **Partial fills** are recorded against the offer hash. A standing offer with `principalMax` of 50,000 USDG that fills 10,000 today has 40,000 of capacity left until it expires or is cancelled.
* The `expiry` field lets stale offers lapse on their own with no transaction.

## The relayer's job

Offers that have not yet been used sit in an off-chain relayer: a stateless service exposing REST and WebSocket endpoints that

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

Nothing about the relayer is privileged. Anyone can run one, and because offers are plain signed messages, every relayer sees the same book. The indexer keeps a mirror so the book outlives any one relayer. See [Off-chain services](/architecture/off-chain-services.md).

## How a fill is assembled

Matching is deterministic and happens off-chain. Given a borrow request, the relayer keeps offers that

1. match on `side`, `loanToken` and `termSeconds`,
2. cover the request's `collateralToken`, either by name or through its tier,
3. carry an `aprBps` no higher than the borrower's ceiling,
4. carry a `maxLtvBps` no lower than the request's LTV,
5. are still valid: unexpired, uncancelled, with capacity remaining.

It then orders them by APR from lowest to highest and takes capacity down the list until the principal is covered. Before sending anything, the borrower is shown the proposed syndicate, the blended APR and each slice's terms.

## What origination checks

`originate(request, offers[], signatures[])` runs the following, reverting if any step fails:

1. Recompute the request hash and confirm the fill deadline has not passed.
2. For every offer, recover the signer (ECDSA or EIP-1271), confirm the nonce bit is clear, confirm it has not expired, confirm capacity remains, and confirm the term and tokens fit.
3. Ask the access registry about the borrower and each lender.
4. Ask the price gate for the collateral price, session status and staleness, applying the closed-market haircut when it applies.
5. Compare the resulting LTV with the tier ceiling and with each lender's `maxLtvBps`.
6. Confirm the per-token exposure cap is not breached.
7. Move the collateral from the borrower into escrow.
8. For each lender, draw the principal (straight from the wallet, or from the idle-capital vault when `parkIdle` is set) and pass it to the borrower.
9. Issue one slice token per slice, recording principal, APR, start time and loan ID.
10. Take the origination fee.
11. Emit `LoanSettled` carrying the complete slice list.

Verifying the batch of signatures is the most expensive part of the call. A Rust implementation of the verifier on Stylus is on the roadmap to bring that cost down. See [Smart contracts](/architecture/smart-contracts.md).

## A fill in numbers

The borrower asks for 20,000 USDG against 250 NVDA Stock Tokens, for 30 days, at no more than 9.00% APR. NVDA is trading at 176.40, so the collateral is worth 44,100 USDG and the request LTV comes to 45.4%, inside the 55% Tier A ceiling.

The relayer locates 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%         |

Blended across the three, the APR is 8.825%. The borrower sends one `originate` call and receives 20,000 USDG minus the 0.25% origination fee. Three slice tokens are issued. The health factor at origination works out to `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.ethosis.org/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.
