# Introduction

A credit platform backed by financial guarantees

Welcome to the Cap documentation!\
\
Cap is a credit platform backed by financial guarantees built on the Ethereum blockchain. Each loan is underwritten with escrowed collateral to secure the Lender.

The platform relies on a market of Underwriters to independently originate and insure USD loans. Underwriters put their own capital behind each allocation decision and receive an underwriting premium from credit spreads. Lenders earn secured yield that is insured by Underwriter collateral, with all risk coverage transparent and enforced by smart contracts.


# Why Cap?

Verifiable Credit

## The Principal-Agent Problem

Capital allocators in both legacy finance and DeFi encounter the principal-agent problem when allocating funds to yield opportunities: those that make decisions are not fully aligned with liquidity providers. This misalignment of incentives results in an unfavorable outcome for Lenders, with either risky or underperforming opportunities, opaque reporting, and uncertain recourse.

More concretely, the structural problems in the credit space today are the following:<br>

1. **Overfitting for growth over quality:** Originators who do not hold the loans they make have little direct reason to underwrite carefully. Throughput and AUM growth are rewarded more directly than credit quality.
2. **Scalability:** Underwriting and origination bottleneck on a small set of institutional decision-makers. With finite capacity, they either overallocate to existing borrowers or originate more than they can underwrite rigorously.
3. **Opacity**: Portfolios are priced by the same managers who originated the loans. Collateral is tracked through borrower self-reporting and cannot be verified real time. Misreporting and double-pledging tend to surface only after the damage is done.

## Cap's Solution

Cap fixes the root by changing the allocation mechanism: a credit marketplace where every loan is backed by onchain financial guarantees. Each loan has a dedicated Underwriter who puts their own capital behind the decision, making honest underwriting the dominant strategy. Allocation distributes across independent specialists rather than a single team, so rigor scales with the network.

Cap is designed by two guiding principles:

* **Verification over trust:** Every guarantee is enforceable by smart contract. Collateral is onchain so that any Lender can verify their protection in real time.
* **Market driven:** Well-designed rewards and penalties minimize reliance on human judgement. Underwriters compete to back the best Borrowers. Borrowers compete for the lowest-cost credit lines. The market sets the price; the code enforces the rules.

Together, these principles produce:

1. **Safe yield.** Lenders of any size access institutional credit yield, protected by escrowed, overcollateralized guarantees. Because each Underwriter–Borrower pair is isolated, a default in one position cannot cascade to others.
2. **Scalable yield:** An open network of Borrowers competes to deliver the best yield. Game theory dynamically dictates capital allocation, ensuring stcUSD delivers competitive yield invariant to market conditions or scale.
3. **Verifiability.** Every guarantee is enforceable by smart contract. A Underwriter's collateral is onchain and backs exactly one Borrower. Any Lender can verify their protection on the blockchain in real time.
4. **Global and open.** Underwriters and Borrowers can participate across geographies on public blockchain rails.


# Protocol Overview

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

### Protocol Overview

Cap is a credit marketplace where the functions of underwriting, borrowing, and lending are separated and enforced by code. Underwriters escrow collateral to backstop Borrowers. Those Borrowers gain access to credit lines funded by Lenders depositing dollar denominated assets. The Underwriter's escrowed collateral is the first-loss buffer: if a Borrower defaults, the collateral is liquidated before Lenders absorb any loss. Every guarantee, every collateral position, and every Borrower exposure can be verified onchain.

### Protocol Actors

* **Lenders** receive cUSD in exchange for depositing whitelisted assets to the collateral pool. cUSD can be staked for stcUSD to earn auto-compounding yield on institutional loans, with principal protected by Underwriter collateral.
* **Borrowers** draw reserve assets against Underwriter collateral to execute yield strategies. When repaying, they pay the hurdle rate: an aggregate of underwriting premium and borrow rate.
* **Underwriters** escrow collateral to back a specific Borrower, earning a fixed underwriting premium they set per loan. Underwriter delegations are used to protect Lenders against default risk.
* **Liquidators** liquidate Underwriter collateral via permissionless reverse Dutch auction when a Borrower's health factor falls below threshold.

### **Aligning incentives**

* **Lenders** earn yield regardless of market condition and size. More importantly, their principal is protected by verifiable financial guarantees.
* **Borrowers** can source on demand capital with no cost basis, retaining any surplus yield after hurdle rate fees. Furthermore, borrow terms are flexible as Borrowers are able to negotiate with their Underwriter.
* **Underwriters** earn premiums in exchange for their delegations. They have full autonomy to charge risk premium per Borrower, paid out in USD denominated assets.
* **Liquidators** earn liquidation bonuses. Liquidators can earn up fees up to the predetermined bonus cap.

### Flow of Funds

1. **Deposit.** Lenders deposit whitelisted reserve assets (stablecoins, tokenized MMFs) to the Cap Reserve, minting cUSD. Staked cUSD (stcUSD) accrues yield.
2. **Delegate.** Underwriters escrow collateral to underwrite a specific Borrower for an underwriting premium.
3. **Borrow.** Borrowers draw reserve assets liquidity against that delegation to execute a yield strategy.
4. **Repay.** Borrowers repay principal and interest. Underwriters earn an underwriting premium, Lenders earn yield, Borrowers retain the surplus.
5. **Liquidate.** If a Borrower acts maliciously or collateral drops significantly in value, Underwriter collateral is liquidated and redistributed to the Reserve — cUSD remains fully backed at all times.


# Lending Mechanics

stcUSD is the yield-bearing stablecoin of Cap that enables users to earn rewards via a decentralized credit marketplace.

A network of Borrowers can draw from Cap's reserves based on their ability to generate yield over the hurdle rate of the asset. Borrowing in Cap is always overcollateralized: whitelisted Borrowers can permissionlessly access liquidity, provided that they receive sufficient collateral from Underwriters. Undercollateralized loans trigger liquidation events, liquidating Underwriter collateral from their Shared Security Networks. The liquidated funds are redistributed back to the stablecoin holder, such that cUSD is backed 1:1 at all times.

Let us exemplify the mechanics of stcUSD.

## How does stcUSD work?

<figure><img src="/files/2KZLUvSOcasBKWQZWT5A" alt=""><figcaption><p>Happy Path: Yield is distributed to all stakeholders</p></figcaption></figure>

1. **Minting and Staking**: Alice deposits \~$100 worth of $stable (a dollar-pegged asset) to the reserve to mint \~100 cUSD. She then stakes her cUSD in exchange for \~100 stcUSD.
2. **Idle Asset Rewards**: The idle $stable in the reserve automatically accrues rewards. Rewards can come directly from the underlying of $stable, or via integrated protocols (e.g. Aave, Morpho), determined programmatically depending on the rates.
3. **Underwriter Delegations:**
   1. A Borrower identifies a yield opportunity exceeding Cap's 8% hurdle rate\* for $stable. In order to borrow, the Borrower must first secure overcollateralized delegations from a willing Underwriter.
   2. An Underwriter runs due diligence on the Borrower and decides to delegate $stake. Underwriters and Borrowers can optionally enter into a bilateral agreement to negotiate terms such as loan tenor and fixed underwriting premiums (risk premium).
4. **Borrower Draws**: The Borrower permissionlessly borrows $stable via Cap's smart contracts.

\*Note, 8% is an arbitrary number set as an example. The actual hurdle rate is a dynamic rate: for more information, refer to [Borrow Rates](#borrow-rates)

There are two possible paths the flow can go from here.

### Happy Path: Competitive Yield

Let us first examine the happy path, where the Borrower successfully repays the loan and the value of $stake remains stable.

5. **Generate Yield**: The Borrower executes the strategy and generates yield. The Borrower successfully repays the loan and the associated interest in $stable.
6. **Yield Distribution**: Yield is distributed across all stakeholders. If the Borrower generates 15% yield on the principal, then 8% (hurdle rate) flows back to the stcUSD holders. The fixed underwriting premium (assume 2%) is rewarded to the Underwriters, leaving the excess 5% as Borrower profit.

### Unhappy Path: Liquidation and Redistribution

In Cap, the liquidation logic is triggered by one objective condition: the Borrower's health factor (ratio of collateral to debt) drops below the liquidation threshold. This can happen if the value of the Underwriter's delegated collateral falls or the Borrower's debt grows beyond safe levels.

<figure><img src="/files/p8VCXIIoKxYVlfFJRsME" alt=""><figcaption><p>Unhappy Path: Stablecoin Holders are protected against yield-generation risk</p></figcaption></figure>

When such an event happens, the path forks from step 5.

5. **Liquidation Event**: The Borrower's health factor drops below the liquidation threshold. After a grace period, a liquidation window opens permissionlessly.
6. **Dutch Auction**: Underwriter collateral is auctioned via a descending-price Dutch auction. Liquidators repay a portion of the Borrower's debt in exchange for the Underwriters' $stake at a bonus. The bonus grows over time to incentivize faster liquidation. Liquidators liquidate up to the amount needed to restore the Borrower's position to a healthy state.
7. **Redistribution**: The collected $stable is redistributed back to Cap's reserve, ensuring stcUSD holders like Alice remain whole at all times.

## Borrow Rates

The borrow rate that the Borrower has to repay is a function of underwriting premium and hurdle rate.

The **underwriting premium** is the fixed annual premium negotiated with Underwriters.

The **hurdle rate** is a dynamic rate composed of a minimum rate and a utilization rate:

* **Minimum rate**: The greater of the benchmark rate (a protocol-set floor) and the market rate (current borrow rate from external protocols such as Aave). This ensures Cap's lending rates always exceed the base yield earned from idle capital in Fractional Reserves.
* **Utilization rate**: Adjusts via a piecewise linear function, escalating sharply when reserve utilization exceeds target thresholds.

Both rates are applied per reserve asset. This mechanism ensures that there is always liquidity available for withdrawals, while setting a competitive floor for the hurdle rate. For more detail, see [Interest Rates](/concepts/lender/interest-rates).


# Underwriting Mechanics

Cap is a decentralized credit marketplace that leverages Shared Security Networks (SSNs) such as Symbiotic and EigenLayer to separate yield generation from risk. Instead of relying on centralized decision making to allocate capital, Cap empowers independent Borrowers to draw funds and deploy them into yield-generating strategies. Borrowers are not trusted by default; rather, they must secure economic guarantees from Underwriters on SSNs who lock up capital as performance bonds. Hence, SSNs regulate the permissionless collective of Borrowers that generate yield for Lenders.

The result is a trustless, modular stablecoin architecture where risk is underwritten by the market, rather than being absorbed by users.

Let's dive deeper into how Cap utilizes SSNs.

## 1. Productivity-based Incentives

Cap builds on the idea of Shared Security Networks as a trust marketplace, extending the common usage as Proof of Stake (PoS) networks to a productivity based platform.

### Higher Borrower Agency

Traditionally, SSNs have focused on validation services. In these models, operators perform uniform validation tasks such as verifying signatures, and rewards are distributed passively and evenly amongst participants. Newer models, such as network wide insurance-based services, have been introduced, where Underwriters underwrite a generic event rather than individual Borrower risk. While serving a different usecase, these models also provide passive rewards.

Cap's approach to SSNs provides a more granular and productivity-based incentive structure. Cap enables Borrowers to take on more agency, such that they can be rewarded in proportion to their individual value creation. Borrowers participate in Cap based on their borrowing competitiveness, taking the delta between their returns and the hurdle rate. Accordingly, Underwriters opt in to a subset of the Borrowers by evaluating their strategies, rates and legal agreements.

### Customizable Rewards

Cap innovates reward mechanisms for Underwriters on SSNs. Underwriters are able to charge premiums based on the productivity and risk of the specific Borrowers they underwrite. The premium is paid out in blue chip assets like ETH and USD, rather than inflationary governance tokens or offchain points programs. This stands in stark contrast to the passive, network-wide reward structures of PoS services on SSNs.

### Defined Counterparty Risk

Cap's delegation model isolates risk, ensuring that liquidation events are tied directly to the Borrower in question, rather than affecting the entire network. As a result, contagion risk is minimized, and Underwriters gain greater control and visibility over their exposure.

More specifically, because all Borrowers in Cap are regulated financial entities, Underwriters can enter into legal agreements with their counterparties. Hence, the chance of an actual liquidation event is equivalent to these regulated entities filing for bankruptcy.

## 2. Credible Financial Guarantees

The key innovation in Cap is that stablecoin holders are fully protected from yield-related losses via credible financial guarantees. If a liquidation condition is triggered on a particular Borrower, the Underwriters backing that Borrower are liquidated. Rather than burning them, the liquidated funds are redistributed via the SSNs to cover the shortfall, ensuring cUSD remains fully collateralized at all times. Redistribution enables verifiable downside protection for end users via transparent code.

### Liquidation conditions

The liquidation conditions are automated, objective and verifiable onchain. Rather than relying on human judgement to penalize malicious behavior, Cap powers a robust, decentralized trust marketplace, where the recourse provision is fully transparent.

Liquidation conditions depend on the ratio of two variables: the amount of collateral (delegations) and debt. Liquidation is deterministically triggered when the health factor of the Borrower drops below the liquidation threshold.

Liquidation is executed instantly and permissionlessly. There are no delay periods nor veto committees for liquidations to occur, or for the liquidated funds to be redistributed. As a buffer, grace periods are introduced, where Borrowers are given a fixed time period to regain their position's health.

## 3. Unlocking Capital Efficiencies

Crypto lending markets require borrowers to overcollateralize their own position (post 1 USDC worth of ETH as collateral to borrow 0.7 USDC). While this design ensures the most verifiable form of credit, it is capital inefficient, especially when the borrowers themselves are the ones posting the collateral.

Cap improves on credit in DeFi via SSNs. Cap is able to achieve *insured* private credit via credit delegations backed by restaked crypto assets and off-chain legal agreements.

This model is possible because of the inherently different cost of capital between crypto-denominated delegation assets and the dollar-denominated reserve assets. There is a lack of use case for locked crypto assets — staked ETH on the Beacon chain can only be used as limited form of *trust.* As such, the cost of capital for using ETH as delegation is much cheaper than it would be on the dollar. The delegation model is not limited to ETH; rather, the market size of delegations easily extends to all crypto asset types such as BTC.


# Financial Guarantees

On why Cap makes sense

A **financial guarantee** is a binding agreement where an Underwriter guarantees repayment to the Lender, with the Borrower in turn guaranteeing repayment to both. If the Borrower defaults, the Underwriter covers the loss and retains recourse against the Borrower. This distinguishes financial guarantees from instruments like credit default swaps, where no such right of recovery exists.

Cap splits the guarantee across two layers:

* **Onchain**, the Underwriter escrows collateral to back a specific loan and receives the underwriting premium via smart contract. Default triggers immediate, automated liquidation of that collateral to make the Lender whole.
* **Offchain**, Underwriter and Borrower enter a legal agreement that provides the Underwriter's right of recovery against the Borrower after liquidation.

Each Underwriter has full agency over risk-reward. They assess Borrower credit, set LTVs per Borrower, and negotiate the premium bilaterally. Every guarantee is priced by the Underwriter who stands behind it — not by pooled governance or protocol-wide risk parameters.

The mechanism turns idle alternative assets — BTC, ETH, and tokenized RWAs (commodities, alternative currencies, stocks) — into productive underwriting capital. By backing USD-denominated loans with their holdings, Underwriters capture the yield differential between USD and their own asset class without selling the underlying.

**Excess Value**

To understand how Cap can generate value via financial guarantees, let us follow the cashflows on a single loan.

A Borrower pays the full unsecured USD borrow rate. That payment must cover two things: the Lender's required return (the USD risk-free rate) and the Underwriter's required return (the unsecured borrow rate for the alternative asset, since that is the opportunity cost of locking it up).

The surplus between these is **Excess Value**:

`Excess Value = USD unsecured borrow rate − USD risk-free rate − alternative asset unsecured borrow rate`

Because the escrowed collateral is unfunded — it backs the loan without being consumed — it continues to earn its native yield in parallel. Fiat reserves sweep into money-market funds; crypto assets stake on their L1. This adds a second layer of yield:

`Excess Value = USD unsecured borrow rate + alternative asset native yield − USD risk-free rate − alternative asset unsecured borrow rate`

How Excess Value is split among participants is **dynamically determined** based on market forces. Lenders earn the floating hurdle rate based on utilization rate. Each Underwriter earns the premium they individually charge the Borrower.

**Worked Example: ETH**

Consider a $100M USD loan backed by $200M of staked ETH collateral (200% overcollateralization) with hypothetical rates set below.&#x20;

| Parameter                  | Value |
| -------------------------- | ----- |
| USD unsecured borrow rate  | 10%   |
| USD risk-free rate         | 4%    |
| ETH unsecured lending rate | 4%    |
| ETH native (PoS) yield     | 3%    |

**Flow of cashflows**

```
$10M from Borrower
  ├─→ $4M  Lenders' minimum        (risk-free rate on $100M)
  ├─→ $2M  Underwriters' minimum   (4% target on $200M − 3% native PoS)
  └─→ $4M  Excess Value            (available for distribution)
```

Lenders earn the floating hurdle rate above the risk-free floor, Underwriters earn their negotiated premium above the 4% minimum, and the Excess Value is distributed by market pricing relative to the asset premium.

***


# Risks

Below are the main risks associated with Cap's current design.

**Smart Contract Risk:**

* Cap operates without reliance on third-party custodians, regulatory frameworks, or manual oversight. cUSD holder protections are enforced exclusively by code-based adherence to Cap’s protocol rules. While Cap’s smart contracts have been rigorously audits, users should be aware of risks associated with potential code vulnerabilities.

**Counterparty Risk:**

* **Shared Security Models**: Cap is built on Shared Security Marketplaces like EigenLayer. As such, it is exposed to their platform risk.
* **Reserve collateral**: If the reserve assets may depeg, users are exposed to these changes of price. Underlying reserve assets may also be frozen, seized or forfeited for illegal or sanctioned use.
* **Delegation collateral**: If the delegation assets are volatile (i.e. levered via looping), this increases the chance of liquidations as collateral value may fluctuate. Cap restricts the type of collateral to mitigate this risk.
* **Bridge and Oracle Risk**: If users wish to interact with cUSD from any chain, they will be exposed to the risk of third-party bridges that transfer cUSD from Ethereum and oracles that price cUSD.
* **Idle Asset Risk**: Idle assets may be deposited into integrated protocols such as Aave and Morpho until they are borrowed. As such, Cap is exposed to these protocol risks.

**cUSD Depeg Risk:**

* A force majeure event may create a mismatch between cUSD and the reserve assets. However, the highly liquid and regulated nature of the reserve assets render this unlikely.

**Protocol Risk:**

* **Redemption Risk:** Redemptions are guaranteed at all times for cUSD holders. However, redemptions may be delayed if the reserve assets are fully utilized. Dynamic interest rates prevent full utilization, always ensuring liquidity to be withdrawn atomically.
* **Liquidation Risk:** Malicious or undercollateralized Borrowers put Underwriters in liquidation risk. However, Underwriters can preemptively mitigate these risks, due to the fact that Borrowers in Cap are whitelisted regulated financial institutions.
  * **Guarantor Agreements**: Underwriters are able to sign off-chain Guarantor Agreements (GAs) with their counterparties. These agreements can specify recourse provision, loan terms, and strategy constraints. Hence, the chance of an actual liquidation event is equivalent to these regulated entities filing for bankruptcy.
  * **Quantifying risk**: It is possible to model the risk premium via credit spread. Particularly, the [return per unit of risk](https://docs.google.com/spreadsheets/d/1D04AYjvRBMCg09kSLpL8yN94tSrCpVhD8lBBT9037R4/edit?gid=0#gid=0) can be measured based on the institution's credit ratings, credit spread and default rates.


# FAQs

## Borrower Control and Restrictions

**Q: Do Borrowers have full control over collateral once disbursed?**

No. The Borrower set will be comprised of regulated financial institutions that enter into legal agreements with Underwriters to restrict permissible strategies.

**Q. How does the protocol ensure there are no risky strategies?**

Underwriters bear direct risk for their delegation choices. By underwriting specific Borrowers, Underwriters incentivize due diligence on strategy viability.

**Q: Are there caps on collateral lent to Borrowers?**

Yes. Delegation limits and nominal exposure thresholds prevent concentration risk.

## Loan Security

**Q: Does the protocol impose minimum overcollateralization requirements on Underwriters?**

Yes, the protocol checks the collateralization ratio before the Borrower can borrow. The overcollateralization ratio is different for every collateral asset and is set conservatively to prevent any unexpected liquidations.

**Q: Which assets are accepted as Underwriter collateral?**

Only blue-chip cryptocurrencies: ETH, wBTC, Liquid Staking Tokens (LSTs), and stablecoins.

**Q: What happens if Underwriters withdraw their delegation mid-loan?**

Underwriters and Borrowers should have a prior agreement when Underwriters intend to remove delegation for position unwinding. Unilateral withdrawals will trigger liquidation. Note that there is a built-in buffer period in Shared Security Networks to mitigate an accidental withdrawal.

## Shared Security Network

**Q. How is Liquidation and Redistribution used in Cap?**

Malicious or undercollateralized Borrowers are autonomously liquidated, where Underwriter delegations are redistributed back to the Lenders.

**Q. How does Cap differ from other restaking protocols?**

Cap replaces passive Proof-of-Stake rewards with productivity-based incentives:

* Underwriters earn premiums tied to Borrower performance.
* Borrowers retain surplus yield, fostering competitive strategy innovation.

Cap takes an innovative approach of rewarding Underwriters on a Borrower basis, where counterparty risk is established one-to-one with the Borrower they are underwriting. This architecture allows Borrowers to take more agency and be rewarded for their productivity.

For details, please check out our [Productivity Based Incentives](https://mirror.xyz/0x83c21bb4Bf0EC116f5a1945AaeF847Fe3b321B32/Xg7gCAqgmcxgXhuwaWXVJDSCEIMn3_dewNWAie2Tn_k) article.


# Tokenomics

### Overview

$CAP is the governance and utility token of Cap with a fixed supply of 10 billion tokens. Token holders can exercise decision-making rights over core protocol parameters. \
\
Revenue generated will be used to conduct discretionary buybacks

***

#### Governance Rights

$CAP holders vote on the following protocol decisions:

1. Adding/removing reserve assets for cUSD
2. Adding/removing collateral assets eligible for underwriting
3. Adjusting liquidation thresholds on approved collateral
4. Adjusting the minting fee on cUSD
5. Whitelisting institutions as borrowers in the private credit marketplace
6. Setting maximum coverage limits for financial guarantee surety exposure

***

#### Token Allocation

| Allocation            | % of Supply |
| --------------------- | ----------- |
| Ecosystem & Community | 47.97%      |
| Private Investors     | ≤20.00%     |
| Project Team          | ≤20.00%     |
| ICO                   | 5.00%       |
| Private TVL Deals     | 3.75%       |
| Echo Community Sale   | 3.28%       |

***

#### Supply Schedule

The circulating supply at TGE consists of the ICO allocation (5%) and 8% from the Ecosystem & Community allocation, summing up to roughly 13% of total supply.

Cliff-gated allocations (Private Investors, Project Team, Echo Community Sale) commence unlocking 12 months post-TGE.&#x20;


# Borrower Onboarding

The following outlines the process for onboarding as a Borrower to borrow from Cap.

### 1. Requirements

* **Borrower Address**: A valid Ethereum address (EOA or multisig) to borrow
* **Underwriter**: Entity that provides credit for the Borrower. The list of current Underwriters can be found [here](https://cap.app/underwriters).
* **Borrower Parameters:**
  * **Underwriting Premium**: Fixed premium rate on the amount borrowed
  * **LTV**: Loan-to-Value ratio for initial maximum borrow capacity
* **Protocol Whitelisting**: The Borrower must be whitelisted by the system.

{% hint style="info" %}
A Borrower may only use one Ethereum address per Underwriter and collateral pair. They must generate a new address for each new Underwriter. Addresses cannot be changed after deployment. For best practices, we recommend using a multisig for the address.
{% endhint %}

### 2. Register to a SSN of choice

Currently, Cap supports the following Shared Security Networks.

1. [Symbiotic](/guides/borrower-onboarding/symbiotic)
2. [EigenLayer](/guides/borrower-onboarding/eigenlayer)

### 3. Complete Legal Agreements (Optional)

Borrowers and Underwriters may enter into legal agreements outlining terms of delegation, responsibilities, and compliance.

### 4. Participate in Loan Activity

Once coverage is active from the respective shared security network, Borrowers can participate in borrowing activity directly from Cap's application using the Borrower's address.

Review the risk parameters prior to loan:

* **LTV (Loan-to-Value)**: Maximum borrowing capacity (e.g., 50% = 0.5e27)
* **Maximum Borrow Amount:** Coverage x LTV
* **Liquidation Threshold**: Health factor at which liquidation can occur (e.g., 80% = 0.8e27)
* **LTV Buffer**: Minimum gap between LTV and liquidation threshold (10% = 0.05e27)

{% hint style="info" %}
While the liquidation threshold is a global parameter, the LTV can be set by the Borrower-Underwriter Pair
{% endhint %}

### 5. Updating Parameters

To update the Underwriting Premium or the LTV for the Borrower, request a change to the Cap team.

Namely, the underwriting premium can be updated via the [setRestakerRate](https://github.com/cap-labs-dev/cap-contracts/blob/1064b6a969d55c822dcf0b2c4b733ceb4118737e/contracts/delegation/Delegation.sol) function, and the LTV and LT via the [modifyAgent](https://github.com/cap-labs-dev/cap-contracts/blob/1064b6a969d55c822dcf0b2c4b733ceb4118737e/contracts/delegation/Delegation.sol#L97) function.


# Symbiotic

The following is the onboarding guide for Borrowers (referred to as *agents* in Cap contracts). The onboarding flow has been mostly automated for the Borrower — the Underwriter can handle most bulk work when deploying an instance of the [Symbiotic Vault Factory](https://github.com/cap-labs-dev/cap-contracts/blob/1064b6a969d55c822dcf0b2c4b733ceb4118737e/contracts/delegation/providers/symbiotic/CapSymbioticVaultFactory.sol#L57) contract.

Steps:

1. Once agreements have been arranged with the Underwriter of choice, provide the Borrower's Ethereum address to the Underwriter. The Underwriter will then create the Borrower-specific Symbiotic Vault

* After receiving the delegations from the Underwriter, the Borrower is now eligible to borrow. Note, Delegations will take up to 6 days to be effective stake.

{% hint style="warning" %}
To isolate liquidation risk, each Borrower can only receive effective stake from one Symbiotic Vault. Delegations from other Vaults will not count as collateral. The Borrower must create a new address for each new Vault.
{% endhint %}

Under the hood, the following steps happen:

1. The Underwriter first creates a vault via the [CapSymbioticVaultFactory](https://github.com/cap-labs-dev/cap-contracts/blob/1064b6a969d55c822dcf0b2c4b733ceb4118737e/contracts/delegation/providers/symbiotic/CapSymbioticVaultFactory.sol#L57) including the Borrower address. The call will register the Borrower as SymbioticOperator in Symbiotic's [Operator Registry](https://docs.symbiotic.fi/modules/registries/#2-operatorregistry) and complete the [Operator to Network Opt-in](https://docs.symbiotic.fi/modules/registries/#2-operator-to-network-opt-in) and [Operator to Vault Opt-in](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/delegation/providers/symbiotic/SymbioticNetwork.sol#L77) process via the [SymbioticOperator](https://github.com/cap-labs-dev/cap-contracts/blob/1064b6a969d55c822dcf0b2c4b733ceb4118737e/contracts/delegation/providers/symbiotic/SymbioticOperator.sol#L4) contract.
2. Once the Vault is deployed, Cap will register the Borrower to Cap's system via the [addAgent](https://github.com/cap-labs-dev/cap-contracts/blob/1064b6a969d55c822dcf0b2c4b733ceb4118737e/contracts/interfaces/ISymbioticAgentManager.sol#L45) function. The function takes in the following struct:

```solidity
struct AgentConfig {
    address agent;
    address vault;
    address rewarder;
    uint256 ltv;
    uint256 liquidationThreshold;
    uint256 delegationRate;
}
```

This function will add the Borrower to the [Delegation](https://github.com/cap-labs-dev/cap-contracts/blob/1064b6a969d55c822dcf0b2c4b733ceb4118737e/contracts/delegation/Delegation.sol) contract, register the Vault and Borrower to Cap's [Symbiotic Network Middleware](https://github.com/cap-labs-dev/cap-contracts/blob/1064b6a969d55c822dcf0b2c4b733ceb4118737e/contracts/delegation/providers/symbiotic/SymbioticNetworkMiddleware.sol) and set underwriting premiums.

After stake becomes effective, Borrowers can start borrowing via the Asset page on Cap's app.

### Understanding Epochs

To be compatible with [Symbiotic's epoch model](https://docs.symbiotic.fi/learn/core-concepts/network#epoch), Cap has its own epoch system built. As a result, there are checkpoints where Cap reads stake from Symbiotic Vaults. The checkpoints are updated every 6 days, or when there is a borrow event.&#x20;

Functionally, this means that when Borrowers first receive stake, they cannot initiate a borrow until past the next Cap epoch.

### Verifying the Vault

Once the Symbiotic Vault is deployed, Borrowers may verify the deployment status via the [Delegation](https://etherscan.io/address/0x09A3976d8D63728d20DCDFEe1e531C206Ba91225#readProxyContract) and [Symbiotic Network](https://etherscan.io/address/0x98e52Ea7578F2088c152E81b17A9a459bF089f2a#readProxyContract) Contracts. Use the \_agent field to input the Borrower's Ethereum address.

* Delegation contract methods:
  * Collateral: delegation asset address
  * Coverage: how much stake is live
  * Vaults: the Borrower-Underwriter specific Symbiotic Vault
  * LTV: the max borrowable loan-to-value ratio
  * liquidationThreshold
* Symbiotic Network methods:
  * getOperator: get SymbioticOperator address for Borrower address

### Managing the Symbiotic App

Once registered, the Borrower profile will be updated in [Symbiotic's app page](https://app.symbiotic.fi/networks). If you wish to change the metadata on the app, please refer to [Symbiotic' guide](https://github.com/symbioticfi/metadata-mainnet) to do so.


# EigenLayer

The following is the onboarding guide for Borrowers (referred to as *agents* or *operators* in EigenLayer contracts) using EigenLayer. The process is fully automated by the Cap system via the [EigenAgentManager](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/delegation/providers/eigenlayer/EigenAgentManager.sol) contract.

### 1. Submit AgentConfig information to the Cap team

This is the only action a Borrower needs to take. The rest of the process is handled on behalf of the Borrower.

```solidity
struct AgentConfig {
    address agent;              // Your agent/borrower address
    address strategy;           // EigenLayer strategy address (e.g., wstETH)
    address restaker;           // Address of the Underwriter who will delegate to you
    string avsMetadata;         // AVS metadata URI (can be empty string)
    string operatorMetadata;    // Operator metadata URI (can be empty string)
    uint256 ltv;               // Loan-to-Value ratio (e.g., 5e26 for 50%)
    uint256 liquidationThreshold; // Liquidation threshold (e.g., 8e26 for 80%)
    uint256 delegationRate;    // Underwriting premium for rewards (e.g., 2e25 for 2%)
}
```

{% hint style="warning" %}
The Borrower must use a new address for each new Underwriter. The Borrower **cannot** delegate to other AVSs.
{% endhint %}

### 2. Agent Registration

Cap's Admin registers the Agent to both EigenLayer and Cap via the [addEigenAgent](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/delegation/providers/eigenlayer/EigenAgentManager.sol#L44) function.

Specifically, the function performs the following:

* Registers Agent to Cap via [addAgent](https://github.com/cap-labs-dev/cap-contracts/blob/1064b6a969d55c822dcf0b2c4b733ceb4118737e/contracts/interfaces/ISymbioticAgentManager.sol#L45) function in the Delegation contract
* Deploys [EigenOperator](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/delegation/providers/eigenlayer/EigenOperator.sol) beacon proxy which handles
  * Registering to EigenLayer's DelegationManager
  * Creating and registering to a new Operator Set in EigenLayer's AllocationManager
  * Setting Operator AVS reward split 0%
  * Allowlisting TOTP digest for Underwriters (valid for 28 days)

Once registered, you will have an EigenOperator proxy address and an Operator Set ID for the Cap AVS.

### 3. Allocation

After registration, there is a **17.5** day delay of the allocation configuration for EigenLayer. In this time, Underwriters cannot delegate to the EigenOperator and as such, Borrowers cannot participate in loan activities. This is because allocation magnitude cannot be updated prior to this allocation configuration delay period.

After the delay period, Borrowers can allocate delegated collateral to Cap so that it counts as effective coverage, via the permissionless [allocate](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/delegation/providers/eigenlayer/EigenServiceManager.sol#L227) function. Adding and withdrawing collateral after this time is now instant.

Once coverage is live, Borrowers can now borrow against delegated collateral.

{% hint style="info" %}
If allocation or delegation fails, make sure the TOTP is updated via the [advanceTotp](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/delegation/providers/eigenlayer/EigenOperator.sol#L105) function.
{% endhint %}

### 4. Verifying Information

* Agent Registration:
  * IDelegation(delegation).agentData(agent).network should return EigenServiceManager's address
* Agent Config:
  * IDelegation(delegation).agentData(agent)
* EigenOperator Deployment:
  * IEigenServiceManager(eigenServiceManager).getEigenOperator(agent)
  * Operator address:
    * IEigenOperator(eigenOperator).operator()
  * Underwriter address:
    * IEigenOperator(eigenOperator).restaker()
* Current Coverage:
  * IEigenServiceManager(eigenServiceManager).coverage(agent)
* TOTP expiry:
  * IEigenOperator(eigenOperator).getCurrentTotpExpiryTimestamp()

### 5. Updating Metadata

Borrowers can update operator metadata at any time via the [updateOperatorMetadataURI](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/delegation/providers/eigenlayer/EigenOperator.sol#L98). Please reach out to the Cap team if you are unable to host the metadata.


# Underwriter Onboarding

The following outlines the process for onboarding Underwriters to Shared Security Networks (SSNs) in Cap's protocol. Onboarded Underwriters may delegate to Borrowers approved in the Cap system.

### 1. Setup

Ensure you have:

* **Borrower Address:** Borrower's Ethereum address that will receive delegations. The Borrower must be already registered in Cap's system. The list of current Underwriters can be found [here](https://cap.app/borrowers).
* **Underwriting Premium**: Agree on a fixed rate to receive from the Borrower
* **Collateral Asset:** The asset to be used as vault collateral (ETH/BTC-denominated ERC20s)

### 2. Choose SSN

Select SSN of choice for delegations, and follow the corresponding onboarding guide to complete set up and start delegating.

1. [Symbiotic](/guides/underwriter-onboarding/symbiotic)
2. [EigenLayer](/guides/underwriter-onboarding/eigenlayer)

Collateral management, i.e. delegations and withdrawals, are handled within each SSN. Underwriters are advised to monitor delay periods specific to the SSN.

{% hint style="danger" %}
Withdrawing delegations immediately lowers coverage. Delegated assets in the withdrawal queue are liquidatable until the end of the withdrawal delay. Hence, a withdrawal below the liquidation threshold makes the delegation asset liquidatable.
{% endhint %}

While a time buffer is in place to mitigate risk, it is recommended that Underwriters whitelist depositors to prevent malicious/accidental withdrawals that may trigger unintended liquidation.

### 3. Complete Legal Agreements (Optional)

Borrowers and Underwriters may enter into legal agreements outlining terms of delegation, responsibilities, and compliance.

### 4. Updating Parameters

To update the Underwriting Premium or the LTV for the Borrower, request a change to the Cap team.

Namely, the underwriting premium can be updated via the [setRestakerRate](https://github.com/cap-labs-dev/cap-contracts/blob/1064b6a969d55c822dcf0b2c4b733ceb4118737e/contracts/delegation/Delegation.sol) function, and the LTV and LT via the [modifyAgent](https://github.com/cap-labs-dev/cap-contracts/blob/1064b6a969d55c822dcf0b2c4b733ceb4118737e/contracts/delegation/Delegation.sol#L97) function.


# Symbiotic

## Overview

Vault curators on Symbiotic can easily deploy Cap-specific Symbiotic Vaults using the [CapSymbioticVaultFactory](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/delegation/providers/symbiotic/CapSymbioticVaultFactory.sol) directly on the app's [Create Vault page](https://cap.app/delegators/create-vault). Cap must first approve the Vault before delegated stake can be used as collateral.

### 1. Deploy Symbiotic Vault

Curators can deploy Cap-specific Symbiotic Vaults either via

1. Cap's [UI](https://cap.app/underwriters/create-vault) for creating Vault
2. Vault Factory contract's [<kbd>createVault</kbd>](https://github.com/cap-labs-dev/cap-contracts/blob/1064b6a969d55c822dcf0b2c4b733ceb4118737e/contracts/delegation/providers/symbiotic/CapSymbioticVaultFactory.sol#L57) function on Etherscan.

As most of the parameters and modules are preconfigured, curators only have to specify the Borrower address and Collateral asset address when creating the Vault.

The factory contract will create Symbiotic's Delegator, Burner, Slasher and Rewarder modules as needed in Cap's Symbiotic Vault requirements.

```solidity
/// @param _owner The owner of the vault, will manage delegations and set deposit limits 
/// @param asset The asset of the vault 
/// @param _agent The agent of the vault 
/// @param _network The network of the vault 
/// @return vault The address of the new vault function 
createVault(address _owner, address asset, address _agent, address _network) external 
returns (address vault, address delegator, address burner, address slasher, address stakerRewards); 
```

{% hint style="info" %}
To ensure that all parameters are set-up as intended in Cap's design, Cap only accepts Vaults deployed via the factory contract.
{% endhint %}

Once deployed,the Borrower-Underwriter pair will be added to Cap's contracts via SymbioticAgentManager's [`addAgent`](https://github.com/cap-labs-dev/cap-contracts/blob/1064b6a969d55c822dcf0b2c4b733ceb4118737e/contracts/delegation/providers/symbiotic/SymbioticAgentManager.sol#L42) function. The function handles necessary registry of the Vault. Once this part is complete, Underwriters can start using delegated stake as collateral.

{% hint style="info" %}
Vault creation will revert if the Borrower's address is already receiving delegations from another Vault.
{% endhint %}

### 2. Admin Controls

Key administrative functions available to vault admins include configuring admin fees, setting deposit limits, and whitelisting depositors. Admin fees are taken from the rewards distributed to depositors, as a percentage of the total rewards.

```solidity
// Set an admin fee
stakerRewards.setAdminFee(adminFee);

// Enable/disable deposit limit
vault.setIsDepositLimit(status);

// Set deposit limit
vault.setDepositLimit(amount);

// Enable/disable deposit whitelist
vault.setDepositWhitelist(status);

// Add/remove whitelisted depositors
vault.setDepositorWhitelistStatus(depositor, status);
```

The Borrower-Underwriter pair may also update loan specific parameters; while the liquidation threshold is a global parameter, the LTV can be set by the pair.

### 3. Collecting Rewards

Rewards are automatically distributed to the StakerRewarder when a Borrower repays a loan.

Underwriters can also manually claim rewards via the <kbd>realizeRestakerInterest</kbd> function on the [Lender](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/lendingPool/Lender.sol) contract. The contract will distribute accrued interest on the Borrower's borrowed amount to the StakerRewarder.

Vault admins can claim the admin fee via the <kbd>claimAdminFee</kbd> function of the Symbiotic StakerRewards.

### 4. Managing Withdrawals

Withdrawals from Symbiotic Vaults take up to 2 epochs to process. Specifically, withdrawals start after the current epoch plus another epoch. Since an epoch is 7 days for Cap Symbiotic Vaults, it takes 8-14 days to complete the withdrawal.

While the withdrawal is processed, the delegation assets are still liquidatable. If the withdrawal triggers an unhealthy position for the Borrower, then the asset may be liquidated (after the grace period). It is thus crucial that the withdrawal amount does not affect the Borrower's position: it is best advised for Underwriters to coordinate the withdrawal with the Borrower.

### 5. Verifying the Vault

Once the Symbiotic Vault is deployed, Underwriters may verify the deployment status via the [Delegation](https://etherscan.io/address/0x09A3976d8D63728d20DCDFEe1e531C206Ba91225#readProxyContract) and [Symbiotic Network](https://etherscan.io/address/0x98e52Ea7578F2088c152E81b17A9a459bF089f2a#readProxyContract) Contracts. Use the \_agent field to input the Borrower's Ethereum address.

* Delegation contract methods:
  * Collateral: delegation asset address
  * Coverage: how much stake is live
  * Vaults: the Borrower-Underwriter specific Symbiotic Vault
  * LTV: the max borrowable loan-to-value ratio
  * liquidationThreshold
* Symbiotic Network methods:
  * getOperator: get SymbioticOperator address for Borrower address

### 6. Managing the Symbiotic App

Once registered, the Underwriter profile will be updated in [Symbiotic's app page](https://app.symbiotic.fi/networks). If you wish to change the metadata on the app, please refer to [Symbiotic' guide](https://github.com/symbioticfi/metadata-mainnet) to do so.


# EigenLayer

The following is the onboarding guide for Underwriters using EigenLayer. The process is fully automated by the Cap system via the [EigenAgentManager](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/delegation/providers/eigenlayer/EigenAgentManager.sol) contract.

### 1. Submit AgentConfig information to the Cap team

This is the only action the Underwriter needs to take. The step can be completed by the Borrower (Agent). The rest of the process is handled on behalf of the Underwriter.

```solidity
struct AgentConfig {
    address agent;              // Your agent/borrower address
    address strategy;           // EigenLayer strategy address (e.g., wstETH)
    address restaker;           // Address of the Underwriter who will delegate to you
    string avsMetadata;         // AVS metadata URI (can be empty string)
    string operatorMetadata;    // Operator metadata URI (can be empty string)
    uint256 ltv;               // Loan-to-Value ratio (e.g., 5e26 for 50%)
    uint256 liquidationThreshold; // Liquidation threshold (e.g., 8e26 for 80%)
    uint256 delegationRate;    // Underwriting premium for rewards (e.g., 2e25 for 2%)
}
```

If the strategy does not exist for the collateral asset, it can be permissionlessly created from the [StrategyFactory](https://github.com/Layr-Labs/eigenlayer-contracts/blob/8e25c9da31d5bb92de4fda14164d73911207aad5/src/contracts/strategies/StrategyFactory.sol#L4) contract.

{% hint style="warning" %}
The Underwriter cannot delegate to multiple Borrowers. A new address must be used to delegate to a new Borrower.
{% endhint %}

### 2. Agent Registration

Cap's Admin registers the Agent to both EigenLayer and Cap via the [addEigenAgent](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/delegation/providers/eigenlayer/EigenAgentManager.sol#L44) function.

Specifically, the function performs the following:

* Registers Agent to Cap via [addAgent](https://github.com/cap-labs-dev/cap-contracts/blob/1064b6a969d55c822dcf0b2c4b733ceb4118737e/contracts/interfaces/ISymbioticAgentManager.sol#L45) function in the Delegation contract
* Deploys [EigenOperator](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/delegation/providers/eigenlayer/EigenOperator.sol) beacon proxy which handles
  * Registering to EigenLayer's DelegationManager
  * Creating and registering to a new Operator Set in EigenLayer's AllocationManager
  * Setting Operator AVS reward split 0%
  * Allowlisting TOTP digest for Underwriters (valid for 28 days)

### 3. Deposit

At this time, Underwriters can deposit into the Strategy to receive strategy shares via the [depositIntoStrategy](https://github.com/Layr-Labs/eigenlayer-contracts/blob/8e25c9da31d5bb92de4fda14164d73911207aad5/src/contracts/core/StrategyManager.sol#L80) function.

### 4. Allocation Delay

After registration, there is a **17.5** day delay of the allocation configuration for EigenLayer. During this delay, any delegations will **not** count as effective collateral. This is because the allocation magnitude cannot be updated prior to this allocation configuration delay period.

After 17.5 days, Borrowers can allocate delegated collateral via the permissionless [allocate](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/delegation/providers/eigenlayer/EigenServiceManager.sol#L227) function.

### 5. Delegate

Once Borrowers allocate, strategy shares can be delegated to the EigenOperator via the [delegateTo](https://github.com/Layr-Labs/eigenlayer-contracts/blob/8e25c9da31d5bb92de4fda14164d73911207aad5/src/contracts/core/DelegationManager.sol#L124) function in the [DelegationManager](https://github.com/Layr-Labs/eigenlayer-contracts/blob/8e25c9da31d5bb92de4fda14164d73911207aad5/src/contracts/core/DelegationManager.sol) contract. Delegations will immediately count as effective coverage.

#### TOTP

When the addEigenAgent function is called, it creates a TOTP digest that is used when delegating to the EigenOperator. The TOTP delegation approval digest has an expiry timestamp that can be queried by the [getCurrentTotpExpiryTimestamp](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/delegation/providers/eigenlayer/EigenOperator.sol#L148) function. If expired, a new digest can be generated via the [advanceTotp](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/delegation/providers/eigenlayer/EigenOperator.sol#L105) function.

With live coverage, the onboarding process is complete. Borrowers can now borrow against delegated collateral.

### 6. Verifying Information

* Agent Registration:
  * IDelegation(delegation).agentData(agent).network should return EigenServiceManager's address
* Agent Config:
  * IDelegation(delegation).agentData(agent)
* EigenOperator Deployment:
  * IEigenServiceManager(eigenServiceManager).getEigenOperator(agent)
  * Operator address:
    * IEigenOperator(eigenOperator).operator()
  * Underwriter address:
    * IEigenOperator(eigenOperator).restaker()
* Current Coverage:
  * IEigenServiceManager(eigenServiceManager).coverage(agent)
* Delegated Shares:
  * IDelegationManager(delegationManager).getDepositedShares(restakerAddress)
* TOTP expiry:
  * IEigenOperator(eigenOperator).getCurrentTotpExpiryTimestamp()

### 7. Tracking Rewards

When the Borrower borrows, interest is paid and distributed to Underwriters through EigenLayer's RewardsCoordinator. Please refer to EigenLayer's [docs](https://docs.eigencloud.xyz/eigenlayer/concepts/rewards/rewards-claiming) to understand how rewards can be claimed.


# Overview

Cap consists of the six main modules:

1. [**Vault**](/concepts/vault): Stores reserve assets and issues cUSD tokens, providing liquidity to the Lender
2. [**Lender**](/concepts/lender): Handles borrowing, repayment, liquidation, and interest calculations
3. [**Fee Auction**](/concepts/fee-auction): Converts generated yield and fees to cUSD via a Dutch auction
4. [**Delegation**](/concepts/delegation): Connects to Shared Security Networks for delegated collateral, rewards, and liquidation
5. [**Oracles**](/concepts/oracles): Price oracles for asset valuation and rate oracles for interest calculations
6. [**Access Controls**](/concepts/access-controls): Function-level granular access controls across all protocol operations


# Vault

The Vault is the core module responsible for the storage, issuance, and redemption of cUSD, and for managing the underlying collateral assets. It plays a pivotal role as the protocol's liquidity backbone by enabling minting and burning of cUSD, facilitating liquidity for borrowers, and ensuring capital efficiency through the Fractional Reserves.

## Overview of operations

* **Mint/Burn:** cUSD can be minted/burned *\~1:1* for any of the supported backing assets. Fees are dynamically calculated according to the allocation ratio of the asset.
* **Redeem:** cUSD can be redeemed for a proportional amount of the basket of assets for a fixed fee.
* [**Fractional Reserves**](/concepts/vault/fractional-reserves): Idle capital in the Vault is deployed into yield-generating strategies (US T-bill yield, crypto lending markets) until borrowed or withdrawn.
* **Borrow/Repay**: When operators borrow/repays assets, the Vault tracks utilization of the pool

## Mechanics

### Mint/Burn

The Vault is the main interface for liquidity providers to mint and burn cUSD. cUSD can be minted/burned at Oracle value for any of the assets. Fees are calculated in the [Minter](/concepts/vault/minter) contract according to the allocation of the assets.

When a user mints/burns, the flow is as follows:

1. **User Call**: User calls `Vault.mint()` (or burn) with asset and amount
2. **Fee Calculation**: Vault calls `Minter.getMintAmount()` (or getBurnAmount) to calculate fees
3. **Logic Processing**: MinterLogic calculates amount out and fees at current Oracle value
4. **State Update**: VaultLogic handles asset transfer and state updates
5. **Token Minting**: cUSD is minted/burned in exchange for underlying assets

{% hint style="info" %}
Mint and burn functions are disabled if Oracle prices are stale, until Oracles are back in sync
{% endhint %}

### Redeem

A depeg event of any of the underlying assets can result in a last man standing problem where the last to withdraw will be left with the depegged asset. When such events occur, the protocol incentivizes users to withdraw proportionally to the ratio of the assets, effectively socializing the losses. Redeem fees are charged at a fixed percentage, whereas burn fees are dynamic, making it economically infeasible to burn for USDT beyond the burn kink ratio. For instance, if the basket contains 50% of USDC valued at $0.9 and 50% of USDT valued at $1, then the redeem request for $100 cUSD should withdraw $45 worth of USDC and $50 worth of USDT minus fees.

When a user redeems, the flow is as follows:

1. **User Call**: User calls `Vault.redeem()` with cUSD
2. **Fee Calculation**: Vault calls `Minter.getRedeemAmount()` to calculate proportional amounts
3. **State Update**: cUSD is burned from user, assets are divested from strategies
4. **Asset Transfer**: All underlying assets are transferred to user

### Borrow Operations

Registered Borrowers can borrow assets from the Vault via the Lender, provided they are sufficiently collateralized by delegated assets from shared security networks. While assets are lent out from the Vault, the core borrow logic is implemented in the Lender contract. When the Borrower requests to borrow via the Lender, the Lender calls `Vault.borrow()` to transfer assets to the Lender, updating the utilization and reserve state of the Vault.

Similarly, Borrowers can repay via the Lender, which transfers borrowed assets back to the Vault.

### Vault Management

Assets must first be whitelisted in order to be used in Cap. Only regulated dollar-denominated crypto assets are accepted as a backing assets.

Interest rates for borrowing each asset of the reserve is a function of the utilization rate of the asset. The [current utilization rate](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/vault/Vault.sol#L198) of each asset can be queried in the Vault contract.

### Configurations

* **Whitelist minters**: Supports fee bypass for privileged users
* **Pause mechanisms**:
  * Asset-Level Pause: Individual assets can be paused while others remain active
  * Protocol-Level Pause: Complete protocol shutdown capability

### Key Vault Parameters

* **Total Supplies**: Total amount of each asset deposited in the vault
* **Total Borrows**: Total amount of each asset borrowed from the vault
* **Utilization Rate**: Ratio of borrowed assets to total supplies, calculated as (Total Borrows / Total Supplies) \* 100%
* **Available Balance**: Amount of asset available for borrowing, calculated as Total Supplies - Total Borrows
* **Utilization Index**: Cumulative utilization tracking for interest rate calculations
* **Insurance Fund**: Address that receives fees from mint/burn operations
* **Pause States**: Individual asset pause states and protocol-wide pause capability

For function signatures, parameters, and data structures, see the [Vault Contract Reference](/developers/contracts/vault).


# Fractional Reserves

The [Fractional Reserve](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/vault/FractionalReserve.sol) contracts allows the Vault to deploy idle capital into yield-generating strategies per reserve asset. In order to ensure sufficient liquidity for immediate withdrawals and redemptions, a fixed amount of capital can be set in the buffer reserve.

The yield strategies are restricted to safe, verifiable sources such as direct revenue sharing from the reserve assets, or deployment into leading crypto lending markets such as Aave. This design ensures capital efficiency of the underlying assets while maintaining redeemability guarantees and reserve transparency.

[Gelato](https://www.gelato.cloud/web3-functions) is used in Cap's Fractional Reserve system to automate capital deployment, yield harvesting, and fee distribution.

## Mechanics

<figure><img src="/files/lrvScKJp6AelrjZ8fsYR" alt=""><figcaption><p>Flow of Funds: Fractional Reserves</p></figcaption></figure>

The flow is as follows:

1. cUSD minters deposit assets into Cap Vault
2. Excess capital is invested into Fractional Reserve Vaults, where TokenHolder strategies will generate passive yield
3. Accrued yield is sent to the Fee Auction, sold for cUSD
4. The cUSD is transferred to the Fee Receiver which then periodically distributes rewards to stcUSD holders

Fractional Reserve Vault & Gelato Mechanics

* A ERC4626 [TokenHolder](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/fractionalReserve/TokenHolder.sol) Fractional Reserve Vault is deployed for the underlying reserve asset (i.e. USDC) that acts as a strategy following Yearn V3's [Tokenized Strategy](https://github.com/yearn/tokenized-strategy) pattern. (i.e. lending to Aave V3). Only the Fractional Reserve Vault can deposit and withdraw from the Vault.
* The [CapSweeper](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/gelato/CapSweeper.sol) contract automatically invests excess assets every 6 hours
* The strategy earns interest in the asset supplied until divested. [CapInterestHarvester](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/gelato/CapInterestHarvester.sol) is used to automate yield harvesting from the fractional reserve strategies to the Fee Auction

### Key Fractional Reserve Parameters

* **Reserve Level**: Minimum amount of each asset to keep in the vault (not invested)
* **Loaned Amount**: Total amount of each asset currently invested in fractional reserve strategies
* **Interest Receiver**: Address that receives realized interest (set to [Fee Auction](/concepts/fee-auction))
* **Claimable Interest**: Amount of interest available to be realized from strategies
* **Investment Threshold**: Minimum amount required to invest in strategies

For function signatures, parameters, and data structures, see the [Fractional Reserve Contract Reference](/developers/contracts/fractional-reserve).

## Assets and corresponding strategies

Currently, USDC is supported with the [Aave V3 lending strategy](https://github.com/cap-labs-dev/tokenized-aave-v3). More assets and strategies coming soon!


# Minter

The [Minter](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/vault/Minter.sol) contract handles fee calculation for the minting, burning and redeeming of cUSD, where dynamic fees are used to maintain the exposure of each backing asset to its optimal level. A balanced reserve composition reduces centralization of a particular asset, ensuring that the protocol is resilient to black swan events. Fees accrue to the treasury used for protocol safety.

## Mechanics

### Dynamic Mint/Burn Fees

Fees are dynamically adjusted according to the exposure of assets in the system. For each asset, there are two predefined piecewise linear functions that determine the mint/burn fees. Fees increase as they deviate from the optimal ratio, increasingly sharply beyond the kink ratio.

#### **Process Flow for Mint/Burn**:

1. **Pre-fee Calculation**: Calls `_amountOutBeforeFee` to get base amount and new ratio
2. **Whitelist Check**: Bypasses fees if user is whitelisted
3. **Fee Application**: Applies dynamic fees using `_applyFeeSlopes` if not whitelisted

#### Fee Calculations

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

The fee system operates across three distinct zones based on where the current ratio is:

1. `current ratio <= optimal ratio`: Minimum mint fee to mint, 0 fees for burn
2. `optimal ratio < current ratio < kinkRatio`: Linear fee increase using the first slope
3. `current ratio > kinkRatio`: Steep fee increase using the second slope

Price Oracles are used for fee calculations and amount determination. A minimum mint fee is placed to prevent manipulation around price oracles.

The current minimum mint fee is set to 10bps, capped to 5% maximum.

#### Fee Parameters

| Parameter       | Description              | Validation       |
| --------------- | ------------------------ | ---------------- |
| `minMintFee`    | Base fee for minting     | ≤ 0.05e27 (5%)   |
| `optimalRatio`  | Target allocation ratio  | 0 < ratio < 1e27 |
| `mintKinkRatio` | Mint fee kink point      | 0 < ratio < 1e27 |
| `burnKinkRatio` | Burn fee kink point      | 0 < ratio < 1e27 |
| `slope0`        | First slope coefficient  |                  |
| `slope1`        | Second slope coefficient |                  |

### Redeem Fees

Redeem fees in Cap Protocol are simple percentage-based fees applied to proportional redemption operations. Same fees apply to all assets proportional to their asset allocation. Whitelisted users pay 0% redeem fee

**Process Flow for Redeem**:

1. **Fee Determination**: Sets redeem fee (0 if whitelisted)
2. **Share Calculation**: Calculates user's share of total supply
3. **Amount Calculation**: Calculates proportional amount for each asset
4. **Fee Application**: Applies redeem fee to each asset amount

### Whitelisting

Whitelisted users can bypass fees for larger scale operations. Whitelist management is restricted to authorized admins.

For function signatures, parameters, and data structures, see the [Minter Contract Reference](/developers/contracts/minter).


# Lender

The Lender module manages the borrowing, repayment, and liquidation processes for Borrowers.

## Overview of Operations

1. [**Borrow/Repay**](/concepts/lender/borrow): Borrowers can borrow reserve assets against their delegated collateral
2. [**Liquidation**](/concepts/lender/liquidation): Multi-stage liquidation with grace periods and bonus incentives
3. [**Interest Rate Calculation**](/concepts/lender/interest-rates)**:** Dynamic protocol rates and fixed Borrower-specific rates

## Key Loan Parameters

* **Total Delegation:** Total amount of a Borrower's received collateral from Underwriters
* **Total Liquidatable Collateral:** Total amount of collateral that can be liquidated from an Underwriter
* **Total Debt:** A Borrower's total debt denominated in USD. Interest accrues to Total Debt
* **Initial Loan-to-Value (LTV):** The maximum amount a Borrower can borrow relative to the Total Liquidatable Collateral. A 50% LTV implies that delegations must be at least 2x the size of the borrow.
* **Current LTV**: A Borrower's current Loan-to-Value ratio, calculated as
  * (Total Debt / Total Delegation) \* 100%
* **Liquidation Threshold**: Threshold that determines when a Borrower's position becomes liquidatable in LTV ratio. By default, the threshold is set to 80%.
* **Health Factor**: Represents a Borrower's loan health
  * Calculated as (Total Delegation \* Liquidation Threshold) / Total Debt
  * Liquidations are triggered when health factor is below 1
* **Grace Period**: Period for Borrowers to recover health before liquidation can occur. Set to 12 hours
* **Expiry Period:** Period after which liquidation rights expire. Set to 3 days
* **Target health:** Target health factor that Liquidators aim to achieve when liquidating a Borrower. Set to 125%
* **Bonus Cap:** Maximum bonus for Liquidators, set to 10%.

## Debt Management

The protocol has two distinct types of interest:

1. **Vault Interest**: Interest paid to the Vault (stcUSD holders)
2. **Underwriting Premium**: Premium paid to Underwriters who provide collateral coverage

Debt in Cap is managed via Debt tokens. Debt tokens are non-transferrable ERC-20 tokens that track a Borrower's debt. Debt tokens are minted when a Borrower borrows, and burned when the debt is repaid.

Interest automatically accrues to the debt token per asset, where the interest rate is calculated based on the [interest rate mechanism](/concepts/lender/interest-rates). Accrued interest is handled automatically via index-based scaling, inherited from the ScaledToken base class.

For function signatures, parameters, and data structures, see the [Lender Contract Reference](/developers/contracts/lender).


# Borrow

Whitelisted Borrowers in Cap can borrow and repay the underlying Vault assets, provided they have secured sufficient collateral from an Underwriter.

## Mechanics

### Borrow

The borrow process flow is as follows:

1. Borrower calls the <kbd>borrow</kbd> function on the Lender contract
2. Underwriter interest is realized first to prevent charging for compounded interest
3. If eligible to borrow\*, assets are lent out from the Vault to the Borrower
4. Debt tokens are minted to track the loan

**Key Validations\***:

* Borrower must have sufficient collateral and health factor
* Borrower must be whitelisted and not paused
* Asset being borrowed should not be paused
* Borrow amount must meet minimum requirements, and may not exceed maximum borrows
  * Minimum: set by Admin via the setMinBorrow function
  * Maximum: The smaller value of the Borrower's remaining borrow capacity, and the remaining available amount to be borrowed from the Vault

### Repay

The repay process flow is as follows:

1. Borrower calls the <kbd>repay</kbd> function on the Lender contract
2. Underwriter interest is realized first to ensure all interest is accounted for
3. The repayment is processed in the following order:
   1. Unrealized underwriting premium (if any)
   2. Vault principal debt
   3. Vault interest
4. Debt tokens corresponding to the total amount repaid are burned

{% hint style="info" %}
If the repayment is not in full, the system will maintain a minimum borrow amount for the Borrower.
{% endhint %}

#### Interest Distribution

The repaid assets are distributed back to the shareholders:

1. **Vault Principal**: Sent to [Vault](/concepts/vault) contract's reserves
2. **Vault Interest**: Sent to `interestReceiver` (set to [Fee Auction](/concepts/fee-auction)). Any excess interest will go to the interest receiver
3. **Underwriting Premium**: Sent to [Delegation](/concepts/delegation) contract for distribution to Underwriters
4. **Unrealized Interest**: Added to Borrower's debt balance for future repayment

### Realizing Interest

Interest in Cap is **accrued continuously** but **realized discretely**:

* **Accrued Interest**: Calculated in real-time based on elapsed time and rates
* **Realized Interest**: Actually borrowed from vault and distributed to recipients
* **Unrealized Interest**: Accrued but not yet realized due to vault liquidity constraints

As can be seen, interest can be realized prior to repayment in Cap. Both stcUSD holders and Underwriters may permissionlessly realize interest by borrowing from the vault and distributing to interest receivers.

There are two functions to realize interest: <kbd>realizeInterest</kbd> and <kbd>realizeRestakerInterest</kbd>

RealizeInterest:

* Realizes vault interest (interest paid to the stcUSD holders)
* Interest is borrowed from the Vault and paid to the interest receiver

RealizeRestakerInterest:

* Realizes underwriting premium (premium paid to Underwriters)
* Interest is borrowed from the Vault and paid to the Delegation contract

For both functions, the process flow is as follows:

1. Determine available interest to realize
2. Increase reserve debt
3. Borrow interest amount from Vault
4. Transfer assets to recipient

For function signatures, parameters, and data structures, see the [Lender Contract Reference](/developers/contracts/lender).


# Liquidation

When a Borrower's health factor drops below 1, Liquidators can purchase the Borrower's delegated collateral by repaying their outstanding debt via a Dutch auction.

## Mechanics

The liquidation process is as follows:

1. The liquidation process kicks off with a Liquidator calling the <kbd>openLiquidation</kbd> function
2. After the liquidation has been initiated, the Borrower has a grace period of 12 hours to improve the health of the loan
   1. If the current Loan-to-Value exceeds the emergency liquidation threshold, the grace period is overridden, opening the liquidation window immediately.
3. At the end of the grace period, Liquidators can call the <kbd>liquidate</kbd> function until the expiry of the liquidation period. Liquidations will expire in 3 days after the end of the grace period.
4. A successful liquidation will execute a repayment of the borrowed asset, reducing the debt by the liquidation amount
5. The liquidated amount and the liquidation bonus is taken from the Underwriter's collateral via the Shared Security Network, transferring the collateral to the Liquidator
6. The liquidation window will close once the Borrower's health is recovered, i.e. health factor over 1

#### Liquidation Threshold

By default, the liquidation threshold is set to 80% LTV. If the current LTV exceeds the liquidation threshold, then a liquidation may be opened. An emergency liquidation mechanism is used to override the grace period if the health factor drops significantly. The emergency liquidation threshold is set to 90% LTV.

#### Liquidation Expiry

All liquidations have an expiry window. The window ensures that once the position becomes healthy again, the Borrower will no longer be liquidatable. Hence, if the Borrower were to be liquidatable again, the Borrower will be ensured another grace period. If the Borrower's health factor is below 1 after the expiry, any Liquidator can initiate the liquidation process again.

#### Target Health and Maximum Liquidatable Amount

Target health is a health factor threshold that defines the desired health level that liquidations should restore a Borrower's position to, currently set at 1.25. It acts as a "safety buffer" that liquidations aim to achieve.

The target health defines the maximum liquidatable amount. The maximum liquidatable amount is calculated as:

<kbd>((Target Health \* Total Debt) - (Total Delegation \* Liquidation Threshold)) / ((Target Health - Liquidation Threshold) \* Asset Price)</kbd>

In other words, the goal is for the health factor, i.e.

<kbd>Total Delegation \* Liquidation Threshold / (TotalDebt - Liquidated Amount)</kbd>

to be equal to the target health factor.

#### Liquidation Bonus (Dutch Auction)

The protocol implements a dynamic liquidation bonus system via a descending-price Dutch auction that provides incentives for Liquidators. The liquidation bonus rises linearly with time from the grace period until the maximum amount reaches the bonus cap. At expiry, Liquidators earn the maximum bonus which is set to 10%. If an emergency liquidation is triggered, Liquidators bypass the grace period, immediately earning the maximum bonus.

Note that the bonus is only available when the total delegation exceeds total debt. The protocol prevents over-liquidating beyond available collateral.

For function signatures, parameters, and data structures, see the [Lender Contract Reference](/developers/contracts/lender).


# Interest Rates

Cap's interest rate mechanism consists of benchmark rates, market rates, utilization rates, and underwriting premiums to ensure sustainable lending operations and proper compensation for risk-takers.

## Mechanics

The total interest that Borrowers pay can be summarized as:

* Minimum Rate + Utilization Rate + Underwriting Premium

where Minimum Rate is the maximum of the Benchmark Rate and the Market Rate

Each rate is defined as below:

1. **Benchmark Rate**: Minimum interest rates set by protocol, in fixed yearly rates
2. **Market Rate**: Dynamic rates fetched from external lending markets and oracles
3. **Utilization Rate**: Dynamic rates based on asset utilization. Calculated via a piecewise linear function and a rate multiplier
4. **Underwriting Premium**: Fixed annual rate paid by the Borrower to the Underwriter backing them

<figure><img src="/files/6MtiONM3JEioNgtMfRbB" alt=""><figcaption></figcaption></figure>

Let us breakdown the interest formula:

#### 1. Minimum rate

The minimum rate represents a lower bound on the interest rates, and is determined by the greater value of the preconfigured benchmark rate and the market rate. The rationale for the minimum rate stems from the Fractional Reserves. Because Cap earns base yield from the underlying idle assets, Borrowers must be able to provide more than the base yield in order to borrow from Cap.

For instance, if the benchmark yield set by Cap is set at 5%, and the current strategy for the Fractional Reserve of USDC is AAVE v3 earning rate is 4.5%, the minimum rate would be 5%. The minimum rate ensures that Cap's interest rates are greater than the base yield at all times.

#### 2. Utilization Rate

The utilization rate is a function of a piecewise linear-kink model and a rate multiplier.

The piecewise function allows interest rates to rise linearly along the first slope up to the "kink", or target utilization rate, set at 90%, after which rates increase rapidly along the second slope. This allows short term modulation of the rate where borrowing is disincentivized beyond the kink.

The rate multiplier is determined by the deviation of the current utilization rate from the target rate. If the current rate is far below the target rate, and remains there for a long period, then it must be the case that the current target rate is too high to incentivize borrowing. Based on the elapsed duration of the deviation and the degree of the difference between the current utilization rate and target, the multiplier will decrease the current rate, effectively shifting the piecewise function down. This control allows for a longer term modulation, optimizing Cap's interest rates in an autonomous way.

#### 3. Underwriting Premium

The underwriting premium is the fixed yearly rate that Borrowers pay to the Underwriters that back them. The rate is determined as a bilateral agreement between the Borrower and Underwriter, and is unique per each pair.

Putting the pieces together, when calculating the interest rate, the system does the following:

1. Fetch market rate from oracle
2. Compare with benchmark rate (minimum floor)
3. Use higher of market or benchmark rate
4. Add utilization rate on top

For function signatures, parameters, and data structures, see the [Lender Contract Reference](/developers/contracts/lender) and [Oracle Contract Reference](/developers/contracts/oracle).


# Fee Auction

The [Fee Auction](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/feeAuction/FeeAuction.sol) facilitates permissionless Dutch auctions where collected protocol fees are sold to the winning bidder. The proceeds are sent to the [Fee Receiver](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/feeReceiver/FeeReceiver.sol) to convert to cUSD and distribute to stcUSD holders. The Fee Auction module provides an efficient and transparent way to distribute accumulated fees to participants while ensuring fair price discovery through time-based price decay.

## Mechanics

### Overview of Operations

1. **Interest Harvesting**: Interest Harvester realizes accumulated interest and sends it to Fee Auction
2. **Dutch Auction**: Fee Auction sells accumulated assets via a Dutch auction mechanism
3. **Fee Distribution**: Fee Receiver collects cUSD from auction sales and distributes to stcUSD token holders

<figure><img src="/files/JYkphMO0T9MyY0OeVC4H" alt=""><figcaption><p>Flow of Funds: Fee Auction</p></figcaption></figure>

### Dutch Auction Mechanics

* Admin sets a starting price (in cUSD) and the duration of auctions.
* The price decays linearly over time (up to 90% or until minimum price is reached) until a winning bidder makes a purchase. All accumulated fees are distributed to the buyer.
* The purchase triggers the start of the next auction, where the starting price is set to be double the settled price.

### Key Auction Parameters

* **Starting Price**: Initial price set by admin for each auction (in cUSD)
* **Duration**: Auction duration set to 24 hours by default
* **Minimum Start Price**: Minimum starting price set to 100 cUSD
* **Payment Token**: cUSD is used as the payment token for all auctions
* **Recipient**: Fee Receiver contract receives all auction proceeds
* **Price Multiplier**: New auction starts at 2x the settled price of the previous auction

For function signatures, parameters, and data structures, see the [Fee Auction Contract Reference](/developers/contracts/fee-auction).


# Delegation

The Delegation module serves as a middleware between Shared Security Networks and the credit marketplace, enabling:

* **Borrower Management:** Register SSN Borrowers to Cap, setting their LTV/liquidation ratio and underwriting premiums
* **Collateral Provision**: Underwriters can collateralize Borrowers for borrow capacity
* **Liquidation:** Execute liquidation of delegated collateral during liquidation events
* **Reward Distribution**: Distribute rewards to Underwriters proportionally to their coverage

Cap supports multiple SSNs. Currently integrated SSNs include:

* [Symbiotic](/concepts/delegation/symbiotic)
* EigenLayer

## Overview of Cap SSN Requirements

### Isolated Coverage

Delegation coverage is isolated both on a Network level and a Borrower level. Each Borrower is uniquely mapped to a SSN and an Underwriter. Each Borrower receives unique, isolated stake from a single Underwriter such that liquidation risk is not shared.

Network level isolation means that delegations cannot be "restaked" across multiple slashable restaking protocols — because delegations are used as collateral in Cap, a liquidation event in a different Network other than Cap would result in bad debt for Cap.

Similarly, restaking across multiple Borrowers within Cap is not allowed. When multiple Underwriters are delegating to the same Borrower, a withdrawal from one of the Underwriters can result in a liquidation event for other Underwriters.

In other words, an Underwriter's stake can only be used for a single Borrower in Cap. Alternatively, a Borrower cannot receive stake from multiple Underwriters. If the Borrower were to receive delegations from a new Underwriter, then the Borrower can simply create a new Ethereum address to do so.

### Underwriting Premiums

Each Borrower-Underwriter pair agrees upon a predetermined premium for underwriting. As such, each Borrower-Underwriter pair has a unique fixed yearly rate that can be updated via Admin controls. The rates are set in the respective SSN upon deployment.

Underwriting premiums are distributed per each SSN's reward handler.

### Liquidation and Redistribution

Liquidation conditions in Cap are objective: they are triggered when the health factor of the Borrower drops below the liquidation threshold. Liquidations can be called permissionlessly, and verified onchain. Liquidated funds are redistributed back to the Liquidators, ensuring cUSD is always backed 1:1.

To incentivize liquidations, liquidation and redistribution happen instantly. There are no veto committees nor delay periods for liquidations to occur, nor for the liquidated funds to be redistributed.

### Whitelisted Participants

While the protocol can function fully autonomously, to prevent malicious actors from attacking the protocol, Cap will initially whitelist Underwriters and Borrowers. Underwriters and Borrowers that seek to join the protocol should refer to the Onboarding Guides to do so.

For function signatures, parameters, and data structures, see the [Delegation Contract Reference](/developers/contracts/delegation).


# Symbiotic

Cap integrates Symbiotic's restaking infrastructure to create a delegation-based credit system where Underwriters provide coverage for Borrowers (agents). Underwriters can deploy Vaults to provide stake to Borrowers as coverage, where depositors can deposit and withdraw out of the vault.

## Overview

### Vault to Borrower relationship

* **Unique Borrower-Vault pair**: Each Symbiotic Vault can only delegate to one Borrower. A Borrower must use a different Ethereum address in order to receive delegations from a new Vault. Likewise, an Underwriter must deploy a new Vault in order to delegate to a new Borrower.
* Once a Borrower starts receiving delegations, the Borrower address is immutable.

### Lifecycle of Symbiotic Vault

1. Vault Creation: Underwriter creates a Vault via the Cap Symbiotic Vault Factory Contract, which accepts one ERC20 collateral type
2. Cap Whitelisting: After the Vault is deployed, Cap adds the Vault and Borrower address to Cap system along with loan parameters and underwriting premiums
3. Vault Management: Once the Borrower-Vault pair is live, Underwriters have access control over depositors and claiming rewards. Liquidations will trigger liquidation events on the Vault.

Let's dive deeper into each of the steps.

## Vault Creation

### Deploy Vault

<figure><img src="/files/JYkphMO0T9MyY0OeVC4H" alt=""><figcaption><p>Cap Symbiotic Vault creation and deposit flow</p></figcaption></figure>

The `createVault` function is the core deployment function in the `CapSymbioticVaultFactory` contract.

{% hint style="info" %}
Vaults that are not registered using the Vault Factory will not be accepted in Cap's system.
{% endhint %}

```solidity
function createVault(
    address _owner,      // Vault owner/admin address
    address _asset,      // Collateral asset address (e.g., wstETH)
    address _agent,      // Agent address for delegation coverage
    address _network     // Cap Symbiotic network address
) external returns (
    address vault,       // Deployed vault contract address
    address delegator,   // Deployed delegator contract address
    address burner,      // Deployed burner router address
    address slasher,     // Deployed slasher contract address
    address stakerRewards // Deployed staker rewards contract address
);
```

Specifically, the function executes the following:

1. deploys a Symbiotic operator for the specified agent to manage delegation
2. deploys Symbiotic Vault and modules according to Cap requirements

Let us dive deeper into the specific requirements of Cap Symbiotic Vaults.

#### 1. Burner

The burner specifies where assets are transferred to when liquidation happens.

A burner router is deployed to set the receiver of the liquidated assets. By setting the owner of the router to be the zero address, the global receiver is immutably set to be Cap's middleware.

```solidity
function _deployBurner(address _collateral) internal returns (address) {
    return burnerRouterFactory.create(
        IBurnerRouter.InitParams({
            owner: address(0),                    // No owner (immutable)
            collateral: _collateral,              // Asset to burn
            delay: 0,                            // Instant execution
            globalReceiver: middleware,          // Cap network middleware
            networkReceivers: new IBurnerRouter.NetworkReceiver[](0),
            operatorNetworkReceivers: new IBurnerRouter.OperatorNetworkReceiver[](0)
        })
    );
}
```

#### 2. Delegator

The Delegator module specifies whether restaking is allowed across networks and Borrowers, and the allocation of assets.

Cap requires that staked assets are solely used as coverage for the specific Borrower receiving delegations (i.e. stake cannot be shared with other networks/Borrowers). As such, the [OperatorNetworkSpecificDelegator](https://docs.symbiotic.fi/modules/vault/delegation#3-operatornetworkspecificdelegator) is used to ensure delegations are siloed. Each vault can only delegate to one Borrower.

#### 3. Slasher

The Slasher handles slash requests from Cap’s middleware, by fetching stake from the Delegator and calling the Vault to transfer the assets to the Burner.

The vault uses `INSTANT` slasher type for immediate slash execution, so that liquidation bonuses can be redistributed immediately.

#### 4. Vault

The Vault handles deposit and withdrawals on an epoch basis. Assets leave Symbiotic vaults only when there is a withdrawal or liquidation event.

Deposits are instant. Withdrawals take until the end of the next epoch to withdraw. The epoch is fixed to 7 days, hence withdrawals take up to 14 days to execute.

#### 5. Staker Rewards

The stakerRewards contract creates a rewards contract to distribute underwriting premiums to Underwriters

```solidity
stakerRewards = defaultStakerRewardsFactory.create(
    IDefaultStakerRewards.InitParams({
        vault: vault,
        adminFee: 0,                             // Initial admin fee (0%)
        defaultAdminRoleHolder: _owner,          // Vault owner gets admin role
        adminFeeClaimRoleHolder: _owner,         // Vault wner can claim admin fees
        adminFeeSetRoleHolder: _owner            // Vault owner can set admin fees
    })
);
```

### Whitelisting

After the Vault is created, the Vault needs to be added to Cap's system.

Cap whitelists the Borrower-Vault pair via the addAgent function in the [SymbioticAgentManager](https://github.com/cap-labs-dev/cap-contracts/blob/1064b6a969d55c822dcf0b2c4b733ceb4118737e/contracts/delegation/providers/symbiotic/SymbioticAgentManager.sol#L42) contract. The contract acts as a bridge between the Cap delegation system and the Symbiotic restaking infrastructure, ensuring proper registration and configuration of Borrowers in the system.

The function takes in the following parameters:

```solidity
struct AgentConfig {
    address agent;              // Agent address (operator)
    address vault;              // Associated Symbiotic vault
    address rewarder;           // Staker rewards contract
    uint256 ltv;                // Loan-to-value ratio (e.g., 0.5e27 = 50%)
    uint256 liquidationThreshold; // Liquidation threshold (e.g., 0.7e27 = 70%)
    uint256 delegationRate;     // Underwriting premium for interest calculation
}
```

First, the pair is added to the delegation contract, configuring loan parameters such as LTV and LT. By default, LTV is set to 50%, with the liquidation threshold at 80%. Notice, the underwriting premium is also configured in this step.

```solidity
// Add agent to delegation contract
IDelegation(delegation).addAgent(
    agentAddress,    // Agent address
    middleware,      // Network middleware
    0.5e27,         // LTV (50%)
    0.8e27          // Liquidation threshold (80%)
);
```

Next, the Vault, rewarder and agent are registered to Cap's Middleware, completing necessary Symbiotic Opt In processes.

```solidity
ISymbioticNetworkMiddleware($.networkMiddleware).registerVault(
    _agentConfig.vault,
    _agentConfig.rewarder,
    _agentConfig.agent
);
```

In particular, a subnetwork identifier is created for the Borrower. The identifier is used to enforce the one-to-one relationship between the Borrower and the vault. Delegations to other subnetwork identifiers will not count as effective stake.

## Vault Management

### Rewards

**`distributeRewards`**: Distributes rewards through Symbiotic network

```solidity
function distributeRewards(address _agent, address _token) external checkAccess(this.distributeRewards.selector)
```

* `_agent`: Agent address for reward distribution
* `_token`: Reward token address

### Liquidation

**`slash`**: Executes liquidation through Symbiotic network via the Burner

```solidity
function slash(address _agent, address _recipient, uint256 _slashShare, uint48 _timestamp) external checkAccess(this.slash.selector)
```

* `_agent`: Borrower address to liquidate
* `_recipient`: Address to receive liquidated collateral
* `_slashShare`: Share of collateral to liquidate
* `_timestamp`: Timestamp for liquidation

### Admin Control

* `DEFAULT_ADMIN_ROLE` - Full vault management
* `DEPOSIT_WHITELIST_SET_ROLE` - Manage deposit whitelist
* `DEPOSIT_LIMIT_SET_ROLE` - Set deposit limitst


# Oracles

The Oracle module in CAP is responsible for providing reliable, up-to-date price and rate data to the protocol. It acts as the backbone for all value calculations, including minting, burning, borrowing, and liquidation processes.

## Oracle Data Sources

The protocol leverages multiple oracle sources for different types of data:

* [**RedStone Oracles**](https://www.redstone.finance/): Primary source for reserve asset pricing ([USDC, USDT, pyUSD & cUSD](https://app.redstone.finance/app/feeds/?page=1\&sortBy=popularity\&sortDesc=false\&perPage=32\&networks=1))
* [**Chainlink Oracles**](https://chain.link/): Used for delegation asset pricing (wstETH, wBTC) on shared security side
* [**Cap Token Adapter**](https://github.com/cap-labs-dev/cap-contracts/blob/1064b6a969d55c822dcf0b2c4b733ceb4118737e/contracts/oracle/libraries/CapTokenAdapter.sol): Calculates weighted average of underlying basket for cUSD pricing
* [**Staked Cap Adapter**](https://github.com/cap-labs-dev/cap-contracts/blob/1064b6a969d55c822dcf0b2c4b733ceb4118737e/contracts/oracle/libraries/StakedCapAdapter.sol): Accounts for accrued yield and cUSD price for stcUSD pricing
* [**Aave Adapter**](mailto:undefined): Fetches current borrow rates from external markets
* [**Vault Adapter**](mailto:undefined): Calculates utilization-based interest rates

For function signatures, parameters, and data structures, see the [Oracle Contract Reference](/developers/contracts/oracle).


# Access Controls

The Access Control system implements granular, function-level permissions across all contracts of Cap. It provides a sophisticated role-based access control mechanism that allows precise management of who can call specific functions on specific contracts, building on OpenZeppelin's [AccessControlEnumberable](https://docs.openzeppelin.com/contracts/3.x/access-control) Integration.

Roles are managed by the Access Control admin, currently set to Cap's multisig address

## Mechanics

* Each function on each contract has its own unique 32-byte role ID
* The role ID is generated by combining:
  * The function selector (first 4 bytes of the function signature)
  * The contract address
* Permissions can be granted/revoked at the function level
* Contracts inherit access control through the `Access` abstract contract and uses the checkAccess modifier on protected functions

For function signatures and data structures, see the [Access Controls Contract Reference](/developers/contracts/access-controls).

## Role Hierarchy

```
DEFAULT_ADMIN_ROLE
├── access_control_admin
│   ├── oracle_admin
│   ├── rate_oracle_admin
│   ├── lender_admin
│   ├── delegation_admin
│   └── vault_config_admin
└── Emergency Admin
```

1. **DEFAULT\_ADMIN\_ROLE**: Can upgrade the AccessControl contract
2. **Access Management Roles**: Can grant/revoke permissions
3. **Function-Specific Roles**: Control access to individual functions

## Permission Types

**1. Administrative Permissions**

* Contract upgrades (`bytes4(0)`)
* Access management (`grantAccess`, `revokeAccess`)

**2. Operational Permissions**

* Vault operations (`borrow`, `repay`, `mint`, `burn`)
* Oracle managements (`setOracleData`, `setStaleness`, `setRates`)
* Asset management (`addAsset`, `removeAsset`, `pauseAsset`, `setReserve`)
* Fee Auctions (`setDuration`, `setStartPrice` ,`setPaymentToken`)
* Delegations ( `addAgent`, `modifyAgent` ,`registerNetwork`)

**3. Emergency Permissions**

* Protocol pause (`pauseProtocol`, `unpauseProtocol`)
* Emergency functions (`emergencyWithdraw`, `rescueERC20`)

## Timelock

Critical administrative operations are routed through an OpenZeppelin [TimelockController](https://docs.openzeppelin.com/contracts/5.x/api/governance#TimelockController) with a **1-day minimum delay** (86,400 seconds). The Timelock enforces a mandatory waiting period between when an operation is proposed and when it can be executed, giving participants time to react to parameter changes.

The Timelock holds permissions on Cap contracts via the same role-based access control system described above. Operations must be scheduled, wait the full delay period, and then be explicitly executed — they cannot bypass the delay.

| Parameter | Value                                                                                                                   |
| --------- | ----------------------------------------------------------------------------------------------------------------------- |
| Min Delay | 86,400 seconds (1 day)                                                                                                  |
| Contract  | [`0xD8236031d8279d82E615aF2BFab5FC0127A329ab`](https://etherscan.io/address/0xD8236031d8279d82E615aF2BFab5FC0127A329ab) |


# Frontier Program

The Frontier Program marks a pivotal launch phase for Cap, opening its doors to the public on Ethereum mainnet. The program aims to foster a vibrant, committed community at the heart of Cap’s ecosystem.

The Frontier Program incentivizes early and consistent participation to earn "Caps". Those who join and remain active from the outset stand to earn the highest number of Caps.

Users can start accruing Caps by exploring Cap's flagship products: cUSD and stcUSD. Below is the link to all venues where users can participate in the Frontier Program. Detailed information about the Program can be found [here](https://mirror.xyz/0x83c21bb4Bf0EC116f5a1945AaeF847Fe3b321B32/BNYypWfT0CyrV2De1_MM5_KqInuONerxJFi1anNHWPU).

#### Pendle

* [PT cUSD](https://app.pendle.finance/trade/markets/0x307c15f808914df5a5dbe17e5608f84953ffa023/swap?view=pt\&chain=ethereum)
* [PT stcUSD](https://app.pendle.finance/trade/markets/0xcc781b043933c10a04409b22aada3a3d1a7f29d4/swap?view=pt\&chain=ethereum)
* [Penpie PT cUSD](https://www.pendle.magpiexyz.io/stake/0x307c15f808914df5a5dbe17e5608f84953ffa023)
* [Penpie PT stcUSD](https://www.pendle.magpiexyz.io/stake/0xcc781b043933c10a04409b22aada3a3d1a7f29d4)
* [Equilibria PT stcUSD](https://equilibria.fi/stake)

#### Yield Optimizer

* [Beefy penpie stcUSD](https://app.beefy.com/vault/pendle-stcusd-29jan26)
* [Beefy equilibria stcUSD](https://app.beefy.com/vault/pendle-eqb-stcusd-29jan26)

#### Lending Markets

* [Morpho stcUSD / USDC](https://app.morpho.org/ethereum/market/0xeb17955ea422baeddbfb0b8d8c9086c5be7a9cfdefb292119a102e981a30062e/stcusd-usdc)
* [Morpho PT-stcUSD-29JAN2026 / USDC](https://app.morpho.org/ethereum/market/0x03f715ef1ae508ab3e1faf4dffdbf2a077d1f0ad10c5aad42cf4438d5e3328af/pt-stcusd-29jan2026-usdc)

{% hint style="info" %}
Caps are rewarded to USDC suppliers to the markets above
{% endhint %}


# Homestead Program

With the Homestead program, we’re entering the next phase of the protocol: sustained production.

### Homestead timeline

* Start date: January 29th, 2026
* End date: July 23rd, 2026

Within the program, there are two tracks: Caps and Cogs

### Caps

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

USD holders have a choice: yield or Caps. cUSD holders are incentivized to earn Caps. Those who join and remain active from the outset stand to earn the highest number of Caps.

Users can accrue Caps by exploring cUSD integrations across DeFi. The leaderboard for Caps can be found [here](https://cap.app/caps).

{% hint style="info" %}
For the first month of the program, users will earn **Double Caps** for all Homestead activities
{% endhint %}

#### Holding cUSD

Holding cUSD earns **10x** Caps

#### Pendle

* [YT cUSD / LP cUSD](https://app.pendle.finance/trade/markets/0x307c15f808914df5a5dbe17e5608f84953ffa023/swap?view=yt\&chain=ethereum): Holding YT cUSD or LP cUSD for **20x** Caps
* [YT stcUSD / LP stcUSD](https://app.pendle.finance/trade/markets/0x307c15f808914df5a5dbe17e5608f84953ffa023/swap?view=yt\&chain=ethereum): Holding YT stcUSD or LP stcUSD for **5x** Caps

{% hint style="info" %}
Pendle markets expire Jul 22 2026
{% endhint %}

#### Lending Markets

Lending stablecoins to stcUSD and Cap PTs earn **0.5x** Caps per dollar per day

* [Morpho stcUSD / USDC](https://app.morpho.org/ethereum/market/0xeb17955ea422baeddbfb0b8d8c9086c5be7a9cfdefb292119a102e981a30062e/stcusd-usdc)

#### Further Integrations

Stay tuned for additional campaigns! Please follow [Cap’s community handle](https://x.com/capmoney_) on X for updates.

### Cogs

Delegators on Cap will continue to earn COGs following the Frontier program.

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

The formula to calculate COGs is the following:

<p align="center"><em>COGs = Delegation Value * Borrow Boost * Delegation Boost</em></p>

Breaking it down, Delegators earn COGs proportional to the amount delegated.<br>

The Borrow Boost is calculated as:

<p align="center"><em>Borrow Boost = min (18 * LTV + 1, 10)</em></p>

In English, the borrow boost is determined based on the current Loan To Value of the delegated Borrower. If the Borrower's LTV is currently 20%, the boost will be **4.6x**. The minimum boost is **1x** and is capped at **10x.**

Finally, we are introducing the Delegation Boost. Delegations over certain USD amounts will receive a multiplier boost. Specifically, the boost follows a step function at 3 amounts: $25m, $50m, and $100m. For example, a $75M delegation will receive **1.5x** COGs.

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

{% hint style="info" %}
A minimum delegation of $2M is required to earn Cogs
{% endhint %}


# Addresses

## Ethereum Mainnet

### **Cap Vault**

<table data-full-width="false"><thead><tr><th width="198.5">Contract</th><th width="541.5">Address</th></tr></thead><tbody><tr><td>cUSD</td><td>0xcCcc62962d17b8914c62D74FfB843d73B2a3cccC</td></tr><tr><td>stcUSD</td><td>0x88887bE419578051FF9F4eb6C858A951921D8888</td></tr><tr><td>Debt USDC</td><td>0xfa8C6D0b95d9191B5A1D51C868Da2BDFd6C04Ff9</td></tr><tr><td>Cap Token</td><td>0x99991c6AAbba5a096f24f250b73580F5179b9999</td></tr></tbody></table>

### **Cap Infra**

<table data-full-width="false"><thead><tr><th width="198.5">Contract</th><th width="541.5">Address</th></tr></thead><tbody><tr><td>Oracle</td><td>0xcD7f45566bc0E7303fB92A93969BB4D3f6e662bb</td></tr><tr><td>Lender</td><td>0x15622c3dbbc5614E6DFa9446603c1779647f01FC</td></tr><tr><td>Access Control</td><td>0x7731129a10d51e18cDE607C5C115F26503D2c683</td></tr><tr><td>Delegation</td><td>0xF3E3Eae671000612CE3Fd15e1019154C1a4d693F</td></tr><tr><td>Fee Auction</td><td>0xa1a20aBdc873CF291c22Ce3C8968EC06277324D0</td></tr><tr><td>Fee Receiver</td><td>0x0036c7b9b62c53F47c804a5643F0c09f864beF0b</td></tr><tr><td>USDC Fractional Reserve Vault</td><td>0x3Ed6aa32c930253fc990dE58fF882B9186cd0072</td></tr><tr><td>cUSD Adapter</td><td>0xAcc9ce4C15A0F6A2bec49C3F81261d60553D2Faf</td></tr><tr><td>stcUSD Adapter</td><td>0xdf48Eb321B38bc19E7F5b2CCA8242Cc6B9a6EcD0</td></tr><tr><td>Timelock</td><td>0xD8236031d8279d82E615aF2BFab5FC0127A329ab</td></tr></tbody></table>

### Symbiotic

<table data-full-width="false"><thead><tr><th width="198.5">Contract</th><th width="541.5">Address</th></tr></thead><tbody><tr><td>Network</td><td>0x98e52Ea7578F2088c152E81b17A9a459bF089f2a</td></tr><tr><td>Network Middleware</td><td>0x09A3976d8D63728d20DCDFEe1e531C206Ba91225</td></tr><tr><td>Vault Factory</td><td>0x0B92300C8494833E504Ad7d36a301eA80DbBAE2e</td></tr><tr><td>Agent Manager</td><td>0x08A728CF4E6b39f4AFa059c6eE376103722953eA</td></tr></tbody></table>

### Oracles

<table data-full-width="false"><thead><tr><th width="198.5">Contract</th><th width="541.5">Address</th></tr></thead><tbody><tr><td><a href="https://app.redstone.finance/app/feeds/ethereum-mainnet/cusd_fundamental/">Redstone cUSD</a></td><td>0x9A5a3c3Ed0361505cC1D4e824B3854De5724434A</td></tr><tr><td><a href="https://etherscan.io/address/0x8E3386B2f6084eB1B0988070c3d826995BD175c0">Morpho stcUSD</a></td><td>0x8E3386B2f6084eB1B0988070c3d826995BD175c0</td></tr></tbody></table>

## MegaETH Mainnet

### **Cap Vault**

<table data-full-width="false"><thead><tr><th width="198.5">Contract</th><th width="541.5">Address</th></tr></thead><tbody><tr><td>cUSD</td><td>0xcCcc62962d17b8914c62D74FfB843d73B2a3cccC</td></tr><tr><td>stcUSD</td><td>0x88887bE419578051FF9F4eb6C858A951921D8888</td></tr></tbody></table>

## Tempo Mainnet

### **Cap Vault**

<table data-full-width="false"><thead><tr><th width="198.5">Contract</th><th width="541.5">Address</th></tr></thead><tbody><tr><td>cUSD</td><td>0x20C0000000000000000000000520792DcCccCccC</td></tr><tr><td>stcUSD</td><td>0x20c0000000000000000000008EE4fcFF88888888</td></tr></tbody></table>

## Multi-sigs

**Developer multi-sig Address:** 0xb8FC49402dF3ee4f8587268FB89fda4d621a8793

**Cap token owner multi-sig:**

0x80A216738E4e49B262Deae6bEb6578Bdf164c2eA

Signers:

* 0xDD30a4712e6B34926d4f5aA99c1881573407538C
* 0xdf466Fa3ddd0042d990FA9A023e040884CBaD439
* 0x7c29F6A93df60Bcd3B20f03B57a2F9e698FD4128
* 0x62D0b3c0a77bE77EaB2060266a95FfaD9e6A3F51
* 0xA62f87A9D4B5EE1F83cb644Ea076832A396101b8


# Price Sources

This page describes **recommended ways to obtain an accurate cUSD price** (and similarly for stcUSD), plus how to derive pricing **on-chain** from the cUSD contract and the protocol oracle.

Contract addresses for Ethereum mainnet are listed in [Addresses](/developers/addresses).

## Why not use DEX / AMM pool prices?

Do **not** rely on on-chain DEX prices from venues such as Curve, Uniswap, or similar AMMs as your primary cUSD reference.

cUSD can be **minted and redeemed 1:1 with USDC** (and other whitelisted reserve assets at published rates). There is **little economic reason** for liquidity providers to maintain **deep** two-sided AMM liquidity when mint and redeem offer a direct, tight on/off-ramp. As a result, DEX pools are often **shallow**, and spot or TWAP prices there can be **noisy, easy to move, or unrepresentative** of the price implied by reserves and the protocol oracle. Prefer the feeds and oracle methods on this page instead.

## Recommended: external feeds

### RedStone (reserve oracle)

The RedStone feed used for cUSD reserve / fundamental pricing is the on-chain wrapper [`0x9A5a3c3Ed0361505cC1D4e824B3854De5724434A`](https://etherscan.io/address/0x9A5a3c3Ed0361505cC1D4e824B3854De5724434A) (Etherscan). The same feed is documented in the RedStone app at <https://app.redstone.finance/app/token/cUSD_FUNDAMENTAL/>.

### DefiLlama (Coins API)

DefiLlama exposes current prices via the Coins API. Endpoint documentation: [https://api-docs.defillama.com/#tag/coins/get/prices/current/{coins}](https://api-docs.defillama.com/#tag/coins/get/prices/current/%7Bcoins%7D). Example for **cUSD** on Ethereum (checksum-insensitive in the path):

```
https://coins.llama.fi/prices/current/ethereum:0xcccc62962d17b8914c62d74ffb843d73b2a3cccc
```

For **stcUSD**, use the stcUSD token address on the same network, for example:

```
https://coins.llama.fi/prices/current/ethereum:0x88887be419578051ff9f4eb6c858a951921d8888
```

DefiLlama’s pegged-asset adapter for Cap cUSD (issued supply / peg logic) is <https://github.com/DefiLlama/peggedassets-server/blob/master/src/adapters/peggedAssets/cap-cusd/index.ts>.

## On-chain

The patterns below use the **cUSD** token contract and the mainnet **Oracle** (see [Addresses](/developers/addresses)).

### Backing balances per asset

Call `cUSD.assets()` to obtain the ordered list of backing assets, then for each `asset` call `cUSD.totalSupply(asset)` to read how much of that reserve token is held in the cUSD vault for that asset. Together, these balances describe the collateral side of the basket that backs outstanding cUSD.

### Redemption weights (1 cUSD → each underlying)

Using the same asset ordering from `cUSD.assets()`, call `cUSD.getRedeemAmount(1e18)` with **1e18** wei of cUSD (18 decimals). The return values give the amounts of each underlying you would receive for that redemption path, which you can treat as the per-asset conversion from one full cUSD unit into the basket (subject to fees and current oracle pricing encoded in the call). Align each entry in the returned arrays with the corresponding index in `assets()`.

### Protocol oracle price

Query the deployed **Oracle** contract ([0xcD7f45566bc0E7303fB92A93969BB4D3f6e662bb](https://etherscan.io/address/0xcD7f45566bc0E7303fB92A93969BB4D3f6e662bb)) with `getPrice(cusd_address)`, where `cusd_address` is the cUSD token address. That is the same `getPrice` surface as in <https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/interfaces/IPriceOracle.sol> (`IPriceOracle` in cap-contracts):

```solidity
function getPrice(address _asset) external view returns (uint256 price, uint256 lastUpdated);
```

For semantics (staleness, backup oracles, adapters), see [Oracles](/concepts/oracles).

## Etherscan proxy API

When trading volume on secondary-market pairs is low, a robust approach for both **cUSD** and **stcUSD** is to read the **protocol oracle** directly—sometimes described as using a **virtual price** (the economically meaningful value from the Oracle rather than thin AMM liquidity).

`getPrice(address _asset)` returns **two** `uint256` values: the **price** and **`lastUpdated`** (last update timestamp). The signature is specified in Cap’s [`IPriceOracle`](https://github.com/cap-labs-dev/cap-contracts/blob/main/contracts/interfaces/IPriceOracle.sol#L51) (`getPrice`).

**Integrator note:** If you only deploy a small utility contract that forwards `getPrice` but **drops** `lastUpdated`, consumers may still get a spot price, but **historical** pricing pipelines often need **both** fields (or an equivalent archive of oracle updates) to attribute values correctly in time. Where possible, decode the full ABI return from `eth_call` against the Oracle (or index oracle update events) instead of stripping the timestamp.

### Example: `eth_call` via Etherscan API v2

These are illustrative `module=proxy` `eth_call` URLs for Ethereum mainnet (`chainid=1`). Replace `YOURAPIKEY` with a valid [Etherscan API](https://docs.etherscan.io/) key.

**cUSD** (`getPrice` on the Oracle for the cUSD token address):

```
https://api.etherscan.io/v2/api?chainid=1&module=proxy&action=eth_call&to=0xcD7f45566bc0E7303fB92A93969BB4D3f6e662bb&data=0x41976e09000000000000000000000000cccc62962d17b8914c62d74ffb843d73b2a3cccc&tag=latest&apikey=YOURAPIKEY
```

**stcUSD:**

```
https://api.etherscan.io/v2/api?chainid=1&module=proxy&action=eth_call&to=0xcD7f45566bc0E7303fB92A93969BB4D3f6e662bb&data=0x41976e0900000000000000000000000088887be419578051ff9f4eb6c858a951921d8888&tag=latest&apikey=YOURAPIKEY
```

Example JSON-RPC style response (shape may vary slightly by client; `result` is the ABI-encoded return data):

```json
{"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000000000005f56ea50000000000000000000000000000000000000000000000000000000069ca971b"}
```

Decode as two 32-byte words: first word = `price`, second word = `lastUpdated` (Unix timestamp in seconds). Scaling and staleness rules for `price` follow the Oracle configuration described in [Oracles](/concepts/oracles).


# Contract Reference

Technical reference for all Cap protocol smart contracts. Each page covers the contract's mechanics, full function signatures, data structures, and usage examples.

| Module                                                         | Description                                                         |
| -------------------------------------------------------------- | ------------------------------------------------------------------- |
| [Vault](/developers/contracts/vault)                           | cUSD backing asset vault — mint, burn, redeem, utilization tracking |
| [Minter](/developers/contracts/minter)                         | Dynamic mint/burn fee model, deposit caps, and asset whitelisting   |
| [Fractional Reserve](/developers/contracts/fractional-reserve) | ERC4626 yield strategy integration — invest, divest, harvest        |
| [Lender](/developers/contracts/lender)                         | Operator borrowing, two-stream interest accrual, and liquidations   |
| [Delegation](/developers/contracts/delegation)                 | Slashable collateral tracking across EigenLayer and Symbiotic       |
| [Fee Auction](/developers/contracts/fee-auction)               | Dutch auction distributing protocol yield to stcUSD holders         |
| [Oracle](/developers/contracts/oracle)                         | Price and rate oracle with pluggable adapter architecture           |
| [Access Controls](/developers/contracts/access-controls)       | Per-function role-based permissioning                               |
| [Tokens](/developers/contracts/tokens)                         | cUSD (CapToken), stcUSD (StakedCap), and L2Token (OFT)              |


# Vault

The Vault is the core abstract contract inherited by every cUSD deployment. It holds the backing assets deposited by cUSD minters and lent out to whitelisted Operators, tracking total supplies and borrows per asset. Mint/burn/redeem operations flow through the Minter for fee calculation and through the FractionalReserve layer for liquidity management — the Vault itself only tracks principal amounts, leaving interest calculations to the Lender.

## Mechanics

### Minting cUSD

Depositing a backing asset in exchange for cUSD:

1. **Deposit cap check**: `_amountIn` is silently capped to the remaining deposit capacity for the asset.
2. **Fee calculation**: The Minter computes `(amountOut, fee)` based on the asset's current allocation ratio vs. its optimal ratio.
3. **Slippage / deadline check**: Reverts if `amountOut < _minAmountOut` or the deadline has passed.
4. **Transfer in**: The asset is pulled from `msg.sender` and `totalSupplies[asset]` is incremented.
5. **Mint**: `amountOut` cUSD is minted to `_receiver`; if a fee applies, an additional `fee` cUSD is minted to the insurance fund.

### Burning cUSD

Exchanging cUSD for a single backing asset:

1. **Fee calculation**: The Minter computes the post-fee asset output and fee amount.
2. **cUSD burn**: `_amountIn` cUSD is burned from `msg.sender`.
3. **Divest**: The FractionalReserve layer divests `amountOut + fee` of the asset from the active yield strategy if needed to cover the withdrawal.
4. **Transfer out**: `amountOut` is sent to `_receiver`; `fee` is sent to the insurance fund. `totalSupplies[asset]` is decremented.

### Redeeming cUSD

Proportional withdrawal across every backing asset:

1. **Amount calculation**: The Minter computes proportional `amountsOut` and `fees` for each asset in the vault.
2. **cUSD burn**: `_amountIn` cUSD is burned from `msg.sender`.
3. **Divest many**: The FractionalReserve layer divests the required amounts across all assets.
4. **Transfer out**: For each asset, `amountsOut[i]` is sent to `_receiver` and `fees[i]` to the insurance fund.

### Borrowing and Repaying

These paths are access-controlled and called only by the Lender contract:

* **Borrow**: Divests the asset from the yield strategy if needed, decrements available balance, transfers to `_receiver`, increments `totalBorrows[asset]`.
* **Repay**: Accepts the asset back from the Lender via `transferFrom`, decrements `totalBorrows[asset]`.

### Utilization Tracking

The vault tracks a **cumulative utilization index** per asset — a time-weighted integral of the utilization ratio. This is consumed by the RateOracle to compute borrow interest rates.

```
utilizationIndex += utilization * (block.timestamp - lastUpdate)
utilization = totalBorrows / totalSupplies  (ray format, 1e27 = 100%)
```

The index is updated on every mint, burn, borrow, and repay via the `updateIndex` modifier.

***

## Architecture

### Core Functions

#### `mint(address _asset, uint256 _amountIn, uint256 _minAmountOut, address _receiver, uint256 _deadline)`

```solidity
function mint(
    address _asset,
    uint256 _amountIn,
    uint256 _minAmountOut,
    address _receiver,
    uint256 _deadline
) external returns (uint256 amountOut);
```

Deposit `_asset` and receive cUSD. Requires ERC20 approval from `msg.sender` to the vault. `_amountIn` is silently reduced to the remaining deposit cap if exceeded.

| Parameter       | Type      | Description                                   |
| --------------- | --------- | --------------------------------------------- |
| `_asset`        | `address` | Whitelisted backing asset to deposit          |
| `_amountIn`     | `uint256` | Amount of asset to deposit                    |
| `_minAmountOut` | `uint256` | Minimum cUSD to receive (slippage protection) |
| `_receiver`     | `address` | Recipient of the minted cUSD                  |
| `_deadline`     | `uint256` | Unix timestamp after which the tx reverts     |

**Returns**: `amountOut` — cUSD minted to `_receiver` (excludes fee).

***

#### `burn(address _asset, uint256 _amountIn, uint256 _minAmountOut, address _receiver, uint256 _deadline)`

```solidity
function burn(
    address _asset,
    uint256 _amountIn,
    uint256 _minAmountOut,
    address _receiver,
    uint256 _deadline
) external returns (uint256 amountOut);
```

Burn `_amountIn` cUSD and receive `_asset`. The caller must hold the cUSD.

| Parameter       | Type      | Description                                    |
| --------------- | --------- | ---------------------------------------------- |
| `_asset`        | `address` | Backing asset to withdraw                      |
| `_amountIn`     | `uint256` | Amount of cUSD to burn                         |
| `_minAmountOut` | `uint256` | Minimum asset to receive (slippage protection) |
| `_receiver`     | `address` | Recipient of the asset                         |
| `_deadline`     | `uint256` | Unix timestamp after which the tx reverts      |

**Returns**: `amountOut` — asset sent to `_receiver` (excludes fee).

***

#### `redeem(uint256 _amountIn, uint256[] calldata _minAmountsOut, address _receiver, uint256 _deadline)`

```solidity
function redeem(
    uint256 _amountIn,
    uint256[] calldata _minAmountsOut,
    address _receiver,
    uint256 _deadline
) external returns (uint256[] memory amountsOut);
```

Burn `_amountIn` cUSD and receive a proportional share of every backing asset. `_minAmountsOut` must have one entry per vault asset in the same order as `assets()`.

**Returns**: `amountsOut` — asset amounts sent to `_receiver` (one per vault asset).

***

#### `borrow(address _asset, uint256 _amount, address _receiver)`

```solidity
function borrow(address _asset, uint256 _amount, address _receiver) external;
```

Access-controlled. Called by the Lender to transfer assets to an Operator. Reverts with `InsufficientReserves` if available balance is too low.

***

#### `repay(address _asset, uint256 _amount)`

```solidity
function repay(address _asset, uint256 _amount) external;
```

Access-controlled. Called by the Lender when an Operator repays. Pulls `_amount` from `msg.sender` via `transferFrom`.

***

#### `availableBalance(address _asset)`

```solidity
function availableBalance(address _asset) external view returns (uint256 amount);
```

Returns `totalSupplies[_asset] - totalBorrows[_asset]` — the amount currently available to borrow or withdraw.

***

#### `utilization(address _asset)`

```solidity
function utilization(address _asset) external view returns (uint256 ratio);
```

Returns the current utilization ratio in ray format (`1e27 = 100%`). For example, `5e26` = 50% utilization.

***

#### `currentUtilizationIndex(address _asset)`

```solidity
function currentUtilizationIndex(address _asset) external view returns (uint256 index);
```

Returns the up-to-date cumulative utilization index including the current unsettled period. Used by the RateOracle for interest rate calculations.

***

#### `getRemainingMintCapacity(address _asset)`

```solidity
function getRemainingMintCapacity(address _asset) external view returns (uint256 remainingMintCapacity);
```

Returns how much more of `_asset` can be deposited before hitting the deposit cap. Returns 0 if the cap is already reached.

***

#### Admin Functions

| Function                                         | Description                                                             |
| ------------------------------------------------ | ----------------------------------------------------------------------- |
| `addAsset(address _asset)`                       | Add a new backing asset to the vault                                    |
| `removeAsset(address _asset)`                    | Remove an asset (only if `totalSupplies == 0`)                          |
| `pauseAsset(address _asset)`                     | Pause minting, burning, and borrowing for a specific asset              |
| `unpauseAsset(address _asset)`                   | Resume operations for a paused asset                                    |
| `pauseProtocol()`                                | Pause all vault operations globally                                     |
| `unpauseProtocol()`                              | Resume all vault operations                                             |
| `setInsuranceFund(address)`                      | Update the insurance fund address                                       |
| `rescueERC20(address _asset, address _receiver)` | Recover a non-vault, non-strategy token sent to the contract by mistake |

***

### Data Structures

#### `VaultStorage`

ERC-7201 namespaced storage for the Vault module.

```solidity
struct VaultStorage {
    EnumerableSet.AddressSet assets;
    mapping(address => uint256) totalSupplies;
    mapping(address => uint256) totalBorrows;
    mapping(address => uint256) utilizationIndex;
    mapping(address => uint256) lastUpdate;
    mapping(address => bool) paused;
    address insuranceFund;
}
```

| Field              | Type                          | Description                                                 |
| ------------------ | ----------------------------- | ----------------------------------------------------------- |
| `assets`           | `EnumerableSet.AddressSet`    | Set of whitelisted backing assets                           |
| `totalSupplies`    | `mapping(address => uint256)` | Total asset deposited (principal, not accounting for yield) |
| `totalBorrows`     | `mapping(address => uint256)` | Total asset currently borrowed by Operators                 |
| `utilizationIndex` | `mapping(address => uint256)` | Cumulative time-weighted utilization integral per asset     |
| `lastUpdate`       | `mapping(address => uint256)` | Timestamp of the last index update per asset                |
| `paused`           | `mapping(address => bool)`    | Per-asset pause state                                       |
| `insuranceFund`    | `address`                     | Address that receives minting/burning fees in cUSD          |

***

#### `MintBurnParams`

```solidity
struct MintBurnParams {
    address asset;
    uint256 amountIn;
    uint256 amountOut;
    uint256 minAmountOut;
    address receiver;
    uint256 deadline;
    uint256 fee;
}
```

#### `RedeemParams`

```solidity
struct RedeemParams {
    uint256 amountIn;
    uint256[] amountsOut;
    uint256[] minAmountsOut;
    address receiver;
    uint256 deadline;
    uint256[] fees;
}
```

***

### Events

| Event          | Key Parameters                                      | Emitted when                    |
| -------------- | --------------------------------------------------- | ------------------------------- |
| `Mint`         | `minter, receiver, asset, amountIn, amountOut, fee` | cUSD minted                     |
| `Burn`         | `burner, receiver, asset, amountIn, amountOut, fee` | cUSD burned for a single asset  |
| `Redeem`       | `redeemer, receiver, amountIn, amountsOut, fees`    | cUSD redeemed across all assets |
| `Borrow`       | `borrower, asset, amount`                           | Operator borrow executed        |
| `Repay`        | `repayer, asset, amount`                            | Operator repayment received     |
| `AddAsset`     | `asset`                                             | New backing asset added         |
| `RemoveAsset`  | `asset`                                             | Backing asset removed           |
| `PauseAsset`   | `asset`                                             | Asset paused                    |
| `UnpauseAsset` | `asset`                                             | Asset unpaused                  |

***

### Errors

| Error                   | Condition                                                |
| ----------------------- | -------------------------------------------------------- |
| `PastDeadline`          | `block.timestamp > deadline`                             |
| `Slippage`              | `amountOut < minAmountOut`                               |
| `InvalidAmount`         | `amountOut == 0`                                         |
| `AssetPaused`           | Mint/burn/borrow attempted on a paused asset             |
| `AssetNotSupported`     | Asset is not in the vault's asset list                   |
| `AssetAlreadySupported` | `addAsset` called for an already-listed asset            |
| `AssetHasSupplies`      | `removeAsset` called while `totalSupplies > 0`           |
| `AssetNotRescuable`     | `rescueERC20` called for a listed or strategy-held asset |
| `InvalidMinAmountsOut`  | `_minAmountsOut.length` doesn't match `assets().length`  |
| `InsufficientReserves`  | Available balance is less than the amount requested      |

***

## Usage Examples

### 1. Minting cUSD

```solidity
interface IVault {
    function mint(address _asset, uint256 _amountIn, uint256 _minAmountOut, address _receiver, uint256 _deadline)
        external returns (uint256 amountOut);
    function getMintAmount(address _user, address _asset, uint256 _amountIn)
        external view returns (uint256 amountOut, uint256 fee);
}

address vault    = 0x...; // cUSD vault proxy
address usdc     = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
uint256 amountIn = 10_000e6; // 10,000 USDC

// 1. Preview the mint
(uint256 expectedOut, ) = IVault(vault).getMintAmount(msg.sender, usdc, amountIn);

// 2. Approve and mint
IERC20(usdc).approve(vault, amountIn);
uint256 cusdReceived = IVault(vault).mint(
    usdc,
    amountIn,
    expectedOut * 99 / 100, // 1% slippage tolerance
    msg.sender,
    block.timestamp + 5 minutes
);
```

### 2. Checking vault utilization before borrowing

```solidity
interface IVault {
    function availableBalance(address _asset) external view returns (uint256);
    function utilization(address _asset) external view returns (uint256 ratio);
    function totalSupplies(address _asset) external view returns (uint256);
    function totalBorrows(address _asset) external view returns (uint256);
}

function vaultStatus(address vault, address asset)
    external
    view
    returns (uint256 available, uint256 utilizationPct)
{
    available       = IVault(vault).availableBalance(asset);
    utilizationPct  = IVault(vault).utilization(asset) / 1e25; // convert ray to bps (1e27 → 100%)
}
```

### 3. Redeeming cUSD across all assets

```solidity
interface IVault {
    function assets() external view returns (address[] memory);
    function getRedeemAmount(address _user, uint256 _amountIn)
        external view returns (uint256[] memory amountsOut, uint256[] memory fees);
    function redeem(uint256 _amountIn, uint256[] calldata _minAmountsOut, address _receiver, uint256 _deadline)
        external returns (uint256[] memory amountsOut);
}

address vault      = 0x...;
uint256 cusdAmount = 5_000e18;

// Preview
(uint256[] memory expected, ) = IVault(vault).getRedeemAmount(msg.sender, cusdAmount);

// Apply 1% slippage tolerance to each asset
uint256[] memory minOut = new uint256[](expected.length);
for (uint256 i; i < expected.length; i++) {
    minOut[i] = expected[i] * 99 / 100;
}

IVault(vault).redeem(cusdAmount, minOut, msg.sender, block.timestamp + 5 minutes);
```


# Minter

The Minter is an abstract contract inherited by the Cap vault that handles fee calculation and routing for minting, burning, and redeeming cUSD. Dynamic fees are applied based on each backing asset's current allocation relative to its optimal ratio, incentivising deposits that balance the reserve and penalising those that over-concentrate it. Whitelisted addresses bypass all dynamic fees, and a flat redeem fee applies when burning cUSD proportionally across all backing assets.

## Mechanics

### Dynamic Mint/Burn Fees

Every cUSD mint or burn is priced using oracle-based valuations of the input asset and the cUSD token itself. After computing a pre-fee output amount, the contract calculates the **new ratio** — the asset's share of the total basket value after the swap — and runs it through a kinked piecewise-linear fee schedule.

**Mint fee zones** (based on post-mint asset ratio):

1. **Below or at `optimalRatio`**: Only `minMintFee` applies (a flat floor, capped at 5%, typically 10 bps).
2. **Between `optimalRatio` and `mintKinkRatio`**: Fee rises linearly from `minMintFee` toward `minMintFee + slope0`.
3. **Above `mintKinkRatio`**: Fee steepens further, adding `slope1` proportional to the excess above the kink.

**Burn fee zones** (based on post-burn asset ratio):

1. **At or above `optimalRatio`**: Zero fee — burning a well-allocated asset is always free.
2. **Between `burnKinkRatio` and `optimalRatio`**: Fee rises linearly using `slope0`.
3. **Below `burnKinkRatio`**: Fee steepens using `slope1` proportional to the shortfall below the kink.

All rates, ratios, and fees are expressed in **ray format** where `1e27 = 100%`. For example, a `minMintFee` of `1e24` is 0.1% (10 bps).

### Mint/Burn Process Flow

1. **Pre-fee amount**: `_amountOutBeforeFee` fetches oracle prices for the asset and cUSD, computes the raw output amount, and derives the post-swap allocation ratio.
2. **Whitelist check**: If `$.whitelist[user]` is `true`, the pre-fee amount is returned unchanged with zero fee.
3. **Fee application**: For non-whitelisted users, `_applyFeeSlopes` selects the correct zone from the kinked curve, computes the fee in ray arithmetic, and deducts it from the output.
4. **Return**: `(amountOut, fee)` is returned to the calling vault for execution.

### Redeem Process Flow

Redeeming burns cUSD in exchange for a **proportional slice of every backing asset** in the vault, at the current basket weights. This path avoids dynamic mint/burn fees entirely but applies a flat `redeemFee`.

1. A `shares` ratio is computed: `cUSD burned / total cUSD supply`, scaled to `1e33` precision.
2. For each asset in the vault, the user's proportional entitlement is `totalSupplies(asset) × shares / 1e33`.
3. If the user is whitelisted, `redeemFee` is set to zero; otherwise the flat fee is deducted from each asset amount.
4. Arrays of `amountsOut` and `fees` (one entry per asset) are returned.

### Deposit Caps

Each backing asset has an independent deposit cap (`depositCap[asset]`). Deposits that would push total supply beyond this cap revert. A cap of `0` means unlimited.

### Whitelisting

Whitelisted addresses receive fee-free minting, burning, and redeeming. This is intended for protocol-controlled addresses (e.g. the insurance fund, authorised integrators). The whitelist is managed by the access-controlled `setWhitelist` function.

***

## Architecture

### Core Functions

#### `getMintAmount(address _asset, uint256 _amountIn)`

```solidity
function getMintAmount(address _asset, uint256 _amountIn)
    external
    view
    returns (uint256 amountOut, uint256 fee);
```

Simulates a mint for `msg.sender`. Returns the cUSD amount received and the fee charged given the current basket state.

| Parameter   | Type      | Description                       |
| ----------- | --------- | --------------------------------- |
| `_asset`    | `address` | The backing asset being deposited |
| `_amountIn` | `uint256` | Amount of `_asset` to deposit     |

**Returns**: `amountOut` — cUSD minted after fees; `fee` — cUSD equivalent of the fee deducted.

***

#### `getMintAmount(address _user, address _asset, uint256 _amountIn)`

```solidity
function getMintAmount(address _user, address _asset, uint256 _amountIn)
    external
    view
    returns (uint256 amountOut, uint256 fee);
```

Same as above but simulates for an arbitrary `_user` address. Use this overload when quoting on behalf of another address (e.g. checking whether a specific wallet is whitelisted).

| Parameter   | Type      | Description                               |
| ----------- | --------- | ----------------------------------------- |
| `_user`     | `address` | Address whose whitelist status is applied |
| `_asset`    | `address` | The backing asset being deposited         |
| `_amountIn` | `uint256` | Amount of `_asset` to deposit             |

***

#### `getBurnAmount(address _asset, uint256 _amountIn)`

```solidity
function getBurnAmount(address _asset, uint256 _amountIn)
    external
    view
    returns (uint256 amountOut, uint256 fee);
```

Simulates burning `_amountIn` cUSD to receive `_asset`. Uses `msg.sender` for the whitelist check.

| Parameter   | Type      | Description                  |
| ----------- | --------- | ---------------------------- |
| `_asset`    | `address` | The backing asset to receive |
| `_amountIn` | `uint256` | Amount of cUSD to burn       |

**Returns**: `amountOut` — asset amount received after fees; `fee` — fee in cUSD terms.

***

#### `getBurnAmount(address _user, address _asset, uint256 _amountIn)`

```solidity
function getBurnAmount(address _user, address _asset, uint256 _amountIn)
    external
    view
    returns (uint256 amountOut, uint256 fee);
```

Same as above but simulates for an arbitrary `_user`.

***

#### `getRedeemAmount(uint256 _amountIn)`

```solidity
function getRedeemAmount(uint256 _amountIn)
    external
    view
    returns (uint256[] memory amountsOut, uint256[] memory redeemFees);
```

Simulates redeeming `_amountIn` cUSD for a proportional share of every backing asset. Uses `msg.sender` for the whitelist check.

| Parameter   | Type      | Description              |
| ----------- | --------- | ------------------------ |
| `_amountIn` | `uint256` | Amount of cUSD to redeem |

**Returns**: `amountsOut` — array of asset amounts (one per vault asset, in vault asset order); `redeemFees` — flat fee deducted per asset.

***

#### `getRedeemAmount(address _user, uint256 _amountIn)`

```solidity
function getRedeemAmount(address _user, uint256 _amountIn)
    external
    view
    returns (uint256[] memory amountsOut, uint256[] memory redeemFees);
```

Same as above but simulates for an arbitrary `_user`.

***

#### `getFeeData(address _asset)`

```solidity
function getFeeData(address _asset) external view returns (FeeData memory feeData);
```

Returns the current fee schedule for `_asset`.

***

#### `setFeeData(address _asset, FeeData calldata _feeData)`

```solidity
function setFeeData(address _asset, FeeData calldata _feeData) external;
```

Updates the fee schedule for `_asset`. Access-controlled. Reverts with:

* `InvalidMinMintFee` if `minMintFee >= 0.05e27` (>= 5%)
* `InvalidMintKinkRatio` if `mintKinkRatio == 0` or `>= 1e27`
* `InvalidBurnKinkRatio` if `burnKinkRatio == 0` or `>= 1e27`
* `InvalidOptimalRatio` if `optimalRatio == 0`, `>= 1e27`, or equals either kink

***

#### `getRedeemFee()`

```solidity
function getRedeemFee() external view returns (uint256 redeemFee);
```

Returns the current flat redeem fee in ray format (`1e27 = 100%`).

***

#### `setRedeemFee(uint256 _redeemFee)`

```solidity
function setRedeemFee(uint256 _redeemFee) external;
```

Sets the flat redeem fee. Access-controlled. Emits `SetRedeemFee`.

***

#### `setWhitelist(address _user, bool _whitelisted)`

```solidity
function setWhitelist(address _user, bool _whitelisted) external;
```

Grants or revokes fee-free access for `_user`. Access-controlled. Emits `SetWhitelist`.

***

#### `setDepositCap(address _asset, uint256 _cap)`

```solidity
function setDepositCap(address _asset, uint256 _cap) external;
```

Sets the maximum total deposit allowed for `_asset`. `_cap = 0` means no limit. Access-controlled. Emits `SetDepositCap`.

***

#### `whitelisted(address _user)`

```solidity
function whitelisted(address _user) external view returns (bool isWhitelisted);
```

Returns `true` if `_user` is whitelisted (fee-exempt).

***

#### `depositCap(address _asset)`

```solidity
function depositCap(address _asset) external view returns (uint256 cap);
```

Returns the deposit cap for `_asset` in asset-native units.

***

### Data Structures

#### `MinterStorage`

Internal ERC-7201 namespaced storage layout for the Minter module.

```solidity
struct MinterStorage {
    address oracle;
    uint256 redeemFee;
    mapping(address => FeeData) fees;
    mapping(address => bool) whitelist;
    mapping(address => uint256) depositCap;
}
```

| Field        | Type                          | Description                                                    |
| ------------ | ----------------------------- | -------------------------------------------------------------- |
| `oracle`     | `address`                     | Address of the price oracle used to value assets and cUSD      |
| `redeemFee`  | `uint256`                     | Flat fee applied on redeem, in ray format (`1e27 = 100%`)      |
| `fees`       | `mapping(address => FeeData)` | Fee schedule per backing asset                                 |
| `whitelist`  | `mapping(address => bool)`    | Fee-exempt addresses                                           |
| `depositCap` | `mapping(address => uint256)` | Maximum asset deposit per backing asset, in asset-native units |

***

#### `FeeData`

Fee schedule for a single backing asset. All ratio and rate fields are in ray format (`1e27 = 100%`).

```solidity
struct FeeData {
    uint256 minMintFee;
    uint256 slope0;
    uint256 slope1;
    uint256 mintKinkRatio;
    uint256 burnKinkRatio;
    uint256 optimalRatio;
}
```

| Field           | Type      | Description                                                                                              |
| --------------- | --------- | -------------------------------------------------------------------------------------------------------- |
| `minMintFee`    | `uint256` | Floor mint fee, always applied regardless of ratio. Max `0.05e27` (5%). Typical value: `1e24` (0.1%).    |
| `slope0`        | `uint256` | Rate of fee increase between `optimalRatio` and the kink ratio.                                          |
| `slope1`        | `uint256` | Rate of fee increase beyond the kink ratio — steeper than `slope0`.                                      |
| `mintKinkRatio` | `uint256` | Ratio above which `slope1` activates for mints. Must be in `(0, 1e27)`.                                  |
| `burnKinkRatio` | `uint256` | Ratio below which `slope1` activates for burns. Must be in `(0, 1e27)`.                                  |
| `optimalRatio`  | `uint256` | Target allocation ratio for the asset in the basket. Minting below this is cheap; burning above is free. |

***

### Events

| Event           | Parameters                       | Emitted when                            |
| --------------- | -------------------------------- | --------------------------------------- |
| `SetFeeData`    | `address asset, FeeData feeData` | Fee schedule updated for an asset       |
| `SetRedeemFee`  | `uint256 redeemFee`              | Flat redeem fee updated                 |
| `SetWhitelist`  | `address user, bool whitelisted` | Whitelist status changed for an address |
| `SetDepositCap` | `address asset, uint256 cap`     | Deposit cap updated for an asset        |

***

### Errors

| Error                  | Condition                                                                 |
| ---------------------- | ------------------------------------------------------------------------- |
| `InvalidMinMintFee`    | `minMintFee >= 0.05e27` (5%)                                              |
| `InvalidMintKinkRatio` | `mintKinkRatio == 0` or `>= 1e27`                                         |
| `InvalidBurnKinkRatio` | `burnKinkRatio == 0` or `>= 1e27`                                         |
| `InvalidOptimalRatio`  | `optimalRatio == 0`, `>= 1e27`, or equals `mintKinkRatio`/`burnKinkRatio` |

***

## Usage Examples

### 1. Quoting a mint before transacting

```solidity
interface IMinter {
    function getMintAmount(address _user, address _asset, uint256 _amountIn)
        external
        view
        returns (uint256 amountOut, uint256 fee);
}

address vault    = 0x...; // Cap vault proxy
address usdc     = 0x...;
address alice    = 0x...;
uint256 amountIn = 1000e6; // 1,000 USDC (6 decimals)

(uint256 cusdOut, uint256 feePaid) = IMinter(vault).getMintAmount(alice, usdc, amountIn);
// cusdOut  — cUSD minted after dynamic fee
// feePaid  — cUSD equivalent of the fee charged (goes to insurance fund)
```

### 2. Checking whitelist status before routing

```solidity
interface IMinter {
    function whitelisted(address _user) external view returns (bool);
    function getMintAmount(address _user, address _asset, uint256 _amountIn)
        external
        view
        returns (uint256 amountOut, uint256 fee);
}

function quoteMint(address vault, address user, address asset, uint256 amountIn)
    external
    view
    returns (uint256 amountOut, uint256 fee, bool isFeeExempt)
{
    isFeeExempt = IMinter(vault).whitelisted(user);
    (amountOut, fee) = IMinter(vault).getMintAmount(user, asset, amountIn);
}
```

### 3. Simulating a redeem across all basket assets

```solidity
interface IMinter {
    function getRedeemAmount(address _user, uint256 _amountIn)
        external
        view
        returns (uint256[] memory amountsOut, uint256[] memory redeemFees);
}

interface IVault {
    function assets() external view returns (address[] memory);
}

function previewRedeem(address vault, address user, uint256 cusdAmount)
    external
    view
    returns (address[] memory assetAddresses, uint256[] memory amounts, uint256[] memory fees)
{
    assetAddresses = IVault(vault).assets();
    (amounts, fees) = IMinter(vault).getRedeemAmount(user, cusdAmount);
    // amounts[i] and fees[i] correspond to assetAddresses[i]
}
```


# Fractional Reserve

The Fractional Reserve layer sits inside the cUSD vault and puts idle backing assets to work in external ERC4626 yield vaults (e.g. Yearn, Aave, TokenizedStrategy vaults) while keeping a configurable liquid reserve on hand to service borrows and withdrawals without delay. When the vault needs liquidity — for a burn, redeem, or Operator borrow — it automatically divests from the yield vault before transferring funds. Any yield above principal is forwarded to the `interestReceiver` (the FeeAuction).

## Mechanics

### Invest Flow

Idle capital above the configured reserve floor is deposited into the ERC4626 yield vault:

1. **Balance check**: The vault's current `balanceOf(asset)` is compared against `reserve[asset]`.
2. **Invest**: If balance exceeds the reserve, `balance - reserve` is deposited into the ERC4626 vault via `deposit()`. The invested amount is tracked in `loaned[asset]`.
3. **Access-controlled**: `investAll` requires the `checkAccess` modifier — typically called by an admin or keeper after new deposits accumulate.

### Partial Divest Flow

When a withdrawal, redemption, or borrow needs assets that aren't in the reserve:

1. **Balance check**: If `withdrawAmount > balanceOf(asset)`, a divest is triggered.
2. **Divest amount**: The logic withdraws `withdrawAmount + reserve[asset] - balanceOf(asset)` — enough to cover the request *and* refill the buffer reserve.
3. **Share cap**: If the required shares exceed what the vault holds, the full vault balance is redeemed instead.
4. **Interest pass-through**: If `divestAmount > loaned[asset]`, the excess is forwarded to `interestReceiver` and `loaned[asset]` is reduced only by principal.

### Full Divest Flow

Used when migrating to a new yield vault or during emergency divestment:

1. All ERC4626 shares held by the contract are redeemed.
2. If `redeemedAssets > loaned[asset]`: surplus forwarded to `interestReceiver`.
3. If `redeemedAssets < loaned[asset]`: reverts with `FullDivestRequired` — a loss in the strategy, requiring human intervention.

### Interest Realization

Interest accrues passively as the ERC4626 vault appreciates. It can be realized at any time — permissionlessly via `realizeInterest`:

1. `claimableInterest` is computed: `vaultShares - sharesRequiredToWithdrawLoaned`.
2. The excess shares are redeemed and sent directly to `interestReceiver`.

The Harvester contract automates this flow: it calls `report()` on each strategy in the vault's queue, processes each report via `process_report`, then calls `realizeInterest`.

### Reserve Buffer

`reserve[asset]` is a minimum floor of liquid assets the vault always keeps on hand. Setting it higher improves withdrawal UX (fewer divestments) at the cost of yield. Setting it to 0 means all idle capital is deployed.

### LimitModule

The `LimitModule` is a companion contract deployed alongside each ERC4626 vault. It enforces that only the cUSD vault can deposit or withdraw from the fractional reserve vault, preventing external actors from interfering with the strategy.

### TokenHolder

`TokenHolder` is a minimal ERC4626-compatible strategy (built on Yearn's `BaseStrategy`) that simply holds tokens without deploying them. It is used when a strategy slot is needed but no external yield is desired — for example, holding a stablecoin in reserve during an audit or migration period.

***

## Architecture

### Core Functions

#### `investAll(address _asset)`

```solidity
function investAll(address _asset) external;
```

Access-controlled. Deposits all idle balance above `reserve[asset]` into the configured ERC4626 vault for `_asset`. No-ops if no vault is set or if balance ≤ reserve.

***

#### `divestAll(address _asset)`

```solidity
function divestAll(address _asset) external;
```

Access-controlled. Fully redeems all shares from the ERC4626 vault for `_asset`. Surplus above `loaned[asset]` is forwarded to `interestReceiver`. Reverts with `FullDivestRequired` if a loss has occurred.

***

#### `setFractionalReserveVault(address _asset, address _vault)`

```solidity
function setFractionalReserveVault(address _asset, address _vault) external;
```

Access-controlled. Migrates `_asset` to a new ERC4626 vault. The existing vault is fully divested first. Reverts if `_vault` is already registered for another asset.

***

#### `setReserve(address _asset, uint256 _reserve)`

```solidity
function setReserve(address _asset, uint256 _reserve) external;
```

Access-controlled. Sets the minimum liquid buffer for `_asset` in asset-native units. Funds above this threshold are eligible for investment.

***

#### `realizeInterest(address _asset)`

```solidity
function realizeInterest(address _asset) external;
```

Permissionless. Withdraws accrued yield (ERC4626 appreciation above `loaned[asset]`) and sends it to `interestReceiver`. No-ops if no yield has accrued or no vault is set.

***

#### `claimableInterest(address _asset)`

```solidity
function claimableInterest(address _asset) external view returns (uint256 interest);
```

Returns the amount of `_asset` that can currently be claimed as yield. Computed as the asset value of the shares held in excess of those needed to recover `loaned[asset]`.

***

#### View Functions

| Function                                 | Returns     | Description                                                   |
| ---------------------------------------- | ----------- | ------------------------------------------------------------- |
| `fractionalReserveVault(address _asset)` | `address`   | ERC4626 vault address configured for `_asset`                 |
| `fractionalReserveVaults()`              | `address[]` | All registered ERC4626 vault addresses                        |
| `reserve(address _asset)`                | `uint256`   | Configured liquid reserve floor for `_asset`                  |
| `loaned(address _asset)`                 | `uint256`   | Principal currently deposited in the yield vault for `_asset` |
| `interestReceiver()`                     | `address`   | Address that receives realized yield (FeeAuction)             |

***

### Harvester

The `Harvester` is a stateless keeper contract. A keeper (e.g. Gelato) calls `harvest` periodically to report strategy performance and realize interest.

#### `harvest(address _cusd, address _asset)`

```solidity
function harvest(address _cusd, address _asset)
    external
    returns (uint256 profit, uint256 loss, uint256 interest);
```

| Parameter | Type      | Description                      |
| --------- | --------- | -------------------------------- |
| `_cusd`   | `address` | The cUSD vault proxy address     |
| `_asset`  | `address` | The backing asset to harvest for |

**Returns**:

* `profit` — cumulative profit across all strategies in the vault queue
* `loss` — cumulative loss across all strategies
* `interest` — amount of interest realized and sent to the fee auction

**Process flow**:

1. Fetches the ERC4626 vault's `default_queue` (ordered list of strategies).
2. For each strategy: calls `strategy.report()` then `vault.process_report(strategy)`.
3. Calls `realizeInterest(_asset)` if any claimable interest exists.

***

### LimitModule

A deployment-time immutable contract that gates deposits and withdrawals in the ERC4626 vault to the cUSD vault only.

#### `available_deposit_limit(address receiver)`

```solidity
function available_deposit_limit(address receiver) external view returns (uint256 limit);
```

Returns `type(uint256).max` if `receiver == vault`, otherwise 0.

#### `available_withdraw_limit(address owner, uint256, address[] calldata)`

```solidity
function available_withdraw_limit(address owner, uint256, address[] calldata)
    external view returns (uint256 limit);
```

Returns `type(uint256).max` if `owner == vault`, otherwise 0.

***

### TokenHolder

A minimal Yearn `BaseStrategy` that holds tokens in place without deploying them to an external yield source. `_deployFunds` and `_freeFunds` are intentionally empty. Deposit and withdrawal are restricted to `vault`.

***

### Data Structures

#### `FractionalReserveStorage`

```solidity
struct FractionalReserveStorage {
    address interestReceiver;
    mapping(address => uint256) loaned;
    mapping(address => uint256) reserve;
    mapping(address => address) vault;
    EnumerableSet.AddressSet vaults;
}
```

| Field              | Type                          | Description                                                         |
| ------------------ | ----------------------------- | ------------------------------------------------------------------- |
| `interestReceiver` | `address`                     | Receives yield forwarded from the fractional reserve (FeeAuction)   |
| `loaned`           | `mapping(address => uint256)` | Principal deposited into each asset's yield vault                   |
| `reserve`          | `mapping(address => uint256)` | Minimum liquid buffer to maintain per asset (in asset units)        |
| `vault`            | `mapping(address => address)` | ERC4626 yield vault address per backing asset                       |
| `vaults`           | `EnumerableSet.AddressSet`    | Set of all registered yield vault addresses (for uniqueness checks) |

***

### Events

| Event                               | Parameters       | Emitted when                                   |
| ----------------------------------- | ---------------- | ---------------------------------------------- |
| `FractionalReserveInvested`         | `asset, amount`  | Assets deposited into the yield vault          |
| `FractionalReserveDivested`         | `asset, amount`  | Assets withdrawn from the yield vault          |
| `FractionalReserveVaultUpdated`     | `asset, vault`   | Yield vault configured or changed for an asset |
| `FractionalReserveReserveUpdated`   | `asset, reserve` | Reserve buffer updated for an asset            |
| `FractionalReserveInterestRealized` | `asset, amount`  | Yield forwarded to `interestReceiver`          |

***

### Errors

| Error                              | Condition                                                          |
| ---------------------------------- | ------------------------------------------------------------------ |
| `FullDivestRequired`               | Full divest resulted in fewer assets than `loaned` — strategy loss |
| `FractionalReserveVaultAlreadySet` | `_vault` is already registered for a different asset               |

***

## Usage Examples

### 1. Keeper harvest job

```solidity
interface IHarvester {
    function harvest(address _cusd, address _asset)
        external
        returns (uint256 profit, uint256 loss, uint256 interest);
}

address harvester = 0x...;
address cusd      = 0x...; // cUSD vault proxy
address usdc      = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;

// Called by a Gelato task or cron keeper
(uint256 profit, uint256 loss, uint256 interest) = IHarvester(harvester).harvest(cusd, usdc);
// interest is now in the FeeAuction, available for stcUSD holders to bid on
```

### 2. Checking available liquidity before a large redemption

```solidity
interface IFractionalReserve {
    function loaned(address _asset) external view returns (uint256);
    function claimableInterest(address _asset) external view returns (uint256);
}

interface IVault {
    function availableBalance(address _asset) external view returns (uint256);
}

function checkLiquidity(address vault, address asset)
    external
    view
    returns (uint256 liquid, uint256 investedPrincipal, uint256 pendingYield)
{
    liquid            = IVault(vault).availableBalance(asset);   // on-hand balance
    investedPrincipal = IFractionalReserve(vault).loaned(asset); // in yield vault
    pendingYield      = IFractionalReserve(vault).claimableInterest(asset);
}
```

### 3. Admin: onboarding a new yield vault for USDC

```solidity
interface IFractionalReserve {
    function setFractionalReserveVault(address _asset, address _vault) external;
    function setReserve(address _asset, uint256 _reserve) external;
    function investAll(address _asset) external;
}

address vault = 0x...; // cUSD vault proxy
address usdc  = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address yvUSDC = 0x...; // New ERC4626 vault

// 1. Set the new yield vault (automatically divests old one if set)
IFractionalReserve(vault).setFractionalReserveVault(usdc, yvUSDC);

// 2. Set a 500,000 USDC liquid reserve floor
IFractionalReserve(vault).setReserve(usdc, 500_000e6);

// 3. Deploy idle balance into the new vault
IFractionalReserve(vault).investAll(usdc);
```


# Lender

The Lender is the lending pool that allows whitelisted Operators to borrow backing assets from the cUSD vault against delegated collateral. It tracks debt per Operator per asset, accrues two separate interest streams (vault interest paid to stcUSD holders, restaker interest paid to Delegators), and manages a liquidation system with a time-windowed grace period and linearly increasing bonus. All health factors, LTV ratios, and rates are in ray format where `1e27 = 100%`.

## Mechanics

### Borrow Flow

1. **Realize restaker interest**: Any accrued restaker interest for the Operator is realized first, updating their debt balance before new principal is added.
2. **Max borrow calculation** (if `maxBorrow = true`): `ViewLogic.maxBorrowable` computes remaining capacity based on the Operator's delegation coverage and current LTV.
3. **Validation**: `ValidationLogic.validateBorrow` checks that the Operator is whitelisted, not paused, the asset is not paused, minimum borrow is met, and vault liquidity is sufficient.
4. **State updates**: `IDelegation.setLastBorrow` is called; the Operator is marked as borrowing the reserve.
5. **Asset transfer**: The vault borrows the asset and sends it to `_receiver`; a debt token is minted to the Operator; `reserve.debt` is incremented.

### Repay Flow

1. **Realize restaker interest**: Accrued restaker interest is settled first.
2. **Amount cap**: Repayment is silently capped at the Operator's total debt. If the remaining debt after repayment would fall below `minBorrow`, the repayment is reduced to leave exactly `minBorrow` outstanding.
3. **Repayment allocation** (in priority order):
   * **Vault interest** (`interestRepaid`): Any excess above `reserve.debt + unrealizedInterest` goes to `interestReceiver`.
   * **Restaker interest** (`restakerRepaid`): Settled unrealized restaker interest is sent to the Delegation contract and distributed to Delegators.
   * **Vault principal** (`vaultRepaid`): Repays `reserve.debt` and returns assets to the vault.
4. **Debt token burn**: The debt token is burned for the full `repaid` amount.

### Interest Accrual

Cap uses two separate interest streams, both accrued continuously but realized discretely:

**Vault interest** — paid to stcUSD holders via the FeeAuction:

* Accrual is tracked by the RateOracle's utilization index (time-weighted borrow rate).
* Realized by calling `realizeInterest(_asset)` — permissionless.
* The Lender borrows the interest amount from the vault and sends it directly to `interestReceiver`.

**Restaker interest** — paid to Delegators:

* Accrues per-Operator per-asset: `debtBalance × restakerRate × elapsedTime / SECONDS_IN_YEAR`.
* `restakerRate` is set per-Operator by admin via the oracle.
* Realized on every borrow and repay, or permissionlessly via `realizeRestakerInterest`.
* If vault liquidity is insufficient, the shortfall becomes **unrealized interest** — added to the Operator's debt balance and settled during future repayments.

### Liquidation Flow

Liquidation is a two-step process:

**Step 1 — Open liquidation window:**

1. Anyone calls `openLiquidation(_agent)` when `health < 1e27` (below 100%).
2. `liquidationStart[agent]` is recorded. Reverts if a non-expired window is already open.

**Step 2 — Liquidate:**

1. `liquidate` is callable after `grace` seconds have elapsed since `liquidationStart`.
2. The liquidator repays up to `maxLiquidatable` of the Operator's debt for one asset.
3. A **bonus** is awarded in collateral value: starts at 0 after grace, grows linearly to `bonusCap` by the time `expiry` is reached. Emergency liquidations (health dangerously low) receive the full `bonusCap` immediately.
4. `IDelegation.slash` is called to seize the bonus value from the Operator's Delegators.
5. If health recovers to `≥ 1e27` after the liquidation, the window is automatically closed.

**Emergency liquidations**: If `totalDelegation × emergencyLiquidationThreshold / totalDebt < 1e27`, the grace period is skipped and the full bonus is applied immediately.

### Health Factor

```
health = (totalDelegation × liquidationThreshold) / totalDebt
```

* `health ≥ 1e27` (≥ 100%): Healthy — borrowing allowed, no liquidation.
* `health < 1e27` (< 100%): Undercollateralised — liquidation window can be opened.
* `totalDebt = 0`: Health returns `type(uint256).max`.

All values in USD with 8 decimals. `liquidationThreshold` and `ltv` are set per-Operator by the Delegation contract.

***

## Architecture

### Core Functions

#### `borrow(address _asset, uint256 _amount, address _receiver)`

```solidity
function borrow(address _asset, uint256 _amount, address _receiver) external returns (uint256 borrowed);
```

Called by a whitelisted Operator. `msg.sender` is the Operator. Pass `_amount = 0` with `maxBorrow = true` (via the BorrowParams struct internally) to borrow the maximum available.

| Parameter   | Type      | Description                            |
| ----------- | --------- | -------------------------------------- |
| `_asset`    | `address` | Asset to borrow                        |
| `_amount`   | `uint256` | Amount to borrow in asset decimals     |
| `_receiver` | `address` | Address to receive the borrowed assets |

**Returns**: `borrowed` — actual amount borrowed (may be less if vault liquidity is limited).

***

#### `repay(address _asset, uint256 _amount, address _agent)`

```solidity
function repay(address _asset, uint256 _amount, address _agent) external returns (uint256 repaid);
```

Repay debt on behalf of `_agent`. `msg.sender` provides the assets via `transferFrom`. Pass `_amount = type(uint256).max` to repay the full balance.

| Parameter | Type      | Description                         |
| --------- | --------- | ----------------------------------- |
| `_asset`  | `address` | Asset to repay                      |
| `_amount` | `uint256` | Amount to repay in asset decimals   |
| `_agent`  | `address` | Operator whose debt is being repaid |

**Returns**: `repaid` — actual amount repaid.

***

#### `realizeInterest(address _asset)`

```solidity
function realizeInterest(address _asset) external returns (uint256 actualRealized);
```

Permissionless. Borrows accrued vault interest from the vault and sends it to `interestReceiver` (typically the FeeAuction). Reverts with `ZeroRealization` if there is nothing to realize.

***

#### `realizeRestakerInterest(address _agent, address _asset)`

```solidity
function realizeRestakerInterest(address _agent, address _asset) external returns (uint256 actualRealized);
```

Permissionless. Realizes the accrued restaker interest for `_agent` for `_asset`. Borrows from the vault and sends to the Delegation contract for distribution to Delegators. If vault liquidity is insufficient, the shortfall is recorded as unrealized interest.

***

#### `openLiquidation(address _agent)`

```solidity
function openLiquidation(address _agent) external;
```

Opens the liquidation window for `_agent`. Requires `health < 1e27`. Reverts if an active, non-expired window already exists.

***

#### `closeLiquidation(address _agent)`

```solidity
function closeLiquidation(address _agent) external;
```

Closes the liquidation window when `health ≥ 1e27`. Anyone can call this once the Operator is healthy again.

***

#### `liquidate(address _agent, address _asset, uint256 _amount, uint256 _minLiquidatedValue)`

```solidity
function liquidate(
    address _agent,
    address _asset,
    uint256 _amount,
    uint256 _minLiquidatedValue
) external returns (uint256 liquidatedValue);
```

Repays up to `_amount` of `_agent`'s debt in `_asset` and receives slashed collateral value in return. Requires an open liquidation window past the grace period.

| Parameter             | Type      | Description                                                      |
| --------------------- | --------- | ---------------------------------------------------------------- |
| `_agent`              | `address` | Operator to liquidate                                            |
| `_asset`              | `address` | Asset to repay on the Operator's behalf                          |
| `_amount`             | `uint256` | Amount of `_asset` to repay (capped at `maxLiquidatable`)        |
| `_minLiquidatedValue` | `uint256` | Minimum USD value of collateral to receive (slippage protection) |

**Returns**: `liquidatedValue` — USD value (8 decimals) of collateral slashed and awarded to the liquidator.

***

#### View Functions

**`agent(address _agent)`**

```solidity
function agent(address _agent) external view returns (
    uint256 totalDelegation,
    uint256 totalSlashableCollateral,
    uint256 totalDebt,
    uint256 ltv,
    uint256 liquidationThreshold,
    uint256 health
);
```

Returns a complete snapshot of the Operator's collateral and health position. All USD values use 8 decimals; ratios are in ray (`1e27 = 100%`).

***

**`maxBorrowable(address _agent, address _asset)`**

```solidity
function maxBorrowable(address _agent, address _asset) external view returns (uint256 maxBorrowableAmount);
```

Returns the maximum additional amount of `_asset` the Operator can borrow given their current delegation and debt.

***

**`maxLiquidatable(address _agent, address _asset)`**

```solidity
function maxLiquidatable(address _agent, address _asset) external view returns (uint256 maxLiquidatableAmount);
```

Returns the maximum amount of `_asset` that can be liquidated against `_agent` in a single call to reach `targetHealth`.

***

**`bonus(address _agent)`**

```solidity
function bonus(address _agent) external view returns (uint256 maxBonus);
```

Returns the current liquidation bonus in ray format. Grows linearly from 0 (at grace) to `bonusCap` (at expiry). Returns `bonusCap` immediately for emergency liquidations.

***

**`debt(address _agent, address _asset)`**

```solidity
function debt(address _agent, address _asset) external view returns (uint256 totalDebt);
```

Returns the Operator's total debt for `_asset` including accrued restaker interest not yet settled.

***

**`accruedRestakerInterest(address _agent, address _asset)`**

```solidity
function accruedRestakerInterest(address _agent, address _asset) external view returns (uint256 accruedInterest);
```

Returns the restaker interest accrued since the last realization: `debt × rate × elapsed / SECONDS_IN_YEAR`.

***

**`maxRealization(address _asset)` / `maxRestakerRealization(address _agent, address _asset)`**

```solidity
function maxRealization(address _asset) external view returns (uint256 _maxRealization);
function maxRestakerRealization(address _agent, address _asset)
    external view returns (uint256 newRealizedInterest, uint256 newUnrealizedInterest);
```

Preview functions for how much interest can be realized right now vs. how much would become unrealized due to vault liquidity constraints.

***

#### Admin Functions

| Function                                | Description                                                     |
| --------------------------------------- | --------------------------------------------------------------- |
| `addAsset(AddAssetParams)`              | Add a new reserve (asset, vault, debt token, interest receiver) |
| `removeAsset(address)`                  | Remove a reserve with zero outstanding debt                     |
| `pauseAsset(address, bool)`             | Pause or unpause borrowing for an asset                         |
| `setInterestReceiver(address, address)` | Update the vault interest recipient for an asset                |
| `setMinBorrow(address, uint256)`        | Set minimum borrow floor for an asset                           |
| `setGrace(uint256)`                     | Update the liquidation grace period (seconds)                   |
| `setExpiry(uint256)`                    | Update the liquidation expiry window (seconds)                  |
| `setBonusCap(uint256)`                  | Update the maximum liquidation bonus (ray)                      |

***

### Data Structures

#### `LenderStorage`

```solidity
struct LenderStorage {
    address delegation;
    address oracle;
    mapping(address => ReserveData) reservesData;
    mapping(uint256 => address) reservesList;
    uint16 reservesCount;
    mapping(address => AgentConfigurationMap) agentConfig;
    mapping(address => uint256) liquidationStart;
    uint256 targetHealth;
    uint256 grace;
    uint256 expiry;
    uint256 bonusCap;
    uint256 emergencyLiquidationThreshold;
}
```

| Field                           | Type      | Description                                                       |
| ------------------------------- | --------- | ----------------------------------------------------------------- |
| `delegation`                    | `address` | Delegation contract — provides coverage, LTV, and slash functions |
| `oracle`                        | `address` | Oracle — provides asset prices and per-Operator restaker rates    |
| `reservesData`                  | `mapping` | Reserve configuration and state per asset                         |
| `targetHealth`                  | `uint256` | Health ratio to restore after liquidation (ray, `1e27 = 100%`)    |
| `grace`                         | `uint256` | Seconds after `openLiquidation` before liquidation is allowed     |
| `expiry`                        | `uint256` | Seconds after which a liquidation window expires if unused        |
| `bonusCap`                      | `uint256` | Maximum liquidation bonus (ray)                                   |
| `emergencyLiquidationThreshold` | `uint256` | Ratio below which emergency liquidation rules apply (ray)         |

#### `ReserveData`

```solidity
struct ReserveData {
    uint256 id;
    address vault;
    address debtToken;
    address interestReceiver;
    uint8 decimals;
    bool paused;
    uint256 debt;
    uint256 totalUnrealizedInterest;
    mapping(address => uint256) unrealizedInterest;
    mapping(address => uint256) lastRealizationTime;
    uint256 minBorrow;
}
```

| Field                     | Description                                                                                   |
| ------------------------- | --------------------------------------------------------------------------------------------- |
| `vault`                   | cUSD vault that holds the backing assets for this reserve                                     |
| `debtToken`               | Non-transferrable ERC20 tracking each Operator's outstanding principal                        |
| `interestReceiver`        | Receives vault interest (typically the FeeAuction)                                            |
| `debt`                    | Total vault principal currently borrowed across all Operators                                 |
| `totalUnrealizedInterest` | Sum of all per-Operator unrealized restaker interest                                          |
| `unrealizedInterest`      | Per-Operator restaker interest that couldn't be realized due to illiquidity                   |
| `lastRealizationTime`     | Timestamp of the last restaker interest realization per Operator                              |
| `minBorrow`               | Minimum outstanding balance — partial repayments that would leave less than this are rejected |

***

## Usage Examples

### 1. Operator borrows an asset

```solidity
interface ILender {
    function borrow(address _asset, uint256 _amount, address _receiver) external returns (uint256 borrowed);
    function maxBorrowable(address _agent, address _asset) external view returns (uint256);
    function agent(address _agent) external view returns (
        uint256, uint256, uint256, uint256, uint256, uint256 health
    );
}

address lender = 0x...;
address usdc   = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;

// Check health before borrowing
(,,,,,uint256 health) = ILender(lender).agent(msg.sender);
require(health >= 1e27, "Undercollateralised");

// Check capacity
uint256 capacity = ILender(lender).maxBorrowable(msg.sender, usdc);

// Borrow
uint256 borrowed = ILender(lender).borrow(usdc, capacity, msg.sender);
```

### 2. Keeper realizes vault interest

```solidity
interface ILender {
    function maxRealization(address _asset) external view returns (uint256);
    function realizeInterest(address _asset) external returns (uint256 actualRealized);
}

address lender = 0x...;
address usdc   = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;

uint256 available = ILender(lender).maxRealization(usdc);
if (available > 0) {
    ILender(lender).realizeInterest(usdc);
    // Interest is now in the FeeAuction, available for stcUSD holders
}
```

### 3. Liquidator opens and executes a liquidation

```solidity
interface ILender {
    function agent(address) external view returns (uint256, uint256, uint256, uint256, uint256, uint256);
    function openLiquidation(address _agent) external;
    function maxLiquidatable(address _agent, address _asset) external view returns (uint256);
    function bonus(address _agent) external view returns (uint256);
    function liquidate(address _agent, address _asset, uint256 _amount, uint256 _minValue)
        external returns (uint256 liquidatedValue);
    function grace() external view returns (uint256);
    function liquidationStart(address) external view returns (uint256);
}

address lender = 0x...;
address agent  = 0x...; // Undercollateralised Operator
address usdc   = 0x...;

// Step 1: Open the liquidation window
(,,,,,uint256 health) = ILender(lender).agent(agent);
require(health < 1e27, "Operator is healthy");
ILender(lender).openLiquidation(agent);

// Step 2: Wait for grace period, then liquidate
// (off-chain: poll until block.timestamp >= liquidationStart + grace)
uint256 maxAmount = ILender(lender).maxLiquidatable(agent, usdc);
IERC20(usdc).approve(lender, maxAmount);

uint256 collateralValue = ILender(lender).liquidate(agent, usdc, maxAmount, 0);
// collateralValue in USD (8 decimals) has been slashed from the Operator's Delegators
```


# Delegation

The Delegation contract is the backbone of Cap's financial guarantee market. It tracks how much slashable collateral Delegators have committed to each Operator across EigenLayer and Symbiotic, routes restaker interest rewards to Delegators, and executes slashing when an Operator is liquidated. Each Operator is assigned a network — either the EigenLayer AVS or the Symbiotic network — and their borrowing capacity is determined by the USD value of collateral their Delegators have staked on that network.

## Mechanics

### Coverage and Borrowing Capacity

Each Operator's borrowing capacity is derived from their **coverage** — the minimum of several collateral snapshots designed to prevent gaming:

```
coverage = min(
    currentEpochCoverage,       // collateral at the current epoch boundary
    slashableCollateral,        // collateral at slashTimestamp (most conservative)
    currentDelegation,          // live delegation value
    lastBorrowMinusOneDelegation, // delegation at (lastBorrow - 1 second)
    coverageCap                 // admin-set USD cap
)
```

All USD values use 8 decimals. Coverage is queried from the network middleware contract (Symbiotic or EigenLayer), which aggregates across all Delegators backing that Operator.

The Lender computes `maxBorrowable = coverage × ltv / 1e27`. The **LTV buffer** (default 5%) ensures `liquidationThreshold ≥ ltv + ltvBuffer`, maintaining a gap between the borrow limit and the liquidation trigger.

### Slash Timestamp

The slash timestamp is used to determine which snapshot of collateral is slashable during a liquidation. It is the most recent of:

* `(epoch - 1) × epochDuration` — the previous epoch boundary
* `lastBorrow - 1 second` — one second before the Operator's most recent borrow

This conservative snapshot prevents Delegators from front-running liquidations by rapidly removing collateral after an Operator borrows.

### Reward Distribution

When an Operator repays restaker interest, the Lender transfers the asset to the Delegation contract and calls `distributeRewards`. The contract forwards the full balance to the Operator's network middleware, which distributes proportionally to each Delegator based on their collateral share. If `coverage == 0` (no active Delegators), rewards go to the `feeRecipient` fallback address.

### Slash Execution

When the Lender liquidates an Operator, it calls `slash(_agent, _liquidator, _amount)`:

1. The `slashTimestamp` is computed for the Operator.
2. The network middleware's `slashableCollateral` is queried at that timestamp.
3. A `slashShare` ratio is computed: `_amount / networkSlashableCollateral` (capped at 100%).
4. The network middleware's `slash` function is called with the share — which propagates to the underlying EigenLayer/Symbiotic slashing mechanisms.

### Epochs

The epoch system provides a time window during which collateral state is considered stable for slashing purposes. `epochDuration` is set at initialization. The current epoch is `block.timestamp / epochDuration`. Symbiotic vaults use epoch-based accounting for slashing; the Delegation contract aligns with this by using the previous epoch boundary as one of the slash timestamp candidates.

***

## Architecture

### Delegation (core)

#### `slash(address _agent, address _liquidator, uint256 _amount)`

```solidity
function slash(address _agent, address _liquidator, uint256 _amount) external;
```

Access-controlled (Lender only). Executes a slash against the Operator's network. Reverts with `NoSlashableCollateral` if no slashable collateral exists at the slash timestamp.

| Parameter     | Type      | Description                                    |
| ------------- | --------- | ---------------------------------------------- |
| `_agent`      | `address` | Operator being liquidated                      |
| `_liquidator` | `address` | Address receiving the slashed collateral value |
| `_amount`     | `uint256` | USD value (8 decimals) to slash                |

***

#### `distributeRewards(address _agent, address _asset)`

```solidity
function distributeRewards(address _agent, address _asset) external;
```

Permissionless (called by the Lender). Transfers the contract's full balance of `_asset` to the Operator's network for distribution to Delegators. Falls back to `feeRecipient` if coverage is zero.

***

#### `addAgent(address _agent, address _network, uint256 _ltv, uint256 _liquidationThreshold)`

```solidity
function addAgent(address _agent, address _network, uint256 _ltv, uint256 _liquidationThreshold) external;
```

Access-controlled. Registers a new Operator with their network, LTV, and liquidation threshold. Both values are in ray format (`1e27 = 100%`). Enforces `liquidationThreshold ≥ ltv + ltvBuffer` and `liquidationThreshold ≤ 1e27`.

***

#### `modifyAgent(address _agent, uint256 _ltv, uint256 _liquidationThreshold)`

```solidity
function modifyAgent(address _agent, uint256 _ltv, uint256 _liquidationThreshold) external;
```

Access-controlled. Updates an existing Operator's LTV and liquidation threshold. Same validation as `addAgent`.

***

#### `registerNetwork(address _network)`

```solidity
function registerNetwork(address _network) external;
```

Access-controlled. Registers a new network middleware contract (EigenLayer AVS or Symbiotic network). An Operator can only be assigned to a registered network.

***

#### View Functions

| Function                               | Returns                | Description                                                  |
| -------------------------------------- | ---------------------- | ------------------------------------------------------------ |
| `coverage(address _agent)`             | `uint256` (USD, 8 dec) | Conservative collateral coverage used for borrowing capacity |
| `slashableCollateral(address _agent)`  | `uint256` (USD, 8 dec) | Collateral slashable at the current slash timestamp          |
| `ltv(address _agent)`                  | `uint256` (ray)        | Maximum LTV ratio for the Operator                           |
| `liquidationThreshold(address _agent)` | `uint256` (ray)        | Health ratio below which liquidation can be triggered        |
| `slashTimestamp(address _agent)`       | `uint48`               | Most conservative timestamp for collateral snapshots         |
| `epoch()`                              | `uint256`              | Current epoch index (`block.timestamp / epochDuration`)      |
| `epochDuration()`                      | `uint256`              | Epoch length in seconds                                      |
| `ltvBuffer()`                          | `uint256` (ray)        | Minimum gap between LTV and liquidation threshold            |
| `coverageCap(address _agent)`          | `uint256` (USD, 8 dec) | Admin-set USD cap on an Operator's coverage                  |
| `collateral(address _agent)`           | `address`              | Collateral token address for the Operator's network          |
| `networks(address _agent)`             | `address`              | Network middleware contract for the Operator                 |
| `agents()`                             | `address[]`            | All registered Operator addresses                            |

***

#### Admin Functions

| Function                           | Description                                      |
| ---------------------------------- | ------------------------------------------------ |
| `setLastBorrow(address)`           | Records borrow timestamp (Lender only)           |
| `setLtvBuffer(uint256)`            | Update the LTV safety buffer (ray, must be > 1%) |
| `setFeeRecipient(address)`         | Update the fallback reward recipient             |
| `setCoverageCap(address, uint256)` | Set a USD cap on an Operator's coverage          |

***

### Data Structures

#### `DelegationStorage`

```solidity
struct DelegationStorage {
    EnumerableSet.AddressSet agents;
    mapping(address => AgentData) agentData;
    EnumerableSet.AddressSet networks;
    address oracle;
    uint256 epochDuration;
    uint256 ltvBuffer;
    address feeRecipient;
    mapping(address => uint256) coverageCap;
}
```

| Field           | Description                                                           |
| --------------- | --------------------------------------------------------------------- |
| `agents`        | Set of all registered Operators                                       |
| `agentData`     | Per-Operator config: network, LTV, liquidation threshold, last borrow |
| `networks`      | Set of all registered network middleware contracts                    |
| `oracle`        | Oracle contract used for price feeds (passed to network middleware)   |
| `epochDuration` | Epoch length in seconds — aligns with Symbiotic's slash window        |
| `ltvBuffer`     | Minimum gap between LTV and liquidation threshold (ray, default 5%)   |
| `feeRecipient`  | Receives rewards when an Operator has no active coverage              |
| `coverageCap`   | Per-Operator USD cap on coverage                                      |

#### `AgentData`

```solidity
struct AgentData {
    address network;
    uint256 ltv;
    uint256 liquidationThreshold;
    uint256 lastBorrow;
}
```

| Field                  | Description                                            |
| ---------------------- | ------------------------------------------------------ |
| `network`              | Network middleware contract (EigenLayer or Symbiotic)  |
| `ltv`                  | Maximum loan-to-value ratio in ray (`1e27 = 100%`)     |
| `liquidationThreshold` | Health threshold triggering liquidation in ray         |
| `lastBorrow`           | `block.timestamp` of the Operator's most recent borrow |

***

### EigenLayer Integration

The EigenLayer integration consists of three contracts:

**`EigenAgentManager`** — The per-Operator contract deployed when an Operator registers on EigenLayer. It tracks which EigenLayer operator address backs this Cap Operator and queries the AVS's allocation system for slashable collateral.

**`EigenOperator`** — Manages an EigenLayer operator's registration into the Cap AVS (opt-in to the AVS, register operator sets, manage allocation delays).

**`EigenServiceManager`** — The EigenLayer AVS entry point. Registered as the `ServiceManager` on EigenLayer. Handles slash execution by interacting with EigenLayer's `AllocationManager`.

Key EigenLayer concepts:

* Operators opt into the Cap AVS via `EigenServiceManager`
* Delegators stake into EigenLayer strategies (e.g. stETH, EIGEN) and delegate to an EigenLayer operator
* Slashable collateral is the USD value of delegated stake that Cap can slash via the `AllocationManager`

***

### Symbiotic Integration

The Symbiotic integration consists of five contracts:

**`SymbioticAgentManager`** — Tracks collateral for Operators backed by Symbiotic Delegators. Queries Symbiotic vault/delegator contracts for slashable amounts.

**`SymbioticOperator`** — Handles Symbiotic operator registration, opt-in to vaults and networks.

**`SymbioticNetwork`** — The Symbiotic network contract. Cap is registered as a Symbiotic network; this contract manages network-level configuration.

**`SymbioticNetworkMiddleware`** — The primary integration point. Implements `coverage`, `slashableCollateral`, `slash`, and `distributeRewards` for the Symbiotic path. Aggregates collateral across all Symbiotic vaults that have allocated to Cap.

**`CapSymbioticVaultFactory`** — Deploys new Symbiotic collateral vaults compatible with Cap's network. Delegators use these vaults to stake collateral that backs Cap Operators.

***

## Usage Examples

### 1. Querying an Operator's borrowing position

```solidity
interface IDelegation {
    function coverage(address _agent) external view returns (uint256);
    function slashableCollateral(address _agent) external view returns (uint256);
    function ltv(address _agent) external view returns (uint256);
    function liquidationThreshold(address _agent) external view returns (uint256);
}

address delegation = 0x...;
address operator   = 0x...;

uint256 coverageUsd  = IDelegation(delegation).coverage(operator);
// coverageUsd in USD with 8 decimals, e.g. 1_000_000e8 = $1M

uint256 maxBorrowUsd = coverageUsd * IDelegation(delegation).ltv(operator) / 1e27;
// maxBorrowUsd = how much USD value the Operator can borrow in total
```

### 2. Admin: onboarding a new Operator

```solidity
interface IDelegation {
    function registerNetwork(address _network) external;
    function addAgent(address _agent, address _network, uint256 _ltv, uint256 _liquidationThreshold) external;
    function setCoverageCap(address _agent, uint256 _coverageCap) external;
}

address delegation = 0x...;
address network    = 0x...; // SymbioticNetworkMiddleware or EigenAgentManager
address operator   = 0x...;

// 1. Register the network if not already registered
IDelegation(delegation).registerNetwork(network);

// 2. Add the Operator: 80% LTV, 90% liquidation threshold, both in ray
IDelegation(delegation).addAgent(operator, network, 0.8e27, 0.9e27);

// 3. Set a $10M USD coverage cap
IDelegation(delegation).setCoverageCap(operator, 10_000_000e8);
```

### 3. Checking slash eligibility before liquidation

```solidity
interface IDelegation {
    function slashableCollateral(address _agent) external view returns (uint256);
    function slashTimestamp(address _agent) external view returns (uint48);
}

interface ILender {
    function agent(address _agent) external view returns (
        uint256, uint256 slashable, uint256 debt, uint256, uint256, uint256 health
    );
}

address delegation = 0x...;
address lender     = 0x...;
address operator   = 0x...;

(, uint256 slashable, uint256 debt,,, uint256 health) = ILender(lender).agent(operator);
uint256 slashableRaw = IDelegation(delegation).slashableCollateral(operator);

// health < 1e27 = undercollateralised, liquidation can be opened
// slashableRaw > 0 = collateral exists to cover the liquidation
```


# Fee Auction

The FeeAuction distributes protocol yield — collected as backing assets from Operator interest payments — to stcUSD holders via a Dutch auction mechanism. stcUSD holders bid cUSD at a continuously decaying price to purchase the accumulated backing assets at a discount, creating a fixed-rate yield experience. Each successful purchase resets the auction at double the settlement price, establishing a self-calibrating price discovery cycle. The FeeReceiver acts as the upstream collector, receiving vault interest from the Lender and holding it until the FeeAuction sweeps it.

## Mechanics

### Dutch Auction Price Curve

The auction price decays linearly from `startPrice` to 10% of `startPrice` over `duration` seconds:

```
price = startPrice × (1 - 0.9 × elapsed / duration)
```

* At `t = 0`: price = `startPrice` (100%)
* At `t = duration`: price = `startPrice × 0.1` (10%)
* Price never reaches zero — it floors at 10% of the start price

All price values are in the `paymentToken` (cUSD) with 18 decimals.

### Buy Flow

1. **Price check**: `currentPrice()` is compared against caller's `_maxPrice`. Reverts with `InvalidPrice` if `currentPrice > _maxPrice`.
2. **Validation**: Asset list must be non-empty and match `_minAmounts` length; `_receiver` must be non-zero; deadline must be in the future.
3. **New auction reset**: `startTimestamp` is set to `block.timestamp`; `startPrice` is set to `max(currentPrice × 2, minStartPrice)`.
4. **Asset transfer**: Full balances of each requested `_asset` held by the FeeAuction are transferred to `_receiver`. Reverts with `InsufficientBalance` if any asset balance is below `_minAmounts[i]`.
5. **Payment**: `currentPrice` in `paymentToken` is pulled from `msg.sender` and sent to `paymentRecipient` (the stcUSD distribution contract or treasury).

### Self-Calibrating Start Price

Each successful purchase sets the next auction's `startPrice` to `2 × settlementPrice`. This means:

* If buyers wait for a deep discount, the next auction starts low.
* If buyers buy early, the next auction starts high.
* The auction converges on a market-clearing rate for protocol yield.

The `minStartPrice` floor prevents the auction from becoming trivially cheap during low-activity periods.

### Interest Flow End-to-End

```
Operator repays interest
        ↓
Lender sends vault interest to interestReceiver (FeeReceiver)
        ↓
FeeReceiver holds backing assets (USDC, wETH, etc.)
        ↓
Buyer calls buy() on FeeAuction, pays cUSD at current price
        ↓
Buyer receives backing assets; cUSD goes to paymentRecipient
        ↓
paymentRecipient distributes yield to stcUSD holders
```

***

## Architecture

### FeeAuction

#### Core Functions

**`buy(uint256 _maxPrice, address[] _assets, uint256[] _minAmounts, address _receiver, uint256 _deadline)`**

```solidity
function buy(
    uint256 _maxPrice,
    address[] calldata _assets,
    uint256[] calldata _minAmounts,
    address _receiver,
    uint256 _deadline
) external;
```

Purchase all accumulated fee assets at the current Dutch auction price.

| Parameter     | Type        | Description                                                             |
| ------------- | ----------- | ----------------------------------------------------------------------- |
| `_maxPrice`   | `uint256`   | Maximum cUSD to pay; reverts if `currentPrice > _maxPrice`              |
| `_assets`     | `address[]` | List of backing assets to receive from the auction contract             |
| `_minAmounts` | `uint256[]` | Minimum balance of each asset to accept (slippage protection per asset) |
| `_receiver`   | `address`   | Address to receive the purchased backing assets                         |
| `_deadline`   | `uint256`   | Unix timestamp after which the tx reverts                               |

***

**`currentPrice()`**

```solidity
function currentPrice() external view returns (uint256 price);
```

Returns the current auction price in `paymentToken` units. Decays linearly from `startPrice` to `startPrice × 0.1` over `duration`. Use this before calling `buy` to set `_maxPrice`.

***

#### Admin Functions

| Function                    | Description                                                        |
| --------------------------- | ------------------------------------------------------------------ |
| `setStartPrice(uint256)`    | Manually reset the auction start price (must be ≥ `minStartPrice`) |
| `setDuration(uint256)`      | Update the auction duration in seconds                             |
| `setMinStartPrice(uint256)` | Update the minimum allowed start price floor                       |
| `setPaymentToken(address)`  | Change the payment token (cUSD by default)                         |

***

#### View Functions

| Function             | Returns   | Description                            |
| -------------------- | --------- | -------------------------------------- |
| `currentPrice()`     | `uint256` | Current auction price in payment token |
| `startPrice()`       | `uint256` | Starting price of the current auction  |
| `startTimestamp()`   | `uint256` | When the current auction started       |
| `duration()`         | `uint256` | Auction duration in seconds            |
| `minStartPrice()`    | `uint256` | Floor price for future auction starts  |
| `paymentToken()`     | `address` | Token buyers pay with (cUSD)           |
| `paymentRecipient()` | `address` | Receives the cUSD payment from buyers  |

***

#### Data Structures

**`FeeAuctionStorage`**

```solidity
struct FeeAuctionStorage {
    address paymentToken;
    address paymentRecipient;
    uint256 startPrice;
    uint256 startTimestamp;
    uint256 duration;
    uint256 minStartPrice;
}
```

| Field              | Description                                                                      |
| ------------------ | -------------------------------------------------------------------------------- |
| `paymentToken`     | ERC20 token buyers pay with — cUSD                                               |
| `paymentRecipient` | Receives cUSD from every purchase — distributes to stcUSD holders                |
| `startPrice`       | Current auction starting price in `paymentToken` (18 decimals)                   |
| `startTimestamp`   | `block.timestamp` of the last purchase (or initialization)                       |
| `duration`         | Seconds for the price to decay from `startPrice` to `startPrice × 0.1`           |
| `minStartPrice`    | Minimum `startPrice` floor — prevents the auction going below a meaningful level |

***

#### Events

| Event              | Parameters                           | Emitted when                  |
| ------------------ | ------------------------------------ | ----------------------------- |
| `Buy`              | `buyer, price, assets[], balances[]` | A successful purchase is made |
| `SetDuration`      | `duration`                           | Auction duration updated      |
| `SetMinStartPrice` | `minStartPrice`                      | Minimum start price updated   |
| `SetPaymentToken`  | `paymentToken`                       | Payment token changed         |
| `SetStartPrice`    | `startPrice`                         | Start price manually reset    |

***

#### Errors

| Error                 | Condition                                                  |
| --------------------- | ---------------------------------------------------------- |
| `InvalidPrice`        | `currentPrice > _maxPrice`                                 |
| `InvalidAssets`       | Empty asset list or `assets.length ≠ minAmounts.length`    |
| `InvalidDeadline`     | `_deadline < block.timestamp`                              |
| `InvalidReceiver`     | `_receiver == address(0)`                                  |
| `InvalidStartPrice`   | `setStartPrice` called with value below `minStartPrice`    |
| `InvalidPaymentToken` | `setPaymentToken` called with zero address                 |
| `InsufficientBalance` | Asset balance in the contract is below `_minAmounts[i]`    |
| `NoMinStartPrice`     | `initialize` called with `_minStartPrice == 0`             |
| `NoDuration`          | `initialize` or `setDuration` called with `_duration == 0` |

***

### FeeReceiver

The FeeReceiver is a simple holding contract that sits between the Lender and the FeeAuction. The Lender sends vault interest to the FeeReceiver, which holds the assets until a buyer triggers the FeeAuction sweep.

It has no complex logic — its purpose is to batch up interest from multiple reserves before each auction, so buyers receive a meaningful bundle of assets per purchase rather than tiny per-asset drips.

***

## Usage Examples

### 1. Buying auction assets (stcUSD holder / arbitrageur)

```solidity
interface IFeeAuction {
    function currentPrice() external view returns (uint256);
    function buy(
        uint256 _maxPrice,
        address[] calldata _assets,
        uint256[] calldata _minAmounts,
        address _receiver,
        uint256 _deadline
    ) external;
}

address feeAuction = 0x...;
address cusd       = 0x...;
address usdc       = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address weth       = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

// Check current price
uint256 price = IFeeAuction(feeAuction).currentPrice();

// Approve payment
IERC20(cusd).approve(feeAuction, price);

// Specify assets to buy and minimum amounts (0 = accept any amount)
address[] memory assets     = new address[](2);
uint256[] memory minAmounts = new uint256[](2);
assets[0] = usdc; assets[1] = weth;
minAmounts[0] = 0; minAmounts[1] = 0;

// Buy — receive all USDC and WETH held by the FeeAuction
IFeeAuction(feeAuction).buy(
    price,         // exact price (no slippage on price)
    assets,
    minAmounts,
    msg.sender,
    block.timestamp + 1 minutes
);
```

### 2. Monitoring the auction price off-chain

```solidity
interface IFeeAuction {
    function currentPrice() external view returns (uint256);
    function startPrice() external view returns (uint256);
    function startTimestamp() external view returns (uint256);
    function duration() external view returns (uint256);
}

function getAuctionState(address feeAuction) external view returns (
    uint256 price,
    uint256 discountPct,   // how far the price has decayed (0-90%)
    uint256 secondsLeft    // seconds until price reaches floor
) {
    price = IFeeAuction(feeAuction).currentPrice();
    uint256 start     = IFeeAuction(feeAuction).startPrice();
    uint256 elapsed   = block.timestamp - IFeeAuction(feeAuction).startTimestamp();
    uint256 dur       = IFeeAuction(feeAuction).duration();

    discountPct = elapsed >= dur ? 90 : (elapsed * 90 / dur);
    secondsLeft = elapsed >= dur ? 0 : dur - elapsed;
}
```

### 3. Full interest flow — from Operator repayment to stcUSD yield

```
1. Operator calls Lender.repay(usdc, amount, operator)
   → Lender sends vault interest to FeeReceiver (interestReceiver)

2. FeeReceiver now holds USDC (and other backing assets from other reserves)

3. Buyer calls FeeAuction.buy(maxPrice, [usdc, weth], [0, 0], buyer, deadline)
   → Buyer pays cUSD at currentPrice
   → Buyer receives USDC + WETH from FeeAuction
   → cUSD goes to paymentRecipient

4. paymentRecipient distributes cUSD yield to stcUSD holders
   (yield rate = cUSD paid / total stcUSD supply, annualised)
```


# Oracle

The Oracle system provides two types of data to the Cap protocol: **asset prices** (USD values for backing assets, collateral tokens, and Cap-native tokens) and **interest rates** (borrow rates for vault utilization and per-Operator restaker rates). Both systems share the same adapter architecture — a central contract stores a payload per asset, and delegates the actual calculation to a stateless library via `staticcall`. This makes the oracle fully composable: any data source can be integrated by deploying a new adapter library.

## Mechanics

### Adapter Architecture

Each asset has an `OracleData` struct stored on-chain containing two fields:

* `adapter` — the address of the library or contract that computes the value
* `payload` — ABI-encoded calldata passed to the adapter

When a price or rate is requested, the Oracle does a low-level `staticcall` to `adapter(payload)`. Errors in the adapter are silently swallowed — a failed call returns zero rather than reverting. This means any data source failure falls through to the backup rather than halting the protocol.

### Price Oracle: Primary and Backup

Each asset has two `OracleData` slots — primary and backup. `getPrice` attempts the primary first:

1. Call `primary.adapter` with `primary.payload`.
2. If the returned price is zero **or** the timestamp is older than `staleness[asset]`, fall through.
3. Call `backup.adapter` with `backup.payload`.
4. If backup also fails or is stale, revert with `PriceError`.

All prices are normalized to **8 decimals** (USD). Adapters are responsible for decimal conversion.

### Rate Oracle: Market and Utilization

Rates drive two separate interest streams:

* **Market rate** — an external floor rate (e.g. Aave borrow rate). Used as a benchmark.
* **Utilization rate** — derived from the cUSD vault's time-weighted utilization (via VaultAdapter). This is the actual rate Operators pay on vault interest.

Both are fetched via the same adapter pattern, but rate adapters use `call` rather than `staticcall` because VaultAdapter writes state (it updates the utilization index snapshot).

Additionally, two admin-set rates are stored directly:

* `benchmarkRate[asset]` — a manual floor rate per asset (ray)
* `restakerRate[agent]` — per-Operator restaker interest rate (ray)

***

## Architecture

### PriceOracle

Abstract contract. Inherited by the central `Oracle` contract.

#### `getPrice(address _asset)`

```solidity
function getPrice(address _asset) external view returns (uint256 price, uint256 lastUpdated);
```

Returns the asset's USD price (8 decimals) and the timestamp of the last update. Tries the primary adapter first; falls back to the backup if the primary returns zero or a stale price. Reverts with `PriceError` if both fail.

***

#### Admin Functions

| Function                                               | Description                                      |
| ------------------------------------------------------ | ------------------------------------------------ |
| `setPriceOracleData(address _asset, OracleData)`       | Set primary price adapter + payload for an asset |
| `setPriceBackupOracleData(address _asset, OracleData)` | Set backup price adapter + payload for an asset  |
| `setStaleness(address _asset, uint256 _staleness)`     | Set max age (seconds) for a valid price          |

***

#### View Functions

| Function                         | Returns      | Description                                 |
| -------------------------------- | ------------ | ------------------------------------------- |
| `priceOracleData(address)`       | `OracleData` | Primary adapter + payload for the asset     |
| `priceBackupOracleData(address)` | `OracleData` | Backup adapter + payload for the asset      |
| `staleness(address)`             | `uint256`    | Max acceptable age in seconds for the price |

***

### RateOracle

Abstract contract. Inherited by the central `Oracle` contract.

#### `marketRate(address _asset)`

```solidity
function marketRate(address _asset) external returns (uint256 rate);
```

Returns the current market rate for `_asset` in ray format (`1e27 = 100%` annualised). Calls the configured market adapter (e.g. AaveAdapter). Returns zero if the adapter fails.

***

#### `utilizationRate(address _asset)`

```solidity
function utilizationRate(address _asset) external returns (uint256 rate);
```

Returns the utilization-based interest rate for `_asset` in ray. Calls the configured utilization adapter (VaultAdapter). This is the rate Operators pay on borrowed vault principal.

***

#### Admin Functions

| Function                                               | Description                                          |
| ------------------------------------------------------ | ---------------------------------------------------- |
| `setMarketOracleData(address _asset, OracleData)`      | Configure the market rate adapter + payload          |
| `setUtilizationOracleData(address _asset, OracleData)` | Configure the utilization rate adapter + payload     |
| `setBenchmarkRate(address _asset, uint256 _rate)`      | Set a manual benchmark rate floor (ray)              |
| `setRestakerRate(address _agent, uint256 _rate)`       | Set the restaker interest rate for an Operator (ray) |

***

#### View Functions

| Function                         | Returns         | Description                           |
| -------------------------------- | --------------- | ------------------------------------- |
| `benchmarkRate(address)`         | `uint256` (ray) | Manually set rate floor for the asset |
| `restakerRate(address)`          | `uint256` (ray) | Per-Operator restaker interest rate   |
| `marketOracleData(address)`      | `OracleData`    | Market adapter + payload              |
| `utilizationOracleData(address)` | `OracleData`    | Utilization adapter + payload         |

***

### Data Structures

#### `OracleData`

```solidity
struct OracleData {
    address adapter;
    bytes payload;
}
```

| Field     | Description                                                                    |
| --------- | ------------------------------------------------------------------------------ |
| `adapter` | Contract or library address called via `staticcall` (prices) or `call` (rates) |
| `payload` | ABI-encoded calldata passed directly to the adapter                            |

***

## Price Adapters

### ChainlinkAdapter

Reads a Chainlink price feed aggregator. Normalizes the returned value to 8 decimals regardless of the feed's native decimals.

**Payload**: `abi.encodeCall(ChainlinkAdapter.price, (_source))` where `_source` is the Chainlink aggregator address.

**Returns**: `(uint256 price, uint256 lastUpdated)` — price in 8 decimals, `lastUpdated` from `latestRoundData`.

***

### ChainlinkAdapterChained

Multiplies multiple Chainlink feeds together to derive a cross-rate. For example, to price an asset quoted in ETH: feed `A/ETH` × feed `ETH/USD`. The final price is normalized to 8 decimals; `lastUpdated` is the minimum across all feeds (most conservative).

**Payload**: `abi.encodeCall(ChainlinkAdapterChained.price, (_sources))` where `_sources` is an array of aggregator addresses.

***

### wstETHAdapter

Prices wstETH by reading the stETH/USD Chainlink feed and multiplying by the stETH-to-wstETH conversion ratio from the Lido contract (`getPooledEthByShares(1 ether)`).

**Payload**: `abi.encodeCall(wstETHAdapter.price, (_source, _stETH))` where `_source` is the stETH Chainlink aggregator and `_stETH` is the stETH token address.

***

### CapTokenAdapter

Prices a cUSD vault token (e.g. cUSD itself) as the weighted average of its backing assets:

```
price = Σ(totalSupplies[asset] × assetPrice / 10^assetDecimals) / capTokenSupply × 10^capDecimals
```

Calls back into the Oracle via `msg.sender` to get each underlying asset's price. `lastUpdated` is the minimum across all underlying prices. Returns `1e8` if supply is zero.

**Payload**: `abi.encodeCall(CapTokenAdapter.price, (_asset))` where `_asset` is the Cap vault token address.

***

### StakedCapAdapter

Prices stcUSD (staked cUSD) by reading the cUSD price and multiplying by the `convertToAssets` ratio of the ERC4626 staking vault:

```
price = capTokenPrice × convertToAssets(1e18) / 1e18
```

**Payload**: `abi.encodeCall(StakedCapAdapter.price, (_asset))` where `_asset` is the stcUSD address.

***

### FixedPriceOracle

A standalone contract (not a library) that always returns `1e8` (1 USD). Used for stablecoins or test deployments.

**Payload**: `abi.encodeCall(FixedPriceOracle.price, ())` — no parameters.

***

### UniswapV3Adapter

On-chain TWAP oracle using Uniswap V3 pool observations. Supports chained routes (e.g. `TOKEN → WETH → USDC`) to derive prices for any token with sufficient liquidity.

**Payload**: `abi.encode(tokens[], pools[], twapPeriods[])` where:

* `tokens` — ordered token route (length = `pools.length + 1`)
* `pools` — Uniswap V3 pool addresses for each hop
* `twapPeriods` — TWAP window in seconds per hop

The first token in the route must already have a registered price in the Oracle (used as the base price). The adapter validates the route via `validateData` before the payload is stored.

***

## Rate Adapters

### AaveAdapter

Reads Aave's `getReserveData` to fetch the current variable borrow rate for an asset. Used as a market rate benchmark.

**Payload**: `abi.encodeCall(AaveAdapter.rate, (_aaveDataProvider, _asset))`.

***

### VaultAdapter

Computes a utilization-based interest rate using Cap's own vault utilization history. This is the primary rate source for vault interest.

The adapter tracks a per-vault per-asset **utilization snapshot** (`index`, `lastUpdate`) and computes the time-weighted average utilization since the last call. The rate is determined by a two-slope model with a dynamic multiplier:

* **Below kink**: rate = `slope0 × utilization / kink × multiplier`. Multiplier decays when utilization is low.
* **Above kink**: rate = `(slope0 + slope1 × excess) × multiplier`. Multiplier grows when utilization is high.

The multiplier is bounded by `[minMultiplier, maxMultiplier]` and adjusts at speed `rate` (ray per second).

#### Admin Functions

| Function                                               | Description                                |
| ------------------------------------------------------ | ------------------------------------------ |
| `setSlopes(address _asset, SlopeData)`                 | Configure the two-slope curve for an asset |
| `setLimits(uint256 _max, uint256 _min, uint256 _rate)` | Set multiplier bounds and adjustment speed |

#### `SlopeData`

```solidity
struct SlopeData {
    uint256 slope0;  // ray — base rate at kink
    uint256 slope1;  // ray — additional rate above kink per unit of excess
    uint256 kink;    // ray — utilization ratio that separates the two slopes
}
```

***

## Usage Examples

### 1. Reading an asset price

```solidity
interface IOracle {
    function getPrice(address _asset) external view returns (uint256 price, uint256 lastUpdated);
}

address oracle = 0x...;
address usdc   = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;

(uint256 price, uint256 updatedAt) = IOracle(oracle).getPrice(usdc);
// price = 1_0000_0000 (1.00 USD, 8 decimals)

uint256 usdValue = amount * price / 10 ** 6; // USDC has 6 decimals
```

***

### 2. Configuring a Chainlink price feed

```solidity
interface IOracle {
    function setPriceOracleData(address _asset, OracleData calldata) external;
    function setPriceBackupOracleData(address _asset, OracleData calldata) external;
    function setStaleness(address _asset, uint256 _staleness) external;
}

struct OracleData { address adapter; bytes payload; }

address oracle          = 0x...;
address chainlinkAdapter = 0x...;
address wbtcFeed        = 0x...; // Chainlink WBTC/USD aggregator
address wbtc            = 0x...; // WBTC token

// Primary: Chainlink WBTC/USD
IOracle(oracle).setPriceOracleData(wbtc, OracleData({
    adapter: chainlinkAdapter,
    payload: abi.encodeWithSignature("price(address)", wbtcFeed)
}));

// Allow up to 1 hour staleness
IOracle(oracle).setStaleness(wbtc, 3600);
```

***

### 3. Setting a restaker rate for an Operator

```solidity
interface IOracle {
    function setRestakerRate(address _agent, uint256 _rate) external;
    function restakerRate(address _agent) external view returns (uint256);
}

address oracle   = 0x...;
address operator = 0x...;

// Set 5% annual restaker rate (ray = 1e27)
IOracle(oracle).setRestakerRate(operator, 0.05e27);

// Verify
uint256 rate = IOracle(oracle).restakerRate(operator);
// rate = 5e25 (5% in ray)
```


# Access Controls

Cap uses a two-contract access control system. The `AccessControl` contract is the single authority that maps `(function selector, contract address)` pairs to addresses that are allowed to call them. The `Access` abstract contract is a thin mixin inherited by every Cap contract — it provides the `checkAccess` modifier that calls out to `AccessControl` to validate each privileged operation.

This architecture gives Cap fine-grained, per-function permissioning across the entire protocol with a single upgradeable registry.

## Mechanics

### Role Derivation

Every permission in the system is represented as an OpenZeppelin role — a `bytes32` identifier. The role for a given `(selector, contract)` pair is computed as:

```solidity
roleId = bytes32(selector) | bytes32(uint256(uint160(contract)));
```

The selector occupies the top 4 bytes; the contract address fills the lower 20 bytes. This means two functions with the same selector on different contracts get distinct roles automatically.

### Granting and Revoking Access

`grantAccess` and `revokeAccess` are themselves access-controlled — only addresses holding the `role(grantAccess.selector, AccessControl)` role can modify permissions. The deployer (initial admin) receives:

* `DEFAULT_ADMIN_ROLE` (OpenZeppelin admin role)
* The role to call `grantAccess` on `AccessControl`
* The role to call `revokeAccess` on `AccessControl`

Self-revocation of the `revokeAccess` role is blocked — an admin cannot accidentally lock themselves out by revoking their own revocation privilege.

### `checkAccess` Modifier

Every privileged function in Cap contracts uses the `checkAccess` modifier:

```solidity
modifier checkAccess(bytes4 _selector) {
    _checkAccess(_selector);
    _;
}
```

`_checkAccess` calls `IAccessControl.checkAccess(_selector, address(this), msg.sender)` on the stored `accessControl` address. If the caller does not hold the required role, the call reverts with `AccessDenied`. There are no owner or operator patterns — all access is role-based.

The `bytes4(0)` selector is conventionally used for UUPS upgrade authorization (`_authorizeUpgrade`) across all upgradeable Cap contracts.

***

## Architecture

### AccessControl

```solidity
contract AccessControl is IAccessControl, UUPSUpgradeable, AccessControlEnumerableUpgradeable
```

The central permissions registry. UUPS upgradeable; upgrade requires `DEFAULT_ADMIN_ROLE`.

#### `initialize(address _admin)`

```solidity
function initialize(address _admin) external initializer;
```

Bootstraps the contract. Grants `DEFAULT_ADMIN_ROLE` and both `grantAccess` / `revokeAccess` roles to `_admin`.

***

#### `grantAccess(bytes4 _selector, address _contract, address _address)`

```solidity
function grantAccess(bytes4 _selector, address _contract, address _address) external;
```

Grants `_address` permission to call `_selector` on `_contract`. Requires the caller to hold the `grantAccess` role on this `AccessControl` contract.

| Parameter   | Type      | Description                          |
| ----------- | --------- | ------------------------------------ |
| `_selector` | `bytes4`  | Function selector being permissioned |
| `_contract` | `address` | Contract on which the function lives |
| `_address`  | `address` | Address being granted access         |

***

#### `revokeAccess(bytes4 _selector, address _contract, address _address)`

```solidity
function revokeAccess(bytes4 _selector, address _contract, address _address) external;
```

Revokes `_address`'s permission to call `_selector` on `_contract`. Cannot be used to revoke the caller's own `revokeAccess` role (reverts with `CannotRevokeSelf`).

***

#### `checkAccess(bytes4 _selector, address _contract, address _caller)`

```solidity
function checkAccess(bytes4 _selector, address _contract, address _caller)
    external view returns (bool hasAccess);
```

Verifies that `_caller` holds the role for `(_selector, _contract)`. Reverts (via OpenZeppelin `_checkRole`) if they do not. Returns `true` if access is granted.

***

#### `role(bytes4 _selector, address _contract)`

```solidity
function role(bytes4 _selector, address _contract) public pure returns (bytes32 roleId);
```

Computes the role identifier for a `(selector, contract)` pair. Pure function — can be called off-chain to compute role IDs for `grantAccess` / `revokeAccess`.

***

### Access (mixin)

```solidity
abstract contract Access is IAccess, Initializable, AccessStorageUtils
```

Inherited by every Cap contract that has privileged functions. Stores the `accessControl` address and exposes the `checkAccess` modifier.

#### `__Access_init(address _accessControl)`

Called from each contract's `initialize`. Stores the `AccessControl` contract address in ERC-7201 namespaced storage.

***

## Usage Examples

### 1. Granting a keeper permission to call `realizeInterest`

```solidity
interface IAccessControl {
    function grantAccess(bytes4 _selector, address _contract, address _address) external;
    function role(bytes4 _selector, address _contract) external pure returns (bytes32);
}

address accessControl = 0x...;
address lender        = 0x...;
address keeper        = 0x...;

bytes4 selector = ILender.realizeInterest.selector;

// Grant the keeper permission to call realizeInterest on the Lender
IAccessControl(accessControl).grantAccess(selector, lender, keeper);
```

***

### 2. Revoking a deprecated admin address

```solidity
interface IAccessControl {
    function revokeAccess(bytes4 _selector, address _contract, address _address) external;
    function role(bytes4 _selector, address _contract) external pure returns (bytes32);
}

address accessControl = 0x...;
address delegation    = 0x...;
address oldAdmin      = 0x...;

// Revoke permission to call addAgent on the Delegation contract
bytes4 selector = IDelegation.addAgent.selector;
IAccessControl(accessControl).revokeAccess(selector, delegation, oldAdmin);
```

***

### 3. Timelock

Critical admin operations are executed through an OpenZeppelin [TimelockController](https://docs.openzeppelin.com/contracts/5.x/api/governance#TimelockController) deployed at [`0xD8236031d8279d82E615aF2BFab5FC0127A329ab`](https://etherscan.io/address/0xD8236031d8279d82E615aF2BFab5FC0127A329ab). The Timelock holds roles in Cap's AccessControl system like any other permissioned address — the difference is that all calls from the Timelock must first be scheduled and wait a minimum delay before execution.

| Parameter | Value                             |
| --------- | --------------------------------- |
| Min Delay | 86,400 seconds (1 day)            |
| Type      | OpenZeppelin `TimelockController` |

The Timelock follows the standard OZ lifecycle:

1. **Schedule** — `schedule()` or `scheduleBatch()` queues an operation with at least `minDelay` seconds of waiting time
2. **Wait** — the operation cannot be executed until the delay has elapsed
3. **Execute** — `execute()` or `executeBatch()` runs the queued operation
4. **Cancel** (optional) — `cancel()` removes a pending operation before execution

For full interface details, see the [OpenZeppelin TimelockController documentation](https://docs.openzeppelin.com/contracts/5.x/api/governance#TimelockController).

***

### 4. Checking access off-chain (role ID calculation)

```solidity
interface IAccessControl {
    function role(bytes4 _selector, address _contract) external pure returns (bytes32 roleId);
    function hasRole(bytes32 role, address account) external view returns (bool);
}

address accessControl = 0x...;
address vault         = 0x...;
address addr          = 0x...;

bytes4 selector = IVault.addAsset.selector;
bytes32 roleId  = IAccessControl(accessControl).role(selector, vault);

bool canAddAsset = IAccessControl(accessControl).hasRole(roleId, addr);
```


# Tokens

Cap has three token contracts: **cUSD** (the backing stablecoin vault), **stcUSD** (the yield-bearing staked version), and **L2Token** (a LayerZero OFT for cross-chain bridging). cUSD and stcUSD are deployed on mainnet; L2Token is deployed on destination chains.

## cUSD — CapToken

`CapToken` is the cUSD stablecoin. It is a UUPS upgradeable contract that inherits the full `Vault`, `Minter`, and `FractionalReserve` modules — see those pages for the complete function reference. The token itself is an ERC20 whose supply is backed 1:1 (plus protocol fees) by a basket of whitelisted backing assets.

**Key properties**:

* Minted by depositing backing assets (`mint`)
* Burned to withdraw backing assets (`burn`) or proportionally across all assets (`redeem`)
* Supply is managed by the Vault module — no direct `mint`/`burn` by admin
* Price is the weighted average of underlying asset values (via `CapTokenAdapter`)

### `initialize`

```solidity
function initialize(
    string memory _name,
    string memory _symbol,
    address _accessControl,
    address _feeAuction,
    address _oracle,
    address[] calldata _assets,
    address _insuranceFund
) external initializer;
```

| Parameter           | Description                                            |
| ------------------- | ------------------------------------------------------ |
| `_name` / `_symbol` | ERC20 name and symbol (e.g. "Cap USD", "cUSD")         |
| `_accessControl`    | AccessControl contract address                         |
| `_feeAuction`       | FeeAuction address — receives fractional reserve yield |
| `_oracle`           | Oracle contract address                                |
| `_assets`           | Initial list of whitelisted backing assets             |
| `_insuranceFund`    | Receives mint/burn fees in cUSD                        |

***

## stcUSD — StakedCap

`StakedCap` is a UUPS upgradeable ERC4626 vault that accepts cUSD deposits and distributes protocol yield to holders. Yield accrues as the Lender realizes vault interest and transfers cUSD to this contract. A **profit-locking** mechanism prevents flashloan manipulation by vesting newly notified yield linearly over `lockDuration` seconds.

### Mechanics

#### Deposit and Withdrawal

StakedCap is a standard ERC4626: deposit cUSD, receive stcUSD shares proportional to the current exchange rate. Withdraw shares to receive cUSD plus accrued yield.

```
sharePrice = totalAssets() / totalSupply()
totalAssets() = storedTotal - lockedProfit()
```

`storedTotal` is the raw cUSD balance held by the contract. `lockedProfit()` is the portion of the most recent yield notification still vesting — it decays linearly to zero over `lockDuration`.

#### `notify()`

```solidity
function notify() external;
```

Permissionless. Snapshots the current balance above `storedTotal` as new locked profit and starts the vesting timer. Reverts with `StillVesting` if the previous yield batch has not fully unlocked yet — this prevents yield dilution by stacking notifications.

**Typical flow**: The FeeAuction's `paymentRecipient` is the StakedCap contract. After a buyer purchases auction assets, cUSD flows to StakedCap. Any keeper (or the buyer themselves) then calls `notify()` to begin vesting.

#### `lockedProfit()`

```solidity
function lockedProfit() external view returns (uint256 locked);
```

Returns the portion of the last notified yield still locked. Decays linearly from `totalLocked` at `lastNotify` to zero at `lastNotify + lockDuration`.

```
lockedProfit = totalLocked × (lockDuration - elapsed) / lockDuration
```

Returns zero if `elapsed ≥ lockDuration`.

***

### View Functions

| Function         | Returns   | Description                                                    |
| ---------------- | --------- | -------------------------------------------------------------- |
| `totalAssets()`  | `uint256` | `storedTotal - lockedProfit()` — effective cUSD backing shares |
| `lockedProfit()` | `uint256` | Yield still vesting (decays to 0 over `lockDuration`)          |
| `lastNotify()`   | `uint256` | Timestamp of the most recent `notify()` call                   |
| `lockDuration()` | `uint256` | Seconds over which newly notified yield vests                  |
| `totalLocked()`  | `uint256` | Total cUSD locked at the time of the last notify               |

***

### `initialize`

```solidity
function initialize(
    address _accessControl,
    address _asset,
    uint256 _lockDuration
) external initializer;
```

| Parameter        | Description                                       |
| ---------------- | ------------------------------------------------- |
| `_accessControl` | AccessControl contract                            |
| `_asset`         | cUSD token address (the underlying ERC4626 asset) |
| `_lockDuration`  | Yield vesting period in seconds                   |

The contract's ERC20 name and symbol are derived automatically from the underlying cUSD token: `"Staked " + name` and `"st" + symbol`.

***

## L2Token

`L2Token` is a LayerZero OFT (Omnichain Fungible Token) with EIP-2612 permit support. It is deployed on L2/alt-chain destinations to represent bridged cUSD or other Cap tokens.

**Key properties**:

* Standard `OFT` from the LayerZero SDK — `send` and `receive` cross-chain transfers
* `permit` support via EIP-2612 for gasless approvals
* Not upgradeable — deployed with a fixed constructor
* Cross-chain supply is managed by the LayerZero endpoint; no manual minting

### Constructor

```solidity
constructor(
    string memory _name,
    string memory _symbol,
    address _lzEndpoint,
    address _delegate
)
```

| Parameter           | Description                                                              |
| ------------------- | ------------------------------------------------------------------------ |
| `_name` / `_symbol` | ERC20 name and symbol                                                    |
| `_lzEndpoint`       | LayerZero endpoint contract on this chain                                |
| `_delegate`         | Address authorized to configure the OApp (peer chains, gas limits, etc.) |

***

## Usage Examples

### 1. Staking cUSD to earn yield

```solidity
interface IStakedCap {
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
    function totalAssets() external view returns (uint256);
    function convertToAssets(uint256 shares) external view returns (uint256);
}

address stcusd  = 0x...;
address cusd    = 0x...;
uint256 deposit = 10_000e18; // 10,000 cUSD

// Approve and deposit
IERC20(cusd).approve(stcusd, deposit);
uint256 shares = IStakedCap(stcusd).deposit(deposit, msg.sender);

// Later: check accrued value
uint256 currentValue = IStakedCap(stcusd).convertToAssets(shares);
// currentValue > deposit once yield has been notified and vested
```

***

### 2. Notifying new yield

```solidity
interface IStakedCap {
    function notify() external;
    function lockedProfit() external view returns (uint256);
    function lastNotify() external view returns (uint256);
    function lockDuration() external view returns (uint256);
}

address stcusd = 0x...;

// Check if previous yield has finished vesting before notifying
uint256 locked = IStakedCap(stcusd).lockedProfit();
if (locked == 0) {
    IStakedCap(stcusd).notify(); // starts new vesting cycle
}
```

***

### 3. Checking the cUSD vault composition

```solidity
interface ICapToken {
    function assets() external view returns (address[] memory);
    function totalSupplies(address _asset) external view returns (uint256);
    function totalBorrows(address _asset) external view returns (uint256);
    function availableBalance(address _asset) external view returns (uint256);
    function utilization(address _asset) external view returns (uint256 ratio);
}

address cusd = 0x...;
address[] memory vaultAssets = ICapToken(cusd).assets();

for (uint i; i < vaultAssets.length; i++) {
    address asset = vaultAssets[i];
    uint256 supply    = ICapToken(cusd).totalSupplies(asset);
    uint256 borrowed  = ICapToken(cusd).totalBorrows(asset);
    uint256 available = ICapToken(cusd).availableBalance(asset);
    uint256 util      = ICapToken(cusd).utilization(asset); // ray
    // util / 1e25 = utilization in basis points
}
```


# Terms and Conditions

By using our platform, you agree to comply with and be bound by the following terms and conditions, as well as all policies referenced herein.

* [Platform Terms of Use](/resources/terms-and-conditions/platform-terms-of-use)
* [Privacy Policy](/resources/terms-and-conditions/privacy-policy)

Last updated: June 2, 2025


# Platform Terms of Use

**COVERED AGRENTS S.A.**\
**PLATFORM TERMS OF USE**<br>

Last Revised: March 26, 2025<br>

The website located at cap.app is published, owned, and operated by Covered Agents S.A., a corporation incorporated under the laws of the Republic of Panama (“Company”), its Affiliates, and related entities. These Platform Terms of Use (the “Terms,” or “Agreement”) govern the user’s (“User”) access to and use of the Platform’s (as defined below) front-end, whether accessed via computer, mobile device, or otherwise (individually and collectively, the “Website”), as well as any products and services provided by Company (the “CAP Services”) (the Website, together with the CAP Services, collectively referred to as the “Service”).

1. ACCEPTANCE OF AGREEMENT\
   THESE TERMS SET FORTH THE LEGALLY BINDING TERMS AND CONDITIONS THAT GOVERN USER’S USE OF THE SERVICE AND ALL RELATED TOOLS, MOBILE APPLICATIONS, WEB APPLICATIONS, DECENTRALIZED APPLICATIONS, SMART CONTRACTS, AND APPLICATION PROGRAMMING INTERFACES (“APIS”) LOCATED AT ANY COMPANY WEBSITE INCLUDING WITHOUT LIMITATION, SUCCESSOR WEBSITE(S) OR APPLICATION(S) THERETO (COLLECTIVELY, THE “PLATFORM”). THESE TERMS SET OUT USER’S RIGHTS AND RESPONSIBILITIES WITH RESPECT TO USER’S USE OF THE PLATFORM FOR ANY PURPOSE, INCLUDING BUT NOT LIMITED TO DEPOSITING VARIOUS DIGITAL ASSETS AS COLLATERAL INTO THE PLATFORM’S SMART CONTRACTS, ENABLING THEM TO MINT cUSD, PROVIDE LIQUIDITY, OR GENERATE YIELD. BY USING THE SERVICE OR ACCESSING THE PLATFORM IN ANY MANNER, USER\
   ACCEPTS AND AGREES TO BE BOUND AND ABIDE BY THESE TERMS AND ALL OF THE TERMS\
   INCORPORATED HEREIN BY REFERENCE. BY AGREEING TO THESE TERMS, USER HEREBY CERTIFIES THAT USER IS AT LEAST 18 YEARS OF AGE. IF USER DOES NOT AGREE TO THESE TERMS, USER MAY NOT ACCESS OR USE THE WEBSITE OR THE PLATFORM.\
   PLEASE BE AWARE THAT THESE TERMS REQUIRE THE USE OF ARBITRATION (SECTION 14.5) ON AN INDIVIDUAL BASIS TO RESOLVE DISPUTES, RATHER THAN JURY TRIALS OR CLASS ACTIONS, AND LIMIT THE REMEDIES AVAILABLE TO USER IN THE EVENT OF A DISPUTE.\
   By accessing, browsing, submitting information to, and/or using the Platform, User accepts and agrees to be bound and abide by these Terms and Company’s Privacy Policy, incorporated herein by reference, and to comply with all applicable laws, including, without limitation, all federal, state and local tax and tariff laws, regulations, and/or directives. Accordingly, under Article 6 of the General Data Protection Regulation, or “GDPR,” Users in the European Union acknowledge and consent to Company’s processing of personal data as necessary for the performance of these Terms, any applicable agreements, and use of the Website. If User does not agree to the Terms, please do not use the Platform.
2. AMENDMENTS\
   Company reserves the right to amend this Agreement and/or Company’s Privacy Policy described in Section 4 below, at any time with or without notice, as determined by Company in its sole discretion. Company will post any amendment on the Website. User should check this Agreement and Company’s Privacy Policy regularly for updates. By continuing to use the Platform, the Website, or the Service after such amendment is made, User accepts and agrees to such amendment. If\
   User does not agree to any amendment to any of these agreements, User must stop using the Platform, the Website, and the Service. If User has any questions about the terms and conditions in this Agreement or Company’s Privacy Policy, please contact Company at <info@coveredagents.com>.
3. DEFINITIONS AND INTERPRETATION\
   3.1 Defined Terms. Capitalized terms not otherwise defined in these Terms will have the following\
   meanings: (a) “Affiliate” means, with respect to a party, any person, firm, corporation, partnership (including, without limitation, general partnerships, limited partnerships, and limited liability partnerships), limited liability company, or other entity that now or in the future, directly controls, is controlled with or by or is under common control with such party.\
   (b) “Applicable Law” means all laws, statutes, rules, regulations, ordinances, and other\
   pronouncements having the effect of law of any Governmental Authority, including the Republic of Panama.\
   (c) “Blockchain” generally means a peer-to-peer distributed and public immutable ledger that\
   maintains a record of all transactions occurring on such ledger, through a growing list of records (blocks) that are securely linked together via cryptographic hashes. Each block contains a cryptographic hash of the previous block, a timestamp, and transaction data.\
   (d) “Business Day” means any day (other than a Saturday, Sunday, or legal holiday) on which\
   financial institutions in the Commonwealth of Virginia are authorized or obligated to close.\
   (e) “CAP Ecosystem” means all the projects and functionalities listed at cap.app including without\
   limitation any transactions, communications, and collaborations with other established projects.\
   (f) “Governmental Authority” means any court, tribunal, arbitrator, authority, agency, commission,\
   official, or other instrumentality of the United States or any state, county, city, or other political subdivision or similar governing entity.\
   (g) “Smart Contract” means a program hosted on a Blockchain, consisting of code specifying\
   predetermined conditions that, when met, trigger self-executing outcomes.\
   (h) “Wallet” means a secure digital wallet, created through a combination of private and public\
   cryptographic keys, that enables users to interact with, and transact on, blockchain networks including but not limited to cryptographically signing smart contracts, and sending, receiving, and monitoring cryptocurrencies and other digital tokens.
4. PRIVACY\
   By using the Platform, Website, or Service, User agrees to, and is bound by, the terms of Company’s Privacy Policy, which is incorporated by reference into this Agreement as if it were set forth herein in its entirety. The Privacy Policy describes how Company collects, uses, and discloses information provided by User.
5. COMMUNICATION WITH USERS\
   User affirms that it is aware of and acknowledges that Company is a Blockchain service provider and has designed the Platform to be directly accessible by Users without any involvement or actions taken by Company or any third-party.
6. THIRD-PARTY LINKS, PRODUCTS, AND APPLICATIONS\
   6.1 Third-Party Sites. The Website may contain links to websites controlled or operated by persons and companies other than Company (“Linked Sites”), including but not limited to any sites related to digital transactions occasionally hyperlinked, such as X, Discord, Discourse, Gitbook, and Telegram, and websites referencing or supporting Blockchain Technology projects, marketplaces, and trading platforms. Linked Sites are not under the control of Company, and Company is not responsible for the contents of any Linked Site, including without limitation any link contained on a Linked Site, or any changes or updates to a Linked Site. Company is not responsible if the Linked Site is not working correctly or for any viruses, malware, or other harms resulting from User’s use of a Linked Site. Company is providing these links to User only as a convenience, and the inclusion of any\
   link does not imply endorsement by Company of the site or any association with its operators. User is responsible for viewing and abiding by the privacy policies and terms of use posted on the Linked Sites. User is solely responsible for any dealings with third parties who support Company or are identified on the Website, including any delivery of and payment for goods and services. Company does not store any information shared with a Linked Site and is not responsible for any personally identifiable information shared with any Linked Site.\
   6.2 Third-Party Smart Contracts. User acknowledges and understands that Company uses certain\
   third-party Smart Contracts that it has no ownership of, or control over, which are core components of the Platform. Company is not responsible for any coding errors, glitches, or any functionality, or lack thereof, of such third-party Smart Contracts.\
   6.3 Release. To the fullest extent permitted by law, User hereby releases and forever discharges Company (and its Affiliates, officers, employees, agents, successors, and assigns) from, and hereby waives and relinquishes, each and every past, present, and future dispute, claim, controversy, demand, right, obligation, liability, action and cause of action of every kind and nature (including personal injuries, death, and property damage), that has arisen or arises directly or indirectly out of, or that relates directly or indirectly to, the Platform (including any interactions\
   with, or act or omission of, Company’s partners or any other third-party or any Linked Sites). IF USER IS A CALIFORNIA RESIDENT, USER HEREBY WAIVES CALIFORNIA CIVIL CODE SECTION 1542 IN CONNECTION WITH THE FOREGOING, WHICH STATES: “A GENERAL RELEASE DOES NOT EXTEND TO CLAIMS WHICH THE CREDITOR DOES NOT KNOW OR SUSPECT TO EXIST IN HIS OR HER FAVOR AT THE TIME OF EXECUTING THE RELEASE, WHICH IF KNOWN BY HIM OR HER MUST HAVE MATERIALLY AFFECTED HIS OR HER SETTLEMENT WITH THE DEBTOR.”
7. THE PLATFORM\
   7.1 Purpose of the Platform and Website. The Platform is a fully decentralized lending protocol. The\
   Platform allows Users to mint cUSD by staking USDC or USDT. Similarly, the Platform can provide liquidity to Users by allowing Users to borrow from the collateral pool. Stakers and restakers can earn yield by providing collateral to the Platform’s pools. The Website is provided for Users to access the Platform, use the Service, and to provide updates about the CAP Ecosystem to Users.\
   7.2 Website Content. Company does not warrant the accuracy, completeness, or usefulness of this\
   information at any particular time for any particular purpose. Any reliance User places on such information is strictly at User’s own risk. Company disclaims all liability and responsibility arising from any reliance placed on such content by User, or by anyone who may be informed of any of its contents.\
   7.3 Use of the Website, Platform and Service; Licenses. Subject to this Agreement, Company grants User a limited, revocable, non-exclusive, non-transferable, non-sublicensable license to access and use the Platform, including the Website and the data, material, content, or information herein (collectively, the “Content”) solely for User’s personal use. User’s right to access and use the same shall be limited to the purposes described in these Terms unless User is otherwise expressly authorized by Company, in writing, to use the Platform for User’s own commercial purposes. User agrees to use the Platform only for lawful purposes, comply with all rules governing any\
   transactions on and through the Platform and comply with the law. Any rights not expressly granted herein are reserved, and no license or right to use any trademark of Company or any third-party is granted to User.\
   7.4 Additional Considerations\
   (a) Transactions Are Recorded on Public Blockchains. The transactions that take place on the\
   Platform are managed and confirmed via public Blockchains. User understands that its Wallet address will be made publicly visible whenever it engages in a transaction on the Platform. Company does not own or control any Blockchain that Company chooses to interface with, or any other third-party site, product, or service that User might access, visit, or use for the purpose of enabling User to access and utilize the various features of the Platform. Company is not liable for the acts or omissions of any such third parties, and will not be liable for any damage that a User may suffer as a result of its transactions or any other interaction with any such third\
   parties.\
   (b) Gas. All transactions on the Platform are facilitated by Smart Contracts. Public Blockchains\
   require the payment of a transaction fee (a “Gas Fee”) for every transaction that occurs on its network, and thus every transaction occurring on the Platform. The value of the Gas Fee changes, often unpredictably, and is entirely outside of the control of Company or the Platform. User acknowledges that under no circumstances will a transaction on the Platform be invalidated, revocable, retractable, or otherwise unenforceable on the basis that the Gas Fee for the given transaction was unknown, too high, or otherwise unacceptable to User.\
   7.5 Prohibitions and Restrictions\
   (a) Prohibited Uses. User agrees that it will not:\
   (i) Use the Platform or the Service in any manner that could damage, disable, overburden, or\
   impair the Website or the Platform or interfere with any other party’s use and enjoyment of the same;\
   (ii) Attempt to gain unauthorized access to any website or platform, computer systems, or\
   networks associated with Company, the Platform, or the Website;\
   (iii) Obtain or attempt to obtain any materials or information through the Website by any\
   means not intentionally made available or provided by Company;\
   (iv) Use any robot, spider, or other automatic device, process or means to access the Website\
   for any purpose, including monitoring or copying any of the material on the Website;\
   (v) Introduce any viruses, Trojan horses, worms, logic bombs, or other material which is\
   malicious or technologically harmful;\
   (vi) Send unsolicited messages or use the Website to send unsolicited messages such as spam;\
   (vii) Perform any benchmark tests or analyses related to the Website or the Platform without\
   express written permission of Company;\
   (viii) Send spam or engage in phishing. Spam is unwanted or unsolicited bulk email, postings,\
   contact requests, or similar electronic communications. Phishing is sending emails or other electronic communications to fraudulently or unlawfully induce recipients to reveal personal or sensitive information, such as passwords, dates of birth, Social Security numbers, passport numbers, credit card information, financial information, or other sensitive information, or to gain access to Wallets or records, exfiltration of documents or other sensitive information, payment and/or financial benefit;\
   (ix) Attack the Website or the Platform via a denial-of-service attack or a distributed\
   denial-of-service attack;\
   (x) Impersonate or attempt to impersonate Company, a Company employee, another User or\
   any other person or entity (including, without limitation, by using email addresses associated with any of the foregoing);\
   (xi) License, sell, rent, lease, transfer, assign, distribute, host, or otherwise commercially\
   exploit the Service, whether in whole or in part, or any Content displayed on the Service except as\
   permitted herein;\
   (xii) Modify, make derivative works of, disassemble, reverse compile or reverse engineer any\
   part of the Service or the Platform; or\
   (xiii) Access the Platform in order to build a similar or competitive website, product, or\
   service.\
   (b) Restrictions. Except as expressly stated herein, no part of the Platform may be copied,\
   reproduced, distributed, republished, downloaded, displayed, posted, or transmitted in any form or by any means. Unless otherwise indicated, any future release, update, or other addition to functionality of the Platform will be subject to this Agreement. All copyright and other proprietary notices related to the Platform (including on any Content displayed on the Website) must be retained on all copies thereof. User will not use the Website or Platform for any illegal purpose.\
   7.6 Modification. Company reserves the right, at any time, to modify, suspend, or discontinue the Platform\
   (in whole or in part) with or without notice to Users. User agrees that Company will not be liable to User or to any third-party for any modification, suspension, or discontinuation of the Platform or any part thereof.\
   7.7 Affiliates. The rights, duties and/or obligations of Company under this Agreement may be exercised and/or performed by Company and/or any of Company’s Affiliates, or any of their subcontractors and/or agents. User agrees that any claim or action arising out of or related to any act or omission of any of Company or its Affiliates, or any of their respective subcontractors or agents, related to the subject matter hereof, shall only be brought against Company, and not against any of Company’s Affiliates, licensors, or any subcontractor or agent of Company or any\
   its Affiliates.
8. INTELLECTUAL PROPERTY\
   8.1 Company Intellectual Property. The contents of the Platform, including the Website, are intended for User’s personal, noncommercial use. User acknowledges and agrees that Company (or, as applicable, Company’s licensors) owns all legal right, title, and interest in and to all elements of the Platform, Company’s logo, graphics, design, systems, methods, information, computer code, software, services, “look and feel,” organization, compilation of the content, code, data, and all other elements of the Platform (collectively, the “Company Materials”). The\
   Website, Platform, Company Materials, and Content are protected by copyrights, trademarks, trade secrets, database\
   rights, sui generis rights and other intellectual or proprietary rights therein pursuant to U.S. and international laws.\
   Accordingly, User is not permitted to use the Website or Content in any manner, except as expressly permitted by\
   Company in these Terms. The Website or Content may not be copied, reproduced, modified, published, uploaded,\
   posted, transmitted, performed, or distributed in any way, and User agrees not to modify, rent, lease, loan, sell,\
   distribute, transmit, broadcast, or create derivatives without the express written consent of Company. Except as\
   expressly set forth herein, User’s use of the Platform does not grant User ownership of or any other rights with\
   respect to any Content, code, data, or other materials that User may access on or through the Platform. Company\
   reserves all rights in and to Company Materials not expressly granted to Users in the Terms.\
   User may not use any of Company’s Content to link to the Website or Content without Company’s express written\
   consent. User may not use framing techniques to enclose any such Content without Company’s express written\
   consent. In addition, the “look and feel” of the Website and Content, including without limitation, all page headers,\
   custom graphics, button icons, and scripts constitute the service mark, trademark, or trade dress of Company and may\
   not be copied, imitated, or used, in whole or in part, without Company’s prior written consent.\
   8.2 Non-Company Intellectual Property. Excluding Company Materials, all other trademarks, product\
   names, logos, and similar intellectual property on the Platform are the property of their respective owners and may\
   not be copied, imitated, or used, in whole or in part, without the permission of the applicable owner.\
   8.3 Aggregate Data. Company shall have the right to collect and analyze data and other information relating\
   to provision and use of various aspects of the Platform. Company will be free to (i) use the data to improve and\
   enhance the Service and for other development, diagnostic, and corrective purposes in connection with the Platform\
   and (ii) disclose data solely in aggregate or other de-identified form in connection with its business.
9. INDEMNIFICATION\
   User agrees to release, indemnify, and hold harmless Company and its Affiliates, and their respective officers, directors,\
   employees and agents, from and against any claims, liabilities, damages, losses, and expenses, including, without\
   limitation, reasonable legal and accounting fees, arising out of or in any way related to: (a) User’s access to, use of, or\
   inability to use the Platform, the Website, or Service; (b) User’s breach of this Agreement; (c) User’s violation of any\
   rights of a third party; (d) User’s violation of any Applicable Law; and (e) any and all financial losses User may suffer, or\
   cause others to suffer, due to utilizing, transferring, staking, or re-staking cryptocurrency, or any other digital assets.
10. ASSUMPTION OF RISK\
    10.1 User Acknowledges the Risk of Cryptocurrency and Smart Contracts. USER REPRESENTS AND\
    WARRANTS THAT IT UNDERSTANDS AND IS WILLING TO ACCEPT THE RISKS ASSOCIATED WITH\
    CRYPTOGRAPHIC SYSTEMS SUCH AS SMART CONTRACTS, THRID PARTY BRIDGES, PUBLIC\
    BLOCKCHAINS (INCLUDING, BUT NOT LIMITED TO, THE ETHEREUM BLOCKCHAIN), LIQUIDITY\
    PROTOCOLS, AND THE INTERPLANETARY FILE SYSTEM.\
    10.2 Company is Not Responsible for Technical Errors on Any Blockchain. COMPANY IS NOT\
    RESPONSIBLE FOR LOSSES ARISING FROM THE USE OF BLOCKCHAINS OR ANY OTHER FEATURES\
    OF ANY BLOCKCHAIN NETWORK OR WALLET THAT COMPANY OR USER MAY INTERFACE WITH,\
    INCLUDING, BUT NOT LIMITED TO, LATE REPORT BY DEVELOPERS OR REPRESENTATIVES (OR NO\
    REPORT AT ALL) OF ANY ISSUES WITH A BLOCKCHAIN NETWORK OR ANY ASSOCIATED LAYER 2\
    BLOCKCHAINS THAT COMPANY OR USER MAY INTERFACE WITH, INCLUDING FORKS, TECHNICAL\
    NODE ISSUES, OR ANY OTHER ISSUES RESULTING IN LOSS OF FUNDS.\
    10.3 User Acknowledges the Risks of the Platform. User acknowledges that the Platform is subject to flaws\
    and that User is solely responsible for evaluating any information provided by the Platform. This warning and others\
    provided in this Agreement by Company in no way evidence or represent an ongoing duty to alert User to all of the\
    potential risks of utilizing or accessing the Platform. The Platform may experience sophisticated cyber-attacks,\
    cryptocurrency based economic exploits, unexpected surges in activity, or other operational or technical difficulties\
    that may cause interruptions to or delays on the Platform. The Platform utilizes shared security marketplaces like\
    EigenLayer, accordingly, the User may be exposed to the risks associated with such platforms. User agrees to accept\
    the risk of the Platform failure resulting from unanticipated or heightened technical difficulties, including those\
    resulting from sophisticated attacks, and User agrees that it will not hold Company accountable for any related losses.\
    Company will not bear any liability, whatsoever, for any damage or interruptions caused by any viruses that may\
    affect User’s computer or other equipment, or any phishing, spoofing or other attack.\
    10.4 Company Does Not Make Any Representations Regarding the Value of Cryptocurrency or Other\
    Digital Assets. The prices of blockchain assets, including USDT and USDC, are extremely volatile. Fluctuations in\
    the price of other digital assets could materially and adversely affect the value of cryptocurrency, which may also be\
    subject to significant price volatility. A lack of use or public interest in the creation and development of distributed\
    ecosystems could negatively impact the development, potential utility, or value of cryptocurrency. The CAP\
    Ecosystem and other digital assets could be impacted by one or more regulatory inquiries or regulatory actions. For\
    all of the foregoing reasons, as well as for reasons that may not presently be known to Company, Company makes\
    absolutely no representations or warranties of any kind regarding the value of cryptocurrency or other digital assets.\
    10.5 User Acknowledges Financial Risk of Digital Assets. The risk of loss associated with the use of digital\
    assets can be substantial. User should, therefore, carefully consider whether creating, buying, selling, or otherwise\
    using digital assets is suitable for User in light of its circumstances and financial resources. By using the Platform,\
    accessing the Website, and/or purchasing cryptocurrency, User represents that it has been, is and will be solely\
    responsible for making its own independent appraisal and investigations into the risks of a given transaction and the\
    underlying digital assets. User represents that it has sufficient knowledge, market sophistication, professional advice,\
    and experience to make its own evaluation of the merits and risks of any transaction conducted via any digital asset.\
    Under no circumstances will the operation of all or any portion of the Platform be deemed to create a relationship\
    that includes the provision or tendering of investment advice.\
    10.6 Company is Not Responsible for Losses Due to Jurisdictional Blocks. User acknowledges that\
    Company has no control over jurisdictional blocks which may prevent User from utilizing the Website. Under no\
    circumstances will Company be liable for User’s inability to access the Website due to a jurisdictional block.\
    10.7 Violations by Other Users. User irrevocably releases, acquits, and forever discharges Company and its\
    subsidiaries, Affiliates, officers, and successors for and against any and all past or future causes of action, suits, or\
    controversies arising out of another User’s violation of these Terms.
11. LIMITATION OF LIABILITY AND WARRANTY DISCLAIMER\
    11.1 Limitation of Liability. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT WILL\
    COMPANY (OR COMPANY’S AFFILIATES) BE LIABLE TO USER OR ANY THIRD-PARTY FOR ANY\
    FINANCIAL LOSS, LOST PROFITS, LOST DATA, COSTS OF PROCUREMENT OF SUBSTITUTE\
    PRODUCTS, OR ANY INDIRECT, CONSEQUENTIAL, EXEMPLARY, INCIDENTAL, SPECIAL OR\
    PUNITIVE DAMAGES ARISING FROM OR RELATING TO THESE TERMS OR USER’S USE OF, OR\
    INABILITY TO USE, THE PLATFORM, THE WEBSITE OR THE SERVICE, CONTENT OR INFORMATION\
    ACCESSED VIA THE WEBSITE OR ANY HYPERLINKED WEBSITE, OR ANY DISRUPTION OR DELAY IN\
    THE PERFORMANCE OF THE WEBSITE, THE PLATFORM, OR THE SERVICE EVEN IF COMPANY HAS\
    BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ACCESS TO, AND USE OF, THE PLATFORM\
    IS AT USER’S OWN DISCRETION AND RISK, AND USER WILL BE SOLELY RESPONSIBLE FOR ANY\
    MONETARY LOSS AND/OR DAMAGE TO ITS DEVICE OR COMPUTER SYSTEM, OR LOSS OF DATA\
    RESULTING THEREFROM.\
    SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OR EXCLUSION OF LIABILITY FOR\
    INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THE ABOVE LIMITATION OR EXCLUSION MAY\
    NOT APPLY TO USER.\
    11.2 No Warranties. ALL INFORMATION OR SERVICE PROVIDED BY COMPANY TO USER VIA THE\
    WEBSITE AND THE PLATFORM, INCLUDING, WITHOUT LIMITATION, ALL CONTENT, IS PROVIDED\
    “AS IS” AND “WHERE IS” AND WITHOUT ANY WARRANTIES OF ANY KIND. COMPANY AND ANY\
    THIRD-PARTY LICENSORS WITH CONTENT ON THE WEBSITE EXPRESSLY DISCLAIM ALL\
    WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION,\
    THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND\
    NON-INFRINGEMENT. NOTWITHSTANDING ANY PROVISION CONTAINED HEREIN TO THE\
    CONTRARY, COMPANY AND AFFILIATES MAKE NO REPRESENTATION, WARRANTY OR COVENANT\
    CONCERNING THE ACCURACY, QUALITY, SUITABILITY, COMPLETENESS, SEQUENCE, TIMELINESS,\
    SECURITY OR AVAILABILITY OF THE WEBSITE, THE PLATFORM OR ANY CONTENT POSTED ON OR\
    OTHERWISE ACCESSIBLE VIA THE PLATFORM OR THE WEBSITE. USER SPECIFICALLY\
    ACKNOWLEDGES THAT COMPANY AND AFFILIATES ARE NOT LIABLE FOR ANY DEFAMATORY,\
    OBSCENE OR UNLAWFUL CONDUCT OF THIRD-PARTIES OR USERS OF THE WEBSITE OR THE\
    PLATFORM AND THAT THE RISK OF INJURY FROM THE FOREGOING RESTS ENTIRELY WITH USER.\
    NEITHER COMPANY NOR ANY OF ITS AFFILIATES REPRESENT, WARRANT, OR COVENANT THAT THE\
    WEBSITE AND/OR THE PLATFORM WILL BE SECURE, UNINTERRUPTED OR ERROR-FREE. COMPANY\
    FURTHER MAKES NO WARRANTY THAT THE WEBSITE WILL BE FREE OF VIRUSES, WORMS, OR\
    TROJAN HORSES OR THAT IT WILL FUNCTION OR OPERATE IN CONJUNCTION WITH ANY OTHER\
    PRODUCT OR SOFTWARE. USER EXPRESSLY AGREES THAT USE OF THE PLATFORM IS AT USER’S\
    SOLE RISK AND THAT COMPANY, ITS AFFILIATES AND THEIR THIRD-PARTY LICENSORS SHALL NOT\
    BE RESPONSIBLE FOR ANY TERMINATION, INTERRUPTION OF SERVICE, DELAYS, ERRORS,\
    FAILURES OF PERFORMANCE, DEFECTS, OR OMISSIONS ASSOCIATED WITH THE WEBSITE AND/OR\
    THE PLATFORM OR USER’S USE THEREOF. USER’S SOLE REMEDY AGAINST COMPANY FOR\
    DISSATISFACTION WITH THE WEBSITE, THE PLATFORM, OR THE CONTENT IS TO CEASE ITS USE OF\
    THE PLATFORM AND THE WEBSITE. SOME JURISDICTIONS DO NOT PERMIT THE EXCLUSION OR\
    LIMITATION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT APPLY TO USER.\
    USER MAY HAVE OTHER RIGHTS, WHICH VARY BY JURISDICTION. WHEN THE IMPLIED\
    WARRANTIES ARE NOT ALLOWED TO BE EXCLUDED IN THEIR ENTIRETY, USER AGREES THAT\
    THEY WILL BE LIMITED TO THE GREATEST EXTENT AND SHORTEST DURATION PERMITTED BY\
    LAW.
12. TERM AND TERMINATION\
    This Agreement will remain in full force and effect while User uses the Platform, the Website, or the Service (the “Term”).\
    Company may suspend or terminate User’s rights to use the Platform, the Website, or the Service at any time for any\
    reason at Company’s sole discretion, including for any use of the Website, the Platform, or the Service in violation of this\
    Agreement. User may terminate this Agreement at any time by ending User’s use of the Platform and notifying Company\
    at <info@coveredagents.com>. Such notice to Company must include User’s Wallet address(es). Upon termination of User’s\
    rights under this Agreement, User’s right to access and use the Platform will terminate immediately. Company will not\
    have any liability whatsoever to User for any termination of User’s rights under this Agreement. All provisions of the\
    Agreement which by their nature should survive, shall survive termination of Service, including without limitation,\
    ownership provisions, warranty disclaimers, and limitation of liability.
13. GENERAL TERMS\
    13.1 General Terms. These Terms, together with the Privacy Policy and any other agreements expressly\
    incorporated by reference into these Terms, are the entire and exclusive understanding and agreement between User\
    and Company regarding User’s use of the Service. User may not assign or transfer these Terms or its rights under\
    these Terms, in whole or in part, by operation of law or otherwise, without Company’s prior written consent.\
    Company may assign these Terms at any time without notice or consent. The failure to require performance of any\
    provision will not affect Company’s right to require performance at any other time after that, nor will a waiver by\
    Company of any breach or default of these Terms, or any provision of these Terms, be a waiver of any subsequent\
    breach or default or a waiver of the provision itself. Use of section headers in these Terms is for convenience only\
    and will not have any impact on the interpretation of any provision. Throughout these Terms the use of the word\
    “including” means “including but not limited to”. If any part of these Terms is held to be invalid or unenforceable,\
    the unenforceable part will be given effect to the greatest extent possible, and the remaining parts will remain in full\
    force and effect.\
    13.2 Electronic Communications. By using the Website, the Platform, or the Service, User consents to\
    receiving certain electronic communications from Company as further described in the Privacy Policy. Please read\
    the Privacy Policy to learn more about Company’s electronic communications practices. User agrees that any notices,\
    agreements, disclosures, or other communications that Company sends to User electronically will satisfy any legal\
    communication requirements. Any electronic communications will be deemed to have been received by User\
    immediately after Company posts the same to the Website, whether or not User has received or retrieved the\
    communication from Company. User agrees that these are reasonable procedures for sending and receiving electronic\
    communications. If User wish to withdraw User’s consent to receive Communications electronically, User must\
    discontinue use of the Platform. Company reserves the right, in its sole discretion, to discontinue the provision of\
    electronic communications, or to terminate or change the terms and conditions on which Company provides\
    electronic communications. Company will provide User with notice of any such termination or change as required by\
    Applicable Law.\
    13.3 Changes to these Terms of Use. Company may update or change these Terms from time to time in order\
    to reflect changes in any offered services, changes in the law, or for other reasons as deemed necessary by Company.\
    The effective date of any Terms will be reflected in the “Last Revised” entry at the top of these Terms. User’s\
    continued use of the Website after any such change is communicated shall constitute User’s consent to such\
    change(s).\
    13.4 Governing Law & Jurisdiction. These Terms are governed by the laws of the Republic of Panama,\
    without regard to its conflict of law principles. User hereby irrevocably consents to the exclusive jurisdiction and\
    venue of the competent courts of the Republic of Panama for all disputes arising out of or relating to the use of the\
    Platform, the Website, or the Service not subject to the Arbitration Agreement outlined in Section 14.5.\
    13.5 Dispute Resolution\
    (a) Arbitration Agreement Generally. Please read the following arbitration agreement (“Arbitration\
    Agreement”) carefully. It limits the manner in which User may seek relief from Company, is part of User’s\
    contract with Company, and contains provisions concerning MANDATORY BINDING ARBITRATION AND\
    WAIVER OF THE RIGHT TO A TRIAL BY JURY OR TO PARTICIPATE IN A CLASS ACTION.\
    (b) Exceptions. Nothing in these Terms will be deemed to waive, preclude, or otherwise limit the\
    right of either party to seek injunctive relief in a court of law in aid of arbitration or to file suit in a court of law\
    to address an intellectual property infringement claim.\
    (c) Applicability of Arbitration Agreement. In the interest of resolving disputes between Company\
    and User in the most expedient and cost-effective manner, and except as set forth in Section 14.5(b), User and\
    Company agree that every dispute arising in connection with these Terms that cannot be resolved informally,\
    whether based in contract, tort, statute, fraud, misrepresentation, or any other legal theory, including any\
    unresolved dispute, claim, interpretation, controversy, or issues of public policy arising out of or relating to the\
    Website, the Platform, the Service, these Terms, and the determination of the scope or applicability of this\
    Section 14.5 will be resolved by binding arbitration on an individual basis under the terms of this Arbitration\
    Agreement. Unless otherwise agreed to, all arbitration proceedings shall be held in English. This Arbitration\
    Agreement applies to User and Company, and to any subsidiaries, Affiliates, agents, employees, predecessors\
    in interest, successors, and assigns, as well as all authorized or unauthorized users or beneficiaries of services\
    or goods provided under the Agreement. This Arbitration Agreement shall apply, without limitation, to all\
    disputes or claims and requests for relief that arose or were asserted before the effective date of this Agreement\
    or any prior version of this Agreement.\
    (d) Arbitration Rules. Arbitration will be conducted and administered by the Center for Conciliation\
    and Arbitration of the Chamber of Commerce, Industries, and Agriculture of Panama (“CeCAP”) and its\
    dispute resolution rules (“CeCAP Rules”), as modified by these Terms. The CeCAP rules are available online\
    at <https://cecap.com.pa/en/reglamento-de-cecap/>, or by contacting Company. A single arbitrator will be\
    appointed unless otherwise required by the CeCAP rules.\
    (e) Notice Requirement and Informal Dispute Resolution. Before either party may seek arbitration,\
    the party must first send to the other party a written Notice of Dispute (“Notice”) describing the nature and\
    basis of the claim or dispute, and the specific relief requested. A Notice to Company should be sent by certified\
    U.S. Mail or by Federal Express (signature required) to:\
    Covered Agents S.A.\
    c/o Pacifica Legal\
    Boulevard Costa del Este, PH Financial Park Tower, Piso 17\
    OV320 Ciudad de Panamá\
    Panamá\
    Company may send User a Notice by electronic mail if User has provided Company with such an address.\
    Company may also provide Notice to User’s Wallet by sending an NFT to User’s Wallet. After the Notice is\
    received, User and Company may attempt to resolve the claim or dispute informally. If User and Company do\
    not resolve the claim or dispute within thirty (30) days after the Notice is received, either party may begin an\
    arbitration proceeding. All arbitration proceedings between the parties will be confidential unless otherwise\
    agreed by the parties in writing.\
    (f) Fees; Location. Each party shall be responsible for the payment of its own fees and costs\
    associated with an arbitration, except as otherwise required by the CeCAP Rules. Any arbitration hearing will\
    take place in Panama City, Panama, or another location mutually agreed upon by the parties; provided,\
    however, notwithstanding the foregoing, the parties shall endeavor, where possible, to cause the arbitration\
    proceeding to be conducted: (i) solely on the basis of documents submitted to the arbitrator; or (ii) through a\
    non-appearance-based telephone hearing or videoconference. If the arbitrator finds that either the substance of\
    User’s claim or the relief sought in User’s arbitration demand is frivolous or brought for an improper purpose,\
    in the arbitrator’s reasonable discretion, then the payment of all arbitration fees will be governed by the\
    CeCAP Rules. In that case, User agrees to reimburse Company for all monies previously disbursed by\
    Company that are otherwise User’s obligation to pay under the CeCAP Rules. Regardless of the manner in\
    which the arbitration is conducted, the arbitrator must issue a reasoned written decision sufficient to explain the\
    essential findings and conclusions on which the decision and award, if any, are based. Notwithstanding\
    anything herein to the contrary, each party will be responsible for their own attorneys’ fees associated with an\
    arbitration under these Terms, and in no event may the arbitrator award any party their attorneys’ fees.\
    (g) Enforcement. The parties irrevocably submit to the exclusive jurisdiction of any court of\
    competent jurisdiction with respect to this section to compel arbitration, to confirm an arbitration award or\
    order, or to handle court functions permitted under the CeCAP Rules. The parties irrevocably waive defense of\
    an inconvenient forum to the maintenance of any such action or other proceeding. The parties may seek\
    recognition and enforcement of any court judgment confirming an arbitration award or order in any court\
    having jurisdiction with respect to recognition or enforcement of such judgment.\
    (h) Waiver of Jury Trial. THE PARTIES HEREBY WAIVE THEIR CONSTITUTIONAL AND\
    STATUTORY RIGHTS TO GO TO COURT AND HAVE A TRIAL IN FRONT OF A JUDGE OR A JURY,\
    instead electing that all claims and disputes shall be resolved by arbitration under this Arbitration Agreement.\
    Arbitration procedures are typically more limited, more efficient and less costly than rules applicable in a court\
    and are subject to very limited review by a court. In the event any litigation should arise between User and\
    Company in any state or federal court in a suit to vacate or enforce an arbitration award or otherwise, USER\
    AND COMPANY WAIVE ALL RIGHTS TO A JURY TRIAL, instead electing that the dispute be resolved by\
    a judge.\
    (i) Waiver of Class or Consolidated Actions. ALL CLAIMS AND DISPUTES WITHIN THE\
    SCOPE OF THIS AGREEMENT, INCLUDING THE ARBITRATION AGREEMENT MUST BE\
    ARBITRATED OR LITIGATED ON AN INDIVIDUAL BASIS AND NOT ON A CLASS BASIS, AND\
    CLAIMS OF MORE THAN ONE CUSTOMER OR USER CANNOT BE ARBITRATED OR LITIGATED\
    JOINTLY OR CONSOLIDATED WITH THOSE OF ANY OTHER CUSTOMER OR USER.\
    (j) Severability. If any part or parts of this Arbitration Agreement are found under the law to be\
    invalid or unenforceable by a court of competent jurisdiction, then such specific part or parts shall be of no\
    force and effect and shall be severed and the remainder of the Arbitration Agreement shall continue in full\
    force and effect.\
    (k) Right to Waive. Any or all of the rights and limitations set forth in this Arbitration Agreement\
    may be waived by the party against whom the claim is asserted. Such waiver shall not waive or affect any other\
    portion of this Arbitration Agreement.\
    13.6 Attorneys’ Fees and Costs. In the event a party files an action in a court of competent jurisdiction\
    pursuant to Section 14.5(b), the party found to be the substantially losing party in any dispute shall be required to pay\
    the reasonable attorneys’ fees and costs of any party determined to be the substantially prevailing party. In the\
    context of this Agreement, reasonable attorneys’ fees and costs shall include but not be limited to legal fees and\
    costs, the fees and costs of witnesses, accountants, experts, and other professionals, and any other forum costs\
    incurred during, or in preparation for, a dispute. It is understood that certain time entries that may appear in the\
    billing records of such party’s legal counsel may be redacted to protect attorney-client or work-product privilege, and\
    this will not prevent recovery for the associated billings.\
    13.7 Third-Party Beneficiaries. This Agreement and the rights and obligations hereunder shall bind and inure\
    to the benefit of the parties and their successors and permitted assigns. Nothing in this Agreement, expressed or\
    implied, is intended to confer upon any person, other than the parties and their successors and permitted assigns, any\
    of the rights hereunder.\
    13.8 No Support or Maintenance. User acknowledges and agrees that Company will have no obligation to\
    provide User with any support or maintenance in connection with the Platform, Website, or Service.


# Privacy Policy

**COVERED AGENTS S.A.**\
**PRIVACY POLICY**

\
Last Revised: March 26, 2025

\
Covered Agents S.A., a corporation incorporated under the laws of the Republic of Panama (“Company”) is committed to protecting User’s privacy. Company has prepared this Privacy Policy (this “Policy”) to describe to User Company’s practices regarding the Personal Information (as defined below) Company collects, why Company collects it, and how Company uses and discloses it. This Policy should be read in conjunction with Company’s Terms of Use (the “Terms”), into which this Policy is incorporated by reference. User is encouraged to read the Terms first, as certain terms used in this Policy are defined in the Terms.

1. ACCEPTANCE OF THE POLICY\
   User’s privacy matters to Company, so User should take the time to get to know Company’s policies and practices. Please\
   understand that Company reserves the right to change any of Company’s policies and practices at any time, but User can\
   always find the latest version of this Policy here on this page. User’s continued use of the Platform after Company makes\
   changes is deemed to be acceptance of those changes, so please check this Policy periodically for updates.\
   This Policy describes the types of information Company collects from User or that User may provide when User uses the\
   Platform and Company’s practices for collecting, using, maintaining, protecting, and disclosing that information.\
   Please read this Policy carefully to understand Company’s practices regarding User’s information and how Company will\
   treat it. If User does not agree with Company’s policies and practices, then please do not use the Platform. By using the\
   Platform, User agrees to the terms of this Policy.
2. PERSONAL INFORMATION COMPANY COLLECTS\
   As used herein, “Personal Information” means information that identifies or is reasonably capable of identifying an\
   individual, directly or indirectly, and information that is capable of being associated with an identified or reasonably\
   identifiable individual.\
   2.1 Personal Information Company Collects from User. Company (or Company’s Affiliates) may collect\
   and store the following categories of Personal Information directly from User:\
   (a) Online identifier information, such as IP address and login information, domain names, and\
   similar identifying names or addresses (collectively, “Online Identifier Information”);\
   (b) Device information, such as hardware, software, operating system, browser, device name,\
   language preferences (collectively, “Device Information”);\
   (c) Usage data, such as system activity, internal and external information related to the Platform,\
   clickstream information (collectively, “Usage Data”); and\
   (d) Geolocation data, such as information about User’s device location (“Geolocation Data”).\
   Automatic collection of Personal Information may involve the use of Cookies, described in greater detail\
   below. Company does not currently store Online Identifier Information, Device Information, Usage Data, or\
   Geolocation Data on Company’s systems; however, please be aware that third-parties with which Company might\
   interact might store such information.\
   2.2 Personal Information That May Be Collected Automatically. Company (or Company’s Affiliates) may\
   collect and store the following categories of Personal Information automatically through User’s use of the Platform:\
   (a) Online Identifier Information; and\
   (b) Additional information, at Company’s discretion, to comply with legal obligations.\
   2.3 Personal Information Company Collects from Third Parties. Company (or Company’s Affiliates) may\
   collect and/or verify the following categories of Personal Information about User from third parties:\
   (a) Online Identifier Information;\
   (b) Wallet (as defined in the Terms) information, and information connected to User’s use of the\
   Platform; and\
   (c) Additional information, at Company’s discretion, to comply with legal obligations.\
   2.4 Accuracy and Retention of Personal Information. Company takes reasonable and practicable steps to\
   ensure that User’s Personal Information held by Company is (i) accurate with regard to the purposes for which it is to\
   be used, and (ii) not kept longer than is necessary for the fulfillment of the purpose for which it is to be used.
3. INTENDED FOR USERS 18+\
   Company does not knowingly collect data from or market to anyone under 18 years of age. Company does not knowingly\
   solicit data from or market to anyone under 18 years of age. By using the Platform, User represents that User is at least 18\
   years old, or that User is the parent or guardian of such a minor and consents to such minor dependent’s use of the\
   Platform. If Company learns that Personal Information, from Users less than 18 years of age has been collected, Company\
   will discontinue User’s access to the Platform, to the extent that is possible, and take reasonable measures to promptly\
   delete such data from Company’s records. If User becomes aware of any data Company may have collected from anyone\
   under age 18, please contact Company at <info@coveredagents.com>.
4. HOW COMPANY USES USER’S PERSONAL INFORMATION\
   Company collects Personal Information about User in an attempt to provide User with the best experience possible,\
   protect User from risks related to improper use and fraud, and help Company maintain and improve the Platform.\
   Company may use User’s Personal Information to:\
   4.1 Provide User with the Platform. Company uses User’s Personal Information to provide User with the\
   Platform pursuant to the Terms.\
   4.2 Comply with Legal and Regulatory Requirements. Company processes User’s Personal Information as\
   required by applicable laws and regulations.\
   4.3 Detect and Prevent Fraud. Company processes User’s Personal Information to detect and prevent fraud.\
   4.4 Protect the Security and Integrity of the Platform. Company uses User’s Personal Information to\
   maintain the security of User’s Wallet and the Platform itself.\
   4.5 Other Business Purposes. Company may use User’s Personal Information for additional purposes if\
   disclosed to User before Company collects User’s Personal Information or if Company obtains User’s consent.
5. HOW COMPANY SHARES USER’S PERSONAL INFORMATION\
   Company will never sell, share, rent, or trade User’s Personal Information with third parties for their commercial\
   purposes. Further, Company will not share User’s Personal Information with third parties, except as described below:\
   5.1 Third-Party Service Providers. Company may share User’s Personal Information with third-party\
   service providers for business or commercial purposes, including fraud detection and prevention, security threat\
   detection, customer support, data analytics, information technology, advertising and marketing, network\
   infrastructure, storage, and transaction monitoring. Company shares User’s Personal Information with these service\
   providers only so that they can provide Company with their services, and Company prohibits its service providers\
   from using or disclosing User’s Personal Information for any other purpose.\
   5.2 Law Enforcement. Company may be compelled to share User’s Personal Information with law\
   enforcement, government officials, and/or regulators.\
   5.3 Corporate Transactions. Company may disclose Personal Information in the event of a proposed or\
   consummated merger, acquisition, reorganization, asset sale, or similar corporate transaction, or in the event of a\
   bankruptcy or dissolution.\
   5.4 Professional Advisors. Company may share User’s Personal Information with Company’s professional\
   advisors, including legal, accounting, or other consulting services for purposes of audits or to comply with\
   Company’s legal obligations.\
   5.5 Consent. Company may share User’s Personal Information with User’s consent.\
   If Company decides to modify the purpose for which User’s Personal Information is collected and used, Company will\
   amend this Policy.
6. COOKIES\
   When User accesses the Platform, Company may make use of the standard practice of placing tiny data files called\
   cookies, flash cookies, pixel tags, or other tracking tools (herein, “Cookies”) on User’s computer or other devices used to\
   visit the Platform. Company uses Cookies to help Company recognize User as a customer, collect information about\
   User’s use of the Platform to better customize the Platform and content for User, and collect information about User’s\
   computer or other access devices to: (i) ensure that User’s Account security has not been compromised by detecting\
   irregular, suspicious, or potentially fraudulent activities; and (ii) assess and improve the Platform and advertising\
   campaigns.\
   User can also learn more about Cookies by visiting <http://www.allaboutcookies.org>, which includes additional useful\
   information on Cookies and how to block Cookies on different types of browsers and mobile devices. Please note that if\
   User rejects Cookies, User will not be able to use some or all of the Service. If User does not consent to the placing of\
   Cookies on User’s device, please do not visit, access, or use the Service.
7. INFORMATION SECURITY\
   No security is foolproof, and the Internet is an insecure medium. Company cannot guarantee absolute security, but\
   Company works hard to protect Company and User from unauthorized access to or unauthorized alteration, disclosure, or\
   destruction of Personal Information Company collects and stores. Measures Company takes include encryption of\
   Company website communications with SSL; periodic review of Company’s Personal Information collection, storage, and\
   processing practices; and restricted access to User’s Personal Information on a need-to-know basis for Company’s\
   employees, contractors and agents who are subject to strict contractual confidentiality obligations and may be disciplined\
   or terminated if they fail to meet these obligations.
8. INFORMATION FOR PERSONS SUBJECT TO EU DATA PROTECTION LAW\
   While customers who are located in the European Union (“EU”), European Economic Area (“EEA”) or the Channel\
   Islands, or other locations subject to EU data protection law (collectively, “Europe”) are customers of Company’s\
   Panamanian entity, Company recognizes and, to the extent applicable to Company, adheres to relevant EU data protection\
   laws. For purposes of this section, “personal data” has the meaning provided in the General Data Protection Regulation\
   (EU) 2016/679 (“GDPR”).\
   8.1 Lawful Bases for Processing. Company processes personal data subject to GDPR on one or more of the\
   following legal bases:\
   (a) Legal Obligations: to conduct anti-fraud and to fulfill Company’s retention and other legal\
   obligations;\
   (b) Contractual Obligations: to satisfy Company’s obligations to User under the Terms, including to\
   provide User with the Platform and customer support services, and to optimize and enhance the Platform;\
   (c) Legitimate Interest: to monitor the usage of the Platform, conduct automated and manual security\
   checks of the Platform, to protect Company’s rights; and\
   (d) Consent: to market Company and the Platform. User may withdraw User’s consent at any time\
   without affecting the lawfulness of processing based on consent before consent is withdrawn.\
   8.2 European Privacy Rights. European residents have the following rights under GDPR, subject to certain\
   exceptions provided under the law, with respect to their personal data:\
   (a) Rights to Access and Rectification: User may submit a request that Company disclose the\
   personal data that Company processes about User and correct any inaccurate personal data.\
   (b) Right to Erasure: User may submit a request that Company delete the personal data that Company\
   has about User.\
   (c) Right to Restriction of Processing: User has the right to restrict or object to Company’s\
   processing of User’s personal data under certain circumstances.\
   (d) Right to Data Portability: User has the right to receive the personal data User has provided to\
   Company in an electronic format and to transmit that personal data to another data controller.\
   To submit a request to exercise these rights, please contact Company using the methods described at the end of this Policy.\
   When handling requests to exercise European privacy rights, Company checks the identity of the requesting party to\
   ensure that he or she is the person legally entitled to make such request. This will require User to provide Company with\
   User’s unique Wallet identification. While Company maintains a policy to respond to these requests free of charge, should\
   User’s request be repetitive or unduly onerous, Company reserves the right to charge User a reasonable fee for compliance\
   with User’s request.
9. COLLECTION AND TRANSFER OF DATA OUTSIDE THE EEA\
   The Platform operates with many of Company’s systems based in the United States. As a result, Company may transfer\
   personal data to Europe, the United States, or other third countries, under the following conditions:\
   9.1 Contractual Obligation. Where transfers are necessary to satisfy Company’s obligation to User under\
   the Terms, including to provide User with the Platform and customer support services, and to optimize and enhance\
   the Platform; and\
   9.2 Consent. Where User has consented to the transfer of User’s personal data to another country.\
   Where transfers to a third country are based on User’s consent, User may withdraw User’s consent at any time. Please\
   understand, however, that the Platform may not be available if Company is unable to transfer personal data to other\
   countries.\
   When Company transfers personal data to third countries, Company endeavors to ensure adequate safeguards are\
   implemented, for example through the use of standard contractual clauses or Privacy Shield certification.
10. CCPA\
    Pursuant to the California Consumer Privacy Act (the “CCPA”), and in addition to the disclosures elsewhere in this Policy,\
    this section describes the types of Personal Information that Company may have collected, disclosed for business\
    purposes, or sold to third parties in the last 12 months.\
    10.1 Personal Information Collected. Internet or other electronic network activity information, geolocation\
    information, and inferences drawn from the foregoing. To learn more about the information Company collects, please\
    see “Personal Information Company Collects,” above. The sources of the information are Users of the Platform and\
    Company’s Affiliates.\
    10.2 Disclosure of Personal Information. In the last 12 months, Company may have disclosed the following\
    categories of Personal Information to Company’s Affiliates or to third-party service providers for business purposes:\
    internet or other electronic network activity information, geolocation information, and inferences drawn from the\
    foregoing. To learn more, please see “How Company Shares User’s Personal Information” above.\
    10.3 “Sale” of Personal Information. In the last 12 months, Company may have “sold” (as defined in the\
    CCPA) the following categories of Personal Information: internet or other electronic network activity information,\
    geolocation information, and inferences drawn from the foregoing. Company does not have actual knowledge that\
    Company sells Personal Information of consumers under 21 years of age. To learn more, please see “How Company\
    Shares User’s Personal Information” above.\
    10.4 Explanation of “Sales” under CCPA. Company is not in the business of collecting and selling data, but,\
    in some cases, Company may share information with Company’s Affiliates or third-party service providers in a\
    transaction that constitutes a “sale” of Personal Information under California law.


# Audits

Ongoing bug bounties:

[Sherlock Bug Bounty](https://audits.sherlock.xyz/bug-bounties/114): Up to $1m in rewards

<table><thead><tr><th width="182.5">Auditors</th><th width="147.5">Start Date</th><th width="195.5">Report</th><th width="179">Scope</th></tr></thead><tbody><tr><td>Certora</td><td>Sep 15th 2025</td><td><a href="https://github.com/cap-labs-dev/cap-audits/blob/main/2025-09-15-Certora%20(EigenAVS).pdf">certora-report</a></td><td>EigenLayer SSN</td></tr><tr><td>Sherlock</td><td>Jul 10th 2025</td><td><a href="https://github.com/cap-labs-dev/cap-audits/blob/main/2025-09-03-Sherlock.pdf">sherlock-bug-bounty</a></td><td>Cap protocol</td></tr><tr><td>Recon</td><td>May 28th 2025</td><td><a href="https://github.com/cap-labs-dev/cap-audits/blob/main/2025-07-04-Recon.pdf">recon-invariant-testing</a></td><td>Invariant testing</td></tr><tr><td>Electisec</td><td>May 25th 2025</td><td><a href="https://github.com/cap-labs-dev/cap-audits/blob/main/2025-05-25-Electisec.pdf">electisec-report</a></td><td>LZ vault</td></tr><tr><td>Spearbit Lead Security Researchers</td><td>Apr 14th 2025</td><td><a href="https://github.com/cap-labs-dev/cap-audits/blob/main/2025-06-23-Spearbit.pdf">spearbit-report</a></td><td>Cap protocol</td></tr><tr><td>Trail of Bits</td><td>Mar 3rd 2025</td><td><a href="https://github.com/cap-labs-dev/cap-audits/blob/main/2025-05-15-TrailOfBits.pdf">tob-report</a></td><td>Cap protocol</td></tr><tr><td>Zellic</td><td>Feb 17th 2025</td><td><a href="https://github.com/cap-labs-dev/cap-audits/blob/main/2025-03-17-Zellic.pdf">zellic-report</a></td><td>Cap protocol</td></tr></tbody></table>


# Whitepaper

ENG:&#x20;

{% file src="/files/G7NlanujUoT3WZW4ETRY" %}

KOR:

{% file src="/files/QXcUPrF4ntETSVOgcUJZ" %}


# Dashboard

TBD


# APIs & SDKs

The list of APIs for Cap's main protocol can be found [here](https://api.cap.app/documentation)


# Helpful Links

If you have a question that is not covered in our documentation, please post it as a topic on our [community forum](https://capcommunity.discourse.group/c/questions/2).

### Helpful links

Twitter: <https://x.com/capmoney_>

Website: [https://cap.app](https://cap.app/)

Media kit: <https://cap.app/media-kit>


