# What is Nomial

Nomial is an *Inventory Access Layer* that connects crosschain intent solvers with liquidity.

Solvers who fill crosschain intents need capital inventory. Nomial allows liquidity providers to lend their capital to solvers via Inventory Pools.

Nomial creates a permissionless marketplace between solvers who need capital inventory and liquidity providers seeking yield on their existing capital.

<figure><img src="/files/3wBxmyLLqoeFvo3zJ5oB" alt=""><figcaption><p>Before and After Nomial</p></figcaption></figure>


# Why use Nomial

Nomial connects solvers and liquidity providers, allowing solvers to offload inventory management and risk to liquidity providers seeking yield on their existing capital.

## Benefits for Solvers

* Supplement your inventory in order to fill more volume
* Reduce complexity around inventory rebalancing between chains
* Handle spikes in demand without having to move assets between chains
* Earn interest on inventory by collateralizing in yield-bearing assets
* Offload price-risk on inventory to passive capital holders

## Benefits for LPs

* Earn yield from intents bridging
* Support liquidity for specific ecosystems, chains, solvers, and assets
* Completely passive — zero infrastructure required


# Architecture Overview

## Core Components

Nomial V1 has three main components:

1. **Smart Contracts:** Contain logic that controls collateral and pooled capital
2. **Validators:** Off-chain clients that control all on-chain operations in the protocol
3. **API:** Off-chain service that facilitates interactions with the protocol

## Smart Contracts

Core contracts:

* CollateralPool
  * Accepts collateral deposits of any ERC20 token from solvers
  * Implements a time-locked withdrawal mechanism to allow time for liquidations
  * Allows Validators to liquidate solver balances if necessary
* InventoryPool
  * Stores LP capital.
  * Borrowers (e.g., solvers) borrow from instances of InventoryPool
  * Each pool instance supports a single ERC20 token

## Validators

Validators are responsible for triggering collateral liquidations and granting borrow permissions for inventory access. A quorum of Validators can:

* Liquidate solver balances and pending withdrawals in the CollateralPool
* Grant permission to borrow from InventoryPools (based on collateral and outstanding positions)
* Update parameters such as withdrawal periods and interest rates

Learn more about validators: [Security Model](/security/security-model-overview).

## API

The API provides:

* An endpoint for solvers to request validator approval for a borrow transaction
* Transaction data for interactions with the smart contracts
* Read-only state info about validators, borrowers, and pools.


# Loan Process

Solvers must obtain a threshold of signatures from validators in order to borrow capital from an Inventory Pool. This diagram illustrates the process:

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

**Step 1:** Solver submits a request to the API service to borrow from an Inventory Pool on a specific chain

**Step 2:** The API service sends this request to the set of validators for the Inventory Pool

**Step 3:** Validators check on-chain state:

1. Solver's requested loan amount plus their current debt does not exceed the solver's collateral value
2. Solver has not initiated a collateral withdraw
3. Solver does not have any overdue repayments (incurring penalties)

**Step 4:** If validators determine that the borrow request is valid, they respond by providing a signature for the reqeust.

**Step 5:** The API service constructs a transaction for the borrow request which includes validator signatures

**Step 6:** Solver executes the borrow transaction to the Access Control contract for the Inventory Pool

**Step 7:** If access control signature checks pass, the `borrow()` function is executed on the Inventory Pool

## Important Notes

* Access Control for different inventory pools can define different validator sets and implement additional security around borrowing (e.g. enforcing borrower whitelists)
* The Nomial API service exists to provide a convenient interaction point, but is not required for the protocol to function. It is possible for solvers to bypass the API and request to validators directly.
* The access controller sets the rules around required validator signature thresholds. Default access control requires a majority (e.g. 2/3).


# Interest Rate Model

Nomial provides configurable interest rate models out of the box, with the ability to add your own.

## Configurable Rate Models

Nomial lets you define a rate model for each inventory pool. You can choose from existing model implemenations or make your own.

A Nomial pool defines it's interest rate model by referencing an instance of an inventory pool params contract (an interface defined in [IInventoryPoolParams01.sol](https://github.com/nomial-io/nomial-contracts-v1/blob/main/src/interfaces/IInventoryPoolParams01.sol)).

Nomial includes two implementations of interest rate models already. They are:

* [**OwnableParams01.sol**](https://github.com/nomial-io/nomial-contracts-v1/blob/main/src/OwnableParams01.sol)**:** allows an owner to set interest rates. For V1 deployments, the owner is [InventoryPoolDefaultAccessManager01.sol](https://github.com/nomial-io/nomial-contracts-v1/blob/main/src/owners/InventoryPoolDefaultAccessManager01.sol), which requires an admin address with majority validator sign-off to update interest rate. This allows interest rate updates to happen off-chain using a model dictated by validators.
* [**UtilizationBasedRateParams01.sol**](https://github.com/nomial-io/nomial-contracts-v1/blob/main/src/UtilizationBasedRateParams01.sol)**:** This models Aave V3’s two-slope utilization based rate behavior. See an example of a two-slope interest rate on the [Aave USDC pool page](https://app.aave.com/reserve-overview/?underlyingAsset=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48\&marketName=proto_mainnet_v3).

## Borrow Rate vs Liquidity Provider Yield

A borrower must pay any accrued interest when paying back a pool.&#x20;

A Liquidity Provider (LP) supplies assets to a pool and earns their share of the accrued interest. When a pool is not fully utilized, a borrower pays a higher interest rate than the yield rate earned by LP's.

For example, if an LP supplies 100 USDC but a borrower borrows 50 USDC at 10% APR[^1], the LP earns 5% APY[^2] since only half of the supply is utilized.

The following sections provide more precise definitions of utilization, borrow rates, LP yield, and how they interact.

## Utilization

Let:

* **B** = total borrowed amount
* **L** = total liquidity *available* (i.e. supplied – borrowed)

Then the pool’s **utilization** is:

$$
U = \frac{B}{B + L}, \quad 0 \le U \le 1
$$

* If nobody’s borrowing, B=0 ⇒ U=0.
* If the pool is “dry” (all funds borrowed), L=0 ⇒ U=1.

## Borrow Rate

As an example, Nomial's [UtilizationBasedRateParams01](https://github.com/nomial-io/nomial-contracts-v1/blob/main/src/UtilizationBasedRateParams01.sol) contract defines a variable borrow rate that increases with utilization. Defined as r<sub>borrow</sub>(U), the formula is:

$$
r\_\mathrm{borrow}(U) = r\_\mathrm{base} + k \times U
$$

* r<sub>base</sub> = the "floor" rate (e.g., 2% APY). At U = 0, this is the borrow rate.
* k = the variable rate which is multiplied by utilization (U) to get the additional rate on top of the base rate. For example, if r<sub>base</sub> is 0.02, k = 0.18, and utilization is 1, then the borrow rate is 0.2 (i.e., 20%).

## LP Yield Rate

The resulting yield for the LP is described by the following formula:

$$
r\_{\mathrm{supply}}(U) = r\_{\mathrm{borrow}}(U) \times U
$$

Why? Because borrowers pay r<sub>borrow</sub> on what they borrow, which may only be a portion of the pool.

## Takeaways

* If utilization is less than 100%, LP's earn less percent yield than borrowers pay in interest rate percent.
* Use existing interest rate models or create your own by implementing the IInventoryPoolParams01 interface.

[^1]: Annual Percentage Rate, typically used for loans and credit cards, indicating the total annual cost of borrowing, including interest and fees.

[^2]: APY (Annual Percentage Yield) is used for savings accounts and investments, showing the annual return including the effects of compounding interest.


# Take out a loan

This guide explains how to take out a loan from an inventory pool

### Prerequisites

* An account that has been whitelisted as a borrower on the target inventory pool
* A sufficient amount of collateral deposited

### Process Overview

1. Request a borrow digest from the API
2. Sign the digest with your private key
3. Submit the borrow request with your signature
4. Execute the resulting transaction on-chain

### Step 1: Request Borrow Digest

This step authenticates your request, preventing any other address from requesting a borrow on your behalf.

Make a GET request to `https://api.nomial.io/borrow/digest` with the following query parameters:

| Name        | Description                                                                |
| ----------- | -------------------------------------------------------------------------- |
| `chain_id`  | The chain ID (e.g., `1` for Ethereum mainnet)                              |
| `pool`      | The inventory pool address                                                 |
| `borrower`  | Your borrower address                                                      |
| `amount`    | The amount of ERC20 to borrow (in base units)                              |
| `recipient` | The address that will receive the borrowed funds (can be same as borrower) |
| `expiry`    | Unix timestamp when the request expires (e.g., 1 hour in the future)       |
| `salt`      | Arbitrary 32-byte hex string to ensure request uniqueness                  |

**Example**

```
curl "https://api.nomial.io/borrow/digest?\
    chain_id=1&\
    pool=0xabc123...&\
    borrower=0xdef456...&\
    amount=1000000&\
    recipient=0xdef456...&\
    expiry=1234567890&\
    salt=0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef"

```

The API will return a digest that needs to be signed by your borrower address

```json
{
  "digest": "0x..."  // The digest to sign
}
```

### Step 2: Sign the Digest

Sign the provided hex digest directly with the private key of your borrower address. The resulting signature must use the standard 65-byte Ethereum format (r, s, v).

Be sure to sign the raw digest, without including an ERC-191 (“Ethereum Signed Message”) prefix.

#### Using Foundry cast to sign manually

You can use the [cast](https://getfoundry.sh/cast/overview) CLI tool to sign the digest manually with the [cast wallet sign](https://getfoundry.sh/cast/reference/cast-wallet-sign) command

```
cast wallet sign --private-key <BORROWER_PRIVATE_KEY> --no-hash <DIGEST>
```

### Step 3: Submit Borrow Request

Make a POST request to `https://api.nomial.io/borrow`. This should include identical parameters and values to your digest request, with the addition of `signature` (your signature of the request digest).

```json
{
  "chain_id": "1",         // The chain ID
  "pool": "0x...",         // The inventory pool address
  "borrower": "0x...",     // Your borrower address
  "amount": "1000000",     // The amount to borrow in base units
  "recipient": "0x...",    // The address that will receive the borrowed funds
  "expiry": "1234567890",  // Unix timestamp when the request expires
  "salt": "0x...",         // The same salt used for the request digest
  "signature": "0x..."     // Your signature of the request digest
}
```

If your borrow request is valid, the API will return a transaction with validator signatures that can be executed to the inventory pool's owner (access manager):

```json
{
  "call": {
    "chain": {
      "id": "1",
      "name": "ethereum"
    },
    "contract": {
      "name": "InventoryPoolDefaultAccessManager01",
      "address": "0x..."
    },
    "function": "borrow(address pool, uint256 amount, address recipient, uint256 expiry, bytes32 salt, bytes[] signatures)",
    "selector": "0x...",
    "calldata": "0x...",
    "params": [
      {
        "name": "pool",
        "value": "0x..."
      },
      {
        "name": "amount",
        "value": "1000000"
      },
      {
        "name": "recipient",
        "value": "0x..."
      },
      {
        "name": "expiry",
        "value": "1234567890"
      },
      {
        "name": "salt",
        "value": "0x..."
      },
      {
        "name": "signatures",
        "value": ["0x...", "0x..."]
      }
    ]
  },
  "tx": {
    "to": "0x...",         // The access manager contract address
    "data": "0x...",       // The encoded function call data
    "value": "0",          // The amount of native token to send
    "chain_id": "1"        // The chain ID
  },
  "signed_digest": "0x...", // The digest that was signed
  "validator_signatures": [ // Array of validator signatures
    {
      "validator": "validator1",
      "signature": "0x..."
    }
  ]
}
```

### Step 4: Execute the Transaction

You can submit your signed transaction to the chain it was intended for. Here's an example using ethers.js:

<pre class="language-javascript"><code class="lang-javascript">const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
<strong>
</strong><strong>const tx = {
</strong>  to: response.tx.to,
  data: response.tx.data,
  value: response.tx.value
};

// Send the transaction
const result = await wallet.connect(provider).sendTransaction(tx);
await result.wait();
</code></pre>

Alternatively, you can construct the transaction yourself using the `call` object:

```javascript
// Import the contract ABI
const accessManagerABI = [
  "function borrow(address pool, uint256 amount, address recipient, uint256 expiry, bytes32 salt, bytes[] signatures)"
];

// Create contract instance
const accessManager = new ethers.Contract(
  response.call.contract.address,
  accessManagerABI,
  wallet
);

// Execute the borrow function
const result = await accessManager.borrow(
  response.call.params[0].value,  // pool
  response.call.params[1].value,  // amount
  response.call.params[2].value,  // recipient
  response.call.params[3].value,  // expiry
  response.call.params[4].value,  // salt
  response.call.params[5].value   // signatures
);
await result.wait();
```

Note that you must execute your transaction on the access manager contract that is `owner` on the inventory pool, and not the inventory pool itself.


# View loan status

This guide explains how to check the status of your loans and collateral across all inventory pools.

### Request Borrower Status

Make a GET request to `https://api.nomial.io/borrowers/{address}` where `{address}` is your borrower address:

```bash
GET https://api.nomial.io/borrowers/0x1234...
```

The API will return current collateral and debt for the borrower:

```json
{
  "borrower": {
    "name": "borrower1",
    "address": "0x1234..."
  },
  "collateral": {
    "assets": [
      {
        "collateral_pool_address": "0x...",
        "chain": {
          "id": "1",
          "name": "ethereum"
        },
        "token": {
          "address": "0x...",
          "name": "Wrapped Ether",
          "symbol": "WETH",
          "decimals": 18
        },
        "balance": {
          "amount": "10.0",
          "raw": "10000000000000000000"
        }
      }
    ]
  },
  "debt": [
    {
      "pool_address": "0x...",
      "chain": {
        "id": "1",
        "name": "ethereum"
      },
      "token": {
        "address": "0x...",
        "name": "USD Coin",
        "symbol": "USDC",
        "decimals": 6
      },
      "base_debt": {
        "amount": "1000.0",
        "raw": "1000000000"
      },
      "penalty_debt": {
        "amount": "0.0",
        "raw": "0"
      }
    }
  ]
}
```

### Understanding the Response

The response includes:

1. **Borrower Information**:
   * `name`: The name of the borrower
   * `address`: The borrower's address
2. **Collateral Information**:
   * `assets`: Array of collateral assets, each containing:
     * `collateral_pool_address`: The address of the pool where the asset is deposited
     * `chain`: Chain details (ID and name)
     * `token`: Token details (address, name, symbol, decimals)
     * `balance`: Amount currently deposited
3. **Debt Information**:
   * Array of debt positions by pool, each containing:
     * `pool_address`: The inventory pool address
     * `chain`: Chain details (ID and name)
     * `token`: Token details (address, name, symbol, decimals)
     * `base_debt`: Principal and interest the borrower owes to the pool
     * `penalty_debt`: Late payment penalties the borrower owes to the pool

If the borrower has no collateral or no debt, these arrays will be empty in the response.


# Repay a loan

This guide explains how to repay debt owed to an inventory pool

### Prerequisites

* A whitelisted borrower with current debt on an inventory pool

### Process Overview

1. Approve the inventory pool contract to spend your tokens
2. Call the `repay()` function directly on the inventory pool contract

### Step 1: Approve Token Spending

First, get the token address from the inventory pool and approve it to spend your tokens:

```javascript
// Using ethers.js
const inventoryPoolContract = new ethers.Contract(
  inventoryPoolAddress,
  [
    "function asset() view returns (address)"
  ],
  signer
);

// Get the token address from the inventory pool
const tokenAddress = await inventoryPoolContract.asset();

// Set up the token contract
const tokenContract = new ethers.Contract(
  tokenAddress,
  [
    "function approve(address spender, uint256 amount) returns (bool)",
    "function decimals() view returns (uint8)"
  ],
  signer
);

const decimals = await tokenContract.decimals();
const amount = ethers.parseUnits("1000.0", decimals);

// Approve the inventory pool to spend your tokens
const tx = await tokenContract.approve(inventoryPoolAddress, amount);
await tx.wait();
```

### Step 2: Repay the Loan

After approval, call the `repay()` function on the inventory pool contract. Note that the borrower parameter can be any address that has an active loan - it doesn't have to be the caller of the function:

```javascript
// Using ethers.js
const inventoryPoolContract = new ethers.Contract(
  inventoryPoolAddress,
  [
    "function repay(uint256 amount, address borrower)"
  ],
  signer
);

// Repay the loan
const tx = await inventoryPoolContract.repay(amount, borrowerAddress);
await tx.wait();
```

### Important Notes

* If repayment amount exceeds debt, the inventory pool will take the exact amount owed


# Security Model Overview

This document describes the security architecture of Nomial V1

### Access Control

All protocol operations are gated by the [InventoryPoolDefaultAccessManager01.sol](https://github.com/nomial-io/nomial-contracts-v1/blob/main/src/owners/InventoryPoolDefaultAccessManager01.sol) contract, which implements OpenZeppelin’s AccessControl and adds custom roles (`VALIDATOR_ROLE`, `BORROWER_ROLE`) on top of `DEFAULT_ADMIN_ROLE`.

### Validator Sign-Off Mechanism

Off-chain validators run clients that monitor system state across all deployed chains. Every protected operation requires a **strict majority** of the active validator set to sign off—e.g., with four validators, at least three must approve. The access manager verifies replay-protected signatures against the `VALIDATOR_ROLE`, ensuring only a majority consensus executes critical functions.

### Collateral Lock & Withdrawal Process

Collateral in Nomial V1 is managed by the [`CollateralPool01`](https://github.com/nomial-io/nomial-contracts-v1/blob/67549ad721ace334fbd727dda49e91d283291008/src/CollateralPool01.sol) contract, which enforces a two-phase, time-locked withdrawal mechanism. Collateral can be liquidated to cover a borrower's debt if they fail to repay their loan. Validators enforce collateral requirements at their discretion. A majority of validator signatures is required to trigger collateral liquidation.

### Loan Process

Borrowers must obtain majority validator sign-off for all borrows. Additionally, borrowers must be granted the `BORROWER_ROLE` in order to borrow funds from an inventory pool. Collateral requirements, enforced by validators, ensure that borrowers have an incentive to repay their debt.

### Administrative Operations

Administrative changes (interest‐rate parameter changes, validator and borrower set updates, pool state overrides) can only be performed by the default admin address **with the validator majority.**

### Permissionless Actions

Two core user operations remain permissionless

* **LP Deposits & Withdrawals** in [`InventoryPool01.sol`](https://github.com/nomial-io/nomial-contracts-v1/blob/main/src/InventoryPool01.sol)
* **Loan Repayments** in [`InventoryPool01.sol`](https://github.com/nomial-io/nomial-contracts-v1/blob/main/src/InventoryPool01.sol)

### Threat Model

An attacker must compromise a strict majority of validator private keys **and** the default admin key to execute any privileged action or steal funds from inventory pools. If an attacker compromises a borrower key, the collateral provided by the borrower can be liquidated to recover the value of stolen funds.

By combining role-based access control with an off-chain validator set and a strict-majority threshold, Nomial V1 minimizes trust in any single party. An adversary would have to control both a majority of validators and a privileged access account key (admin or borrower) in order to exploit the system.


# Terminology

Brief explanations of Nomial terminology.

## Inventory Access Layer

Nomial is an *Inventory Access Layer* for solvers.

An Inventory Access Layer allows users to collateralize on one chain and borrow inventory on many chains. Inventory is provided by passive liquidity providers. When accessing inventory, borrowers pay fees that accrue back to the pools and thus to liquidity providers.

## Inventory Pool

Inventory Pools are instances of the InventoryPool smart contract. They store LP capital. Borrowers borrow from InventoryPool instances. Each pool supports a single ERC20 token.

## Borrower

Borrowers request capital from the Nomial Inventory Access Layer. The protocol keeps track of the borrowers outstanding positions and collateral and then approves or denies the request.

## Liquidity Provider (LP)

Capital is provided by passive LP's. As repayments are made to pools, LP's earn their share of the interest paid by borrowers.

## Interest Rate

The protocol uses a two-slope interest rate based on utilization similar to Aave v3. Learn more on [Interest Rate Model](/interest-rate-model).

## Penalty Rate

When borrowing, there is a grace period (configurable, but intended to be less than 24 hours) where borrowers only accrue interest at the Interest Rate. After the grace period, the Penalty Rate applies. This Penalty rate is much higher than the normal interest rate. Since Nomial is intended to be used by solvers that need short term access to capital, this penalty rate exists to encourage quick repayment.

## Collateral

Borrowers need to post collateral. This collateral is used to enforce repayment of borrowed positions. Under normal operation, borrowers should be repaying their positions instead of the protocol pulling from their collateral. Collateral acts as a backstop if the borrowers does not repay their position. There is a 7 day waiting period for collateral withdrawal. The waiting ensures that borrows

## Collateral Pool

Collateral is stored in instances of the CollateralPool smart contract. They accept deposits and implement a time-lock withdrawal mechanism.

## Repayment

Borrowers close positions by repaying the principal and interest back to the pool.


# Connect with us

We are eager to collaborate with interested teams and individuals. Please contact us on whatever platform works best!

* [Nomial Telegram Group](https://t.me/nomial_io)
* [Nomial\_io on X (Twitter)](https://x.com/nomial_io)


