> This is part 1 of 3 of the full documentation (pages 1–100 of 220).
> The content is paginated: fetch every part to see all of it.
> Next part: https://docs-v4.venus.io/llms-full.txt/1
> Page index: https://docs-v4.venus.io/llms.txt

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

## Querying This Documentation
This site has an agentic ask interface you may use to query the documentation dynamically by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs-v4.venus.io/index.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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

---

# Overview

Launched in 2020, Venus Protocol (a.k.a. “Venus”) pushed the edges of decentralized finance through its composition of two pre-existing solutions and deployment on BNB chain, lowering the barrier to entry for millions of new users around the globe. By combining the stablecoin minting facility introduced by Maker and algorithmic money markets developed by Compound, Venus simplified the user experience and provided core capabilities that enabled decentralized finance to flourish in a single application. As a result, Venus found remarkable success and quickly rose to be one of the most widely used decentralized applications in web3.

The latest iteration of Venus Protocol, the most trusted and battle-tested lending and borrowing protocol on the BNB Chain, builds on prior successes and lessons learned to improve in 3 key areas:

1. Risk management
2. Decentralization
3. User experience

In doing so, Venus continues to push the boundaries of what's possible within the realm of decentralized finance.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-6a46103a491f9ecae3a846915415633bb7e49932%2Foverview.svg?alt=media&amp;token=cd1368b3-fbd1-441d-b570-9e32f3c3336c" alt=""><figcaption></figcaption></figure>


# FAQ

### **What is Venus Protocol?**

Venus Protocol is a trusted decentralized finance lending and borrowing protocol that's deployed on the BNB Chain. Initially launched in 2020, it combines the stablecoin minting facility of Maker and the algorithmic money markets developed by Compound, providing a simplified user experience and core capabilities in a single application.

### How do I interact with Venus Protocol?

Interacting with Venus V4 is straightforward. Supply your chosen asset and the amount to start earning interest. Additionally, once you supply assets, you can also borrow against them. Any interest earned from supplying assets can help offset the interest you accrue when borrowing.

### Where are my supplied funds stored?

Your supplied funds are stored in a smart contract on the BNB Chain. The contract's code is public, open-source, and has been formally verified and audited by external auditors. You can withdraw your funds on demand or receive Venus Tokens (vTokens) representing your stake. vTokens are as freely tradable as any other cryptographic asset on BNB Chain.

### **What is the cost of interacting with Venus Protocol?**

Transactions on the Venus V4 protocol require BNB Chain fees, which depend on network congestion and the complexity of the transaction.

### **Is there any risk?**

No platform can be considered entirely risk-free. Risks associated with Venus V4 include smart contract risk and liquidation risk. However, every possible step has been taken to minimize these risks, including making the protocol code public and conducting thorough audits.

### **What are the key areas Venus Protocol V4 aims to improve?**

Venus Protocol focuses on improving three main areas:

* **Risk Management:** Venus introduced more sophisticated risk parameters, layered oracle validation, and fine-grained controls.
* **Decentralization:** The governance model has been enhanced by introducing fast-track VIPs, role-based access control, and a fine-grained pause mechanism.
* **User Experience:** The latest version offers an enhanced user interface, a more effective reward system, and Venus Prime, all aimed at providing a smooth user experience.

### **What is the Resilient Price Oracle?**

The Resilient Price Oracle introduced in Venus V4 fetches prices from multiple sources and validates them, providing a more reliable price indicator and protecting against price manipulations. It supports the integration of new price oracles and allows enabling and disabling price oracles per token.

### **What are Isolated Pools?**

Standalone Isolated Pools were separate collections of lending markets with their own risk configurations. This product has since been fully deprecated on BNB Chain, Ethereum, and Arbitrum, and its screens have been removed from the Venus interface. If you still have a position or unclaimed rewards in one of these legacy pools, follow the [exit and rewards guide](/guides/isolated-pools-deprecation).

This retirement does not apply to the Core Pool or to [Isolation Mode](/guides/isolated-e-mode), which applies borrowing restrictions to selected collateral within the Core Pool.

### **What is the Risk Fund?**

The Risk Fund is a reserve-management component used to hold protocol reserves that can help address bad debt. The original V4 isolated-pool design used per-pool accounting, but that accounting model has since been removed; the `RiskFundV2` contract itself remains active.

### **What changes were made to the governance model in Venus V4?**

Venus V4 features a new governance model that introduces fast-track Venus Improvement Proposals (VIPs), role-based access control, and a fine-grained pause mechanism. This new model allows for more agile and accurate decision-making, ensuring the protocol remains competitive and secure.


# Fixed Term Vaults

Venus Protocol is introducing a new way to earn and borrow: **Fixed Term Vaults**.

Instead of the variable rates and shared liquidity pools of Venus core markets, Fixed Term Vaults offer a scheduled rate and term set when the vault is created. An institution borrows stablecoins against collateral, and suppliers receive ERC-20 vault shares representing a claim on the assets available at settlement. Principal and target interest are not guaranteed: a shortfall or liquidation can reduce the amount redeemable.

Each loan uses a separate vault clone with its own collateral, debt accounting, and supplier shares; it does not share liquidity with Venus Core markets or other vault clones. The system still depends on shared controller, PositionToken, oracle, ProtocolShareReserve, LiquidationAdapter, and governance components, and some risk and liquidation settings can change.

Every vault exposes an **ERC-4626 tokenised vault interface**, with lifecycle constraints on methods such as `deposit`, `withdraw`, and `redeem`. Share tokens are standard ERC-20s and can be transferred, but transferability does not guarantee a buyer, secondary market, or sale price.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-8362c10ae8771e13d7222b2a97dedf24ad366097%2Ffixed-rate-vault-flow.svg?alt=media" alt="Fixed Term Vault fund flow diagram"><figcaption><p>Suppliers supply stablecoins into the vault, the institution receives the loan, and suppliers redeem their pro-rata share of the assets available at settlement</p></figcaption></figure>

## How a Vault Progresses

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-6e10c8791b9cff11d9a6bb90ecedb562e99d7494%2Ffixed-rate-vault-state-machine.svg?alt=media" alt="Fixed Term Vault state machine diagram"><figcaption><p>Fixed Term Vault state transitions</p></figcaption></figure>

Every vault follows the same journey from creation to close. States move in one direction only — there's no going back.

1. **Waiting for margin** — the vault exists on-chain, but the institution must post the full required collateral margin in a single transaction before anything else can happen.
2. **Margin deposited** — the margin is locked in escrow. Governance reviews the vault and calls `openVault()` to begin the fundraising window.
3. **Fundraising** — suppliers can now supply. The vault accepts stablecoins up to its maximum borrow cap. During this same window, the institution tops up their collateral to the required level. Both sides must complete their part before the window closes.
4. **Lock** — fundraising succeeded. The fixed-term loan begins. Total interest is computed and fixed immediately as a single lump sum — the full lifetime obligation is known from this moment.
5. **Pending settlement** — the lock period has ended. The institution now has until the settlement deadline to repay principal plus interest in full.
6. **Settlement deadline exceeded** — the deadline passed with debt still outstanding. The institution can still repay voluntarily; if they don't, whitelisted settlers can trigger overdue liquidation.
7. **Terminal states** — the vault resolves into one of three outcomes, after which governance calls `closeVault()`:
   * **Matured** — the institution repaid in full. Suppliers redeem their shares for principal plus yield.
   * **Failed** — two distinct cases: (a) *Raise shortfall* — not enough suppliers funded the vault; suppliers recover their principal and the institution recovers all collateral including the margin. (b) *Collateral underdelivery* — the raise met its minimum but the institution didn't post full collateral by close; the margin is confiscated and distributed pro-rata to suppliers in the collateral asset.
   * **Liquidated** — bad-debt rescue completed. The collateral value fell below outstanding debt and a permissionless repayer covered the principal; suppliers redeem against the settlement waterfall over the remaining assets.

## Participants

### Suppliers

Fixed Term Vaults are designed to give lenders certainty:

* **The scheduled rate and term are set upfront.** The target APR and lock duration are set before fundraising opens. Actual redemption still depends on the vault's terminal state and settlement assets.
* **Collateral is posted before you can supply.** The institution's margin is on-chain and locked before the fundraising window opens. Each vault has separate assets and accounting, so a default does not directly draw liquidity or collateral from another vault or Venus Core market. Vaults still depend on shared system components and governance.
* **Vault shares are transferable, but early liquidity is not guaranteed.** Shares are ERC-20 tokens and can be transferred, but Venus does not guarantee a buyer, secondary market, or sale price. The vault's `withdraw` and `redeem` functions are available only after it reaches a terminal state.

### Institutions

Fixed Term Vaults give borrowers control over their cost of capital:

* **Scheduled interest cost.** The target APR is fixed at vault creation rather than accruing at a variable borrow rate. Liquidation and late-penalty terms remain separate risk parameters.
* **Collateral is held in the vault and is not lent or rehypothecated.** It remains subject to the vault's health checks and documented liquidation paths, under which authorized liquidators or settlers can receive seized collateral. The controller also has governance-controlled pause and recovery functions. Any collateral remaining for the position-token holder is subject to the vault's debt, liquidation, and settlement rules.
* **Plan the scheduled repayment from day one.** The scheduled interest is calculated at lock entry. Liquidation conditions, penalties, and the final settlement outcome remain subject to the vault's risk parameters and state.

## Liquidations

Fixed Term Vaults use liquidation entry points and accounting that are separate from Venus Core Pool liquidation. This is not full system independence: the vault paths still rely on the shared ResilientOracle, controller, LiquidationAdapter, ProtocolShareReserve, governance, and configured permissions. Two vault-specific paths exist:

* **Health-based liquidation** — available during the Lock and settlement phases if the vault's outstanding debt exceeds the liquidation-threshold value of its collateral. Whitelisted liquidators submit a debt repayment and receive collateral at the liquidation incentive rate. The vault applies the global close factor to the repayment amount; a request above the allowed amount reverts with `ExceedsCloseFactor` rather than being silently reduced to the limit. A share of the bonus goes to the protocol.
* **Overdue liquidation** — available once the institution has missed the settlement deadline, regardless of collateral health. The same hard-reverting close-factor check applies, but collateral is seized at the late-penalty rate.

Both paths route through the `LiquidationAdapter`, which maintains separate ACM-gated whitelists for health-based liquidators and overdue settlers. Direct vault calls are blocked.

## Go Deeper

* [Supplier Guide](/guides/fixed-rate-vaults/supplier-guide) — step-by-step walkthrough for lenders.
* [Institution Guide](/guides/fixed-rate-vaults/institution-guide) — step-by-step walkthrough for borrowers.
* [Fixed Term Vaults Technical Reference](/technical-reference/reference-technical-articles/fixed-rate-vaults) — contract architecture, math, and liquidation paths in full detail.
* [Solidity API Reference](/technical-reference/reference-fixed-rate-vaults) — full function-level reference for all contracts.
* [Deployed Contracts](/deployed-contracts/fixed-rate-vaults) — published contract addresses and vault-discovery guidance; verify live implementations, configuration, and permissions at a recorded block.


# Trade

{% hint style="info" %}
The published architecture and deployment records are scoped to the BNB Chain Core Pool. Current user availability still depends on the live RelativePositionManager configuration and pause state, active DSA markets, supported market pairs and swap routes, and the current Venus interface. Verify those before treating a pair or action as available.
{% endhint %}

## Overview

**Venus Trade** is a relative performance trading product built on top of Venus Protocol's existing lending and borrowing infrastructure. It allows users to express a view that one asset will outperform another — packaged into a single, easy-to-manage position.

Instead of requiring every lending and borrowing step to be submitted separately, the published contracts support combined activation-and-open and proportional close flows. Some full-close flows can also be combined with deactivation; the exact actions presented by the current interface may vary.

This is not directional trading. Trade positions profit (or lose) based on the **relative price movement between two assets**, regardless of whether the overall market is going up or down.

## What's New

Trade introduces a set of new periphery contracts and capabilities alongside Venus's existing lending infrastructure:

| Component                          | Description                                                                                                                                                                                                                                     |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **RelativePositionManager**        | The main orchestration contract that manages the full lifecycle of Trade positions — from activation and opening to proportional closing and deactivation                                                                                       |
| **PositionAccount**                | A dedicated smart contract account deployed per user per trading pair. Its collateral, borrow positions, and yield accrual are accounted for separately from other PositionAccounts                                                             |
| **Paired Positions**               | Long collateral and short debt managed under one position record; account health also includes the configured DSA collateral                                                                                                                    |
| **Default Settlement Asset (DSA)** | A settlement-asset vToken selected from the manager's governance-configured active DSA list and supplied as initial principal. It contributes to collateral backing the short borrow, and realized profits and losses are settled in that asset |
| **Proportional Closing**           | Flexible partial or full position closing using on-chain flash loans and token swaps, with automatic dust handling                                                                                                                              |
| **Capital Utilization Tracking**   | Real-time calculation of how much deposited collateral is locked by open positions, enabling accurate withdrawable balance reporting                                                                                                            |

## Architecture Overview

Trade is built as a peripheral orchestration layer that interacts with existing Venus Core Pool, Comptroller, and vToken interfaces. A PositionAccount is a regular Core account from the Comptroller's perspective and remains subject to Core market and liquidation rules.

{% hint style="warning" %}
This overview describes the published BNB Chain architecture. Before integrating, verify the live RelativePositionManager proxy implementation, PositionAccount implementation and clone immutables, and ACM permissions at a recorded block using the [deployed-contracts page](/deployed-contracts/periphery). Repository ABIs and deployment records alone do not prove the current on-chain configuration.
{% endhint %}

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-4995205d95515ca5d6ceab00671544383648e884%2Fimage-architecture.png?alt=media" alt="Trade Architecture Overview"><figcaption></figcaption></figure>

Each user gets a **dedicated PositionAccount** per trading pair, deployed as a minimal proxy clone. This account holds the position's funds, enters markets on the Comptroller, and delegates supported operations to the RelativePositionManager and LeverageStrategiesManager.

Each PositionAccount is the on-chain holder of that position's collateral and debt. Its own operational entry points accept calls only from the configured RelativePositionManager, and normal user lifecycle operations are scoped to the caller's stored position. This is not an exclusive-control guarantee: standard Venus liquidations can seize vTokens held by the account, the account delegates supported actions to the RelativePositionManager and LeverageStrategiesManager through the Comptroller, and an ACM-authorized caller can use `executePositionAccountCall` for emergency or administrative calls from a PositionAccount. These protocol and governance paths do not require a contemporaneous transaction from the account owner.

PositionAccounts are deployed as deterministic EIP-1167 clones with CREATE2. The salt is `keccak256(user, longVToken, shortVToken)`, but the resulting address also depends on the RelativePositionManager deployer address and its configured PositionAccount implementation. `getPositionAccountAddress` therefore predicts a stable address only for that specific manager deployment and implementation; the same wallet and pair can produce a different address on another chain or manager deployment.

### Position Isolation

Every `(user, trading pair)` combination has separate PositionAccount balances, debt, and health calculations; debt in one PositionAccount is not directly netted against another. PositionAccounts still use shared Venus Core markets, oracles, liquidity, rates, periphery contracts, and governance, so market-wide and protocol-wide conditions can affect multiple positions.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-dbba1b135db4234af8b4471929dd55eba1e2fe09%2Fimage-isolated.png?alt=media" alt="Trade Position Isolation"><figcaption></figcaption></figure>

## Key Concepts

### Long Leg and Short Leg

A Trade position's relative exposure is described through two distinct markets; the configured DSA is an additional collateral and settlement component and may use the same market as either the long or short side:

* **Long side** — the asset you believe will outperform. The PositionAccount supplies this asset into Venus.
* **Short side** — the asset you believe will underperform. The PositionAccount borrows this asset from Venus.

The product can present them as one position, but the on-chain close paths still calculate and execute distinct debt-repayment, profit-conversion, or loss-coverage legs with separate amounts and swap data.

### Default Settlement Asset (DSA)

When activating a position, you select a **Default Settlement Asset** from the manager's governance-configured DSA vTokens that are active for new activations. Published interfaces may offer stablecoins such as USDT or USDC, but the source does not hardcode the list: governance can add a listed vToken or deactivate one for new activations. The selected asset is supplied as initial principal in the PositionAccount and contributes to collateral backing the short borrow. The collateral factors of the DSA and long asset contribute to borrow capacity and maximum available leverage. Realized profits and losses are settled in the selected DSA.

### Leverage

Trade supports leveraged positions, amplifying your exposure to relative price movements. The target leverage setting is recorded when a position is activated. While the position is active, the user can still increase exposure with `scalePosition` and can supply more principal; the maximum additional borrow is recalculated from current position values and collateral factors, so actual exposure and the effective limit can change over time.

### Capital Utilization

Capital utilization tells you how much of your deposited collateral is currently locked by your open position. The remaining portion — **Available Capital** — can support an increase to that position or be withdrawn, subject to the live risk checks.

## How It Works

### Step 1 — Activate and Open

Deposit your DSA collateral, select a trading pair, set your leverage, and submit in one transaction. Behind the scenes, the system:

1. Deploys a dedicated PositionAccount for this pair (first time only)
2. Enters the DSA market and supplies your collateral
3. Flash loans the short asset via LeverageStrategiesManager
4. Swaps the short asset to the long asset
5. Supplies the long asset to Venus, establishing the leveraged position

### Step 2 — Monitor and Manage

Once open, your position generates yield automatically:

* **Supply APY** on the long asset
* **DSA APY** on your collateral
* minus **Borrow APY** on the short asset

You can monitor Health Factor, PnL, entry price, and liquidation price at any time. Available management actions include increasing the position, supplying additional collateral, proportionally reducing the position, withdrawing unused collateral, or fully closing and deactivating.

### Step 3 — Reduce and Exit

Reducing is proportional — the contract accepts `closeFractionBps` from 1 to 10,000 basis points, so the source-level range is **0.01% to 100%** (`1 bp = 0.01%`). Profit and loss closes use different parameter sets and swap legs: profit output can be converted into DSA collateral, while a loss close can redeem DSA collateral to cover remaining short debt.

After a full reduce, the PositionAccount can remain active. `deactivatePosition` requires the short debt to be zero, redeems remaining long and DSA balances to the user, and marks the position inactive; it does not destroy the PositionAccount. The same `(user, long, short)` account can be activated again later, including with a different currently active DSA.

The published contract exposes two atomic combined paths: `closeWithProfitAndDeactivate` and `closeWithLossAndDeactivate`. Each hardcodes a 100% close and then deactivates, but callers must choose the path and provide its required swap parameters. Whether the current Venus interface offers either path as a one-click action is a separate runtime/UI availability question.

## Impact on Existing Users

Trade is implemented through periphery contracts that interact with existing Venus Core markets. Trade PositionAccounts use the same market liquidity, utilization, interest rates, oracle values, and liquidation mechanisms as other Core accounts, so they can affect and be affected by normal shared-market conditions.

## Security

Published RelativePositionManager and PositionAccount audit reports are available in the [Security & Audits](/links/security-and-audits) section. An audit report applies to the code and version reviewed; verify the live proxy and implementation against the report before treating it as coverage for a deployment.

For a full technical breakdown of the implementation, see the [Trade Technical Article](/technical-reference/reference-technical-articles/trade).


# Leveraged Positions

The Leveraged Positions feature allows users to significantly amplify their exposure in the protocol using a single-click workflow powered by flash loans. Instead of manually looping borrow → swap → supply multiple times, users can instantly achieve their desired leverage level in one atomic transaction, while maintaining full control and precision.

The feature consists of two complementary tools:

## **1. Boost**

The Boost tab enables users to open a leveraged position by fully utilizing their available (unused) borrowing power.

**How it works**

* Users must first supply assets to the protocol and enable them as collateral.
* The interface displays the maximum “available to boost” amount, which is derived from the user’s remaining borrowing power.
* When a Boost is executed, the protocol uses a flash loan to temporarily borrow a larger amount, swaps it into the chosen collateral asset, and supplies it — all in a single transaction.
* This results in a leveraged position consisting of a larger supplied (collateral) amount and a corresponding borrowed amount.
* Upon execution, the user's health factor will be greater than 1, which avoids immediate liquidation. Thereafter, the user must continue monitoring their health factor, as the protocol is not responsible for any subsequent liquidations that may occur.

**Key benefit**

With only a modest amount of unused borrowing power, users can open positions that are multiple times larger than what would be possible through ordinary borrowing — achieving high leverage efficiently and effortlessly.

## **2. Repay with Collateral**

Repaying debt or reducing a leveraged position is equally straightforward.

Instead of needing to hold the borrowed asset in their wallet to repay debt, users can directly repay any outstanding borrow using their supplied collateral in one click.

This is particularly convenient for deleveraging or fully closing leveraged positions, as it eliminates the need for additional swaps or external funds.

Together, Boost and Repay with Collateral provide a seamless, user-friendly way to enter and exit highly leveraged positions while keeping the process safe, fast, and capital-efficient.


# E-Mode

{% hint style="info" %}
Only available on BNB Chain Core Pool.
{% endhint %}

## Overview

The Venus Protocol introduces **E-Mode (Efficiency Mode)** to the **BNB Chain Core Pool**, a new feature designed to **boost capital efficiency** for specific asset pools, such as stablecoins or ETH-based tokens. It also aims to **isolate risk** within these pools while reusing liquidity from the Core Pool.

E-Mode allows users to activate specialized **pools** within the Core Pool, each with **customized risk settings**. By selecting an E-Mode pool, your eligible assets follow optimized parameters for **collateral factor (CF)** and **liquidation threshold (LT)**, which are **increased**, while the **liquidation incentive (LI) is decreased**, making borrowing more efficient and cost-effective while keeping risks contained.

## What’s Changed

To support E-Mode, the Core Pool itself has been **enhanced with new risk mechanics**:

* **Liquidation Threshold (LT) Support** – Similar to isolated pools, LT is now used to determine liquidation conditions, separate from CF.
* **Per-Market Liquidation Incentive (LI)** – LI is no longer global; each market has its own incentive, configurable by Governance.
* **User-Specific Risk Factors** – Effective CF, LT, and LI now depend on the user’s selected E-Mode pool, giving each user a tailored borrowing and collateral profile.

## Architecture Overview

E-Mode operates as a **lightweight overlay on the Core Pool**, enabling per-user risk management without moving funds to separate pools.

* **Core Pool (poolId = 0)**: The default pool for all users. Supports **per-market LIs** and **LT**, even for non-E-Mode users.
* **E-Mode Pools (poolId > 0)**: Each pool defines a set of assets (e.g., Stablecoin Pool, ETH Pool) with customized CF, LT, and LI for each asset in the pool. Users select a pool to activate its risk parameters.
* **Pool-Market**: Markets are tracked per pool, allowing **pool-specific overrides** while preserving Core Pool compatibility.
* **User Pool Tracking**: Each user is associated with exactly one pool at a time via `userPoolId`. Switching pools updates this mapping and triggers the relevant risk rules.

This architecture ensures **flexibility, backward compatibility, and gas efficiency**, while giving users **higher borrowing efficiency and better risk isolation**.

## Impact on Users

* **No Action Needed** – Users remain in the Core Pool by default. Their positions continue to function normally without switching to E-Mode.
* **Optional Upgrade** – By entering an E-Mode pool, your account will use its risk settings for approved assets.
* **Note: Improved Risk Mechanics** – Even without switching, Core Pool users now benefit from **per-market liquidation incentives (LI)** and **liquidation threshold (LT) support**, making liquidation calculations and account health evaluations more precise.

## Impact on Liquidators

E-Mode introduces more precision and flexibility for liquidators in the Core Pool:

* **Per-Market Liquidation Incentives** – Liquidation incentives (LI) are now set on a per-market basis rather than being global. Before liquidating, liquidators should check the LI for each asset. If a user is in an E-Mode pool and uses collateral that isn’t enabled in that pool, the LI for that asset can be zero.
* **Liquidation Thresholds (LT)** – LT now determines when accounts can be liquidated, replacing the older CF-based check for liquidations.
* **User-Specific Risk Factors** – Effective CF, LT, and LI can vary per user depending on their E-Mode pool, making liquidation decisions more targeted and strategic.

This gives liquidators **smarter targeting, higher transparency, and better reward optimization** across markets.

## How It Works

1. **Default in the Core Pool**\
   All users start in the Core Pool and can continue their positions normally.
2. **Select an E-Mode Pool**\
   Explore available pools (e.g., Stablecoin) via the Venus App or Venus Lens.
3. **Check Your Borrows**\
   Borrowed assets must be approved in the selected pool. Repay any disallowed assets first.
4. **Check Core Pool Fallback** Each E-Mode pool has a flag `allowCorePoolFallback` that determines whether assets not included in the E-Mode pool will use the Core Pool’s risk factors (i.e. CF, LT, LI). If fallback is not allowed, users must exit these markets (i.e., remove such assets as collateral) to ensure they aren’t unintentionally liquidated.
5. **Enter E-Mode**\
   Eligible assets follow E-Mode risk settings, while the behavior of assets not included in the E-Mode pool depends on the `allowCorePoolFallback` flag.
6. **Enjoy Higher Efficiency**\
   Borrow and manage positions with optimized collateral and liquidation rules.

For a **detailed technical explanation**, including implementation and user examples, check out the [full E-Mode technical article](/technical-reference/reference-technical-articles/emode).


# Isolated E-Mode

## **Overview**

Isolation Mode enables the safe addition of new or highly volatile assets to the protocol, preventing any potential risks from spreading system-wide. Assets listed in this mode can serve as collateral exclusively for borrowing a predefined set of assets—usually stablecoins—subject to a fixed debt ceiling. This approach effectively isolates risk while broadening the range of supported assets.

## **Why is it important**

When a user chooses to enable an Isolated asset as a collateral

* Only specifically permitted assets can be borrowed.
* Borrowing is capped through a conservative supply cap.
* No additional assets can be used as a collateral.

This mechanism safeguards the protocol by restricting the total debt against higher-risk assets, thereby minimizing potential systemic impact.

<div align="center"><img src="https://github.com/user-attachments/assets/c1fccc4b-8754-47cb-bf13-5798452470e3" alt="image" height="254" width="592"></div>


# Reward Distributor

### Overview

The Venus Reward Distributor allows for the configuration and management of rewards for lenders and borrowers, dependent on the user's activity within the associated markets. The addition of one or more reward distributors to a pool is facilitated through the addRewardsDistributor feature, enhancing the protocol's capability to customize distribution rates on a per-market basis.

In the Venus Protocol V4, the reward system has been upgraded to allow for rewards per market and lending activity, as well as the support for multiple reward tokens. This revamp serves to provide further incentives and yield opportunities for users.

### Reward Distributor Architecture

The Rewards Distributor system is centered around the `RewardsDistributor` contract. This contract maintains the functionality to configure, track and distribute rewards to users based on their borrow and supply activities within the protocol. Upon initialization, each `RewardsDistributor` proxy is associated with a specific reward token and Comptroller, from which point the reward token can be disseminated to users that supply or borrow in the corresponding pool.

### User Interactions

Users have a range of possible interactions with the Reward Distributor:

* **Supply:** Users can supply assets and earn rewards based on the supply speeds set for the reward token.
* **Borrow:** Users can borrow assets and earn rewards according to the configured borrow speed of the reward token.
* **Claim Rewards:** Users can claim their accrued reward tokens for individual markets, a feature that significantly reduces gas fees and streamlines the reward claiming process.


# Peg Stability Module

## Overview

The Peg Stability Module (PSM) is a crucial component of the Venus Protocol designed to maintain the value of the VAI stablecoin at $1. It functions similarly to the system provided by MakerDAO for DAI. The PSM contract utilizes two stablecoins: VAI (the target stablecoin) and USDT (used to help maintain the peg).

## Features

**Convert Functionality**:

* Users can exchange VAI and USDT with a "fixed" conversion rate of 1 VAI = $1.
* Users can send VAI to the PSM and receive USDT if enough USDT is available in the PSM.
* Users can send USDT to the PSM and receive VAI, provided that the PSM hasn't reached its maximum allowed minted VAI limit.

**No Stability Fee**

The VAI minted through the PSM does not accrue any interest or stability fee.

**Configurable Parameters**:

The PSM contract has three configurable variables set via the Venus Improvement Proposal (VIP):

* `feeIn`: Fee charged when users send USDT to the PSM.
* `feeOut`: Fee charged when users send VAI to the PSM.
* `maxMintedVAI`: The maximum amount of VAI that the PSM can distribute. Conversions that exceed this limit will be reverted.

**Fees Sent to Treasury**: The collected fees are sent to the Venus Treasury contract in each operation.

**Integration with Oracle Price**: The PSM considers the USD value of the stablecoin to peg VAI to its value accurately.

## Convert Functions

### Function `swapStableForVAI`

This function allows users to exchange the paired stablecoin (USDT) for VAI.

**Expected Parameters**:

* `receiver`: Address of the user who will receive the VAI.
* `amount`: The amount of stablecoin (USDT) the sender wants to convert.

The received stablecoins will be held by the PSM, and the fee specified by `feeIn` will be sent to the Treasury contract.

This function returns the amount of VAI transferred to the receiver.

### Function `swapVAIForStable`

This function enables users to exchange VAI for the paired stablecoin (USDT).

**Expected Parameters**:

* `receiver`: Address of the user who will receive the stablecoin.
* `amount`: The expected amount of stablecoin (USDT) the user should receive.

The received VAI will be burnt, and the fee specified by `feeOut` will be sent to the Treasury contract.

This function returns the amount of VAI transferred from the sender (burnt + fee).

## Preview Functions

The PSM also offers preview functions that help users estimate the outcome of convert operations:

### `previewSwapVAIForStable(uint256 stableTknAmount)`

Returns the amount of VAI that the sender would transfer (burnt + fee) to receive the specified stablecoin amount.

### `previewSwapStableForVAI(uint256 stableTknAmount)`

Returns the amount of VAI that the receiver would receive after executing the `swapVAIForStable` function with the specified stablecoin amount.

## Integration of the Oracle Price

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-b3cb91c913325592ca108adf908c6d72b2950cc3%2Fpsm.png?alt=media&amp;token=ca253fc0-62a9-4ae6-80a3-5f6baf1f46e5" alt="USD price considered by the Peg Stability Module at Venus"><figcaption></figcaption></figure>

To protect the value of VAI and consider the USD value of the paired stablecoin, the PSM integrates with the Resilient Oracle. The following rules are applied:

**swapVAIForStable** (the user sends VAI and receives USDT)

* If the oracle price of the paired stablecoin is below $1, the conversion rate is 1 stablecoin = $1.

{% hint style="info" %}
**For example if 1 USDT = $0.90**

* Input: The user wishes to receive 1 USDT.

* Calculation: The conversion rate to consider is max(1, 0.9) => 1 USDT = $1 = 1 VAI.

* The user needs to send 1 VAI (1 USDT \* 1 $/USDT) + fees.

* Assuming feeOut = 10%, 0.10 VAI will be sent to the treasury, and 1 VAI will be burnt, resulting in a total of 1.1 VAI provided by the user.

* The fee is calculated considering the "principal" VAI amount that we are to burn (1 VAI = $1).
  {% endhint %}

* If the oracle price of the paired stablecoin is above $1, the conversion rate is 1 stablecoin = oracle price.

{% hint style="info" %}
**For example if 1 USDT = $1.1**

* Input: The user desires to receive 10 USDT.
* Calculation: The conversion rate to consider is max(1, 1.1) => 1 USDT = $1.1 = 1.1 VAI.
* The user needs to send 11 VAI (10 USDT \* 1.1 $/USDT) + fees.
* Assuming feeOut = 10%, 1.1 VAI will be sent to the treasury, and 10 VAI will be burnt, resulting in a total of 12.1 VAI provided by the user.
* The fee is calculated considering the "principal" VAI amount that we are to burn (11 VAI = $11).
  {% endhint %}

**swapStableForVAI** (the user sends USDT and receives VAI)

* If the oracle price of the paired stablecoin is below $1, the conversion rate is 1 stablecoin = oracle price.

{% hint style="info" %}
**For example if 1 USDT = $0.90**

* Input: The user wishes to send 10 USDT to the PSM.

* Calculation: The conversion rate to consider is min(1, 0.9) => 10 USDT = $9 = 9 VAI.

* The user will receive 9 VAI (10 USDT \* 0.9) - fees.

* Assuming feeIn = 10%, 0.9 VAI will be sent to the treasury, and 8.1 VAI will be sent to the user.

* The fee is calculated considering the "principal" VAI amount (9 VAI = $9).
  {% endhint %}

* If the oracle price of the paired stablecoin is above $1, the conversion rate is 1 stablecoin = $1.

{% hint style="info" %}
**For example if 1 USDT = $1.1**

* 1 USDT = $1.1 (conversion rate according to our oracles)
* Input: The user wishes to send 10 USDT to the PSM.
* Calculation: The conversion rate to consider is min(1, 1.1) => 10 USDT = $10 = 10 VAI.
* The user will receive 10 VAI (10 USDT \* 1.0) - fees.
* Assuming feeIn = 10%, 1 VAI will be sent to the treasury, and 9 VAI will be sent to the user.
* The fee is calculated considering the "principal" VAI amount (10 VAI = $10).
  {% endhint %}

{% hint style="warning" %}
This documentation is designed to be user-friendly and does not cover the technical implementation details of the Peg Stability Module. For technical information, developers and smart contract auditors can refer to the [smart contract code](https://github.com/VenusProtocol/venus-protocol/blob/develop/contracts/PegStability/PegStability.sol).
{% endhint %}


# Automatic Income Allocation

### Overview

This innovation is designed to distribute the incomes generated by Venus Protocol seamlessly, channeling them to different destinations according to specific rules and percentages. This functionality will incorporate incomes generated through various sources, including interest reserves and liquidation incentives, for both the Core pools and Isolated pools. The goal is to distribute these incomes in underlying tokens and not in vTokens, based on the latest protocol [tokenomics](https://snapshot.org/#/venus-xvs.eth/proposal/0xc9d270ccecb7b91c75b95b8d9af24fc7c20cd38c0c0c44888ed4e7724f4e7ce9).

### Key Aspects of Automatic Income Allocation

* **Near-Streaming Distribution**: The income will be distributed in a near-streaming fashion, from the different pools to the ProtocolShareReserve contract.
* **Flexible Distribution Rules**: The incomes generated by Venus Protocol are distributed according to two schemas. The percentages below are the BNB Chain configuration since [VIP-638](https://venus.io/governance/proposal/638?chainId=56) (1 July 2026, [Tokenomics Phase II](https://community.venus.io/t/venus-tokenomics-phase-ii-prime-rewards-redesign/5774)); XVS Vault rewards are no longer a destination, and deployments below the revenue-eligibility threshold route 100% to the Treasury (see [Tokenomics](/governance/tokenomics)).
  * **Schema (PROTOCOL\_RESERVES)**: Applied specifically to income generated by the interest reserves in every Venus market.
    * 40% to Treasury
    * 40% to Venus Prime
    * 20% to Risk Fund
  * **Schema (ADDITIONAL\_REVENUE)**: Valid for liquidation and other product developments.
    * 60% to Treasury
    * 20% to Venus Prime
    * 20% to Risk Fund
* **Buyback Delivery**: Since [VIP-620](https://venus.io/governance/proposal/620?chainId=56) (May 2026) every destination is a single-purpose TokenBuyback contract. The Prime share is bought into USDT and U and forwarded to the PrimeLiquidityProvider, the Risk Fund share into USDT for RiskFundV2, and the Treasury share into U, BTCB, ETH, USDT, USDC and XVS for the VTreasury. An off-chain operator triggers the swaps.
* **Integration with Existing Architecture**: The Automatic Income Allocation will seamlessly integrate with the existing ProtocolShareReserve contract, ensuring that the incomes are sent to their designated reserves.

### Benefits

* **Efficiency**: Automating the income distribution process allows for quicker and more accurate allocation.
* **Transparency**: All distributions are recorded on the blockchain, reinforcing Venus Protocol's commitment to clear and open practices.
* **Flexibility**: The ability to define specific rules and percentages for different destinations ensures that the distribution aligns with the community's needs and goals.

### Architecture

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-4d5f3933b86a619b8a7548c77a9e83529438d79b%2Fautomatic-income-allocation.svg?alt=media" alt=""><figcaption></figcaption></figure>

*The dashed lines represent transactions initiated by external agents (VIP’s, scripts, arbitrage bots, etc.), and the solid lines represent transfers of funds.*


# TokenBuyback Contract

### Overview

Token Converter Phase 2 replaces the original community-driven Token Converter system with a single, lightweight contract class — `TokenBuyback` — deployed as one instance per (destination, base asset) pair. Conversions are now executed by an ACM-authorized finance-team cron job using DEX aggregators at market rate, eliminating the dependency on external community participation entirely.

Protocol revenues, sourced from reserve interests and liquidations, are processed through the [Automatic Income Allocation](/whats-new/automatic-income-allocation) module. Once allocated, these underlying tokens are sent to the appropriate `TokenBuyback` instances, which swap them on a defined schedule and forward the output to the configured destination.

### Problem with the Original System

The original Token Converter relied on external community members to manually trigger token swaps. In practice:

* Tokens sat idle for hours or days waiting for someone to act
* When conversions did happen, the protocol paid up to 50% above market price as an incentive to attract participants
* The system spanned 5 contract classes across 8 deployed instances (\~2,000+ lines of Solidity), making it expensive to audit and slow to iterate on
* No guaranteed conversion cadence — protocol income accumulation was unpredictable

### Solution: TokenBuyback

A single upgradeable contract (`TokenBuyback`) is deployed as a Transparent Proxy per (destination, base asset) pair. Each instance:

* Passively accumulates any token sent by `ProtocolShareReserve` (PSR)
* Swaps on demand via `executeBuyback`, which is ACM-restricted to the finance-team cron job
* Forwards the `BASE_ASSET` directly to its `DESTINATION` after each swap
* Enforces a rolling 24h USD cap on per-token consumption (`executeBuyback` reverts past the cap) to bound blast radius if the operator key is compromised
* Uses `ResilientOracle` to USD-price `tokenIn` and `BASE_ASSET` for the cap and for an event-only abnormal-slippage signal

No community. No premium. Conversions happen on a defined schedule at market rate using off-chain-built DEX calldata. Oracle is used only for safety rails (cap + slippage signal), not for swap pricing.

### Key Design Decisions

| Aspect                       | Detail                                                                                                                                                                                                                                                                                                                              |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DESTINATION` & `BASE_ASSET` | Constructor immutables — changing either requires a new deployment                                                                                                                                                                                                                                                                  |
| Swap routing                 | Off-chain (cron builds DEX calldata); router must be on the on-chain allowlist                                                                                                                                                                                                                                                      |
| `updateAssetsState` caller   | Pinned to `PROTOCOL_SHARE_RESERVE` immutable — prevents spoofed `AssetsReceived` events                                                                                                                                                                                                                                             |
| Pool attribution             | No per-pool accounting on-chain; `comptroller` is echoed in events for off-chain attribution                                                                                                                                                                                                                                        |
| Upgradeability               | Transparent Proxy — upgradeable without migrating accumulated funds                                                                                                                                                                                                                                                                 |
| Daily USD cap                | Rolling 24h leaky bucket (`dailyCapUsd`); linear decay rate `dailyCapUsd / 24h`; `executeBuyback` reverts with `DailyCapExceeded` past the cap. Default `$30,000`.                                                                                                                                                                  |
| Abnormal slippage detection  | Event-only — `executeBuyback` emits `AbnormalSlippage` when `usdIn − usdOut > slippageEventUsd` (default `$500`). Does not revert.                                                                                                                                                                                                  |
| Oracle dependency            | `RESILIENT_ORACLE` constructor immutable — used only to USD-price the cap and the slippage signal, not for swap pricing. `executeBuyback` calls `updateAssetPrice(tokenIn)` and `updateAssetPrice(BASE_ASSET)` before reading prices, so the cap and slippage check use a freshly pushed oracle snapshot rather than a stale value. |

### Contract Architecture

`PSR` requires no contract changes — `TokenBuyback` implements `IIncomeDestination`, the same interface used by the original converters. The migration is a governance VIP that rewires PSR's `distributionTargets` rows to point to the new instances.

### Key Functions

**`updateAssetsState(address comptroller, address asset)`**

Called by PSR after transferring tokens. Records the balance delta and emits `AssetsReceived` for off-chain tracking. Only callable by `PROTOCOL_SHARE_RESERVE`. `AssetsReceived` is only emitted when the balance delta is non-zero. The reported amount is the observed `balanceOf(this)` delta against the previous watermark — tokens transferred directly to the contract outside the PSR flow are merged into the next event under whatever comptroller PSR is processing at the time.

**`executeBuyback(address tokenIn, uint256 amountIn, uint256 minAmountOut, uint256 deadline, address router, bytes calldata routerCalldata, address comptroller)`**

ACM-restricted. Swaps `amountIn` of `tokenIn` to `BASE_ASSET` via the specified router (must be allowlisted). Validates slippage against `minAmountOut`. Forwards the output directly to `DESTINATION`. Emits `BuybackExecuted`. The reported `amountIn` in `BuybackExecuted` is the actual on-chain `tokenIn` delta consumed by the router (`balanceBefore − balanceAfter`), not the caller-supplied `amountIn` parameter, so the event is honest about what the router actually pulled. After the swap settles, the call pushes a fresh oracle price for both `tokenIn` and `BASE_ASSET` (`updateAssetPrice`), then enforces the rolling 24h USD cap on `tokenIn` consumption (reverts with `DailyCapExceeded` if exceeded) and emits `AbnormalSlippage` if `usdIn − usdOut` exceeds `slippageEventUsd`.

**`forwardBaseAsset(address comptroller, uint256 amount)`**

ACM-restricted. Forwards a caller-specified `amount` of accumulated `BASE_ASSET` to `DESTINATION` without a swap. The `amount` parameter lets the operator partition multi-pool `BASE_ASSET` inflows so each portion is attributed separately via `BaseAssetForwarded` events.

**`setAllowedRouter(address router, bool allowed)`**

Governance-only. Adds or removes a DEX router from the allowlist.

**`setDailyCapUsd(uint256 newCap)`**

ACM-restricted. Updates the rolling 24h USD cap on `tokenIn` consumption (1e18-scaled). Reverts with `ZeroValueNotAllowed` if `newCap` is zero (the cap cannot be used to fully disable buybacks — use ACM revocation for that). Emits `DailyCapUpdated`.

**`setSlippageEventUsd(uint256 newThreshold)`**

ACM-restricted. Updates the absolute USD threshold above which `AbnormalSlippage` fires (1e18-scaled). Reverts with `ZeroValueNotAllowed` if `newThreshold` is zero. Emits `SlippageEventUsdUpdated`.

**`sweepToken(address token, address to, uint256 amount)`**

Governance-only. Emergency token recovery from the contract. Also the canonical recovery path for tokens transferred directly to the contract outside the PSR flow.

### Events

| Event                                                                 | When emitted                                                            |
| --------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `AssetsReceived(comptroller, asset, amount)`                          | PSR deposits tokens                                                     |
| `BuybackExecuted(tokenIn, amountIn, amountOut, router, comptroller)`  | Successful DEX swap                                                     |
| `BaseAssetForwarded(comptroller, amount)`                             | `BASE_ASSET` forwarded without swap                                     |
| `RouterAllowlisted(router, allowed)`                                  | Router added/removed from allowlist                                     |
| `SweepToken(token, to, amount)`                                       | Emergency token sweep                                                   |
| `AbnormalSlippage(tokenIn, actualAmountIn, amountOut, usdIn, usdOut)` | Swap returned less USD value than input by more than `slippageEventUsd` |
| `DailyCapUpdated(oldCap, newCap)`                                     | `setDailyCapUsd` succeeds                                               |
| `SlippageEventUsdUpdated(oldThreshold, newThreshold)`                 | `setSlippageEventUsd` succeeds                                          |

### BNB Chain: Before & After

**Before (8 contracts)**

| Contract             | Destination              |
| -------------------- | ------------------------ |
| `RiskFundConverter`  | `RiskFundV2`             |
| `USDTPrimeConverter` | `PrimeLiquidityProvider` |
| `USDCPrimeConverter` | `PrimeLiquidityProvider` |
| `BTCBPrimeConverter` | `PrimeLiquidityProvider` |
| `ETHPrimeConverter`  | `PrimeLiquidityProvider` |
| `XVSVaultConverter`  | `XVSVaultTreasury`       |
| `WBNBBurnConverter`  | Retired (was burn path)  |
| `ConverterNetwork`   | Retired (registry)       |

**After (10 TokenBuyback instances, deployed on BNB Chain mainnet)**

| Instance              | Base Asset | Destination              | Proxy address                                                                                                          |
| --------------------- | ---------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| `UTreasuryBuyback`    | U          | `VTreasury`              | [`0xec63411423D03327De19135446dDdA3055D2feA8`](https://bscscan.com/address/0xec63411423D03327De19135446dDdA3055D2feA8) |
| `BTCBTreasuryBuyback` | BTCB       | `VTreasury`              | [`0x1F306a0d929a7098a0A0b12248Ba97600AB79026`](https://bscscan.com/address/0x1F306a0d929a7098a0A0b12248Ba97600AB79026) |
| `ETHTreasuryBuyback`  | ETH        | `VTreasury`              | [`0x41954F0bf26959dF2e1B8302DEBf736B5b154B64`](https://bscscan.com/address/0x41954F0bf26959dF2e1B8302DEBf736B5b154B64) |
| `USDTTreasuryBuyback` | USDT       | `VTreasury`              | [`0xB3dDf13E8B6b8dE10F5826087C202b80F1D1b490`](https://bscscan.com/address/0xB3dDf13E8B6b8dE10F5826087C202b80F1D1b490) |
| `USDCTreasuryBuyback` | USDC       | `VTreasury`              | [`0xd7aC40f9bd9A1beb8E2d121b4446CF90417cf169`](https://bscscan.com/address/0xd7aC40f9bd9A1beb8E2d121b4446CF90417cf169) |
| `XVSTreasuryBuyback`  | XVS        | `VTreasury`              | [`0x6D2d239c16453062cF145A7a5128A6a60710d236`](https://bscscan.com/address/0x6D2d239c16453062cF145A7a5128A6a60710d236) |
| `USDTPrimeBuyback`    | USDT       | `PrimeLiquidityProvider` | [`0xD721932C7CA41Eb5305867287010587a266346a8`](https://bscscan.com/address/0xD721932C7CA41Eb5305867287010587a266346a8) |
| `UPrimeBuyback`       | U          | `PrimeLiquidityProvider` | [`0xBC9fFBfb799B2d189669D3816E2B7273c69041bd`](https://bscscan.com/address/0xBC9fFBfb799B2d189669D3816E2B7273c69041bd) |
| `RiskFundBuyback`     | USDT       | `RiskFundV2`             | [`0x0c71EFabD00329E839745ef23aB946d3ed24A805`](https://bscscan.com/address/0x0c71EFabD00329E839745ef23aB946d3ed24A805) |
| `XVSBuyback`          | XVS        | `XVSVaultTreasury`       | [`0x637E6246BBb0F9aBae9d764F5e1bB6347f028C12`](https://bscscan.com/address/0x637E6246BBb0F9aBae9d764F5e1bB6347f028C12) |

`WBNBBurnConverter` and `ConverterNetwork` are retired. The 6 `TreasuryBuyback` instances are new — treasury previously accepted arbitrary tokens without conversion.

### RiskFundV2 Changes

`RiskFundV2` is upgraded as part of this migration:

* `poolAssetsFunds` mapping removed — per-pool accounting deprecated (isolated pools wound down; core pool does not auction via Shortfall)
* `preSweepToken` simplified — plain balance check replaces the `getPools()` proportional loop
* `transferReserveForAuction` draws against contract balance; `comptroller` retained only for ABI parity and event attribution
* `getPoolsBaseAssetReserves` returns 0 for ABI parity with `Shortfall.sol`

### Migration

The BNB Chain mainnet migration executed across two governance proposals — [VIP-620](https://app.venus.io/#/governance/proposal/620?chainId=56) and [VIP-621](https://app.venus.io/#/governance/proposal/621?chainId=56) ([vips PR #708](https://github.com/VenusProtocol/vips/pull/708)). The work was originally proposed as a single transaction in [VIP-618](https://app.venus.io/#/governance/proposal/618?chainId=56), which became unexecutable when the BNB Smart Chain (BSC) Osaka hardfork enforced a hard per-transaction gas cap of 2^24 = 16,777,216: the single-call `helper.execute()` required \~17.5M gas, driven primarily by the converter drain (6 converters × 47 core-pool tokens) and the router allowlist (10 buybacks × 9 routers). Splitting the drain and router allowlist into a separate `execute2()` entrypoint drops both halves comfortably under the cap.

**Pre-VIP** (deploy-script setup):

* 10 new `TokenBuyback` proxies redeployed (the proxies originally deployed for VIP-618 are abandoned), each initialized with its `(DESTINATION, BASE_ASSET, PROTOCOL_SHARE_RESERVE, RESILIENT_ORACLE)` immutables and `pendingOwner = TokenBuybackMigrationHelper`
* New `RiskFundV2` implementation deployed (see *RiskFundV2 Changes* above)
* `TokenBuybackMigrationHelper` redeployed with three one-shot entrypoints — `execute1()`, `executeSwap()` and `execute2()` — each gated to `NormalTimelock`

**VIP-620 (Part 1)** — non-drain, non-allowlist phase plus the May 2026 Prime Rewards Allocation:

1. Grant `DEFAULT_ADMIN_ROLE` on ACM to the helper (renounced inside `execute1()` so the helper retains no residual ACM privilege between the two VIPs)
2. Transfer ownership of the 6 timelock-owned legacy converters to the helper
3. `helper.execute1()` accepts all 16 ownerships, pauses every timelock-owned converter and `Shortfall` auctions, repoints PSR's `distributionTargets` to the new buybacks (18 rows added, 12 stale rows zeroed, in a sequence that respects PSR's `maxLoopsLimit` and the per-schema percentage invariant at every checkpoint), grants the cron operator persistent `executeBuyback` + `forwardBaseAsset` ACM permissions on every buyback, and renounces `DEFAULT_ADMIN_ROLE`. The helper retains ownership of all 16 contracts across the gap between the two VIPs but holds no ACM privileges.
4. May 2026 Prime Rewards Allocation, driven directly from `NormalTimelock` (the helper only wraps the swap leg): `Prime.addMarket(coreComptroller, vU, ...)`, `PrimeLiquidityProvider.initializeTokens([U])`, `PrimeLiquidityProvider.setMaxTokensDistributionSpeed([U], [1e18])`, `PrimeLiquidityProvider.sweepToken(USDC, helper, 10_000e18)`, `helper.executeSwap()` (single soft-failing USDC → USDT → U multihop on PancakeSwap V3 with a 1% slippage floor; the deep USDT/U pool is required because the direct USDC/U pool is too thin), `PrimeLiquidityProvider.setTokensDistributionSpeed([USDT, U], [...])`.
5. Upgrade `RiskFundV2` to the new implementation. Safe because `RiskFundConverter` was paused inside `execute1()`, so no in-flight `convertExactTokens` callback can reach the removed `updatePoolState` selector.

**Between Part 1 and Part 2**: the 6 legacy converters are paused, PSR no longer routes revenue to them, and `Shortfall` auctions are paused — so converter balances are frozen and there is no economic surface from them while the helper still holds ownership.

**VIP-621 (Part 2)** — router allowlist, converter drain, ownership handback:

1. `helper.execute2()` allowlists 9 swap routers on every buyback (PancakeSwap V2 / V3 / Smart / Universal, Uniswap V2 SwapRouter02 / V3 SwapRouter02 / V4 / Universal, 1inch v5), drains every non-zero core-pool ERC20 balance off each timelock-owned converter into its replacement buyback, and transfers ownership of all 16 contracts (10 buybacks + 6 converters) back to `NormalTimelock`.
2. `NormalTimelock` accepts ownership of all 16 contracts.

**Post-VIP**:

* The 6 timelock-owned legacy converters (`RiskFundConverter`, `USDTPrimeConverter`, `USDCPrimeConverter`, `BTCBPrimeConverter`, `ETHPrimeConverter`, `XVSVaultConverter`) remain deployed but are paused and empty
* `WBNBBurnConverter` is Guardian-owned and was not handed to the helper; its sub-dollar residual is drained in a separate multisig transaction, and its PSR distribution row was already removed inside `execute1()`
* `ConverterNetwork` is unreferenced — no contract changes were needed to it
* All 10 new buybacks are owned by `NormalTimelock` and ready to receive PSR distributions
* The finance-team cron operator can now call `executeBuyback` and `forwardBaseAsset` on each buyback via its ACM permissions
* The migration helper holds no privileges, no balances, and no ownership over any contract; all three entrypoints revert `AlreadyExecuted` on any subsequent call
* Pre-existing ACM grants on legacy converter functions are not explicitly revoked; conversions are paused, which renders those permissions inert

### Impact Summary

| Metric                         | Before                         | After                              |
| ------------------------------ | ------------------------------ | ---------------------------------- |
| Solidity lines                 | \~2,160 across 5 contracts     | \~425 lines, single contract class |
| Deployed instances (BNB Chain) | 8                              | 10 (all `TokenBuyback`)            |
| Conversion trigger             | External community (voluntary) | Finance cron (scheduled)           |
| Pricing                        | Oracle + up to 50% premium     | DEX market rate                    |
| Community dependency           | Required                       | None                               |


# Venus Prime

### **Overview**

Venus Prime is the protocol's flagship incentive program, designed to reward long-term, committed users by boosting their yield across selected markets. It focuses on the core markets USDT, USDC, BTC, ETH and BNB, and promotes sustained $XVS staking.

Venus Prime is powered by two contracts:

* **PrimeV2** — the boost engine. It holds each user's non-transferable (Soulbound) Prime token, tracks per-market scores, and distributes boosted rewards funded by protocol revenue.
* **PrimeLeaderboard** — the eligibility engine. It measures how much $XVS each user has staked and for how long, producing a time-weighted **Prime Score** that ranks users for Prime eligibility.

### **Venus Prime Essentials**

Venus Prime's uniqueness lies in its self-sustaining rewards system: instead of relying on external sources or token emissions, rewards are derived from the protocol's revenue, fostering a sustainable and ever-growing program.

Eligible $XVS stakers receive a single, non-transferable Soulbound Prime token, which boosts rewards across the selected markets. The token is capped at a configurable supply (`tokenLimit`, **500** by default), changeable via VIP.

#### Eligibility: time-weighted Prime Score

Prime eligibility is no longer a fixed "stake X for Y days" rule. Instead, PrimeLeaderboard computes a **Prime Score** that rewards both the size and the age of each XVS deposit.

Every deposit and withdrawal in the XVS Vault is reported to PrimeLeaderboard, which records individual deposit tranches and applies a duration-based multiplier to each:

| Holding duration | Multiplier  |
| ---------------- | ----------- |
| < 30 days        | 1.0x (base) |
| ≥ 30 days        | 1.3x        |
| ≥ 60 days        | 1.6x        |
| ≥ 90 days        | 2.0x (cap)  |

A user's Prime Score is the sum, across all of their active deposits, of:

```
amount × multiplier(holdingDuration) × min(holdingDuration, 90 days)
```

Withdrawals are processed **LIFO** (newest deposits first), so a user's oldest, highest-multiplier stake is preserved for as long as possible. The tiers and the 90-day cap are configurable by governance via `setMultiplierTiers`.

#### Minting a Prime token

The primary minting path is **governance issuance**. Each epoch is one calendar month. After each epoch, a keeper reads the leaderboard off-chain (via `getEffectiveStakeBatch`), ranks users by Prime Score, and governance mints Prime tokens to the qualifying users with the ACM-gated `issue`/`issueBatch` functions (and revokes them, when needed, via `burn`/`burnBatch`).

A **permissionless minting** window is available as a fallback and is **disabled by default**. It only becomes usable if governance opens it by setting a minimum Prime Score threshold with `setMintThreshold(threshold, deadline)`; until then, `mintThreshold` is `0` and `claimPrime` reverts. When open, anyone can call `claimPrime(user)` (or `claimPrimeBatch`) to mint a Prime token for any user whose Prime Score is at or above the threshold, until the optional deadline passes. This serves as a last resort if the keeper-driven flow is unavailable. Governance can close it again at any time by setting the threshold back to `0`.

There is no separate "revocable" vs. "irrevocable" token — there is a single Prime token whose lifecycle is managed by governance based on the leaderboard.

### **Expected Impact**

Venus Prime incentivizes larger stake sizes and, through the time-weighted leaderboard, rewards users who keep their XVS staked. This is expected to increase XVS staking, Total Value Locked (TVL), and market growth, while discouraging premature withdrawals.

Stake your $XVS tokens today to climb the leaderboard and become eligible for Venus Prime.

### Technical Reward Details

Once a user holds a Prime token, their boosted rewards in each market are distributed with a Cobb-Douglas function, weighting their XVS stake against their qualified supply and borrow balances.

**Reward Formula: Cobb-Douglas function**

$$
Rewards\_{i,m} = \Gamma\_m \times \mu \times \frac{\tau\_{i}^\alpha \times \sigma\_{i,m}^{1-\alpha}}{\sum\_{j,m} \tau\_{j}^\alpha \times \sigma\_{j,m}^{1-\alpha}}
$$

Where:

* $$Rewards\_{i,m}$$ = Rewards for user $$i$$ in market $$m$$
* $$\Gamma\_m$$ = Protocol Reserve Revenue for market $$m$$
* $$μ$$ = Proportion to be distributed as rewards
* $$α$$ = Protocol stake and supply & borrow amplification weight
* $$τ\_{i}​$$ = XVS staked amount for user $$i$$
* $$\sigma\_i$$ = Sum of **qualified** supply and borrow balance for user $$i$$
* $$∑\_{j,m}​$$ = Sum for all users $$j$$ in markets $$m$$

**Qualifiable supply and borrow:**

$$
\begin{align\*} \sigma\_{i,m} &= \min(\tau\_i \times borrowMultiplier\_m, borrowedAmount\_{i,m}) \ &+ \min(\tau\_i \times supplyMultiplier\_m, suppliedAmount\_{i,m}) \end{align\*}
$$

*Note: There is a limit for the qualifiable supply and borrow amounts, set by the staked XVS value and the market multiplier.*

### User Reward Example:

*Note: the example below uses a BTC market for illustration; the same math applies to whichever reward markets are enabled.*

**Model Parameters**

* $$α$$ = 0.5
* $${\sum\_{j,BTC} \tau\_{j}^\alpha \times \sigma\_{j,BTC}^{1-\alpha}}$$ = 744,164
* $$\Gamma\_{BTC}$$ = 8 BTC
* $$\mu$$ = 0.2
* BTC Supply Multiplier = 2
* XVS Price = $4.0

**User Parameters**

| User Parameters | Token Value | USD Value |
| --------------- | ----------- | --------- |
| Staked XVS      | 1,200       | $4,800    |
| BTC Supply      | 0.097       | $2,500    |

**Qualifiable Supply and Borrow**

$$\sigma\_{i,\text{BTC}} = \textit{min}(\text{$9600}, \text{$2500})$$

**User Rewards**

$$Rewards\_{i, BTC} = 8\times 0.2\times \dfrac{1,200^{0.5}\times 2,500^{0.5}}{744,164}$$

$$Rewards\_{i, BTC} = \ 0.00372$$

$$\text{User APY Increase} = \dfrac{0.00372}{0.097} = 3.88%$$

**Expected Rewards Function**

Rewards in the Venus Prime program automatically increase as a user increases its XVS stake, so long as the amount staked and market participation fall within the limits outlined in the "Technical Reward Details" section.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-4ba1c040b81cf593db319f06b748dcae02e09b2e%2Fapy_graph_transparent_2500_corrected_labels.png?alt=media&amp;token=46d24a16-e80f-4f1c-a84c-8251457c7515" alt=""><figcaption><p><em>Please note that the rewards can vary based on the total market participation and the amount of XVS staked, as illustrated by the formula and example above.</em></p></figcaption></figure>

The graph above demonstrates the relationship between an increased XVS staked amount and its effect on market rewards, assuming a constant participation of $2.5K USD in the BTC supply market. This helps visualize how an increase in the staked amount influences the APY.


# Liquidity Hub

### Overview

The **Venus Liquidity Hub** is a per-asset *allocator vault*. A lender deposits a single asset (USDT, for example) and receives a yield-bearing share token. Under a governance-set policy, the Hub automatically spreads that capital across the yield families it is wired to — **Core** (Venus Core lending), **Flux** (Fluid Lending, a third-party protocol), **FRV** (Venus Fixed-Rate Vaults) and new yield sources as they come online — blending the underlying returns into a single yield, removing the need for lenders to pick a product, allocate across it, and assess each one's risk on their own. Capital routed to Flux is exposed to Fluid's contracts, not only to Venus's.

There is **one Hub per asset**, with no cross-asset coupling: a USDT Hub only ever holds and routes USDT. Each Hub is a standard [ERC-4626](https://eips.ethereum.org/EIPS/eip-4626) vault, so any wallet, aggregator, or partner that already speaks ERC-4626 can integrate it once and automatically benefit as Venus adds new yield products behind it.

Yield accrues through a **rising exchange rate** — one share becomes redeemable for more underlying over time — never by rebasing. The number of shares in a wallet does not change; their redemption value does.

### Why a Liquidity Hub

Today a lender has to choose between independent products — Venus Core lending, Fluid-backed Flux markets, and Venus Fixed-Rate Vaults — allocate manually, and monitor each separately. Large and institutional lenders especially want a single, transparent entry point rather than onboarding to each vault individually. The Liquidity Hub shifts the lender experience from *"pick a vault per product"* to *"one-click deposit per asset,"* while giving governance a curated, transparent allocation layer on top of products it already runs.

The Hub is purely a routing layer. It does **not** modify the parameters or governance of the underlying Core / Flux / FRV products — it only moves capital into and out of them.

### The three yield families (Sources)

A **Source** groups downstream products of the same kind behind one uniform interface. v1 ships three:

| Source   | Underlying protocol         | What the Hub holds                      |
| -------- | --------------------------- | --------------------------------------- |
| **Core** | Venus Core lending          | vTokens (Compound-style receipt tokens) |
| **Flux** | Fluid Lending (third-party) | fTokens (ERC-4626 shares)               |
| **FRV**  | Venus Fixed-Rate Vaults     | Fixed-Rate Vault shares (ERC-4626)      |

At launch only **Core** and **Flux** hold a live product. The FRV Source is registered on every Hub with its caps set, but no Fixed-Rate Vault instance exists for these assets on BNB Chain yet, so it is wired to nothing and receives no capital until a follow-up proposal adds one. That follow-up, [VIP-657](https://app.venus.io/#/governance/proposal/657?chainId=56), has since wired the Solv (Ceffu custody) fixed-rate vault into the USDT Hub and the Asseto CASH+ vault into the U Hub; the USDC Hub's FRV Source is still unwired.

The Source set is **governance-extensible**: new yield families can be added later without changing the Hub or the share token, because every Source is reached through the same interface.

### How deposits and withdrawals are routed

The Hub holds two independent, governance-configured ordered queues — a **deposit queue** and a **withdraw queue**. Neither is derived from the other.

* **Deposit** — capital cascades down the deposit queue. Each Source absorbs up to its available capacity (bounded by its cap), and any remainder overflows to the next Source. If the total deposit is larger than the combined free capacity of every Source, the **entire transaction reverts** — there is no partial fill.
* **Withdraw** — the Hub serves the request from its own idle balance first, then walks the withdraw queue, pulling liquidity in order until the request is filled. If the request exceeds total available liquidity, or exceeds the per-transaction withdrawal cap, the **entire transaction reverts**.

At launch the queues are configured **Core-first in, Flux-first out** — deposit `[Core, Flux]`, withdraw `[Flux, Core, FRV]`. Core absorbs everyday inflows, so new capital lands in the deepest and most liquid market first; withdrawals are served from Flux ahead of Core, which keeps Core's balance intact as a buffer. FRV is out of the deposit queue entirely (nothing to route into) and sits last on the withdraw side, where an empty Source costs nothing to walk past. The two orders are set independently and governance can reorder either.

### Operator rebalancing

Beyond user-driven flow, a privileged **Operator** can proactively rebalance capital between *already-registered* Sources and products — for example, pulling funds back to Core when general-market utilization tightens, or seeding a newly onboarded product. Rebalancing is **net-zero** (the amount pulled equals the amount pushed; nothing enters or leaves the Hub) and is bounded by the same caps governance sets. The Operator can never create a new route or move funds outside the registered set.

### Safety envelope

* **Atomic-or-revert** — deposits, withdrawals, and rebalances either complete in full or revert. No partial fills, no stranded remainder.
* **Dual caps per Source** — each Source carries both an absolute cap and a percentage-of-Hub cap; the stricter one binds. A large Source can never quietly exceed its share of the Hub.
* **Per-transaction withdrawal cap** — bounds any single withdrawal so one transaction cannot drain a downstream product's liquidity.
* **Multi-level pause** — the Hub, an individual Source, or a single product can each be paused independently. A broader pause blocks everything beneath it; unaffected siblings keep operating, and the underlying products themselves keep running normally even while the Hub is paused.
* **Asymmetric permissions** — every privileged function is a separate role, and the split governs who holds it *besides* governance. *Loosening* — unpausing at any level, registering a new route, raising the per-transaction withdrawal cap, setting fees — is governance-only, behind a VIP. *Tightening* — lowering a cap, pausing — governance holds as well, but it is additionally delegated to the **Operator**, and pausing is delegated further to a **Guardian** multisig that acts with no timelock delay so it can contain an incident immediately. The Guardian is granted containment only: it can pause at every level but holds no unpause, so it can never undo a governance-ordered pause. The one deliberate exception to the pattern is raising a Source's cap, which the Operator also holds so it can open headroom immediately before a rebalance; it remains bounded by everything else governance set.

### Fees

The Hub supports three governance-controlled fees:

* **Management fee** — a time-based fee on assets under management, minted as dilution shares to a fee recipient.
* **Performance fee** — charged only on gains above a high-water mark, so a fee can never be claimed twice on the same gain. The mark ratchets upward on every accrual and is re-anchored to the entry price only when the vault is refilled after emptying completely, so a new cohort is neither shielded by nor charged for a prior cohort's high. Also minted as dilution shares.
* **Redeem fee** — an exit fee applied when a lender withdraws or redeems. Unlike the other two it is **not protocol revenue** and is never paid to the fee recipient: it stays in the vault and is socialised to the remaining lenders as a price-per-share uplift. Its purpose is anti-sandwich friction — it discourages racing out ahead of a downward repricing at a Fixed-Rate Vault settlement.

At launch all three are set to **0%**. The machinery exists for governance to enable them later; the management and performance fees are capped at 50%, and the redeem fee at 5%.

### Status

The Liquidity Hub launches on **BNB Chain** with three supported assets — **USDT**, **USDC** and **U** — and more to follow. All fees are set to `0` at launch, the Operator role is held by a Venus operations multisig (the routine keeper), and a separate Guardian multisig holds pause rights with no timelock delay. Only **Core** and **Flux** carry a live product at launch: an FRV Source is registered on every Hub, but no Fixed-Rate Vault exists for these assets yet, so no capital routes to it until a follow-up proposal wires one. [VIP-657](https://app.venus.io/#/governance/proposal/657?chainId=56) has since wired fixed-rate vaults into the USDT and U Hubs (see above). Contract addresses are listed in the [technical reference](/technical-reference/reference-liquidity-hub#deployment).

For the contract-level architecture, flows, and full API, see the [Liquidity Hub technical reference](/technical-reference/reference-liquidity-hub).


# VIPs

Venus Protocol is governed through Venus Improvement Proposals (VIPs). On BNB Chain, the Governor reads voting power from the XVS Vault and executes successful proposals through route-specific timelocks. Cross-chain VIPs use the [omnichain governance system](/technical-reference/reference-technical-articles/omnichain-governance) to deliver approved actions to other networks.

## Proposal lifecycle

1. A proposer with enough delegated voting power submits a VIP.
2. Voting power is snapshotted at the proposal's start block.
3. Delegates vote **For**, **Against**, or **Abstain**.
4. A successful proposal is queued in its assigned timelock.
5. After the timelock delay, anyone can execute the queued proposal.

A proposal succeeds only when **For** votes are greater than **Against** votes and at least the quorum. Abstain votes are recorded but do not satisfy the quorum requirement.

## Proposal routes and current configuration

Governance supports Normal, Fast-track, and Critical proposal routes. The Governor stores the voting delay, voting period, and proposal threshold separately for each route.

The following values were read from the BNB Chain Governor at block `118,362,254`:

| Route      | Voting delay |  Voting period | Proposal threshold | Timelock delay |
| ---------- | -----------: | -------------: | -----------------: | -------------: |
| Normal     |      1 block | 192,384 blocks |      1,000,000 XVS |       48 hours |
| Fast-track |      1 block | 192,384 blocks |      1,000,000 XVS |        6 hours |
| Critical   |      1 block |  48,096 blocks |      1,000,000 XVS |         1 hour |

The global quorum is currently 1,500,000 **For** votes, and a proposal can contain at most 100 actions.

{% hint style="warning" %}
These are configurable on-chain values, not permanent protocol constants. Voting periods are measured in blocks, so they should not be presented as a guaranteed wall-clock duration. Before creating a VIP, verify `proposalConfigs`, `quorumVotes`, `proposalTimelocks`, and the selected timelock's `delay` directly on-chain. See [Deployed Governance Contracts](/deployed-contracts/governance).
{% endhint %}

### Critical route status

[VIP-645](https://venus.io/governance/proposal/645?chainId=56) removed every permission held by the Critical Timelock across Venus deployments. The route and its configuration still exist, but it cannot currently execute privileged protocol actions. Emergency responses instead use explicitly granted guardian permissions, the fine-grained pause mechanism, or a governance route that has the required permission.

## Access control and guardians

Venus contracts use an Access Control Manager (ACM) for action-scoped permissions. A permission grants a specific caller the ability to invoke a specific function; it does not make that caller a global administrator.

This model lets governance grant narrowly scoped capabilities to timelocks or guardians. For example, an authorized guardian may be able to pause a particular market action without being able to change unrelated parameters. Because these permissions can change through VIPs, integrations and operators should check the live ACM state instead of relying on a static list of roles.

The fine-grained pause mechanism can independently pause actions such as supplying, borrowing, or using an asset as collateral. The exact supported actions and authorized callers depend on the deployed contract and network.

## Related documentation

* [Delegate voting power and vote](/guides/governance/delegating)
* [Submit a VIP](/guides/governance/vip)
* [Governor Bravo Delegate reference](/technical-reference/reference-governance/governor-bravo-delegate)
* [Deployed Governance Contracts](/deployed-contracts/governance)

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-1e3970c4834ee43bf998d6c756bcad56d6961e50%2F0ba42e0a-87cc-4694-9a73-52334a5fd28e.png?alt=media" alt="Venus governance proposal, voting, timelock, and execution process"><figcaption><p><em>Governance process</em></p></figcaption></figure>


# Tokenomics

Venus Protocol revenue distribution is configured by governance. [Tokenomics version 4.1](https://snapshot.box/#/s:venus-xvs.eth/proposal/0xb8f03ad2dd2988a6d2e89a1adbebc52c7a62b284ea493008752c71b7f00b3386) established the model, [VIP-585](https://venus.io/governance/proposal/585?chainId=56) ended the BNB Burn allocation and introduced chain-revenue eligibility rules, [VIP-620](https://venus.io/governance/proposal/620?chainId=56) (May 2026) replaced the token converters with single-purpose TokenBuyback contracts, and [VIP-638](https://venus.io/governance/proposal/638?chainId=56) (executed 1 July 2026) implemented [Tokenomics Phase II](https://community.venus.io/t/venus-tokenomics-phase-ii-prime-rewards-redesign/5774): the share previously routed to XVS Vault rewards now goes to Venus Prime.

{% hint style="warning" %}
The percentages and reward speeds below are a governance-policy snapshot, not immutable token properties. Check recent VIPs and the live Protocol Share Reserve and XVS Vault configuration before using them in financial models.
{% endhint %}

## Protocol reserve revenue

Protocol reserves are primarily generated from interest paid by borrowers. For an eligible deployment, the configured allocation is:

| Destination | Share |
| ----------- | ----: |
| Treasury    |   40% |
| Venus Prime |   40% |
| Risk Fund   |   20% |

XVS Vault rewards no longer receive a share of protocol revenue on BNB Chain; since VIP-638 that 20% is allocated to Venus Prime. The Prime allocation supplies eligible markets with protocol-funded rewards. The Risk Fund helps cover protocol shortfalls.

Each share is delivered through a TokenBuyback contract rather than a direct transfer: the Prime share is bought into USDT and U and sent to the PrimeLiquidityProvider, the Risk Fund share is bought into USDT and sent to RiskFundV2, and the Treasury share is bought into U, BTCB, ETH, USDT, USDC and XVS and sent to the VTreasury. An off-chain operator executes the buybacks. The live rows can be read with `totalDistributions()` and `distributionTargets(i)` on the [Protocol Share Reserve](/deployed-contracts/funds).

## Other revenue

Revenue streams such as liquidation income use this allocation:

| Destination | Share |
| ----------- | ----: |
| Treasury    |   60% |
| Venus Prime |   20% |
| Risk Fund   |   20% |

New products or revenue sources may use a different governance-approved configuration.

## Chain eligibility

A deployment is eligible to allocate revenue to Venus Prime and the Risk Fund when it generates at least $50,000 in average monthly revenue over the preceding six months. An ineligible deployment routes 100% of its revenue to the Treasury; at the time of writing Ethereum and Arbitrum One route 100% of both revenue types to their VTreasury. Governance periodically evaluates eligibility, so it can change as the rolling revenue window changes.

## XVS Vault rewards

Vault reward speed is governance-configured. Since VIP-638 the vault is not a Protocol Share Reserve destination; its rewards are funded by governance proposals, which can combine a base allocation with XVS bought back into the Treasury. It is not a guaranteed yield or a permanent emission rate.

[VIP-641](https://venus.io/governance/proposal/641?chainId=56) retained a base allocation equivalent to 308.7 XVS per day and, together with buybacks, configured a total BNB Chain vault speed equivalent to 535 XVS per day for Q3 2026. At BNB Chain block `118,362,254`, the XVS Vault reported `0.002786458333333333` XVS per block, equivalent to the same nominal daily rate at 192,000 blocks per day.

Future VIPs can fund the vault and change the speed at any time. Read `rewardTokenAmountsPerBlockOrSecond(XVS)` on the live XVS Vault and review its funding balance before quoting a current reward rate.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-4d0807b1f555b6f3fc1353be4ed85d383de4b9df%2Ftokenomics.svg?alt=media" alt="Venus Protocol revenue distribution between the Treasury, Venus Prime, and Risk Fund"><figcaption><p><em>Governance-configured revenue distribution</em></p></figcaption></figure>


# Resilient Price Oracle

### Overview

Before Venus V4, Venus relied on a Chainlink oracle adapter for protocol prices. A single configured source can become a single point of failure: an erroneous, manipulated, or stale price can cause improper liquidations or unsafe borrowing.

Venus V4 introduced `ResilientOracle`, which lets governance assign main, pivot, and fallback roles per underlying asset. When multiple roles are enabled, the contract can compare sources within governance-set bounds and try another valid source when a comparison fails.

This design provides configurable validation and fallback paths; it does not guarantee that every asset has multiple independent sources. If no pivot is configured or enabled, a valid main-oracle response can be returned without cross-validation. Oracle addresses, roles, enable flags, and bounds are mutable governance configuration and must be checked on-chain.

### Key Features

#### Resilient Price Feeds

The Resilient Price Feeds replace a single provider in the Comptroller with per-asset source roles. Published configurations currently include Chainlink, RedStone, Binance, Atlas, APRO, and asset-specific adapters. A provider can be used as main, pivot, or fallback depending on the asset; the provider name alone does not determine its role.

#### Governance Configurations

Authorized governance actions can pause or unpause a `ResilientOracle`, set an asset's source addresses and roles, enable or disable a role, and update validation bounds. Fixed-price behavior, where used, is implemented by the configured source rather than by treating this page as configuration.

### Safety Measures

In implementing the Resilient Price Oracle, several safety measures have been adopted to ensure the security and continuity of the Venus Protocol:

* **Price Continuity:** Asset prices pre and post upgrade were validated in a simulated environment to ensure consistency.
* **Testnet Deployment:** The oracles have been deployed and tested in the Venus Protocol testnet environment.
* **Auditing:** Specific revisions have been reviewed by OpenZeppelin, Peckshield, Certik, and Hacken. Read each report's exact scope and verify that the deployed implementation maps to the reviewed revision.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-9831fd80e9f9e966dbc3c9d020db062e52aedbad%2F17b75928-d6a2-4207-9a0b-89d1d41690d4.png?alt=media" alt=""><figcaption></figcaption></figure>

### Protection Mode

For low-liquidity collateral assets, the Resilient Oracle's spot price is wrapped by the [DeviationBoundedOracle](/technical-reference/reference-oracle/deviation-bounded-oracle) on the borrow-power path. When the spot deviates beyond a per-asset threshold from the recent rolling window, [Protection Mode](/risk/protection-mode) activates and the Comptroller switches to bounded prices — `min(spot, windowMin)` for collateral and `max(spot, windowMax)` for debt — until conditions normalize. Liquidations continue to use the Resilient Oracle's spot price directly.

### Correlated Token Oracles

For correlated tokens, such as liquid staking tokens (LSTs), an adapter can read the on-chain exchange rate between correlated assets and multiply it by the underlying asset's USD price.

Venus uses dedicated adapters for supported correlated assets to calculate a price as follows:

* convert the LST to its underlying token using the rate exposed by the relevant protocol contract; and
* convert the resulting underlying amount to USD using the configured market-price oracle.

The current list of correlated token oracles in Venus is:

* `AnkrBNBOracle`. It returns the USD price of the [ankrBNB](https://bscscan.com/address/0x52F24a5e03aee338Da5fd9Df68D2b6FAe1178827) token, converting on-chain from ankrBNB to BNB using the exchange rate from the ankrBNB contract.
* `BNBxOracle`. It returns the USD price of the [BNBx](https://bscscan.com/address/0x1bdd3Cf7F79cfB8EdbB955f20ad99211551BA275) token, converting on-chain from BNBx to BNB using the exchange rate from the [stake manager](https://bscscan.com/address/0x3b961e83400D51e6E1AF5c450d3C7d7b80588d28) contract.
* `eBTCAccountantOracle` (instance of `EtherfiAccountantOracle`). It returns the USD price of the [eBTC](https://etherscan.io/token/0x657e8C867D8B37dCC18fA4Caead9C45EB088C642) token, converting on-chain from eBTC to WBTC using the exchange rate from the [Accountant](https://etherscan.io/address/0x1b293DC39F94157fA0D1D36d7e0090C8B8B8c13F) contract.
* `PendleOracle`. It returns the USD price of the PT Pendle token, converting on-chain from the PT token to the underlying token using a Pendle market contract.
* `SFraxOracle`. It returns the USD price of the [sFRAX](https://etherscan.io/token/0xa663b02cf0a4b149d2ad41910cb81e23e1c41c32) token, converting on-chain from sFRAX to FRAX using the exchange rate from the sFRAX contract.
* `SlisBNBOracle`. It returns the USD price of the [slisBNB](https://bscscan.com/address/0xB0b84D294e0C75A6abe60171b70edEb2EFd14A1B) token, converting on-chain from slisBNB to BNB using the exchange rate from the [stake manager](https://bscscan.com/address/0x1adB950d8bB3dA4bE104211D5AB038628e477fE6) contract.
* `AsBNBOracle`. It returns the USD price of the [asBNB](https://bscscan.com/address/0x77734e70b6E88b4d82fE632a168EDf6e700912b6) token, converting on-chain from asBNB to slisBNB using the exchange rate from the [asBNB minter](https://bscscan.com/address/0x2F31ab8950c50080E77999fa456372f276952fD8) contract.
* `StkBNBOracle`. It returns the USD price of the [stkBNB](https://bscscan.com/address/0xc2E9d07F66A89c44062459A47a0D2Dc038E4fb16) token, converting on-chain from stkBNB to BNB using the exchange rate from the [stake pool](https://bscscan.com/address/0xC228CefDF841dEfDbD5B3a18dFD414cC0dbfa0D8) contract.
* `WBETHOracle`. It returns the USD price of the [WBETH](https://bscscan.com/address/0xa2e3356610840701bdf5611a53974510ae27e2e1) token, converting on-chain from WBETH to BNB using the exchange rate from the WBETH contract.
* `WeETHOracle`. It returns the USD price of the [weETH](https://etherscan.io/token/0xcd5fe23c85820f7b72d0926fc9b05b43e359b7ee) token, converting on-chain from weETH to eETH using the exchange rate from the [liquidity pool](https://etherscan.io/address/0x308861A430be4cce5502d0A12724771Fc6DaF216) contract and assuming 1 eETH = 1 ETH.
* `WeETHsOracle` (instance of `WeETHAccountantOracle`). It returns the USD price of the [weETHs](https://etherscan.io/token/0x917ceE801a67f933F2e6b33fC0cD1ED2d5909D88) token, converting on-chain from weETHs to WETH using the exchange rate from the [Accountant](https://etherscan.io/address/0xbe16605B22a7faCEf247363312121670DFe5afBE) contract.
* `WstETHOracle`. It returns the USD price of the [wstETH](https://etherscan.io/token/0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0) token, converting on-chain from wstETH to stETH using the exchange rate from the [stETH](https://etherscan.io/token/0xae7ab96520de3a18e5e111b5eaab095312d7fe84) contract and assuming 1 stETH = 1 ETH.

{% hint style="warning" %}
**Assumption on Liquid Staked Tokens**

`WeETHOracle` and `WstETHOracle` assume a 1:1 price ratio between the LST's immediate underlying asset and ETH (for example, 1 stETH = 1 ETH). Smart-contract, counterparty, liquidity, or redemption failures can break that assumption. The existence of a market-price adapter, monitor, pivot, or fallback does not prove it is configured or enabled for an asset. Verify the live source roles, bounds, and latest governance actions before relying on the assumption or any secondary path.
{% endhint %}

### Current configuration

{% hint style="warning" %}
The tables below are a documentation snapshot, last updated for BNB Chain oracle changes in VIP-662 on September 21, 2026. They cover only the listed Core markets and networks and are not an exhaustive deployment registry or a live status feed. A `(Paused)` label is also only a snapshot of market lifecycle state. For live verification, resolve the Comptroller's active oracle, then read `getTokenConfig(asset)`, `getOracle(asset, role)`, the relevant `BoundValidator` bounds, and the latest governance execution. See [deployed oracle addresses](/deployed-contracts/oracles).
{% endhint %}

#### BNB chain

| Pool | Market                      | MAIN oracle                                                                                                 | PIVOT oracle                                                                                              | FALLBACK oracle                                                                    | Notes                                                                                                  |
| ---- | --------------------------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| Core | AAVE                        | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | ADA                         | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | asBNB                       | [AsBNBOracle](https://bscscan.com/address/0x652B90D1d45a7cD5BE82c5Fb61a4A00bA126dde5)                       | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | -                                                                                  | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | BCH                         | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | BETH (Paused)               | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | -                                                                                                         | -                                                                                  |                                                                                                        |
| Core | BNB                         | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.01. Lower bound: 0.99                                                                   |
| Core | WBNB                        | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.01. Lower bound: 0.99                                                                   |
| Core | BTCB                        | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.01. Lower bound: 0.99                                                                   |
| Core | BUSD (Paused)               | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | -                                                                                                         | -                                                                                  | [Price fixed to $1](https://venus.io/governance/proposal/226?chainId=56)                               |
| Core | CAKE                        | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | DAI                         | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | DOGE                        | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | DOT                         | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | ETH                         | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.01. Lower bound: 0.99                                                                   |
| Core | FDUSD                       | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | FIL                         | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | LINK                        | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | lisUSD                      | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                             | -                                                                                                         | -                                                                                  |                                                                                                        |
| Core | LTC                         | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | MATIC (Paused)              | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | -                                                                                                         | -                                                                                  | Price feed from [$POL](https://data.chain.link/feeds/bsc/mainnet/pol-usd)                              |
| Core | SOL                         | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | slisBNB                     | [SlisBNBOracle](https://bscscan.com/address/0xDDE6446E66c786afF4cd3D183a908bCDa57DF9c1)                     | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | -                                                                                  | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | solvBTC                     | [SolvBTCOneJumpChainlinkOracle](https://bscscan.com/address/0x3f4bC081E749032cffF29dcA2E8408Ec375e745A)     | [SolvBTCOneJumpFundamentalOracle](https://bscscan.com/address/0x1f785B1AFE0808d69d1188db9e47b7B9Dd95ab09) | -                                                                                  | Upper bound: 1.02. Lower bound: 0.98. MAIN reads the Chainlink SolvBTC/BTC exchange rate feed          |
| Core | sUSDe                       | [sUSDeOneJumpRedstoneOracle](https://bscscan.com/address/0x2B2895104f958E1EC042E6Ba5cbfeCbAD3C5beDb)        | [sUSDeOneJumpChainlinkOracle](https://bscscan.com/address/0xA67F01322AF8EBa444D788Ee398775b446de51a0)     | -                                                                                  | Upper bound: 1.01. Lower bound: 0.99                                                                   |
| Core | SXP (Paused)                | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | -                                                                                                         | -                                                                                  | [Price fixed to $0.00046](https://venus.io/governance/proposal/631?chainId=56)                         |
| Core | THE                         | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | TRX                         | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.01. Lower bound: 0.99                                                                   |
| Core | TUSD                        | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | -                                                                                  | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | TRXOLD (Paused)             | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a)                        | -                                                                                  | Upper bound: 1.01. Lower bound: 0.99                                                                   |
| Core | TUSDOLD (Paused)            | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | -                                                                                                         | -                                                                                  |                                                                                                        |
| Core | TWT                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                             | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a)                        | -                                                                                  | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | UNI                         | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | USDC                        | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.01. Lower bound: 0.99                                                                   |
| Core | USDe                        | [USDTChainlinkOracle](https://bscscan.com/address/0x22Dc2BAEa32E95AB07C2F5B8F63336CbF61aB6b8)               | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                       | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.06. Lower bound: 0.94                                                                   |
| Core | USDT                        | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.01. Lower bound: 0.99                                                                   |
| Core | USD1                        | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) |                                                                                                        |
| Core | VAI                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                             | -                                                                                                         | -                                                                                  | Single price source: no pivot cross-validation and no fallback. Max stale period 86,700 seconds        |
| Core | WBETH                       | [WBETHOracle](https://bscscan.com/address/0x49938fc72262c126eb5D4BdF6430C55189AEB2BA)                       | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | -                                                                                  | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | XRP                         | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | xSolvBTC                    | [xSolvBTCOneJumpChainlinkOracle](https://bscscan.com/address/0xDfDbF9DAFbc94Cb1F827d3364637dDBB26823739)    | [xSolvBTCOneJumpRedstoneOracle](https://bscscan.com/address/0xf5534f78Df9b610B19A63956d498d00CFaD8B9D3)   | -                                                                                  | Upper bound: 1.02. Lower bound: 0.98. MAIN reads the Chainlink SVR xSolvBTC/SolvBTC exchange rate feed |
| Core | XVS                         | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | [RedStone](https://bscscan.com/address/0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a) | Upper bound: 1.05. Lower bound: 0.95                                                                   |
| Core | U                           | [Chainlink](https://bscscan.com/address/0x1B2103441A0A108daD8848D8F5d790e4D402921F)                         | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                           | -                                                                                  | Upper bound: 1.01. Lower bound: 0.99                                                                   |
| Core | PT-clisBNB-25JUN2026        | [PendleOracle-PT-clisBNB-25JUN2026](https://bscscan.com/address/0x0FfFBb55d51cD46cD10C7dc865Dc73BD76201310) | -                                                                                                         | -                                                                                  |                                                                                                        |
| Core | PT-sUSDE-26JUN2025 (Paused) | [PendleOracle-PT-sUSDe-26JUN2025](https://bscscan.com/address/0xC407403fa78Bce509821D776b6Be7f91cC04474f)   | -                                                                                                         | -                                                                                  |                                                                                                        |
| Core | TSLAB                       | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                             | [APRO](https://bscscan.com/address/0x04480f1Ba2252CDF89deB022B58d0a03d1B4cF91)                            | -                                                                                  | Upper bound: 1.05. Lower bound: 0.95. Oracle Dynamic Protection (16.67% deviation trigger)             |
| Core | NVDAB                       | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                             | [APRO](https://bscscan.com/address/0x04480f1Ba2252CDF89deB022B58d0a03d1B4cF91)                            | -                                                                                  | Upper bound: 1.05. Lower bound: 0.95. Oracle Dynamic Protection (16.67% deviation trigger)             |
| Core | SPCXB                       | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                             | [APRO](https://bscscan.com/address/0x04480f1Ba2252CDF89deB022B58d0a03d1B4cF91)                            | -                                                                                  | Upper bound: 1.05. Lower bound: 0.95. Oracle Dynamic Protection (16.67% deviation trigger)             |
| Core | SKHYB                       | [Atlas](https://bscscan.com/address/0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0)                             | [APRO](https://bscscan.com/address/0x04480f1Ba2252CDF89deB022B58d0a03d1B4cF91)                            | -                                                                                  | Upper bound: 1.05. Lower bound: 0.95. Oracle Dynamic Protection (16.67% deviation trigger)             |

#### Ethereum

| Pool | Market         | MAIN oracle                                                                                              | PIVOT oracle                                                                         | FALLBACK oracle                                                                      | Notes                                |
| ---- | -------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | ------------------------------------ |
| Core | crvUSD         | [Chainlink](https://etherscan.io/address/0x94c3A2d6B7B2c051aDa041282aec5B0752F8A1F2)                     | -                                                                                    | -                                                                                    |                                      |
| Core | DAI            | [Chainlink](https://etherscan.io/address/0x94c3A2d6B7B2c051aDa041282aec5B0752F8A1F2)                     | -                                                                                    | -                                                                                    |                                      |
| Core | TUSD (Paused)  | [Chainlink](https://etherscan.io/address/0x94c3A2d6B7B2c051aDa041282aec5B0752F8A1F2)                     | -                                                                                    | -                                                                                    |                                      |
| Core | USDC           | [Chainlink](https://etherscan.io/address/0x94c3A2d6B7B2c051aDa041282aec5B0752F8A1F2)                     | -                                                                                    | -                                                                                    |                                      |
| Core | USDT           | [Chainlink](https://etherscan.io/address/0x94c3A2d6B7B2c051aDa041282aec5B0752F8A1F2)                     | -                                                                                    | -                                                                                    |                                      |
| Core | WBTC           | [Chainlink](https://etherscan.io/address/0x94c3A2d6B7B2c051aDa041282aec5B0752F8A1F2)                     | -                                                                                    | -                                                                                    |                                      |
| Core | WETH           | [Chainlink](https://etherscan.io/address/0x94c3A2d6B7B2c051aDa041282aec5B0752F8A1F2)                     | -                                                                                    | -                                                                                    |                                      |
| Core | FRAX (Paused)  | [Chainlink](https://etherscan.io/address/0x94c3A2d6B7B2c051aDa041282aec5B0752F8A1F2)                     | -                                                                                    | -                                                                                    |                                      |
| Core | LBTC           | [LBTCOneJumpRedStoneOracle](https://etherscan.io/address/0x54B033D102db7DD734E0Ad649463E90fFA78D853)     | -                                                                                    | -                                                                                    |                                      |
| Core | EIGEN (Paused) | [Chainlink](https://etherscan.io/address/0x94c3A2d6B7B2c051aDa041282aec5B0752F8A1F2)                     | -                                                                                    | -                                                                                    |                                      |
| Core | sFRAX (Paused) | [SFraxOracle](https://etherscan.io/address/0x1aDCE75BB3164bBf6060a4f44262df5414473110)                   | -                                                                                    | -                                                                                    |                                      |
| Core | eBTC           | [eBTCAccountantOracle](https://etherscan.io/address/0x04d6096A6F089047C7af6E4644D18fB766B8d4cE)          | -                                                                                    | -                                                                                    |                                      |
| Core | USDS           | [Chainlink](https://etherscan.io/address/0x94c3A2d6B7B2c051aDa041282aec5B0752F8A1F2)                     | -                                                                                    | -                                                                                    |                                      |
| Core | sUSDS          | [sUSDS-ERC4646Oracle](https://etherscan.io/address/0xEC3865a8a5DCb8C507781DA17A38b754E3d01C50)           | -                                                                                    | -                                                                                    |                                      |
| Core | weETHs         | [WeETHAccountantOracle(weETHs)](https://etherscan.io/address/0x47f7a7f3486b08a019e0c10af969adc4b6e415cd) | -                                                                                    | -                                                                                    |                                      |
| Core | BAL (Paused)   | [Chainlink](https://etherscan.io/address/0x94c3A2d6B7B2c051aDa041282aec5B0752F8A1F2)                     | -                                                                                    | -                                                                                    |                                      |
| Core | sUSDe          | [sUSDe-ERC4626Oracle](https://etherscan.io/address/0xaE847E81ff6dD2bdFB1fD563ccB4f848c74D2B70)           | -                                                                                    | -                                                                                    |                                      |
| Core | USDe           | [RedStone](https://etherscan.io/address/0x0FC8001B2c9Ec90352A46093130e284de5889C86)                      | [Chainlink](https://etherscan.io/address/0x94c3A2d6B7B2c051aDa041282aec5B0752F8A1F2) | [Chainlink](https://etherscan.io/address/0x94c3A2d6B7B2c051aDa041282aec5B0752F8A1F2) | Upper bound: 1.01. Lower bound: 0.99 |
| Core | tBTC           | [Chainlink](https://etherscan.io/address/0x94c3A2d6B7B2c051aDa041282aec5B0752F8A1F2)                     | -                                                                                    | -                                                                                    |                                      |

#### Arbitrum One

| Pool | Market      | MAIN oracle                                                                                        | PIVOT oracle | FALLBACK oracle | Notes |
| ---- | ----------- | -------------------------------------------------------------------------------------------------- | ------------ | --------------- | ----- |
| Core | WBTC        | [SequencerChainlinkOracle](https://arbiscan.io/address/0x9cd9fcc7e3deda360de7c080590aad377ac9f113) | -            | -               |       |
| Core | WETH        | [SequencerChainlinkOracle](https://arbiscan.io/address/0x9cd9fcc7e3deda360de7c080590aad377ac9f113) | -            | -               |       |
| Core | USDC        | [SequencerChainlinkOracle](https://arbiscan.io/address/0x9cd9fcc7e3deda360de7c080590aad377ac9f113) | -            | -               |       |
| Core | USDT        | [SequencerChainlinkOracle](https://arbiscan.io/address/0x9cd9fcc7e3deda360de7c080590aad377ac9f113) | -            | -               |       |
| Core | gmWETH-USDC | [SequencerChainlinkOracle](https://arbiscan.io/address/0x9cd9fcc7e3deda360de7c080590aad377ac9f113) | -            | -               |       |
| Core | gmBTC-USDC  | [SequencerChainlinkOracle](https://arbiscan.io/address/0x9cd9fcc7e3deda360de7c080590aad377ac9f113) | -            | -               |       |
| Core | ARB         | [SequencerChainlinkOracle](https://arbiscan.io/address/0x9cd9fcc7e3deda360de7c080590aad377ac9f113) | -            | -               |       |

#### ZKsync Mainnet

| Pool | Market  | MAIN oracle                                                                                                   | PIVOT oracle                                                                                     | FALLBACK oracle                                                                                  | Notes                                |
| ---- | ------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ | ------------------------------------ |
| Core | WBTC    | [ChainlinkOracle](https://explorer.zksync.io/address/0x4FC29E1d3fFFbDfbf822F09d20A5BE97e59F66E5)              | -                                                                                                | -                                                                                                |                                      |
| Core | WETH    | [ChainlinkOracle](https://explorer.zksync.io/address/0x4FC29E1d3fFFbDfbf822F09d20A5BE97e59F66E5)              | -                                                                                                | -                                                                                                |                                      |
| Core | USDC    | [ChainlinkOracle](https://explorer.zksync.io/address/0x4FC29E1d3fFFbDfbf822F09d20A5BE97e59F66E5)              | -                                                                                                | -                                                                                                |                                      |
| Core | USDC\_E | [ChainlinkOracle](https://explorer.zksync.io/address/0x4FC29E1d3fFFbDfbf822F09d20A5BE97e59F66E5)              | -                                                                                                | -                                                                                                |                                      |
| Core | USDT    | [ChainlinkOracle](https://explorer.zksync.io/address/0x4FC29E1d3fFFbDfbf822F09d20A5BE97e59F66E5)              | -                                                                                                | -                                                                                                |                                      |
| Core | ZK      | [RedStoneOracle](https://explorer.zksync.io/address/0xFa1e65e714CDfefDC9729130496AB5b5f3708fdA)               | [ChainlinkOracle](https://explorer.zksync.io/address/0x4FC29E1d3fFFbDfbf822F09d20A5BE97e59F66E5) | [ChainlinkOracle](https://explorer.zksync.io/address/0x4FC29E1d3fFFbDfbf822F09d20A5BE97e59F66E5) | Upper bound: 1.01. Lower bound: 0.99 |
| Core | wstETH  | [wstETHOneJumpChainlinkOracle](https://explorer.zksync.io/address/0x2DAaeb94E19145BA7633cAB2C38c76fD8c493198) | -                                                                                                | -                                                                                                |                                      |
| Core | wUSDM   | [wUSDM-ERC4626Oracle](https://explorer.zksync.io/address/0x22cE94e302c8C80a6C2dCfa9Da6c5286e9f28692)          | -                                                                                                | -                                                                                                |                                      |
| Core | zkETH   | [ZkETHOracle](https://explorer.zksync.io/address/0x407dE1229BCBD2Ec876d063F3F93c4D8a38bd81a)                  | -                                                                                                | -                                                                                                | Assume 1:1 for WETH:rzkETH           |

#### Base Mainnet

| Pool | Market      | MAIN oracle                                                                                             | PIVOT oracle | FALLBACK oracle | Notes |
| ---- | ----------- | ------------------------------------------------------------------------------------------------------- | ------------ | --------------- | ----- |
| Core | cbBTC       | [ChainlinkOracle](https://basescan.org/address/0x6F2eA73597955DB37d7C06e1319F0dC7C7455dEb)              | -            | -               |       |
| Core | WETH        | [ChainlinkOracle](https://basescan.org/address/0x6F2eA73597955DB37d7C06e1319F0dC7C7455dEb)              | -            | -               |       |
| Core | USDC        | [ChainlinkOracle](https://basescan.org/address/0x6F2eA73597955DB37d7C06e1319F0dC7C7455dEb)              | -            | -               |       |
| Core | wsuperOETHb | [wsuperOETHb-ERC4626Oracle](https://basescan.org/address/0xcd1d2C99642165440c2CC023AFa2092b487f033e)    | -            | -               |       |
| Core | wstETH      | [wstETHOneJumpChainlinkOracle](https://basescan.org/address/0xDDD4F0836c8016E11fC6741A4886E97B3c3d20C1) | -            | -               |       |

### Further Reading

For more detailed information, refer to the following resources:

#### Audit reports

* [Resilient Price Oracle](/links/security-and-audits#oracles)
* [Resilient Price Oracle upgrade](/links/security-and-audits#oracles-upgrade-2023-07-24)
* [WstETH oracle](/links/security-and-audits#oracle-for-wsteth)
* [Correlated token oracles](/links/security-and-audits#correlated-token-oracles)

#### References

* [Repository](https://github.com/VenusProtocol/oracle)
* [Community post about Venus V4, introducing Resilient Price Feeds](https://community.venus.io/t/proposing-venus-v4/3188#price-feed-redundancy-6)


# Protection Mode

### Overview

Venus already protects every market with the [Resilient Oracle](/risk/resilient-price-oracle), which validates prices across multiple independent sources before any value is used by the protocol. Protection Mode adds a second layer on top of that foundation for whitelisted assets prone to manipulation (typically low-liquidity tokens, used as either collateral or borrow asset): when a sudden price move on an asset looks like manipulation, Protection Mode activates and switches its borrow-capacity price to a conservative, manipulation-resistant value, while liquidations continue to use real-time spot.

The risk it mitigates is well-known on lending protocols. An attacker can pump the price of a low-liquidity token they hold as collateral, borrow far more than the token's real value warrants, and exit before the protocol can recover — leaving the protocol holding collateral worth a fraction of the outstanding debt, bad debt that socializes losses across all lenders. The mirror attack on the debt side has the same structure: an attacker manipulates the oracle to undervalue a borrow asset, drains far more of it than the collateral truly warrants at fair value, and exits before the price recovers — leaving the protocol with debt whose true value exceeds the collateral backing it.

Protection Mode prevents this by introducing a per-asset rolling spot-price window. When the current spot deviates beyond a configured threshold from that window, the asset's borrow-power calculation switches to a conservative dual-price scheme:

* **Collateral value** is computed at `min(spot, windowMin)` — caps borrow power at the recent window low so a pumped asset cannot be over-borrowed against.
* **Debt value** is computed at `max(spot, windowMax)` — floors the valuation of debt at the recent window high so a crashed borrow asset cannot be over-borrowed at the manipulated low price.
* **Liquidations continue to use spot price** — eligibility and seize-amount calculations stay on real-time prices so liquidator incentives stay correct and underwater positions are still caught immediately.
* **Only new actions are constrained** — existing positions keep their health factor evaluated at spot, so activation does not endanger any open position; the bounded prices apply only to new borrows and additional collateral movements.

### Key Mechanics

#### Rolling Price Window

For each whitelisted asset the [DeviationBoundedOracle](/technical-reference/reference-oracle/deviation-bounded-oracle) tracks a `(minPrice, maxPrice)` pair representing the lowest and highest spot prices observed inside a short rolling window (target: \~15 minutes). It is maintained from two sources:

* **User-triggered, expansion only.** Every relevant price read pulls spot from the [Resilient Oracle](/risk/resilient-price-oracle). If spot is below `minPrice` it is pulled down to spot; if above `maxPrice`, pushed up. User activity can only widen the window — never contract it.
* **Keeper-triggered, bidirectional.** An off-chain keeper maintains the true rolling window and pushes corrected `minPrice` / `maxPrice` values on-chain. The keeper queries `checkAndGetWindowDrift` to identify assets whose stored bounds have drifted past the 5% `KEEPER_DEADBAND` and only pushes those, to avoid wasting gas on no-op corrections. This is what brings the window back inwards once an extreme has aged out.

Keeper writes are validated on-chain to prevent inverted or implausible windows: a `newMin` must be `≤ spot` and `≤ maxPrice`, and a `newMax` must be `≥ spot` and `≥ minPrice`. The deadband itself is *not* enforced on-chain — it is purely a keeper-side push decision. Keeper liveness is a hard dependency for window correctness.

#### Trigger

Protection activates as a side-effect of a price read when the spot crosses the deviation threshold relative to the stored bounds:

* **Pump:** `spot > minPrice × (1 + triggerThreshold)` — spot is significantly above the recent low.
* **Crash:** `spot < maxPrice × (1 − triggerThreshold)` — spot is significantly below the recent high.

The trigger threshold is set per asset by governance, sits between 5% and 50% (the lower bound keeps routine keeper corrections from accidentally firing it), and is informed by the asset's collateral factor. The cooldown timer is re-stamped only when spot makes a *new* extreme (a new window low or high) while the trigger is sustained; recovery within the existing window keeps the cooldown ticking, so exit remains reachable once the price stabilises.

#### Exit

Protection cannot turn itself off. Exit is gated by two conditions, both of which must hold:

1. **Cooldown elapsed** since the most recent trigger.
2. **Window converged**: `(maxPrice − minPrice) / minPrice < resetThreshold`, where `resetThreshold` is configured below `triggerThreshold`.

When both conditions are satisfied, the off-chain monitor / keeper calls `exitProtectionMode(asset)` (or includes an `ExitProtectionMode` item inside `syncPriceBoundsAndProtections`) to clear the flag. Governance retains a fallback path.

#### Per-Asset Whitelist and Configuration

Protection Mode is opt-in per asset. Governance enables it by calling `setTokenConfig` (or the batch variant) on the DeviationBoundedOracle, which:

* Initializes the rolling window from the current spot.
* Stores the per-asset `cooldownPeriod`, `triggerThreshold`, and `resetThreshold`.
* Sets the bounded-pricing whitelist flag from the `enableBoundedPricing` argument — when `true` the asset participates in the Comptroller's bounded borrow-power path; when `false` the asset is initialized but the DBO short-circuits to spot until governance flips the flag with `setAssetBoundedPricingEnabled`.
* Sets the per-asset transient-cache flag from the `enableCaching` argument (configurable later via `setCachingEnabled`).

VAI is hard-rejected at config time and can never be whitelisted. For all other assets, whether Protection Mode is enabled is a governance decision driven by each asset's liquidity profile and manipulation surface; it can be toggled later with `setAssetBoundedPricingEnabled` (it cannot be turned off while protection is active).

### Where Protection Mode Applies

| Operation                                 | Price Used When Protected | Price Used Otherwise |
| ----------------------------------------- | ------------------------- | -------------------- |
| Borrow-power evaluation (collateral side) | `min(spot, windowMin)`    | spot                 |
| Borrow-power evaluation (debt side)       | `max(spot, windowMax)`    | spot                 |
| Liquidation eligibility                   | spot                      | spot                 |
| Liquidation seize amount                  | spot                      | spot                 |

The Comptroller routes borrow-power price fetches through the DeviationBoundedOracle; the liquidation path is unchanged and continues to call the Resilient Oracle directly. Non-whitelisted markets always use spot.

### Effect on Existing Positions

Three consequences follow directly from the design and are worth stating explicitly:

1. **Liquidations are unaffected.** Eligibility and seize-amount calculations continue to use the Resilient Oracle's spot price exactly as today — no code path change, and liquidator incentives stay identical.
2. **Existing positions are not retroactively endangered.** Because liquidation eligibility is evaluated at spot, switching an asset into Protection Mode does not, by itself, push any user closer to liquidation. Bounded prices apply only to the borrow-power check, so an active position keeps its existing health factor at spot. What Protection Mode restricts is **new borrow capacity** — additional borrows and collateral withdrawals are evaluated against the conservative bounded prices, reducing the headroom for new actions until protection clears.
3. **Keeper failure or misbehavior cannot threaten existing positions either.** The keeper's on-chain write constraints are limited to `newMin ≤ spot ∧ newMin ≤ maxPrice` and `newMax ≥ spot ∧ newMax ≥ minPrice` — they prevent inverted windows but do not stop a keeper from pushing the window toward an extreme. So in principle a faulty or adversarial keeper could push the window toward an extreme, either triggering Protection Mode unnecessarily or keeping it active longer than warranted. In either case the worst-case effect is still confined to the borrow-power path — it can over-tighten the bounded prices and **block new borrows or collateral withdrawals**, but it cannot trigger a liquidation, change a seize amount, or otherwise alter an open position. Keeper risk is bounded to denial of new capacity, never harm to existing exposure.

### Roles

* **DeviationBoundedOracle** — stores the per-asset window and protection state, performs the trigger check on every read, returns the bounded or spot price as appropriate. Wraps the Resilient Oracle, never replaces it.
* **Resilient Oracle** — the canonical spot-price source, untouched by Protection Mode and read by the DeviationBoundedOracle.
* **Keeper** — off-chain service that maintains the true rolling window, pushes `minPrice` / `maxPrice` corrections, and submits exit transactions when conditions are met.
* **Off-chain monitor** — observes protection activations and confirms price normalization before any keeper-driven exit.
* **Governance** — whitelists assets, configures thresholds and cooldowns, and acts as a fallback for disabling protection if the keeper path is unavailable.

### Frontend Indication

When Protection Mode is active for a market, the Venus interface surfaces a protection icon next to the affected asset in the dashboard, market list, and asset pages.

### Further Reading

* [DeviationBoundedOracle contract reference](/technical-reference/reference-oracle/deviation-bounded-oracle)
* [Resilient Price Oracle](/risk/resilient-price-oracle) — the spot-price source Protection Mode wraps
* [Repository](https://github.com/VenusProtocol/oracle)


# Interest Rate Model

{% hint style="info" %}
**This is a model-selection overview, not a per-market configuration registry.** Each market points to a deployment-specific interest-rate-model contract that governance can replace. Resolve the model address, implementation family, parameters, and clock at the target chain and block before using a formula.
{% endhint %}

An interest-rate model converts market utilization into borrow and supply rates. Venus deployments include WhitePaper, Jump Rate, and Two Kinks model families. That list describes model families found in current and legacy deployments; it does not mean that every family is active on every chain or that those are the only possible future implementations.

The vToken's `interestRateModel()` getter is the source of truth for which model address a market used at a particular block. Governance can replace that address, and some model parameters can also be updated without replacing the model. Static documentation must not be used as a current market-to-model registry.

## Clock and rate units

Rates are `1e18`-scaled mantissas per accrual slot. The meaning of a slot depends on the deployed architecture:

* BNB Chain mainnet Core vTokens use block-based accrual and expose per-block rates; and
* modern TimeManager-based vTokens and interest-rate models can be configured as block-based or time-based, where a time-based slot is one second.

Modern contracts retain ABI names such as `borrowRatePerBlock()` for compatibility even when the implementation describes the value as a per-slot rate. Read `isTimeBased()` and `blocksOrSecondsPerYear()` where the implementation exposes them. For a Core model that does not implement TimeManager, verify its deployed `blocksPerYear` assumption. Never infer the annualization unit from the function name alone.

## Utilization depends on the engine

Let:

* `C` be market cash;
* `B` be active borrows;
* `R` be reserves; and
* `D` be recorded bad debt.

BNB Chain mainnet Core Jump Rate and WhitePaper models use:

$$
u = \frac{B}{C + B - R}
$$

The Core Two Kinks implementation caps that ratio at one:

$$
u\_{twoKinks} = \min\left(1, \frac{B}{C + B - R}\right)
$$

Modern isolated-engine models include bad debt in borrow utilization:

$$
u\_{borrow} = \min\left(1, \frac{B + D}{C + B + D - R}\right)
$$

Because bad debt does not accrue interest or produce supplier income, the corresponding supply utilization is:

$$
u\_{supply} = \frac{B}{C + B + D - R}
$$

For those models, if `r_b` is the borrow rate per slot and `f` is the reserve factor:

$$
r\_s = r\_b \times u\_{supply} \times (1 - f)
$$

Do not add `badDebt` to a Core formula that does not accept it, or omit it from a modern model that does. Inspect the exact ABI and source used by the target vToken.

## WhitePaper model

The WhitePaper model has one linear borrow-rate slope. With per-slot base rate `b` and multiplier `m`:

$$
r\_b(u) = b + m \times u
$$

The model constructor derives its per-slot values from annualized inputs and its configured slots-per-year assumption. An old WhitePaper deployment can remain relevant to an unlisted market with positions, so the family name alone is not evidence that a market can be ignored or archived.

## Jump Rate model

The Jump Rate model uses one slope below a utilization kink and a steeper, separately configured slope above it. With kink `k`, normal multiplier `m`, and jump multiplier `j`:

$$
r\_b(u) =
\begin{cases}
b + m \times u, & u \leq k \\
b + m \times k + j \times (u-k), & u > k
\end{cases}
$$

The names `baseRatePerBlock`, `multiplierPerBlock`, and `jumpMultiplierPerBlock` are retained in some modern contracts even when TimeManager makes them per-second values. Treat the deployed clock configuration, rather than the storage-variable name, as authoritative.

## Two Kinks model

The Two Kinks family divides utilization into three segments around `kink1` and `kink2`. It supports a separately configured slope in each segment, including an increasing or decreasing middle segment, and then a final jump slope above the second kink.

Do not approximate a Two Kinks deployment as a one-kink Jump Rate model. Read both kink points, all slope and base components, the time basis, and the exact implementation. Existing general-design articles are not a substitute for the deployed model source and state.

## Integration checklist

For a rate or APY calculation at a historical or current block, record:

* chain, pool, Comptroller, and vToken address;
* the vToken implementation and `interestRateModel()` address at that block;
* the model implementation family and parameter values;
* whether the accrual slot is a block or a second, plus slots per year;
* cash, active borrows, reserves, bad debt where applicable, and reserve factor; and
* whether the displayed result adds rewards or other incentives to the base supply or borrow rate.

The [deployed markets](/deployed-contracts/markets) page helps locate markets but is not a complete historical model/version map. For mantissa conversion, checkpoint behavior, and APR/APY display math, see [Protocol math](/guides/protocol-math).

Current source boundaries used for the architecture distinctions on this page are [`venus-protocol@v10.3.0`](https://github.com/VenusProtocol/venus-protocol/tree/46afc66b1dbd61a707d0a3492b3ec21bf90fc17a/contracts/InterestRateModels) and [`isolated-pools@943e7db`](https://github.com/VenusProtocol/isolated-pools/tree/943e7db1855c8ab4a09104f1d09e2b2db0506b95/contracts).


# Risk Fund and Shortfall Handling

Protocol income, risk-fund custody, and bad-debt recovery are separate processes. Risk-fund assets are protocol-controlled funds allocated by governance; they are not a per-user insurance policy, and their existence does not guarantee that every loss or bad-debt balance will be repaid.

## Current income path

Markets send withdrawn interest reserves and liquidation-related revenue to `ProtocolShareReserve` (PSR). PSR records income by Comptroller, asset, and one of two schemas:

* `PROTOCOL_RESERVES` for spread income; and
* `ADDITIONAL_REVENUE` for liquidation and other income.

PSR then distributes each schema according to its on-chain `distributionTargets`. Those targets and percentages are governance-controlled configuration, not fixed 50/50 constants. `releaseFunds(comptroller, assets)` is permissionless, but callers cannot choose the recipients or percentages.

The downstream path is chain- and configuration-specific. On BNB Chain mainnet, [VIP-620](https://app.venus.io/#/governance/proposal/620?chainId=56) and [VIP-621](https://app.venus.io/#/governance/proposal/621?chainId=56) replaced the legacy converter network with `TokenBuyback` instances. The risk-fund route is:

```
markets → ProtocolShareReserve → RiskFundBuyback → RiskFundV2
```

`RiskFundBuyback` receives the risk-fund share of underlying assets, and an ACM-authorized finance process converts them through allowlisted routers. Its base asset is USDT, which it forwards to `RiskFundV2`. See [TokenBuyback](/whats-new/token-converter) for the conversion controls and migration details.

| BNB Chain mainnet component                                                                      | Current role                                                                                                       |
| ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ |
| [`ProtocolShareReserve`](https://bscscan.com/address/0xCa01D5A9A248a830E9D93231e791B1afFed7c446) | Active income accounting and distribution proxy.                                                                   |
| [`RiskFundBuyback`](https://bscscan.com/address/0x0c71EFabD00329E839745ef23aB946d3ed24A805)      | Active USDT buyback route whose destination is RiskFundV2.                                                         |
| [`RiskFundV2`](https://bscscan.com/address/0xdF31a28D68A2AB381D42b380649Ead7ae2A76E42)           | Active protocol-fund custody proxy. It holds raw token balances and no longer maintains a per-pool reserve ledger. |
| [`Shortfall`](https://bscscan.com/address/0xf37530A8a810Fcb501AA0Ecd0B0699388F0F2209)            | Deployed legacy auction and recovery proxy. New auction starts and restarts are paused on BNB Chain mainnet.       |

The current BNB Chain mainnet `RiskFundV2` implementation removed `poolAssetsFunds`, `updatePoolState`, and `sweepTokenFromPool`. Its compatibility getter `getPoolsBaseAssetReserves(comptroller)` always returns zero; it must not be used as a current per-pool reserve balance. Governance can sweep raw ERC-20 balances. RiskFundV2 also retains `transferReserveForAuction`, which only the configured Shortfall address can call; it draws from the global raw USDT balance rather than a pool-attributed reserve. That compatibility surface does not make the paused auction system a current bad-debt workflow.

## How isolated-pool bad debt is recorded

An undercollateralized account does not become market bad debt merely because a shortfall is observed. In the isolated-pool lending engine, `healAccount(user)` is limited to an insolvent account whose total collateral does not exceed `minLiquidatableCollateral` and whose collateral-to-incentivized-borrow ratio does not exceed 100%. The call seizes all of the account's collateral vTokens. The caller receives the liquidator share, while each market's protocol seize share is redeemed to underlying and sent to PSR.

For each borrowed market, the caller then attempts the proportional repayment calculated from the account-wide collateral, borrows, and liquidation incentive. The vToken uses the amount actually transferred in: it removes the original borrow from the account and active `totalBorrows`, records `borrowBalance - actualRepayAmount` as market `badDebt`, and leaves the borrower's principal at zero. The written-off amount therefore no longer accrues as that borrower's debt.

This accounting can remain relevant to legacy isolated markets even though the standalone Isolated Pools product is deprecated. It does not mean the recorded market bad debt will be covered automatically.

## Legacy Shortfall auctions

Shortfall was designed to exchange a pool-attributed risk-fund reserve for bidder repayments of isolated-market bad debt. That design depended on the per-pool ledger removed from RiskFundV2.

As part of the joint VIP-620/VIP-621 migration, the BNB Chain mainnet Shortfall auctions were paused and the old converter path was retired. At BNB Chain mainnet block `118349571` (August 27, 2026, 08:12:46 UTC):

* `auctionsPaused()` returned `true`;
* all eight pools returned by PoolRegistry had auction status `NOT_STARTED`;
* `getPoolsBaseAssetReserves(comptroller)` returned zero for all eight pools; and
* the RiskFundV2 proxy held one global raw USDT balance rather than eight attributed balances.

The same snapshot found nonzero `badDebt()` in nine isolated vTokens. No active auction therefore does not mean no legacy market bad debt.

Do not approve tokens to Shortfall or call `startAuction`, `restartAuction`, or `placeBid` based on the old workflow. The pause is governance-controlled, however, and historical transfer debt can still require the `claimTokenDebt` recovery function. See [Shortfall and auctions](/technical-reference/reference-technical-articles/shortfall-and-auctions) for the dated historical design and recovery boundaries.

## Current bad-debt response

There is no universal on-chain function that automatically applies RiskFundV2 to every Venus bad-debt balance. Core Pool and isolated-pool accounting differ, and any use of protocol funds for recovery requires an explicit, scope-specific governance or operational action. Before describing a debt as covered, verify the relevant market state, asset balances, executed governance proposal, and on-chain repayment or recovery transaction.

For current integration work, verify all of the following rather than relying on historical architecture diagrams:

* PSR `distributionTargets` and percentages for the relevant chain and schema;
* the identity and configuration of each downstream income destination;
* RiskFundV2 raw token balances, owner, base asset, and configured Shortfall address;
* Shortfall pause and per-pool auction state, plus any `tokenDebt` owed to a historical bidder; and
* each affected market's `badDebt` balance and the governance actions that address it.

Contract addresses are listed under [Deployed Contracts → Funds](/deployed-contracts/funds). The current source boundaries used by this page are [`protocol-reserve@eaed4e3`](https://github.com/VenusProtocol/protocol-reserve/tree/eaed4e323edd44bf87b5be1e56522fc772cb5990) and [`isolated-pools@943e7db`](https://github.com/VenusProtocol/isolated-pools/tree/943e7db1855c8ab4a09104f1d09e2b2db0506b95).


# Risk Management

### Introduction

Venus Protocol is deeply committed to ensuring the highest level of security and risk management for our users. This section of the documentation outlines our comprehensive approach to risk management, emphasizing our continuous monitoring practices, partnerships for enhanced security, real-time alert systems, and ongoing risk assessments.

### Multiple Audits Before Deployment

* Prior to deployment, Venus Protocol undergoes extensive audits conducted by industry-leading security firms. The results of these audits are publicly accessible and have been fully implemented, ensuring a robust and secure framework.
* Access our detailed audit reports here: [Security and Audits.](https://docs-v4.venus.io/links/security-and-audits)

### Continuous On-Chain Monitoring

* Venus Protocol has partnered with Chaos Labs, a leader in blockchain security and monitoring. This partnership equips us with sophisticated tools for continuous on-chain monitoring, significantly enhancing our risk control and mitigation strategies.
* Explore our monitoring dashboard for real-time insights: [Chaos Labs Risk Overview](https://community.chaoslabs.xyz/venus/risk/overview).

### Real-Time Alert System

* To further enhance our risk management, we have implemented a real-time alert system via Telegram. This system provides immediate notifications for critical events such as market high utilization, significant whale movements, and borrow/supply cap utilization.
* Join our alert channel to stay updated: [Chaos Labs Telegram Alerts](https://community.chaoslabs.xyz/venus/risk/alerts).

### Dynamic Risk Management

* In collaboration with Chaos Labs, we continuously analyze market conditions and adjust risk parameters across all pools regularly or anytime there is need for adjustment to mitigate risks due to market conditions. These adjustments are based on comprehensive data analysis and are integral to maintaining the stability and security of the protocol.

### Frontend

Several security measures have been implemented to mitigate the security risks associated with the frontend app deployed at <https://app.venus.io>.

* The Venus Protocol UI is deployed on AWS infrastructure. Additionally, Cloudflare is employed to distribute the web app. The relevant security services in both AWS and Cloudflare are properly configured to detect and mitigate security risks.
* SPF and DKIM are configured to protect every email sent from a @venus.io account.
* Web certificates, generated by AWS, are set to auto-renew.
* Venus nameservers are hosted on Cloudflare, serving as our DNS provider.
* The Venus Protocol UI utilizes [Chainalysis](https://www.chainalysis.com) to assess the risk profile associated with web3 addresses. If the risk is deemed high, the connection is disallowed, preventing any interactions with contracts from the UI involving that risky address.
* Only privileged users have the authority to deploy the open-source code, accessible on Github (<https://github.com/VenusProtocol/venus-protocol-interface>), to the domain app.venus.io. Additionally, only privileged users are authorized to merge pull requests in the Github repository.

### Conclusion

The risk management practices at Venus Protocol are designed to provide a secure and stable environment for our users. Our commitment to continuous monitoring, real-time alerts, and dynamic risk adjustments ensure that we are always at the forefront of blockchain security. We encourage our community to explore the provided resources for a deeper understanding of our risk management protocols.


# Risk Oracle & Risk Stewards

## Overview

Venus Protocol uses a **Risk Oracle** and a set of **Risk Stewards** to keep risk parameters aligned with real-time market conditions while preserving decentralized governance. Previously, every risk-parameter change went through the same full VIP process, no matter how routine. The framework now lets governance-approved risk managers apply pre-authorized adjustments through a secure, automated on-chain pipeline, but no risk provider is ever given open-ended authority: each change is first proposed openly in the Venus community, and can only move a parameter within limits the DAO has set. Routine moves within a governance-set **safe delta** apply automatically within minutes; anything larger is held for a cooldown period and released only by a whitelisted Venus executor (who can also reject it), while the most sensitive changes still require a full VIP. Governance sets every limit, approves who may publish and who may execute, and can pause the system at any time.

The framework currently automates four parameter types: **supply caps**, **borrow caps**, **collateral factors** (with their liquidation thresholds), and **interest rate models**, across all supported Venus chains. BNB Chain acts as the source chain, and updates targeting other chains are propagated via LayerZero. Every recommendation and execution is recorded on the Risk Oracle, so anyone can inspect a proposed change on-chain, before and after it takes effect.

## How the System Fits Together

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-ef3ea8a8b3ccc7d93252df1b5547d9d14f5f0cf5%2Frisk-stewards-architecture.svg?alt=media" alt="Risk Stewards architecture: whitelisted providers publish to the Venus Risk Oracle on BNB Chain; the RiskStewardReceiver applies updates locally or forwards them over LayerZero to a Destination Steward Receiver on other chains; governance configures and gates everything"><figcaption><p>Whitelisted providers publish to the Risk Oracle on BNB Chain; the Risk Steward Receiver applies updates locally or bridges them to other chains. Governance configures and gates the whole pipeline.</p></figcaption></figure>

The pipeline has four parts:

* **Risk Oracle** (BNB Chain): where whitelisted providers publish recommendations.
* **Risk Steward Receiver** (BNB Chain, the source chain): validates each recommendation, decides how it should be applied, executes local changes, and forwards cross-chain ones via LayerZero.
* **Destination Steward Receiver** (every other supported chain): receives bridged updates, holds them for a short delay, and lets a Venus executor apply them.
* **Risk Stewards** (Market Caps, Collateral Factors, IRM): deployed on each chain to perform the actual change in the relevant Comptroller or vToken.

The contract-level detail lives in the [Risk Stewards technical article](/technical-reference/reference-technical-articles/risk-stewards).

## Risk Oracle

The **Risk Oracle** is Venus Protocol's own on-chain contract and the single source of truth for risk-parameter recommendations. It is owned and controlled by Venus governance, not by any third party.

Whitelisted **risk parameter providers** publish recommendations to it. A provider can be an external risk manager such as Chaos Labs or Allez Labs, or Venus's own risk team. Providers are added or removed only through governance (via the `AccessControlManager`), so the set of authorized senders is fully under DAO control. Each recommendation records the target market, the parameter type, the new value, the destination chain, and a reference ID (typically a link to a Venus community post explaining the change).

## Risk Stewards

A **Risk Steward** is an on-chain contract that applies one family of risk-parameter changes on behalf of the protocol, strictly within bounds set by governance:

| Risk Steward                        | Parameters it can change                      |
| ----------------------------------- | --------------------------------------------- |
| **Market Caps Risk Steward**        | Supply caps, borrow caps                      |
| **Collateral Factors Risk Steward** | Collateral factors and liquidation thresholds |
| **IRM Risk Steward**                | Interest rate model contract for a market     |

Each steward applies only validated updates routed to it, enforces a **safe delta** (changes within a governance-set percentage of the current value can execute immediately; larger ones require a timelock and executor approval), and cannot touch any parameter outside its mandate.

## How Updates Are Applied

Anyone can trigger processing of a published recommendation; the entry point is permissionless. Before anything happens, the recommendation is validated against governance's configuration: the update type must be active, it must be the latest for that market and type, it must not have expired, and a debounce window since the last change must have passed. From there it follows one of two paths.

### On BNB Chain (local updates)

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-29ce6c37b679d02e666eadf009d264e8b9658873%2Frisk-stewards-local-flow.svg?alt=media" alt="Local update flow: a provider publishes to the Risk Oracle, anyone triggers processing on the Risk Steward Receiver, and the change either applies immediately when within the safe delta or is registered behind a timelock for a whitelisted executor to apply"><figcaption><p>A BNB Chain update applies immediately when it is within the safe delta; otherwise it waits through a timelock and is applied (or rejected) by a whitelisted executor.</p></figcaption></figure>

If the change is within the steward's safe delta, it is applied immediately. If it exceeds the safe delta it is registered behind a timelock; after the delay a whitelisted Venus executor applies it, or rejects it. Interest rate model updates and eMode collateral-factor updates always take the timelocked path, regardless of size.

### On Other Chains (remote updates)

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-a0d1ad06844039891fcc62aadac8d59f7759b51a%2Frisk-stewards-remote-flow.svg?alt=media" alt="Remote update flow: a provider publishes on BNB Chain, the Risk Steward Receiver forwards the update over LayerZero to the Destination Steward Receiver on the target chain, which holds it for a remote delay before a whitelisted executor applies it through the local steward"><figcaption><p>An update targeting another chain is forwarded over LayerZero, held for a remote delay on the destination chain, then applied by a whitelisted executor there.</p></figcaption></figure>

When a recommendation targets a market on another chain, the Risk Steward Receiver forwards it over LayerZero to that chain's Destination Steward Receiver, where it is held for a remote delay and then applied by a whitelisted executor on the destination chain. Cross-chain updates therefore always pass through a delay and an executor.

## Roles and Permissions

| Role                                   | Can do                                                                                                                            | Set by                     |
| -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
| **Risk parameter providers**           | Publish recommendations to the Risk Oracle; trigger processing of their own updates                                               | Governance (ACM whitelist) |
| **Anyone**                             | Trigger processing of a published recommendation                                                                                  | Permissionless             |
| **Whitelisted executors (Venus team)** | Apply timelocked and cross-chain updates after their delay; reject updates                                                        | Governance (ACM whitelist) |
| **Governance / ACM**                   | Configure debounce, timelock, safe-delta bounds, and supported update types; add/remove providers and executors; pause processing | DAO (VIP)                  |

## Safeguards and Governance Control

This system does **not** bypass governance; it operates entirely within constraints the DAO approves:

* **Bounded changes**: stewards can only move the four supported parameters, and only within the safe-delta / timelock rules set by governance.
* **Frequency limits**: a debounce period prevents rapid repeated changes to the same market and parameter.
* **Timelock + veto**: larger changes, IRM changes, and eMode collateral-factor changes wait through a timelock during which a Venus executor can reject them.
* **Expiration & replay protection**: updates expire if not executed in time and can never be processed twice.
* **Pause**: governance can pause update processing at any time.
* **Reserved for governance**: high-impact parameters outside the four supported types still require a full VIP.

The framework was enabled on BNB Chain in [VIP-592](https://app.venus.io/#/governance/proposal/592?chainId=56), which also onboarded Allez Labs as the first external risk provider. At launch, governance set a 3-day debounce, a 6-hour timelock, a 50% safe delta for market caps, and a 10% safe delta for collateral factors. These values are set by governance and can be adjusted through it.

## What's Next

The framework starts with supply caps, borrow caps, collateral factors, and interest rate models across supported chains. Additional parameters and refinements may be added over time, always subject to DAO approval and governance-defined constraints.

## Learn More

* [Risk Stewards technical article](/technical-reference/reference-technical-articles/risk-stewards)
* [Proposed Risk Stewards Framework for More Efficient Risk Management](https://community.venus.io/t/proposed-risk-stewards-framework-for-more-efficient-risk-management/5606)
* [VIP-592: Risk Stewards Framework Implementation](https://app.venus.io/#/governance/proposal/592?chainId=56)


# XVS

XVS is the governance token of Venus Protocol. Holders can deposit XVS in the XVS Vault, delegate the associated voting power, and participate in Venus Improvement Proposals (VIPs).

Staking alone does not make voting power active: the vault position must be delegated to the holder's address or another delegate. A proposal uses the delegate's voting-power snapshot at its start block. Delegation does not transfer ownership of the staked XVS.

See [Delegating and Voting](/guides/governance/delegating) for the participation workflow and [Venus Improvement Proposals](/governance/decentralization) for current governance rules.

## Vault rewards

The XVS Vault can distribute governance-funded base rewards and XVS purchased with protocol revenue. Reward funding and emission speed are configurable and may change through VIPs. Staking XVS therefore does not guarantee a particular yield. See [Tokenomics](/governance/tokenomics) for the current policy snapshot and instructions for checking the live reward speed.

## Venus Prime

An XVS Vault position also contributes to [Venus Prime](/whats-new/prime-yield) eligibility. Prime uses a time-weighted leaderboard that considers both the amount and age of eligible XVS stakes. At each configured issuance epoch, governance can issue a limited number of non-transferable Prime tokens to qualifying accounts.

Leaderboard rank does not guarantee issuance, and the epoch schedule, token limit, minimum stake, and other conditions are governance-configurable. Check the current Prime interface and deployed configuration before relying on a particular eligibility rule.


# VAI

### Overview

VAI is the primary stablecoin of the Venus Protocol. It is carefully designed and integrated with a series of stability mechanisms to maintain its peg to 1 USD.

### Stability Fee and Optimised Minting

VAI sustains its value via a stability fee system. This fee is a charge that users incur when they repay their minted VAI. Calculated on a block-by-block basis, this fee gets added to the user's minted VAI balance. This fee system encourages users to either mint or burn VAI in response to its price fluctuations, thereby contributing to its stability.

The stability fee is determined through the following formula:

$$
\text{{stabilityFee}}(%) = \text{{baseRate}}(%) + \max(0, (1 - \text{{currentPriceOfVAI}})) \times \text{{floatingRate}}(%)
$$

The `baseRate` is a fixed rate that users must always pay, while `max(0, (1 - currentPriceOfVAI)) * floatingRate` is a variable rate that users pay based on outstanding VAI. This incentivizes users to burn or mint VAI according to its price. The variable rate will always be greater or equal to zero, meaning the minimum stability fee will be the base rate.

Revenue generated from the stability fee serves as a financial cushion during extreme market conditions, such as bad debt scenarios.


# VAIController

## VAI Comptroller

This is the implementation contract for the VAIUnitroller proxy

## Solidity API

#### INITIAL\_VAI\_MINT\_INDEX

Initial index used in interest computations

```solidity
uint256 INITIAL_VAI_MINT_INDEX
```

***

#### CORE\_POOL\_ID

poolId for core Pool

```solidity
uint96 CORE_POOL_ID
```

***

#### mintVAI

The mintVAI function mints and transfers VAI from the protocol to the user, and adds a borrow balance. The amount minted must be less than the user's Account Liquidity and the mint vai limit.

```solidity
function mintVAI(uint256 mintVAIAmount) external returns (uint256)
```

**Parameters**

| Name          | Type    | Description                         |
| ------------- | ------- | ----------------------------------- |
| mintVAIAmount | uint256 | The amount of the VAI to be minted. |

**Return Values**

| Name | Type    | Description                           |
| ---- | ------- | ------------------------------------- |
| \[0] | uint256 | 0 on success, otherwise an error code |

***

#### repayVAI

The repay function transfers VAI interest into the protocol and burns the rest, reducing the borrower's borrow balance. Before repaying VAI, users must first approve VAIController to access their VAI balance.

```solidity
function repayVAI(uint256 amount) external returns (uint256, uint256)
```

**Parameters**

| Name   | Type    | Description                     |
| ------ | ------- | ------------------------------- |
| amount | uint256 | The amount of VAI to be repaid. |

**Return Values**

| Name | Type    | Description                                                        |
| ---- | ------- | ------------------------------------------------------------------ |
| \[0] | uint256 | Error code (0=success, otherwise a failure, see ErrorReporter.sol) |
| \[1] | uint256 | Actual repayment amount                                            |

***

#### repayVAIBehalf

The repay on behalf function transfers VAI interest into the protocol and burns the rest, reducing the borrower's borrow balance. Borrowed VAIs are repaid by another user (possibly the borrower). Before repaying VAI, the payer must first approve VAIController to access their VAI balance.

```solidity
function repayVAIBehalf(address borrower, uint256 amount) external returns (uint256, uint256)
```

**Parameters**

| Name     | Type    | Description                        |
| -------- | ------- | ---------------------------------- |
| borrower | address | The account to repay the debt for. |
| amount   | uint256 | The amount of VAI to be repaid.    |

**Return Values**

| Name | Type    | Description                                                        |
| ---- | ------- | ------------------------------------------------------------------ |
| \[0] | uint256 | Error code (0=success, otherwise a failure, see ErrorReporter.sol) |
| \[1] | uint256 | Actual repayment amount                                            |

***

#### liquidateVAI

The sender liquidates the vai minters collateral. The collateral seized is transferred to the liquidator.

```solidity
function liquidateVAI(address borrower, uint256 repayAmount, contract VTokenInterface vTokenCollateral) external returns (uint256, uint256)
```

**Parameters**

| Name             | Type                     | Description                                               |
| ---------------- | ------------------------ | --------------------------------------------------------- |
| borrower         | address                  | The borrower of vai to be liquidated                      |
| repayAmount      | uint256                  | The amount of the underlying borrowed asset to repay      |
| vTokenCollateral | contract VTokenInterface | The market in which to seize collateral from the borrower |

**Return Values**

| Name | Type    | Description                                                        |
| ---- | ------- | ------------------------------------------------------------------ |
| \[0] | uint256 | Error code (0=success, otherwise a failure, see ErrorReporter.sol) |
| \[1] | uint256 | Actual repayment amount                                            |

***

#### \_setComptroller

Sets a new comptroller

```solidity
function _setComptroller(contract ComptrollerInterface comptroller_) external returns (uint256)
```

**Return Values**

| Name | Type    | Description                                                                |
| ---- | ------- | -------------------------------------------------------------------------- |
| \[0] | uint256 | uint256 0=success, otherwise a failure (see ErrorReporter.sol for details) |

***

#### setPrimeToken

Set the prime token contract address

```solidity
function setPrimeToken(address prime_) external
```

**Parameters**

| Name    | Type    | Description                                 |
| ------- | ------- | ------------------------------------------- |
| prime\_ | address | The new address of the prime token contract |

***

#### setVAIToken

Set the VAI token contract address

```solidity
function setVAIToken(address vai_) external
```

**Parameters**

| Name  | Type    | Description                               |
| ----- | ------- | ----------------------------------------- |
| vai\_ | address | The new address of the VAI token contract |

***

#### toggleOnlyPrimeHolderMint

Toggle mint only for prime holder

```solidity
function toggleOnlyPrimeHolderMint() external returns (uint256)
```

**Return Values**

| Name | Type    | Description                                                                |
| ---- | ------- | -------------------------------------------------------------------------- |
| \[0] | uint256 | uint256 0=success, otherwise a failure (see ErrorReporter.sol for details) |

***

```solidity
struct AccountAmountLocalVars {
  uint256 oErr;
  enum CarefulMath.MathError mErr;
  uint256 sumSupply;
  uint256 marketSupply;
  uint256 sumBorrowPlusEffects;
  uint256 vTokenBalance;
  uint256 borrowBalance;
  uint256 exchangeRateMantissa;
  uint256 oraclePriceMantissa;
  struct ExponentialNoError.Exp exchangeRate;
  struct ExponentialNoError.Exp oraclePrice;
  struct ExponentialNoError.Exp tokensToDenom;
}
```

#### getMintableVAI

Function that returns the amount of VAI a user can mint based on their account liquidy and the VAI mint rate If mintEnabledOnlyForPrimeHolder is true, only Prime holders are able to mint VAI

```solidity
function getMintableVAI(address minter) public view returns (uint256, uint256)
```

**Parameters**

| Name   | Type    | Description                       |
| ------ | ------- | --------------------------------- |
| minter | address | The account to check mintable VAI |

**Return Values**

| Name | Type    | Description                                                                    |
| ---- | ------- | ------------------------------------------------------------------------------ |
| \[0] | uint256 | Error code (0=success, otherwise a failure, see ErrorReporter.sol for details) |
| \[1] | uint256 | Mintable amount (with 18 decimals)                                             |

***

#### \_setTreasuryData

Update treasury data

```solidity
function _setTreasuryData(address newTreasuryGuardian, address newTreasuryAddress, uint256 newTreasuryPercent) external returns (uint256)
```

**Parameters**

| Name                | Type    | Description                                                     |
| ------------------- | ------- | --------------------------------------------------------------- |
| newTreasuryGuardian | address | New Treasury Guardian address                                   |
| newTreasuryAddress  | address | New Treasury Address                                            |
| newTreasuryPercent  | uint256 | New fee percentage for minting VAI that is sent to the treasury |

***

#### getVAIRepayRate

Gets yearly VAI interest rate based on the VAI price

```solidity
function getVAIRepayRate() public view returns (uint256)
```

**Return Values**

| Name | Type    | Description                      |
| ---- | ------- | -------------------------------- |
| \[0] | uint256 | uint256 Yearly VAI interest rate |

***

#### getVAIRepayRatePerBlock

Get interest rate per block

```solidity
function getVAIRepayRatePerBlock() public view returns (uint256)
```

**Return Values**

| Name | Type    | Description                    |
| ---- | ------- | ------------------------------ |
| \[0] | uint256 | uint256 Interest rate per bock |

***

#### getVAIMinterInterestIndex

Get the last updated interest index for a VAI Minter

```solidity
function getVAIMinterInterestIndex(address minter) public view returns (uint256)
```

**Parameters**

| Name   | Type    | Description           |
| ------ | ------- | --------------------- |
| minter | address | Address of VAI minter |

**Return Values**

| Name | Type    | Description                                          |
| ---- | ------- | ---------------------------------------------------- |
| \[0] | uint256 | uint256 Returns the interest rate index for a minter |

***

#### getVAIRepayAmount

Get the current total VAI a user needs to repay

```solidity
function getVAIRepayAmount(address account) public view returns (uint256)
```

**Parameters**

| Name    | Type    | Description                     |
| ------- | ------- | ------------------------------- |
| account | address | The address of the VAI borrower |

**Return Values**

| Name | Type    | Description                                               |
| ---- | ------- | --------------------------------------------------------- |
| \[0] | uint256 | (uint256) The total amount of VAI the user needs to repay |

***

#### getVAICalculateRepayAmount

Calculate how much VAI the user needs to repay

```solidity
function getVAICalculateRepayAmount(address borrower, uint256 repayAmount) public view returns (uint256, uint256, uint256)
```

**Parameters**

| Name        | Type    | Description                      |
| ----------- | ------- | -------------------------------- |
| borrower    | address | The address of the VAI borrower  |
| repayAmount | uint256 | The amount of VAI being returned |

**Return Values**

| Name | Type    | Description                                             |
| ---- | ------- | ------------------------------------------------------- |
| \[0] | uint256 | Amount of VAI to be burned                              |
| \[1] | uint256 | Amount of VAI the user needs to pay in current interest |
| \[2] | uint256 | Amount of VAI the user needs to pay in past interest    |

***

#### accrueVAIInterest

Accrue interest on outstanding minted VAI

```solidity
function accrueVAIInterest() public
```

***

#### setAccessControl

Sets the address of the access control of this contract

```solidity
function setAccessControl(address newAccessControlAddress) external
```

**Parameters**

| Name                    | Type    | Description                        |
| ----------------------- | ------- | ---------------------------------- |
| newAccessControlAddress | address | New address for the access control |

***

#### setBaseRate

Set VAI borrow base rate

```solidity
function setBaseRate(uint256 newBaseRateMantissa) external
```

**Parameters**

| Name                | Type    | Description                          |
| ------------------- | ------- | ------------------------------------ |
| newBaseRateMantissa | uint256 | the base rate multiplied by 10\*\*18 |

***

#### setFloatRate

Set VAI borrow float rate

```solidity
function setFloatRate(uint256 newFloatRateMantissa) external
```

**Parameters**

| Name                 | Type    | Description                               |
| -------------------- | ------- | ----------------------------------------- |
| newFloatRateMantissa | uint256 | the VAI float rate multiplied by 10\*\*18 |

***

#### setReceiver

Set VAI stability fee receiver address

```solidity
function setReceiver(address newReceiver) external
```

**Parameters**

| Name        | Type    | Description                         |
| ----------- | ------- | ----------------------------------- |
| newReceiver | address | the address of the VAI fee receiver |

***

#### setMintCap

Set VAI mint cap

```solidity
function setMintCap(uint256 _mintCap) external
```

**Parameters**

| Name      | Type    | Description                          |
| --------- | ------- | ------------------------------------ |
| \_mintCap | uint256 | the amount of VAI that can be minted |

***

#### getVAIAddress

Return the address of the VAI token

```solidity
function getVAIAddress() public view virtual returns (address)
```

**Return Values**

| Name | Type    | Description        |
| ---- | ------- | ------------------ |
| \[0] | address | The address of VAI |

***


# VAIUnitroller

## VAI Unitroller

This is the proxy contract for the VAIComptroller

## Solidity API

#### \_setPendingImplementation

* Admin Functions \*\*

```solidity
function _setPendingImplementation(address newPendingImplementation) public returns (uint256)
```

***

#### \_acceptImplementation

Accepts new implementation of comptroller. msg.sender must be pendingImplementation

```solidity
function _acceptImplementation() public returns (uint256)
```

**Return Values**

| Name | Type    | Description                                                             |
| ---- | ------- | ----------------------------------------------------------------------- |
| \[0] | uint256 | uint 0=success, otherwise a failure (see ErrorReporter.sol for details) |

***

#### \_setPendingAdmin

Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.

```solidity
function _setPendingAdmin(address newPendingAdmin) public returns (uint256)
```

**Parameters**

| Name            | Type    | Description        |
| --------------- | ------- | ------------------ |
| newPendingAdmin | address | New pending admin. |

**Return Values**

| Name | Type    | Description                                                             |
| ---- | ------- | ----------------------------------------------------------------------- |
| \[0] | uint256 | uint 0=success, otherwise a failure (see ErrorReporter.sol for details) |

***

#### \_acceptAdmin

Accepts transfer of admin rights. msg.sender must be pendingAdmin

```solidity
function _acceptAdmin() public returns (uint256)
```

**Return Values**

| Name | Type    | Description                                                             |
| ---- | ------- | ----------------------------------------------------------------------- |
| \[0] | uint256 | uint 0=success, otherwise a failure (see ErrorReporter.sol for details) |

***


# Venus interface

Let's take a quick look at the Venus interface and the features available in each menu of the navigation bar.

### Dashboard

In the center of the Dashboard interface, you will find the Supply and Borrow markets. The Supply market allows you to lend your cryptocurrency assets and earn interest on them. You can choose which assets to supply and specify the amount you want to lend. On the other hand, the Borrow market allows you to borrow cryptocurrency assets by using your supplied assets as collateral. You can select the assets you want to borrow and specify the amount you need.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-b835ac66f629e2594d7033cc2f604758acaf1b28%2FScreenshot%202023-08-02%20at%209.35.46%20AM.png?alt=media" alt=""><figcaption></figcaption></figure>

### Account

The Account interface provides an overview of your supplied and borrowed assets. Here, you can keep track of your balances and monitor the status of your transactions.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-73cee1bcc5e912be259ba645832ced2f8fd0cacd%2FVenus_account.png?alt=media" alt=""><figcaption></figcaption></figure>

### Core Pool

The Core Pool is accessed from the Markets interface, which lists lending markets for the selected chain and displays metrics such as Supply APY, Borrow APY, and liquidity. On chains where [Isolation Mode](/whats-new/isolated-e-mode) groups are available, they appear as an **Isolation mode** tab within the Core Pool interface.

Isolation Mode is separate from the deprecated standalone Isolated Pools product described below.

### Pools

The standalone Isolated Pools navigation and market screens described in earlier versions of this guide have been removed. New supply and borrow activity in these pools is not available through the Venus interface. If you still have a position or unclaimed rewards in a legacy isolated pool, follow the [exit and rewards guide](/guides/isolated-pools-deprecation).

### Vaults

The Vaults interface allows you to access and manage the vaults associated with Venus Protocol. Vaults are designed to provide users with automated strategies for optimizing their yields and managing their assets more efficiently.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-8efa195e4b3e05621995440ce3cd5d8a242a7011%2FScreenshot%202023-08-02%20at%209.33.54%20AM.png?alt=media" alt=""><figcaption></figcaption></figure>

### Swap

The Swap interface enables you to swap one cryptocurrency for another within the Venus Protocol. You can exchange your assets conveniently and quickly.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-ea98f4963a1875e4b9577c2946e116e587156597%2FScreenshot%202023-08-02%20at%209.38.18%20AM.png?alt=media" alt=""><figcaption></figcaption></figure>

### History

In the History interface, you can review transaction history and track your previous activities on the Protocol.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-3d606cd513f305a67a0efc0efd99ac890fc02fd1%2FScreenshot%202023-08-02%20at%209.38.32%20AM.png?alt=media" alt=""><figcaption></figcaption></figure>

### Governance

The Governance interface provides access to Venus Protocol's governance features. Here, users can participate in voting and contribute to decision-making processes that shape the future of the protocol.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-826edb888670543a5141d7311f112de9a85ac47c%2FScreenshot%202023-08-02%20at%209.38.48%20AM.png?alt=media" alt=""><figcaption></figcaption></figure>

### XVS

The XVS interface displays the current daily reward distribution rate for each of the protocol markets.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-38cd2d1df00458225568f5dbe71b9b987045c098%2FScreenshot%202023-08-02%20at%209.39.02%20AM.png?alt=media" alt=""><figcaption></figcaption></figure>

### VAI

The VAI interface is where you can mint and manage the VAI stablecoin. VAI is created on Venus Protocol and is pegged to the value of one USD.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-c7be7bc93e85bc8ac79b09efba96511b25e849e7%2FScreenshot%202023-08-02%20at%209.39.11%20AM.png?alt=media" alt=""><figcaption></figcaption></figure>


# Supplying and borrowing

In this guide, we will focus on using Venus Protocol to earn interest and borrow assets. If you’re looking for a more technical understanding of what’s happening under the hood, check out [Venus Protocol’s whitepaper](https://github.com/VenusProtocol/venus-protocol-documentation/blob/main/whitepapers/Venus-whitepaper-v4.pdf).

Once you’ve created your Web3 wallet on [MetaMask](https://metamask.io) or any other supported wallet App, open the Venus app (<https://app.venus.io>). The interface will ask you to connect your Web3 wallet. Connecting your wallet to Venus Protocol enables you to authorize transactions, view balances, and perform other essential actions.

After successfully connecting your wallet, you will gain access to all the features of the Venus Protocol interface. In the Dashboard menu you will find all the markets. Clicking one of the markets a new modal will pop out, enabling you to interact with the selected market. Just make sure you are under the "Supply" or "Borrow" tab, depending on the desired action.

## Supply Assets to Earn Interest

1. Connect your Web3 wallet on MetaMask or other supported wallet app to the Venus app (<https://app.venus.io>).
2. Navigate to the "Dashboard" menu and choose the asset you want to supply. For example, if you want to supply TRX, click on the TRX market.
3. Enable the asset. This will prompt a transaction confirmation in your wallet. Remember that a small gas fee applies, so ensure some native tokens (BNB for BNB chain, or ETH for Ethereum, for example) are available in your wallet.
4. Specify the amount you want to supply. The selected assets are transferred directly from your wallet to Venus Protocol, earning interest immediately. This interest will be automatically added to your Supply Balance.

Each market has a **supply cap** — the maximum total amount of that asset the protocol will accept as supply, set by governance to limit risk concentration; once it is reached, no further supply of that asset is possible until the cap is raised.

## Manage your Borrowing Limit

Your borrowing limit on Venus Protocol is a function of the assets you have supplied. The following steps guide you to manage it:

1. Navigate to the "Account" section.
2. Here, you'll find your Borrow Limit on each pool, represented as a percentage of the total value of your supplied assets.
3. To adjust this limit, you can either supply more assets or repay some of your outstanding loans.

## Borrow Assets on Venus Protocol

After supplying assets, you can borrow other assets within your borrowing limit:

1. From the "Dashboard" menu, select the asset you want to borrow.
2. Input the amount you wish to borrow and confirm the transaction.

## Farm $XVS tokens

In addition to earning interest on supplied assets, you can also farm $XVS tokens:

1. In the "Vaults" section, you can stake your XVS or VAI tokens to earn XVS tokens.
2. Click on 'Stake'. You will be prompted to confirm the transaction in your wallet.

## Lending and Borrowing Example

Let's assume you supply 1000 TRX to Venus, and the vTRX/TRX rate is 0.0204. You'd receive approximately 49,019 vTRX in return (1000 / 0.0204). If the vTRX/TRX rate increases to 0.0215 after a year, your 49,019 vTRX would be worth 1021.5 TRX (49019 \* 0.0215), an increase of 21.5 TRX.

Remember that these vTokens represent your collateral in Venus Protocol. It's crucial not to trade or transfer them if you have an active loan, as they're required to maintain your borrowing limit.

## WETH

ETH is the native token for the Ethereum blockchain, used to pay gas fees, for example. WETH is an [ERC-20](https://ethereum.org/developers/docs/standards/tokens/erc-20) token representing ETH. Most Web3 apps (including Venus) are compatible with ERC-20 tokens, so it's necessary to convert ETH to WETH.

The conversion of ETH -> WETH and WETH -> ETH is always available in the WETH token itself, and the exchange rate is always `1:1`:

* If you wrap 1 ETH, you'll receive 1 WETH.
* If you unwrap 1 WETH, you'll receive 1 ETH.

Only the gas fee to execute the wrap/unwrap transactions will need to be paid. Each Ethereum network (including L2s) has its own WETH token (see the full list on [CoinMarketCap](https://coinmarketcap.com/currencies/weth/))).

An easy way to get WETH from ETH is by using Uniswap. For example, on the Ethereum mainnet, the process would be:

1. Go to the [Uniswap page to wrap ETH on Ethereum](https://app.uniswap.org/swap?chain=mainnet\&inputCurrency=ETH\&outputCurrency=0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2).
2. Connect your wallet.
3. Type the amount of ETH you want to wrap.
4. Click on `Wrap`.
5. Review and accept the transaction in your Web3 wallet.
6. Remember to add the WETH token to your Web3 wallet to see your WETH balance.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-0a41a70706966e346245aa44855a05ec43746bcb%2Fwrap_eth_uniswap.png?alt=media" alt="Wrap ETH at Uniswap" width="75%"><figcaption><p>Wrap ETH at Uniswap</p></figcaption></figure>

To unwrap WETH tokens (getting ETH), you can also use [Uniswap](https://app.uniswap.org/swap?chain=mainnet\&inputCurrency=0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2\&outputCurrency=ETH), switching the input and output currencies.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-e5e959aee565fca799fca873a3c88c0837017364%2Funwrap_eth_uniswap.png?alt=media" alt="Unwrap ETH at Uniswap" width="75%"><figcaption><p>Unwrap ETH at Uniswap</p></figcaption></figure>


# Liquidations

This is a step-by-step guide on how to perform liquidations on the Venus Protocol. Liquidations involve seizing collateral from under-collateralized accounts to repay outstanding debts. The instructions are targeted towards developers or bots that aim to automate the liquidation process.

## Account Liquidity Calculations

Venus Protocol uses two calculations to determine account liquidity: Collateral Factor (CF) and Liquidation Threshold (LT).

### Collateral Factor (CF)

Collateral Factor is the percentage of the supplied funds that can be used to cover a loan. `Comptroller.getAccountLiquidity` in the Core Pool and `Comptroller.getBorrowingPower` in Isolated Pools return the liquidity or an account. They use the Resilient Oracle to retrieve the value of the asset and the collateral factor for the market to determine the percentage of supply can be used as collateral. Relying on `getBorrowingPower` is not sufficient for identifying accounts in need of liquidation on Isolated Pools.

### Liquidation Threshold (LT)

The Liquidation Threshold represents the point at which an account becomes under-collateralized, triggering the possibility of liquidation. In the Core Pool this is the same as the collateral factor. In Isolated Pools, the LT can be retrieved with `Comptroller.getAccountLiquidity`.

Liquidators often use external monitoring systems or other strategies to accurately identify under-collateralized accounts.

{% hint style="info" %}
**Iterating over all accounts and checking the CF and LT for every account is extremely inefficient.**

Instead, liquidators should rely on off-chain computations and maintain an off-chain mapping for accounts and balances. Using functions like `vTokenBalancesAll` and `vTokenUnderlyingPriceAll` from PoolLens can help retrieve balances and prices efficiently for multiple vTokens associated with an account.
{% endhint %}

By combining the information obtained from these functions, one can accurately identify under-collateralized accounts that are suitable for liquidation.

### Minimum Liquidatable Collateral

The `Comptroller.minLiquidatableCollateral` variable represents the minimal collateral in USD required for regular (non-batch) liquidations. Accounts with collateral below this threshold may not be eligible for non-batch liquidations. It's defined in USD value (18 decimals scale).

## Finding Under-Collateralized Accounts

The first step to performing a liquidation is to identify under-collateralized accounts. Here's a suggested approach to finding under-collateralized accounts:

1. Create a record of account balances: To identify under-collateralized accounts efficiently, liquidators can maintain an off-chain mapping of accounts and balances by indexing market events to detect new positions and update existing ones:
   * `Mint`: Event emitted when tokens are minted.
   * `Redeem`: Event emitted when tokens are redeemed.
   * `Borrow`: Event emitted when underlying is borrowed.
   * `RepayBorrow`: Event emitted when a borrow is repaid. By listening to these events, liquidators can track changes in positions and update the balances of users accordingly.

Consider using a subgraph to index these events.

2. Get account balances: Use the `vTokenBalancesAll` function provided by PoolLens (VenusLens on the Core Pool) to retrieve supply and borrow balances for multiple vTokens associated with an account. This function takes an array of vTokens and the account address as parameters.
3. Get underlying asset prices: Use the `vTokenUnderlyingPriceAll` function provided by the PoolLens (VenusLens on the Core Pool) to retrieve the underlying asset prices for multiple vTokens. This function takes an array of vTokens as a parameter.
4. Calculate liquidity shortfall: With the supply and borrow balances obtained from step 2 and the underlying asset prices from step 3, calculate the liquidity shortfall for each account. This can be done by taking the scalar product of the balances and prices and comparing them against the LT values.

By following this approach, you can efficiently identify under-collateralized accounts based on the CF and LT calculations.

{% hint style="warning" %}
Please note that the functions mentioned above are provided by PoolLens and may require integration within your liquidation bot. Additionally, it's important to stay updated with any changes or updates to Venus Protocol that may impact the process of finding under-collateralized accounts.
{% endhint %}

## Performing the Liquidation

Once an under-collateralized account has been identified, the liquidation process can be initiated using either `liquidateBorrow`, `liquidateAccount` or `healAccount` function. `liquidateBorrow` is provided by the relevant vToken contract (Liquidator contract on the Core Pool) whereas `liquidateAccount` and `healAccount` are provided in Comptroller. Here's an overview of the steps involved:

{% hint style="warning" %}
Please note that `healAccount` is an extension of the liquidation mechanism to address handling of bad debt and offseting it with protocol revenue/fees. On the other hand, `liquidateAccount` allows batches of liquidations in a single transaction. In both cases, the total collateral must be lower than the threshold `Comptroller.minLiquidatableCollateral`. Those two functions are only available in Isolated Pools. For Core Pool `liquidateBorrow` function provided by `Liquidator` contract is the only available mechanism to perform liquidations.
{% endhint %}

1. Calculate the liquidation amount: Determine the amount of debt to be repaid and the collateral to be seized. This is typically calculated by examining the borrower's debt balance, the market's collateral factor, and any discounts or liquidation incentives offered.
2. When performing the liquidation, there are 3 different types of liquidations that can be called, taking in mind the **collateral**, **minimum liquidatable collateral** and **solvency** of the account:

* **Collateral > `minLiquidatableCollateral` -->** `liquidateBorrow()`: Call the `liquidateBorrow` function on the relevant vToken contract. This function requires several parameters, including the borrower's address, the liquidator's address, the amount of debt to be repaid, and the collateral to be seized. Refer to the vToken contract documentation for specific details on the function's required parameters.

{% hint style="info" %}
**Example**

Given:

* Collateral Factor: 50%
* Close Factor: 50%
* Liquidation Threshold: 60%
* Borrow Amount: $13,000
* Collateral Amount: $20,000
* Liquidation Incentive: 110%
* Protocol Share Percentage: 5%

The borrowed amount is $1,000 above the liquidation threshold ($12,000), therefore the position is eligible for liquidation. Liquidation can be called with a repayment of up to $6,500 (borrow amount \* close factor). Let's assume the liquidator initiates the liquidation process with a repayment amount of $1,000 Let's Calculate the Collateral Seized Amount (the amount that is seized from the borrower's collateral):

`Collateral Seized Amount = Repayment Amount * Liquidation Incentive`

`Collateral Seized Amount = $1,000 * 1.1`

`Collateral Seized Amount = $1,100`

Therefore, if the borrowed asset value reaches **$13,000** and the repayment amount is **$1,000**, the total collateral seized will be **$1,100** considering the liquidation incentive of 10%. In order to calculate what amount the liquidator will get we need to consider `treasuryPercentMantissa` (in the Core pool) or `protocolSeizeShareMantissa` (in the Isolated pools). This variable (`Protocol Share Percentage`) sets the percentage of the collateral seized that will go to the protocol. Let's assume that the protocol shares for liquidations is `5%` and calculate the liquidator received amount:

`Liquidator Receive Amount = Collateral Seized - Protocol Shares`

`Protocol Shares = (Collateral Seized / Liquidation Incentive) * Protocol Share Percentage`

`Protocol Shares = ($1,100 / 1.1) * 0.05 = $50`

`Liquidator Receive Amount = $1,100 - $50 = $1,050`

In conclusion, the liquidator will provide **$1,000** for the liquidation. After the liquidation, the liquidator will receive **$1,050** and the rest **$50** will go to the protocol.
{% endhint %}

* **Collateral < minLiquidatableCollateral && account is solvent -->** `liquidateAccount()`: this function liquidates all borrows of the borrower.

{% hint style="info" %}
**Example**

* Collateral Factor: 50%
* Liquidation Threshold: 60%
* Min Liquidatable Collateral: $100
* Borrow Amount: $60
* Collateral Amount: $90

Position is already eligible for liquidation since Borrow Amount >= $54. We should call `liquidateAccount()` because **collateral < $100** and account is **solvent**.
{% endhint %}

* **Collateral < minLiquidatableCollateral && account is insolvent -->** `healAccount()`: this function seizes all the remaining collateral from the borrower, requires the person initiating the liquidation (msg.sender) to repay the borrower's existing debt, and treats any remaining debt as bad debt. The sender has to repay a certain percentage of the debt, computed as `collateral / (borrows * liquidationIncentive)`

{% hint style="info" %}
**Example**

* Collateral Factor: 50%
* Liquidation Threshold: 60%
* Min Liquidatable Collateral: $100
* Borrow Amount: $90
* Collateral Amount: $60

Position is eligible for liquidation and **collateral is < $100**, but the account is **insolvent**, so we need to call `healAccount()` to ensure that the remaining debt ($30) is treated as bad debt for the protocol.
{% endhint %}

3. Handle liquidation results: After invoking `liquidateBorrow`, monitor the transaction's success and handle any resulting events or errors. Successful liquidations will transfer the seized collateral to the liquidator's address and repay the debt from the borrower's account.

{% hint style="warning" %}
Please note that the liquidation process involves complex calculations and requires a deep understanding of Venus Protocol. It's essential to thoroughly test and validate your liquidation bot before deploying it in a production environment. Additionally, keep track of any changes or updates to the Venus Protocol that may impact the liquidation process.
{% endhint %}

## Forced liquidations

Usually, accounts are only eligible to be liquidated if they are under-collateralized, as described in the previous sections. An exception is if "forced liquidations" are enabled for a market or an individual account in a market. In this case, borrow positions can be liquidated in that market even when the health rate of the account is greater than 1 (i.e. when the account is sufficiently collateralized). Additionally, the close factor check is ignored, allowing the liquidation of 100% of the debt in one transaction.

This feature is based on the implementation done by Compound V2 [here](https://github.com/compound-finance/compound-protocol/pull/123/files). Compound V2 allows "forced liquidations" on markets as soon as the Collateral factor is zero, the Reserve factor is 100% and the borrows are paused. Venus defines a feature flag to enable/disable "forced liquidations", configurable directly via VIP, not based on other parameters. Compound community talked about this feature in [this post](https://www.comp.xyz/t/deprecate-fei-market/3513).

On Venus, forced liquidations can be enabled either for an entire market (all borrow positions in the market can be forcefully liquidated), or for individual accounts in a market (only the borrows of a particular account can be forcefully liquidated in the market).

To check if forced liquidations are enabled for an entire market, the function `Comptroller.isForcedLiquidationEnabled(address vToken)` can be called on the Comptroller contract of the pool with the address of the market. To check if forced liquidations are enabled for an individual account in the market, the function `Comptroller.isForcedLiquidationEnabledForUser(address borrower, address vToken)` can be used, providing the account and market address as arguments.

{% hint style="info" %}
**Availability**: Forced Liquidations for entire markets are available in the Core pool since [VIP-172](https://app.venus.io/#/governance/proposal/172), in the Isolated pools since [VIP-186](https://app.venus.io/#/governance/proposal/186). Forced Liquidations for individual accounts are available only in the Core pool since [VIP-210](https://app.venus.io/#/governance/proposal/210).
{% endhint %}

{% hint style="info" %}

#### Example

Given:

* `vUSDT` collateral factor: 80%
* 1 USDT = 1 BUSD = 1 USDC = $1 (for simplicity)
* Close factor: 50%
* Liquidation incentive: 10%
* User with the following positions:
  * Supply: 500 USDT
  * Borrow: 200 BUSD
  * Borrow: 100 USDC

The health rate for this user would be `(500 * 0.8) / (200 + 100) = 1.33`. So, in normal circumstances, this user is not eligible to be liquidated.

Now, let’s say we enable the **forced liquidations in the BUSD market** (via VIP). Then:

* Anyone will be allowed to liquidate the BUSD position of the previous user. Moreover, the close factor limit won’t be taken into account. So, the following liquidation would be doable:
  * Repay amount: 200 BUSD
  * Collateral market to seize: USDT
* By doing this liquidation, 220 USDT (repay amount + liquidation incentive) would be seized from the user’s collateral.
* After the liquidation, the global position of our user would be:
  * Supply: 280 USDT (500 USDT - 220 USDT seized during the liquidation)
  * Borrow: 0 BUSD
  * Borrow: 100 USDC
* So, the new health rate would be `(280 * 0.8 / 100) = 2.24`. They will be still ineligible for regular liquidations
* Because the forced liquidation is not enabled in the USDC market, the USDC debt cannot be liquidated (because the health rate is greater than 1).
  {% endhint %}

## Force VAI Debt First

{% hint style="danger" %}
This feature is disabled. Liquidators will have to change their codebase to consider the forced sequence of liquidations when there is a VAI debt. After having the confirmation from the main Liquidators that they have adapted their code, a VIP will be proposed to enable this feature.
{% endhint %}

In the previous section, liquidations are carried out on all accounts without taking into consideration specific amounts of VAI debt. The `forceVAILiquidate` feature enhances the liquidation process by forcing liquidations of borrowers with VAI debt greater than `minLiquidatableVAI`. Forcing VAI liquidations allows the protocol to better manage risk and prevent potential losses due to excessive VAI debt accumulation.

For borrowers with outstanding VAI debt, the force VAI liquidation first includes checks to ensure that only eligible accounts are liquidated before starting the liquidation process.

#### Checks

1. Check that VAI liquidations are not paused in Comptroller.
2. The forceVAILiquidate flag is set to true.
3. Verify whether the borrower's VAI debt is greater than the minimum amount of liquidatable VAI (initially 1,000 VAI).

{% hint style="warning" %}
If the above conditions are true then the protocol checks that the current vToken sent to be liquidated is VAI, otherwise the liquidation fails.
{% endhint %}

{% hint style="info" %}
**Example 1**

Given:

* this feature is enabled
* a user is eligible to be liquidated with the following debt:
  * 2,000 VAI
  * 5,000 USDT

Liquidators will be required to liquidate the VAI position first because it is greater than `minLiquidatableVAI`. If they try to liquidate first the USDT position, the liquidation transaction will be reverted.
{% endhint %}

{% hint style="info" %}
**Example 2**

Given:

* this feature is enabled
* a user eligible to be liquidated with the following debts:
  * 500 VAI
  * 5,000 USDT

Liquidators will be allowed to liquidate first the VAI position **or** the USDT position. Both liquidations will work because the VAI debt is less than `minLiquidatableVAI`
{% endhint %}

## Automatic Income Allocation

In the Core Pool, liquidation income is transferred to the Liquidator contract in the form of vTokens. During a liquidation transaction, the Liquidator contract will try to redeem the protocol's portion of the liquidation incentive in vTokens for the underlying tokens. If the redemption process is successful, the underlying tokens will be sent to the `ProtocolShareReserve` contract. However, if the redemption fails the underlying tokens will be added to a list of pending redemptions and the Liquidator contract will try to redeem the pending redemptions again in subsequent liquidation transactions.

### Distributing Seized Amount

The seized collateral is distributed between the Liquidator and the `ProtocolShareReserve` contract:

1. **Liquidator's Share:**
   * The Liquidator receives a designated portion of the collateral as an incentive.
2. **`ProtocolShareReserve` contract's Share:**
   * The remaining portion of the collateral is sent to the `ProtocolShareReserve` contract.
3. **Conversion (if applicable):**
   * **BNB:**
     * If the seized collateral is BNB, it is converted to Wrapped BNB (wBNB) before sending it to the `ProtocolShareReserve` contract.
   * **Other VTokens:**
     * For other VTokens, the underlying tokens are redeemed and transferred to the `ProtocolShareReserve` contract.

### Redemption Handling

The Liquidator contract attempts to redeem the protocol's portion of the liquidation incentive in underlying tokens for the VTokens:

1. **Successful Redemption:**
   * If the redemption is successful, the underlying tokens are sent to the `ProtocolShareReserve` contract.
2. **Failed Redemption:**
   * If the redemption fails due to insufficient liquidity or other reasons, the VTokens representing the pending redemption are added to a list for later processing.

### Pending Redemption Management

Subsequent liquidation transactions leverage the list of pending redemptions: during each liquidation, the Liquidator contract attempts to redeem pending VTokens for their underlying tokens, and send these underlying tokens to the `ProtocolShareReserve` contract.


# Governance

Use these guides to participate in Venus Protocol governance:

* [Understand VIP routes and proposal rules](/governance/decentralization)
* [Delegate voting power and vote](/guides/governance/delegating)
* [Prepare and submit a VIP](/guides/governance/vip)
* [Review deployed governance contracts](/deployed-contracts/governance)

Current proposals are available in the [Venus Governance portal](https://venus.io/governance). Governance discussions take place on the [Venus Community Forum](https://community.venus.io/).


# Submitting a VIP

Venus Improvement Proposals (VIPs) are executable governance proposals. Prepare and review a proposal as production code: a successful vote can change protocol configuration, transfer assets, or upgrade contracts.

{% hint style="warning" %}
At BNB Chain block `118,362,254`, every proposal route required at least 1,000,000 delegated XVS to propose. This threshold is configurable. Read the Governor's `proposalConfigs` before submitting instead of relying on a value copied from the interface or this page.
{% endhint %}

## Before submitting

1. Publish the motivation and specification on the [Venus Community Forum](https://community.venus.io/).
2. Define every target, value, function signature, argument, and destination network.
3. Add the proposal to the [VenusProtocol/vips repository](https://github.com/VenusProtocol/vips) so the payload and simulations can be reviewed.
4. Run the relevant fork simulations and review their state-change assertions.
5. Confirm that the selected timelock has permission to execute every action.

The Critical route still exists in the Governor, but [VIP-645](https://venus.io/governance/proposal/645?chainId=56) removed all of its privileged permissions. Do not select it for privileged actions unless governance grants the required permissions again.

## Generate and simulate the proposal

The VIP repository provides Hardhat tasks for building proposal files and running simulations. From a current checkout with its dependencies installed, the normal workflow is:

```bash
npx hardhat test simulations/<simulation-path> --fork <network>
npx hardhat createProposal --network <networkName>
```

For the proposal destination, select `venusApp` when generating a file for the Venus Governance interface. Follow the repository's current [Create Proposal instructions](https://github.com/VenusProtocol/vips#create-proposal), because task options can evolve.

Maintainers can also submit a reviewed VIP from the repository with:

```bash
npx hardhat propose <path-to-vip-relative-to-vips> --network bscmainnet
```

## Submit through the Governance portal

1. Open the [Venus Governance portal](https://venus.io/governance), connect the proposer wallet, and select **Create Proposal**.
2. Upload the generated proposal file, or create the proposal manually.
3. Select the intended Normal or Fast-track route.
4. Review the decoded title, description, targets, native values, function signatures, arguments, and destination networks.
5. Confirm that the portal output exactly matches the reviewed source and simulation.
6. Submit the transaction and record its transaction hash and proposal ID.

The Governor permits only one pending or active proposal per proposer. If the address already has one, wait for it to leave those states before submitting another.

## After submission

Share the on-chain proposal link and source with reviewers. Monitor the proposal through voting, then queue and execute it after the applicable timelock if it succeeds. For current route configuration and success conditions, see [Venus Improvement Proposals](/governance/decentralization).


# Delegating & Voting

XVS deposited in the XVS Vault can participate in Venus governance after its voting power has been delegated. You can delegate to your own address or to another address without transferring ownership of the staked XVS.

{% hint style="info" %}
Staking XVS and delegating voting power are separate actions. Voting power must be delegated before a proposal's voting snapshot to count for that proposal.
{% endhint %}

## Delegate voting power

1. Open the [Venus Governance portal](https://venus.io/governance) and connect the wallet that controls the vault position.
2. If necessary, deposit XVS into the XVS Vault.
3. Select the governance delegation action.
4. Enter the address that should receive the voting power. Use your connected address to self-delegate.
5. Review and confirm the delegation transaction in your wallet.

Delegating does not lock or transfer additional tokens. Redelegating moves voting power to a new delegate after the transaction is confirmed, but it does not retroactively change a proposal snapshot.

You can verify delegation on the XVS Vault by reading `delegates(address)`, `getCurrentVotes(address)`, and `getPriorVotes(address, blockNumber)`. Contract addresses are listed in [Deployed Governance Contracts](/deployed-contracts/governance).

## Vote on a VIP

1. Open an active proposal in the [Governance portal](https://venus.io/governance).
2. Review the proposal description, discussion, executable actions, simulations, and proposal route.
3. Choose **For**, **Against**, or **Abstain** and, if available, add a public reason.
4. Confirm the voting transaction in your wallet.
5. Verify that the transaction succeeded and that the proposal displays the vote.

There is no 600,000 XVS minimum for an individual voter. An address can cast the voting power recorded for it at the proposal's start block.

The current global quorum is 1,500,000 **For** votes. A proposal succeeds only if **For** is greater than **Against** and **For** meets the quorum. Abstain votes are visible in the result but do not count toward that quorum. Governance can change these parameters, so check the live Governor when the exact value matters.

For route-specific voting periods and timelock delays, see [Venus Improvement Proposals](/governance/decentralization).


# Vaults

Vaults within Venus Protocol provide a secure and efficient mechanism for users to stake their tokens, whether XVS or VAI, and earn passive income. This staking process allows users to actively participate in and contribute to the stability and security of the Venus ecosystem, while maximizing returns on their holdings.

**Staking in XVS Vault**

1. Navigate to the Venus app (app.venus.io) and connect your BNB Chain Wallet or other supported wallet apps.
2. On the left side of the screen, click on the 'Vault' tab.
3. A new screen will display two options: XVS and VAI vaults. Select the 'XVS' vault.
4. Once you select the XVS vault, a new interface will show. Click the 'Stake' button.
5. A pop-up will appear asking for the amount of XVS you want to stake. Input the desired amount and confirm.
6. After entering the amount, a prompt will ask for the transaction's confirmation in your wallet. Confirm this.
7. Once you've staked your XVS tokens, the interface will show the staked amount, your potential rewards, and other vault statistics.

**Staking in VAI Vault**

1. Follow the same steps as above to access the 'Vault' tab in the Venus app.
2. This time, select the 'VAI' vault.
3. Upon clicking on the 'VAI' vault, you'll see the option to 'Stake'.
4. Enter the amount of VAI you'd like to stake and confirm the staking action.
5. Just like with the XVS vault, you'll need to confirm the transaction in your wallet.
6. After staking, the interface will show the staked amount, potential rewards, and other relevant statistics related to the VAI vault.


# Protocol Math

{% hint style="warning" %}
**Use deployment-specific inputs.** The conversion identities on this page are current, but token decimals, vToken implementation, interest-rate model, and rate time unit are deployment state. Resolve them for the target chain, pool, market, and block before using the examples. Do not reuse vBNB or per-block assumptions on another deployment.
{% endhint %}

Venus contracts commonly represent fractional rates and exchange rates as integer mantissas scaled by `1e18`. Token amounts use the token's own decimal scale instead. Keep those two scales separate and use integer or arbitrary-precision arithmetic for on-chain values.

## vToken and underlying decimals

vTokens are ERC-20-compatible tokens, but the contract initializer accepts the vToken decimal count as a deployment parameter. Read it instead of assuming every vToken has eight decimals:

```solidity
function decimals() external view returns (uint8);
```

For an ERC-20 market, read the underlying address from the vToken and then read the underlying token's `decimals()`. Native-asset markets are an exception: for example, BNB Chain mainnet vBNB has no ERC-20 underlying contract, and native BNB uses 18 decimals.

The [deployed markets](/deployed-contracts/markets) page is an address-discovery starting point. Verify the address, implementation, listing state, and decimals on the target chain before calculating balances.

## Exchange rates

Let:

* `V_raw` be a vToken amount in raw vToken units;
* `E` be the `1e18`-scaled exchange-rate mantissa; and
* `U_raw` be the corresponding underlying amount in raw underlying units.

The exact integer conversion is:

$$
U\_{raw} = \left\lfloor\frac{V\_{raw} \times E}{10^{18}}\right\rfloor
$$

Solidity integer division and the following `BigInt` code both round down:

```javascript
const WAD = 10n ** 18n;

function vTokensToUnderlyingRaw(vTokenRaw, exchangeRateMantissa) {
  return (vTokenRaw * exchangeRateMantissa) / WAD;
}
```

For human-readable values, if `dV` and `dU` are the vToken and underlying decimal counts:

$$
oneVTokenInUnderlying = \frac{E}{10^{18 + dU - dV}}
$$

and:

$$
U\_{human} = V\_{human} \times oneVTokenInUnderlying
$$

Do not apply the human-unit formula to raw integers. For example, with `dV = 8`, `dU = 18`, and `E = 2 × 10^26`, one vToken represents `0.02` underlying tokens. A raw vToken balance of `10^8` therefore converts to `2 × 10^16` raw underlying units.

### Stored versus current exchange rate

```solidity
function exchangeRateStored() external view returns (uint256);

function exchangeRateCurrent() external returns (uint256);
```

`exchangeRateStored()` uses the last persisted interest checkpoint and does not accrue interest. `exchangeRateCurrent()` calls the accrual path first. It is not a Solidity `view` function:

* an off-chain `eth_call` simulates catch-up accrual and returns the resulting rate without persisting state; and
* a successful transaction persists the new checkpoint before returning the rate.

When `totalSupply` is zero, the implementation returns the configured initial exchange-rate mantissa. When comparing a simulated current rate with stored state, pin every read to the same block.

## Interest accrual and checkpoints

Interest is economically accounted for over elapsed slots, but the contract does not update storage autonomously in every block or second. Stored totals and indexes remain at the last checkpoint until a successful call executes `accrueInterest()` directly or reaches it through another function.

For the borrow side, the core checkpoint calculation is:

$$
\Delta = currentSlot - priorAccrualSlot
$$

$$
simpleInterestFactor = borrowRatePerSlot \times \Delta
$$

$$
interestAccumulated = \left\lfloor\frac{simpleInterestFactor \times borrowsPrior}{10^{18}}\right\rfloor
$$

The implementation then increases `totalBorrows` by `interestAccumulated`, adds the reserve-factor share to `totalReserves`, and increases `borrowIndex` by its proportional simple-interest factor. A later successful accrual therefore catches up all elapsed slots; inactivity does not make the elapsed interest disappear.

A supplier does not receive an account-level `supplyRate × principal × slots` storage update. The supplier keeps a vToken balance, and its underlying value changes through the market's updated exchange rate. Calls such as mint, redeem, borrow, repay, liquidation, current-balance getters, reserve operations, and direct `accrueInterest()` can reach the checkpoint path; the exact set depends on the implementation.

One catch-up over multiple slots applies a simple-interest factor for that interval. Do not describe the contract as automatically compounding in every block. Checkpoint frequency, utilization changes, and governance parameter changes affect the realized path.

## Annualizing a per-slot snapshot rate

The ABI names `supplyRatePerBlock()` and `borrowRatePerBlock()` are retained across Venus implementations, but modern vTokens document the return value as a rate per **slot**. A slot can be a block or a second.

Before annualizing a rate:

1. identify whether the target implementation is block-based or time-based;
2. obtain the matching `slotsPerYear` value from the deployed implementation or interest-rate model;
3. obtain the `slotsPerDay` convention used by the display client; and
4. read the rate mantissa at the same target block.

Modern TimeManager-based deployments expose `isTimeBased()` and `blocksOrSecondsPerYear()`. BNB Chain mainnet Core models are block-based and may expose their own `blocksPerYear` assumption. Do not assume `slotsPerDay = slotsPerYear / 365`: a block-based model's annualization parameter and a client's current block-cadence configuration can diverge. Do not hardcode a chain-wide blocks-per-day value in a long-lived integration.

For a `1e18`-scaled rate snapshot:

$$
r\_{slot} = \frac{rateMantissa}{10^{18}}
$$

The deployed model's normalization produces:

$$
modelNormalizedAPR = r\_{slot} \times configuredSlotsPerYear
$$

APY is an off-chain display convention. Define the daily snapshot using the consuming client's `slotsPerDay` value:

$$
dailyRate = r\_{slot} \times slotsPerDay
$$

and the corresponding non-compounded display extrapolation is:

$$
displayAPR = dailyRate \times 365
$$

The official frontend rate utility assumes 365 daily compounding periods:

$$
frontendAPY = \left(1 + dailyRate\right)^{365} - 1
$$

At the pinned source snapshots used for this page, the official API base-market supply/borrow calculator instead raises the same daily factor to 364 (`DAYS_PER_YEAR - 1`):

$$
apiAPY = \left(1 + dailyRate\right)^{364} - 1
$$

A client that consumes those API-provided base-market values can therefore differ from a direct use of the frontend utility. Other API calculations can use different conventions. Neither convention changes on-chain accrual. Choose and label the `slotsPerDay` input and daily exponent required by the consuming system; do not claim one derived value is universal across Venus components.

`modelNormalizedAPR` and `displayAPR` are equal only when `configuredSlotsPerYear = slotsPerDay × 365`. Check that equality explicitly for a block-based deployment rather than treating it as an invariant.

Multiply APR or APY by 100 only when formatting it as a percentage. This implementation uses arbitrary-precision decimal arithmetic, makes the number of compounding periods explicit, and returns zero for a zero rate:

```javascript
import BigNumber from 'bignumber.js';

const WAD = new BigNumber(10).pow(18);
const DAYS_PER_YEAR = 365;

function annualizeRate(rateMantissa, configuredSlotsPerYear, slotsPerDay, dailyExponent = DAYS_PER_YEAR) {
  const ratePerSlot = new BigNumber(rateMantissa.toString()).div(WAD);
  const modelNormalizedApr = ratePerSlot.times(configuredSlotsPerYear.toString());
  const dailyRate = ratePerSlot.times(slotsPerDay.toString());
  const displayApr = dailyRate.times(DAYS_PER_YEAR);
  const apy = dailyRate.plus(1).pow(dailyExponent).minus(1);

  return {
    modelNormalizedAprPercentage: modelNormalizedApr.times(100),
    displayAprPercentage: displayApr.times(100),
    apyPercentage: apy.times(100),
  };
}

console.assert(annualizeRate(0n, 31_536_000n, 86_400n).apyPercentage.isZero());
```

Daily compounding is a display convention, not a promise that the contract compounds once per day or that the current rate will persist for a year. Base supply and borrow APY also exclude token incentives, Prime rewards, and other distribution programs unless those components are calculated separately.

For model selection and utilization formulas, see [Interest-rate models](/risk/interest-rate-model). Source boundaries used for this page are [`venus-protocol@v10.3.0`](https://github.com/VenusProtocol/venus-protocol/tree/46afc66b1dbd61a707d0a3492b3ec21bf90fc17a), [`isolated-pools@943e7db`](https://github.com/VenusProtocol/isolated-pools/tree/943e7db1855c8ab4a09104f1d09e2b2db0506b95), the official frontend rate utilities at [`venus-protocol-interface@532895b`](https://github.com/VenusProtocol/venus-protocol-interface/tree/532895b508b3c7cc3ee94fcd6397978b9264d5ba/apps/evm/src/utilities), and the market API calculation at [`venus-protocol-api@d61125f`](https://github.com/VenusProtocol/venus-protocol-api/blob/d61125f1cccf3b36299a69cab1b09cced90ce1a5/src/modules/calculateInterestRates.ts).


# XVS Bridge

The Venus interface can bridge XVS among BNB Chain, Ethereum, Arbitrum, opBNB, Optimism, Base, zkSync Era, and Unichain. Available paths and limits are mutable; use the networks and live checks shown by the interface rather than a saved limits table.

## Before you start

* Open the official [XVS Bridge](https://app.venus.io/#/bridge) and confirm the domain before connecting a wallet.
* Keep native gas tokens on the source network.
* Confirm the source and destination networks, connected account, XVS amount, quoted LayerZero fee, bridge address, and wallet spending limit.
* The current interface sends destination XVS to the connected account. Do not use this workflow when you need a different recipient.
* Bridging is asynchronous and depends on both networks, Venus bridge configuration, its oracle and limits, destination mint capacity, and LayerZero V1 messaging.

## Submit a transfer

1. Connect the wallet that holds XVS and select the source network.
2. Choose a different destination network. Switching the source network in the form also requests a wallet network switch.
3. Enter the XVS amount. The interface checks the local balance, single and rolling 24-hour bridge limits, destination mint capacity, and estimated native-token fee.
4. If the interface requests an approval, inspect the exact token, spender, amount, and network before signing. BNB Chain outbound transfers lock native XVS with `transferFrom`; destination-chain transfers burn omnichain XVS through the authorized local bridge.
5. Review the final quote and submit the bridge transaction. The fee can change before inclusion, so reject a wallet request whose value or contract differs from the reviewed transaction.
6. Track the source transaction and the corresponding LayerZero message. The destination balance is final only after the destination transaction succeeds.

## If a transfer is delayed or fails

Do not immediately repeat the transfer: a delayed message can still execute and a duplicate transfer would send another amount. Record the source transaction hash and inspect its LayerZero message and destination status.

Common causes include a paused bridge or token, stale or rejected oracle price, path limits, insufficient destination gas, an untrusted route, blacklisting, or exhausted destination mint capacity. `retryMessage` is an administrative recovery path for a stored failed payload, not a normal user action. Report the transaction hash and both networks through an official Venus support channel if the interface does not recover normally.

## Approval hygiene

An ERC-20 approval remains after a transfer unless it is consumed or revoked. Prefer the amount needed for the intended transaction. After bridging, review the remaining XVS allowance to the source bridge and revoke it if you do not plan to bridge again.


# Borrowing VAI

{% hint style="warning" %}
**Current scope: BNB Chain Core Pool.** The official Venus app exposes the VAI borrow and repay flow on BNB Chain mainnet and testnet. Use BNB Chain testnet only for testing; the transaction guidance involving assets of value is for mainnet. New borrowing succeeds only when the selected VAI Controller's current Prime eligibility check accepts the wallet. Repaying existing VAI debt does not require Prime, but both actions depend on the selected deployment being available and the Core Comptroller's `protocolPaused()` flag being false. Check the live app, Controller, and selected network before every transaction.
{% endhint %}

VAI is minted against collateral in your BNB Chain Core Pool account. It is not borrowed from a separate VAI vToken market. Minting VAI creates debt that:

* uses the collateral and outstanding borrows in your Core Pool account to determine how much VAI you may mint;
* reduces your account health and can expose your Core Pool collateral to liquidation;
* accrues interest at a deployment-specific, variable rate; and
* may charge a mint fee. The full requested amount is added to your debt; the Controller separately mints the fee amount to the configured treasury and the remainder to your wallet.

Supplied assets in an isolated pool do not provide collateral for this VAI debt. The values shown in old screenshots or examples are not evidence of the current borrow APR, fee, mintable amount, account health, cap, or pause state.

## Before borrowing

1. Open the official [Venus app](https://app.venus.io) and select BNB Chain mainnet. Use BNB Chain testnet only to test the flow. The VAI route is not exposed by the official app on other networks.
2. Connect the intended wallet and open **VAI** from the navigation menu.
3. Confirm that the wallet satisfies the selected VAI Controller's current Prime eligibility check. Mainnet currently uses PrimeV2. On testnet, the Controller's configured Prime contract can differ from the contract checked by the app, so an enabled form does not prove that `mintVAI` will succeed. Prime is not required to repay existing VAI debt.
4. Review the Core Pool account that will secure the debt. Confirm which supplied assets are enabled as collateral, their current prices and liquidation thresholds, your other borrows, and the simulated account health after minting.
5. Review every live value in the form: **Available amount**, **Borrow APR**, any displayed **Mint fee**, and the simulated account health. **Available amount** is the current hard limit; the form's **Max** button can select a lower amount to preserve the interface's health-factor buffer. These values can change before the transaction is mined.

{% hint style="danger" %}
Neither the displayed available amount nor the form's client-selected maximum guarantees that a position will remain safe. Minting close to a limit can leave too little buffer for price movements, interest accrual, oracle updates, or governance parameter changes. If the account becomes liquidatable, a liquidator can repay debt and seize Core Pool collateral.
{% endhint %}

## Borrow VAI

1. On the **Borrow** tab, enter the amount of VAI to mint. Keep a deliberate health-factor buffer rather than selecting the maximum automatically.
2. Recheck any displayed mint fee. The app shows the fee row only when the current fee percentage is greater than zero. If the requested raw VAI amount is `M` and the Controller's `1e18`-scaled `treasuryPercent` is `P`, the fee amount is `floor(M × P / 1e18)`. The debt increase is `M`; the Controller mints the fee amount to `treasuryAddress` and mints `M - feeAmount` to the borrower. Use the live value rather than hardcoding a percentage or assuming that a current zero fee is permanent.
3. Recheck the variable Borrow APR and the post-transaction account health.
4. Submit the transaction. In the wallet, verify the chain, the VAI Controller proxy as the transaction target, the `mintVAI` action, and the raw amount before signing.
5. After confirmation, verify the transaction receipt, VAI received, VAI debt, and Core Pool account health. A successful wallet submission is not proof that the transaction succeeded on-chain.

Minting can fail if the account is not Prime-eligible according to the Controller, the amount exceeds the account's current mintable amount, the VAI mint cap would be exceeded, the Core Comptroller's `protocolPaused()` flag is true, an oracle check fails, or state changes before execution.

## Repay VAI

You may repay part or all of an existing VAI debt without holding a Prime token. You need VAI in the paying wallet and the network's native token for gas. Because interest accrues, the full repayment amount can be greater than both the amount originally requested and the amount originally received after the mint fee.

1. Open the **Repay** tab and review the current VAI debt, Borrow APR, VAI wallet balance, simulated account health, and spending limit.
2. Enter a partial amount or select **Max**. **Max** fills the current submit limit, which can be smaller than the total debt when the wallet's VAI balance or an existing nonzero allowance is lower. If you intend to repay in full, confirm that the entered amount equals the entire displayed debt.
3. If approval is required, approve the selected network's **VAI Controller proxy** to spend VAI. The VAI Vault, a vToken, an implementation contract, and any third-party address are not valid substitutes for this repayment spender.
4. Review the approval transaction separately from the repayment transaction. Confirm the token, spender, amount, chain, and wallet. After repayment, reduce or revoke any allowance you no longer need through the app's spending-limit control or directly on the token contract.
5. Submit the repayment and verify the receipt, remaining debt, remaining allowance, VAI wallet balance, and updated account health.

Only when the entered amount equals the debt recognized by the frontend does the official app classify the action as a full repayment and replace the input with the maximum `uint256` value for `repayVAI`. The Controller treats any value at least as large as the current total debt as a request to settle that total debt, so newly accrued interest can be included without transferring the entire maximum value. Because debt continues accruing before execution, the wallet needs enough VAI and allowance for the actual amount due. Do not copy this maximum-value convention to liquidation calls or unrelated contracts.

{% hint style="warning" %}
A partial repayment reduces debt but does not close the position or guarantee that the account is safe from liquidation. The VAI repayment path is also blocked while the Core Comptroller's `protocolPaused()` flag is true; if repayment is unavailable, verify that flag and official Venus communications instead of approving an alternative spender.
{% endhint %}

## Verify contracts and live state

Use [Funds — Deployed Contracts](/deployed-contracts/funds#bnb-chain-mainnet) to discover the BNB Chain mainnet VAI Unitroller and [the testnet section](/deployed-contracts/funds#bnb-chain-testnet) for the BNB Chain testnet deployment. The VAI Unitroller is the user-facing VAI Controller proxy. This is a custom Unitroller proxy: read its current implementation through `vaiControllerImplementation()` rather than assuming an EIP-1967 storage slot. Also verify the VAI token under the selected network in [Markets — Deployed Contracts](/deployed-contracts/markets).

Before signing:

* match the app's selected network to the wallet network;
* match the VAI token and VAI Controller proxy against the deployment page and the chain explorer;
* inspect the proxy's current implementation and current state rather than calling the implementation directly; and
* treat mint caps, Prime-only mode, the configured Prime contract, fee, interest rate, oracle state, and the global pause flag as live configuration. In particular, do not assume testnet uses the same Prime contract as mainnet.

For integrators, `mintVAI(uint256)` and `repayVAI(uint256)` accept raw VAI amounts. Read the selected token's decimals and current Controller state instead of assuming values from another chain or block. The behavior described here is bounded by [`venus-protocol@v10.3.0`](https://github.com/VenusProtocol/venus-protocol/blob/46afc66b1dbd61a707d0a3492b3ec21bf90fc17a/contracts/Tokens/VAI/VAIController.sol) and the official VAI interface at [`venus-protocol-interface@c51133c`](https://github.com/VenusProtocol/venus-protocol-interface/tree/c51133cb99c6881b96f9636b9cbec3bcf9c5e572/apps/evm/src/pages/Vai). A source snapshot explains implementation behavior; it does not prove that a particular proxy, implementation, parameter, or pause state is current at a later block.


# Gasless Transactions on ZKsync

The Venus app can offer transactions that use a [Zyfi Paymaster](https://docs.zyfi.org/integration-guide/paymasters-integration/sponsored-paymaster) on ZKsync. Paymaster availability, supported actions, sponsorship percentage, fee token, quota, and quote expiry are dynamic; a displayed option is not a promise that every transaction will be fully sponsored.

{% hint style="danger" %}
Do not depend on sponsorship for urgent position management. Keep enough ETH for a standard ZKsync transaction, or use a fee token only when the current Venus quote explicitly identifies it and its maximum cost. Never sign an opaque or unexpected payload: verify the ZKsync network, contract, action, token, amount, fee, and expiry shown by the app and wallet.
{% endhint %}

## Step-by-Step Guide

### Step 1: Connect to ZKsync on Venus

1. Open the [Venus app](https://app.venus.io) and connect your wallet.
2. Make sure you are connected to the ZKsync network. If you are not, switch your wallet’s network to ZKsync.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-e00d653b80bbf5f6532952633c6b91d3b9d95138%2Fgasless-zksync-network-selection.png?alt=media" alt="Selection of ZKsync network"><figcaption><p>Selection of ZKsync network</p></figcaption></figure>

### Step 2: Prepare the Venus action

In this example, the action enables the ZK market as collateral. Enabling collateral changes liquidation risk; review the market and resulting account health before continuing.

1. Navigate to the ZK market on Venus.
2. Click on "Collateral" to enable the use of this market as collateral.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-38b7d1f4cbb95216f7d4034be984485d81ce68e6%2Fgasless-zksync-features.png?alt=media" alt="Interaction with any feature on ZKsync"><figcaption><p>Interaction with any feature on ZKsync</p></figcaption></figure>

### Step 3: Review the quote and authorize the transaction

1. If sponsorship is currently available for the action, the app displays a paymaster quote. Check whether sponsorship is full or partial, which token pays any remainder, the maximum fee, and the quote expiry.
2. Review the wallet request. Wallets and integration versions can present a transaction or typed authorization differently; confirm the decoded target and action match the Venus action you prepared. Reject an unexpected chain, contract, spender, amount, fee token, value, nonce, or deadline.
3. Authorize only after those values match. If the quote has expired or sponsorship is unavailable, cancel and use the normal wallet transaction flow with ETH rather than repeatedly signing stale payloads.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-3c657ac529d0b0702a0c47f20a4abccb610fe25c%2Fgasless-zksync-rabby.png?alt=media" alt="Example Zyfi authorization prompt in Rabby"><figcaption><p>Example Zyfi authorization prompt in Rabby; verify the current decoded request.</p></figcaption></figure>

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-cf9cc0288c1d74c5061458f0f95bd159ba5b5d2a%2Fgasless-zksync-metamask.png?alt=media" alt="Example Zyfi authorization prompt in MetaMask"><figcaption><p>Example Zyfi authorization prompt in MetaMask; verify the current decoded request.</p></figcaption></figure>

### Viewing the Transaction

Verify the submitted transaction on the [ZKsync Explorer](https://explorer.zksync.io/). If a paymaster was used, the transaction details identify it; also confirm the called Venus contract, status, token movements, and final fee. The screenshots above are illustrative and may not match the current wallet or app flow.


# Import Positions

### Introduction

This guide provides step-by-step instructions on how to import your supply positions from other DeFi platforms into the Venus Protocol.

### Steps for importing supply positions from other DeFi platforms)

#### Step 1: Connect Your Wallet

* Click on the "Connect wallet" button in the top right corner of the Venus Bridge interface to connect your wallet.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-16233b68659ae61deac39c6d18b3e6e19ec3514b%2FScreenshot%202024-01-22%20at%201.15.11%20PM.png?alt=media" alt=""><figcaption></figcaption></figure>

If you have supply positions in other DeFi protocols (we currently only support Aave, with more to come soon) for which we can propose a better APY, a modal will automatically appear showing you options to import your positions to the Venus Protocol.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-1868ec0dd3e730ecc9e9eebe82b77818db28010f%2Fimport-positions-modal.png?alt=media" alt=""><figcaption></figcaption></figure>

If you close this modal, it won't be shown to you ever again. You can still access this UI by selecting the "Import" item in the side menu.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-737ae4cb4c135e7ecff726e9b6d78fc3cca65bf1%2Fimport-positions-page.png?alt=media" alt=""><figcaption></figcaption></figure>

#### Step 2: Initiate an Import

* Click on the "Import" button next to any position listed.
* A confirmation pop-up will appear in your wallet for you to approve the transaction. Note that this transaction is sponsored by the Venus Protocol, therefore it is free of charge.

#### Step 3: Confirm and Wait

* Confirm the transaction in your wallet. The interface will show the transaction as pending, indicating that it is being processed. This may take from a few seconds to a few minutes depending on the current network activity.
* You can import all your positions one-by-one by repeating the process.

#### Step 4: Transaction Completion

* Once the transaction is complete, the new positions will appear in the "Account" page.


# Enable E-mode

### Want to try out e-mode?

Head to the Venus dApp ([app.venus.io](https://app.venus.io)) and ensure you are connected to the BNB Chain.

Click on the “E-mode” icon to view current e-mode groups.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-f90c95181b6efbaa464eb1d39374c7fde4b86add%2Fe-mode-1.png?alt=media" alt=""><figcaption></figcaption></figure>

Make sure your wallet is connected.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-b6240a725749cd6d3de8f6c3154e266673afe7c4%2Fe-mode-2.png?alt=media" alt=""><figcaption></figcaption></figure>

Click “Enable” in the e-mode group you would like to enable. Just like that, enjoy increased LTV on the assets listed in that e-mode group!

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-d633b808e1fd090347a35a296565f8f86e10fc60%2Fe-mode-3.png?alt=media" alt=""><figcaption></figcaption></figure>

When e-mode is enabled, you can only borrow assets listed in that group. These assets will be marked in the “borrowable” column for your reference.

Currently, only the Stablecoins e-mode group is enabled. We plan to release BTC and BNB e-mode groups within the next few weeks. Keep following us on X or join our Telegram and Discord groups to be the first to know!


# Isolated E-mode

## Introduction to Isolated E-mode

**Introduction**

**Isolation Mode** in Venus Protocol is a safeguard mechanism designed to securely onboard high-volatility or newly listed assets—such as RWAs—by limiting their exposure to other assets within the protocol. Assets classified as **“Isolated Collateral Assets”** can only be used to borrow specific assets, such as BNB and stablecoins, as determined through our risk management framework and Venus Protocol Governance. This structure effectively mitigates the risks associated with such assets. Within the Venus Protocol user interface, assets restricted to supply in Isolated E-Mode are clearly marked with the label **“Isolated.”**

## E-mode vs Isolation Mode

**What are the key differences between E-mode and Isolated asset?**

E-Mode refers to Efficiency Mode. It enables users to maximize the potential of their collateral, allowing them to pursue various yield strategies on Venus by offering a higher Loan-to-Value (LTV) ratio for correlated assets within the same E-Mode group.

<div align="center"><img src="https://github.com/user-attachments/assets/940cb81f-9ae3-4315-ad7e-927470fb567e" alt="image" height="322" width="591"></div>

Assets in the Isolation Mode feature more conservative parameters and supply caps than standard core pool assets. However, users can still utilize them as collateral without exposing the protocol to widespread risk.

<div align="center"><img src="https://github.com/user-attachments/assets/70fdf0c8-1171-4c93-b2bc-a19510ff5184" alt="image" height="273" width="587"></div>

## Supplying an Isolated Asset

**Supplying an Isolated Asset**

Before using an isolated asset as a collateral, users must ensure that their account is debt-free. Once this condition is met, they can navigate to the user interface and enable the Isolated E-mode group. When supplying an isolated asset in Isolation mode, users gain access to the core pool's stablecoin liquidity and other designated assets.

## Borrowing in Isolation Mode

**Borrowing in Isolation Mode**

When using an Isolated Asset as collateral, you are limited to borrowing only the stablecoins and other designated assets (i,e BNB) that are approved for the Isolation E-Mode group via Venus risk management process and governance. This restriction helps manage the risk associated with these assets. Additionally, while in Isolation Mode, you cannot use any other assets as collateral,though users may still earn interest on their other supplied assets on the core pool.

## Use-case for users

**Example use-case for users**

Isolated assets listed on Venus offer significant upside potential for both asset holders and the protocol itself—particularly for real-world asset (RWA) tokenizations, such as tokenized stocks. For example, listing $AAPL as an isolated asset with the ability to borrow stablecoins or BNB unlocks powerful use cases for users, including:

* **Instant Borrowing Against AAPL Shares**: Users can borrow funds against their tokenized $AAPL holdings, gaining immediate liquidity without bank visits, paperwork, or approval delays. On Venus, this process is fully permissionless and completes in seconds.
* **Earning Annual Yield Through Smart Strategies**: Users deposit $AAPL, borrow $BNB against it, then stake the $BNB on Aster to earn asBNB—typically yielding 10–13% annually.
* **Shorting $AAPL on Venus**: Users borrow $AAPL using stablecoins, sell it on the open market, and later repurchase $AAPL to repay the loan and close the position.

## Exiting Isolation Mode

**Exiting Isolation Mode**

If users choose to exit the Isolation Mode group, they must first repay all outstanding debt linked to the Isolated group. Once the debt is fully settled, they may disable the Isolated asset, enabling them to return to the standard mode.


# Withdrawing from deprecated isolated pools

Venus isolated pools have been fully deprecated. Their navigation and market screens have been removed from the Venus app, so positions can no longer be managed through the interface.

For markets that still allow repayments and redemptions, legacy positions may be closable by interacting directly with the market contracts. This is not possible for every market: some deprecated markets are unlisted or have **REPAY** or **REDEEM** paused. Check the market status as described below before submitting a transaction. Do not repeatedly submit transactions if the required action is unavailable.

Production isolated pools were deployed on **BNB Chain**, **Ethereum**, and **Arbitrum**. Core Pool positions and other Venus products are separate from the deprecated standalone pools. Legacy testnet deployments also exist on BNB Testnet, Sepolia, and Arbitrum Sepolia; make sure you select the explorer for the network where your position exists.

## Before you begin

* You will interact with Venus contracts through a block explorer, connecting the same wallet that holds the position:
  * BNB Chain — [BscScan](https://bscscan.com) or [BscScan Testnet](https://testnet.bscscan.com)
  * Ethereum — [Etherscan](https://etherscan.io) or [Etherscan Sepolia](https://sepolia.etherscan.io)
  * Arbitrum — [Arbiscan](https://arbiscan.io) or [Arbiscan Sepolia](https://sepolia.arbiscan.io)
* vTokens are beacon proxies, and RewardsDistributor contracts are transparent proxies. On an explorer, open **Contract** and use **Read as Proxy** or **Write as Proxy** when those tabs are available. If the explorer has not recognized the proxy, do not guess an implementation address or construct a transaction manually; stop and ask for verification through the official community forum.
* Confirm the explorer domain, network, and full contract address before connecting your wallet. Token names and symbols can be imitated. Venus contributors and support representatives will never ask for your seed phrase or private key.
* Keep enough of the network's native token for gas. The direct vToken flow operates on ERC-20 underlying tokens. For WBNB or WETH markets, repayment requires the wrapped token and redemption returns the wrapped token; BNB or ETH held for gas is not the same asset.
* Contract inputs use integer base units, not human-readable decimal amounts.

The current [Deployed Contracts → Markets](/deployed-contracts/markets) page lists documented pool and vToken addresses. Older unlisted markets may appear only in your Venus transaction history or the canonical [`isolated-pools` deployment records](https://github.com/VenusProtocol/isolated-pools/tree/943e7db1855c8ab4a09104f1d09e2b2db0506b95/deployments). Deployment records identify contracts but do not prove their current implementation, pause state, liquidity, or oracle status.

Each isolated-pool market is a **vToken** contract. Supplying an asset mints vTokens; borrowing records debt in the vToken. Repayments and redemptions below are made through the vToken proxy, while approvals are made through the underlying token contract.

## Step 1 — Identify your positions and check market status

For every isolated-pool vToken you previously used, open its proxy on the correct explorer and check these functions under **Read as Proxy**:

* `balanceOf(<your address>)` — your vToken balance. A non-zero value represents a supplied position, but does not guarantee that redemption is currently available.
* `borrowBalanceStored(<your address>)` — debt denominated in the underlying token, calculated with the market's latest stored borrow index. It does not first accrue new interest.
* `underlying()` — the ERC-20 token used to repay the market and received on a direct redemption.
* `comptroller()` — the Comptroller proxy that controls this market.

`borrowBalanceCurrent(<your address>)` accrues interest and is not a view function. Explorers generally place it under **Write as Proxy**, where broadcasting a transaction does not display its return value like a normal read. Do not send a transaction merely to read this value. A wallet/client simulation or RPC `eth_call` can obtain the simulated result; otherwise use `borrowBalanceStored` to identify debt and re-check it after repayment.

Token holdings can help find supplied positions, but a borrow-only account can have a zero vToken balance. The absence of a vToken from a wallet's token list—or the absence of an app notice—does not prove that the wallet has no debt or unclaimed rewards. Review the wallet's Venus transaction history and the official market records on every relevant network.

Before using the write functions below, open the market's Comptroller proxy and check:

* `isMarketListed(<vToken address>)` must return `true`.
* `actionPaused(<vToken address>, 3)` checks **REPAY** and must return `false` before repayment.
* `actionPaused(<vToken address>, 1)` checks **REDEEM** and must return `false` before redemption.

If the market is unlisted or the action you need is paused, the direct call below will revert. Stop and ask for help through the [Venus community forum](https://community.venus.io/); include the chain, wallet address, vToken address, and any transaction hash or revert data, but never share private credentials.

## Step 2 — Repay your borrows

Do this only for markets where `borrowBalanceStored` is non-zero and **REPAY** is available. If you have no borrows, skip to Step 3.

1. Open the underlying token returned by `underlying()` and use **Write as Proxy** if it is itself a proxy. Call `approve(spender, amount)`:
   * `spender` is the vToken proxy address, not its implementation.
   * `amount` must cover the debt after newly accrued interest and must be entered in the underlying token's base units. If an existing allowance is non-zero, some tokens require setting it to zero before changing it.
2. Open the vToken under **Write as Proxy** and call `repayBorrow(repayAmount)`. For a full repayment, pass `repayAmount = 115792089237316195423570985008687907853269984665640564039457584007913129639935`, which is `type(uint256).max`.

Passing `type(uint256).max` asks the vToken to repay up to the debt after interest accrual. The vToken caps the attempted transfer at that debt rather than attempting to transfer `uint256.max`. The transaction can still revert because of market state, oracle updates, insufficient token balance or allowance, or token behavior; fee-on-transfer behavior can also leave debt behind.

After confirmation, re-check `borrowBalanceStored(<your address>)`. Do not treat the position as closed unless it is zero. Once it is zero—or if you decide to stop attempting repayment—check `allowance(<your address>, <vToken address>)` on the underlying token and revoke any unused allowance by calling `approve(<vToken address>, 0)`.

Repeat for every borrowed market in each pool and on every affected network.

## Step 3 — Withdraw your supplied assets

For each listed market where `balanceOf` is non-zero and **REDEEM** is available, open the vToken under **Write as Proxy**:

* `redeem(redeemTokens)` — burns the specified vTokens and returns the corresponding underlying. To request a full exit, pass the complete value returned by `balanceOf(<your address>)`. This is the preferred full-exit method.
* `redeemUnderlying(redeemAmount)` — requests a partial withdrawal denominated in the underlying token's base units. Conversion to vTokens and rounding can affect the resulting amount, so do not use it as a guarantee of an exact full exit.

After confirmation, re-check `balanceOf(<your address>)` and confirm that the underlying tokens reached your wallet. Direct redemption from a WBNB or WETH market returns the wrapped token, which must be unwrapped separately if you need BNB or ETH. Token-level transfer fees or other token behavior can affect the amount received.

Repaying all borrows first is the simplest pool-local order because it removes account shortfall as one possible blocker. It does not guarantee redemption. A paused or unlisted market, insufficient market cash, unavailable oracle prices, or token and reward-hook failures can still cause a transaction to revert.

For a market your account has entered, redeeming or calling `exitMarket` causes the Comptroller to check the account across all currently listed entered markets in that pool. The Resilient Oracle first applies its configured validation and fallback logic. If it still cannot produce a valid price for any required market, the operation can revert. `exitMarket` performs the same full-balance redeem check and generally cannot bypass that condition.

If a repayment or redemption fails unexpectedly, stop rather than repeatedly spending gas. Ask for help through the [Venus community forum](https://community.venus.io/) and provide the chain, wallet address, vToken address, and transaction hash or revert data.

## Reward claims

A legacy RewardsDistributor may still record accrued rewards. Claiming updates the accrual and attempts payment; payment succeeds only if that distributor holds enough of its reward token. A pool can have multiple distributors, each covering one reward token. Calling one distributor does not claim from the others.

The following mainnet proxy addresses come from the deployment records at commit [`943e7db`](https://github.com/VenusProtocol/isolated-pools/tree/943e7db1855c8ab4a09104f1d09e2b2db0506b95/deployments). Use these proxy addresses—not a contract named `RewardsDistributorImpl`—and verify the network, proxy, and `rewardToken()` value on the explorer before submitting a claim.

| Network   | Pool              | Reward token and expected address                                                                                               | RewardsDistributor proxy                                                                                                |
| --------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| BNB Chain | DeFi              | BSW — [`0x965f527d9159dce6288a2219db51fc6eef120dd1`](https://bscscan.com/token/0x965f527d9159dce6288a2219db51fc6eef120dd1)      | [`0x7524116CEC937ef17B5998436F16d1306c4F7EF8`](https://bscscan.com/address/0x7524116CEC937ef17B5998436F16d1306c4F7EF8)  |
| BNB Chain | DeFi              | ANKR — [`0xf307910A4c7bbc79691fD374889b36d8531B08e3`](https://bscscan.com/token/0xf307910A4c7bbc79691fD374889b36d8531B08e3)     | [`0x14d9A428D0f35f81A30ca8D8b2F3974D3CccB98B`](https://bscscan.com/address/0x14d9A428D0f35f81A30ca8D8b2F3974D3CccB98B)  |
| BNB Chain | DeFi              | USDT — [`0x55d398326f99059fF775485246999027B3197955`](https://bscscan.com/token/0x55d398326f99059fF775485246999027B3197955)     | [`0xD86FCff6CCF5C4E277E49e1dC01Ed4bcAb8260ba`](https://bscscan.com/address/0xD86FCff6CCF5C4E277E49e1dC01Ed4bcAb8260ba)  |
| BNB Chain | GameFi            | FLOKI — [`0xfb5B838b6cfEEdC2873aB27866079AC55363D37E`](https://bscscan.com/token/0xfb5B838b6cfEEdC2873aB27866079AC55363D37E)    | [`0x501a91b995Bd41177503A1A4144F3D25BFF869e1`](https://bscscan.com/address/0x501a91b995Bd41177503A1A4144F3D25BFF869e1)  |
| BNB Chain | GameFi            | RACA — [`0x12BB890508c125661E03b09EC06E404bc9289040`](https://bscscan.com/token/0x12BB890508c125661E03b09EC06E404bc9289040)     | [`0x2517A3bEe42EA8f628926849B04870260164b555`](https://bscscan.com/address/0x2517A3bEe42EA8f628926849B04870260164b555)  |
| BNB Chain | Liquid Staked BNB | ankrBNB — [`0x52F24a5e03aee338Da5fd9Df68D2b6FAe1178827`](https://bscscan.com/token/0x52F24a5e03aee338Da5fd9Df68D2b6FAe1178827)  | [`0x63aFCe42086c8302659CA0E21F4Eade27Ad85ded`](https://bscscan.com/address/0x63aFCe42086c8302659CA0E21F4Eade27Ad85ded)  |
| BNB Chain | Liquid Staked BNB | stkBNB — [`0xc2E9d07F66A89c44062459A47a0D2Dc038E4fb16`](https://bscscan.com/token/0xc2E9d07F66A89c44062459A47a0D2Dc038E4fb16)   | [`0x79397BAc982718347406Ebb7A6a8845896fdD8dE`](https://bscscan.com/address/0x79397BAc982718347406Ebb7A6a8845896fdD8dE)  |
| BNB Chain | Liquid Staked BNB | SD — [`0x3BC5AC0dFdC871B365d159f728dd1B9A0B5481E8`](https://bscscan.com/token/0x3BC5AC0dFdC871B365d159f728dd1B9A0B5481E8)       | [`0x6a7b50EccC721f0Fa9FD7879A7dF082cdA60Db78`](https://bscscan.com/address/0x6a7b50EccC721f0Fa9FD7879A7dF082cdA60Db78)  |
| BNB Chain | Liquid Staked BNB | SD — [`0x3BC5AC0dFdC871B365d159f728dd1B9A0B5481E8`](https://bscscan.com/token/0x3BC5AC0dFdC871B365d159f728dd1B9A0B5481E8)       | [`0xBE607b239a8776B47159e2b0E9E65a7F1DAA6478`](https://bscscan.com/address/0xBE607b239a8776B47159e2b0E9E65a7F1DAA6478)  |
| BNB Chain | Liquid Staked BNB | lisUSD — [`0x0782b6d8c4551B9760e74c0545a9bCD90bdc41E5`](https://bscscan.com/token/0x0782b6d8c4551B9760e74c0545a9bCD90bdc41E5)   | [`0x888E317606b4c590BBAD88653863e8B345702633`](https://bscscan.com/address/0x888E317606b4c590BBAD88653863e8B345702633)  |
| BNB Chain | Meme              | BabyDoge — [`0xc748673057861a797275CD8A068AbB95A902e8de`](https://bscscan.com/token/0xc748673057861a797275CD8A068AbB95A902e8de) | [`0xC1044437AbfD8592150d612185581c5600851d44`](https://bscscan.com/address/0xC1044437AbfD8592150d612185581c5600851d44)  |
| BNB Chain | Stablecoins       | lisUSD — [`0x0782b6d8c4551B9760e74c0545a9bCD90bdc41E5`](https://bscscan.com/token/0x0782b6d8c4551B9760e74c0545a9bCD90bdc41E5)   | [`0xBA711976CdF8CF3288bF721f758fB764503Eb1f6`](https://bscscan.com/address/0xBA711976CdF8CF3288bF721f758fB764503Eb1f6)  |
| BNB Chain | Stablecoins       | lisUSD — [`0x0782b6d8c4551B9760e74c0545a9bCD90bdc41E5`](https://bscscan.com/token/0x0782b6d8c4551B9760e74c0545a9bCD90bdc41E5)   | [`0xA31185D804BF9209347698128984a43A67Ce6d11`](https://bscscan.com/address/0xA31185D804BF9209347698128984a43A67Ce6d11)  |
| BNB Chain | Stablecoins       | ANGLE — [`0x97B6897AAd7aBa3861c04C0e6388Fc02AF1F227f`](https://bscscan.com/token/0x97B6897AAd7aBa3861c04C0e6388Fc02AF1F227f)    | [`0x177ED4625F57cEa2804EA3A396c8Ff78f314F1CA`](https://bscscan.com/address/0x177ED4625F57cEa2804EA3A396c8Ff78f314F1CA)  |
| BNB Chain | Tron              | BTT — [`0x352Cb5E19b12FC216548a2677bD0fce83BaE434B`](https://bscscan.com/token/0x352Cb5E19b12FC216548a2677bD0fce83BaE434B)      | [`0x804F3893d3c1C3EFFDf778eDDa7C199129235882`](https://bscscan.com/address/0x804F3893d3c1C3EFFDf778eDDa7C199129235882)  |
| BNB Chain | Tron              | WIN — [`0xaeF0d72a118ce24feE3cD1d43d383897D05B4e99`](https://bscscan.com/token/0xaeF0d72a118ce24feE3cD1d43d383897D05B4e99)      | [`0x6536123503DF76BDfF8207e4Fb0C594Bc5eFD00A`](https://bscscan.com/address/0x6536123503DF76BDfF8207e4Fb0C594Bc5eFD00A)  |
| BNB Chain | Tron              | TRX — [`0xCE7de646e7208a4Ef112cb6ed5038FA6cC6b12e3`](https://bscscan.com/token/0xCE7de646e7208a4Ef112cb6ed5038FA6cC6b12e3)      | [`0x22af8a65639a351a9D5d77d5a25ea5e1Cf5e9E6b`](https://bscscan.com/address/0x22af8a65639a351a9D5d77d5a25ea5e1Cf5e9E6b)  |
| BNB Chain | Tron              | USDD — [`0xd17479997F34dd9156Deef8F95A52D81D265be9c`](https://bscscan.com/token/0xd17479997F34dd9156Deef8F95A52D81D265be9c)     | [`0x08e4AFd80A5849FDBa4bBeea86ed470D697e4C54`](https://bscscan.com/address/0x08e4AFd80A5849FDBa4bBeea86ed470D697e4C54)  |
| Ethereum  | Curve             | XVS — [`0xd3CC9d8f3689B83c91b7B59cAB4946B063EB894A`](https://etherscan.io/token/0xd3CC9d8f3689B83c91b7B59cAB4946B063EB894A)     | [`0x8473B767F68250F5309bae939337136a899E43F9`](https://etherscan.io/address/0x8473B767F68250F5309bae939337136a899E43F9) |
| Ethereum  | Curve             | CRV — [`0xD533a949740bb3306d119CC777fa900bA034cd52`](https://etherscan.io/token/0xD533a949740bb3306d119CC777fa900bA034cd52)     | [`0x5f65A7b60b4F91229B8484F80bc2EEc52758EAf9`](https://etherscan.io/address/0x5f65A7b60b4F91229B8484F80bc2EEc52758EAf9) |
| Ethereum  | Curve             | XVS — [`0xd3CC9d8f3689B83c91b7B59cAB4946B063EB894A`](https://etherscan.io/token/0xd3CC9d8f3689B83c91b7B59cAB4946B063EB894A)     | [`0x461dE281c453F447200D67C9Dd31b3046c8f49f8`](https://etherscan.io/address/0x461dE281c453F447200D67C9Dd31b3046c8f49f8) |
| Ethereum  | Liquid Staked ETH | XVS — [`0xd3CC9d8f3689B83c91b7B59cAB4946B063EB894A`](https://etherscan.io/token/0xd3CC9d8f3689B83c91b7B59cAB4946B063EB894A)     | [`0x7A91bEd36D96E4e644d3A181c287E0fcf9E9cc98`](https://etherscan.io/address/0x7A91bEd36D96E4e644d3A181c287E0fcf9E9cc98) |
| Ethereum  | Liquid Staked ETH | wstETH — [`0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0`](https://etherscan.io/token/0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0)  | [`0xe72Aa7BaB160eaa2605964D2379AA56Cb4b9A1BB`](https://etherscan.io/address/0xe72Aa7BaB160eaa2605964D2379AA56Cb4b9A1BB) |
| Ethereum  | Liquid Staked ETH | USDC — [`0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48`](https://etherscan.io/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48)    | [`0xDCB0CfA130496c749738Acbe2d6aA06C7C320f06`](https://etherscan.io/address/0xDCB0CfA130496c749738Acbe2d6aA06C7C320f06) |
| Ethereum  | Liquid Staked ETH | XVS — [`0xd3CC9d8f3689B83c91b7B59cAB4946B063EB894A`](https://etherscan.io/token/0xd3CC9d8f3689B83c91b7B59cAB4946B063EB894A)     | [`0x1e25CF968f12850003Db17E0Dba32108509C4359`](https://etherscan.io/address/0x1e25CF968f12850003Db17E0Dba32108509C4359) |
| Arbitrum  | Liquid Staked ETH | XVS — [`0xc1Eb7689147C81aC840d4FF0D298489fc7986d52`](https://arbiscan.io/token/0xc1Eb7689147C81aC840d4FF0D298489fc7986d52)      | [`0x6204Bae72dE568384Ca4dA91735dc343a0C7bD6D`](https://arbiscan.io/address/0x6204Bae72dE568384Ca4dA91735dc343a0C7bD6D)  |

Not every deprecated pool had a RewardsDistributor deployment record. If your pool appears in the table, check each listed proxy for that pool; duplicate reward-token symbols represent distinct distributors and must not be deduplicated.

Before claiming, read the distributor's `comptroller()` and `maxLoopsLimit()`, then call `getAllMarkets()` on that Comptroller. Check `isMarketListed(<vToken address>)` for every returned market:

* If every returned market is still listed and the number of markets does not exceed `maxLoopsLimit()`, use **Write as Proxy** and call the one-argument `claimRewardToken(address)` with your address.
* If any returned market is unlisted, the one-argument overload will revert because it passes the complete historical market array to the claim logic. If the array is longer than `maxLoopsLimit()`, it will also revert. Instead, use `claimRewardToken(address,address[])`: pass your address first, then all verified listed vToken addresses returned by that Comptroller. Do not include an unlisted vToken, and split the list into separate calls no larger than `maxLoopsLimit()` if necessary.

Both overloads update rewards for the markets they process and then attempt to pay the distributor's recorded aggregate accrued amount. The two-argument fallback does not calculate additional pending rewards from omitted or unlisted markets. If rewards from an unlisted market have not already been recorded, the current claim path cannot update them; ask for help through the [Venus community forum](https://community.venus.io/) because a protocol or governance action may be required.

Each call covers only that proxy's reward token and Comptroller. It does not claim other reward tokens, other distributors, other pools, or other networks. No token approval is required.

After confirmation, verify the reward-token transfer. If the distributor does not hold enough reward tokens, the contract retains the accrued amount instead of making a partial payment. Claiming rewards is optional and independent of repaying and redeeming positions.


# Boost and Repay with Collateral

Boost and Repay with Collateral let users open or reduce leveraged Core Pool positions in a single transaction where the feature is available. Both operations use a flash loan internally, but the resulting position is an ordinary supplied-and-borrowed Venus position. It accrues interest and can be liquidated.

{% hint style="warning" %}
**Version and deployment boundary (BNB Chain).** The behavior in this guide is mapped to `venus-periphery` source commit `c11d10190fb20394a4529872be3aebf34de0db4c` and its committed `deployments/bscmainnet/LeverageStrategiesManager_Implementation.json` artifact. That artifact records candidate implementation `0xB8c418eFf558D7eE517b8f26B5Eb0F4f3c53F5D5`, deployed at block `105684846`, for proxy `0x03F079E809185a669Ca188676D0ADb09cbAd6dC1`.

An artifact and deployment receipt do not prove which implementation or configuration the proxy currently uses. Before acting on this guide, verify at one recorded BNB Chain block the proxy implementation and code hash, owner and ACM permissions, flash-loan authorization and pause state, supported markets, per-market flash-loan configuration, and SwapHelper.
{% endhint %}

The source-and-artifact-mapped implementation uses one flash loan per operation. It does not repeat an internal borrow-swap-supply loop.

> Boost and Repay with Collateral are high-risk, money-moving operations. Check the selected markets, fee, minimum swap output, resulting debt, and projected health factor before signing. Leave a safety buffer rather than targeting the displayed maximum.

## Boost

Boost increases a user's supplied collateral and borrow balance in one atomic transaction.

### How Boost works

For a cross-asset Boost, the leverage manager:

1. Takes one flash loan of the selected borrowed asset.
2. Swaps that asset into the selected collateral asset through the configured swap helper.
3. Supplies the received collateral to Venus on behalf of the user.
4. Borrows the flash-loan fee on behalf of the user and uses it for settlement.
5. Allows the unpaid flash-loan principal to become the user's ordinary borrow balance.
6. Checks the user's borrowing power after the operation.

For a same-asset Boost, the swap step is omitted: the flash-loaned asset is supplied directly as collateral. The fee is still borrowed on behalf of the user, and the principal still becomes ordinary debt.

The user must authorize the leverage manager as a delegate. The operation also depends on current market listing, pause state, oracle prices, collateral factors, caps, available cash, token behavior, and, for cross-asset Boost, the minimum swap output.

The transaction is atomic: if a required check or external call reverts, the state changes made by that operation roll back. Transaction gas is still paid. Token approvals or delegation transactions submitted earlier remain in effect.

### Supported markets

Use only markets offered by the Boost interface. The source-and-artifact-mapped manager rejects `vBNB`, the native BNB Core Pool market; verify the live proxy and interface before relying on that support boundary. Do not treat a reference to BNB as support for `vBNB`; a wrapped-token market, if offered, is a different market.

## Repay with Collateral

Repay with Collateral reduces debt using one selected supplied asset, without requiring the borrowed asset to be held in the user's wallet before the operation.

The source-and-artifact-mapped manager processes one collateral market and one borrowed market per transaction:

1. It takes a flash loan of the borrowed asset.
2. It repays up to the selected amount of the user's debt.
3. It redeems the selected collateral on behalf of the user.
4. If the collateral and borrowed assets differ, it swaps the redeemed collateral into the borrowed asset.
5. It requires the resulting funds to cover the flash-loan settlement and fee.
6. It checks the user's borrowing power after the operation and returns any positive token-balance delta produced by that operation to the initiator.

Repay with Collateral can revert if delegation, redemption, market liquidity, the swap quote, slippage, flash-loan settlement, or the final safety check fails. A maximally leveraged position is not guaranteed to be fully closable with any selected collateral or quote.

## Estimating a Boost amount

There is no single formula that produces a safe Boost amount for every account or market. The result depends on the whole account and current market configuration, including:

* collateral and borrowed-asset prices;
* collateral factors and liquidation thresholds;
* existing supplies and borrows;
* the flash-loan fee and token-unit rounding;
* borrow and supply caps;
* available market cash and accrued interest; and
* quoted swap output, price impact, and slippage for cross-asset Boost.

For a simplified same-asset example, let:

* `S` be the existing supplied amount;
* `D` be the existing debt;
* `c` be the collateral factor;
* `B` be the flash-loan principal used for Boost; and
* `r` be the flash-loan fee rate for that market.

After the operation, the simplified balances are:

```
New supply = S + B
Flash-loan fee = r × B
New debt = D + B + (r × B)
```

The collateral-factor constraint is therefore:

```
c × (S + B) >= D + B + (r × B)
```

Solving only this simplified constraint gives an algebraic boundary:

```
B <= (c × S - D) / (1 - c + r)
```

This boundary is not a recommended amount. It has no execution or liquidation buffer and does not include the other constraints listed above. Health factor also uses liquidation thresholds, while borrowing power uses collateral factors; they are not interchangeable.

### Example: same-asset USDT Boost

Assume only for this example:

* 100 USDT supplied and enabled as collateral;
* 20 USDT already borrowed;
* an 80% collateral factor; and
* an illustrative 0.09% flash-loan fee.

The unused borrowing power before Boost is:

```
(100 × 0.8) - 20 = 60 USDT
```

The fee-omitting calculation `60 / (1 - 0.8) = 300 USDT` is not safe. Even with a zero fee, it lands exactly on the collateral-factor boundary. With the illustrative 0.09% fee:

```
Fee on 300 USDT = 0.27 USDT
Supply after Boost = 100 + 300 = 400 USDT
Debt after Boost = 20 + 300 + 0.27 = 320.27 USDT
Collateral-factor capacity = 400 × 0.8 = 320 USDT
```

The resulting debt exceeds the simplified borrowing capacity, so the operation should revert rather than create the advertised position.

The fee-aware algebraic boundary is approximately:

```
60 / (1 - 0.8 + 0.0009) = 298.65604778 USDT
```

That value still lands on the simplified boundary and should not be used as a target. Choose a lower amount, keep a meaningful safety margin, and verify the final wallet simulation before signing. An interface preview is an estimate, not an execution or liquidation guarantee.

## Risks

### Liquidation risk

Leverage magnifies losses and reduces the distance to liquidation. Under the interface convention, a health factor at or below 1 is the liquidation boundary. A health factor near 1, including below 1.1, means the position has a thin buffer; 1.1 is not itself the liquidation trigger.

Monitor health factor after execution. Price moves, interest accrual, and oracle updates can make a previously valid position liquidatable.

### Amplified price exposure

If one asset drives the entire exposure, debt is fixed, and liquidation has not intervened, a 10% adverse asset move produces an approximate `10% × leverage` loss of starting equity. This simplified relationship is not a prediction: cross-asset correlations, interest, fees, swaps, and liquidation materially change outcomes.

### Swap and execution risk

Cross-asset operations depend on external swap execution. High price impact, insufficient output, token behavior, or an external-call failure can revert the transaction. A revert rolls back that operation, but the user pays gas, and earlier approvals or delegations remain active until revoked.

### Oracle and market risk

Oracle prices, market cash, caps, pause state, and interest rates can change. Oracle problems can cause a failed operation or unexpected liquidation. High or rising borrow rates can make a leveraged strategy unprofitable even when asset prices do not move.

## Safer operating practices

* Use only funds you can afford to lose.
* Confirm the exact collateral and borrowed markets; do not assume native BNB support.
* Leave a safety margin instead of selecting the maximum.
* Review fee, minimum swap output, debt, collateral, and health factor before signing.
* Monitor health factor and borrow rates after execution.
* Revoke approvals or delegation when they are no longer needed.


# Trade

Venus Trade lets you express a view that one asset will outperform another, without needing to predict absolute price direction. This guide walks through the full lifecycle of a Trade position — from opening to closing — and explains all available management actions along the way.

***

## What Is a Trade Position?

A Trade position is made up of two legs that are managed as a single unit:

* **Long Leg** — an asset you believe will outperform, supplied into Venus to earn lending yield
* **Short Leg** — an asset you believe will underperform, borrowed from Venus

When you open a position, the protocol borrows the short asset, swaps it into the long asset, and supplies it — all in a single transaction. When you close, the process reverses automatically.

All profits and losses are settled in your chosen stablecoin (**DSA** — Default Settlement Asset), which is either USDT or USDC.

***

## Step 1: Choose a Trading Pair

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-9f58bf344c07efe6ef6209e29b5a1dadcb3c8fb8%2Ftrade-choose-trading-pair.png?alt=media" alt="Trading pairs overview"><figcaption></figcaption></figure>

Browse the available trading pairs. Each pair shows:

* The current relative price between the long and short asset
* Available liquidity on both the long (supply) and short (borrow) side
* Supply APY, and borrow APY

Pick the pair that matches your market view. For example, if you believe WBNB will outperform ETH, select the WBNB/ETH pair (WBNB = long, ETH = short). You can also go purely long or short on a single asset by pairing it with a stablecoin.

{% hint style="info" %}
The long asset must have a Collateral Factor (CF) > 0 in the Venus Core Pool. The short asset must be available for borrowing in the Venus Core Pool.
{% endhint %}

***

## Step 2: Initialize Your Position

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-022f591aa5f36221bd6d3d60acee32df567e7090%2Ftrade-initialize-position.png?alt=media" alt="Initialize position form"><figcaption></figcaption></figure>

This step activates a new position account for the selected pair and opens your first trade in one atomic transaction.

**What to configure:**

| Field                  | Description                                                                                                                                    |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| **DSA**                | Choose USDT or USDC as your collateral and settlement currency. This cannot be changed later without fully exiting.                            |
| **Collateral Amount**  | The amount of DSA to deposit. This is your initial principal.                                                                                  |
| **Leverage**           | Your exposure multiplier. Higher leverage amplifies both gains and losses. The maximum depends on the collateral factors of the chosen assets. |
| **Short Amount**       | The amount of the short asset to borrow. Cannot exceed the maximum allowed by your available capital and leverage.                             |
| **Slippage Tolerance** | Minimum long asset amount you expect from the swap. The transaction reverts if the swap output is below this.                                  |

Select your desired leverage multiplier and set the short amount to borrow. The UI displays the maximum leverage available based on the collateral factors of the selected assets.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-0943603c486b8520083854418254329a0c8d7ac7%2Ftrade-initialize-preview.png?alt=media" alt="Selecting leverage and short amount"><figcaption></figcaption></figure>

**Before submitting:**

* Review the estimated Assets amount changes, Health Factor, entry price (short/long ratio), liquidation price, and Net APY
* Approve the DSA token spend on the RelativePositionManager

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-f3ab1b2ee0eea217eb2b12b543d1e69c2f30eeeb%2Ftrade-initialize-confirm.png?alt=media" alt="Confirm transaction"><figcaption></figcaption></figure>

**What happens on-chain:**

1. A dedicated PositionAccount is deployed for this trading pair (first time only)
2. Your DSA collateral is supplied into Venus on behalf of the PositionAccount
3. The short asset is borrowed via a flash loan
4. The short asset is swapped into the long asset
5. The long asset is supplied to Venus as leveraged collateral
6. Any dust from the swap is returned to your wallet

{% hint style="info" %}
Leverage and DSA cannot be changed while a position is open. To adjust either, you must fully close all positions, withdraw all collateral, and deactivate (Exit Market), then re-initialize.
{% endhint %}

***

## Step 3: Monitor Your Position

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-8754548008328c1e91cd95373b506927e64144da%2Fyield-plus-monitor-position.png?alt=media" alt="Position monitoring dashboard"><figcaption></figcaption></figure>

Once open, track your position from the dashboard:

| Metric                | Description                                                                            |
| --------------------- | -------------------------------------------------------------------------------------- |
| **PnL %**             | Relative price performance since entry, amplified by leverage                          |
| **PnL (USD)**         | Absolute profit or loss converted to USD                                               |
| **Long Supply**       | Current supplied balance of the long asset                                             |
| **Short Borrow**      | Current outstanding borrow of the short asset                                          |
| **Health Factor**     | How close to liquidation your position is. HF > 1 is safe; HF < 1 triggers liquidation |
| **Entry Price (EP)**  | The short/long ratio at the time of opening                                            |
| **Liquidation Price** | The short/long ratio at which liquidation would occur                                  |
| **Net APY**           | DSA APY + Supply APY − Borrow APY                                                      |
| **Available Capital** | Deposited collateral minus capital currently locked by open positions                  |
| \*\* Collateral\*\*   | Collateral allocated as DSA for this position                                          |

{% hint style="warning" %}
Each position has its own independent Health Factor. If you hold multiple positions, monitor each one individually. The liquidation price may fluctuate as the prices of both legs and the DSA change; always **monitor your Health Factor** closely to avoid unexpected liquidation.
{% endhint %}

***

## Step 4: Manage Your Position

While a position is active, four management actions are available.

### Increase Position (Scale)

Adds to your existing position using available collateral or additional DSA principal. The same leverage ratio applies.

Use this when you want to increase your relative exposure without changing your DSA, leverage, or position account.

**How it works:**

* Based on the current available DSA collateral, the system borrows more of the short asset and swaps it into additional long collateral.
* Your leverage ratio stays the same, capital utilization will increase

### Supply Collateral

Deposits additional DSA into your PositionAccount without opening a new trade. This increases your Available Capital and improves your Health Factor without changing your position size.

Use this when your Health Factor is declining and you want to reduce liquidation risk without unwinding any exposure. The deposited DSA earns supply APY immediately and increases the buffer between your current borrow value and the liquidation threshold.

### Reduce Position

Closes a percentage of your position (from 1% to 100%) and settles PnL.

You can also use a small partial reduce (e.g. 5–20%) as a Health Factor management tool: closing part of the position repays a proportional slice of the short debt, which directly raises your Health Factor without requiring a full exit.

* **With profit:** The excess long collateral (above what's needed to repay the debt fraction) is swapped back to DSA and added to your principal balance
* **With loss:** The long collateral is swapped to repay as much of the debt fraction as possible, and remaining debt is covered by redeeming DSA collateral

After reducing, your collateral remains in the PositionAccount. You can open new trades or withdraw at any time.

{% hint style="info" %}
A position remains visible even after a full reduce. This lets you re-enter the same market without deploying a new PositionAccount. To fully exit, use **Close Market**.
{% endhint %}

### Withdraw Collateral

Removes Available Capital from your PositionAccount back to your wallet.

Only the portion of collateral not locked by an open position can be withdrawn. The withdrawable amount decreases as your position size increases and increases as you reduce or add collateral.

Withdrawing collateral reduces your Health Factor. Always ensure it remains above 1.0 after any withdrawal to avoid liquidation.

***

## Step 5: Close and Deactivate

**Close Market** fully exits the trading pair in one transaction:

1. Closes all remaining open positions and settles PnL (same as a 100% reduce)
2. Exits the DSA market and withdraws all collateral to your wallet
3. Deactivates the PositionAccount

After deactivation, you can re-initialize at any time — with a different DSA, different leverage, or different collateral amount.

***

## Understanding PnL

Trade PnL is based on the **relative price movement** between your two assets, amplified by leverage.

### Formula

$$
\text{PnL%} = \left(\frac{R\_t}{R\_0} - 1\right) \times \text{leverage}
$$

$$
\text{PnL (USD)} = \left(R\_t \times \text{Long Amount} - \text{Short Amount}\right) \times \text{Price(Short)}
$$

Where:

* $R\_0$ = Entry price ratio (short asset / long asset at open)
* $R\_t$ = Current price ratio

### Example

You open a WBNB/ETH position:

* Entry ratio ($R\_0$): 0.50 ETH per BNB
* Leverage: 2x
* Short Amount: 5 ETH, Long Amount: 10 BNB

**If BNB outperforms (ratio moves to 0.55):** PnL% = (0.55 / 0.50 − 1) × 2 = **+20%**

**If ETH outperforms (ratio drops to 0.45):** PnL% = (0.45 / 0.50 − 1) × 2 = **−20%**

***

## Understanding Risks

### Liquidation Risk

Trade uses the same liquidation mechanism as Venus Core. If your Health Factor falls below 1, a third-party liquidator can repay part of your borrow and seize a portion of your collateral.

To manage liquidation risk:

* **Monitor your Health Factor regularly** and track the current ratio vs. your liquidation price
* **Supply more DSA principal** (via Supply Collateral) to improve your Health Factor without changing position size
* **Partially reduce your position** (e.g. 5–20%) to repay a slice of short debt, which raises the Health Factor directly
* Understand that leverage amplifies both gains and losses — a 2x leveraged position has twice the liquidation sensitivity

### Slippage

Opening and closing positions involve on-chain swaps, which are subject to slippage. Set a realistic slippage tolerance before confirming. In extreme market conditions, a transaction may revert if the swap cannot meet the minimum output; you only lose gas in that case.

### Market Risk

Relative performance positions can still lose money. If the asset you are shorting outperforms the asset you are longing, your PnL will be negative. Leverage amplifies both gains and losses symmetrically.

### Interest Rate Risk

You pay borrow interest on the short leg continuously. If the borrow rate rises significantly, your Net APY can turn negative, increasing the effective cost of holding the position.

### Smart Contract Risk

Trade is built on top of Venus's audited lending and borrowing infrastructure. No changes were made to core contracts, risk parameters, or liquidation engines. The new RelativePositionManager and PositionAccount contracts have been independently audited. Audit reports are available in the [Security & Audits](/links/security-and-audits) section.

***

## Frequently Asked Questions

**Can I change my DSA after opening a position?** No. Your DSA is locked while the position is active. To change it, fully close all positions, withdraw all collateral, and deactivate (Exit Market), then re-initialize with a different DSA.

**Can I change my leverage after opening?** No. Leverage is fixed for the lifecycle of each activation cycle. Close, deactivate, and re-activate with the new leverage setting.

**What happens if my position gets liquidated?** Liquidation works the same as Venus Core. A liquidator repays part of your borrow and seizes a portion of your collateral at a discount. After liquidation, your position remains open but with a reduced size. You can continue managing it normally.

**Is my collateral shared across different positions?** No. Each trading pair has its own isolated PositionAccount. A liquidation on one pair does not affect your positions on other pairs.

**What does "Available Capital" mean?** Available Capital = your total deposited collateral minus the amount currently locked by your open position. This is how much you can either use to open additional exposure or withdraw from the account.

**Can I have multiple positions on the same pair?** Each pair has one PositionAccount per wallet. Within that account, you can increase or decrease your position size, but it is managed as a single position.

**What fees do I pay?** You pay standard Venus Protocol borrowing interest on the short leg. On-chain swaps during open and close incur normal DEX trading fees and slippage. Currently, there are no additional platform fees specific to Trade.

**What is the difference between "Reduce Position" and "Close Market"?**

* **Reduce Position** closes your long/short trade and settles PnL, but leaves your collateral in the PositionAccount. You can re-enter the same market immediately.
* **Close Market** does everything at once — closes all positions, settles PnL, withdraws all collateral to your wallet, and deactivates the account.

***

## Glossary

| Term                    | Definition                                                                                         |
| ----------------------- | -------------------------------------------------------------------------------------------------- |
| **DSA**                 | Default Settlement Asset — the stablecoin (USDT or USDC) used as collateral and for PnL settlement |
| **Long Leg**            | The asset you believe will outperform, supplied into Venus                                         |
| **Short Leg**           | The asset you believe will underperform, borrowed from Venus                                       |
| **Leverage**            | Multiplier applied to your position size relative to your collateral; fixed at activation          |
| **Health Factor (HF)**  | Position safety metric; liquidation occurs when HF < 1                                             |
| **Entry Price**         | The short/long ratio at the time of opening                                                        |
| **Liquidation Price**   | The short/long ratio at which liquidation is triggered                                             |
| **Capital Utilization** | How much of your deposited collateral is locked by open positions                                  |
| **Available Capital**   | Deposited collateral minus capital utilization; available to trade or withdraw                     |
| **Net APY**             | Supply APY on the long asset plus DSA APY minus borrow APY on the short asset                      |
| **PositionAccount**     | A dedicated on-chain smart contract per wallet per trading pair; auto-deployed on first trade      |
| **Increase Position**   | Add to your existing position using available capital at the same leverage                         |
| **Reduce Position**     | Partially or fully reduce the long/short size; PnL is settled into DSA collateral                  |
| **Close Market**        | Fully close all positions, settle PnL, withdraw all collateral, and deactivate the PositionAccount |


# Fixed Term Vaults

Fixed Term Vaults facilitate fixed-term loans between an institution and on-chain suppliers. The institution borrows a configured supply asset against collateral at a scheduled rate and duration. Suppliers receive transferable vault shares representing a claim on the assets available when that vault reaches a terminal state.

{% hint style="warning" %}
**Principal and target return are not guaranteed.** A target APR is a scheduled display value, not a promise of payment. Counterparty performance, collateral value, liquidation, pause state, token behavior, shared system dependencies, administrative actions, and the assets actually available at settlement can affect when and how much a supplier can redeem.
{% endhint %}

Each loan uses a separate vault clone with its own assets, debt accounting, configuration, and supplier shares. It does not share liquidity with Venus Core Pool or another vault clone, but its on-chain behavior relies on shared controller, oracle, liquidation, ProtocolShareReserve, and access-control components. Users also depend operationally on the interface and its indexed data.

The product has two sides:

* **Institution** (borrower) — deposits collateral and may claim raised funds, repay debt, and recover collateral subject to the vault state and health checks.
* **Supplier** — supplies the loan asset during fundraising and later redeems a pro-rata share of the assets available in a terminal state.

Current published production deployments are on BNB Chain. Deployment addresses, vault registry entries, implementations, state, terms, risk parameters, pause level, and permissions are live data. A vault card or documentation page does not prove that a vault is currently open for supply: an instance can be pre-fundraising, locked, settling, terminal, or closed. Verify the selected vault before signing.

## Pick Your Guide

| Role                                                                                                                                           | Guide                                                            |
| ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| **Institution** — you want to borrow against collateral at a fixed rate and need to get a vault from Venus and walk through the full lifecycle | [Institution Guide](/guides/fixed-rate-vaults/institution-guide) |
| **Supplier** — you want to supply during fundraising and understand the lock, settlement, and recovery paths                                   | [Supplier Guide](/guides/fixed-rate-vaults/supplier-guide)       |

For contract behavior, see the [Fixed Term Vaults Technical Reference](/technical-reference/reference-technical-articles/fixed-rate-vaults). For address discovery, start with [Deployed Contracts](/deployed-contracts/fixed-rate-vaults) and then confirm the controller registry and chain explorer at a recorded block.


# Institution Guide

This guide explains the institution side of a Fixed Term Vault: accepting a vault allocation, posting margin and collateral, claiming the raised supply asset, servicing the debt, and recovering eligible collateral.

{% hint style="danger" %}
A Fixed Term Vault is a time-sensitive, collateralized obligation. Missing a funding, collateral, or repayment deadline can make funds unavailable, forfeit the configured margin, or expose collateral to liquidation. Contract state, permissions, prices, and pause status are authoritative; an interface label or off-chain agreement does not override them.
{% endhint %}

## Before You Operate a Vault

Vault allocation, eligibility, and commercial terms are coordinated off-chain. Before moving funds, obtain the current terms through the official Venus process and independently verify the resulting on-chain vault.

For the specific vault clone, record and check:

* the vault and controller addresses against the controller registry, rather than relying on a screenshot or an address copied from a message;
* the current owner and token ID of the position NFT;
* the supply and collateral token addresses, symbols, and decimals;
* the margin rate, ideal collateral amount, caps, fundraising duration, lock duration, settlement window, and scheduled rate;
* the liquidation threshold, liquidation incentive, late-penalty rate, oracle, stored state, timestamps, outstanding debt, and pause level; and
* the clone implementation and whether it supports the functions described in this guide.

Use [Deployed Contracts](/deployed-contracts/fixed-rate-vaults) as a starting point, then verify the live controller registry and clone at a recorded block. Repository deployments and frontend data are useful references, but neither proves current on-chain state by itself.

The institution-side functions `depositCollateral`, `claimRaisedFunds`, and `withdrawCollateral` require `msg.sender` to be the **current owner** of the vault's position NFT. An ERC-721 approval or delegated operator is not a substitute for ownership in the published implementation. The NFT is therefore the control credential for the position.

Every NFT transfer requires an ACM-authorized controller caller to approve one exact recipient first. That approval is consumed by the matching transfer. After transfer, the new owner immediately controls the institution-side functions and receives any later cancellation refund; the prior owner loses that control.

## Step 1: Deposit the Margin

The vault begins in `WaitingForMargin`. Approve the vault clone—not the controller—to spend the configured collateral asset, then call:

```
depositCollateral(amount)
```

The first call must make `totalCollateralDeposited` at least:

```
floor(idealCollateralAmount × marginRate / 1e18)
```

Because a sub-threshold call reverts, post at least the full required margin in one transaction. The function accepts collateral-token base units and does not cap the amount at the threshold, so check the asset address and decimals before approving or submitting. The published implementation does not support fee-on-transfer or rebasing collateral tokens.

On success, the state changes to `MarginDeposited`. Check the receipt, `CollateralDeposited` event, recorded collateral total, and remaining allowance. Reduce or revoke an allowance that is no longer needed.

If the vault is not opened, an ACM-authorized controller caller may cancel it while it is still `WaitingForMargin` or `MarginDeposited`. Cancellation sends the recorded collateral to the current position-NFT owner and moves the vault to `Failed`.

## Step 2: Wait for the Vault to Open

An ACM-authorized caller opens a `MarginDeposited` vault through `InstitutionalVaultController.openVault(vault)`. Opening sets the absolute fundraising end, lock end, and settlement deadline from the clone's configured durations and moves the vault to `Fundraising`.

Do not calculate deadlines from an expected opening time. Read the timestamps emitted and stored by the live vault after `openVault` succeeds.

## Step 3: Complete the Collateral During Fundraising

Before the fundraising end, call `depositCollateral(amount)` as needed to bring `totalCollateralDeposited` to at least `idealCollateralAmount`. Use the vault clone as the collateral-token approval spender for every top-up.

At the fundraising deadline, the vault evaluates both sides:

| Result                                                                          | Institution outcome                                                                                                                                             |
| ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Raised amount meets `minBorrowCap` and collateral meets `idealCollateralAmount` | The vault can enter `Lock`                                                                                                                                      |
| Raised amount is below `minBorrowCap`                                           | The vault enters `Failed`; the position holder can recover all recorded collateral, including the margin                                                        |
| Raise succeeds but collateral is below `idealCollateralAmount`                  | The vault enters `Failed`; the configured margin is reserved for supplier compensation, and only the remaining collateral is recoverable by the position holder |

The stored state does not advance merely because time passes. Anyone may call `updateVaultState()` to apply a ready transition. However, calling `depositCollateral` after the deadline does not create a grace period. If collateral was below the ideal amount at the boundary, the call evaluates the failure condition and the late top-up cannot rescue it. If both requirements were already met at the boundary, the same selector can instead serve as a `Lock`-state top-up after state advancement.

{% hint style="warning" %}
Partial and complete pause both block collateral deposits. Monitor the pause level and leave operational time before the deadline; a transaction submitted near the boundary may execute too late.
{% endhint %}

## Step 4: Claim During the Lock Window

After a successful fundraise, state advancement enters `Lock`. At that point the vault records the scheduled full-term interest as debt. The current position-NFT owner may make one all-or-nothing claim:

```
claimRaisedFunds()
```

The call is available only from `lockStartTime` inclusive to `lockEndTime` exclusive, while the effective state is `Lock` and the vault is unpaused. Before transferring the entire raised supply-asset amount, it simulates the post-claim debt—scheduled interest plus principal—against the current collateral and liquidation threshold. It reverts if the claim would create an LT shortfall.

Check `getHypotheticalVaultLiquidity(0, totalRaised)` and current oracle data before claiming, but treat a simulation as a point-in-time estimate. Prices, risk parameters, balances, state, and pause status can change before execution. Adding collateral or repaying existing interest can improve the check; confirm the result again immediately before the claim.

If the lock end passes before the claim succeeds, the principal stays in the vault and can no longer be claimed through this path. The institution still owes the scheduled interest that was recorded at lock entry, but it does **not** owe unclaimed principal. There is no “late claim”: state advancement moves the vault to settlement states and `claimRaisedFunds()` then reverts.

After a successful claim, outstanding debt becomes the raised principal plus the full scheduled interest. Repaying early does not discount that interest.

## Step 5: Manage Health and Repay

During `Lock`, the position-NFT owner can add collateral with `depositCollateral(amount)`. It can withdraw only collateral that remains above both:

1. the minimum collateral floor, scaled to the actual amount raised; and
2. the liquidation-threshold health requirement after the proposed withdrawal.

Use `getVaultLiquidity()` and `getHypotheticalVaultLiquidity(withdrawAmount, 0)` before attempting a withdrawal. Oracle movement between simulation and execution can still cause a revert or create liquidation exposure after the transaction.

Debt service is a separate defense. Any address—not only the NFT owner—may approve the **supply asset** to the vault clone and call:

```
repay(amount)
```

Repayment is accepted in `Lock`, `PendingSettlement`, and `SettlementDeadlineExceeded`, and an amount above `outstandingDebt()` is clamped to the debt at execution. The published implementation does not support fee-on-transfer or rebasing supply assets.

A partial repayment reduces debt and can improve health, but it does not necessarily unlock collateral below the minimum floor and does not eliminate remaining liquidation or late-payment risk. Only full repayment clears the debt. Even after full early repayment, the vault remains in `Lock` until the lock end; it does not mature early or release the entire collateral immediately.

At the lock end, the vault enters `PendingSettlement`. If the debt is zero, it can advance to `Matured`; otherwise repayment remains available through the settlement window. Once the settlement deadline passes with debt outstanding, whitelisted settlement actors can use the overdue-liquidation path even if the vault has no LT shortfall. Health-based liquidation can also occur in active debt states when an LT shortfall exists.

Partial pause still allows repayment and liquidation. Complete pause blocks both until unpaused. Check the live pause level before relying on a defensive transaction.

## Step 6: Recover Eligible Collateral

The current position-NFT owner can call `withdrawCollateral(amount)`:

* in `Lock`, only for an amount allowed by both the floor and LT checks;
* in `Matured`, up to the remaining recorded collateral; or
* in `Failed`, up to the recoverable balance after any confiscated margin is reserved.

It cannot use `withdrawCollateral` in `PendingSettlement`, `SettlementDeadlineExceeded`, `Liquidated`, or `Closed`. Do not transfer or discard the position NFT before all intended institution-side recovery is complete. Verify the terminal state, recorded collateral, token balance, recipient, and pause level, and avoid assuming that a successful supplier settlement automatically sends collateral back to the institution.

An ACM-authorized controller caller can move a `Matured`, `Failed`, or `Liquidated` vault to `Closed`. The published `closeVault` path does not verify that the institution has recovered its collateral, and `withdrawCollateral` is unavailable after closing. Recover eligible collateral promptly and monitor controller actions.

## Parameters That Can Change

The supply asset, collateral asset, scheduled rate, reserve factor, caps, minimum supplier deposit, lifecycle durations, ideal collateral amount, and margin rate are fixed in an existing clone. A renewed deal requires another clone.

The liquidation threshold, liquidation incentive, late-penalty rate, and institution display name can be changed for an existing vault through ACM-gated controller calls. Shared controller, oracle, LiquidationAdapter, treasury, ProtocolShareReserve, pause, whitelist, and ACM configuration can also change. Monitor the live values throughout the position rather than relying only on the creation terms.

The controller also exposes an ACM-gated `sweep(vault, token)` path that transfers the vault's full balance of the specified ERC-20 to the configured treasury. The published source does not exclude the supply or collateral asset. Treat the live controller implementation, treasury, and effective ACM grants as material administrative authorities.

## Operational Checklist

* Keep the position NFT in a wallet whose transaction process can meet short deadlines; custody transfer changes contract control.
* Verify the exact vault, asset, amount, decimals, spender, state, deadline, pause level, and oracle health before every transaction.
* Leave time for approvals, multisig execution, state finalization, and retries before fundraising and settlement boundaries.
* Monitor both health and debt. Collateral top-ups and repayments affect different sides of the risk calculation.
* Confirm receipts and events, then reduce unnecessary token allowances.
* Escalate a pause, stale oracle, failed health simulation, or unexpected state through official Venus channels before moving more funds.

For the complete state machine, settlement waterfall, rounding, pause rules, liquidation paths, and administrative authorities, see the [Fixed Term Vaults Technical Reference](/technical-reference/reference-technical-articles/fixed-rate-vaults). The current source boundary used for this guide is [`InstitutionalLoanVault.sol@91611be`](https://github.com/VenusProtocol/fixed-rate-vaults/blob/91611be77036e05d16f1ce43890b160cb4bc7228/src/institutional-vault/InstitutionalLoanVault.sol) together with [`BaseVault.sol@91611be`](https://github.com/VenusProtocol/fixed-rate-vaults/blob/91611be77036e05d16f1ce43890b160cb4bc7228/src/BaseVault.sol). Immutable historical clones may use an earlier implementation. Verify the live clone, shared contracts, configuration, state, balances, and permissions at a recorded block before acting.


# Supplier Guide

This guide explains how to supply an asset to a Fixed Term Vault and later redeem the resulting vault shares. Supplying funds a single institutional loan; it is not a deposit into Venus Core Pool or shared liquidity.

{% hint style="danger" %}
**Capital and availability are at risk.** The displayed Target APR is not guaranteed, and a supplier can receive less than the amount supplied. There is no contract withdrawal after you deposit during Fundraising or while the loan is active. Redemption depends on the vault reaching a terminal state, its current pause level, and the assets available for settlement.
{% endhint %}

The official interface and deployed state can change after this page is published. Treat screenshots, examples, and static address lists as illustrative only. Verify the selected vault in the live app, controller registry, and chain explorer.

## What the vault shares represent

Each Fixed Term Vault is a separate ERC-4626-based clone with vault-specific overrides. When you supply the configured asset, the vault mints ERC-20 shares representing your pro-rata claim on that clone's settlement assets.

The shares can be transferred, but transferability is not an early redemption mechanism. Venus does not guarantee a buyer, secondary market, price, or liquidity for the shares. A transfer moves the on-chain claim to the recipient and can introduce separate operational, legal, and counterparty risks.

## Before supplying

1. Open the official [Venus Vaults page](https://app.venus.io/#/vaults?chainId=56), select BNB Chain, and identify a card that the current interface labels **Fixed-Term**.
2. Verify the InstitutionalVaultController and candidate vault through [Deployed Contracts](/deployed-contracts/fixed-rate-vaults), the controller's current `allVaults` registry or `VaultCreated` events, and a BNB Chain explorer. A static list may omit vaults created later, while the app's indexed inventory can lag the on-chain registry.
3. Confirm that the exact vault is in **Fundraising**, its pause level is **Unpaused**, the supply deadline has not passed, and `maxDeposit(yourAddress)` is greater than zero.
4. Verify the supply token, collateral token, both token addresses and decimals, vault address, institution, fundraising minimum and maximum, current total raised, lock end, settlement deadline, and risk disclosures.
5. Review the live **Target APR**. The official interface already displays this value net of the vault's reserve factor. Do not deduct the reserve factor a second time.
6. Read the current [Fixed Term Vault Terms of Use](https://app.venus.io/#/fixed-term-vault-terms-of-use) shown by the app and determine whether you are eligible. Do not rely on a jurisdiction list copied into a guide, because the applicable terms can change.

Some commercial configuration, including the supply and collateral assets, scheduled rate, reserve factor, caps, and lifecycle durations, is set when a clone is created. An existing EIP-1167 clone keeps its original implementation; changing the controller's vault template affects future clones, not existing ones. Other state and dependencies remain live: the controller can update per-vault liquidation parameters and institution metadata, controller and shared-component implementations or addresses can change, and authorized callers can pause or administer a vault.

Fee-on-transfer and rebasing tokens are not supported as the supply or collateral asset. Do not infer support from a familiar token symbol; verify the exact address and implementation.

## Supply during Fundraising

1. Connect the intended wallet and open the vault's **Position** tab.
2. Enter an amount. The form limits the input using the wallet balance and remaining vault capacity; a configured minimum supplier amount can also apply. The minimum is waived for a final deposit that fills the residual capacity.
3. Approve the **vault clone** to spend the exact supply token. The controller, LiquidationAdapter, institution, and frontend address are not valid substitutes for this spender.
4. Review the approval separately. Confirm the chain, token, spender, and allowance before signing.
5. Accept the current Fixed Term Vault Terms in the form and submit **Supply**. The cited interface implementation calls `depositWithConsent(assets, yourAddress, consentHash)` for compatible vaults. This emits a `ConsentRecorded` event containing the hash of the displayed consent text before completing the deposit; if the deposit reverts, the event also rolls back. Historical clones can retain an older implementation without this selector, so verify the exact clone's implementation and ABI.
6. Verify the confirmed receipt, actual supply assets transferred, shares minted, vault state, and remaining allowance. Reduce or revoke an allowance you no longer need.

The contract clamps an oversized deposit to the capacity remaining at execution. Another transaction can change capacity before yours is mined, so the assets accepted and shares minted can be lower than the amount first requested. Do not treat wallet confirmation as proof of the final amount; inspect the receipt and balances.

{% hint style="warning" %}
Once a deposit succeeds, `withdraw` and `redeem` are unavailable even if the fundraising window is still open. They become available only in the terminal **Matured**, **Failed**, or **Liquidated** states. Do not supply funds that you may need before the latest plausible settlement or recovery date.
{% endhint %}

## Monitor the lifecycle

Vault states advance in one direction, but time passing alone does not write a new state on-chain. A relevant transaction, including the permissionless `updateVaultState()`, must persist a transition. The app can derive a display status from timestamps, so verify the stored state and the result of your transaction rather than relying only on a badge.

* **Fundraising** — new deposits can be accepted while the deadline, cap, minimum, token approval, and pause checks pass. Existing suppliers cannot withdraw.
* **Lock** — if both the funding and collateral requirements are met, the loan begins. Scheduled interest for the full term is calculated from the actual raise. Suppliers still cannot withdraw.
* **PendingSettlement / SettlementDeadlineExceeded** — debt repayment or liquidation is still pending. Passing the deadline does not itself give suppliers an exit or guarantee liquidation proceeds.
* **Matured / Failed / Liquidated** — supplier `withdraw` and `redeem` paths are enabled, subject to balances and the pause level. A Partial pause does not block terminal supplier exits; a Complete pause does.
* **Closed** — deposits, withdrawals, and redemptions are disabled by the vault state. The published design expects authorized callers to close only after suppliers have exited, but the source does not enforce a zero share supply before closure. Verify your redemption before assuming closure is safe.

You do not service the institution's debt, but you should monitor repayments, collateral and oracle conditions, the settlement deadline, liquidations, pause changes, administrative events, and the amount currently previewed for your shares.

## Redeem or withdraw in a terminal state

When the current interface shows **Claim**, **Refund**, or a liquidated withdrawal state:

1. Confirm the stored state is **Matured**, **Failed**, or **Liquidated**, or simulate that the exit call will advance a stale state to one of them in the same transaction. Confirm the vault is not completely paused.
2. If the stored state is already terminal, read `maxRedeem(yourAddress)`, `maxWithdraw(yourAddress)`, `previewRedeem(shares)`, and `previewWithdraw(assets)` immediately before submission. If the state is stale, simulate the exact exit transaction and read the views again after the state advances. Quotes can change as settlement and other redemptions are processed.
3. In the app, use **Claim** or **Withdraw**. Confirm the vault address, share amount, receiver, and previewed assets in your wallet or transaction simulation.
4. Verify the receipt, shares burned, supply asset received, and any collateral compensation received.

The outcome depends on the terminal path:

| Terminal state                            | Contract settlement path                                                                                                                                                                                                                                                                                                                                                                                               |
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Matured**                               | Suppliers redeem a pro-rata share of the supply asset available after settlement and the protocol reserve. With full repayment and no asset shortfall, this targets principal plus the displayed net interest; use the live preview for the actual amount.                                                                                                                                                             |
| **Failed — funding below minimum**        | No loan is made. Suppliers redeem their pro-rata share of the supply asset held for refunds, with no scheduled interest.                                                                                                                                                                                                                                                                                               |
| **Failed — collateral below requirement** | Suppliers redeem supply assets and receive a pro-rata distribution of the remaining confiscated margin in the collateral token. Each distribution rounds down to token units.                                                                                                                                                                                                                                          |
| **Liquidated**                            | Suppliers redeem a pro-rata share of the supply asset in `settlementAmount`. In the current published bad-debt path, the principal portion of debt must be covered before the transition, but suppliers still receive only the terminal settlement assets and do not have a guaranteed direct claim on all remaining collateral. Do not convert that source-level condition into an unconditional principal guarantee. |

All outcomes remain contingent on the vault's actual token balances, settlement accounting, pause level, and successful transaction execution. “Principal” in the first two failure descriptions is the intended accounting outcome when the expected assets remain available, not a guarantee by Venus or another party.

## Estimate target interest correctly

Suppose the app displays an **8% Target APR**, the lock duration is 90 days, and you supply 10,000 units of the supply asset. The displayed Target APR is already net of the reserve factor:

$$
\text{target net interest} \approx 10{,}000 \times 0.08 \times \frac{90}{365} \approx 197.26
$$

The corresponding target payout is approximately `10,197.26` supply-asset units. Do **not** subtract the reserve factor again. This estimate assumes full scheduled settlement, unchanged share ownership, and enough vault assets. The contract rounds gross scheduled interest and the protocol fee down in supply-token base units; `previewRedeem` rounds assets down, while `previewWithdraw` rounds shares up. Read `asset()`, token and share decimals, `maxRedeem()`, and the previews from the exact vault; do not assume every stablecoin uses six decimals. The live terminal preview, not this estimate, determines the current on-chain quote.

## Risks and administrative authority

* **Counterparty and settlement risk** — the institution can fail to claim, repay late, or fail to repay enough for the target outcome.
* **Collateral and oracle risk** — price movements, oracle behavior, risk-parameter changes, liquidation timing, incentives, and collateral liquidity can reduce recovery.
* **Lock and market-liquidity risk** — the vault offers no supplier exit before a terminal state, and transferable shares may have no buyer or reliable price.
* **Smart-contract and shared-component risk** — a clone also depends on the controller, oracle, PositionToken, LiquidationAdapter, ProtocolShareReserve, token contracts, permissions, frontend, and network.
* **Pause risk** — Partial pause blocks new deposits; Complete pause also blocks supplier withdrawals, redemptions, repayment, and liquidation until unpaused. The current source does not add the vault pause modifiers to ordinary ERC-20 share transfers.
* **Administrative recovery risk** — the controller exposes an ACM-gated `sweep(vault, token)` path that transfers the vault's full balance of the selected ERC-20 to the configured treasury. The published source does not impose a waiting period or exclude supply and collateral tokens. Verify live permissions and events; do not rely on an assumed grace period.
* **Approval and token risk** — wrong-token approvals, unlimited allowances, nonstandard token behavior, and malicious lookalike vaults or interfaces can cause loss.
* **Terms and eligibility risk** — the current app terms, not a static summary here, govern access to the interface and can change.

For the full state machine, settlement waterfall, rounding, pause system, and liquidation paths, see the [Fixed Term Vaults Technical Reference](/technical-reference/reference-technical-articles/fixed-rate-vaults). Source boundaries for the current consent-enabled flow are [`fixed-rate-vaults@91611be`](https://github.com/VenusProtocol/fixed-rate-vaults/tree/91611be77036e05d16f1ce43890b160cb4bc7228), the official interface's [consent-enabled deposit mutation](https://github.com/VenusProtocol/venus-protocol-interface/blob/c51133cb99c6881b96f9636b9cbec3bcf9c5e572/apps/evm/src/clients/api/mutations/useStakeIntoInstitutionalVault/index.ts), and its [Fixed Term Vault UI](https://github.com/VenusProtocol/venus-protocol-interface/tree/c51133cb99c6881b96f9636b9cbec3bcf9c5e572/apps/evm/src/containers/VaultCard/InstitutionalVaultModal). Source snapshots describe code behavior; immutable historical clones can use an earlier implementation. Verify the live clone, shared contracts, configuration, balances, state, and permissions at a recorded block.


# Contracts Overview

Venus Protocol contracts are divided in these repositories:

* [isolated-pools](https://github.com/VenusProtocol/isolated-pools): Contains core contracts for isolated lending, including logic for supplying, borrowing, liquidations, pool and market deployments, and interest rate models.
* [oracle](https://github.com/VenusProtocol/oracle): This repo has contracts for oracles that we support as well as logic for validating prices returned from those oracles.
* [venus-protocol](https://github.com/VenusProtocol/venus-protocol): The core protocol is located in this repo. It contains logic central to lending and borrowing of the core pool.
* [governance-contracts](https://github.com/VenusProtocol/governance-contracts): The contracts used for proposing, voting and executing changes are kept in the \`governance-contracts repo.
* [token-bridge](https://github.com/VenusProtocol/token-bridge): The contracts use to bridge XVS to different networks.
* [venus-periphery](https://github.com/VenusProtocol/venus-periphery): Auxiliary contracts for advanced DeFi operations including leverage strategies and token swaps.

## Isolated Pools Contracts

There are 2 categories of isolated pools contracts:

* Pool
* Risk Management

### Pool

Pool contracts can be divided into 4 categories:

* Configuration
* Logic
* Miscellaneous

#### Configuration

Configuration contracts are used to deploy, configure, and manage pools.

[**PoolRegistry**](/technical-reference/reference-isolated-pools/pool-registry/pool-registry)

Creating and managing pools is done by the [PoolRegistry](https://github.com/VenusProtocol/isolated-pools/blob/main/contracts/Pool/PoolRegistry.sol). It can add markets to pools, update pool metadata, and return pool information.

#### Logic contracts

[**Comptroller**](/technical-reference/reference-isolated-pools/comptroller/comptroller)

The [Comptroller](https://github.com/VenusProtocol/isolated-pools/blob/main/contracts/Comptroller.sol) contract is the central contract for each lending pool. It contains functionality central to borrowing activity in the pool like supplying and borrowing assets and liquidations. Configuration values for the pool such as the liquidation incentive, close factor, and collateral factor can also be set and retrieved from the comptroller. Account liquidity and positions can also be retrieved from the comptroller.

[**vToken**](/technical-reference/reference-isolated-pools/vtoken/vtoken)

[vTokens](https://github.com/VenusProtocol/isolated-pools/blob/main/contracts/VToken.sol) in isolated lending play an identical role as vTokens in the Core Pool. They represent a users supplied tokens to the protocol and can be redeemed (burned) for those tokens.

#### Miscellaneous contracts

Isolated Pools use additional contracts such as lenses, rewards, ect.

[**RewardsDistributor**](/technical-reference/reference-isolated-pools/rewards-distributor)

Users are rewarded for borrowing and lending activities with a reward tokens. The [RewardsDistributor](https://github.com/VenusProtocol/isolated-pools/blob/main/contracts/Rewards/RewardsDistributor.sol) manages these distributions using a configurable rate.

[**PoolLens**](/technical-reference/reference-isolated-pools/pool-lens)

To make querying pool data easier, Isolated Pools contains a [lens](https://github.com/VenusProtocol/isolated-pools/blob/main/contracts/Lens/PoolLens.sol) that queries and formats pool data. These calls can be gas intensive so as a general rule of thumb this contract should not be used in transactions.

#### Risk Management

[**RiskFundV2**](/technical-reference/reference-isolated-pools/risk-fund-and-shortfall/risk-fund-v2)

RiskFundV2 is the current BNB Chain mainnet custody contract for the risk-fund share of protocol income. It holds global raw token balances; the former per-pool reserve ledger was removed. Applying those funds to bad debt is not an automatic workflow and requires a scope-specific governance or operational action.

[**Shortfall**](/technical-reference/reference-isolated-pools/risk-fund-and-shortfall/shortfall)

Shortfall is the deployed legacy auction and transfer-debt recovery contract. New auction starts and restarts are paused on BNB Chain mainnet, and RiskFundV2 no longer exposes the per-pool reserve accounting on which that design depended. Keep the interface only for historical analysis and verified `claimTokenDebt` recovery.

[**ProtocolShareReserve**](/technical-reference/reference-isolated-pools/risk-fund-and-shortfall/protocol-share-reserve)

The [ProtocolShareReserve](https://github.com/VenusProtocol/protocol-reserve/blob/main/contracts/ProtocolReserve/ProtocolShareReserve.sol) remains a current, cross-pool income-accounting and distribution contract. Its on-chain configuration determines each schema's destinations and percentages.

## Oracle Contracts

[**ResilientOracle**](/technical-reference/reference-oracle/resilient-oracle)

Venus Protocol supports main, pivot, and fallback roles to create per-asset validation and fallback strategies. The [ResilientOracle](https://github.com/VenusProtocol/oracle/blob/main/contracts/ResilientOracle.sol) fetches prices for a vToken's underlying asset and manages the source address and enable flag for each role. Multiple roles are optional, so the live configuration must be checked before assuming that a price is cross-validated.

[**DeviationBoundedOracle**](/technical-reference/reference-oracle/deviation-bounded-oracle)

The [DeviationBoundedOracle](https://github.com/VenusProtocol/oracle/blob/main/contracts/DeviationBoundedOracle.sol) wraps the ResilientOracle to provide manipulation-resistant pricing for whitelisted assets on the borrow-power path. It maintains a per-asset rolling spot-price window and, when the spot deviates beyond a configured threshold, switches collateral pricing to `min(spot, windowMin)` and debt pricing to `max(spot, windowMax)`. Liquidations continue to use the spot price from the ResilientOracle. See [Protection Mode](/risk/protection-mode) for the feature-level description.

### Oracles

[**ChainlinkOracle**](/technical-reference/reference-oracle/oracles/chainlink-oracle)

[ChainlinkOracle](https://github.com/VenusProtocol/oracle/blob/main/contracts/oracles/ChainlinkOracle.sol) reads configured Chainlink feeds and normalizes their prices for the Venus oracle interface. Its role is configured per asset; it is not necessarily the main source for every market.

[**RedStoneOracle**](https://redstone.finance/)

[RedstoneOracle](https://docs.redstone.finance/docs/smart-contract-devs/get-started/redstone-classic) is used in the Classic model (Chainlink-compatible interface) as a pivot oracle to validate prices returned by main and fallback oracles.

[**BinanceOracle**](/technical-reference/reference-oracle/oracles/binance-oracle)

The [BinanceOracle](https://github.com/VenusProtocol/oracle/blob/main/contracts/oracles/BinanceOracle.sol) fetches configured Binance oracle feeds. Like every source adapter, whether it is main, pivot, fallback, disabled, or unused is determined by the live per-asset configuration.

## Venus Protocol

Venus Protocol contracts can be grouped as follows:

* Lending
* Tokens
* Vault
* Lens

### Lending Contracts

[**Comptroller**](https://github.com/VenusProtocol/venus-protocol-documentation/tree/main/technical-reference/reference-core-pool/comptroller/Diamond/Diamond.md)

At the heart of the Core Pool is the comptroller. The latest version is [Comptroller](https://github.com/VenusProtocol/venus-protocol/blob/develop/contracts/Comptroller/Diamond/Diamond.sol). The comptroller is responsible for listing markets, managing user's positions in markets, liquidations, and emitting rewards. It contains setters and getters for market configuration variables such as collateral factor, close factor, and liquidation incentive. Lending actions can be be paused globally or per market from the comptroller.

[**JumpRateModel**](/technical-reference/reference-core-pool/interestratemodels/jumpmodel)

Each market gets deployed with an interest rate model. The [JumpRateModel](https://github.com/VenusProtocol/venus-protocol/blob/main/contracts/InterestRateModels/JumpRateModel.sol) uses a linear curve to determine interest rates based on supply and demand of the asset until it reaches the kink after which there is a sharp increase in rates.

[**WhitePaperInterestRateModel**](/technical-reference/reference-core-pool/interestratemodels/whitepapermodel)

Another interest rate model that can be deployed with markets is the [WhitePaperInterestRateModel](https://github.com/VenusProtocol/venus-protocol/blob/main/contracts/InterestRateModels/WhitePaperInterestRateModel.sol). It is similar to the JumpRateModel except it doesn't include a kink. Instead it contains a fixed base rate.

[**Liquidator**](/technical-reference/reference-core-pool/liquidator)

When a borrow becomes insolvent it may be liquidated. The [Liquidator](https://github.com/VenusProtocol/venus-protocol/blob/main/contracts/Liquidator/Liquidator.sol) handles this process. When a borrow is liquidated the seized amount is split between the liquidator and the [treasury](/technical-reference/reference-core-pool/vtreasury)

[**VTreasury**](/technical-reference/reference-core-pool/vtreasury)

Revenue earned by the protocol is kept in the [VTreasury](https://github.com/VenusProtocol/venus-protocol/blob/main/contracts/Governance/VTreasury.sol).

### Token Contracts

[**XVS**](/tokens/xvs)

XVS is an important token in the Venus ecosystem because it powers Venus governance. The [XVS](https://github.com/VenusProtocol/venus-protocol/blob/main/contracts/Tokens/XVS/XVS.sol) token contract defines a lockable BEP20 token with additional methods that enable voting and vote delegation. To vote, a user must first lock their XVS in the vault.

[**VAI**](/tokens/vai)

[VAI](https://github.com/VenusProtocol/venus-protocol/blob/main/contracts/Tokens/VAI/VAI.sol) is the Venus stable coin that can be minted against collateral. Users who mint VAI are charged a fee based on the outstanding supply and price of VAI to keep its value pegged at $1. The [VAIController](https://github.com/VenusProtocol/venus-protocol/blob/develop/contracts/Tokens/VAI/VAIController.sol) controls the amount of VAI a user is allowed to mint which is determined by the collateral a user has provided and their liquidity.

[**vTokens**](/technical-reference/reference-core-pool/vtoken)

When a user supplies a token to the protocol, vTokens are minted to represent their supply. The [VToken](https://github.com/VenusProtocol/venus-protocol/blob/main/contracts/Tokens/VTokens/VBep20.sol) contract contains methods that support lending activities for the asset including lending, borrowing and liquidating

### Vault Contracts

**XVS Vault**

XVS can be locked in the [XVSVault](https://github.com/VenusProtocol/venus-protocol/blob/main/contracts/XVSVault/XVSVault.sol) to earn XVS and enable voting. Each XVS locked gives the locking address one vote to use or delegate.

**VAI Vault**

VAI staked in the [VAIVault](https://github.com/VenusProtocol/venus-protocol/blob/main/contracts/Vault/VAIVault.sol) earn XVS. Staking rewards are accumulated daily.

#### Misc Contracts

**ComptrollerLens**

The [ComptrollerLens](https://github.com/VenusProtocol/venus-protocol/blob/main/contracts/Lens/ComptrollerLens.sol) contains methods for fetching the liquidity of an account and the amount of tokens that can be seized for a repayable amount

**SnapshotLens**

The [SnapshotLens](https://github.com/VenusProtocol/venus-protocol/blob/main/contracts/Lens/SnapshotLens.sol) contains methods for getting the details of account for a specific market or all markets where an account is active.

**VenusLens**

Protocol level data is made available through the [VenusLens](https://github.com/VenusProtocol/venus-protocol/blob/main/contracts/Lens/VenusLens.sol). It contains getters related to XVS distribution, governance, and markets.

### Governance Contracts

There are three main Governance contracts:

* GovernorBravoDelegate
* AccessControlManager
* Timelock

**GovernorBravoDelegate**

The core logic for governance proposals is in the [GovernorBraveDelegate](https://github.com/VenusProtocol/governance-contracts/blob/main/contracts/Governance/GovernorBravoDelegate.sol) contract. It enables submitting proposals, moving proposals through time-gated stages, canceling and executing proposals as well as voting logic. The voting threshold as well as timelocks are set on this contract.

**AccessControlManager**

To enhance security of the protocol, Venus Protocol uses the [AccessControlManager](https://github.com/VenusProtocol/governance-contracts/blob/main/contracts/Governance/AccessControlManager.sol) to grant accounts access to call specific functions on contracts. This contract is responsible for granting and revoking those permissions. It also provides a getter to check if an address is allowed to call a specific function.

**Timelock** Once a proposal has succeeded its execution is managed by the [Timelock](https://github.com/VenusProtocol/governance-contracts/blob/main/contracts/Governance/Timelock.sol) contract. The Timelock can place the proposal in a queue for execution and execute the proposal. It also enables canceling the proposal.

## Periphery Contracts

Periphery contracts extend core protocol functionality with auxiliary features for advanced DeFi operations.

[**LeverageStrategiesManager**](/technical-reference/reference-periphery/leverage-strategies-manager)

The [LeverageStrategiesManager](https://github.com/VenusProtocol/venus-periphery/blob/main/contracts/LeverageManager/LeverageStrategiesManager.sol) enables users to enter and exit leveraged positions atomically using flash loans. It consolidates multiple operations (borrow, swap, supply) into single transactions.

[**SwapHelper**](/technical-reference/reference-periphery/swap-helper)

The [SwapHelper](https://github.com/VenusProtocol/venus-periphery/blob/main/contracts/SwapHelper/SwapHelper.sol) provides a secure multicall interface for token swaps with backend signature verification. It enables authorized interactions with external DEX protocols.


# Technical articles

These articles explain protocol architecture and integration patterns. They are not deployment inventories: for a live integration, pair the relevant article with the [deployed-contract registries](/deployed-contracts/markets) and the versioned API reference for the implementation behind the selected proxy or beacon.

## Architecture and control

* [Diamond Comptroller in the Core Pool](/technical-reference/reference-technical-articles/diamond-comptroller)
* [Omnichain Governance](/technical-reference/reference-technical-articles/omnichain-governance)
* [XVS Bridge](/technical-reference/reference-technical-articles/xvs-bridge)
* [Automatic income allocation](/technical-reference/reference-technical-articles/automatic-income-allocation)

## Markets and execution

* [Trade](/technical-reference/reference-technical-articles/trade)
* [Fixed Term Vaults](/technical-reference/reference-technical-articles/fixed-rate-vaults)
* [VenusERC4626 Vaults](/technical-reference/reference-technical-articles/venus-erc-4626)
* [E-Mode](/technical-reference/reference-technical-articles/emode)
* [Native Token Gateway](/technical-reference/reference-technical-articles/native-token-gateway)
* [FlashLoan](/technical-reference/reference-technical-articles/flashloan-core-pool)

## Risk and pricing

* [Capped Oracles](/technical-reference/reference-technical-articles/capped-oracles)
* [DeviationBoundedOracle](/technical-reference/reference-technical-articles/deviation-bounded-oracle)
* [Risk Stewards](/technical-reference/reference-technical-articles/risk-stewards)
* [Shortfall and auctions](/technical-reference/reference-technical-articles/shortfall-and-auctions)
* [Two Kinks Interest Rate Curve](/technical-reference/reference-technical-articles/two-kinks-interest-rate-curve)

## Rewards and reserves

* [Prime tokens](/technical-reference/reference-technical-articles/prime)
* [TokenBuyback Contract](/technical-reference/reference-technical-articles/token-converters)

See [Contributing](/technical-reference/reference-technical-articles/contributing) before editing generated or version-sensitive reference material.


# Automatic income allocation

The purpose of this document is to explain the technical details for distributing incomes generated by the Venus Protocol. The distributed incomes are generated from the spread of lending rates (borrow minus supply rates) and liquidations (part of the liquidation incentives). The distribution process for both the Core Pool and Isolated Pools as well as how the different amounts are allocated to different destinations based on specific rules and the approach for near-streaming income distribution are covered.

## Income Distribution Schema

The Venus Protocol generates income from two main sources:

* Spread between the borrowing rate and the supply rate
* Part of the liquidation incentives

[Tokenomics](/governance/tokenomics) goes into more detail about the distribution schema and desired outcomes.

The initial distribution excludes VAI funds, which are kept in the VTreasury.

## Streaming Income Distribution

The Venus Protocol aims to distribute accumulated reserves in the markets automatically and in near-real-time, leveraging transactions executed by users. This approach eliminates the need for external tools to manage the distribution process. The `ProtocolShareReserve` contract serves as the designated destination for liquidation incomes, allowing for tracking and distribution within the Venus Protocol.

### Core Pool - Spread Income

Accumulated reserves in the Core Pool markets will be distributed after reaching a specified threshold. The threshold is based on the number of blocks since the last transfer of reserves. This strategy helps socialize part of the cost associated with distribution.

### Core Pook - Liquidation Income

In the Core Pool, liquidation income is transferred to the Liquidator contract in the form of vTokens. During a liquidation transaction, the Liquidator contract will try to redeem the protocol's portion of the liquidation incentive in vTokens for the underlying tokens. If the redemption process is successful, the underlying tokens will be sent to the `ProtocolShareReserve` contract. However, if the redemption fails the underlying tokens will be added to a list of pending redemptions and the Liquidator contract will try to redeem the pending redemptions again in subsequent liquidation transactions.

### Isolated Pool - Spread Income

Distribution of income generated by the interest rate spread in Isolated Pools is socialized and distributed periodically just like in the Core Pool

### Isolated Pool - Liquidation Income

The Isolated Pools liquidations are managed by the VToken contracts. During a liquidation transaction, the protocol's percentage of the seized amount is transferred directly to the `ProtocolShareReserve` contract. No other action is required.

## Distribution of Collected Incomes

The distribution of collected incomes is facilitated by the `ProtocolShareReserve` contract. It supports WBNB transfers but not BNB transfers. Therefore, BNB needs to be wrapped into WBNB before transferring to the `ProtocolShareReserve` contract.

### `updateAssetsState` Function

The `updateAssetsState` function enables the transfer of funds to the `ProtocolShareReserve` contract. After transferring funds to the `ProtocolShareReserve` contract, the `updateAssetsState` function is invoked with the following parameters:

* `address comptroller`: The Comptroller where the market generated the income.
* `address asset`: The asset transferred to the `ProtocolShareReserve`.
* `IncomeOrigin origin`: The origin type, either "spread" or "liquidation."

Within the `updateAssetsState` function, the following steps are performed:

* Calculate the transferred balance, taking into account the previous balance of the asset.
* Assign the received amount to the appropriate entry (comptroller - asset - schema).
* Track the total balance of the received asset for future distribution.

### `releaseFunds` Function

The `releaseFunds` function is responsible for distributing the accumulated funds to the different destinations. This function can be invoked by anyone and follows a two-step process:

1. Transfer the tokens to the destination addresses.
2. Invoke the `updateAssetsState` function in the receiver contract.

## VBNBAdmin

`VBNBAdmin` contract is the admin of the vBNB vToken. All the other vToken contracts send revenue to PSR directly whereas vBNB sends the reserves to the admin i.e., the vBNBAdmin contract. This pattern is different for vBNB because it's not a upgradable contract.

Whenever the `VBNBAdmin` receives BNB from the vBNB contract it converts it to WBNB and then sends it to the Protocol share reserve contract.

To release the reserves of vBNB contract you need to call the `reduceReserves()` function of the `VBNBAdmin` contract.


# Capped Oracles

Some assets, such as Liquid Staking Tokens (LSTs), are closely tied to an underlying asset, often featuring an added growth component.

The exchange rate between an asset and its underlying is typically sourced from an onchain smart contract, which can be susceptible to manipulation. A capped oracle is a design mechanism that mitigates potential losses from such manipulation by limiting the rate at which the exchange rate is allowed to grow.

The capped oracle mechanism should be used for any asset whose price is derived through an intermediary token, where the exchange rate between the source asset and the intermediary is fetched onchain and includes a growth component.

At a high level, the capped oracle determines the maximum allowable exchange rate at the queried block based on a predefined growth rate. It then compares this with the current onchain exchange rate, and if the current rate exceeds the calculated maximum, it caps it to that maximum value.

Typically, the current maximum exchange rate at a given block is calculated by taking a historical exchange rate from a past block and applying a predefined per-second growth rate up to the current block timestamp. However, since the exchange rate may not grow consistently over time, the historical reference rate is periodically updated to ensure a more accurate and reliable maximum exchange rate.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-370a2b2769a84e669d2eaa8553185722b7661377%2Fcapped-oracle.svg?alt=media" alt="Capped Oracle main elements"><figcaption></figcaption></figure>

The historical reference rate is periodically refreshed through a process called snapshotting. During this process, the new reference rate is set to the lower of the current onchain exchange rate or the maximum rate derived from the previous reference rate, helping maintain a more accurate and robust cap. Before querying an asset’s price from the capped oracle, the consumer should trigger the snapshotting process. If the configured interval has elapsed, a new snapshot will be taken.

The capped exchange rate between intervals is constrained by a per-second growth limit. However, since the actual exchange rate may exceed this cap, we inflate the snapshotted exchange rate by a predefined buffer to avoid imposing a hard limit.

## Configuration

The following configuration needs to be defined when configuring the capped oracle for a token:

* Annual Growth Rate: This defines the maximum growth in exchange rate per year. Internally this is converted to growth rate per second.
* Snapshot Interval: After the number of seconds window at which the reference exchange rate is recalculated,
* Initial Snapshot Exchange Rate and Timestamp: Defines the initial reference exchange rate and the timestamp at which the exchange rate is taken from.
* Snapshot Gap: Used to define the inflation of the exchange rate at each snapshot.

Note that all the above variables values can be updated using the governance process after the deployment with the initial values.

## Example

Let’s walk through an example to understand how the capped oracle functions. Suppose we want to retrieve the exchange rate between wstETH and stETH.

* **Annual Growth Rate:** Assume an annual growth rate of 2.9%, based on Lido’s estimated APY. Taking into account Lido compounding period is daily, the estimated APY would be 2.9423% (compounding should be considered when the caps are defined). So, the associated capped oracle could be configured with a maximum annual growth rate of 5%. Internally, this will be converted into a per-second growth rate.
* **Snapshot Interval:** The snapshot can be updated once per month to refresh the reference exchange rate.
* **Initial Snapshot:** We fetch the current exchange rate and timestamp from the onchain wstETH contract. For instance, the current exchange rate is `1.200101369591475639` and the timestamp is `1744895950`. We apply a buffer on top of the current exchange rate: for example 1% of the maximum annual growth rate, that is `1% * 5% = 0.05%`. So, the initial snapshot value would be `1.200101369591475639 * 1.0005 = 1.20070142027627137681`. This way, the capped oracle won't cap spikes on the wstETH exchange rate just after the initialization.
* **Snapshot Gap:** The same concept of the buffer applied to calculate the initial snapshot in the previous step, but to be considered every time the snapshot is automatically updated. This cap could be set to `1.200101369591475639 * 0.0005 = 0.0006`, and reviewed periodically.

Given this initial setup:

* after 15 days, the maximum exchange rate allowed will be `1.20070142027627137681 * (1 + (0.05 * 15 / 365)) = 1.20316860954764085647`
* after 1 month, the snapshot will be automatically updated. Assuming the exchange rate at that time is `1.20300161856832627043`, after applying the snapshot gap, the new maximum exchange rate will be `1.20360161856832627043` (`1.20300161856832627043 + 0.0006`)
* after 15 days, the maximum exchange rate allowed will be `1.20360161856832627043 * (1 + (0.05 * 15 / 365)) = 1.20607476713814428156`


# Contributing

We have a strict policy on how we should commit our work. Following these guidelines will help us to:

1. Avoid hard to solve merge conflicts. Our workflow favors rebase over merge in feature branches, so there are no complex dependencies between branches. We sacrifice the “full history” in favor of just “meaningful history”, thus avoiding unnecessary diffs.
2. Make our contracts more secure. It's easy to make a small mistake that would cost millions of dollars to the contract users. We need to peer review our contracts as thoroughly as possible, thus our workflow ensures the process is easy and rewarding for the reviewers.
3. Save newcomers' time. When new developers join the team, they want to know how the code evolved and why certain design decisions were made. Our workflow encourages exhaustive commit messages that describe the reason behind each change.

Although the policy may seem too restrictive, it is usually quite easy to follow. It may require you to learn git a bit deeper than you're used to — please follow along, we'll show the necessary commands :)

## Configure your git

By default, git merges the upstream changes into your local branch on pull. This **silently** [spoils your history](https://blog.sffc.xyz/post/185195398930/why-you-should-use-git-pull-ff-only-git-is-a) and may introduce unnecessary merge conflicts that are hard to solve and reason about. We encourage you to turn off this feature by running `git config --global pull.ff only`.

## Create a feature branch

1. Prefer to base your feature branches on master. If it is not possible, make sure to **not include** someone else's commits in your PR later.
2. Prefer short and descriptive names for branches (good: `polynomial-interest-curve`, bad: `fix-rate`).
3. Use lowercase words separated by dashes (good: `xvs-vault`, bad: `XVSVault`).
4. You may include work type into the branch name (ok: `feature/polynomial-interest-curve`).
5. Make sure no-one else works in your feature branch.

## Make commmits

Commit messages will need to follow a standard format in order to be able to correctly bumpt the next version and format the release. [Conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) is a nice standard to use for formatting the commit messages. There are different tools available to enforce these messages

### Structure

```bash
<type>[optional scope]: <description>

[optional body]

[optional footer(s)]
```

### Breaking changes

Breaking changes will result in a major version bump. These can be triggered by placing BREAKING CHANGE or BREAKING CHANGES in the footer of the commit message or by placing an exclamation point after the type. See next section on commit format

### Enforcing commits

[commitlint](https://github.com/conventional-changelog/commitlint) - Precommit hook with husky ( currently implement in the api repo)

| Type            | Release | Included in Changelog |
| --------------- | ------- | --------------------- |
| breaking change | major   | true                  |
| feat            | minor   | true                  |
| build           | false   | false                 |
| ci              | false   | false                 |
| fix             | patch   | true                  |
| refactor        | patch   | false                 |
| test            | false   | false                 |

## Prettify your history

Once you have made your changes, it's time to present them to the reviewers. It is quite important that the reviewers only see the relevant up-to-date changes structured by commits.

1. While you've been working on your feature branch, the master branch has most likely evolved. Rebase your changes on top of master by running `git rebase --onto master <parent>`, where `<parent>` is the hash of the commit *immediately preceding* your first commit. You can find `<parent>` by looking at the history: `git log --oneline`.
2. Your feature branch should have linear history. No merge commits are allowed.
3. The commits in your PR should not solve the problems introduced in your previous commits. The reviewers often look at the code commit by commit, and they may comment on the problems you later solve. By making sure your commits are self-contained, you free the reviewers of unnecessary work. Use the interactive rebase feature (`git rebase -i <parent>`) to squash, reorder or drop your commits.

## Notes on force pushing

When you rewrite your history, GitHub will refuse to accept your changes. This is to protect you from wiping out someone else's contributions. We need to follow certain rules to make sure our history is clean and the others' contributions are intact.

1. Every feature branch should have one active maintainer. No-one else is allowed to directly commit to your feature branch.
2. You can safely force-push to your feature branch. If your colleague wants to contribute, ask them to push their changes into a separate branch, and then cherry-pick. If you want to help your colleague, push your changes to a separate branch and let your colleague cherry-pick. This would help you to avoid any potential conflicts stemming from force-pushing.
3. **Always** use `--force-with-lease` and not `--force`/`-f` to force-push. Things happen, and someone may violate the “one active maintainer” rule. Force with lease would save you the trouble of recovering someone's work :)
4. **Never** force-push to master or any branch that has several maintainers. The only exception to this rule is when a secret (API key, private key, etc.) is accidentially committed, in which case you should immediately wipe it out and notify the security team as soon as possible.

## Make a pull request

1. Make sure your commit history follows the guidelines written above. Rebase once again if necessary.
2. If your PR is work in progress, explicitly mark it as WIP.

## Pass the review

1. During the review, your history does not need to satisfy the criteria above. The reviewers are interested in how you have addressed their comments, so do not squash your fixes with your previous commits during the review. You can use `git commit --fixup <target_commit>` to make a fix to a specific commit.
2. Avoid fixing up a fixup :)
3. After addressing all of the review comments, rebase your work so that the commits are self-contained again. You can use `git rebase -i --autosquash` to squash the fixup commits into the target commits automatically.
4. Do not add any new changes to the code after the review (except for squashing and reordering the commits).

## Closing thoughts

This policy may require some time and effort: writing lenghty commit messages and doing an interactive rebase isn't as straightforward as `git commit -am`. This pays out quickly, however. When you do `git bisect` to find a bug, you'd really appreciate descriptive commits instead of just "Update file" or "Some fixes". When you're reviewing a PR, you can limit the mental burden by looking at individual commits instead of diving straight into 40 files changed. When you're a newcomer and wonder why a function you wanted to use suddenly disappeared before you fully understood the code, you'll be happy to see the reason straight in `git log`. The same goes for when you `git blame` to understand why a certain line of code is written the way it is.

If we were to summarize everything written above into just three bullet points, we'd go with:

* **Always** describe the reason for your changes in commit messages like you're talking with the 5-year-old.
* Leave only meaningful changes in your PR history. Git history can't and shouldn't encompass everything, just like your school history textbook.
* Choose **only one** person responsible for a feature. Avoid unnecessary interference.


# Diamond Comptroller in the Core pool

The BNB Chain Core Pool Comptroller uses a Unitroller proxy followed by a Diamond selector router. This architecture is specific to the BNB Core Pool; Core Pools on other networks use BeaconProxy Comptrollers from the `isolated-pools` repository.

## Why the Diamond was introduced

As the original Comptroller grew, its implementation approached the EVM contract-size limit. Venus split application logic into facets while retaining the existing Unitroller address and storage layout.

The call path is:

```
user or vToken
    │
    ▼
Unitroller (storage and public entry point)
    │ delegatecall
    ▼
Diamond (selector router)
    │ delegatecall selected by msg.sig
    ▼
Facet (application logic in Unitroller storage context)
```

Users, vTokens, and integrations continue to call the Unitroller. The Diamond and facet addresses are implementation details and must not be called as stateful application contracts.

## Current facet families

The current Diamond routes selectors among five facets:

* [MarketFacet](/technical-reference/reference-core-pool/comptroller/diamond/facets/market-facet) — markets, accounts, delegation, and liquidity views
* [PolicyFacet](/technical-reference/reference-core-pool/comptroller/diamond/facets/policy-facet) — vToken policy hooks and liquidity checks
* [RewardFacet](/technical-reference/reference-core-pool/comptroller/diamond/facets/reward-facet) — XVS accrual, claims, and seizure
* [SetterFacet](/technical-reference/reference-core-pool/comptroller/diamond/facets/setter-facet) — privileged risk and feature configuration
* [FlashLoanFacet](/technical-reference/reference-core-pool/comptroller/diamond/facets/flashloan-facet) — flash-loan execution

The facets inherit shared checks and the current `ComptrollerV19Storage` layout from `FacetBase`. Depending on the function, access may be limited to the Unitroller admin, an admin-or-guardian path, or a signature-specific Access Control Manager permission.

## Upgrades and selector inspection

The Unitroller admin can install a new Diamond implementation through the Unitroller's pending-implementation flow. The admin can also call `diamondCut` through the Unitroller to add, replace, or remove selector assignments.

An integration can inspect the current routing through:

* `facetAddress(bytes4)` for one selector;
* `facetFunctionSelectors(address)` for one facet;
* `facetAddresses()` or `facets()` for the full map.

At BNB Chain block `118,363,255`, the Unitroller used the Diamond and five facets installed by [VIP-640](https://venus.io/governance/proposal/640?chainId=56). See the [live component map](/technical-reference/reference-core-pool/comptroller) for the snapshot addresses.

{% hint style="warning" %}
The repository's `DiamondConsolidated` contract exists only to generate a combined ABI and TypeChain type. It is not deployed, and its inherited functions are not proof that the corresponding selectors are live.
{% endhint %}


# E-Mode

{% hint style="info" %}
Only available on BNB Chain Core Pool.
{% endhint %}

## Overview

The **Venus Protocol Core Pool** on BNB Chain has been enhanced with **E-Mode (Efficiency Mode)**, a feature designed to provide users with greater **capital efficiency** when lending and borrowing. E-Mode introduces specialized **pools of assets**—such as correlated ones—each with its own customized **risk parameters**.

When a user activates an **E-Mode pool**, their borrowing is restricted to assets within that pool but with **higher collateral factors (CF)** and **liquidation thresholds (LT)** compared to the default Core Pool. A **lower liquidation incentive (LI)** is also applied, reducing the penalty at liquidation and making borrowing within E-Mode more efficient.

This allows users to unlock **more borrowing power** while keeping **risk contained** within the pool.

Unlike **isolated pools**, which fully segregate assets into separate environments, E-Mode keeps everything within the Core Pool. Users do not need to transfer assets to benefit from different risk profiles. Instead, they can simply activate an E-Mode pool, gaining higher efficiency while maintaining their existing positions. This design balances capital optimization with contained risk management, making E-Mode a powerful extension of the Core Pool.

### Key Benefits of E-Mode

* **Increased Borrowing Power**: Higher CF and LT enable users to leverage more borrowing capacity in E-Mode pools.
* **Reduced Liquidation Penalties**: Lower LI minimizes losses during volatile market conditions.
* **Seamless Integration**: No asset transfers are required; all operations remain in the Core Pool.
* **Risk Isolation**: Borrowing limits prevent cross-pool exposure, reducing systemic risks.
* **Governance Flexibility**: Pool parameters can be dynamically adjusted via Venus Governance proposals.

### Potential Risks and Considerations

* **Borrow Restrictions**: Users must ensure existing borrows align with the target pool's allowed assets.
* **VAI Incompatibility**: Users with VAI debt cannot enter E-Mode, ensuring stablecoin-specific isolation.
* **Parameter Changes**: Governance updates (e.g., lowering CF) can impact the user positions.
* **Core Pool Fallback Behavior**: Controlled by a per-pool flag `allowCorePoolFallback` (defaults to `false`).
  * If `true`: Assets not included in the E-Mode pool use Core Pool risk parameters (CF, LT, LI).
  * If `false`: Assets not included in the E-Mode pool have no risk factors applied (effectively CF = 0 and LI = 0), so users are recommended to exit such markets before switching into that E-Mode pool.

## Comptroller Changes for E-Mode in Core Pool

The introduction of **E-Mode** in the Venus Core Pool on BNB Chain required significant enhancements to the `Comptroller` contract. These changes enable fine-grained risk management while preserving the existing Core Pool functionality. Below is a summary of the major updates:

| Change                                              | Description                                                                                                                                                                                                                                                      | Impact                                                                                                                  |
| --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| **Liquidation Threshold (LT) Support in Core Pool** | The Core Pool now supports **Liquidation Thresholds**, similar to isolated pools. LT allows borrowing limits (CF) and liquidation conditions (LT) to be defined separately, improving risk control.                                                              | Enables more precise liquidity calculations, separating borrow caps from liquidation triggers for better user safety.   |
| **Per-Market Liquidation Incentives (LI)**          | The global LI has been replaced with **per-market LIs**, enabling Governance to assign asset-specific liquidation rewards based on risk.                                                                                                                         | Liquidators receive tailored incentives, optimizing protocol security and efficiency for high-risk vs. low-risk assets. |
| **Unified Pool-Market Mapping**                     | The existing `markets` mapping has been replaced with `_poolMarkets`, a flexible structure keyed by a new `PoolMarketId` type (`bytes32`), which uniquely identifies each pool-market pair while maintaining backward compatibility for existing market getters. | Supports multiple pools without storage conflicts, allowing pool-specific overrides while maintaining legacy access.    |

The implementation extends the **Comptroller contract** to support:

* Pool-specific risk configurations.
* User-driven pool selection.
* Refined, pool-based risk management.

All of this is achieved while maintaining **backward compatibility** with the Core Pool’s legacy operations.

## Implementation Details

### 1. Markets Mapping

The core data structure for tracking markets has been upgraded to handle multiple pools efficiently.

**Previously**: Address as index

```solidity
mapping(address => Market) public markets;
```

**Now**: `bytes32` as index

```solidity
type PoolMarketId is bytes32;
mapping(PoolMarketId => Market) public _poolMarkets;
```

* **Core Pool (poolId = 0)**: The `PoolMarketId` is the vToken address left-padded to 32 bytes, preserving storage layout for compatibility.
* **E-Mode Pools (poolId > 0)**: The key is constructed by combining the `poolId` (uint96, shifted left by 160 bits) with the vToken address (uint160).

This approach ensures:

* **No collisions** between pool-market pairs.
* **Pool-specific overrides** (e.g., CF/LT/LI).
* **Backward compatibility** is preserved through updated getter functions, which return Core Pool values when called with only a vToken address, while maintaining the same function signatures.

### 2. Market Struct

The `Market` struct now includes fields to support LT, per-market LI, and pool-specific borrowing rules:

```solidity
struct Market {
    bool isListed;
    uint256 collateralFactorMantissa;
    mapping(address => bool) accountMembership;
    bool isVenus;
    uint256 liquidationThresholdMantissa;
    uint256 liquidationIncentiveMantissa;
    uint96 poolId;
    bool isBorrowAllowed;
}
```

* `accountMembership`: Stored only in Core Pool entries, since borrows and collateral are tracked globally across all pools. In E-Mode entries, this mapping remains empty for structural consistency, reducing storage overhead.
* `isBorrowAllowed`: A pool-level flag that Governance can toggle, overriding global borrow caps for E-Mode restrictions.

### 3. Pool Tracking

Pools are defined and managed via a dedicated mapping for metadata and asset lists:

```solidity
struct PoolData {
    string label;               // e.g., "Stablecoins"
    address[] vTokens;          // Markets in this pool
    bool allowCorePoolFallback; // If true, non-pool assets use Core Pool CF/LT/LI; if false, CF/LT/LI treated as 0
}

mapping(uint96 => PoolData) public pools;

uint96 public lastPoolId;
```

* `lastPoolId` tracks the latest assigned poolId (`0` is reserved for Core).
* Pools hold metadata, a list of associated vTokens, and the `allowCorePoolFallback` flag (default: `false`).

**Governance Workflow**: New pools are created via `createPool(string calldata label)`, which assigns the next `poolId` and initializes the `PoolData` with `allowCorePoolFallback = false` by default. Governance can update this behavior via `setAllowCorePoolFallback(uint96 poolId, bool allowed)`. This allows for easy expansion to new pools like "ETH Correlated" or "BNB Derivatives" while choosing the desired fallback behavior.

### 4. User Pool Selection

Each user is associated with exactly one pool at a time, simplifying state management:

```solidity
mapping(address => uint96) public userPoolId;
```

* Defaults to **0 (Core Pool)**.
* Switching updates `userPoolId` with proper validations.

This ensures all collateral and borrowing operations are evaluated within the context of the user’s active pool. **Edge Case Handling**: If a user's pool is disabled by Governance, their `userPoolId` is automatically reset to 0, triggering a fallback event.

### 5. Pool Markets Configuration

Governance configures E-Mode via a suite of administrative functions:

* **Add/Remove**: `addPoolMarkets` and `removePoolMarket` manage vTokens in non-Core pools.
* **Borrow Control**: `setIsBorrowAllowed` toggles borrowing eligibility.
* **Risk Parameters**: Setters for CF, LT, and LI support poolId for E-Mode and address-only for Core.
* **Fallback Control**: `setAllowCorePoolFallback(uint96 poolId, bool allowed)` controls whether non-pool assets use Core Pool risk parameters.

**Example Setters**:

```solidity
// E-Mode setter
function setCollateralFactor(uint96 poolId, VToken vToken, uint256 newCollateralFactorMantissa, uint256 newLiquidationThresholdMantissa) external returns (uint256);

// Core Pool setter
function setCollateralFactor(VToken vToken, uint256 newCollateralFactorMantissa, uint256 newLiquidationThresholdMantissa) external returns (uint256);
```

## E-Mode: User Journey

### 1. Default State

* Every user starts in the **Core Pool (poolId = 0)**.
* Core Pool applies **standard parameters** (lower CF/LT, higher LI).
* If the user never switches pools, nothing changes—ensuring zero disruption for legacy users.

### 2. Explore Available Pools

Users can discover available E-Mode pools using:

* **Venus Lens**: By calling the `getAllPoolsData` function to fetch pool data directly from the Comptroller. This returns supported E-Mode pools and their vTokens along with parameters such as CF, LT, LI, and borrow permissions.
* **Venus App UI**: A user-friendly interface that displays the same information without requiring direct blockchain queries.

### 3. Validate Compatibility

* Before switching, make sure all your borrowed assets are supported as borrowable assets in the E-Mode pool you want to enter.
* If any borrowed asset is not allowed in the target pool, the `enterPool` transaction will revert on: **hasValidPoolBorrows()** check.

### 4. Enter the Pool

* **Call**: enterPool(uint96 poolId)
* **Comptroller checks**:
  * Pool exists and user isn’t already in it.
  * Borrow compatibility (`hasValidPoolBorrows`).
  * **VAI check**: Users with outstanding VAI debt or active minting positions cannot switch into E-Mode.
  * **Liquidity check**: Runs `_getAccountLiquidity` with new pool’s CF/LT.
    * ❌ If shortfall > 0 (would become liquidatable), tx reverts.
* **On success**:
  * `userPoolId` updated
  * `PoolSelected` event emitted

### 5. Post-Entry Impacts

* **Borrowing**: Restricted to markets marked `isBorrowAllowed` in chosen pool.
* **Collateral**:
  * Pool assets use **pool CF/LT/LI**.
  * Non-pool assets behavior depends on `allowCorePoolFallback`:
    * If `true`: Non-pool assets use **Core parameters** (typically lower CF, higher LI).
    * If `false` (default): Non-pool assets have no applicable risk factors in the selected E-Mode pool (effectively CF = 0 and LI = 0).
* **Liquidation**:
  * Pool assets → more efficient (lower LI).
  * Non-pool assets → liquidators prefer them due to higher LI.
* **VAI**: Minting and repayment only possible in Core Pool.

## Common Scenarios

### Borrowed Asset Not Allowed

* Example: A user has borrowed BTCB and tries to enter the Stablecoins Pool.
* BTCB is not part of that pool, so it is not listed as borrowable.
* Result: `enterPool` fails with `IncompatibleBorrowedAssets`.
* Even if an asset is included in a pool, Governance can set `isBorrowAllowed = false`. In this case, supplying the asset as collateral is fine, but new borrows of that asset are restricted.

### Mixed Collateral

* Users can provide collateral outside of the E-Mode pool’s listed assets **if the pool’s `allowCorePoolFallback` flag is set to true**.
* If `allowCorePoolFallback = true`: Non-pool assets use **Core Pool parameters** (CF, LT, LI). Since Core Pool LI is typically higher, liquidators may target these assets first during liquidation.
* If `allowCorePoolFallback = false` (default): Non-pool collateral **does not contribute to borrowing power**. CF, LT, and LI are treated as **0** in the E-Mode context. Users are strongly recommended to **exit these markets** before or immediately after switching to E-Mode to avoid unnecessary seizure risk for these assets during liquidations.

**Example:** In the Stablecoins Pool, a user supplies **USDC + UNI**:

* **USDC** → CF/LT/LI from E-Mode (e.g., LI = 5%).
* **UNI** →
  * If `allowCorePoolFallback = true`: CF/LT/LI from Core Pool (e.g., LI = 10%).
  * If `allowCorePoolFallback = false`: CF/LT/LI = 0 → does not contribute to borrowing power.

### Switching Back to Core

* Switching back is allowed only if the account remains healthy under Core Pool’s stricter parameters.
* Example: In Stablecoins Pool, a user is safe with CF = 0.90. Switching back to Core Pool with CF = 0.60 may immediately create a shortfall → reverts with `LiquidityCheckFailed`.
* **Important scenario**:
  * If a user is close to liquidation in Core Pool, they may switch into an E-Mode pool (with higher CF/LT) to immediately improve their account health and avoid liquidation.
  * However, if they attempt to switch back to Core, the stricter parameters would again make them liquidatable, so the transaction reverts.
* This ensures users cannot bypass liquidation risk by cycling between pools.

### Governance Changes

Governance has significant control over pool configurations, which can directly affect user positions:

1. **Borrow Restrictions:**
   * Governance can set `isBorrowAllowed = false` for a market.
   * Existing borrows remain, but new borrows are blocked.
   * This behaves similarly to setting the borrow cap to 0 in the Core Pool.
   * When borrow cap set to 0, no additional borrows are possible, but existing borrows are unaffected.
2. **Market Removal from E-Mode:**
   * If Governance removes a market from an E-Mode pool, the market’s risk factors instantly revert to its Core Pool values.
   * This is effectively the same as updating CF, LT, or LI parameters via a VIP.
   * Users need to monitor VIP proposals to anticipate changes.
3. **Disabling Core Pool Fallback:**
   * Governance can disable **Core Pool fallback** for a pool.
   * Assets outside the E-Mode pool will no longer use Core Pool parameters, which can affect user health.
   * Such changes are implemented **only after complete risk analysis** and with **advanced notice to users** so they can adjust their positions safely.
4. **Disabling an E-Mode Pool:**
   * Governance can disable an entire E-Mode pool by setting the `isActive` flag to `false`.
   * When this happens, all users in the disabled pool automatically fall back to Core Pool parameters.
   * Users who remain in a disabled E-Mode pool cannot initiate new borrows and are encouraged to either return to the Core Pool or switch to another active E-Mode pool.

**Rule of Risk Factor Selection:**

* If a parameter (CF, LT, LI) is defined in the active E-Mode pool, that value is used.
* If Governance sets a value to `0` in the pool (e.g., CF = 0), the `0` applies — it does not fall back to Core.
* If the asset is not included in the E-Mode pool:
  * If `allowCorePoolFallback = true`: Use the asset's Core Pool CF/LT/LI.
  * If `allowCorePoolFallback = false`: Treat CF/LT/LI as 0 for that asset in E-Mode; users are recommended to exit such markets.
* If the pool is disabled, users automatically fall back to Core Pool parameters.

## Effect on User on Switching Into E-Mode

To see how E-Mode changes borrowing power, liquidation, and borrow restrictions, let’s follow a single user journey. This example uses hypothetical parameters to illustrate key mechanics.

### Pool Setup

| Asset    | Core Pool CF | Core Pool LT | Core Pool LI | E-Mode CF              | E-Mode LT | E-Mode LI | E-Mode Borrow Allowed? |
| -------- | ------------ | ------------ | ------------ | ---------------------- | --------- | --------- | ---------------------- |
| **USDC** | 60%          | 65%          | 10%          | 90%                    | 93%       | 5%        | Yes                    |
| **DAI**  | 60%          | 65%          | 10%          | 90%                    | 93%       | 5%        | No                     |
| **UNI**  | 50%          | 55%          | 12%          | N/A (Fallback to Core) | N/A       | N/A       | N/A                    |

### Step 1: Alex in Core Pool

* Supplies: **$1,000 USDC**
* Borrows: **$500 DAI**

**Liquidity Calculation**:

$$
\text{Borrow Limit} = 1000 \times 0.6 = 600
$$

$$
\text{Liquidity} = \text{Borrow Limit} - \text{Total Borrow} = 600 - 500 = 100
$$

$$
\text{Shortfall} = 0 \quad (\text{since liquidity ≥ 0})
$$

* **Liquidity = $100, Shortfall = $0 → safe**

### Step 2: Attempting to Enter Stablecoins E-Mode

Alex wants to switch to the Stablecoins Pool.

* Borrowed DAI is **not allowed** in the target pool (`isBorrowAllowed = false`).
* Comptroller checks fail → transaction **reverts** with `IncompatibleBorrowedAssets`.

> This demonstrates **borrow restrictions in E-Mode**: existing borrows in disallowed markets prevent entering the pool.

### Step 3: Adjusting Position and Entering

* Alex repays $500 DAI → no active borrows.
* Calls `enterPool(stablecoinPoolId)` → succeeds.

**Liquidity Calculation in E-Mode**:

$$
\text{Borrow Limit} = 1000 \times 0.9 = 900
$$

$$
\text{Liquidity} = 900 - 0 = 900
$$

$$
\text{Shortfall} = 0 \quad (\text{safe})
$$

**Effect of E-Mode Activation**:

* **Collateral Factor (CF)** for USDC increases from 60% → 90%
* **Liquidation Incentive (LI)** for USDC decreases from 10% → 5%
* Can borrow USDC (allowed), but **cannot borrow DAI** (isBorrowAllowed = false).

### Step 4: Borrowing in E-Mode

* Alex borrows **$500 USDC**
* Borrowing **DAI fails** (restricted by pool-level `isBorrowAllowed`)

**Liquidity Calculation after borrow**:

$$
\text{Borrow Limit} = 1000 \times 0.9 = 900
$$

$$
\text{Liquidity} = 900 - 500 = 400
$$

$$
\text{Shortfall} = 0 \quad (\text{safe})
$$

### Step 5: Switching Back to Core

Suppose Alex borrowed up to **$850 USDC** in E-Mode.

**Core Pool Borrow Limit**:

$$
\text{Borrow Limit} = 1000 \times 0.6 = 600
$$

$$
\text{Liquidity} = 600 - 850 = -250
$$

$$
\text{Shortfall} = 250 \quad (\text{tx reverts})
$$

To switch safely, Alex can either:

1. **Add collateral** → $500 more USDC → new Core borrow limit = 1500 × 0.6 = 900 → liquidity = 900 - 850 = 50 → switch succeeds.
2. **Repay debt** → reduce borrow ≤ $600 → liquidity ≥ 0 → switch succeeds.

## Liquidator Considerations in E-Mode

The introduction of **E-Mode in the Core Pool** on BNB Chain also changes how liquidators must evaluate accounts and select assets for liquidation. To remain effective and profitable, liquidators need to adapt to the updated mechanics and new getter functions.

### 1. Per-Market Liquidation Incentives (LI)

* The Core Pool no longer uses a **global LI**.
* Each market now has its own **per-market liquidation incentive**, configurable by Governance.
* **Impact**: Liquidators must always query the market’s specific LI before deciding which collateral to seize, since incentives may differ significantly between assets.
* **Strategy**: Prioritize assets with higher LI when selecting which collateral to liquidate for maximum profit.

### 2. Liquidation Thresholds (LT) in Core Pool

* The Core Pool now supports **Liquidation Thresholds (LT)**, just like isolated pools.
* Unlike **Collateral Factors (CF)**, which only define borrow limits, **LT determines liquidation conditions**.
* **Impact**: Liquidators must check account health against LT rather than CF.
* **Recommended Function**:

  ```solidity
  function getAccountLiquidity(address account) external view returns (uint256, uint256, uint256);
  ```

  This function has been updated to use LT internally when computing liquidity and shortfall.

### 3. Effective Risk Factor Queries

Because E-Mode overrides Core parameters with pool-specific ones, the protocol provides new getters to return the **effective factors** for any user and market.

* **Get Effective LTV (Collateral Factor or LT depending on weighting):**

  ```solidity
  enum WeightFunction {
      USE_COLLATERAL_FACTOR,        // 0
      USE_LIQUIDATION_THRESHOLD     // 1
  }

  function getEffectiveLtvFactor(
      address account,
      address vToken,
      WeightFunction weightingStrategy
  ) external view returns (uint256);
  ```

  * Use `weightingStrategy = 0` to fetch the **effective CF**.
  * Use `weightingStrategy = 1` to fetch the **effective LT**.
* **Get Effective Liquidation Incentive (LI):**

  ```solidity
  function getEffectiveLiquidationIncentive(
      address account,
      address vToken
  ) external view returns (uint256);
  ```

***

### TL;DR: E-Mode in Venus BNB Chain Core Pool

**Efficiency Boost**: E-Mode lets you use **higher Collateral Factors (CF)** and **Liquidation Thresholds (LT)**, plus **lower Liquidation Incentives (LI)**, for some set of assets (e.g., stablecoins).

**Borrow Limits**: You can only borrow from pool-approved assets (`isBorrowAllowed = true`). If you have borrows in non-approved assets, you can't enter; new borrows are blocked to keep risks isolated.

**How Risk Factors Are Chosen**:

* **In E-Mode Pool**: Use the pool's CF/LT/LI values (even if set to 0).
* **If the asset isn't in your pool**:
  * If `allowCorePoolFallback = true`: Use Core Pool values.
  * If `allowCorePoolFallback = false` (default): Treat CF/LT/LI as 0; exit such markets.
* **Pool Disabled**: Falls back to Core Pool parameters.

**Liquidator Notes**:

* Core Pool now uses **per-market LI** (no global incentive). Liquidators should always fetch the effective liquidation incentive for each user’s market, as markets associated with different E-Mode pools may have different LI values. Additionally, assets outside the E-Mode pool that do not contribute to borrowing power may have **LI = 0**, even if supplied as collateral.
* **Liquidation Threshold (LT)** replaces CF as the trigger for liquidations—use `getAccountLiquidity` for checks.
* Use new getters to fetch **effective parameters** considering pool overrides:
  * `getEffectiveLtvFactor(account, vToken, 0)` → effective CF
  * `getEffectiveLtvFactor(account, vToken, 1)` → effective LT
  * `getEffectiveLiquidationIncentive(account, vToken)` → effective LI


# Trade

{% hint style="info" %}
Available on BNB Chain Core Pool.
{% endhint %}

## Overview

**Venus Trade** is a relative performance trading system built as a peripheral orchestration layer on top of Venus Protocol's existing lending and borrowing infrastructure. It allows users to express a view that one asset will outperform another — packaged into a single, easy-to-manage position with automated execution, proportional closing, and built-in yield generation.

Users deposit a stablecoin collateral (the **Default Settlement Asset**, or DSA), select a long and short vToken pair, and Trade handles all leveraged execution — flash loans, swaps, supply, and borrow — atomically in a single transaction.

This is not directional trading. Trade positions profit (or lose) based on the **relative price movement between two assets**, regardless of whether the overall market is going up or down. While held, positions also generate yield: supply APY on the long asset, DSA APY on collateral, minus borrow APY on the short asset.

| Component                          | Description                                                                                                                                                               |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **RelativePositionManager**        | The main orchestration contract that manages the full lifecycle of Trade positions — from activation and opening to proportional closing and deactivation                 |
| **PositionAccount**                | A dedicated smart contract account deployed per user per trading pair. All collateral, borrow positions, and yield accrual live here, fully isolated from other positions |
| **Paired Positions**               | Long and short legs treated as a single unit with combined PnL, health, and lifecycle management                                                                          |
| **Default Settlement Asset (DSA)** | A designated stablecoin (USDT or USDC) used as collateral and as the currency for all PnL settlement                                                                      |
| **Proportional Closing**           | Flexible partial or full position closing using on-chain flash loans and token swaps, with automatic dust handling                                                        |
| **Capital Utilization Tracking**   | Real-time calculation of how much deposited collateral is locked by open positions, enabling accurate withdrawable balance reporting                                      |

No changes were made to the Venus Core Pool, Comptroller, vToken contracts, interest rate models, oracles, or any existing protocol infrastructure. Existing users are not affected.

***

## Architecture

The Trade system consists of four contracts:

| Contract                      | Role                                                                                                                                                            |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **RelativePositionManager**   | Central orchestration contract. Manages position state, validates operations, calculates utilization, and delegates execution to PositionAccount.               |
| **PositionAccount**           | A minimal proxy clone deployed per user per trading pair. Holds all funds, enters Venus markets, and delegates leverage execution to LeverageStrategiesManager. |
| **LeverageStrategiesManager** | Pre-existing Venus periphery contract. Executes flash loans via the Comptroller and invokes the SwapHelper.                                                     |
| **SwapHelper**                | Pre-existing Venus periphery contract. Executes authorized on-chain swaps using signed multicall data.                                                          |

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-4995205d95515ca5d6ceab00671544383648e884%2Fimage-architecture.png?alt=media" alt="Trade Architecture Overview"><figcaption></figcaption></figure>

### Position Isolation

Every unique `(user, longVToken, shortVToken)` triple gets its own **PositionAccount** — a minimal EIP-1167 clone deployed with a deterministic CREATE2 salt. Collateral, debt, and Health Factor are entirely independent across accounts. A liquidation or loss on one PositionAccount cannot affect any other.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-dbba1b135db4234af8b4471929dd55eba1e2fe09%2Fimage-isolated.png?alt=media" alt="Trade Position Isolation"><figcaption></figcaption></figure>

| Layer                        | Mechanism                                                                                                                                                                                                                                               |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Fund custody**             | Each PositionAccount is a separate on-chain address. Venus vTokens and underlying balances are held there, not in the RelativePositionManager.                                                                                                          |
| **Access restriction**       | `PositionAccount.onlyRelativePositionManager` reverts any call not originating from the single trusted manager address.                                                                                                                                 |
| **Comptroller membership**   | Each PositionAccount enters Venus markets independently. Its Health Factor is computed solely from its own supplied and borrowed balances.                                                                                                              |
| **Delegate approval**        | During initialization the PositionAccount approves both the RelativePositionManager and LeverageStrategiesManager as delegates in the Comptroller — this allows flash-loan execution while keeping all debt on the PositionAccount, not on the manager. |
| **Deterministic deployment** | Clones are deployed with `ClonesUpgradeable.cloneDeterministic(impl, keccak256(user, long, short))`. A given user cannot create two accounts for the same pair; the address is unique and pre-determined.                                               |

***

## Key Concepts

### Long Leg and Short Leg

A Trade position always consists of two legs:

* **Long Leg** — the asset expected to outperform. Supplied into Venus to earn supply APY.
* **Short Leg** — the asset expected to underperform. Borrowed from Venus, creating an interest cost.

The two legs are treated as a single position with combined PnL — users never manage them separately.

### Default Settlement Asset (DSA)

When activating a position, the user selects a **Default Settlement Asset** (USDT or USDC). The DSA serves as:

* The initial collateral deposited into the PositionAccount
* The asset that backs borrow risk (its collateral factor determines how much can be borrowed)
* The currency in which realized profits are accumulated
* The asset used to cover losses on close

### Leverage

Leverage amplifies exposure to relative price movement. The maximum leverage is derived from the collateral factors of the DSA and long asset:

$$
\text{maxLeverage} = \frac{CF\_{DSA}}{1 - CF\_{long} \times (1 - \text{tolerance})}
$$

Where `tolerance` is the `proportionalCloseTolerance`, used as a buffer to prevent activations that would immediately be at the boundary of the safe borrow range. Leverage is recorded at activation and is fixed for the position's lifecycle.

### Capital Utilization

Capital utilization measures how much of the deposited DSA principal is "locked" by the open leveraged position. The remainder — **Available Capital** — can be withdrawn or used to scale the position further. See [Capital Utilization](#capital-utilization-1) for the full formula.

### Yield Generation

Trade positions generate yield while held:

| Component      | Description                                                      |
| -------------- | ---------------------------------------------------------------- |
| **Supply APY** | The long asset is supplied into Venus and earns lending yield    |
| **DSA APY**    | The stablecoin collateral is supplied into Venus and earns yield |
| **Borrow APY** | Interest paid on the borrowed short asset (deducted from yield)  |
| **Net APY**    | DSA APY + Supply APY − Borrow APY                                |

All yield accrues automatically and is reflected in position balances in real time. There is no claim or harvest step.

***

## Implementation Details

### 1. RelativePositionManager

The primary external interface for all Trade operations. All user-facing calls pass through this contract, which enforces lifecycle rules, validates parameters, computes capital utilization, and delegates execution to the PositionAccount.

**Inheritance:**

* `AccessControlledV8` — governance-gated admin functions via AccessControlManager
* `ReentrancyGuardUpgradeable` — reentrancy protection on all state-changing calls
* Custom two-level pause — `isPartiallyPaused` / `isCompletelyPaused` with `whenNotPaused` and `whenNotCompletelyPaused` modifiers

**Key state:**

```solidity
// Per-position state
mapping(address user => mapping(address longVToken => mapping(address shortVToken => Position))) positions;

// DSA configuration
mapping(uint8 index => address vToken) dsaVTokens;
mapping(address vToken => bool active) isDsaVTokenActive;

// Proportional close tolerance (BPS, default 100 = 1%)
uint256 proportionalCloseTolerance;
```

#### activateAndOpenPosition

Atomically activates a new position for the caller and executes the first leveraged open in a single transaction. If a PositionAccount clone does not yet exist for this `(user, long, short)` triple, it is deployed in the same transaction.

The function executes three sequential phases:

1. **Validation** — verifies that `longVToken` and `shortVToken` are listed in the Comptroller and are not the same market, that neither is vBNB, that the DSA index maps to an active registered DSA, that `effectiveLeverage` is in `[1e18, maxLeverage]`, and that no active position already exists for this pair.
2. **Activation** — deploys the PositionAccount clone if needed (via `ClonesUpgradeable.cloneDeterministic`), increments `cycleId`, records the position configuration, calls `Comptroller.enterMarketBehalf` to register the DSA market for the PositionAccount, and calls `vToken.mintBehalf` to deposit `initialPrincipal`. The returned vToken amount is stored as `suppliedPrincipalVTokens`.
3. **Open** — validates `shortAmount ≤ maxBorrow` (available capital × clamped leverage / short price), then calls `positionAccount.enterLeverage(longVToken, 0, shortVToken, shortAmount, minLongAmount, swapData)`. The LeverageStrategiesManager flash-loans `shortAmount`, swaps to the long asset, and supplies it to the PositionAccount's long vToken position.

```solidity
function activateAndOpenPosition(
    address longVToken,
    address shortVToken,
    uint8 dsaIndex,
    uint256 initialPrincipal,
    uint256 effectiveLeverage,
    uint256 shortAmount,
    uint256 minLongAmount,
    bytes calldata swapData
) external;
```

The caller must approve `initialPrincipal` of the DSA underlying to the RelativePositionManager before calling. The function uses `mintBehalf` so that the vToken balance accrues directly on the PositionAccount — the RPM itself holds no collateral.

***

#### scalePosition

Adds additional short exposure to an existing active position. Optionally accepts more DSA principal if `additionalPrincipal > 0`.

Scaling recalculates `maxBorrow` from the updated available capital (including any newly deposited principal) before validating `shortAmount`. The stored `effectiveLeverage` is **not updated** by scaling — it remains fixed at the value set during activation and is used for utilization calculations across the position's lifecycle.

```solidity
function scalePosition(
    IVToken longVToken,
    IVToken shortVToken,
    uint256 additionalPrincipal,
    uint256 shortAmount,
    uint256 minLongAmount,
    bytes calldata swapData
) external;
```

***

#### closeWithProfit

Proportionally closes a fraction of the position when the long asset has outperformed the short asset. Takes two swap legs: one to repay debt and one to harvest the profit into DSA principal.

The caller specifies `closeFractionBps` (1–10000) to indicate what fraction of the position to close. The protocol validates that the total long amount specified (`longAmountToRedeemForRepay + longAmountToRedeemForProfit`) is within `proportionalCloseTolerance` of the expected proportional amount:

```
expectedLong = currentLongBalance × closeFractionBps / 10000
assert |totalLong − expectedLong| ≤ expectedLong × tolerance / 10000
```

**Execution flow:**

1. Redeem `longAmountToRedeemForRepay` from the PositionAccount's long vToken position
2. Swap long → short via `positionAccount.exitLeverage()` to repay the proportional debt
3. Redeem `longAmountToRedeemForProfit` from the long vToken position
4. Swap long → DSA and mint as DSA principal on the PositionAccount
5. Update `suppliedPrincipalVTokens` += newly minted DSA vTokens (profit increases Available Capital)

```solidity
function closeWithProfit(
    IVToken longVToken,
    IVToken shortVToken,
    uint256 closeFractionBps,
    uint256 longAmountToRedeemForRepay,
    uint256 minAmountOutRepay,
    bytes calldata swapDataRepay,
    uint256 longAmountToRedeemForProfit,
    uint256 minAmountOutProfit,
    bytes calldata swapDataProfit
) external;
```

**One-leg close (skip profit conversion):** The profit swap leg is optional. Pass `longAmountToRedeemForProfit = 0`, `minAmountOutProfit = 0`, and `swapDataProfit = "0x"` to skip it. The repay leg always executes. If there is outstanding short debt, `longAmountToRedeemForRepay` must be non-zero — passing zero while debt exists reverts with `MinAmountOutRepayBelowDebt`.

***

#### closeWithLoss

Proportionally closes a fraction of the position when the long asset value is insufficient to cover the proportional short debt. Uses a two-leg approach to repay the full debt fraction.

* **Leg 1** — redeems long collateral, swaps long → short, repays as much debt as possible
* **Leg 2** — redeems DSA collateral, swaps DSA → short (or uses directly if DSA == short via `exitSingleAssetLeverage`), repays remaining debt

Leg 2 is optional — pass `dsaAmountToRedeemForSecondSwap = 0` if leg 1 fully covers the debt. The `suppliedPrincipalVTokens` counter is decremented by the DSA vTokens burned in leg 2, reflecting the principal consumed to cover the loss.

```solidity
function closeWithLoss(
    IVToken longVToken,
    IVToken shortVToken,
    uint256 closeFractionBps,
    uint256 longAmountToRedeemForFirstSwap,
    uint256 shortAmountToRepayForFirstSwap,
    uint256 minAmountOutFirst,
    bytes calldata swapDataFirst,
    uint256 dsaAmountToRedeemForSecondSwap,
    uint256 minAmountOutSecond,
    bytes calldata swapDataSecond
) external;
```

**One-leg close behavior:** Both legs are individually optional, enabling single-asset close paths:

| Close path    | How to invoke                                                                         | When to use                                                                                                  |
| ------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| **Long only** | Set `dsaAmountToRedeemForSecondSwap = 0`                                              | Leg 1 (long → short swap) fully covers the proportional debt; no DSA needs to be burned                      |
| **DSA only**  | Set `longAmountToRedeemForFirstSwap = 0` **and** `shortAmountToRepayForFirstSwap = 0` | Long collateral has been partially or fully seized by a liquidator; only DSA principal remains to repay debt |

**Restriction:** If `longAmountToRedeemForFirstSwap = 0` but `shortAmountToRepayForFirstSwap != 0`, the call reverts with `InvalidLongAmountToRedeem`. Skipping the long leg while claiming a non-zero repay from it would illegitimately reduce the second-leg repay obligation without any actual collateral being redeemed.

***

#### supplyPrincipal

Deposits additional DSA collateral into the PositionAccount without opening a new leveraged trade. Transfers DSA from the caller, calls `vToken.mintBehalf` to supply it on the PositionAccount, and increments `suppliedPrincipalVTokens`.

This is the primary **defensive Health Factor management** tool. Supplying more principal increases the DSA collateral backing the borrow, directly raising the Health Factor without any position unwind or swap. Users facing a declining Health Factor should call this before the position becomes liquidatable. Allowed during partial pause — classified as a defensive operation and never blocked during incidents.

```solidity
function supplyPrincipal(address longVToken, address shortVToken, uint256 amount) external;
```

***

#### withdrawPrincipal

Withdraws Available Capital (unused DSA collateral) from the PositionAccount to the caller's wallet.

The maximum withdrawable amount is computed by `getUtilizationInfo()` as `availableCapitalUSD / dsaPrice`. The function redeems the corresponding DSA vTokens from the PositionAccount and decrements `suppliedPrincipalVTokens`. Blocked during partial pause.

```solidity
function withdrawPrincipal(IVToken longVToken, IVToken shortVToken, uint256 amount) external;
```

***

#### deactivatePosition

Fully exits a position after the short debt has been repaid to zero. Exits all Venus markets, redeems all remaining collateral (both DSA and long), and transfers everything to the caller's wallet.

After deactivation, `isActive` is set to `false` and `suppliedPrincipalVTokens` is cleared. The PositionAccount contract remains deployed — a subsequent call to `activateAndOpenPosition` increments `cycleId` and starts a fresh cycle without redeploying the clone.

When DSA ≠ long, collateral is redeemed from two separate markets. When DSA == long, a single redemption is performed with a treasury fee grossup to avoid over-redemption:

```
grossedUpAmount = ⌈amount / (1 − treasuryPercent)⌉
```

```solidity
function deactivatePosition(IVToken longVToken, IVToken shortVToken) external;
```

***

### 2. PositionAccount

A minimal proxy clone (EIP-1167) deployed per user per trading pair. It holds all collateral and debt positions in Venus and is the sole entity that enters markets and executes borrows and supplies — the RelativePositionManager never holds funds itself.

**Deployment:** Clones use `ClonesUpgradeable.cloneDeterministic` with a CREATE2 salt:

```solidity
salt = keccak256(abi.encodePacked(user, longVToken, shortVToken))
```

This makes the PositionAccount address deterministic and pre-computable via `getPositionAccountAddress()` before the account is deployed.

**Access control:** The `onlyRelativePositionManager` modifier reverts any call not originating from the single trusted RelativePositionManager address. No EOA or other contract can directly move funds out of a PositionAccount.

#### initialize

Called once when the clone is first deployed. Sets the position owner, long vToken, and short vToken for this clone, and approves both the RelativePositionManager and LeverageStrategiesManager as delegates in the Venus Comptroller.

The delegate approval is what allows the RelativePositionManager to call `Comptroller.enterMarketBehalf` and `vToken.mintBehalf` on behalf of the PositionAccount while the PositionAccount remains the on-chain owner of all collateral and debt. The RelativePositionManager and LeverageStrategiesManager addresses are set in the constructor as immutables shared across all clones.

```solidity
function initialize(address owner_, address longVToken_, address shortVToken_) external;
```

***

#### enterLeverage

Initiates the flash loan sequence to open a leveraged long position. Called by the RelativePositionManager as part of `activateAndOpenPosition` and `scalePosition`.

Internally delegates to `LeverageStrategiesManager.flashLoan(...)`, which:

1. Flash-loans `borrowedAmountToFlashLoan` of the short asset from the Comptroller's flash loan module
2. Swaps the short asset to the long asset via the SwapHelper using `swapData`
3. Supplies the resulting long tokens to the PositionAccount's long vToken position via `vToken.mintBehalf`
4. Repays the flash loan by opening a borrow on the PositionAccount's short vToken position

```solidity
function enterLeverage(
    IVToken collateralMarket,
    uint256 collateralAmountSeed,
    IVToken borrowedMarket,
    uint256 borrowedAmountToFlashLoan,
    uint256 minAmountOutAfterSwap,
    bytes calldata swapData
) external onlyRelativePositionManager;
```

***

#### exitLeverage

Initiates the flash loan sequence to close a leveraged position — redeem collateral, swap, and repay debt. Used in both `closeWithProfit` and `closeWithLoss`.

Internally delegates to `LeverageStrategiesManager.flashLoan(...)`, which:

1. Flash-loans `borrowedAmountToFlashLoan` of the short asset
2. Redeems `collateralAmountToRedeemForSwap` from the PositionAccount's collateral vToken position
3. Swaps the redeemed collateral to the short asset via the SwapHelper
4. Repays the flash loan + the original borrow debt using the swap output

```solidity
function exitLeverage(
    IVToken collateralMarket,
    uint256 collateralAmountToRedeemForSwap,
    IVToken borrowedMarket,
    uint256 borrowedAmountToFlashLoan,
    uint256 minAmountOutAfterSwap,
    bytes calldata swapData
) external onlyRelativePositionManager;
```

***

#### exitSingleAssetLeverage

Used when the DSA and the short asset are the same token (e.g., the position uses USDC as both DSA and borrow). In this case, no swap is required to repay the debt — DSA collateral is redeemed directly to cover the borrow.

```solidity
function exitSingleAssetLeverage(
    IVToken collateralMarket,
    uint256 collateralAmountToFlashLoan
) external onlyRelativePositionManager;
```

***

## Position Data Model

```solidity
struct Position {
    address user;                     // Position owner
    address longVToken;               // vToken for the long (supplied) asset
    address shortVToken;              // vToken for the short (borrowed) asset
    address positionAccount;          // Address of the PositionAccount clone
    bool isActive;                    // Whether the position is currently active
    uint8 dsaIndex;                   // Index of the DSA in the dsaVTokens registry
    address dsaVToken;                // vToken for the DSA (collateral and settlement)
    uint256 suppliedPrincipalVTokens; // DSA principal tracked in vToken units
    uint256 effectiveLeverage;        // Stored leverage in 1e18 mantissa
    uint256 cycleId;                  // Incremented each time a position is activated
}
```

**cycleId** provides a monotonically increasing identifier for each activation. This allows off-chain systems and events to distinguish between different activation cycles of the same pair without ambiguity.

**suppliedPrincipalVTokens** is updated on every principal supply, withdrawal, profit conversion, and after liquidations (synced down if the actual balance has been reduced by a liquidator).

***

## Position Lifecycle

### Stage 1: Activation and Opening

`activateAndOpenPosition()` is the entry point. It combines activation and first open into one atomic transaction.

**Activation steps:**

1. Validate that the long and short markets are listed in the Comptroller and are not the same market
2. Validate that vBNB is not used (not supported)
3. Validate that the DSA index refers to an active, registered DSA vToken
4. Validate leverage is in the range `[1e18, maxLeverage]`
5. Deploy a PositionAccount clone if one does not exist for this `(user, long, short)` triple
6. Set `isActive = true`, increment `cycleId`, record `dsaIndex`, `dsaVToken`, `effectiveLeverage`
7. Enter the DSA market on behalf of the PositionAccount via `Comptroller.enterMarketBehalf`
8. Transfer DSA from the user and mint vTokens on behalf of the PositionAccount via `vToken.mintBehalf`
9. Record `suppliedPrincipalVTokens`
10. Emit `PositionActivated` and `PrincipalSupplied`

**Opening steps (executed after activation):**

1. Calculate `maxBorrow` from available capital and clamped leverage
2. Validate `shortAmount <= maxBorrow`
3. Call `positionAccount.enterLeverage(longVToken, 0, shortVToken, shortAmount, minLongAmount, swapData)`
4. LeverageStrategiesManager flash-loans `shortAmount` of the short asset, swaps to the long asset, and mints it on the PositionAccount
5. Any dust from the swap is swept from the PositionAccount to the user's wallet
6. Emit `PositionOpened`

**Maximum leverage formula:**

$$
\text{maxLeverage} = \frac{CF\_{DSA}}{1 - CF\_{long} \times (1 - \text{tolerance})}
$$

Where `tolerance` is the `proportionalCloseTolerance` used as a buffer. This prevents activations that would immediately be at the boundary of the safe borrow range.

***

### Stage 2: Scaling (Adding to a Position)

`scalePosition()` adds additional exposure to an existing active position.

* Optionally supplies additional DSA principal if `additionalPrincipal > 0`
* Recalculates `maxBorrow` using the updated available capital
* Calls `positionAccount.enterLeverage(...)` for the additional amount
* Emits `PositionScaled`

The stored `effectiveLeverage` is not changed by scaling. Only `suppliedPrincipalVTokens` increases if additional principal is supplied.

***

### Stage 3: Closing

Closing is proportional. The caller specifies `closeFractionBps` (1–10000 BPS, where 10000 = 100%) and the system closes exactly that fraction of the current position.

There are two closing paths depending on whether the position is in profit or in loss.

#### Profit Close (`closeWithProfit`)

Used when the long asset value (converted to short asset) exceeds the proportional short debt.

**Validation:**

```
expectedLong = currentLongBalance × closeFractionBps / 10000
totalLong = longAmountToRedeemForRepay + longAmountToRedeemForProfit
assert |totalLong − expectedLong| ≤ expectedLong × proportionalCloseTolerance / 10000
assert minAmountOutRepay ≥ expectedShortDebt (with tolerance bump for 100% closes)
```

**Execution:**

1. Redeem `longAmountToRedeemForRepay` from the PositionAccount's long vToken position
2. Swap long → short via `positionAccount.exitLeverage()`
3. Repay `amountToRepay` of short debt
4. Redeem `longAmountToRedeemForProfit` from the long vToken position
5. Swap long → DSA and mint as DSA principal on the PositionAccount
6. Update `suppliedPrincipalVTokens` += newly minted DSA vTokens
7. Emit `PositionClosed` and `ProfitConverted`

#### Loss Close (`closeWithLoss`)

Used when the proportional long asset value is insufficient to cover the full proportional short debt.

**Two-leg repayment:**

* **Leg 1:** Redeem long collateral → swap to short → repay as much debt as possible
* **Leg 2:** Redeem DSA collateral → swap to short (or use directly if DSA == short) → repay remaining debt

Both legs are individually optional. Set `dsaAmountToRedeemForSecondSwap = 0` to use long collateral only (leg 1 fully covers debt). Set `longAmountToRedeemForFirstSwap = 0` and `shortAmountToRepayForFirstSwap = 0` together to use DSA only (leg 2 only) — this is the correct path when the long collateral has been partially or fully seized by a liquidator and only DSA principal remains.

**Validation:**

```
expectedLong = currentLongBalance × closeFractionBps / 10000
assert |longAmountToRedeemForFirstSwap − expectedLong| within tolerance
expectedShort = currentShortDebt × closeFractionBps / 10000
amountToRepayFirst = min(leg1Output, expectedShort)
amountToRepaySecond = expectedShort − amountToRepayFirst
assert shortDust ≤ expectedShort × proportionalCloseTolerance / 10000
```

**Execution:**

1. Leg 1 (if `longAmountToRedeemForFirstSwap > 0`): `positionAccount.exitLeverage(longVToken → shortVToken)` — redeems long collateral, swaps to short, repays debt
2. Leg 2 (if `dsaAmountToRedeemForSecondSwap > 0`): `positionAccount.exitLeverage(dsaVToken → shortVToken)` — or `exitSingleAssetLeverage` when DSA == short
3. Update `suppliedPrincipalVTokens` -= DSA vTokens burned in leg 2
4. Emit `PositionClosed`

***

### Stage 4: Principal Management

**`supplyPrincipal()`** — deposit additional DSA without opening a new trade. Transfers DSA from the user, mints vTokens on the PositionAccount, increments `suppliedPrincipalVTokens`. This is the recommended action when the Health Factor is declining: adding DSA principal directly increases the collateral backing the borrow and raises HF without any position unwind. A small partial close (`closeWithProfit` or `closeWithLoss` with a low `closeFractionBps`) is the alternative: it repays a proportional slice of debt, which similarly raises HF but also reduces position size.

**`withdrawPrincipal()`** — withdraw Available Capital back to the user's wallet. Validates that `amount <= withdrawableAmount` (from `getUtilizationInfo()`), redeems the corresponding DSA vTokens from the PositionAccount, and decrements `suppliedPrincipalVTokens`.

***

### Stage 5: Deactivation

`deactivatePosition()` fully exits the position account for a trading pair.

**Prerequisites:** The short debt must be fully repaid (zero borrow balance).

**Steps:**

1. Set `isActive = false`, clear `suppliedPrincipalVTokens`
2. Exit all markets and redeem all remaining collateral:
   * **DSA ≠ long:** Redeem all long vTokens, exit DSA market, redeem all DSA vTokens separately
   * **DSA == long:** Redeem all vTokens from the shared market (accounting for the treasury redemption fee grossup)
3. Transfer all redeemed underlying to the user's wallet
4. Emit `PositionDeactivated`

After deactivation, the PositionAccount contract remains deployed. A new activation (`activateAndOpenPosition`) will increment `cycleId` and start a fresh position cycle without redeploying the contract.

***

## Capital Utilization

Capital utilization determines how much of the user's DSA principal is "in use" by the open position and how much can be withdrawn or used to open new positions.

### Formula

```
// Nominal utilization: based on the stored leverage ratio
nominalCapitalUtilized = ⌈borrowValueUSD × 1e18 / clampedLeverage⌉

// Actual utilization: based on how much DSA backing is needed given long collateral
excessBorrow = max(0, borrowValueUSD − longValueUSD × CF_long)
actualCapitalUtilized =
    if CF_dsa > 0: ⌈excessBorrow × 1e18 / CF_dsa⌉
    if CF_dsa == 0 and excessBorrow > 0: suppliedPrincipalUSD  // entire principal is at risk

// Final utilization: conservative (use whichever is higher)
finalCapitalUtilized = min(suppliedPrincipalUSD, max(nominalCapitalUtilized, actualCapitalUtilized))

// Available capital
availableCapitalUSD = suppliedPrincipalUSD − finalCapitalUtilized
withdrawableAmount = availableCapitalUSD × 1e18 / dsaPrice
```

### Leverage Clamping

The stored `effectiveLeverage` is the leverage set at activation. However, if collateral factors change after activation (e.g., governance lowers a CF), the actual maximum leverage may drop below the stored value. In this case, `clampedLeverage = min(storedLeverage, currentMaxLeverage)` is used for all utilization and borrow capacity calculations.

This ensures that capital utilization reporting and borrow limits remain conservative and safe even when market parameters change post-activation.

### Two Utilization Paths

The system tracks utilization from two perspectives simultaneously:

**Nominal:** "Given the leverage ratio I agreed to, how much capital does this position conceptually consume?"

* This path uses the stored leverage and is predictable for users.

**Actual:** "Given the current value of my long collateral and short debt, how much DSA backing is actually needed to maintain the position safely?"

* This path accounts for real market prices and collateral factors.
* The long asset's CF reduces the DSA backing needed (long collateral partially covers the borrow).
* As prices diverge from the entry, this number can grow.

The **maximum of the two** is used as the final utilization, ensuring the most conservative estimate is always applied.

***

## DSA Mechanics

### DSA ≠ Long (Standard Case)

The PositionAccount holds two separate vToken positions:

* Long vTokens (from leveraged swaps)
* DSA vTokens (user's principal)

Principal tracking: `suppliedPrincipalVTokens` directly maps to the DSA vToken balance. Long collateral balance is simply `longVToken.balanceOfUnderlying(positionAccount)`.

### DSA == Long (Shared Market Case)

When the user selects a DSA that is the same token as the long asset (e.g., USDC as both DSA and long), the PositionAccount holds only **one** vToken balance that represents the sum of both.

The RelativePositionManager separates them using:

```
totalVTokens = dsaVToken.balanceOf(positionAccount)
longCollateralVTokens = totalVTokens − suppliedPrincipalVTokens
```

When `suppliedPrincipalVTokens` exceeds `totalVTokens` (which can happen if the position is partially liquidated), the system syncs `suppliedPrincipalVTokens` down to `totalVTokens` and emits `RefreshedSuppliedPrincipal`. This prevents the system from treating liquidated collateral as still available.

**Deactivation in this case** redeems from the shared market in a single call, accounting for the treasury fee grossup to avoid over-redemption:

```
grossedUpAmount = ⌈amount / (1 − treasuryPercent)⌉
```

***

## Proportional Close Tolerance

`proportionalCloseTolerance` (default 100 BPS = 1%) defines the acceptable deviation band for close amounts.

**Why it is needed:** Interest accrues continuously on the borrow position, and swap outputs are subject to slippage. The close amount derived from `closeFractionBps × currentBalance` will not match exactly by the time the transaction executes.

**Tolerance band for long amounts:**

```
lower = expectedLong × (10000 − tolerance) / 10000
upper = expectedLong × (10000 + tolerance) / 10000
assert lower ≤ totalLong ≤ upper
```

**Tolerance bump for 100% closes:** For full closes (closeFractionBps = 10000), the repay amount is bumped by the tolerance to ensure that accumulated interest since the close was estimated does not cause a dust leftover:

```
amountToRepay = expectedShortDebt × (10000 + tolerance) / 10000
```

Any short dust remaining after the repay is validated to be within:

```
shortDust ≤ expectedShort × tolerance / 10000
```

**Governance can adjust `proportionalCloseTolerance`** via `setProportionalCloseTolerance()`. A wider tolerance allows more flexibility but reduces proportionality precision; a narrower tolerance enforces stricter accounting but may cause more reverts due to market movement between estimation and execution.

***

## Flash Loan Integration

Trade uses the existing `LeverageStrategiesManager` for all flash loan execution. The PositionAccount calls into the LeverageStrategiesManager which uses the Venus Comptroller's flash loan module.

### Opening a Position (`enterLeverage`)

```
positionAccount.enterLeverage(
    collateralMarket = longVToken,
    collateralAmountSeed = 0,           // DSA is the true collateral seed, not long
    borrowedMarket = shortVToken,
    borrowedAmountToFlashLoan = shortAmount,
    minAmountOutAfterSwap = minLongAmount,
    swapData = signedMulticallData
)

LeverageStrategiesManager:
  1. Flash loan shortAmount on shortVToken
  2. Swap shortAmount (short) → longAmount (long) using swapData
  3. Mint longAmount into positionAccount.longVToken position
  4. Repay flash loan from positionAccount.shortVToken (opens borrow)
```

### Closing a Position (`exitLeverage`)

```
positionAccount.exitLeverage(
    collateralMarket = longVToken,
    collateralAmountToRedeemForSwap = longAmountToRedeem,
    borrowedMarket = shortVToken,
    borrowedAmountToFlashLoan = shortAmountToRepay,
    minAmountOutAfterSwap = minAmountOutRepay,
    swapData = signedMulticallData
)

LeverageStrategiesManager:
  1. Flash loan shortAmountToRepay on shortVToken
  2. Redeem longAmountToRedeem from positionAccount.longVToken
  3. Swap longAmount (long) → shortAmount (short) using swapData
  4. Repay flash loan + original debt
```

### Same-Asset Close (`exitSingleAssetLeverage`)

When DSA == short (e.g., closing a loss using USDC as both DSA and borrow repayment), no swap is needed:

```
positionAccount.exitSingleAssetLeverage(
    collateralMarket = dsaVToken,      // same as shortVToken
    collateralAmountToFlashLoan = amountToRepay
)

LeverageStrategiesManager:
  1. Flash loan amountToRepay on dsaVToken
  2. Redeem amountToRepay from positionAccount.dsaVToken
  3. Repay flash loan + original debt (no swap)
```

***

## Liquidation

Trade positions are subject to the standard Venus liquidation mechanism. The PositionAccount is a regular address from the Comptroller's perspective — it enters markets, borrows, and supplies like any other account.

When the Health Factor drops below 1 (i.e., the borrow value exceeds the liquidation threshold-weighted collateral value), third-party liquidators can call `liquidateBorrow` on the appropriate vToken. This repays part of the debt and seizes a portion of the supplied collateral at a discount.

**Post-liquidation behavior:**

* If DSA == long, `suppliedPrincipalVTokens` may now exceed the actual vToken balance. The next call to `_getLongCollateralBalance` or `_getSuppliedPrincipalBalance` will detect this and emit `RefreshedSuppliedPrincipal` to sync the counter down.
* The position remains `isActive = true` after liquidation. The user can still manage (close, supply, withdraw) the reduced position.

***

## Pause Mechanism

The contract supports two pause levels:

| Pause Level        | Blocked Operations                                                                    | Allowed Operations                                    |
| ------------------ | ------------------------------------------------------------------------------------- | ----------------------------------------------------- |
| **Partial Pause**  | `activateAndOpenPosition`, `scalePosition`, `withdrawPrincipal`, `deactivatePosition` | `closeWithProfit`, `closeWithLoss`, `supplyPrincipal` |
| **Complete Pause** | All state-changing operations                                                         | None                                                  |

This design allows governance to freeze risky operations (opening new exposure, withdrawing, deactivating) while keeping defensive operations (closing, adding collateral) available during an incident.

***

## Governance and Access Control

All privileged functions are gated via the Venus `AccessControlManager`.

| Function                            | Permission Required | Notes                                         |
| ----------------------------------- | ------------------- | --------------------------------------------- |
| `setPositionAccountImplementation`  | Admin               | One-time; locked after first call             |
| `addDSAVToken`                      | Admin               | Adds a new stablecoin to the DSA registry     |
| `setDSAVTokenActive`                | Admin               | Enables/disables DSA for new activations      |
| `setProportionalCloseTolerance`     | Admin               | Adjusts the close tolerance band              |
| `partialPause` / `partialUnpause`   | Admin               | Toggles partial pause                         |
| `completePause` / `completeUnpause` | Admin               | Toggles complete pause                        |
| `executePositionAccountCall`        | Admin               | Emergency call forwarder (e.g., asset rescue) |

**DSA management:** When a DSA is set to inactive (`setDSAVTokenActive(index, false)`), existing positions using that DSA are unaffected. Only new position activations are blocked from selecting that DSA index.

**PositionAccount implementation:** The EIP-1167 clone implementation is set once and then locked (`isPositionAccountImplementationLocked = true`). This prevents governance from changing the implementation after positions have been deployed, ensuring users' PositionAccount contracts are immutable.

***

## Security Considerations

### Access Control

* Only the RelativePositionManager can call PositionAccount methods. All external position operations are gated through this single entrypoint.
* All admin functions are controlled by the Venus `AccessControlManager`.

### Reentrancy

* `ReentrancyGuardUpgradeable` is applied to all state-changing external functions on the RelativePositionManager.

### Oracle Dependency

* All USD value calculations use the Venus `ResilientOracle`. If an oracle price is zero or stale, the relevant function reverts with `InvalidOraclePrice`.

### Flash Loan Safety

* The flash loan callback (`executeOperation`) on the LeverageStrategiesManager validates that the initiator is the LeverageStrategiesManager itself and that the caller is the Comptroller, preventing unauthorized replay.

### Proportional Close Integrity

* The tolerance band enforces that close amounts cannot deviate significantly from the intended BPS fraction. This prevents griefing attacks that would allow disproportionate collateral extraction relative to debt repayment.

### PositionAccount Immutability

* The EIP-1167 clone implementation is locked after the first call to `setPositionAccountImplementation`. This means the RelativePositionManager cannot upgrade the PositionAccount logic for existing positions after deployment.

***

## Audits

RelativePositionManager and PositionAccount were audited by CertiK and Quantstamp before mainnet deployment. Audit reports are available in the [venus-periphery repository](https://github.com/VenusProtocol/venus-periphery/tree/main/audits) and in the [Security & Audits](/links/security-and-audits) section of the Venus documentation.


# Fixed Term Vaults

A Fixed Term Vault is a fixed-term loan between an institution and a pool of on-chain suppliers. The institution borrows a configured supply asset against collateral at a scheduled rate and duration set at vault creation. Suppliers receive vault shares representing a claim on the assets available at settlement. If the loan is fully repaid, redemption includes principal and the supplier portion of scheduled interest; partial-interest or principal shortfalls can reduce the payout, so principal and return are not guaranteed.

Each loan clone keeps separate collateral, debt accounting, and supplier shares and does not share liquidity with other vault clones or Venus Core markets. It nevertheless relies on shared controller, PositionToken, oracle, ProtocolShareReserve, LiquidationAdapter, and governance components, so shared dependencies and parameters can affect more than one vault.

{% hint style="warning" %}
This article describes the published contract architecture. Deployed controller and adapter implementations, each vault's clone implementation and state, risk parameters, whitelists, and ACM permissions are live on-chain data. Verify them at a recorded block using the [deployed-contracts page](/deployed-contracts/fixed-rate-vaults); repository source and deployment records alone do not prove the current on-chain configuration.
{% endhint %}

## Architecture

### Contracts

The system is composed of four primary contracts. Each loan's assets, debt accounting, and supplier shares live in a separate clone, while the controller registry, PositionToken, oracle, ProtocolShareReserve, and LiquidationAdapter are shared system components.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-a889fd124acec21491cac80c684e2a0d6b8dd693%2Ffixed-rate-vault-architecture.svg?alt=media" alt="Fixed Term Vault contract architecture diagram"><figcaption><p>The controller deploys a fresh vault clone and mints a position NFT per loan; all liquidation calls are routed through the LiquidationAdapter</p></figcaption></figure>

[**InstitutionalVaultController**](/technical-reference/reference-fixed-rate-vaults/institutional-vault-controller) is the sole factory and the only conduit through which any admin operation reaches a vault. It deploys each vault clone and mints the corresponding position NFT in the same transaction. It also exposes the ACM-gated lifecycle calls (`openVault`, `cancelVault`, `partialPauseVault`, `completePauseVault`, `unpauseVault`, `closeVault`) and risk-parameter updates that governance invokes separately over the life of each vault. All ACM permission checks are enforced here — individual vaults carry no ACM wiring and trust calls from the controller implicitly. The controller is deployed behind a transparent proxy, so its live implementation and effective ACM permissions must be resolved on-chain rather than inferred from a repository release.

[**InstitutionalLoanVault**](/technical-reference/reference-fixed-rate-vaults/institutional-loan-vault) is the core execution contract for a single loan. It holds all assets — collateral and supply stablecoin — enforces the `VaultState` machine, and is the only place debt is created, tracked, and cleared. Suppliers interact through the ERC-4626 interface (`deposit`, `mint`, `withdraw`, `redeem`); institution-side calls (`depositCollateral`, `claimRaisedFunds`, `withdrawCollateral`) are gated by `onlyPositionHolder`; liquidation entry points are gated by `onlyLiquidationAdapter`. Each vault is a non-upgradeable, single-use EIP-1167 clone. Its supply asset, collateral asset, scheduled rate, caps, and lifecycle durations are set at creation. Its liquidation threshold, liquidation incentive, and late-penalty rate can be updated per vault through the controller; shared adapter parameters and controller dependencies are also governance-controlled. A renewed deal uses a new clone rather than resetting an existing vault.

[**InstitutionPositionToken**](/technical-reference/reference-fixed-rate-vaults/institution-position-token) is a singleton ERC-721 shared across all vaults — one contract, one token ID per vault. Whoever holds a given token ID controls all institution-side operations on that vault, since `depositCollateral`, `claimRaisedFunds`, and `withdrawCollateral` all resolve to `positionToken.ownerOf(positionTokenId)` at call time. Keeping ownership in a transferable NFT rather than hardcoded in the vault means control can move to a new address without any state change inside the vault itself. Every transfer requires a prior single-use governance approval via `approvePositionTransfer(vault, recipient)`, consumed on the next `safeTransferFrom`.

[**LiquidationAdapter**](/technical-reference/reference-fixed-rate-vaults/liquidation-adapter) is the only address permitted to call `vault.liquidate()`, enforced by `onlyLiquidationAdapter` on each vault. The adapter controls the liquidator and settler whitelists, global close factor, and protocol share of liquidation incentives. Each vault stores its per-vault liquidation threshold, liquidation incentive, and late-penalty rate, checks liquidation eligibility, and calculates the seize amount; the adapter routes repayment and distributes seized collateral. The adapter also accumulates the protocol's share of seized collateral and transfers it to PSR via `sweepProtocolShareToReserve(address collateral)`.

### BaseVault, ERC-4626, and extensibility

`InstitutionalLoanVault` inherits `BaseVault`, an abstract contract built on top of ERC-4626 that was designed to be reusable across different kinds of fixed-term vault. `BaseVault` captures everything that any such vault has in common — fundraising, interest computation, the settlement waterfall, state machine scaffolding, and the pause system — so a new vault type only has to implement what is specific to its loan structure. Future vault types follow the same pattern: inherit `BaseVault`, override its three virtual hooks (`_checkAndAdvanceState`, `_afterWithdrawHook`, `_beforeClaimRaisedFunds`), and add the remaining type-specific logic on top.

ERC-4626 is used as the supplier-facing API, but the vault changes the usual limit behavior to enforce its lifecycle. ERC-4626 uses the `max*` views to report current limits; it does not require deposits or withdrawals to remain open in every state. Calls that request more than the reported maximum are normally expected to revert. This implementation instead clamps `deposit` and `mint` requests to the remaining fundraising capacity:

| Method        | Relevant ERC-4626 semantics                                          | Vault behavior                                                                                                                 |
| ------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `deposit`     | Deposits an exact asset amount; `previewDeposit` rounds shares down  | Only in `Fundraising`; assets above `maxDeposit` are clamped to the remaining capacity, then shares round down                 |
| `mint`        | Mints an exact share amount; `previewMint` rounds required assets up | Only in `Fundraising`; shares above `maxMint` are clamped, then required assets round up                                       |
| `withdraw`    | Withdraws an exact asset amount; `previewWithdraw` rounds shares up  | Only in `Matured`, `Failed`, or `Liquidated`; requests above `maxWithdraw` revert                                              |
| `redeem`      | Redeems an exact share amount; `previewRedeem` rounds assets down    | Only in `Matured`, `Failed`, or `Liquidated`; requests above `maxRedeem` revert                                                |
| `maxDeposit`  | Reports the current asset limit and may return zero                  | `maxBorrowCap − totalRaised` in `Fundraising`; zero otherwise                                                                  |
| `maxMint`     | Reports the current share limit and may return zero                  | Converts remaining asset capacity to shares with rounding down; zero outside `Fundraising`                                     |
| `maxWithdraw` | Reports the owner's current asset limit                              | `previewRedeem(balanceOf(owner))` in `Matured`, `Failed`, or `Liquidated`; zero otherwise                                      |
| `maxRedeem`   | Reports the owner's current share limit                              | `balanceOf(owner)` in `Matured`, `Failed`, or `Liquidated`; zero otherwise                                                     |
| `totalAssets` | Reports assets managed by the vault according to its accounting      | `settlementAmount` only in `Matured`, `Failed`, or `Liquidated`; `totalRaised` in every other stored state, including `Closed` |

`totalAssets()` is backed by internal accounting variables (`totalRaised` and `settlementAmount`) rather than directly returning `balanceOf(address(this))`. A direct token transfer therefore does not immediately change the reported share price through `totalAssets()`, avoiding the donation-based share-price manipulation path found in naive ERC-4626 implementations. The actual token balance is still read when the settlement waterfall runs. After the controller changes a fully resolved vault from one of those three states to `Closed`, the implementation's state branch returns `totalRaised` again; governance is expected to close only after suppliers have withdrawn, and integrations should not treat `Closed.totalAssets()` as a live redemption quote.

`minSupplierDeposit` adds a minimum deposit floor absent from the spec. The floor is waived for the final deposit that fills the remaining capacity exactly, preventing the vault from getting permanently stuck below `maxBorrowCap` when the residual slot is smaller than the minimum. Fee-on-transfer and rebasing tokens are not supported for either the supply asset or collateral.

### Oracle

All USD valuations in the system route through Venus `ResilientOracle`. For an asset with `d` decimals, `oracle.getPrice(asset)` is scaled to `36 − d` decimals. The vault multiplies an amount expressed in the asset's smallest units by that price and divides by `1e18`; the result is an 18-decimal USD value. The raw oracle return is therefore not itself an 18-decimal price for every asset. A zero price reverts with `InvalidOraclePrice`.

Both the supply asset and the collateral asset must have a non-zero oracle price at vault creation. The controller probes the oracle during `createVault` and reverts with `InvalidConfig` if either price is missing. This prevents a vault from entering price-dependent states — liquidation checks, claim validation, bad-debt detection — with an asset the oracle cannot price.

## State machine

The vault tracks lifecycle as a `VaultState` enum. Transitions are monotonic — no state ever goes backward. Time passing alone does not write a new state: storage advances only when a function that invokes `_checkAndAdvanceState()` runs. Those paths include the supplier ERC-4626 entry points, collateral and claim operations, repayment, liquidation, bad-debt rescue, and the permissionless `updateVaultState()` function. Controller-only functions such as pause, parameter updates, sweep, cancel, and close do not all invoke this hook, and view calls report the stored state without advancing it.

The hook can catch up the time- and debt-dependent `Fundraising → Lock/Failed → PendingSettlement → Matured/SettlementDeadlineExceeded` chain on a relevant call. `Liquidated` still requires a successful `repayBadDebt` call, and `Closed` requires the controller to call `closeVault()`. Anyone can call `updateVaultState()` when a hook-driven transition is ready but no other interaction is pending.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-6e10c8791b9cff11d9a6bb90ecedb562e99d7494%2Ffixed-rate-vault-state-machine.svg?alt=media" alt="Fixed Term Vault state machine diagram"><figcaption><p>State transitions are monotonic — no state ever goes backward. Dashed red paths show cancel and failure routes; the grey arrows at the bottom show all three terminal states collapsing into Closed via <code>closeVault()</code></p></figcaption></figure>

## Core mechanics

### Margin deposit

Before suppliers can interact, the institution must post a security deposit — the required margin — in a single `depositCollateral` call. It acts as a credible commitment: the institution either tops up collateral to `idealCollateralAmount` before fundraising closes or forfeits the margin to suppliers. The vault stays in `WaitingForMargin` until this condition is met:

$$\text{requiredMargin} = \left\lfloor\text{idealCollateralAmount} \times \frac{\text{marginRate}}{10^{18}}\right\rfloor$$

Once `totalCollateralDeposited ≥ requiredMargin` the vault advances to `MarginDeposited`, where it waits for governance to inspect the configuration and call `openVault()` to open the fundraising window.

### Fundraising

`deposit` and `mint` are permissionless during `Fundraising`. Unlike the usual ERC-4626 over-limit behavior, both calls clamp to remaining capacity (`maxBorrowCap - totalRaised`): an oversized `deposit` fills the residual and computes shares with rounding down; an oversized `mint` clamps the share request to `maxMint` and computes the required assets with rounding up. If the vault is already full, the clamped amount is zero and the call reverts with `ExceedsMaxCap`. `minSupplierDeposit` is enforced on every call except one that fills or exceeds the remaining capacity — because the residual may be smaller than the minimum, skipping the check on that deposit prevents the vault from being permanently stuck below `maxBorrowCap`. Share tokens are standard ERC-20s, freely transferable at all times.

At `fundraisingEndTime` both sides are evaluated simultaneously. If `totalRaised ≥ minBorrowCap` and `totalCollateralDeposited ≥ idealCollateralAmount` the vault transitions to `Lock` and the loan begins.

If either condition is not met, the vault transitions to `Failed`. Two distinct failure modes are possible, distinguished by `InstitutionalRuntime.institutionDefaulted`:

**Raise shortfall** (`totalRaised < minBorrowCap` at window close). `institutionDefaulted = false`. No default has occurred; suppliers recover full principal and the institution recovers all collateral including the margin.

**Collateral underdelivery** (`totalRaised ≥ minBorrowCap` but `totalCollateralDeposited < idealCollateralAmount` at window close). `institutionDefaulted = true`. Only the pre-determined margin is confiscated, not the institution's full collateral position. `confiscatedMargin` is set to exactly `requiredMargin`:

$$\text{requiredMargin} = \left\lfloor\text{idealCollateralAmount} \times \frac{\text{marginRate}}{10^{18}}\right\rfloor$$

Suppliers recover principal plus a pro-rata share of `confiscatedMargin` (see [Margin confiscation](#margin-confiscation-collateral-underdelivery) for the per-redemption distribution). The institution recovers `totalCollateralDeposited - confiscatedMargin`.

#### Margin confiscation: collateral underdelivery

When the vault fails via collateral underdelivery, `confiscatedMargin = requiredMargin`. Each `withdraw` / `redeem` triggers `_afterWithdrawHook`, which distributes a pro-rata slice of the remaining confiscated margin:

$$\text{compensation} = \text{confiscatedMarginRemaining} \times \frac{\text{shares}}{\text{totalSupplyBeforeBurn}}$$

Compensation is denominated in the **collateral asset** (e.g. BTC or ETH), not the supply stablecoin. Each calculation uses integer division and rounds down to collateral-token units. `confiscatedMarginRemaining` and the share supply both decrease after each redemption, so the formula allocates the remaining pool pro rata, but smallest-unit rounding can leave residual collateral for later redeemers; exact per-share equality across redemption order is not guaranteed.

### Lock entry and borrowing

When the vault transitions to `Lock`, two values are fixed for the lifetime of the loan:

**Total interest** is stored immediately as `totalDebt` and is owed in full regardless of when the institution repays — there is no early-repayment discount:

$$\text{totalInterest} = \frac{\text{totalRaised} \times \text{fixedAPY} \times \text{lockDuration}}{\text{BPS} \times \text{YEAR}}$$

`BPS = 10000`, `YEAR = 365 days`. Solidity integer division rounds `totalInterest` down to supply-asset units. `totalDebt = totalInterest` at lock entry; after `claimRaisedFunds` it becomes `totalInterest + totalRaised`.

**Minimum collateral floor** is recalculated proportionally to the actual raise. When the raise underfills `maxBorrowCap`, the floor scales down, freeing excess collateral above it for withdrawal during `Lock`:

$$\text{minimumCollateralRequired} = \left\lfloor\text{idealCollateralAmount} \times \frac{\text{totalRaised}}{\text{maxBorrowCap}}\right\rfloor$$

#### Claiming raised funds

`claimRaisedFunds()` is a one-shot call (gated by `fundsWithdrawn`), available only in `Lock`. It transfers the entire supply asset balance to the position-NFT holder. Before releasing funds, it simulates **interest plus principal** against current collateral via `_getHypotheticalVaultLiquidity(0, totalRaised)` — not just the principal being claimed. The call reverts with `ClaimWouldBreachLT` if the combined debt would breach the liquidation threshold.

After a successful claim, `totalDebt = totalInterest + totalRaised` — the full lifetime obligation.

#### Repaying

`repay(amount)` is unrestricted: any address can reduce `totalDebt` by pulling supply asset from its own balance. This is intentional — third parties can service the debt without holding the position NFT. Available in `Lock`, `PendingSettlement`, and `SettlementDeadlineExceeded`; overpayment silently clamps to `outstandingDebt()`.

#### Collateral during Lock

The institution may add or withdraw collateral during `Lock`. Withdrawal requires both checks to pass:

1. **Floor check** — `totalCollateralDeposited − amount ≥ minimumCollateralRequired`. The locked floor cannot be touched.
2. **LT health check** — the post-withdrawal state must not produce an LT shortfall.

The tighter of the two determines how much can be withdrawn.

### Settlement window

At `lockEndTime` the vault enters `PendingSettlement`. This state is never skipped — even if the institution cleared all debt before the lock expired, the vault holds in `Lock` until `block.timestamp ≥ lockEndTime`, then moves to `PendingSettlement`, and only transitions to `Matured` once `outstandingDebt() == 0`.

The institution has until `settlementDeadline` to repay in full. `repay()` remains available and unrestricted throughout. If the debt is cleared before the deadline the vault moves to `Matured` and the settlement waterfall runs. If the deadline passes with debt still outstanding the vault enters `SettlementDeadlineExceeded` — the institution may still repay voluntarily, but whitelisted settlers can now trigger overdue liquidation at the late-penalty rate (see [Overdue](#overdue)).

### Settlement waterfall

On entry to `Matured` or `Liquidated`, `_settleProtocolShare` runs once and distributes the supply asset balance:

| Branch                     | Condition                                               | Protocol fee                                | `settlementAmount`                  |
| -------------------------- | ------------------------------------------------------- | ------------------------------------------- | ----------------------------------- |
| Full repayment             | `available ≥ totalRaised + totalInterest`               | `totalInterest × reserveFactor`             | `available − protocolFee − surplus` |
| Partial interest shortfall | `totalRaised < available < totalRaised + totalInterest` | `(available − totalRaised) × reserveFactor` | `available − protocolFee`           |
| Principal shortfall        | `available ≤ totalRaised`                               | 0                                           | `available`                         |

Surplus above `totalRaised + totalInterest` is forwarded to PSR. Protocol-fee multiplication and division use integer arithmetic and round down to supply-asset units. `ShortfallDetected(expected, available)` fires in the shortfall branches.

`totalAssets()` returns `totalRaised` throughout `Lock` and `PendingSettlement`, then uses `settlementAmount` while the stored state is `Matured`, `Failed`, or `Liquidated` — the conversion anchor for ERC-4626 redemption in those states. Each supplier's payout on redemption is:

$$\text{payout} = \left\lfloor\text{shares} \times \frac{\text{settlementAmount}}{\text{totalSupply}}\right\rfloor$$

This is the `previewRedeem` rounding direction. Both `settlementAmount` and `totalSupply` decrease after each completed withdrawal, so integrations should use the live preview immediately before submission rather than a stale aggregate ratio.

### Liquidation

#### Health factor

The vault's health is computed by `_getHypotheticalVaultLiquidity`, which follows the same accounting approach as Compound V2 (the protocol Venus is built on) — collateral is weighted by a liquidation threshold and compared against outstanding debt, both expressed in USD:

$$\text{LT-cap} = \left\lfloor\frac{\text{collateralUSD} \times \text{liquidationThreshold}}{10^{18}}\right\rfloor$$

$$\begin{cases} \text{liquidity} = \text{LT-cap} - \text{debtUSD} & \text{if } \text{debtUSD} \leq \text{LT-cap} \ \text{shortfall} = \text{debtUSD} - \text{LT-cap} & \text{otherwise} \end{cases}$$

`liquidationThreshold` is a mantissa (e.g. `0.85e18` = 85%). Asset-to-USD conversion and the LT multiplication each use integer division and round down. A shortfall greater than zero means the vault is liquidatable. The same function is used with non-zero `withdrawAmount` or `additionalDebt` arguments to simulate hypothetical state changes — called by `claimRaisedFunds` and `withdrawCollateral` before executing the action, so those calls revert if the calculated post-operation state has a shortfall.

#### Seize calculation and Compound V2 lineage

The collateral seize calculation follows the same accounting principle as Compound V2: the repaid value is converted to USD, multiplied by the incentive multiplier, then divided by the collateral price to arrive at collateral units. The implementation performs two integer divisions, so the exact sequence is:

$$\text{repayValueUSD} = \left\lfloor\frac{\text{repayAmount} \times \text{supplyPrice}}{10^{18}}\right\rfloor$$

$$\text{seizeAmount} = \left\lfloor\frac{\text{repayValueUSD} \times \text{incentive}}{\text{collateralPrice}}\right\rfloor$$

`incentive` is `liquidationIncentive` for health-based liquidations and `latePenaltyRate` for overdue liquidations. Both are mantissa-encoded multipliers greater than `1e18` — an incentive of `1.1e18` targets 10% more collateral value than the repaid debt before token-unit rounding. `supplyPrice` and `collateralPrice` use their respective `36 − asset.decimals()` oracle scales; the differing scales are what convert supply-asset base units into collateral-asset base units.

The vault transfers the full `seizeAmount` to `LiquidationAdapter`. The adapter then isolates the bonus slice and takes the protocol's share of that slice only — not of the full seizure:

```
repayEquivalent  = totalSeized × MANTISSA_ONE / incentive
incentiveAmount  = totalSeized − repayEquivalent
protocolAmount   = incentiveAmount × protocolLiquidationShare / MANTISSA_ONE
callerAmount     = totalSeized − protocolAmount
```

In the published implementation, `repayEquivalent`, `protocolAmount`, and the vault's seize calculation each round down. The protocol share is calculated only from the resulting `incentiveAmount`; the caller receives `totalSeized − protocolAmount`. Smallest-unit rounding can therefore affect the exact split.

#### Health-based

Available in `Lock`, `PendingSettlement`, and `SettlementDeadlineExceeded` when the vault has a non-zero shortfall (see [Health factor](#health-factor) above).

Whitelisted liquidators call `LiquidationAdapter.liquidate(vault, repayAmount)`. The adapter validates vault registration and forwards to `vault.liquidate(repayAmount)` through the `onlyLiquidationAdapter` modifier. Inside the vault, `liquidate()` checks for an LT shortfall and reverts with `NotLiquidatable` if none exists; `_executeLiquidation` then enforces the close factor: if `actualRepay > outstandingDebt × closeFactor` the call reverts with `ExceedsCloseFactor` — it is a hard revert, not a silent cap. (`actualRepay` is `min(repayAmount, outstandingDebt)` — the close-factor check fires on the clamped value, not the raw input.) Seized collateral is split between the caller and the protocol per the formula above.

A health-based liquidation does not directly trigger a state transition. The vault advances normally — through `PendingSettlement` and into `Matured` once debt is zero. The `Liquidated` state is never reached via health-based liquidation.

#### Overdue

Available once the vault enters `SettlementDeadlineExceeded`. No LT shortfall is required — the time breach alone qualifies. The same hard-reverting `closeFactor` limit applies, but collateral is seized at `latePenaltyRate` instead of `liquidationIncentive`. A vault that breaches both the LT cap and the deadline can be liquidated through either path; the chosen path determines the bonus rate. Like health-based liquidation, an overdue liquidation that clears all debt transitions the vault to `Matured`, not `Liquidated`. The `Liquidated` state is reached exclusively via `repayBadDebt`.

#### Bad-debt rescue

Available in `Lock`, `PendingSettlement`, and `SettlementDeadlineExceeded` whenever the USD value of deposited collateral falls below the USD value of outstanding debt. `repayBadDebt` is permissionless — any address may call it. The repayment must be large enough to reduce `totalDebt` to at most `totalInterest` in a single call (i.e., the principal must be fully covered); the call reverts with `InsufficientRepayment` otherwise. Once that condition is met the vault transitions to `Liquidated` and the settlement waterfall runs immediately over the combined supply asset balance. Without a successful rescue, the published vault source does not automatically enter `Liquidated` or create an additional supplier payout; ordinary liquidation and voluntary repayment paths may remain available according to state. This contract description does not determine any off-chain legal or governance recovery rights.

## Risk parameters

All tunable parameters grouped by contract, mutability, and who sets them.

**Set once at vault creation via `createVault` — fixed for the life of the vault:**

| Parameter                | Location                                    | Units                  | Constraint                                                     |
| ------------------------ | ------------------------------------------- | ---------------------- | -------------------------------------------------------------- |
| Supply asset             | `VaultConfig.supplyAsset`                   | address                | Must have non-zero oracle price; must differ from collateral   |
| Collateral asset         | `InstitutionalConfig.collateralAsset`       | address                | Must have non-zero oracle price; must differ from supply asset |
| Target APR               | `VaultConfig.fixedAPY`                      | basis points           | 1 – 10 000                                                     |
| Reserve factor           | `VaultConfig.reserveFactor`                 | mantissa               | ≤ `1e18`                                                       |
| Minimum borrow cap       | `VaultConfig.minBorrowCap`                  | supply asset units     | > 0; ≤ `maxBorrowCap`                                          |
| Maximum borrow cap       | `VaultConfig.maxBorrowCap`                  | supply asset units     | ≥ `minBorrowCap`                                               |
| Minimum supplier deposit | `VaultConfig.minSupplierDeposit`            | supply asset units     | 0 = no floor                                                   |
| Fundraising duration     | `VaultConfig.openDuration`                  | seconds                | > 0                                                            |
| Lock duration            | `VaultConfig.lockDuration`                  | seconds                | > 0                                                            |
| Settlement window        | `VaultConfig.settlementWindow`              | seconds                | > 0                                                            |
| Ideal collateral amount  | `InstitutionalConfig.idealCollateralAmount` | collateral token units | > 0                                                            |
| Margin rate              | `InstitutionalConfig.marginRate`            | mantissa               | 0 < rate ≤ `1e18`                                              |

**Set at vault creation — mutable per vault by governance via the controller:**

| Parameter             | Location                          | Units    | Constraint                                                       |
| --------------------- | --------------------------------- | -------- | ---------------------------------------------------------------- |
| Liquidation threshold | `RiskConfig.liquidationThreshold` | mantissa | 0 < LT ≤ `1e18`; `LT × LI < 1e36`; `LT × latePenaltyRate < 1e36` |
| Liquidation incentive | `RiskConfig.liquidationIncentive` | mantissa | `1e18 < LI ≤ 1.5e18`; `LT × LI < 1e36`                           |
| Late penalty rate     | `RiskConfig.latePenaltyRate`      | mantissa | `1e18 < rate ≤ 1.5e18`; `LT × rate < 1e36`                       |

**Held on `LiquidationAdapter` — global across all vaults, mutable by governance:**

| Parameter                  | Field                      | Constraint      |
| -------------------------- | -------------------------- | --------------- |
| Close factor               | `closeFactor`              | 0 < CF ≤ `1e18` |
| Protocol liquidation share | `protocolLiquidationShare` | ≤ `1e18`        |

The constraint `LT × LI < 1e36` (and the equivalent for `latePenaltyRate`) ensures that a liquidation always improves vault health rather than worsening it. The controller enforces this invariant on both creation (`_validateLiquidationInvariant`) and every subsequent per-vault update.

## Governance and access control

### Pause system

A two-level pause is controlled by governance via `partialPauseVault` / `completePauseVault`. The table describes the published vault implementation's user-facing money-moving paths:

| Level        | Blocked by pause modifier                                                                               | Still callable subject to normal state, authorization, and balance checks                                                                     |
| ------------ | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| **Partial**  | `deposit`, `mint`, `depositCollateral`, `withdrawCollateral`, `claimRaisedFunds`                        | `withdraw`, `redeem`, `repay`, `repayBadDebt`, health-based liquidation, overdue liquidation, `updateVaultState`                              |
| **Complete** | All partial-pause paths, plus `withdraw`, `redeem`, `repay`, `repayBadDebt`, and both liquidation paths | `updateVaultState`; controller-only pause controls, close/cancel, parameter updates, and `sweep` are not guarded by the vault pause modifiers |

Partial pause therefore freezes new supplier deposits and position-holder collateral/fund operations without interrupting supplier terminal withdrawals, active debt service, or liquidation. Complete pause blocks those user money-moving paths, but it is not a universal freeze of views, state advancement, or controller-authorized administration.

### Administrative recovery

The controller's ACM-gated `sweep(vault, token)` path calls `vault.sweep(token)`, which transfers the vault's full balance of the specified ERC-20 to the controller's configured treasury. The published source does not exclude the supply or collateral token, so integrators should treat this as a material administrative authority and verify the live controller implementation, treasury, and ACM grants.

### Position NFT

`depositCollateral`, `claimRaisedFunds`, and `withdrawCollateral` are gated by `onlyPositionHolder`, which checks `positionToken.ownerOf(positionTokenId) == msg.sender` at call time. Transferring the NFT immediately reassigns control of all three. `repay` carries no such guard — intentional, for the permissionless debt-service case.

NFT transfers require a single-use governance approval: `approvePositionTransfer(vault, recipient)` records the approved target, consumed on the next `safeTransferFrom`. `revokePositionTransfer` cancels a pending approval before it is used.

### ACM permissions

Governance operations (create, open, cancel, pause, close, risk-parameter updates) route through `InstitutionalVaultController` and are gated per selector by the Venus AccessControlManager. Liquidation entry points on the vault are gated by `onlyLiquidationAdapter`; the adapter maintains its own ACM-gated whitelists for liquidators and settlers independently.

## Further reading

* [Supplier Guide](/guides/fixed-rate-vaults/supplier-guide)
* [Institution Guide](/guides/fixed-rate-vaults/institution-guide)
* [Solidity API Reference](/technical-reference/reference-fixed-rate-vaults)
* [Deployed Contracts](/deployed-contracts/fixed-rate-vaults)
* [Repository](https://github.com/VenusProtocol/fixed-rate-vaults)


# Native Token Gateway

The NativeTokenGateway contract serves the purpose of facilitating seamless interaction with Wrapped Native Token markets, even for users who do not possess any Wrapped Native Tokens. It achieves this by allowing users to utilize the native currency in their wallets directly.

## Overview

The Venus contracts deployed on the new networks do not directly support native currencies. For example on Ethereum instead of a native ether market there is a market for WETH. Wrapped Native Token markets address this limitation by facilitating the conversion of native currency into a format compatible with the ERC-20 standard.

To streamline user experience and eliminate the need for an additional transaction to convert native tokens into wrapped tokens, wrapping an unwrapping of native tokens is done behind the scenes when users interact with the market.

## User Journey

The introduction of the NativeTokenGateway contract significantly enhances the user experience when interacting with wrapped native token markets. Unlike before, where users were required to have the wrapped native token to engage with markets, they now have the flexibility to choose between native currency or wrapped version for market interaction.

If the user opts for native currency, the following functions are executed to facilitate interaction with the protocol:

### Approve Delegate

The `updateDelegate` function facilitates the granting or revocation of borrowing and redeeming delegate rights to or from an account. By invoking this function, borrowing delegate rights or redeeming delegate rights can be assigned to a specific address (delegate), enabling them to borrow or redeeming funds on behalf of the User.

This mechanism provides flexibility and control over borrowing and redeeming activities within the protocol, enhancing the overall efficiency and functionality of the system.

## Interaction with the NativeTokenGateway contract

### Supply (**wrapAndSupply**)

* The `wrapAndSupply` function is invoked sending the native currency, and the execution proceeds as follows:
  * Native currency is wrapped, providing the wrapped token.
  * VWrappedNative tokens are minted on behalf of the user.
  * VWrappedNative tokens are transferred to the user.

### Redeem

1. **redeemUnderlyingAndUnwrap**
   * The user approves the NativeTokenGateway contract as the delegate.
   * The `redeemUnderlyingAndUnwrap` function is called with the amount of underlying tokens to redeem, and the execution proceeds as follows:
     * VWrappedNative tokens are redeemed by the NativeTokenGateway contract on behalf of the user.
     * Received wrapped native tokens are unwrapped to obtain native currency.
     * Native currency is transferred to the user.
2. **redeemAndUnwrap**
   * The user approves the NativeTokenGateway contract as the delegate.
   * The `redeemAndUnwrap` function is called with the amount of VWrappedNative tokens to redeem, and the execution proceeds as follows:
     * VWrappedNative tokens are redeemed by the NativeTokenGateway contract on behalf of the user.
     * Received wrapped native tokens are unwrapped to obtain native currency.
     * Native currency is transferred to the user.

### Borrow (**borrowAndUnwrap**)

* The user approves the NativeTokenGateway contract as the delegate.
* The `borrowAndUnwrap` function is invoked, and the execution proceeds as follows:
  * Wrapped native tokens are borrowed on behalf of the user.
  * The borrowed wrapped native tokens are unwrapped to obtain native currency.
  * Native currency is transferred to the user.

### Repay (**wrapAndRepay**)

* The `wrapAndRepay` function is invoked sending the native currency, and the execution proceeds as follows:
  * The native currency sent is wrapped, providing the wrapped native.
  * The NativeTokenGateway repays the debt on behalf of the user.
  * Any unused native currency for repayment is returned to the user.


# Omnichain Governance

The contract descriptions are pinned to stable [`governance-contracts` v2.15.0](https://github.com/VenusProtocol/governance-contracts/tree/v2.15.0). This governance transport uses LayerZero V1 `uint16` endpoint IDs (named chain IDs in the ABI) and trusted-remote APIs. It is separate from the Risk Steward V2 OApp transport, which uses `uint32` EIDs and peers.

## System Overview

Omnichain Governance is designed to facilitate the execution of VIP across multiple blockchain networks, integrating with the Access Control Manager (ACM) and LayerZero communication protocol. It extends the [governance model proposed by LayerZero](https://github.com/LayerZero-Labs/omnichain-governance-executor/tree/main).

### Key features

1. **VIP Types and Delays:**
   * Provides three VIP options: Normal, Fast-track, and Critical.
   * Delays can be configured prior to remote execution on destination networks.
   * Normal VIPs have the greatest delays, whereas Critical VIPs have the smallest delays, indicating their urgency and importance.
2. **Inter-chain communication:**
   * LayerZero provides secure and reliable cross-chain messaging for remote execution commands.
3. **Bridging:**
   * Works with a bridge solution to deliver messages to destination networks.
   * Enables smooth interoperability between the BNB Chain and other supported networks.
   * Bridge configurations are flexible to accommodate various networks.
4. **Guardian account:**
   * Authorized by the ACM to revoke orders before they are executed on the target network.
   * Serves as a fail-safe mechanism, preventing unauthorized or incorrect commands from being performed.
5. **Command limits and pausing:**
   * Allows you to establish daily command restrictions for destination networks.
   * Adds pause/resume functionality for execution to temporarily halt operations in case of an emergency.

### Detailed breakdown

1. **Proposing and Voting:**
   * Proposers send VIPs with BNB Chain commands and remote commands.
   * Voting takes place on the BNB Chain utilizing existing governance contracts.
   * Proposals are validated and approved according to predetermined criteria and threshold.
2. **Remote Execution Flow:**
   * Commands for destination networks generate a "Remote VIPs payload”
   * The payload is routed to the destination network using the bridge solution.
3. **Delay Mechanism:**
   * Remote execution has two delays: bridge delay and executor delay.
   * Bridge delay is the time it takes for the bridge to propagate a message to the target network, which is commonly measured in minutes.
   * Executor delay is the duration between the message's arrival on the target network and its execution, which is customizable dependent on VIP type (Normal, Fast-track, or Critical)
4. **Execution and Expiry:**
   * User-triggered execution occurs once both delays have passed, signaling the destination network's readiness to perform the orders via Timelock.
   * The Guardian account can cancel orders before they are executed, offering a safeguard against malicious or erroneous acts.
   * "Remote VIPs" become "Expired" if no execution happens within a set grace period, avoiding stale or outdated commands from being executed.
5. **Command Restrictions:**
   * VIPs can only include one set of commands per destination network to prevent duplication and conflicts.
   * Duplicate commands for the same network within a VIP are not permitted, ensuring consistent and reliable execution.
6. **Executor-Side Features:**
   * Sets a daily limit on the number of commands received per network.
   * Implements pause/resume functionality for the execute function in the target governance contract, enabling administrators to manage the system's operational state effectively.

## Detailed overview of remote proposal execution (step-by-step)

1. Proposing a remote VIP on BNB Chain

* A proposer submits a VIP through the existing governance mechanism on the BNB Chain.
* The VIP must include a command invoking `OmnichainProposalSender::execute`, which sends the remote VIP payload.
* The `execute` function takes four arguments:
  * `chainID`: The destination's [LayerZero V1 endpoint ID](https://docs.layerzero.network/v1/deployments/deployed-contracts), not its EVM chain ID or LayerZero V2 EID.
  * `payload`: Encoded data (off-chain) containing the specific commands to be executed on the target network.
  * `adapterParams`: Parameters specifying destination execution gas, encoded with `ethers.utils.solidityPack(["uint16", "uint256"], [1, gasValue])` for a version-1 adapter payload.
  * `zroPaymentAddress_`: The address of the ZRO token holder who would pay for the transaction.

2. Eligibility checks and limits

Before sending the remote execution message, the system verifies eligibility based on predefined thresholds and limits. These restrictions ensure responsible resource allocation and prevent potential misuse.

3. Remote proposal ID generation

It's crucial to understand that the proposal ID for the remote execution on the destination network differs from the initial VIP ID proposed on the BNB Chain. This remote proposal ID starts from 1.

4. Message relay based on outcome

* **Success:** If the eligibility checks and limits are met, the encoded message (payload) is relayed across chains using the LayerZero bridge.
* **Failure:** If the checks fail, the system handles the situation differently based on the cause:
  * **Insufficient Gas:** The `retryExecute` function is used to attempt redelivering the message with potentially adjusted gas fees.
  * **Logical Error or Check Failure:** The `fallbackWithdraw` function removes the message from the queue, preventing further retries if the failure stems from inherent logic errors or failed eligibility checks.

5. Receiving and Queuing on the Destination Network

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-dc3a69fd74810897d8cbc4164e3bb64bcea583fa%2Fmultichain_governance_vip_states.svg?alt=media&amp;token=74888420-f298-40ac-a011-42d5edb3a3fd" alt="Omnichain Governance VIP states"><figcaption></figcaption></figure>

* Upon successful reception by the destination network's executor contract (`OmnichainGovernanceExecutor`), the remote proposal enters a "Queued" state.
* This queuing process applies additional eligibility checks specific to the receiving network, ensuring compliance with its governance rules and thresholds of commands limits.

6. Delay mechanism and execution

* Once the configured delay for the remote proposal type (Normal, Fast-track, Critical) elapses, the proposal becomes eligible for execution.
* Any user can then trigger the execution of the queued commands on the destination network.

7. Ownership and Access Control

* `OmnichainProposalSender` (BNB Chain):
  * Owned by: NormalTimelock contract on the BNB Chain.
  * Authorized callers: the Normal and Fast-track Timelocks are authorized to call the `execute` function on this contract. The Critical Timelock was also authorized originally, but every permission it held was revoked in [VIP-645](https://app.venus.io/#/governance/proposal/645?chainId=56).
* `OmnichainGovernanceExecutor` (destination network):
  * Owned by: `OmnichainExecutorOwner` contract. This owner performs Access Control Manager (ACM) checks before allowing any function calls on this contract.
* `TimelockV8`:
  * Owned by: `OmnichainGovernanceExecutor` contract. This ownership grants `TimelockV8` the authority to perform specific actions like queuing, canceling, and executing remote proposals.

## Potential Failures and Retry Options

* If a VIP fails to send the proposal from source chain (BNB Chain) to destination network, the message will be saved and can be retried with the `retryExecute` function. This mechanism allows for the redelivery of the message with potentially adjusted gas fees to ensure successful execution.
* In case where the VIP fails to send the proposal from source chain (BNB Chain) to destination network due to non-retryable conditions and funds are stuck in the `OmnichainProposalSender` contract, funds can be withdrawn by invoking the `fallbackWithdraw` function. This ensures that funds are not permanently locked in the contract due to failed execution attempts.
* If a proposal's queueing fails on the destination network due to insufficient gas, the message will be saved and can be retried with the `retryMessage` function.
* If a proposal's queueing fails on the destination network due to reasons such as reaching caps or other constraints, the message will be saved and can be retried with the `retryMessage` function. This provides flexibility in addressing various failure scenarios and ensures that execution attempts are made until successful.
* If a proposal's queueing fails on the destination network because the contract has been paused, the message will be saved and can be retried with the `retryMessage` function after the contract has been unpaused.

## Contracts overview

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-0e3f05f86fd7958f5d0c96468d3ac3f9960f98a4%2Fmultichain_governance_contracts.svg?alt=media&amp;token=73d9061d-4341-4b46-8e15-e5afd47cd164" alt="Omnichain Governance contracts"><figcaption></figcaption></figure>

### Contract 1: `BaseOmnichainControllerSrc`

#### Functionality

This contract serves as the framework for secure omnichain (cross-chain) communication. Its primary responsibilities include managing daily command limits and enabling pausing of controlled message transmission across chains.

#### Key features

1. **Access Control Integration**: Integrates an `AccessControlManager` contract to enforce access control for critical functions. This ensures that only authorised entities can execute commands, enhancing security.
2. **Daily Command Limits**: Establishes a per-chain limit on the number of commands that can be sent within a 24-hour window. This feature prevents potential abuse and maintains system stability.
3. **Pausable**: Implements a pausing mechanism, inherited from the OpenZeppelin `Pausable` contract. This functionality allows the contract owner to temporarily halt omnichain communication if necessary.

#### Architecture

* **Inheritance**: Extends the functionalities of the `Ownable` and `Pausable` contracts from OpenZeppelin, inheriting ownership management and pausing capabilities.

#### State variables

* **`accessControlManager (address)`**: Stores the address of the `AccessControlManager` contract.
* **`chainIdToMaxDailyLimit (mapping)`**: Maps chain IDs to their corresponding daily command limits.
* **`chainIdToLast24HourCommandsSent (mapping)`**: Tracks the number of commands sent within the last 24 hours for each chain.
* **`chainIdToLast24HourWindowStart (mapping)`**: Records the timestamp when the last 24-hour window for a chain began.
* **`chainIdToLastProposalSentTimestamp (mapping)`**: maintains the timestamp of the last proposal sent to a specific chain to prevent sending multiple proposals within the same block.

#### Events

* **`SetMaxDailyLimit (event)`**: Emitted when the daily command limit for a chain is modified.
* **`NewAccessControlManager (event)`**: Triggered when the address of the `AccessControlManager` is updated.

#### Functions

* **`constructor(address accessControlManager_)`**: Initializes the contract with the address of the `AccessControlManager`.
* **`setMaxDailyLimit(uint16 chainId_, uint256 limit_)`**: Sets the maximum daily command limit for a specific chain ID. Requires permission from the `AccessControlManager`.
* **`pause()`**: Triggers the paused state, halting omnichain communication. Requires `AccessControlManager` permission.
* **`unpause()`**: Resumes omnichain communication from the paused state. Requires `AccessControlManager` permission.
* **`setAccessControlManager(address accessControlManager_)`**: Updates the address of the `AccessControlManager` contract. Only callable by the contract owner.
* **`isEligibleToSend(uint16 dstChainId, uint256 noOfCommands_)`**: Checks if sending the specified number of commands to the given chain is permissible based on daily limits and time windows.
* **`ensureAllowed(string memory functionSig)`**: Ensures the caller has permission to execute a specific function, leveraging the `AccessControlManager`.

### Contract 2: `OmnichainProposalSender`

#### Functionality

This contract facilitates cross-chain message transmission triggered by governor proposals on the main (BNB) chain. It sends proposal execution data to designated remote chains for processing.

#### Key features

* **LayerZero Integration**: Utilises the LayerZero communication protocol for efficient and reliable cross-chain message delivery.
* **Remote Chain Management**: Allows defining trusted remote contracts (receivers) on other chains using `setTrustedRemoteAddress`.
* **Failed Message Handling**: Stores the execution hashes of failed messages to facilitate resending or clearing them in case of insufficient fees or other issues.
* **Security Measures**: Enforces access control using the `AccessControlManager` for critical functions.

#### Architecture

* **Inheritance**: Inherits functionalities from both the `ReentrancyGuard` and `BaseOmnichainControllerSrc` contracts, providing reentrancy protection and foundational omnichain communication capabilities.

#### State variables

* **`proposalCount (uint256)`**: Tracks the total number of remote proposals.
* **`storedExecutionHashes (mapping)`**: Stores the execution hashes of failed messages for retry or clearing purposes.
* **`LZ_ENDPOINT (ILayerZeroEndpoint)`**: Interface for interacting with the LayerZero communication protocol.
* **`trustedRemoteLookup (mapping)`**: Maps remote chain IDs to trusted remote contract addresses for message transmission.

#### Events

* **`SetTrustedRemoteAddress (event)`**: Triggered when a trusted remote address is set for a remote chain.
* **`TrustedRemoteRemoved (event)`**: Emitted when a trusted remote address is removed from storage.
* **`ExecuteRemoteProposal (event)`**: Indicates the execution of a proposal on a remote chain.
* **`ClearPayload (event)`**: Signals the successful clearing of a previously failed message.
* **`StorePayload (event)`**: Records the storage of an execution hash for a failed message, along with relevant details.
* **`FallbackWithdraw (event)`**: Indicates a fallback withdrawal of funds in case of failed messages.

#### Functions

* **`constructor(ILayerZeroEndpoint lzEndpoint_, address accessControlManager_)`**: Initializes the contract with the LayerZero endpoint and the address of the `AccessControlManager`.
* **`estimateFees(uint16 remoteChainId_, bytes calldata payload_, bytes calldata adapterParams_)`** : Estimates LayerZero fees for cross-chain message delivery based on payload and adapter parameters.
* **`execute(uint16 remoteChainId_, bytes calldata payload_, bytes calldata adapterParams_)`**: Sends a message to execute a remote proposal, storing execution hashes if the message fails.
* **`retryExecute(...)`**: Resends a previously failed message with potentially additional fees, ensuring reentrancy protection.
* **`fallbackWithdraw(...)`**: Allows the owner to withdraw funds in case of failed messages.
* **`setTrustedRemoteAddress(uint16 remoteChainId_, bytes calldata newRemoteAddress_)`**: Sets the remote message receiver address for a specified remote chain, requiring `AccessControlManager` permission.
* **`setConfig(uint16 version_, uint16 chainId_, uint256 configType_, bytes calldata config_)`**: Sets the configuration of the LayerZero messaging library, controlled by the `AccessControlManager`.
* **`setSendVersion(uint16 version_)`**: Sets the messaging library version, with permission from the `AccessControlManager`.
* **`getConfig(uint16 version_, uint16 chainId_, uint256 configType_)`**: Retrieves the configuration of the LayerZero messaging library.

### Contract 3: `BaseOmnichainControllerDest`

#### Functionality

This contract serves as the base for the Omnichain controller destination contract. It provides functionality related to daily command limits and pausing.

#### State variables

* **`maxDailyReceiveLimit (uint256)`**: Maximum daily limit for receiving commands from BNB Chain.
* **`last24HourCommandsReceived (uint256)`**: Total received commands within the last 24-hour window from BNB Chain.
* **`last24HourReceiveWindowStart (uint256)`**: Timestamp when the last 24-hour window started from BNB Chain.

#### Events

* **`SetMaxDailyReceiveLimit (event)`**: Emitted when the maximum daily limit for receiving commands from BNB Chain is modified.

#### Functions

* **`constructor(address endpoint_)`**: Initializes the contract with the LayerZero endpoint address.
* **`setMaxDailyReceiveLimit(uint256 limit_)`**: Sets the maximum daily limit for receiving commands. Only callable by the contract owner.
* **`pause()`**: Triggers the paused state of the controller. Only callable by the contract owner.
* **`unpause()`**: Triggers the resume state of the controller. Only callable by the contract owner.
* **`renounceOwnership()`**: Overrides the renounceOwnership function to prevent accidental renouncement of ownership.
* **`_isEligibleToReceive(uint256 noOfCommands_)`**: Checks the eligibility to receive commands based on the daily limit and updates the state accordingly.

### Contract 4: `OmnichainGovernanceExecutor`

#### Functionality

This contract executes proposal transactions sent from the main chain. It controls LayerZero configuration and implements a non-blocking behavior.

#### State variables

* **`guardian (address)`**: A privileged role that can cancel a queued remote proposal.
* **`srcChainId (uint16)`**: Stores the layerzero endpoint ID.
* **`lastProposalReceived (uint256)`**: Last proposal count received.
* **`proposals (mapping)`**: Official record of all proposals ever proposed.
* **`proposalTimelocks (mapping)`**: Mapping containing Timelock addresses for each proposal type.
* **`queued (mapping)`**: Represents the queue state of a proposal.

#### Events

* **`ProposalReceived (event)`**: Emitted when a proposal is received.
* **`ProposalQueued (event)`**: Emitted when a proposal is queued.
* **`ProposalExecuted (event)`**: Emitted when a proposal is executed.
* **`ReceivePayloadFailed (event)`**: Emitted when a payload receive fails.
* **`ProposalCanceled (event)`**: Emitted when a proposal is canceled.
* **`TimelockAdded (event)`**: Emitted when a Timelock is added.
* **`SetSrcChainId (event)`**: Emitted when the source layer zero endpoint ID is updated.
* **`SetTimelockPendingAdmin (event)`**: Emitted when pending admin of Timelock is updated.
* **`NewGuardian (event)`**: Emitted when guardian of OmnichainGovernanceExecutor is updated.

#### Functions

* **`constructor(address endpoint_, address guardian_, uint16 srcChainId_)`**: Initialises the contract with the LayerZero endpoint address, guardian address, and source chain ID.
* **`setSrcChainId(uint16 srcChainId_)`**: Updates the source layerzero endpoint ID. Only callable by the contract owner.
* **`addTimelocks(ITimelock[] memory timelocks_)`**: Adds Timelocks to the ProposalTimelocks mapping. Only callable by the contract owner.
* **`execute(uint256 proposalId_)`** : Executes a queued proposal if the ETA has passed.
* **`cancel(uint256 proposalId_)`**: Cancels a proposal if the sender is the guardian and the proposal is not executed.
* **`state(uint256 proposalId_)`**: Gets the state of a proposal.
* **`_blockingLzReceive(...)`** and **`_nonblockingLzReceive(...)`**: Process LayerZero receive requests, with blocking and non-blocking behaviour respectively.
* **`_queue(uint256 proposalId_)`**: Queues a proposal for execution.
* **`_queueOrRevertInternal(...)`**: Checks for a unique proposal and queues it or reverts if already queued.
* **`setTimelockPendingAdmin(address pendingAdmin_, uint8 proposalType_)`**: Sets the new pending admin of the Timelock.
* **`setGuardian(address newGuardian)`**: Sets the new guardian of the OmnichainGovernanceExecutor.

### Contract 5: `OmnichainExecutorOwner`

#### Functionality

The OmnichainExecutorOwner contract serves as a governance and access control mechanism for managing the `OmnichainGovernanceExecutor` contract. It allows the owner to control the functions that can be executed on the `OmnichainGovernanceExecutor` contract, upsert function signatures into a registry, and transfer ownership of the `OmnichainGovernanceExecutor` contract.

#### State variables

* **`OMNICHAIN_GOVERNANCE_EXECUTOR (immutable)`:**
  * This variable holds the address of the `OmnichainGovernanceExecutor` contract and is immutable once initialized.
* **`functionRegistry (mapping)`:**
  * This mapping stores the function signatures along with their corresponding 4-byte hash values.
  * It allows the contract owner to register which functions are permitted to be executed on the `OmnichainGovernanceExecutor` contract.

#### Events

* **`FunctionRegistryChanged (event)`:**
  * This event is emitted when a function is added or removed from the function registry.
  * It provides information about the function signature and whether it is active (added) or inactive (removed).

#### Functions

* **`constructor(address omnichainGovernanceExecutor_)`:**
  * Initializes the contract with the address of the `OmnichainGovernanceExecutor` contract.
  * This constructor ensures that the provided address is not zero.
* **`initialize(address accessControlManager_)`:**
  * Initializes the contract with the address of the access control manager.
  * This function sets up access control for the contract.
* **`fallback(bytes calldata data_)`:**
  * Acts as a fallback function that is invoked when a called function does not exist in the contract.
  * It checks if the function signature exists in the function registry and if the caller has permission to execute it.
  * Then it forwards the function call to the `OmnichainGovernanceExecutor` contract.
  * If the call fails, it reverts with an error message.
* **`upsertSignature(string[] calldata signatures_, bool[] calldata active_)`:**
  * Allows the owner to upsert (add or remove) function signatures into the function registry.
  * The owner can specify whether each function signature should be added (**`true`**) or removed (**`false`**).
  * This function ensures that input arrays have the same length.
  * It emits the **`FunctionRegistryChanged`** event for each function signature that is added or removed.
* **`transferBridgeOwnership(address newOwner_)`:**
  * Transfers the ownership of the `OmnichainGovernanceExecutor` contract from this contract to a new owner.
  * This function is controlled by the access control manager to ensure that only authorized entities can transfer ownership.
  * It ensures that the new owner address is not zero before transferring ownership.
* **`renounceOwnership()`:**
  * Overrides the **`renounceOwnership`** function from the parent contract to provide an empty implementation.
  * This prevents accidental renouncement of ownership, as ownership renouncement is handled through the **`transferBridgeOwnership`** function.

### Contract 6: `TimelockV8`

#### Functionality

The `TimelockV8` contract is a Solidity V8 implementation of a timelock mechanism designed to execute transactions with a delay. It incorporates additional features such as setting a delay period, accepting pending admins, and queuing, executing, and canceling transactions. This contract ensures that specific transactions can only be executed after a predefined period, enhancing security and providing governance control.

#### Key features

* **Delayed Transaction Execution:** Allows transactions to be queued with an associated delay period before execution.
* **Admin Management:** Supports the transition of admin roles by accepting pending admin proposals.
* **Transaction Queueing:** Provides functionality to queue transactions for future execution, ensuring timely processing.
* **Transaction Execution:** Executes queued transactions after the specified delay period has elapsed, subject to admin authorisation.
* **Transaction Cancellation:** Allows cancellation of queued transactions before execution, providing flexibility and control.

#### Events

* **`NewAdmin (event)`**: Signals the acceptance of a new admin account.
* **`NewPendingAdmin (event)`**: Indicates the proposal of a new admin account.
* **`NewDelay (event)`**: Notifies when the delay period for transaction execution is updated.
* **`CancelTransaction (event)`**: Broadcasts the cancellation of a queued transaction.
* **`ExecuteTransaction (event)`**: Broadcasts the execution of a queued transaction.
* **`QueueTransaction (event)`**: Broadcasts the queuing of a new transaction for future execution.

#### Constants

* **`DEFAULT_GRACE_PERIOD`**: Default grace period for executing queued transactions (14 days).
* **`DEFAULT_MINIMUM_DELAY`**: Default minimum delay period for queuing transactions (1 hour).
* **`DEFAULT_MAXIMUM_DELAY`**: Default maximum delay period for queuing transactions (30 days).

#### State variables

* **`admin (address)`**: Stores the address of the current admin authorised to manage the timelock.
* **`pendingAdmin (address)`** : Stores the address of the proposed admin awaiting acceptance.
* **`delay (uint256)`** : Specifies the delay period for executing queued transactions.

#### Functions

* **`setDelay(uint256 delay_)`**: Allows the admin to set the delay period for transaction execution.
* **`acceptAdmin()`**: Enables the pending admin to accept the admin role.
* **`setPendingAdmin(address pendingAdmin_)`**: Allows to propose a new admin account via proposal or admin.
* **`queueTransaction(...)`**: Queues a transaction for future execution after a specified delay.
* **`cancelTransaction(...)`**: Cancels a queued transaction before execution.
* **`executeTransaction(...)`**: Executes a queued transaction after the delay period has elapsed.


# Prime tokens

{% hint style="info" %}
Venus Prime runs on the PrimeV2 + PrimeLeaderboard architecture, live on BNB Chain mainnet. Other networks still run the earlier Prime (V1) contract and migrate to PrimeV2 per-chain via governance.
{% endhint %}

## Overview

This technical article explains the implementation details of the Venus Prime program. The high-level overview of the program can be found [here](/whats-new/prime-yield).

Venus Prime is split across two contracts:

* **PrimeV2** — holds Soulbound Prime tokens, tracks per-market user scores, and distributes boosted rewards funded by protocol revenue through `PrimeLiquidityProvider`.
* **PrimeLeaderboard** — tracks time-weighted XVS staking and exposes a **Prime Score** used to decide who is eligible to mint a Prime token.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-5921199920548912ceb049bca3d1869ce092a0c0%2Fprime_architecture.svg?alt=media" alt="Venus Prime architecture: XVSVault, PrimeLeaderboard, PrimeV2, PrimeLiquidityProvider and governance"><figcaption><p>PrimeV2 architecture — eligibility, governance and reward wiring</p></figcaption></figure>

## Eligibility and the leaderboard

### Prime Score

A user's **Prime Score** is their time-weighted stake, exposed on-chain via `getEffectiveStake` / `getEffectiveStakeBatch` (the contract returns it as `effectiveStake`).

`PrimeLeaderboard` records each user's XVS deposits as individual tranches (amount + timestamp). It is notified of every stake change by the `XVSVault` through the existing `xvsUpdated(user)` callback, which diffs the user's current vault stake (net of pending withdrawal requests) against the last known total and records a deposit or a LIFO withdrawal accordingly.

Each tranche earns a multiplier based on how long it has been held:

| Holding duration | Multiplier (1e18) |
| ---------------- | ----------------- |
| < 30 days        | 1.0x (base)       |
| ≥ 30 days        | 1.3x              |
| ≥ 60 days        | 1.6x              |
| ≥ 90 days        | 2.0x (cap)        |

The Prime Score is:

```jsx
primeScore = Σ  deposit.amount × multiplier(holdingDuration) × min(holdingDuration, capSeconds)
```

where `capSeconds` is the longest configured tier duration (90 days by default). Tiers and the cap are configurable via `setMultiplierTiers(durations, multipliers)`; durations and multipliers must be strictly ascending and multipliers must be ≥ base (1e18).

Withdrawals are processed **LIFO** so the oldest, highest-multiplier deposits survive longest. To bound gas, a user keeps at most `MAX_DEPOSITS_PER_USER` (30) tranches; when the limit is reached, deposits are compacted — first by losslessly merging all max-tier tranches, then, if needed, by merging tranches within the same tier using an amount-weighted average timestamp.

### Minting

The primary minting path is **governance issuance**:

* `issue(user)` / `issueBatch(users)` let governance mint Prime tokens directly (ACM-gated). A keeper reads the leaderboard off-chain with `getEffectiveStakeBatch(users)`, ranks users, and governance issues tokens to the qualifying set.
* `burn(user)` / `burnBatch(users)` let governance revoke tokens (ACM-gated).

A **permissionless minting** window exists as a fallback and is **disabled by default**: `mintThreshold` initializes to `0`, so `claimPrime` / `claimPrimeBatch` revert with `MintThresholdNotSet`. Governance can open the window by calling `setMintThreshold(threshold, deadline)` — intended as a last resort if the keeper-driven issuance flow is unavailable. While open:

* `claimPrime(user)` / `claimPrimeBatch(users)` are permissionless — anyone can mint a Prime token for any user whose Prime Score ≥ `mintThreshold`. In the batch variant, users below the threshold are skipped (with a `SkippedIneligibleUser` event) rather than reverting the whole call.

Total Prime tokens are capped by `tokenLimit` (default 500). Setting `mintThreshold` back to `0` closes the window; a non-zero `mintDeadline` auto-closes it once `block.timestamp` passes it.

### Staker migration

When PrimeLeaderboard is first deployed, existing stakers are seeded with `initializeStakers(users, amounts, timestamps)` in batches (idempotent — already-seeded users are skipped). Once seeding is complete, `finalizeInitialization()` locks it permanently. The `XVSVault.primeToken` reference is then pointed at the leaderboard to begin live tracking.

## Rewards

([*Main explanation of Prime rewards*](/whats-new/prime-yield#technical-reward-details))

Qualifiable supply and borrow amount limits are set by the staked XVS value and the market multiplier. The USD values of the tokens and of XVS are taken into account to calculate these caps. The following pseudocode shows how $$\sigma\_{i,m}$$ is calculated considering the caps:

```jsx
// xvsBalanceOfUser is the user's XVS Vault stake, net of pending withdrawal requests
borrowUSDCap = toUSD(xvsBalanceOfUser * marketBorrowMultipler)
supplyUSDCap = toUSD(xvsBalanceOfUser * marketSupplyMultipler)
borrowUSD = toUSD(borrowTokens)
supplyUSD = toUSD(supplyTokens)

// borrow side
if (borrowUSD < borrowUSDCap) {
  borrowQVL = borrowTokens
else {
  borrowQVL = borrowTokens * borrowUSDCap/borrowUSD
}

// supply side
if (supplyUSD < supplyUSDCap) {
  supplyQVL = supplyTokens
else {
  supplyQVL = supplyTokens * supplyUSDCap/supplyUSD
}

return borrowQVL + supplyQVL
```

**Significance of α**

A higher α value increases the weight of stake contributions when determining rewards and decreases the weight of supply/borrow contributions. The value of α is between 0-1 (both excluded).

A default weight of 0.5 has been evaluated as a good ratio and is not likely to be changed. A higher value would only be needed if Venus wanted to attract more XVS stake from Prime token holders at the expense of supply/borrow rewards.

Here is an example to show how the score is impacted based on the value of α:

```jsx
User A:
Stake: 200
Supply/Borrow: 500

User B:
Stake: 100
Supply/Borrow: 1000

If alpha is 0.7 then:
user A score: 263.2764409
user B score: 199.5262315

If alpha is 0.3 then:
user A score: 379.8288965
user B score: 501.1872336
```

### Implementation of the rewards in solidity

`rewardIndex` and `sumOfMembersScore` are global variables in supported markets used when calculating rewards. `sumOfMembersScore` represents the current sum of all Prime token holders' scores, and `rewardIndex` is updated whenever interest is accrued for a market.

```jsx
// every time accrueInterest is called. delta is interest per score
delta = distributionIncome / sumOfMembersScore;
rewardIndex += delta;
```

If interest accrues for a market while it has no scored members yet, the slice is recorded in `undistributedReward[underlying]` so governance can reclaim it later via `sweepUndistributed` instead of stranding it.

Whenever a user's supply/borrow or XVS Vault balance changes, the rewards accrued are recalculated and added to their account:

* In the Comptroller (specifically in the `PolicyFacet`), after any operation that could impact a Prime score or interest, `accrueInterestAndUpdateScore(user, market)` is called on PrimeV2.
* In the `XVSVault`, after depositing or requesting a withdrawal, `xvsUpdated(user)` is invoked on `PrimeLeaderboard`. The leaderboard updates its deposit tracking and then calls `accrueInterestAndUpdateScore(user)` on PrimeV2 so rewards are accrued at the old score before the score is recalculated.

User rewards are calculated as:

```jsx
rewards = (rewardIndex - userRewardIndex) * scoreOfUser;
```

The `userRewardIndex` (`interests[market][account].rewardIndex`) is then updated to the current global value.

### Per-cycle reward accounting

Rewards are tracked in monthly **cycles** for off-chain reporting. Each user's `interests[market][user].lifetimeAccrued` is a monotonic running total of every reward ever accrued to that user in that market — it grows alongside `accrued` but is never reset on claim. The off-chain pipeline computes a user's earnings for a given cycle as the difference between their `lifetimeAccrued` at the cycle's start and end.

Cycle boundaries are anchored on-chain: a keeper calls `recordCycleSnapshot(cycleId)`, which emits `CycleSnapshotRecorded(cycleId, block.number, block.timestamp)`. This is an operational hook (ACM-gated to a keeper, not the Timelock) and is intentionally not idempotent — the indexer de-duplicates repeated `cycleId`s. The snapshots themselves are read off-chain via `getLifetimeAccruedByMarket` / `getLifetimeAccruedByUser`, so no per-cycle state is stored on-chain beyond `lifetimeAccrued`.

## Income collection and distribution

Every market in Venus (including Isolated Lending markets) contributes to the rewards that PrimeV2 distributes, following the protocol [tokenomics](/governance/tokenomics).

Prime rewards are denominated in the reward tokens accumulated by the `PrimeLiquidityProvider`. Protocol income that arrives in other tokens is converted into those reward tokens by [TokenBuyback](/whats-new/token-converter) instances whose destination is the `PrimeLiquidityProvider`. On BNB Chain these are `USDTPrimeBuyback` (base asset USDT) and `UPrimeBuyback` (base asset U), so Prime rewards accumulate in USDT and U.

Interest reserves (part of the protocol income) from Isolated Pools and Core Pool markets are sent to the PSR ([Protocol Share Reserve](https://github.com/VenusProtocol/protocol-reserve/blob/main/contracts/ProtocolReserve/ProtocolShareReserve.sol)) contract. Based on the configuration, a percentage of income from all markets is reserved for Prime token holders. The interest reserves are sent to the PSR periodically (currently every 6 hours, changeable by the community via [VIP](https://app.venus.io/governance)).

The PSR has a function `releaseFunds` that releases the funds to the destination contracts. The Prime-destined `TokenBuyback` instances receive income from the PSR, swap it into the Prime reward tokens at DEX market rate via an ACM-authorized finance-team cron, and forward the output to `PrimeLiquidityProvider`. How much of each reward token is released to Prime users over time is controlled by the per-token distribution speeds configured in the `PrimeLiquidityProvider` via VIP (`setTokensDistributionSpeed`); there is no fixed allocation percentage hardcoded in Prime.

`PrimeLiquidityProvider` then releases the funds to the PrimeV2 contract according to distribution speeds configured per reward token.

If a user tries to claim their rewards and PrimeV2 doesn't have enough funds, the release of funds from `PrimeLiquidityProvider` to PrimeV2 is triggered in the same transaction (in the `claimInterest` function).

Two details about `claimInterest`:

* A permissionless overload `claimInterest(vToken, user)` lets anyone trigger a claim on behalf of a user — the reward tokens are always sent to the user, never to the caller.
* If the balance is still insufficient after pulling from the `PrimeLiquidityProvider`, the claim is partial: the shortfall stays recorded in the user's `accrued` balance and can be claimed later, instead of the transaction reverting.

The following diagram shows the integration of the `TokenBuyback` contracts with the Prime contracts:

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-7b2c57b95c9ef87dbf1a59ae7fd101e7b58dcd6f%2Fprime_token_buyback.svg?alt=media" alt="Integration of the TokenBuyback contracts with the Prime contracts"><figcaption><p>PSR → Prime buybacks → PrimeLiquidityProvider → PrimeV2 → users</p></figcaption></figure>

More information about income collection and distribution can be found [here](/whats-new/automatic-income-allocation).

## Update cap multipliers and alpha

Market multipliers and alpha can be updated at any time and then need to be propagated to all users. When `addMarket`, `updateMultipliers` or `updateAlpha` is called, PrimeV2 opens a new score-update round: it increments `nextScoreUpdateRoundId` and sets `pendingScoreUpdates` to the total number of Prime tokens. If a previous round was still in progress, it is discarded with an `IncompleteRoundDiscarded` event.

While `pendingScoreUpdates > 0`, issuing and burning are blocked (`ScoreUpdateInProgress`) until all scores have been recomputed. A keeper completes the round by calling the permissionless `updateScores(users)` in batches (split across multiple transactions to avoid running out of gas). Each user is updated at most once per round, tracked via `isScoreUpdated[roundId][user]`.

This mechanism ensures that when multipliers/alpha change, or a new market is added to the program, all Prime users' scores are brought up to date promptly rather than drifting until each user next interacts with the markets — which would otherwise let the first users in a new market collect disproportionately large rewards.

### Adding and removing markets

`addMarket(market, supplyMultiplier, borrowMultiplier)` (ACM-gated) enrolls a Core Pool market into the program. The market must be listed in the Core Pool Comptroller, its underlying token must have at most 18 decimals, and only one Prime market can exist per underlying asset. As described above, adding a market opens a score-update round.

`removeMarket(market)` (ACM-gated) removes a market from the program. It is only allowed once no Prime holder retains a score in that market (`sumOfMembersScore == 0`, enforced with `MarketHasActiveMembers`) — which happens only once every Prime holder has zero capital (supply and borrow) in that market and their scores have been recomputed, or the holders' Prime tokens have been burned. Before deletion, any pending income slice from the `PrimeLiquidityProvider` is flushed into `undistributedReward` so it stays recoverable via `sweepUndistributed`. Rewards already accrued by users in a removed market remain claimable through `claimInterest`.

## Calculate APR associated with a Prime market and user

APR estimation lives in a separate read-only contract, [**PrimeLens**](/technical-reference/reference-core-pool/prime/prime-lens), kept out of PrimeV2 to stay within the EVM contract-size limit. The [Venus UI](https://app.venus.io) calls it to show the APR a user's Prime boost adds in a given market.

* `calculateAPR(market, user)` returns an `APRInfo` struct with the user's `supplyAPR` and `borrowAPR` (in BPS) plus the inputs behind them: `userScore`, `totalScore`, `xvsBalanceForScore`, `capital`, `cappedSupply`, `cappedBorrow`, `supplyCapUSD`, `borrowCapUSD`.
* `estimateAPR(market, user, borrow, supply, xvsStaked)` returns the same `APRInfo` struct for a hypothetical position — the supply, borrow and staked XVS amounts are passed in rather than read from the user's current state, so the UI can simulate the APR impact of a planned action.
* `incomeDistributionYearly(vToken)` returns the annualized income for a market — the PrimeLiquidityProvider effective distribution speed for the market's underlying multiplied by `blocksOrSecondsPerYear` (PrimeV2 runs on `TimeManagerV8`, so this is per-second on time-based chains and per-block otherwise).

Conceptually the lens performs these steps:

1. Fetch the annualized income for the market (`incomeDistributionYearly`)
2. Compute the user's score and the market's total score, giving the user's share of that income over a year
3. Compute the user's capped supply and borrow for the market
4. Split the user's income share across the capped supply and borrow legs
5. Derive the supply and borrow APR from those legs

**Example:**

1. Annualized market income: 315.36 USDT
2. With user score 3 and total market score 10, the user's yearly income is 94.608 USDT
3. User positions — borrow 30 USDT (capped at 15), supply 10 USDT (capped at 10)
4. Allocating 94.608 USDT across the capped legs (15 + 10 = 25): borrow 94.608 × 15/25 = 56.76 USDT, supply 94.608 × 10/25 = 37.84 USDT
5. APR: borrow 56.76/30 = 189%, supply 37.84/10 = 378%

Only the supply and borrow amounts below the cap generate Prime rewards. Amounts above the cap do not generate extra rewards. In the example, if the user supplies more USDT they won't generate more rewards (the supply amount considered is capped), so the supply APR would decrease.

`getPendingRewards(user)` / `getPendingRewardsStatic(user)` on PrimeV2 remain available for reading already-accrued, claimable rewards per market (the former accrues first, the latter is a read-only view).

## Bootstrap liquidity for the Prime program

There is bootstrap liquidity available for the Prime program. This liquidity:

* should be uniformly distributed over a period of time, configurable via VIP
* is defined by the tokens enabled for the Prime program

These requirements are enforced with the `PrimeLiquidityProvider` contract:

* PrimeV2 holds a reference to the `PrimeLiquidityProvider` contract
* PrimeV2 transfers to itself the available liquidity from the `PrimeLiquidityProvider` as soon as it is needed when a user claims interest, to reduce the number of transfers
* PrimeV2 takes into account the tokens available in the `PrimeLiquidityProvider` contract when interest is accrued and the estimated APR is calculated

Regarding the `PrimeLiquidityProvider`:

* It maintains a speed per token (`tokenDistributionSpeeds`, the number of tokens to release each block or second, depending on the chain — PrimeLiquidityProvider runs on `TimeManagerV8`) and the indexes needed to release the required funds
* Anyone can send tokens to it
* Only accounts authorized via ACM can change the `tokenDistributionSpeeds`
* It accrues the distributable funds for a token via `accrueTokens` (public), based on the elapsed blocks or seconds, the token's speed, and the last accrued block/second; `getEffectiveDistributionSpeed` returns the current effective per-block/second rate for a token
* It exposes a function to transfer the available funds to the PrimeV2 contract

## Pausing

PrimeV2 uses OpenZeppelin's `PausableUpgradeable`. The ACM-gated `pause()` / `unpause()` functions control the user-facing entry points (`claimPrime`, `claimPrimeBatch`, `claimInterest`). Accrual and score-update functions (`accrueInterest`, `accrueInterestAndUpdateScore`, `updateScores`) are intentionally **not** gated by the pause, so reward accounting stays fair and keepers can finish score rounds even while the contract is paused.


# DeviationBoundedOracle

The DeviationBoundedOracle (DBO) is the contract that sits between the [ResilientOracle](/technical-reference/reference-oracle/resilient-oracle) and the Core Pool Comptroller on the borrow-power path. It maintains a per-asset rolling price window, detects when spot deviates beyond a configured threshold, and returns conservative bounded prices while the deviation persists. Its user-visible behaviour is the [Protection Mode](/risk/protection-mode) feature; this article covers the contract itself — where it gets prices, what it stores, and how the Comptroller calls it.

For function-level signatures, structs, events, and errors see the [DeviationBoundedOracle reference](/technical-reference/reference-oracle/deviation-bounded-oracle).

## Pricing stack

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-ee77449383aa568ec03843b5e6b729947b025386%2Fdeviation-bounded-oracle-pricing-stack.png?alt=media" alt="DeviationBoundedOracle pricing stack architecture diagram"><figcaption><p>DeviationBoundedOracle pricing stack</p></figcaption></figure>

* The **ResilientOracle** fetches and cross-validates spot from the configured sources, exactly as today. The DBO does not replace this — it wraps it. The borrow-power path on the Comptroller never queries the ResilientOracle itself; the DBO is the only oracle the Comptroller talks to on that path.
* The **DeviationBoundedOracle (DBO)** is the single entry point for every borrow-power read on every market. On each call it fetches spot from the ResilientOracle, then returns one of three outcomes:
  * **Asset not whitelisted** (bounded pricing disabled): returns `(spot, spot)` immediately. No window read, no trigger logic, no storage write — a thin pass-through.
  * **Asset whitelisted, protection inactive**: returns `(spot, spot)`. The price equals spot, but the call still expands the rolling window if needed and runs the trigger check, so this is the path that *enters* protection mode if a deviation appears.
  * **Asset whitelisted, protection active**: returns `(min(spot, windowMin), max(spot, windowMax))`.
* The **liquidation path** (`USE_LIQUIDATION_THRESHOLD` and `liquidateCalculateSeizeTokens`) calls the ResilientOracle directly and never touches the DBO. Eligibility, seize amount, and incentive calculations stay on real-time spot regardless of whether protection is active on either side of the position.

So the DBO is always in the call chain for the borrow-power path. For non-whitelisted markets it is transparent and the price the Comptroller sees is identical to what the ResilientOracle returned; for whitelisted markets it adds the window/trigger logic on top.

## Rolling price window

For each whitelisted asset the DBO stores a `(minPrice, maxPrice)` pair representing the lowest and highest spot prices observed inside a short rolling window (target \~15 minutes). It is maintained from two distinct sources:

* **User-triggered, expansion only.** Every relevant price read pulls spot from the ResilientOracle. If `spot < minPrice`, `minPrice` is pulled down to spot; if `spot > maxPrice`, `maxPrice` is pushed up. User activity can only widen the window — never contract it.
* **Keeper-triggered, bidirectional.** The keeper maintains the true rolling window off-chain and pushes corrected values via `updateMinPrice` / `updateMaxPrice`. On-chain validation enforces both a spot bound (`newMin ≤ spot`, `newMax ≥ spot`) and a cross bound (`newMin ≤ maxPrice`, `newMax ≥ minPrice`) so the window can never be inverted. The 5% `KEEPER_DEADBAND` is *not* enforced inside the write path; the keeper queries `checkAndGetWindowDrift` (a view) to skip pushes whose delta is below the deadband, purely as a gas-saving heuristic. This is what brings the window back inwards once an extreme has aged out.

## Trigger and exit

Protection activates as a side-effect of the price read whenever spot has moved past the deviation threshold relative to the stored window:

```
pump:  spot >  minPrice × (1 + triggerThreshold)
crash: spot <  maxPrice × (1 − triggerThreshold)
```

`triggerThreshold` is per-asset, configured by governance, and bounded between `MIN_THRESHOLD = 5%` and `MAX_THRESHOLD = 50%` (the 5% floor keeps routine keeper corrections from accidentally firing it). The cooldown timer (`lastProtectionTriggeredAt`) is re-stamped only on the first activation or when spot makes a *new* extreme this update (`windowExpanded == true`). Recovery within the existing window keeps the cooldown ticking, so `exitProtectionMode` remains reachable once the price stabilises — sustained reads at the same elevated/depressed level do not, by themselves, defer exit.

Exit cannot turn itself off. Both conditions must hold:

```
1. now − lastTrigger ≥ cooldownPeriod
2. (maxPrice − minPrice) / minPrice < resetThreshold        // resetThreshold < triggerThreshold
```

When both are satisfied, the keeper / monitor calls `exitProtectionMode(asset)` (or includes an `ExitProtectionMode` item inside `syncPriceBoundsAndProtections`) to clear the active flag. Governance retains a fallback path.

## Bounded-price computation

For each whitelisted asset the DBO returns a `(collateralPrice, debtPrice)` pair:

```
protection inactive:  collateralPrice = spot,                  debtPrice = spot
protection active:    collateralPrice = min(spot, windowMin),  debtPrice = max(spot, windowMax)
```

* Collateral is capped at the recent window low so a pumped asset cannot be over-borrowed against.
* Debt is floored at the recent window high so a crashed borrow asset cannot be repaid cheaply.
* Both legs collapse back to spot once the trigger clears.

## Transient price cache

Every non-view path writes the resolved `(collateralPrice, debtPrice)` pair into EIP-1153 transient storage so a follow-up `getBoundedPricesView` in the same transaction can return it without re-fetching spot. The cache is per-asset, transaction-scoped, and clears at the end of the transaction.

Caching is configurable per asset via `setCachingEnabled(asset, enabled)`, independently of the bounded-pricing flag. When disabled, writes are no-ops and reads always miss, so every view call recomputes live from the ResilientOracle — a safety lever for forcing fresh evaluation, not a default knob.

## Function surface

The contract exposes three small groups of entry points, one per caller role.

**Comptroller — borrow-power path.** Computing a bounded price requires fetching fresh spot and updating the rolling window — both are state-mutating, so they cannot live inside a `view` function. But the borrow-power check runs through `ComptrollerLens`, which is `view`. The DBO splits the work into two calls and uses the transient cache (above) as the bridge:

* `updateProtectionState(vToken)` — non-view. Pulls fresh spot, expands the window, evaluates the trigger, and writes the resolved pair to the cache. The Comptroller calls this once per entered asset at the start of every borrow-power check, *before* the lens runs. This is also the single non-view entry point that advances DBO state day-to-day.
* `getBoundedPricesView(vToken)` (and the per-leg `getBoundedCollateralPriceView` / `getBoundedDebtPriceView`) — view. Returns the cached pair if `updateProtectionState` ran earlier in the same transaction; otherwise recomputes live from spot without any state writes.
* `getBoundedPrices(vToken)` — non-view convenience that does both in one call; for integrators that don't need the pre-warmed-cache split.

**Keeper — window correction and exit.** All three actions are gated by `AccessControlManager`.

* `updateMinPrice(asset, newMin)` / `updateMaxPrice(asset, newMax)` — push corrected bounds. On-chain checks enforce `newMin ≤ spot ∧ newMin ≤ maxPrice` and `newMax ≥ spot ∧ newMax ≥ minPrice`. The 5% `KEEPER_DEADBAND` is checked *off-chain* via `checkAndGetWindowDrift`, not in this write path.
* `exitProtectionMode(asset)` — clears the active flag once cooldown has elapsed and the window has converged below `resetThreshold`. Reverts otherwise.
* `syncPriceBoundsAndProtections(items[])` — atomic batch of the above across multiple assets in a single ACM check; any item revert rolls back the whole batch.

**Governance — configuration.**

* `setTokenConfig(...)` (and the batch variant) — initializes a market's `MarketProtectionState`, sets `triggerThreshold`, `resetThreshold`, `cooldownPeriod`, the initial `cachingEnabled` flag, and the initial bounded-pricing flag. Reverts on `MarketAlreadyInitialized`, `VAINotAllowed` (VAI is rejected by design), threshold violations, or if the seeded spot price overflows `uint128`. The initial bounded-pricing flag is independent of initialization, so an asset can be configured first and whitelisted later via `setAssetBoundedPricingEnabled`.
* `setAssetBoundedPricingEnabled(asset, enabled)` — toggles whether the DBO runs the full window/trigger logic for the asset or short-circuits to spot.
* `setCachingEnabled(asset, enabled)` — toggles the per-asset transient cache participation independently of bounded pricing.

## Liquidation path is unchanged

```
Liquidator → Comptroller (USE_LIQUIDATION_THRESHOLD) → ComptrollerLens → ResilientOracle.getUnderlyingPrice(...)
```

The borrow-power path uses `WeightFunction.USE_COLLATERAL_FACTOR` and is the only path that calls `_updateProtectionStates`. The liquidation path uses `WeightFunction.USE_LIQUIDATION_THRESHOLD` and reads spot from the ResilientOracle directly. No bounded prices touch eligibility, seize-amount, or incentive calculations regardless of whether protection is active on either side of the position.

## Off-chain components

* **Keeper.** Maintains the authoritative 15-minute rolling window off-chain and pushes corrected `minPrice` / `maxPrice` on-chain whenever the stored values have drifted past the 5% deadband. The on-chain writes are constrained by `newMin ≤ spot` and `newMax ≥ spot`. Once the on-chain exit conditions are satisfied — cooldown elapsed and window converged below `resetThreshold` — the keeper submits `exitProtectionMode(asset)`, or includes an `ExitProtectionMode` item inside a `syncPriceBoundsAndProtections` batch.
* **Monitor.** Observes protection events and price normalization across independent feeds; gates keeper exit calls on real recovery rather than just the on-chain timer.
* **Governance.** Whitelists assets via `setTokenConfig` (single or batch), sets per-asset `triggerThreshold`, `resetThreshold`, and `cooldownPeriod`, can toggle bounded pricing through `setAssetBoundedPricingEnabled`, and retains a fallback path to disable protection for an asset if the keeper path is unavailable.

## Further Reading

* [Protection Mode (risk overview)](/risk/protection-mode)
* [DeviationBoundedOracle contract reference](/technical-reference/reference-oracle/deviation-bounded-oracle)
* [Resilient Price Oracle](/risk/resilient-price-oracle)
* [Repository](https://github.com/VenusProtocol/oracle)


# FlashLoan

{% hint style="warning" %}
**Technical version map (BNB Chain).** This article is mapped to `venus-protocol` source commit `46afc66b1dbd61a707d0a3492b3ec21bf90fc17a`. The committed deployment artifacts record:

* Core Comptroller Unitroller proxy `0xfD36E2c2a6789Db23113685031d7F16329158384`;
* candidate `FlashLoanFacet` `0x7F00af2f30a55e79311392C98fBBfA629D19b3A5`, deployed at block `95559359`, whose embedded `FlashLoanFacet.sol` matches that source commit apart from a final newline; and
* candidate `VBep20Delegate` `0xb25b57599BA969c4829699F7E4Fc4076D14745E1`, deployed at block `87124541`, whose embedded `VToken.sol` contains the transfer, fee, and debt functions described below but differs from the source commit in later flash-loan interest-accrual and accounting changes.

These artifacts are candidate-build evidence, not proof of current routing or market configuration. At a recorded block, resolve the Unitroller diamond routing and selected facet code for `executeFlashLoan(address,address,address[],uint256[],bytes)` (selector `0x5544ed9c`). For every market used, also resolve the active vToken delegate and code, flash-loan enablement and fee/share values, listing, caps, cash, and pause state. Verify the initiator whitelist, user delegation, and effective ACM permissions at the same block.
{% endhint %}

The Core Pool flash-loan implementation is a hybrid settlement mechanism for authorized initiating contracts. It supports full repayment in the same transaction and, when all ordinary borrowing checks pass, partial repayment with the unpaid balance created as debt for an `onBehalf` account.

The operation is atomic, but atomicity does not mean the protocol keeps custody of the tokens throughout the callback. Each vToken transfers underlying tokens to the receiver, and the receiver can interact with external contracts while it holds them. Either the final state, including any newly created debt, commits as a whole or a revert rolls back the transaction's state changes.

## Important properties

* *Authorized initiators:* `msg.sender` must be in the Core Comptroller's flash-loan authorization mapping.
* *Delegation:* if `msg.sender` differs from `onBehalf`, the initiating contract must be an approved delegate for that account.
* *Multi-asset support:* one request can include up to 200 listed, flash-loan-enabled Core Pool markets.
* *Per-market fees:* each vToken stores its own flash-loan fee rate and protocol-share rate.
* *Hybrid settlement:* actual repayment must cover at least the fee. Any remaining principal-and-fee balance is attempted as ordinary debt for `onBehalf`.
* *Conditional debt creation:* debt conversion can fail because of market, pause, oracle, cap, policy, or collateral-factor borrowing-power checks. If it fails, the entire transaction reverts.
* *System and market controls:* flash loans can be paused system-wide and enabled or disabled per market.

Successful debt conversion creates a normal interest-bearing borrow. It does not protect the account from later liquidation.

## Core execution

### `FlashLoanFacet.executeFlashLoan`

```solidity
function executeFlashLoan(
    address payable onBehalf,
    address payable receiver,
    VToken[] memory vTokens,
    uint256[] memory underlyingAmounts,
    bytes memory param
) external nonReentrant;
```

The function validates and settles the complete operation.

### Validation

Before transferring funds, the Comptroller checks that:

* flash loans are not paused system-wide;
* `onBehalf` and `receiver` are nonzero addresses;
* the asset array is nonempty and contains no more than 200 entries;
* `vTokens` and `underlyingAmounts` have equal lengths;
* every market is listed and has flash loans enabled;
* every requested amount is nonzero;
* `msg.sender` is authorized to initiate flash loans; and
* `msg.sender` is an approved delegate when it differs from `onBehalf`.

Authorization applies to the initiating caller. The receiver is a separate address and must independently authenticate the callback.

### Phase 1: fees and asset transfer

For every requested market, the Comptroller:

1. calls that vToken's `calculateFlashLoanFee` function; and
2. calls `transferOutUnderlyingFlashLoan(receiver, amount)`.

`transferOutUnderlyingFlashLoan` transfers the underlying asset from the vToken to the receiver. The funds therefore leave the vToken during the callback. Transaction rollback and settlement checks protect atomic execution; they are not continuous protocol custody and do not eliminate receiver or external-protocol risk.

### Phase 2: receiver callback

The Comptroller calls:

```solidity
function executeOperation(
    VToken[] calldata vTokens,
    uint256[] calldata amounts,
    uint256[] calldata premiums,
    address initiator,
    address onBehalf,
    bytes calldata param
) external returns (bool success, uint256[] memory repayAmounts);
```

`initiator` is the authorized caller of `executeFlashLoan`. `repayAmounts` contains numeric repayment amounts, not approval booleans. The receiver must return one amount for each requested asset and approve each corresponding vToken to pull that amount.

If the receiver returns `false` or reverts, the entire transaction reverts.

### Phase 3: settlement and optional debt

For each asset, settlement uses the following sequence:

1. Calculate `maximum repayment = principal + fee`.
2. Cap the receiver's requested repayment at that maximum.
3. Revert before the token pull if the requested repayment is less than the fee.
4. Pull the requested amount from the receiver through the vToken.
5. Revert if the actual amount received by the vToken is less than the fee.
6. Calculate `unpaid balance = principal + fee - actual amount received`.
7. If the unpaid balance is nonzero, call `flashLoanDebtPosition(onBehalf, unpaid balance)`.

The receiver needs sufficient balance and allowance for the amount it reports. Missing allowance does not skip repayment and trigger debt conversion; it causes the token transfer, and therefore the transaction, to fail.

`flashLoanDebtPosition` uses the ordinary borrow path. Debt creation must pass the relevant protocol and market pause checks, listing and pool policy, valid oracle prices, borrow cap, and the `onBehalf` account's collateral-factor borrowing power. Merely having supplied collateral does not guarantee conversion.

For multi-asset requests, a failure while settling any asset reverts all state changes in the transaction.

## Fee handling

### `VToken.calculateFlashLoanFee`

```solidity
function calculateFlashLoanFee(uint256 amount) public view returns (uint256, uint256);
```

The function returns:

* the total fee, calculated from the vToken's `flashLoanFeeMantissa`; and
* the protocol portion, calculated from the vToken's `flashLoanProtocolShareMantissa`.

Both calculations use fixed-point arithmetic and truncate to token units. Fee configuration is per vToken, not one global Comptroller value.

### `VToken.transferInUnderlyingFlashLoan`

```solidity
function transferInUnderlyingFlashLoan(
    address payable from,
    uint256 repaymentAmount,
    uint256 totalFee,
    uint256 protocolFee
) external nonReentrant returns (uint256 actualAmountTransferred);
```

Only the Comptroller can call this function. It pulls underlying from the receiver, checks the actual amount received against the fee, sends the protocol portion of the fee to the Protocol Share Reserve, records the PSR income type, and clears the active flash-loan amount. The non-protocol portion remains in market cash; there is no separate per-supplier credit call during settlement.

Tokens with transfer taxes or other non-standard transfer behavior can make `actualAmountTransferred` differ from the requested amount. Receiver code must reason about the actual amount the vToken will receive.

### Fee and market configuration

```solidity
function setFlashLoanEnabled(bool enabled) external returns (uint256);

function setFlashLoanFeeMantissa(
    uint256 flashLoanFeeMantissa_,
    uint256 flashLoanProtocolShare_
) external returns (uint256);
```

These vToken functions are controlled through the Access Control Manager using their exact function signatures. Both fee parameters are scaled by `1e18` and cannot exceed `1e18`.

The Core Comptroller also exposes ACM-controlled functions to authorize initiating accounts and pause flash loans system-wide:

```solidity
function setWhiteListFlashLoanAccount(address account, bool isWhiteListed) external;

function setFlashLoanPaused(bool paused) external;
```

Do not assume the initiator whitelist is temporary or that the feature is permissionless. Integrators must verify current on-chain authorization, pause state, market enablement, and fee configuration.

## VToken cash accounting

```solidity
function getCash() external view returns (uint256);
```

`getCash` reports the vToken's actual underlying-token cash. During an active flash loan, it therefore reflects the lower balance after funds have been transferred to the receiver. Flash-loan-aware internal accounting can include the tracked active amount where required for rate and exchange-rate calculations. Integrators that depend on the precise accounting sequence must bind their analysis to the active vToken implementation, not only to repository `main`.

## Receiver requirements

A receiver should implement `IFlashLoanReceiver` directly or use a separately published, versioned, and audited integration library.

The `FlashLoanReceiverBase` found under `contracts/test` in the protocol repository is a test helper, not a production integration standard. Its Comptroller field is ordinary public storage, not a Solidity `immutable`, and the helper does not authenticate callbacks or implement settlement.

Production receiver code must:

1. Authenticate that `msg.sender` is the expected Core Comptroller.
2. Validate `initiator`, `onBehalf`, `vTokens`, amounts, premiums, and strategy parameters.
3. Execute only calls and routes the receiver is designed to trust.
4. Return one numeric repayment amount for every asset.
5. Approve each corresponding vToken to pull the returned amount.
6. Ensure the vToken will actually receive at least the fee for each asset.
7. Obtain explicit user authorization before intentionally leaving debt for `onBehalf`.
8. Verify that the account has sufficient current borrowing power for any intended unpaid balance.
9. Return `false` or revert when strategy validation fails.

Receiver code must not treat atomic rollback as protection from every integration risk. External protocols, swap routes, token behavior, callback authentication, approvals, and any successfully created user debt remain part of the receiver's security model.

## Numerical examples

The following examples use a 100,000 USDC principal and an illustrative 0.09% fee:

```
Fee = 100,000 × 0.0009 = 90 USDC
Maximum repayment = 100,000 + 90 = 100,090 USDC
```

The example fee is not a statement of the current fee for every market. Read the selected vToken's on-chain configuration.

### Full repayment

If a strategy finishes with 100,200 USDC, approves the vToken, and reports full repayment:

```
Amount pulled = 100,090 USDC
Amount remaining for the receiver = 100,200 - 100,090 = 110 USDC
Debt created = 0
```

The arithmetic does not guarantee strategy profit. It only describes settlement if the receiver actually has the stated balance and every check succeeds.

### Partial repayment: 99,500 USDC returned

```
Unpaid balance = 100,090 - 99,500 = 590 USDC
```

The protocol attempts to create 590 USDC of ordinary debt for `onBehalf`. The transaction completes only if the vToken receives the 99,500 USDC and the additional 590 USDC borrow passes every ordinary borrow check. Otherwise, the transaction reverts.

### Partial repayment: 80,000 USDC returned

```
Unpaid balance = 100,090 - 80,000 = 20,090 USDC
```

The debt attempt is 20,090 USDC, not 20,000 USDC. A statement such as “the user has 10 ETH collateral” is not enough to determine success. The ETH oracle price, applicable collateral factor, existing supplies and borrows, caps, pause state, and other account and market data are required.

### Repayment below the fee

If the receiver reports 50 USDC when the fee is 90 USDC, the Comptroller detects `50 < 90` and reverts before asking the vToken to pull repayment. No debt conversion occurs, all transaction state changes roll back, and the transaction sender still pays gas.

## Events

The relevant events include:

* `FlashLoanExecuted`;
* `FlashLoanRepaid`;
* `TransferOutUnderlyingFlashLoan`;
* `TransferInUnderlyingFlashLoan`;
* `FlashLoanStatusChanged`;
* `FlashLoanFeeUpdated`;
* `IsAccountFlashLoanWhitelisted`; and
* `FlashLoanPauseChanged`.

Use the ABI of the active implementations as the authoritative event schema.

## Errors

Important errors include:

* `FlashLoanPausedSystemWide`;
* `FlashLoanNotEnabled`;
* `NoAssetsRequested`;
* `TooManyAssetsRequested`;
* `InvalidFlashLoanParams`;
* `InvalidAmount`;
* `MarketNotListed`;
* `SenderNotAuthorizedForFlashLoan`;
* `NotAnApprovedDelegate`;
* `ExecuteFlashLoanFailed`;
* `NotEnoughRepayment`;
* `InsufficientRepayment`;
* `FailedToCreateDebtPosition`;
* `InvalidComptroller`; and
* `FlashLoanAlreadyActive`.

The exact error set depends on the active Comptroller facet and vToken implementation. Integrations should generate selectors from the matching deployed ABI.


# Risk Stewards

The Risk Stewards system is a permissioned, automated pipeline for applying a bounded set of risk-parameter changes to Venus markets without a manual governance proposal for each one. Whitelisted risk providers publish recommendations to a Venus-owned on-chain oracle; the protocol validates each one, applies small changes immediately, holds larger ones behind a timelock, and bridges changes destined for other chains over LayerZero. Governance keeps control of everything that matters: who may publish, who may execute held updates, the bounds, and an emergency pause.

It currently supports four update types: `supplyCap`, `borrowCap`, `collateralFactors` (collateral factor and liquidation threshold, set together), and `interestRateModel`. Anything outside this set still requires a full VIP.

This article describes the contracts and the update lifecycle. For function-level signatures, structs, events, and errors, see the reference page linked under each contract.

## Architecture

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-ef3ea8a8b3ccc7d93252df1b5547d9d14f5f0cf5%2Frisk-stewards-architecture.svg?alt=media" alt="Risk Stewards architecture: providers publish to the Risk Oracle on BNB Chain; the RiskStewardReceiver routes updates to local stewards or forwards them over LayerZero to a Destination Steward Receiver on remote chains; governance configures the whole system"><figcaption><p>Providers publish to the Risk Oracle on BNB Chain; the RiskStewardReceiver applies updates locally or bridges them to a Destination Steward Receiver on remote chains. Governance configures and gates the whole pipeline.</p></figcaption></figure>

BNB Chain is the **source chain**: it hosts the oracle and the source-chain receiver. Every update is published there, even ones destined for another chain. Each supported chain (including BNB Chain itself) hosts the steward contracts that perform the actual change, plus a destination receiver on non-source chains.

[**Risk Oracle**](/technical-reference/reference-governance/risk-oracle) is the single source of truth for recommendations. A whitelisted provider calls `publishRiskParameterUpdate(referenceId, newValue, updateType, market, poolId, dstEid, additionalData)`; the oracle assigns a monotonic `updateId`, records the previous value for history, and emits `UpdatePublished`. Senders are added and removed only by governance (`addAuthorizedSender` / `removeAuthorizedSender`), as are the supported update-type strings (`addUpdateType`, `setUpdateTypeActive`). The oracle only stores recommendations; it never touches the protocol itself.

[**RiskStewardReceiver**](/technical-reference/reference-governance/risk-steward-receiver) **(RSR)** on BNB Chain is where updates are processed. `processUpdate(updateId)` is permissionless: anyone can push a published update through the pipeline. The RSR holds a per-type config (`riskParameterConfigs`) mapping each update type to its steward, a `debounce` period, and a `timelock`, all set by governance. It validates the update, decides the execution path, and either applies it locally, registers it behind a timelock, or forwards it cross-chain.

[**DestinationStewardReceiver**](/technical-reference/reference-governance/destination-steward-receiver) **(DSR)** lives on each non-source chain. It receives bridged updates over LayerZero, registers them with status `Pending`, and holds them for a fixed `remoteDelay` (6 hours by default) before a whitelisted executor applies them.

**Risk Stewards** do the actual writing. All three inherit [**BaseRiskSteward**](/technical-reference/reference-governance/base-risk-steward), which holds the `safeDeltaBps` bound and enforces that `applyUpdate` may only be called by the steward's receiver (`OnlyRiskStewardReceiver`). Each steward owns one parameter family:

| Steward                                                                                                   | Update type(s)           | Encoded `newValue`     | Writes via                                                         |
| --------------------------------------------------------------------------------------------------------- | ------------------------ | ---------------------- | ------------------------------------------------------------------ |
| [MarketCapsRiskSteward](/technical-reference/reference-governance/market-caps-risk-steward)               | `supplyCap`, `borrowCap` | one `uint256`          | `setMarketSupplyCaps` / `setMarketBorrowCaps`                      |
| [CollateralFactorsRiskSteward](/technical-reference/reference-governance/collateral-factors-risk-steward) | `collateralFactors`      | two `uint256` (CF, LT) | `setCollateralFactor`                                              |
| [IRMRiskSteward](/technical-reference/reference-governance/irm-risk-steward)                              | `interestRateModel`      | one `address`          | `_setInterestRateModel` (core) / `setInterestRateModel` (isolated) |

## Update types and encoding

Each update carries its new value as opaque ABI-encoded `bytes`. Passing it this way is deliberate: one `newValue` field (and so a single `publishRiskParameterUpdate` entry point and one update struct) can express payloads of completely different shapes. A market cap is a single number, collateral factors are a pair (the factor and its liquidation threshold), and an interest rate model is an address; each steward decodes the bytes into the shape it expects and rejects anything of the wrong length. New parameter types can therefore be added later without changing the oracle or receiver interfaces.

The encoding per type (type strings are case-sensitive):

```
supplyCap / borrowCap   abi.encode(uint256)            // 18-decimal amount
collateralFactors       abi.encode(uint256, uint256)   // collateral factor, liquidation threshold (both required)
interestRateModel       abi.encode(address)            // new IRM contract
```

`poolId` selects the pool: `0` for core-pool and isolated-pool markets, and a non-zero group id for an eMode pool inside the core pool. `dstEid` is `0` (or the source chain's own endpoint id) for a local BNB Chain update, or a LayerZero endpoint id for a remote chain.

## Validation and routing

`processUpdate` is permissionless, and that is safe by design: every safety property lives in the validation below and in the steward bounds, not in gating who may call it; the caller only supplies the gas. Before acting, the RSR validates the update:

* the update type's config is **active**;
* it is the **latest** published update for that market and type: a newer recommendation supersedes an older one, which then reverts as `UpdateIsExpired`, so a provider corrects a mistake simply by publishing again;
* it has **not expired** (updates are valid for `UPDATE_EXPIRATION_TIME`, 2 days, from publication) and has **not already been resolved** (executed, rejected, or expired);
* registering it behind a timelock would not push the unlock time past that 2-day expiry (`UpdateWillExpireBeforeUnlock`), so the system never parks an update that could never be executed in time; and
* for local updates, the **debounce** window since the last applied change for that `(market, updateType)` has passed, and no other non-expired pending update of the same type is already registered.

Debounce and timelock are different levers: **debounce** rate-limits how often a given market/type can change at all; **timelock** delays an individual large change so it can be reviewed. A change can clear debounce yet still be timelocked. Once validated, the RSR asks the steward whether the change is safe for immediate execution and routes it down one of three paths: local-immediate, local-timelocked, or cross-chain.

## Local execution: immediate vs. timelocked

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-29ce6c37b679d02e666eadf009d264e8b9658873%2Frisk-stewards-local-flow.svg?alt=media" alt="Local update flow on BNB Chain: provider publishes to the Risk Oracle, anyone calls processUpdate on the RiskStewardReceiver, which either applies the change immediately when it is within the safe delta or registers it behind a timelock for an executor to apply"><figcaption><p>A local (BNB Chain) update. Within the safe delta it applies immediately; otherwise it is registered behind a timelock and applied later by a whitelisted executor.</p></figcaption></figure>

The split is decided by the steward's `isSafeForDirectExecution`, which compares the new value against the live on-chain value using `safeDeltaBps`:

* **Market caps** execute immediately when the change is within `±safeDeltaBps` of the current cap, but only if the current cap is non-zero. Setting a cap from zero always takes the timelock, because there is no baseline to bound the change against.
* **Collateral factors** execute immediately only when *both* the collateral factor and the liquidation threshold move within `safeDeltaBps`, and neither current value is zero. **eMode** updates (`poolId != 0`) always take the timelock; eMode runs at higher leverage, so its collateral changes are never auto-applied.
* **Interest rate model** changes *always* take the timelock: an IRM is a contract address, so there is no meaningful "small change" to bound.

A redundant update (new value equal to the current one) reverts with `RedundantValue`.

The bound is symmetric and per-steward. `safeDeltaBps` is applied to the absolute difference (`|new − current| ≤ safeDeltaBps × current`), so a 50% bound permits a move of up to ±50% in either direction. Each steward holds its own bound, set independently by governance: at launch the Market Caps steward uses 50% and the Collateral Factors steward 10%; collateral factors and liquidation thresholds bear directly on account solvency, so they are allowed a far smaller automatic move than caps.

When the change is safe, the RSR applies it in the same `processUpdate` transaction. Otherwise it registers the update with `unlockTime = now + timelock` and status `Pending`; after the timelock a **whitelisted executor** calls `executeRegisteredUpdate(updateId)` to apply it, or `rejectUpdate(updateId)` to discard it. A registered update that is neither executed nor rejected within the 2-day window expires.

## Cross-chain execution

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-a0d1ad06844039891fcc62aadac8d59f7759b51a%2Frisk-stewards-remote-flow.svg?alt=media" alt="Remote update flow: provider publishes on BNB Chain, the RiskStewardReceiver forwards the update over LayerZero to the Destination Steward Receiver on the target chain, which holds it for the remote delay before a whitelisted executor applies it through the local steward"><figcaption><p>A remote update is forwarded over LayerZero, registered on the destination chain, held for the remote delay, then applied by a whitelisted executor through the destination-chain steward.</p></figcaption></figure>

When `dstEid` points at another chain, the RSR never executes locally. It registers the update and forwards it over LayerZero with `lzSend`; the local status becomes `SENT_TO_DESTINATION`. If a bridge message fails to deliver, a whitelisted executor can re-send it with `resendRemoteUpdate`.

On the destination chain the DSR receives the message (`_lzReceive`), registers it as `Pending`, and stamps its arrival time. A whitelisted executor on that chain calls `executeUpdate(updateId)` once `remoteDelay` has elapsed and before the 2-day expiry, or `rejectUpdate(updateId)` to discard it. The write is performed by the same kind of steward as on the source chain. Remote updates therefore always pass through a delay and an executor; there is no immediate cross-chain path.

Two design choices make this robust. First, the delay clock lives on the destination, not the source: the RSR is a LayerZero OApp sender and the DSR a receiver, and because bridge latency is unpredictable the DSR measures `remoteDelay` from the message's *arrival* time. Its config carries no timelock field at all (only a steward, a debounce, and the chain-wide `remoteDelay`), so locally the forwarded update is recorded with an immediate unlock and status `SENT_TO_DESTINATION` purely as bookkeeping, while the real wait plays out on the destination. Second, the RSR pays the LayerZero messaging fee from its own native balance (funded through its `receive()` and recoverable by governance with `sweepNative`), so the `processUpdate` caller never pre-pays the bridge, and a message that fails to deliver can be re-sent with `resendRemoteUpdate` rather than re-published.

## Roles and governance

| Role                     | Capability                                                                                                                   | Granted by       |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------- | ---------------- |
| Risk parameter providers | Publish recommendations; may also call `processUpdate` for their own                                                         | Governance (ACM) |
| Anyone                   | Call `processUpdate`                                                                                                         | Permissionless   |
| Whitelisted executors    | Execute / reject timelocked and remote updates; resend failed bridges                                                        | Governance (ACM) |
| Governance / ACM         | Whitelist providers & executors; set debounce, timelock, `safeDeltaBps`, `remoteDelay`; register update types; pause the RSR | DAO (VIP)        |

The framework never bypasses governance. Stewards can only touch the four supported parameters, and only within governance-set bounds; larger, IRM, and eMode changes are delayed and vetoable; updates expire and cannot be replayed; and governance can pause processing or remove a provider at any time. The framework was enabled on BNB Chain in [VIP-592](https://app.venus.io/#/governance/proposal/592?chainId=56), which onboarded Allez Labs as the first external risk provider; at launch governance set a 3-day debounce, a 6-hour timelock, a 50% safe delta for market caps, and a 10% safe delta for collateral factors.

## Further reading

* [Risk Oracle & Risk Stewards (overview)](/risk/risk-oracle-and-risk-stewards)
* Contract references: [Risk Oracle](/technical-reference/reference-governance/risk-oracle) · [RiskStewardReceiver](/technical-reference/reference-governance/risk-steward-receiver) · [DestinationStewardReceiver](/technical-reference/reference-governance/destination-steward-receiver) · [BaseRiskSteward](/technical-reference/reference-governance/base-risk-steward) · [MarketCapsRiskSteward](/technical-reference/reference-governance/market-caps-risk-steward) · [CollateralFactorsRiskSteward](/technical-reference/reference-governance/collateral-factors-risk-steward) · [IRMRiskSteward](/technical-reference/reference-governance/irm-risk-steward)
* [VIP-592: Risk Stewards Framework Implementation](https://app.venus.io/#/governance/proposal/592?chainId=56)
* [Repository](https://github.com/VenusProtocol/governance-contracts)


# Shortfall and auctions (historical; BNB mainnet paused)

{% hint style="warning" %}
**Legacy and disabled on BNB Chain.** This page describes the former Shortfall auction design used by Venus Isolated Pools. [Isolated Pools have been deprecated](/guides/isolated-pools-deprecation).

At BNB Chain block `118328948` (August 27, 2026, 05:38:04 UTC), `Shortfall.auctionsPaused()` was `true`, and every pool returned by the PoolRegistry had auction status `NOT_STARTED`. The pause was executed as part of the TokenBuyback migration in [this transaction](https://bscscan.com/tx/0xe49e06d4711752f731cb38879c23c9a6954b2c6d7b57181352a32a2ef8552c85).

Do not approve tokens to Shortfall or call `startAuction`, `restartAuction`, or `placeBid` based on this historical page. Users with remaining Isolated Pool positions should follow the deprecation guide linked above. A historical bidder with a nonzero `tokenDebt(token, account)` may still need the separate `claimTokenDebt` recovery path.
{% endhint %}

## Historical design

When an undercollateralized borrow was written off in an Isolated Pool market, its borrow balance stopped accruing interest and the market tracked the amount as bad debt.

*V* represents total bad debt including interest accrued before the write-off. If the initial borrow index was 1.2 and the borrow index was 1.5 when 100 USDC of principal became bad debt, then:

```
V = 100 USDC × (1.5 / 1.2) = 125 USDC
```

Historically, anyone could start an auction when:

* no auction was in progress for that pool (`status` was `NOT_STARTED` or `ENDED`); and
* the pool's USD-denominated bad debt was greater than or equal to `Shortfall.minimumPoolBadDebt()`.

`Shortfall.incentiveBps()` configured the bidder incentive. Its value at the snapshot block above was 1,000 bps, or 10%; 10% was a configured value, not a source-enforced maximum.

*N* represents the pool's total bad debt in USD and *M* represents the pool's risk-fund balance in USD. After calculating the bad debt plus the configured incentive, the historical source selected the auction type as follows:

* `LARGE_POOL_DEBT` when the bad debt plus incentive was greater than or equal to *M*: bidders competed by offering a larger percentage of every market's bad debt. The winner was owed the auction's entire `seizedRiskFund` snapshot; Shortfall either transferred the payout or recorded it as claimable `tokenDebt` if the transfer to the winner failed.
* `LARGE_RISK_FUND` when the bad debt plus incentive was less than *M*: every bidder covered 100% of every market's bad debt and competed by requesting a smaller percentage of the `seizedRiskFund` snapshot.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-36d8fc1fde84217ae6cd1d1e88f54a5ca64cd6d4%2Fauctions.png?alt=media&amp;token=c1469add-ce0d-429c-9472-8b567dd559e3" alt="Historical Shortfall auction scenarios"><figcaption><p>Historical Shortfall auction scenarios</p></figcaption></figure>

## Auction state and pause semantics

The historical ABI was:

```solidity
function startAuction(address comptroller) external;

function restartAuction(address comptroller) external;

function placeBid(
    address comptroller,
    uint256 bidBps,
    uint256 auctionStartBlockOrTimestamp
) external;

function closeAuction(address comptroller) external;
```

The third argument to `placeBid` had to equal `auctions(comptroller).startBlockOrTimestamp`. It was not `startBidBps`, and the deployed struct did not have a `startBlock` field.

The generated `auctions(address)` getter exposed only scalar fields. It did not expose the dynamic `markets` array or the per-market `marketDebt` and `bidAmount` mappings. Historical integrations had to obtain `auctionStartBlockOrTimestamp`, `markets`, `marketsDebt`, and `startBidBps` from the matching `AuctionStarted` event, then independently verify every underlying token and its decimals. `Comptroller.getAllMarkets()` describes current pool membership and is not a substitute for an auction snapshot.

Pausing had function-specific effects:

| Function         | Effect while `auctionsPaused() == true`                                                                             |
| ---------------- | ------------------------------------------------------------------------------------------------------------------- |
| `startAuction`   | Reverted.                                                                                                           |
| `restartAuction` | Reverted.                                                                                                           |
| `placeBid`       | Did not check the pause flag; it could accept a bid only if an auction was already `STARTED` and not stale.         |
| `closeAuction`   | Did not check the pause flag; it could close an already `STARTED` auction with a bidder after the configured limit. |
| `claimTokenDebt` | Remained available for historical transfer debt.                                                                    |

Therefore, a pause flag alone did not prove that no auction required completion. The block-specific state check recorded at the top of this page separately confirmed that all eight registered BNB Chain pools were `NOT_STARTED`.

At the snapshot block, both `waitForFirstBidder` and `nextBidderBlockLimit` were 100 blocks. In the historical source, an auction with no bidder became stale only when the current block was greater than `startBlockOrTimestamp + waitForFirstBidder`. An auction with a bidder could be closed only when the current block was greater than `highestBidBlockOrTimestamp + nextBidderBlockLimit`.

## Historical examples

The following examples explain the old source calculations. They are not instructions to participate in an auction.

### Scenario 1: bad debt plus incentive equals or exceeds the risk fund

Assume:

* market bad debt: 10 BTC;
* BTC price: $20,000, so *N* = $200,000;
* `incentiveBps`: 1,000, or 10%; and
* risk-fund balance *M*: 100,000 USDT, worth $100,000.

This was a `LARGE_POOL_DEBT` auction. The source calculated the starting bid as:

```
startBidBps = floor(10,000 × 10,000 × M
                    / (N × (10,000 + incentiveBps)))

                = floor(100,000,000 × 100,000
                    / (200,000 × 11,000))

                = 4,545 bps = 45.45%
```

For a market with 10 BTC of snapshotted debt, the first bid transferred:

```
floor(10 BTC × 4,545 / 10,000) = 4.545 BTC
```

The bidder historically had to approve the Shortfall proxy for at least the calculated amount of each underlying token in the `AuctionStarted` snapshot. Integer division rounded each token amount down in its smallest unit.

The first bid had to be at least 4,545 bps. Subsequent bids had to be higher than the current highest bid—for example, 4,600 bps and then 4,700 bps. A 4,090 bps, 41%, or 43% bid would not satisfy this example's starting-bid constraint. When a better bid was accepted, the previous bidder's deposited tokens were returned or recorded as claimable token debt if the transfer failed.

After the winning bid remained unchallenged beyond the configured next-bidder limit, `closeAuction(comptroller)` transferred the winning token amounts to the markets and requested the full 100,000 USDT `seizedRiskFund` snapshot for the winner. Shortfall then transferred that payout to the winner or recorded it as claimable `tokenDebt` if the winner transfer failed.

### Scenario 2: the risk fund exceeds bad debt plus incentive

Assume the same 10 BTC of debt at $20,000 per BTC, but a 500,000 USDT risk-fund balance.

This was a `LARGE_RISK_FUND` auction. The source applied the 10% incentive once:

```
badDebtPlusIncentive = $200,000 × (1 + 1,000 / 10,000)
                     = $220,000

share of total risk fund = $220,000 / $500,000 = 44%
```

The auction's `seizedRiskFund` snapshot was therefore 220,000 USDT, not 242,000 USDT. Every bidder transferred the full 10 BTC of snapshotted bad debt. `bidBps` selected the fraction of the 220,000 USDT snapshot that the bidder requested:

|  Bid | Requested risk-fund amount |
| ---: | -------------------------: |
| 100% |               220,000 USDT |
|  95% |               209,000 USDT |
|  94% |               206,800 USDT |

The first bid could be at most 100%, and each subsequent bid had to be lower than the current highest bid. After the next-bidder limit passed, `closeAuction(comptroller)` calculated the winner's payout from the winning percentage of the 220,000 USDT snapshot; the payout was transferred or recorded as claimable `tokenDebt` if the winner transfer failed.

### Historical restart behavior

When an auction had no bidder and became stale, `restartAuction(comptroller)` ended the old auction and created a new snapshot of bad debt and risk-fund balances. Restarting required auctions not to be paused. Because BNB Chain auctions are currently paused and all registered pools were `NOT_STARTED` at the recorded block, this historical restart flow is not currently available.


# TokenBuyback Contract

{% hint style="info" %}
See the [TokenBuyback overview](/whats-new/token-converter) for migration context and the [deployed-contracts page](/deployed-contracts/token-converters) for proxy addresses. Live on BNB Chain via [VIP-620](https://app.venus.io/#/governance/proposal/620?chainId=56) and [VIP-621](https://app.venus.io/#/governance/proposal/621?chainId=56);
{% endhint %}

## Overview

`TokenBuyback` is the contract that holds protocol income arriving from `ProtocolShareReserve` (PSR) and converts it into a configured `BASE_ASSET` via on-chain DEX swaps. Each instance is deployed as a Transparent Proxy bound to a `(DESTINATION, BASE_ASSET)` pair at construction time; both addresses are immutable, so retargeting either requires a new deployment.

Swaps are initiated by an ACM-authorized finance-team cron, not by external community members. The cron builds the router calldata off-chain using DEX aggregators and submits it to `executeBuyback`. The contract validates a minimum output, pulls the actual `tokenIn` delta consumed by the router, forwards the resulting `BASE_ASSET` directly to `DESTINATION`, and applies two on-chain safety rails (USD daily cap + abnormal-slippage event) using a `ResilientOracle` reference price.

`TokenBuyback` implements `IIncomeDestination`, the same interface as the legacy converters, so PSR rewires to the new instances via a governance VIP without any PSR contract changes.

## Architecture

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-269cac600e8f3ce86446a97bc12d3f5349e04a78%2Ftoken_buyback_funds.svg?alt=media" alt="TokenBuyback architecture and PSR distribution flow"><figcaption><p>PSR distribution across TokenBuyback instances and their destinations (example: BNB Chain — 10 instances; percentages from PSR schema 0 — interest reserves)</p></figcaption></figure>

Per-instance state:

| Slot                                 | Purpose                                                                                           |
| ------------------------------------ | ------------------------------------------------------------------------------------------------- |
| `DESTINATION` (immutable)            | Address that receives `BASE_ASSET` after each swap or forward                                     |
| `BASE_ASSET` (immutable)             | Output token of every buyback — every swap converts into this                                     |
| `PROTOCOL_SHARE_RESERVE` (immutable) | Only address permitted to call `updateAssetsState` — prevents spoofed `AssetsReceived` events     |
| `RESILIENT_ORACLE` (immutable)       | USD-pricing source for the daily cap and the abnormal-slippage signal — not used for swap pricing |
| `allowedRouters`                     | On-chain allowlist of DEX routers (governance-managed)                                            |
| `assetsReserves[token]`              | Balance watermark used to derive the inflow delta on each PSR call                                |
| `dailyCapUsd`                        | Rolling 24h USD cap on `tokenIn` consumption (1e18-scaled)                                        |
| `slippageEventUsd`                   | Absolute USD threshold above which `AbnormalSlippage` fires (1e18-scaled)                         |
| `usdConsumedInWindow`, `lastUpdate`  | Leaky-bucket accumulator state for the daily cap                                                  |

## Income Flow

1. PSR transfers underlying tokens to `TokenBuyback` and calls `updateAssetsState(comptroller, asset)`.
2. The contract reads `balanceOf(this)`, compares against the `assetsReserves[asset]` watermark, emits `AssetsReceived(comptroller, asset, delta)` when the delta is non-zero, and resyncs the watermark to the current balance.
3. The cron monitors `AssetsReceived` events to know which token landed and from which pool.
4. When the cron decides to convert, it calls `executeBuyback` with off-chain-built router calldata.
5. `executeBuyback` swaps `tokenIn → BASE_ASSET` through the allowlisted router, validates `minAmountOut`, forwards the output to `DESTINATION`, pushes fresh oracle prices, enforces the daily cap, and emits `AbnormalSlippage` if the USD delta crosses the threshold.
6. `BASE_ASSET` that lands without needing a swap (e.g. PSR delivers `BASE_ASSET` directly) is sent on via `forwardBaseAsset`, which partitions the balance by caller-supplied `amount` so each portion can be attributed to a different comptroller via separate events.

## Daily USD Cap (leaky bucket)

`executeBuyback` enforces a rolling 24h USD cap on `tokenIn` consumption. The accumulator decays linearly between calls:

```
elapsed   = block.timestamp - lastUpdate
decayed   = elapsed >= WINDOW ? 0 : usdConsumedInWindow * (WINDOW - elapsed) / WINDOW
newUsage  = decayed + usdIn
require(newUsage <= dailyCapUsd)
usdConsumedInWindow = newUsage
lastUpdate          = block.timestamp
```

Where `WINDOW = 24 hours` and `usdIn = actualAmountIn * priceIn / 1e18`. The accumulator `usdConsumedInWindow` is bounded by `dailyCapUsd` at every moment, which caps sustainable throughput at `dailyCapUsd / 24h` plus a one-time burst of up to `dailyCapUsd` (so worst-case cumulative consumption over any rolling 24h interval is up to \~2 × `dailyCapUsd`). Reverts with `DailyCapExceeded(attempted, cap)` past the cap.

Default: **$30,000** cap at deploy. Tunable via `setDailyCapUsd` (ACM-restricted, rejects zero).

## Abnormal Slippage Signal

After settling the swap, `executeBuyback` USD-prices both legs and emits `AbnormalSlippage(tokenIn, actualAmountIn, amountOut, usdIn, usdOut)` when `usdIn − usdOut > slippageEventUsd`. The event is informational only — it does not revert. Used by off-chain monitoring to flag swaps routed through hostile pools or stale-aggregator outputs.

Default: **$500** absolute. Tunable via `setSlippageEventUsd` (ACM-restricted, rejects zero).

The slippage check uses the same oracle snapshot as the cap, refreshed via `oracle.updateAssetPrice(tokenIn)` and `oracle.updateAssetPrice(BASE_ASSET)` immediately before the read.

## Functions

### `updateAssetsState(address comptroller, address asset)`

PSR-only. Records the balance delta against the `assetsReserves[asset]` watermark and emits `AssetsReceived(comptroller, asset, delta)` when non-zero. Restricted by the `PROTOCOL_SHARE_RESERVE` immutable. The reported amount is a balance delta, not an authenticated source-of-funds record — tokens transferred directly to the contract outside the PSR flow are merged into the next event under whichever comptroller PSR happens to be processing.

### `executeBuyback(tokenIn, amountIn, minAmountOut, deadline, router, routerCalldata, comptroller)`

ACM-restricted. Performs the swap via the supplied router (must be on the allowlist) and forwards the output to `DESTINATION`.

Sequence:

1. Validate `deadline`, `tokenIn != BASE_ASSET`, router allowlisted, `amountIn > 0` and within `balanceOf(this)`.
2. Snapshot `tokenIn` and `BASE_ASSET` balances on this contract.
3. `forceApprove(router, amountIn)`, `router.functionCall(routerCalldata)`, `forceApprove(router, 0)`.
4. Compute `actualAmountIn = tokenInBefore - tokenInAfter` and `amountOut = baseAssetAfter - baseAssetBefore`.
5. Revert with `SlippageExceeded` if `amountOut < minAmountOut`.
6. Transfer `amountOut` of `BASE_ASSET` to `DESTINATION` and resync both watermarks.
7. Push fresh oracle prices, enforce the daily USD cap, emit `AbnormalSlippage` if applicable.
8. Emit `BuybackExecuted(tokenIn, actualAmountIn, amountOut, router, comptroller)`.

The reported `amountIn` in `BuybackExecuted` is the actual router consumption, not the caller-supplied parameter — the event reflects what the router actually pulled.

### `forwardBaseAsset(address comptroller, uint256 amount)`

ACM-restricted. Forwards a caller-specified `amount` of accumulated `BASE_ASSET` to `DESTINATION` without a swap. The `amount` parameter is exposed so the operator can partition multi-pool `BASE_ASSET` inflows and attribute each portion via a separate `BaseAssetForwarded(comptroller, amount)` event. No-op when `amount == 0`. Resyncs the `BASE_ASSET` watermark after the transfer.

### `setAllowedRouter(address router, bool allowed)`

Governance-only. Adds or removes a DEX router from the on-chain allowlist. Emits `RouterAllowlisted(router, allowed)`.

### `setDailyCapUsd(uint256 newCap)`

ACM-restricted. Updates the rolling 24h USD cap (1e18-scaled). Reverts with `ZeroValueNotAllowed` if `newCap` is zero — the cap is not a kill switch; revoke ACM permissions to fully disable buybacks. Emits `DailyCapUpdated(oldCap, newCap)`.

### `setSlippageEventUsd(uint256 newThreshold)`

ACM-restricted. Updates the absolute USD slippage-event threshold (1e18-scaled). Reverts with `ZeroValueNotAllowed` if `newThreshold` is zero. Emits `SlippageEventUsdUpdated(oldThreshold, newThreshold)`.

### `sweepToken(address token, address to, uint256 amount)`

Governance-only. Emergency token recovery. Also the canonical recovery path for tokens transferred directly to the contract outside the PSR flow, since on-chain accounting cannot distinguish donations from authenticated inflows. Resyncs `assetsReserves[token]` to the post-transfer balance. Emits `SweepToken(token, to, amount)`.

## Events

| Event                                                                 | When emitted                                                            |
| --------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `AssetsReceived(comptroller, asset, amount)`                          | PSR delivers tokens (non-zero delta)                                    |
| `BuybackExecuted(tokenIn, amountIn, amountOut, router, comptroller)`  | Successful DEX swap; `amountIn` is the on-chain router consumption      |
| `BaseAssetForwarded(comptroller, amount)`                             | `BASE_ASSET` forwarded to `DESTINATION` without swap                    |
| `RouterAllowlisted(router, allowed)`                                  | Router added or removed from allowlist                                  |
| `SweepToken(token, to, amount)`                                       | Emergency token sweep                                                   |
| `AbnormalSlippage(tokenIn, actualAmountIn, amountOut, usdIn, usdOut)` | Swap returned less USD value than input by more than `slippageEventUsd` |
| `DailyCapUpdated(oldCap, newCap)`                                     | `setDailyCapUsd` succeeded                                              |
| `SlippageEventUsdUpdated(oldThreshold, newThreshold)`                 | `setSlippageEventUsd` succeeded                                         |

## Errors

| Error                                              | Cause                                                                         |
| -------------------------------------------------- | ----------------------------------------------------------------------------- |
| `UnauthorizedCaller(caller)`                       | `updateAssetsState` called by anyone other than `PROTOCOL_SHARE_RESERVE`      |
| `RouterNotAllowed(router)`                         | `executeBuyback` with a router not on the allowlist                           |
| `InvalidTokenIn(tokenIn)`                          | `executeBuyback` with `tokenIn == BASE_ASSET`                                 |
| `InsufficientBalance(token, requested, available)` | `executeBuyback` or `forwardBaseAsset` requested more than the contract holds |
| `SlippageExceeded(expected, actual)`               | Swap output fell below `minAmountOut`                                         |
| `DeadlineExpired(deadline, blockTimestamp)`        | `executeBuyback` after the supplied deadline                                  |
| `DailyCapExceeded(attempted, cap)`                 | `executeBuyback` would push cumulative USD usage past `dailyCapUsd`           |
| `ZeroAddressNotAllowed`                            | Constructor / `setAllowedRouter` / `sweepToken` with zero address             |
| `ZeroValueNotAllowed`                              | Setters or `sweepToken` invoked with zero                                     |

## Destinations

The `(DESTINATION, BASE_ASSET)` pair is fixed per instance. Common destinations across deployments:

* **`VTreasury`** — accumulates protocol-revenue tokens (U, BTCB, ETH, USDT, USDC, XVS) bought back from non-Prime, non-RiskFund income.
* **`PrimeLiquidityProvider`** — accumulates Prime rewards in USDT and U; later distributed to Prime users according to per-token speeds configured via VIP. See [Prime tokens](/technical-reference/reference-technical-articles/prime).
* **`RiskFundV2`** — receives the risk-fund route's converted USDT on BNB Chain mainnet. Per-pool accounting was removed alongside the migration, and Shortfall auction starts and restarts are paused; use of the global raw balance is governance-controlled rather than an automatic auction workflow.
* **`XVSVaultTreasury`** — accumulates XVS that funds `XVSVault` rewards via VIP.

See [deployed contracts](/deployed-contracts/token-converters) for per-chain proxy addresses and their `(base asset, destination)` mapping.

## Operator Notes

* Conversions run on a defined schedule rather than on every inflow. The cron decides when to call `executeBuyback`; the contract has no internal trigger.
* The oracle is consulted **only** for the cap and slippage signal — not for swap pricing. Swap economics are determined by the off-chain-built router calldata.
* The daily cap bounds blast radius if the operator key is compromised. It is not intended to throttle normal operation.
* Routers must be added to the allowlist by governance before the cron can route through them. Removing a router via `setAllowedRouter(router, false)` immediately blocks future swaps through it.
* Donations to the contract (tokens sent directly, outside the PSR flow) are not distinguished from authenticated PSR inflows on-chain. Use `sweepToken` to recover them if needed.


# Two Kinks Interest Rate Curve

## Overview

The 2-Kink Interest Rate Curve introduces a new model to optimize interest rates and utilization across Venus protocol markets. By incorporating two separate "kinks", this model aims to provide more flexible control over market dynamics, enhancing both the predictability and efficiency of APYs (Annual Percentage Yields) for borrowers and suppliers.

This design helps reduce the volatility of APYs during periods of high demand, such as during launchpool events, and encourages greater participation by offering more predictable costs and returns.

## 2-Kink Interest Rate Model Architecture

The 2-Kink IR Curve is built into the Interest Rate Models used by Venus markets. It allows for two distinct kinks in the curve, separating the initial utilization phase from the high-utilization phase. This enables better control over borrow and supply rates as utilization increases.

The curve is defined by:

* **Kink**: A utilization threshold where the interest rate curve’s slope changes.
* **Multiplier**: The rate at which the interest rate increases before and after the kink, controlling the curve’s steepness.
* **Base Rate**: A predefined APY jump at the start of the curve or any kink, providing a step-up in interest rates as utilization increases.

By using two kinks and base rates, the protocol can more accurately balance supply and borrow demand, especially in markets with varying demand cycles like launchpools.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-47935c84d7f0ddc9a1690657ee42bef79073b7bf%2Ftwo_kinks_ir.svg?alt=media&amp;token=59646e14-8865-472c-88a2-6e1ee17303ef" alt="Two Kinks Interest Rate Model"><figcaption><p>Two Kinks Interest Rate Model</p></figcaption></figure>

## Benefits of 2-Kink IR Curve

* **More Predictable APYs**: By introducing a second kink, the interest rate curve allows for smoother transitions in APYs as market utilization changes.
* **Increased Participation**: Both borrowers and suppliers benefit from more predictable rates, encouraging greater market participation even during high-demand periods.
* **Optimized Market Efficiency**: By reducing sharp spikes in borrow APYs, the 2-Kink IR Curve improves the overall efficiency of Venus markets, ensuring better control over utilization rates and protocol reserves.

## Smart Contract Reference

* [Two Kinks Rate Model contract (for the Core pool on BNB Chain)](/technical-reference/reference-core-pool/interestratemodels/twokinksinterestratemodel)
* [Two Kinks Rate Model contract (for the Isolated pools)](/technical-reference/reference-isolated-pools/interest-rate-models/two-kinks-interest-rate-model)
* [Jump Rate Model contract (used in the Core pool on BNB Chain)](/technical-reference/reference-core-pool/interestratemodels/jumpmodel)
* [Jump Rate Model V2 contract (used in the Isolated pools)](/technical-reference/reference-isolated-pools/interest-rate-models/jump-rate-model-v2)


# VenusERC4626 Vaults

## Overview

Venus Protocol introduces **native ERC-4626 vaults**, bringing standardized, composable yield vaults to the Venus ecosystem. This integration represents a significant advancement in making Venus's yield-bearing markets more accessible and composable within the broader DeFi ecosystem.

### Key Benefits:

* **Full ERC-4626 Compliance** – Interoperable with DeFi primitives (DAOs, aggregators, etc.)
* **Native Venus Yield Integration** – Auto-compounding via vTokens
* **Gas-Optimized Architecture** – Beacon proxy pattern for efficient deployments, so all vaults share the same implementation contract.
* **Secure & Upgradeable** – Governance-controlled upgrades and reward management

## Understanding ERC-4626

ERC-4626 is a tokenized vault standard designed to unify how yield-bearing assets are deposited, managed, and withdrawn in DeFi protocols. It builds on the ERC-20 token standard and introduces a consistent interface for vaults that accept a specific asset (like USDC) and issue shares representing ownership in the vault.

The primary goal of ERC-4626 is **standardization**—allowing developers to integrate with vaults without needing to understand their internal mechanics. Functions like deposit, withdraw, mint, and redeem, follow predictable behaviors across all compliant contracts.

In essence, ERC-4626 makes it easier for users to earn yield on their assets and for protocols to plug into vaults in a reliable, composable way—enhancing both usability and interoperability across the DeFi ecosystem.

#### Reference: <https://eips.ethereum.org/EIPS/eip-4626>

## The implementation of the Venus ERC-4626 vaults consists of two core smart contracts:

#### **1. VenusERC4626Factory.sol - The factory contract for deploying standardized vaults**

* Deploys individual vaults for individual vTokens via **BeaconProxy**
* Ensures deterministic addresses using **CREATE2**
* Managed by Venus Governance

#### **2. VenusERC4626.sol - The vault logic implementing ERC-4626 functionality**

* ERC-4626-compliant mint, deposit, redeem, and withdraw functions
* Integrates with Venus **vToken** interest accrual
* Handles reward distribution (e.g., XVS)

## Architecture

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-8696ae4e675b49787ab7ea7e534341c123e4f79e%2Ferc4626-flow-diagram.svg?alt=media" alt="Flow of funds related to ERC4626 wrappers"><figcaption></figcaption></figure>

## **VenusERC4626Factory.sol: The Vault Factory**

### **Architecture Overview**

The factory contract implements a sophisticated deployment system using OpenZeppelin's upgradeability patterns:

```jsx
contract VenusERC4626Factory is AccessControlledV8, MaxLoopsLimitHelper {
    UpgradeableBeacon public beacon;
    mapping(address vToken => ERC4626Upgradeable vault) public createdVaults;

    function createERC4626(address vToken) external returns (ERC4626Upgradeable) {
        // Deployment logic...
    }
}
```

### **Core Components**

### **Beacon Proxy System**

* **UpgradeableBeacon**: Stores the current implementation address.
* **BeaconProxy**: Proxy delegates to beacon implementation.
* **CREATE2**: Deterministic deployment with fixed salt for beacon proxies.

#### Benefits:

* Single implementation contract shared by all vaults.
* Gas-efficient deployments.
* Centralized upgrade capability.

### **PoolRegistry Integration**

```jsx
function createERC4626(address vToken) external {
    // Validate vToken is registered in PoolRegistry
    if (vToken != poolRegistry.getVTokenForAsset(comptroller, underlying)) {
        revert VenusERC4626Factory__InvalidVToken();
    }
    // Proceed with deployment...
}
```

This ensures:

* Only legitimate Venus vTokens can create vaults.
* Proper asset/vToken pairing.
* Compliance with Venus's risk parameters.

### Key Features

* **Deterministic Deployment**: Uses a constant salt to enable deterministic address generation for ERC-4626 vault proxies.
* **Upgradeable Architecture**: Utilizes a beacon proxy pattern to support upgradeability of all deployed vaults via a single beacon.
* **Vault Tracking**: Maintains a mapping `createdVaults` of vTokens to their corresponding deployed ERC-4626 vaults.
* **Reward Routing**: Allows configuration of a centralized reward recipient for all vaults and supports liquidity mining incentives.
* **Permissioned Admin**: Restricts administrative operations (e.g., setting reward recipient, max loops) via Access Control Manager (ACM).

### Events

* **`CreateERC4626 (event)`**: Emitted when a new ERC-4626 vault is created for a vToken.
* **`RewardRecipientUpdated (event)`**: Emitted when the reward recipient address is updated.

### Constants

* **`SALT (bytes32)`**: Constant salt used for deterministic deployment of vaults.

### State variables

* **`beacon (UpgradeableBeacon)`**: Stores the address of the beacon contract holding the ERC-4626 vault implementation.
* **`poolRegistry (PoolRegistryInterface)`**: Reference to the Venus Pool Registry contract.
* **`rewardRecipient (address)`**: Address designated to receive liquidity mining rewards.
* **`createdVaults (mapping(address => ERC4626Upgradeable))`**: Maps vTokens to their deployed ERC-4626 vault instances.

### Functions

* **`initialize()`**: Initializes the factory with core configuration.
* **`createERC4626(address vToken)`**: Creates a new ERC-4626 vault for the specified vToken.
* **`computeVaultAddress(address vToken)`**: Returns the predicted address of a vault for a specific vToken.
* **`setRewardRecipient(address newRecipient)`**: Updates the address receiving reward distributions (ACM-restricted).
* **`setMaxLoopsLimit(uint256 loopsLimit)`**: Configures the maximum allowed loop iterations (ACM-restricted).

### **Security Considerations**

### **1. Access Controls**

* **Admin-Only Functions** (via **AccessControlledV8**):
  * **setRewardRecipient()**
  * **setMaxLoopsLimit()**
  * **upgradeBeacon()** (for emergency fixes)

### **2. Input Validation**

* **ensureNonzeroAddress()** prevents invalid configurations.

### **Attack Surface Mitigation**

| **Threat Vector**  | **Mitigation Strategy**   |
| ------------------ | ------------------------- |
| Reentrancy         | `nonReentrant` modifiers  |
| Invalid vTokens    | PoolRegistry validation   |
| Governance attacks | ACM with timelocks        |
| Upgrade risks      | Beacon ownership controls |

***

## VenusERC4626.sol: The Vault Implementation

### Core Architecture

The **VenusERC4626** contract serves as an ERC-4626 compliant wrapper around Venus's yield-bearing vTokens. It inherits from multiple OpenZeppelin and Venus-specific base contracts to provide a secure, feature-rich implementation:

```jsx
contract VenusERC4626 is
    ERC4626Upgradeable,
    AccessControlledV8,
    MaxLoopsLimitHelper,
    ReentrancyGuardUpgradeable {
    // Implementation...
}
```

### Key Features

* **ERC-4626 Compliant**: Fully compliant with the ERC-4626 Tokenized Vault standard, enabling integrations with yield aggregators and frontends.
* **vToken Wrapping**: Provides tokenized access to underlying vTokens with proportional interest accrual.
* **Dual-Stage Initialization**: Separates base contract setup and access/reward configuration for modular deployment.
* **Reward Claiming**: Allows vaults to claim accrued rewards and direct them to a predefined recipient.
* **Failsafes and Admin Tools**: Includes recovery mechanisms, such as `sweepToken`, and loop control for security and operational safety.

### Key Inherited Functionality:

* **ERC4626Upgradeable**: Uses OpenZeppelin's 4626 contract as the base implementation of the ERC-4626 standard.
* **AccessControlledV8**: Venus role-based access control system.
* **ReentrancyGuardUpgradeable**: Protection against reentrancy attacks.
* **MaxLoopsLimitHelper**: Prevents gas exhaustion in loops.

### Events

* **`ClaimRewards (event)`**: Emitted when rewards are claimed and distributed.
* **`RewardRecipientUpdated (event)`**: Emitted when the reward recipient address is updated.
* **`SweepToken (event)`**: Emitted when ERC-20 tokens are swept from the contract.

### State variables

* **`vToken (VToken)`**: The underlying Venus vToken being wrapped.
* **`comptroller (IComptroller)`**: The Comptroller contract associated with the vToken.
* **`rewardRecipient (address)`**: Address designated to receive reward tokens.

### Functions

#### Core ERC-4626 Functions

* **`deposit(uint256 assets, address receiver)`**: Deposits assets and mints shares to the receiver.
* **`mint(uint256 shares, address receiver)`**: Mints exact shares by depositing required assets.
  * Note - It can mint slightly fewer shares than requested, because vToken.mint rounds down.
* **`withdraw(uint256 assets, address receiver, address owner)`**: Withdraws exact assets and burns shares from the owner.
  * Note - Receiver can receive slightly more assets than requested, because VToken.redeemUnderlying rounds up
* **`redeem(uint256 shares, address receiver, address owner)`**: Redeems exact shares and transfers assets to the receiver.
* **`totalAssets()`**: Returns the total underlying assets held by the vault.

#### Initialization

* **`initialize(address vToken_)`**: Initializes the vault with the target vToken (first-stage init).
* **`initialize2(address accessControlManager_, address rewardRecipient_, uint256 loopsLimit_)`**: Second-stage initialization with access control, reward recipient, and loop limit.

#### Reward Management

* **`claimRewards()`**: Claims all available rewards and sends them to the recipient.
* **`setRewardRecipient(address newRecipient)`**: Updates the reward recipient address (ACM-restricted).

#### Admin Functions

* **`sweepToken(IERC20Upgradeable token)`**: Allows the owner to recover any ERC-20 tokens that were mistakenly sent to the vault.
* **`setMaxLoopsLimit(uint256 loopsLimit)`**: Configures the maximum loop iterations (ACM-restricted).

### Error Codes

* **`VenusError(uint256 errorCode)`**: Generic error returned from Venus protocol operations.
* **`ERC4626__ZeroAmount(string operation)`**: Thrown when a zero amount is provided during an operation.
* **`ERC4626__DepositMoreThanMax()`**: Error triggered when a deposit exceeds the maximum limit.
* **`ERC4626__MintMoreThanMax()`**: Error triggered when a mint exceeds the maximum limit.
* **`ERC4626__WithdrawMoreThanMax()`**: Error triggered when a withdrawal exceeds the maximum limit.
* **`ERC4626__RedeemMoreThanMax()`**: Error triggered when a redeem exceeds the maximum limit.

### **Deposit Flow**

```jsx
function deposit(uint256 assets, address receiver)
    public
    override
    nonReentrant
    returns (uint256)
{
    // Input validation
    if (assets == 0) revert ERC4626__ZeroAmount("deposit");
    if (assets > maxDeposit(receiver)) revert ERC4626__DepositMoreThanMax();

    // Process deposit
    uint256 shares = previewDeposit(assets);
    _deposit(_msgSender(), receiver, assets, shares);

    return shares;
}
```

1. Validates input parameters.
2. Calculates shares to mint.
3. Transfers assets from the user.
4. Mints vTokens via Venus Protocol.
5. Issues vault shares to the receiver.

#### Example

**Scenario**: Alice deposits 100 USDC.

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-2261626ea64112e3ade401b47a5ffccd41c75f2e%2Ferc4626-deposit.svg?alt=media" alt="Flow of funds related to ERC4626 wrapper"><figcaption></figcaption></figure>

#### Result:

* Alice gets 100 vault shares.
* Vault holds 100 vUSDC (earning yield).

### **Withdrawal Flow**

```jsx
function withdraw(uint256 assets, address receiver, address owner)
    public
    override
    nonReentrant
    returns (uint256)
{
    // Input validation
    if (assets == 0) revert ERC4626__ZeroAmount("withdraw");
    if (assets > maxWithdraw(owner)) revert ERC4626__WithdrawMoreThanMax();

    // Process withdrawal
    uint256 shares = previewWithdraw(assets);
    beforeWithdraw(assets); // Redeems from Venus
    _withdraw(_msgSender(), receiver, owner, assets, shares);

    return shares;
}
```

1. Validates input parameters.
2. Calculates shares to burn.
3. Redeems underlying assets from Venus.
4. Transfers assets to the receiver.
5. Burns vault shares.

#### Example

**Scenario**: Alice withdraws 50 USDC (after interest accrual).

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-b6b505ee530c507b064d3bedbdbfd24e4ee06a59%2Ferc4626-withdraw.svg?alt=media" alt="Flow of funds related to ERC4626 wrappers"><figcaption></figcaption></figure>

#### Result

Alice receives 50 USDC.

* Vault burns shares adjusted for interest (e.g., 48.54 shares at a 1.03 exchange rate).

### **Security Features**

1. **Reentrancy Protection**:
   * All state-changing functions use the **nonReentrant** modifier.
   * Critical Venus operations (mint/redeem) are atomic.
2. **Input Validation**:
   * Zero-address checks are performed via **ensureNonzeroAddress**.
   * Zero-amount validation for all operations.
   * Explicit error codes for Venus operations.
3. **Access Control**:
   * Sensitive functions are protected by Venus's ACM.
   * The reward recipient can only be changed by authorized accounts.

***


# XVS Bridge

This article describes the stable [`token-bridge` v2.7.0 architecture](https://github.com/VenusProtocol/token-bridge/tree/v2.7.0). It uses LayerZero OFT V2 contracts on the LayerZero V1 messaging stack (`uint16` endpoint IDs, named chain IDs in the ABI); it is not the LayerZero V2 OApp API.

The current Venus interface includes these mainnets:

* [Arbitrum](https://arbitrum.io)
* [Base](https://www.base.org/)
* [BNB Chain](https://www.bnbchain.org)
* [Ethereum](https://ethereum.org)
* [opBNB](https://opbnb.bnbchain.org)
* [Optimism](https://app.optimism.io)
* [Unichain](https://www.unichain.org/)
* [ZKsync](https://zksync.io/)

## Supported Transfer Paths

The interface can construct transfers between its supported networks, but a path is usable only while both bridge contracts, their trusted remotes, limits, oracle, LayerZero configuration, and destination mint capacity permit it. Recheck the selected path immediately before signing.

The system consists of [XVSBridgeAdmin](https://github.com/VenusProtocol/token-bridge/blob/v2.7.0/contracts/Bridge/XVSBridgeAdmin.sol), [XVSProxyOFTSrc](https://github.com/VenusProtocol/token-bridge/blob/v2.7.0/contracts/Bridge/XVSProxyOFTSrc.sol), [XVSProxyOFTDest](https://github.com/VenusProtocol/token-bridge/blob/v2.7.0/contracts/Bridge/XVSProxyOFTDest.sol), and the destination-chain [XVS](https://github.com/VenusProtocol/token-bridge/blob/v2.7.0/contracts/Bridge/token/XVS.sol) token.

***The functionality of the bridge relies on*** [***LayerZero***](https://layerzero.network) ***for the seamless transfer of XVS tokens across different networks. Consequently, the security and integrity of the token on each network are subject to potential vulnerabilities inherent in the bridging mechanism. It is essential to note that these risks are a general characteristic of integrating with network bridges and do not stem from any particular weaknesses within the token implementation.***

## 1. Getting Started

To start using the XVS Cross-Chain Bridge, follow these steps:

### 1.1. Approving XVS Tokens

Before transferring XVS tokens, you need to approve the `Bridge` contract on the BNB chain to spend XVS tokens on your behalf. Follow these steps:

1. Call the `approve` function of the XVS token contract with the following parameters:
   * `_spender`: Address of the `Bridge` contract on the BNB chain.
   * `_amount`: Amount of XVS tokens to approve for transfer.

### 1.2. Estimating Transaction Fees

To estimate the transaction fees required to send XVS tokens to the destination chain, call `estimateSendFee` on the local bridge with the following parameters:

* `_dstChainId`: Destination [LayerZero V1 endpoint ID](https://docs.layerzero.network/v1/deployments/deployed-contracts), not the EVM chain ID (for example, Ethereum V1 endpoint ID `101`)
* `_toAddress`: Receiver address on the destination chain
* `_amount`: Amount of XVS tokens you want to send, defined with 18 decimals
* `_useZro`: `false` (indicating that you are not paying in LayerZero ZRO tokens)
* `_adapterParams`: Versioned LayerZero V1 adapter parameters with destination gas at or above the bridge's current `minDstGasLookup` requirement. Do not copy a historical fixed gas value.

## 2. Transferring Tokens

The actual token transfer is performed using the `sendFrom` function of the `Bridge` contract. Follow these steps:

### 2.1. Sending Tokens

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-c6979acfabf1e4029bd8915aa4835bb505bc8d56%2FXVS_bridge_BNB_to_dest.svg?alt=media&amp;token=10546e16-b5b4-47c4-842d-d7ce4ecda4cb" alt="Assets bridging from src chain to dest chain"><figcaption></figcaption></figure>

1. Call the `sendFrom` function of the local bridge with the following parameters:
   * `_from`: Your address on the source network
   * `_dstChainId`: Destination LayerZero V1 endpoint ID (for example, Ethereum `101`)
   * `_toAddress`: The address on the destination chain where you want to receive the XVS tokens
   * `_amount`: Amount of XVS tokens you want to send, defined with 18 decimals
   * `_callParams`: `[refundAddress, zroPaymentAddress, adapterParams]`
     * `RefundGasAddress`: Address where you want to receive a refund for excessive gas sent. It can be the sender's address.
     * `ZROaddress`: `0x0000000000000000000000000000000000000000` (indicating that you are not paying in ZRO tokens)
     * `adapterParams`: The same current LayerZero V1 adapter parameters used for the fee estimate.

## 3. Receiving Tokens on the Destination Chain

When BNB-chain XVS leaves BNB Chain it is locked by XVSProxyOFTSrc; XVSProxyOFTDest mints destination-chain XVS to the receiver. For destination-to-destination transfers, the source destination token is burned and the target destination token is minted.

## 4. Transferring Tokens Back to the BNB chain

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-3ab1fcbadf0225856027c76ef798a68d033558da%2FXVS_bridge_dest_to_BNB.svg?alt=media&amp;token=823447e9-3fcb-4718-a08f-5d12d2134ffb" alt="Assets bridging from dest chain to src chain"><figcaption></figcaption></figure>

To transfer XVS tokens back to the BNB chain, follow a similar process as mentioned in the earlier send section. You don't need to approve the `Bridge` contract on the destination chain to spend XVS tokens on your behalf. The tokens will be burned on the destination chain on your behalf and unlocked and transferred to the receiver's address on the BNB chain.

To transfer XVS tokens between destination chains, such as from Ethereum to opBNB, the process remains similar to the earlier send section. You don't need to approve the `Bridge` contract on the destination chain to spend XVS tokens on your behalf. The tokens will be burned on the one destination chain (Ethereum) and minted on the other destination chain (opBNB).

## 5. Monitoring Transaction Status

After initiating a token transfer, you should wait for the transaction to confirm. This process may take a few minutes. Once the transaction confirms, you will receive the bridged XVS tokens on the destination chain. You can use [LayerZero scan](https://layerzeroscan.com) to monitor your cross-chain transactions.

## 6. Security and Risks

<figure><img src="https://1811016925-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FSfxZmzf9N5T5Npv859XY%2Fuploads%2Fgit-blob-206b91d638f4304d64c78ce2c59416505481b838%2FXVS_bridge_risks.svg?alt=media&amp;token=462a9ea7-781a-467c-8bf2-b1bfd3e2ba63" alt="Risks and security"><figcaption></figcaption></figure>

### 6.1. Ownership Transfer

* Use the `transferOwnership` method in the `XVSBridgeAdmin` contract to transfer ownership of the admin contract.
* Use the `transferBridgeOwnership` method to transfer ownership of the `Bridge` contract from one contract to another.
* Ownership control is crucial in case of emergencies or security issues.
* The initial-Guardian rollout statement is obsolete. On August 27, 2026, each mainnet XVSBridgeAdmin was owned by that network's Normal Timelock, and each local bridge was owned by its XVSBridgeAdmin. Verify both `owner()` values before preparing an administrative action.

### 6.2. Pause and Resume

* The `Bridge` includes a pause and unpause mechanism. Use the `pause` method to halt the contract's functionality and `unpause` to resume.
* Pausing is a security measure to prevent further transactions during emergencies or potential attacks.
* XVS Cross-chain messages that attempt to mint or release tokens to the receiver can be received by the destination `Bridge` contract. These messages will fail, but they can be retried once the destination `Bridge` Contract has been unpaused.

### 6.3. Limit the Amount of XVS Transfers

* Example: Limit the maximum XVS transfer to USD 1,000 in one transaction and USD 100,000 in one day. These limits can be adjusted using VIPs.

### 6.4. Message Finality

* The bridge contract does not expose a user-set transfer delay. Delivery time and confirmation requirements come from the selected LayerZero V1 path, both networks, adapter gas, and current messaging-library configuration.

### 6.5. Token Controller Contract

* The [TokenController](https://github.com/VenusProtocol/token-bridge/blob/v2.7.0/contracts/Bridge/token/TokenController.sol) within destination-chain XVS can blacklist addresses, preventing them from transferring or receiving XVS. Its protected functions use the local AccessControlManager.

### 6.6. Cap on Token Minting

* Destination-chain XVS already enforces `minterToCap` and `minterToMintedAmount` for each authorized bridge minter. This is a live second bound in addition to bridge send/receive limits.

### 6.7. Mitigation Plans for Mint Cap Reached

* A mint-cap failure creates an operational incident; it does not guarantee that governance will raise the cap. Any cap change and message retry require an independently reviewed authorized action.

### 6.8. Bridge Model

* XVSProxyOFTDest is authorized through the destination XVS token's AccessControlManager to mint and burn. Its bridge owner is XVSBridgeAdmin; current administrative caller permissions must be read from the local ACM.
* The stable mainnet artifacts contain one bridge per network. TokenController supports migrating minter accounting if governance replaces a destination bridge, but that recovery capability is not evidence of multiple simultaneously supported user routes on one network.

### 6.9. Bridge Replacement Scenario

A bridge replacement is a governance and incident-response procedure, not one atomic contract call. A reviewed plan can include:

1. **Pause the Bridge:**
   * Temporarily pause the `Bridge` contract to prevent further transactions.
2. **Token Evaluation:**
   * Evaluate whether pausing the XVS token is necessary during the replacement process.
3. **Migrate MinterToMintedAmount:**
   * Move the recorded `minterToMintedAmount` value to a replacement bridge with `migrateMinterTokens`. This updates accounting only; it does not transfer users' ERC-20 balances.
4. **Reduce the old minter cap:**
   * Set the affected bridge's `minterToCap` to its remaining accounted minted amount or complete a valid migration before reducing it further; `setMintCap` rejects a cap below `minterToMintedAmount`.

BNB-side custody must be reconciled separately with the source bridge's `fallbackWithdraw` and `fallbackDeposit` accounting and the exact failed-message state. None of these steps automatically proves global XVS supply conservation; the complete cross-chain supply and in-flight messages must be reconciled before enabling the replacement path.

### 6.10. Messaging Dependency

* Delivery depends on each path's current LayerZero V1 endpoint, messaging library, oracle/relayer configuration, trusted remote, and destination execution gas. The old blanket statement that every route uses one fixed default relayer is not a durable configuration guarantee. Inspect the live endpoint configuration for the exact route when diagnosing downtime.

## 7. Contract Details

Here, we provide more details about the key contracts used in the XVS Cross-chain Bridge:

### 7.1. XVSBridgeAdmin

* [XVSBridgeAdmin](https://github.com/VenusProtocol/token-bridge/blob/v2.7.0/contracts/Bridge/XVSBridgeAdmin.sol) is the admin contract for the bridge, ensuring proper setup.
* It contains a `functionRegistry` mapping for function signatures, allowing the contract to call corresponding methods in destination contracts after ensuring access control permissions.
* Ownership transfers for `XVSBridgeAdmin` and `Bridge` can be executed via the `transferOwnership` and `transferBridgeOwnership` methods respectively.

### 7.2. XVSProxySrc

* [XVSProxyOFTSrc](https://github.com/VenusProtocol/token-bridge/blob/v2.7.0/contracts/Bridge/XVSProxyOFTSrc.sol) extends the LayerZero `BaseOFTV2` contract and includes custom logic for token transfers.
* It overrides the `_debitFrom` and `_creditTo` functions, checking transaction limits and user eligibility.
* It enforces transaction limits, tracks 24-hour window limits, and allows whitelisting of users.
* `XVSProxySrc` can be paused and resumed in emergencies.

### 7.3. XVSProxyDest

* [XVSProxyOFTDest](https://github.com/VenusProtocol/token-bridge/blob/v2.7.0/contracts/Bridge/XVSProxyOFTDest.sol) shares the base bridge controls but uses burn/mint token accounting.
* Transaction limits are enforced primarily for outbound amounts only in the source chain.
* It overrides the `debitFrom` function to include custom logic for checking transaction limits in USD and performs an external call to the XVS token contract to burn tokens from the sender.
* It overrides the `creditTo` function to trigger an external call to the XVS token contract to mint tokens for the receiver.
* When sending tokens from the destination chain to the BNB chain, it burns user tokens, with the burning logic residing in the XVS token contract.
* When receiving tokens from the BNB chain (to the Destination Chain), it mints tokens for the receiver, with the minting logic residing in the XVS token contract.

### 7.4. XVS Token

* The [XVS](https://github.com/VenusProtocol/token-bridge/blob/v2.7.0/contracts/Bridge/token/XVS.sol) token contract is deployed on destination chains and is used by XVSProxyOFTDest.
* The XVS token follows the ERC-20 standard and extends the [TokenController](https://github.com/VenusProtocol/token-bridge/blob/v2.7.0/contracts/Bridge/token/TokenController.sol) ownable contract.
* It is responsible for setting minting limits for the minter (in this case, the remote `Bridge` contract).
* When receiving transactions and tokens from the source chain's `Bridge` contract, an external call is made to mint tokens for the receiver.
* When sending tokens to the source chain's `Bridge` contract, an external call is made from the `Bridge` contract to burn tokens from the sender.
* Offers a blacklisting feature to prevent certain users from receiving, transferring and bridging XVS tokens.
* [AccessControlManager v2.15.0](https://github.com/VenusProtocol/governance-contracts/blob/v2.15.0/contracts/Governance/AccessControlManager.sol) integration is used for mint caps, blacklisting, pause controls, and bridge administration. The authorized caller must be checked from current permissions; it is not implied by the labels “VIP” or “Guardian.”

## 8. Additional Features

In addition to the core functionality, the XVS Cross-chain Bridge includes additional features to enhance its capabilities:

### 8.1. Oracle Integration

* The contract incorporates an oracle integration through the `ResilientOracleInterface`. It allows the contract to fetch price data for the token using the `getPrice` function.

### 8.2. Whitelist Mechanism

* The contract implements a whitelist mechanism to skip checks on transaction limits for whitelisted addresses. The `whitelist` mapping is used to track whitelisted addresses. The `setWhitelist` function allows adding or removing addresses from the whitelist.

### 8.3. Transaction Limits

* The bridge enforces mutable single-send, daily-send, single-receive, and daily-receive USD limits per LayerZero path. Read `chainIdToMaxSingleTransactionLimit`, `chainIdToMaxDailyLimit`, `chainIdToMaxSingleReceiveTransactionLimit`, and `chainIdToMaxDailyReceiveLimit` on the source contract selected by the interface.
* The historical static matrices have been removed: they omitted Unichain and could become unsafe after a governance update. Also read the current rolling-window counters and the destination XVS mint capacity before signing.

### 8.4. Pause and Unpause Mechanism

* The contract incorporates a pause and unpause mechanism using the `Pausable` library. The `pause` and `unpause` functions can be used to halt and resume the contract's functionality, respectively.

## 9. Possible Failures of Bridging XVS Tokens

### 9.1. Sending XVS tokens from the source chain

* The oracle temporarily fails due to reasons including being paused by the owner, incorrect address configuration, or price validation failures.
* The transfer amount exceeds the single or daily sending transaction limit.
* The transfer amount is too small, becoming zero after removing dust.
* The sender is blacklisted by the XVS token.
* The destination bridge is not configured as a trusted remote.

### 9.2. Receiving XVS tokens on the destination chain

* The oracle temporarily fails due to reasons including being paused by the owner, incorrect address configuration, or price validation failures.
* The transfer amount exceeds the single or daily receiving transaction limit.
* The recipient is blacklisted by the XVS token.
* The minting cap on the destination bridge is exceeded.

### Retry Mechanism for Failed Transactions

In the event of a failed transaction, inspect the destination event and the stable [`retryMessage` implementation](https://github.com/VenusProtocol/token-bridge/blob/v2.7.0/contracts/Bridge/BaseXVSProxyOFT.sol). Retrying is a state-changing action: confirm that the stored payload hash still exists, the trusted path is unchanged, and the underlying failure has been resolved.

1. **Identify the Failed Transaction:**
   * Use [LayerZero scan](https://layerzeroscan.com) to identify the failed transaction within the target network by providing the transaction hash from the source network where the transaction was initiated.
2. **Examine the MessageFailed Log:**
   * Examine the emitted `MessageFailed` event. Its source chain ID, source path, nonce, payload, and reason identify the stored message and retry parameters.
3. **Extract Function Parameters:**
   * From the `MessageFailed` log, extract the following essential function parameters:
     * `_srcChainId`
     * `_srcAddress`
     * `_nonce`
     * `_payload`
4. **Construct a RetryMessage:**
   * In the event of a transaction failure on the BNB chain, invoke the `retryMessage` function of the `XVSProxyOFTSrc` contract on the BNB chain. Use the parameters extracted from the `MessageFailed` log for this operation. Conversely, if the transaction fails on any network other than BNB chain, invoke the `retryMessage` function of the `XVSProxyOFTDest` contract on that network.


# Core Pool

“Core Pool” is a product label, not a single contract architecture. Select the reference for the deployment you are integrating with.

## Architecture map

| Deployment                                                                 | Comptroller architecture                            | Market architecture                                                              | Source repository                                                 |
| -------------------------------------------------------------------------- | --------------------------------------------------- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| BNB Chain Core Pool                                                        | Unitroller proxy → Diamond → selector-routed facets | VBep20Delegator/VBep20Delegate for ERC-20 assets; standalone VBNB for native BNB | [venus-protocol](https://github.com/VenusProtocol/venus-protocol) |
| Ethereum, opBNB, Arbitrum, ZKsync, Optimism, Base, and Unichain Core Pools | BeaconProxy Comptroller                             | BeaconProxy VToken                                                               | [isolated-pools](https://github.com/VenusProtocol/isolated-pools) |

{% hint style="danger" %}
Do not use the BNB Core Pool ABI for a Core Pool on another network. Those deployments use the same architecture as the contracts historically documented under “Isolated Pools,” even when the pool is named Core Pool in the interface.
{% endhint %}

## Choose the correct reference

For the BNB Chain Core Pool, use:

* [BNB Core Comptroller](/technical-reference/reference-core-pool/comptroller)
* [BNB Core vTokens](/technical-reference/reference-core-pool/vtoken)
* [Diamond Comptroller architecture](/technical-reference/reference-technical-articles/diamond-comptroller)

For every other Core Pool, use:

* [BeaconProxy Comptroller](/technical-reference/reference-isolated-pools/comptroller/comptroller)
* [BeaconProxy VToken](/technical-reference/reference-isolated-pools/vtoken/vtoken)

The deployed address determines the runtime version. Resolve the proxy or beacon implementation at the block you are querying, then use an ABI generated from that implementation. Current market and Comptroller addresses are listed in [Deployed Markets](/deployed-contracts/markets).

## Generated API pages

Many child pages are generated from a repository source release. They describe that source contract, but they do not prove that every selector is available at every deployed address. For BNB Diamond functions, query the live selector map. For beacon-based deployments, resolve the live beacon implementation.


# Comptroller

This section applies only to the BNB Chain Core Pool Comptroller. The entry point is the Unitroller at [`0xfD36E2c2a6789Db23113685031d7F16329158384`](https://bscscan.com/address/0xfD36E2c2a6789Db23113685031d7F16329158384).

Calls follow this path:

```
caller → Unitroller → Diamond implementation → facet selected by msg.sig
```

State remains in the Unitroller context throughout the chained delegate calls. Integrations call the Unitroller address, not the Diamond or facet addresses.

## Live version map

At BNB Chain block `118,363,255`, the Unitroller reported:

| Component              | Address                                      |
| ---------------------- | -------------------------------------------- |
| Unitroller entry point | `0xfD36E2c2a6789Db23113685031d7F16329158384` |
| Diamond implementation | `0xA66B2b5D50ce68A125bBad6B2265b637868c6E66` |
| ComptrollerLens        | `0xd5DEb631cB6c6a667e926a482aadc95a471b120c` |
| Admin                  | `0x939bD8d64c0A9583A7Dcea9933f7b21697ab6396` |

The live Diamond selector map contained five facets:

| Facet          | Address                                      |
| -------------- | -------------------------------------------- |
| MarketFacet    | `0x21f8E1471b153f49BE1d645A008E4a57434eEd23` |
| PolicyFacet    | `0x8930B02c69EDd37464B50991680D306Bb9B8FDBD` |
| RewardFacet    | `0x9e0CCD70b5E0030472D5013bbBd37B6E868d416f` |
| SetterFacet    | `0xbc4885e5A27050E321d094503597aC6734AB1871` |
| FlashLoanFacet | `0xAC54A4D148690b7FDA22B1D29c4439aCBF668fb2` |

These implementations were installed by [VIP-640](https://venus.io/governance/proposal/640?chainId=56). The matching stable repository release is [venus-protocol v10.3.0](https://github.com/VenusProtocol/venus-protocol/releases/tag/v10.3.0), which uses `ComptrollerV19Storage`.

{% hint style="warning" %}
The addresses and selector routing are upgradeable. Before encoding a call, read `comptrollerImplementation`, `facetAddress(bytes4)`, or `facets()` through the Unitroller. A function present in a repository or generated page is not necessarily installed at the live proxy.
{% endhint %}

## ABI selection

Use the Unitroller admin ABI for proxy administration and the ABI of the facet currently assigned to each application selector. [DiamondConsolidated](/technical-reference/reference-core-pool/comptroller/diamond/diamond-consolidated) is a build-time ABI aggregate; it is not deployed and does not prove live selector availability.

The [Diamond reference](/technical-reference/reference-core-pool/comptroller/diamond) documents selector inspection and the current facet families. `FacetBase` supplies shared storage and checks to facets; it is not a separate call target.

Core Pools on other networks use a different Comptroller architecture. Start with the [Core Pool architecture map](/technical-reference/reference-core-pool) before choosing an ABI.


# ComptrollerLens

## ComptrollerLens Contract

The ComptrollerLens contract has functions to get the number of tokens that can be seized through liquidation, hypothetical account liquidity and shortfall of an account.

## Solidity API

```solidity
struct AccountLiquidityLocalVars {
  uint256 sumCollateral;
  uint256 sumBorrowPlusEffects;
  uint256 vTokenBalance;
  uint256 borrowBalance;
  uint256 exchangeRateMantissa;
  uint256 oraclePriceMantissa;
  struct ExponentialNoError.Exp weightedFactor;
  struct ExponentialNoError.Exp exchangeRate;
  struct ExponentialNoError.Exp oraclePrice;
  struct ExponentialNoError.Exp tokensToDenom;
}
```

#### liquidateCalculateSeizeTokens

Computes the number of collateral tokens to be seized in a liquidation event

```solidity
function liquidateCalculateSeizeTokens(address comptroller, address vTokenBorrowed, address vTokenCollateral, uint256 actualRepayAmount) external view returns (uint256, uint256)
```

**Parameters**

| Name              | Type    | Description                                                       |
| ----------------- | ------- | ----------------------------------------------------------------- |
| comptroller       | address | Address of comptroller                                            |
| vTokenBorrowed    | address | Address of the borrowed vToken                                    |
| vTokenCollateral  | address | Address of collateral for the borrow                              |
| actualRepayAmount | uint256 | Repayment amount i.e amount to be repaid of total borrowed amount |

**Return Values**

| Name | Type    | Description                                |
| ---- | ------- | ------------------------------------------ |
| \[0] | uint256 | A tuple of error code, and tokens to seize |
| \[1] | uint256 |                                            |

***

#### liquidateCalculateSeizeTokens

Computes the number of collateral tokens to be seized in a liquidation event

```solidity
function liquidateCalculateSeizeTokens(address borrower, address comptroller, address vTokenBorrowed, address vTokenCollateral, uint256 actualRepayAmount) external view returns (uint256, uint256)
```

**Parameters**

| Name              | Type    | Description                                                       |
| ----------------- | ------- | ----------------------------------------------------------------- |
| borrower          | address | Address of borrower whose collateral is being seized              |
| comptroller       | address | Address of comptroller                                            |
| vTokenBorrowed    | address | Address of the borrowed vToken                                    |
| vTokenCollateral  | address | Address of collateral for the borrow                              |
| actualRepayAmount | uint256 | Repayment amount i.e amount to be repaid of total borrowed amount |

**Return Values**

| Name | Type    | Description                                |
| ---- | ------- | ------------------------------------------ |
| \[0] | uint256 | A tuple of error code, and tokens to seize |
| \[1] | uint256 |                                            |

***

#### liquidateVAICalculateSeizeTokens

Computes the number of VAI tokens to be seized in a liquidation event

```solidity
function liquidateVAICalculateSeizeTokens(address comptroller, address vTokenCollateral, uint256 actualRepayAmount) external view returns (uint256, uint256)
```

**Parameters**

| Name              | Type    | Description                                                           |
| ----------------- | ------- | --------------------------------------------------------------------- |
| comptroller       | address | Address of comptroller                                                |
| vTokenCollateral  | address | Address of collateral for vToken                                      |
| actualRepayAmount | uint256 | Repayment amount i.e amount to be repaid of the total borrowed amount |

**Return Values**

| Name | Type    | Description                                |
| ---- | ------- | ------------------------------------------ |
| \[0] | uint256 | A tuple of error code, and tokens to seize |
| \[1] | uint256 |                                            |

***

#### getHypotheticalAccountLiquidity

Computes the hypothetical liquidity and shortfall of an account given a hypothetical borrow A snapshot of the account is taken and the total borrow amount of the account is calculated

```solidity
function getHypotheticalAccountLiquidity(address comptroller, address account, contract VToken vTokenModify, uint256 redeemTokens, uint256 borrowAmount, enum WeightFunction weightingStrategy) external view returns (uint256, uint256, uint256)
```

**Parameters**

| Name              | Type                | Description                                                                                                                                                                |
| ----------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| comptroller       | address             | Address of comptroller                                                                                                                                                     |
| account           | address             | Address of the borrowed vToken                                                                                                                                             |
| vTokenModify      | contract VToken     | Address of collateral for vToken                                                                                                                                           |
| redeemTokens      | uint256             | Number of vTokens being redeemed                                                                                                                                           |
| borrowAmount      | uint256             | Amount borrowed                                                                                                                                                            |
| weightingStrategy | enum WeightFunction | The weighting strategy to use: - `WeightFunction.USE_COLLATERAL_FACTOR` to use collateral factor - `WeightFunction.USE_LIQUIDATION_THRESHOLD` to use liquidation threshold |

**Return Values**

| Name | Type    | Description                                             |
| ---- | ------- | ------------------------------------------------------- |
| \[0] | uint256 | Returns a tuple of error code, liquidity, and shortfall |
| \[1] | uint256 |                                                         |
| \[2] | uint256 |                                                         |

***


# Diamond

The Diamond is the BNB Core Pool Unitroller's implementation. It routes application selectors to independently upgradeable facets while preserving state in the Unitroller.

* [Diamond selector management and inspection](/technical-reference/reference-core-pool/comptroller/diamond/diamond)
* [DiamondConsolidated build-time ABI](/technical-reference/reference-core-pool/comptroller/diamond/diamond-consolidated)
* [Facet version map and API pages](/technical-reference/reference-core-pool/comptroller/diamond/facets)

The live component addresses and snapshot block are recorded in the [BNB Core Pool Comptroller version map](/technical-reference/reference-core-pool/comptroller).

{% hint style="warning" %}
Call application functions through the Unitroller. Calling a facet directly does not operate on the Unitroller's storage and is not a supported integration path.
{% endhint %}


# Diamond

The Diamond is the selector router used as the BNB Core Pool Unitroller implementation. The current stable source inherits `ComptrollerV19Storage`.

{% hint style="warning" %}
Use these functions through the Unitroller at `0xfD36E2c2a6789Db23113685031d7F16329158384`. The Diamond implementation address and selector map are upgradeable; verify the [live version map](/technical-reference/reference-core-pool/comptroller) before use.
{% endhint %}

Source: [Diamond.sol in venus-protocol v10.3.0](https://github.com/VenusProtocol/venus-protocol/blob/v10.3.0/contracts/Comptroller/Diamond/Diamond.sol).

## Solidity API

### `_become`

Accepts the pending Unitroller implementation role. Only the Unitroller admin can initiate this transition.

```solidity
function _become(Unitroller unitroller) public
```

### `diamondCut`

Adds, replaces, or removes selector assignments. Only the Unitroller admin can call it in the delegated Unitroller context.

```solidity
function diamondCut(IDiamondCut.FacetCut[] diamondCut_) public
```

### `facetFunctionSelectors`

Returns the selectors currently assigned to a facet address.

```solidity
function facetFunctionSelectors(address facet) external view returns (bytes4[])
```

### `facetPosition`

Returns a facet's position in the internal facet-address array.

```solidity
function facetPosition(address facet) external view returns (uint256)
```

### `facetAddresses`

Returns every facet address currently registered in the Diamond.

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

### `facetAddress`

Returns the current facet address and selector position for a function selector.

```solidity
function facetAddress(bytes4 functionSelector)
    external
    view
    returns (ComptrollerV19Storage.FacetAddressAndPosition)
```

### `facets`

Returns every registered facet with its assigned selectors.

```solidity
function facets() external view returns (Diamond.Facet[])
```


# DiamondConsolidated

`DiamondConsolidated` inherits the Diamond and all five facet contracts to generate a convenient combined ABI and TypeChain type.

It is a build-time artifact and is too large to deploy. It has no runtime address, and it must not be treated as the Unitroller implementation or as evidence that every inherited selector is currently installed.

Use the combined ABI to decode calls only after checking the selector against `facetAddress(bytes4)` or `facets()` on the live Unitroller. See the [BNB Core Pool Comptroller version map](/technical-reference/reference-core-pool/comptroller).

Source: [DiamondConsolidated.sol in venus-protocol v10.3.0](https://github.com/VenusProtocol/venus-protocol/blob/v10.3.0/contracts/Comptroller/Diamond/DiamondConsolidated.sol).


# Facets

Facets contain the BNB Core Pool Comptroller's application logic. They are reached through chained delegate calls from the Unitroller and Diamond.

At BNB Chain block `118,363,255`, the selector map installed by [VIP-640](https://venus.io/governance/proposal/640?chainId=56) was:

| Facet          | Responsibility                                                  | Live address                                 | Reference                                                                                  |
| -------------- | --------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------ |
| MarketFacet    | Market membership, account delegation, liquidity and pool views | `0x21f8E1471b153f49BE1d645A008E4a57434eEd23` | [API](/technical-reference/reference-core-pool/comptroller/diamond/facets/market-facet)    |
| PolicyFacet    | vToken policy hooks and liquidity checks                        | `0x8930B02c69EDd37464B50991680D306Bb9B8FDBD` | [API](/technical-reference/reference-core-pool/comptroller/diamond/facets/policy-facet)    |
| RewardFacet    | XVS accrual, claim and seizure flows                            | `0x9e0CCD70b5E0030472D5013bbBd37B6E868d416f` | [API](/technical-reference/reference-core-pool/comptroller/diamond/facets/reward-facet)    |
| SetterFacet    | Risk, oracle, pause, cap, pool and feature configuration        | `0xbc4885e5A27050E321d094503597aC6734AB1871` | [API](/technical-reference/reference-core-pool/comptroller/diamond/facets/setter-facet)    |
| FlashLoanFacet | Multi-market flash-loan execution                               | `0xAC54A4D148690b7FDA22B1D29c4439aCBF668fb2` | [API](/technical-reference/reference-core-pool/comptroller/diamond/facets/flashloan-facet) |

`FacetBase` is inherited shared logic and storage, not a separate runtime facet. It defines `ComptrollerV19Storage` access helpers such as admin-only, admin-or-guardian, and signature-specific Access Control Manager checks.

{% hint style="danger" %}
Always call facet selectors through the Unitroller. Never send an application call directly to a facet address. Query `facetAddress(bytes4)` before relying on a selector, because governance can replace or remove individual assignments.
{% endhint %}


# FacetBase (inherited API)

## FacetBase

`FacetBase` is shared inherited code for the BNB Core Pool facets, not a separately deployed runtime facet. The public getters below are reached through whichever installed facet exposes their selectors. Resolve each selector through the live Diamond before calling it.

## Solidity API

#### venusInitialIndex

The initial Venus index for a market

```solidity
uint224 venusInitialIndex
```

***

#### corePoolId

Returns the Core Pool identifier used by the shared facet storage.

```solidity
function corePoolId() external pure returns (uint96)
```

***

#### actionPaused

Checks if a certain action is paused on a market

```solidity
function actionPaused(address market, enum Action action) public view returns (bool)
```

**Parameters**

| Name   | Type        | Description    |
| ------ | ----------- | -------------- |
| market | address     | vToken address |
| action | enum Action | Action id      |

***

#### getXVSAddress

Returns the XVS address

```solidity
function getXVSAddress() external view returns (address)
```

**Return Values**

| Name | Type    | Description              |
| ---- | ------- | ------------------------ |
| \[0] | address | The address of XVS token |

***

#### getPoolMarketIndex

Returns the unique market index for the given poolId and vToken pair

```solidity
function getPoolMarketIndex(uint96 poolId, address vToken) public pure returns (PoolMarketId)
```

**Parameters**

| Name   | Type    | Description                        |
| ------ | ------- | ---------------------------------- |
| poolId | uint96  | The ID of the pool                 |
| vToken | address | The address of the market (vToken) |

**Return Values**

| Name | Type         | Description                                                                       |
| ---- | ------------ | --------------------------------------------------------------------------------- |
| \[0] | PoolMarketId | PoolMarketId The `bytes32` key that uniquely represents the (poolId, vToken) pair |

***


# MarketFacet

## MarketFacet

This facet contract contains functions regarding markets

{% hint style="warning" %}
BNB Core Pool only. At block `118,363,255`, this facet was routed to `0x21f8E1471b153f49BE1d645A008E4a57434eEd23`. Call its selectors through the Unitroller and query the live selector map before use. The [v10.3.0 source](https://github.com/VenusProtocol/venus-protocol/blob/v10.3.0/contracts/Comptroller/Diamond/facets/MarketFacet.sol) includes newer pool, delegation, and protection-mode behavior that may not be fully represented in this generated page.
{% endhint %}

## Solidity API

#### isComptroller

Indicator that this is a Comptroller contract (for inspection)

```solidity
function isComptroller() public pure returns (bool)
```

***

#### getAssetsIn

Returns the vToken markets an account has entered in the Core Pool

```solidity
function getAssetsIn(address account) external view returns (contract VToken[])
```

**Parameters**

| Name    | Type    | Description                         |
| ------- | ------- | ----------------------------------- |
| account | address | The address of the account to query |

**Return Values**

| Name | Type               | Description                                                      |
| ---- | ------------------ | ---------------------------------------------------------------- |
| \[0] | contract VToken\[] | assets A dynamic array of vToken markets the account has entered |

***

#### getAllMarkets

Return all of the markets

```solidity
function getAllMarkets() external view returns (contract VToken[])
```

**Return Values**

| Name | Type               | Description                  |
| ---- | ------------------ | ---------------------------- |
| \[0] | contract VToken\[] | The list of market addresses |

***

#### liquidateCalculateSeizeTokens

Calculate number of tokens of collateral asset to seize given an underlying amount

```solidity
function liquidateCalculateSeizeTokens(address vTokenBorrowed, address vTokenCollateral, uint256 actualRepayAmount) external view returns (uint256, uint256)
```

**Parameters**

| Name              | Type    | Description                                                                     |
| ----------------- | ------- | ------------------------------------------------------------------------------- |
| vTokenBorrowed    | address | The address of the borrowed vToken                                              |
| vTokenCollateral  | address | The address of the collateral vToken                                            |
| actualRepayAmount | uint256 | The amount of vTokenBorrowed underlying to convert into vTokenCollateral tokens |

**Return Values**

| Name | Type    | Description                                                                  |
| ---- | ------- | ---------------------------------------------------------------------------- |
| \[0] | uint256 | (errorCode, number of vTokenCollateral tokens to be seized in a liquidation) |
| \[1] | uint256 |                                                                              |

***

#### liquidateCalculateSeizeTokens

Calculate number of tokens of collateral asset to seize given an underlying amount

```solidity
function liquidateCalculateSeizeTokens(address borrower, address vTokenBorrowed, address vTokenCollateral, uint256 actualRepayAmount) external view returns (uint256, uint256)
```

**Parameters**

| Name              | Type    | Description                                                                     |
| ----------------- | ------- | ------------------------------------------------------------------------------- |
| borrower          | address | Address of borrower whose collateral is being seized                            |
| vTokenBorrowed    | address | The address of the borrowed vToken                                              |
| vTokenCollateral  | address | The address of the collateral vToken                                            |
| actualRepayAmount | uint256 | The amount of vTokenBorrowed underlying to convert into vTokenCollateral tokens |

**Return Values**

| Name | Type    | Description                                                                  |
| ---- | ------- | ---------------------------------------------------------------------------- |
| \[0] | uint256 | (errorCode, number of vTokenCollateral tokens to be seized in a liquidation) |
| \[1] | uint256 |                                                                              |

***

#### liquidateVAICalculateSeizeTokens

Calculate number of tokens of collateral asset to seize given an underlying amount

```solidity
function liquidateVAICalculateSeizeTokens(address vTokenCollateral, uint256 actualRepayAmount) external view returns (uint256, uint256)
```

**Parameters**

| Name              | Type    | Description                                                                     |
| ----------------- | ------- | ------------------------------------------------------------------------------- |
| vTokenCollateral  | address | The address of the collateral vToken                                            |
| actualRepayAmount | uint256 | The amount of vTokenBorrowed underlying to convert into vTokenCollateral tokens |

**Return Values**

| Name | Type    | Description                                                                  |
| ---- | ------- | ---------------------------------------------------------------------------- |
| \[0] | uint256 | (errorCode, number of vTokenCollateral tokens to be seized in a liquidation) |
| \[1] | uint256 |                                                                              |

***

#### checkMembership

Returns whether the given account has entered the specified vToken market in the Core Pool

```solidity
function checkMembership(address account, contract VToken vToken) external view returns (bool)
```

**Parameters**

| Name    | Type            | Description                         |
| ------- | --------------- | ----------------------------------- |
| account | address         | The address of the account to check |
| vToken  | contract VToken | The vToken to check                 |

**Return Values**

| Name | Type | Description                                          |
| ---- | ---- | ---------------------------------------------------- |
| \[0] | bool | True if the account is in the asset, otherwise false |

***

#### isMarketListed

Checks whether the given vToken market is listed in the Core Pool (`poolId = 0`)

```solidity
function isMarketListed(contract VToken vToken) external view returns (bool)
```

**Parameters**

| Name   | Type            | Description                               |
| ------ | --------------- | ----------------------------------------- |
| vToken | contract VToken | The vToken Address of the market to check |

**Return Values**

| Name | Type | Description                                                              |
| ---- | ---- | ------------------------------------------------------------------------ |
| \[0] | bool | listed True if the (Core Pool, vToken) market is listed, otherwise false |

***

#### enterMarkets

Add assets to be included in account liquidity calculation

```solidity
function enterMarkets(address[] vTokens) external returns (uint256[])
```

**Parameters**

| Name    | Type       | Description                                               |
| ------- | ---------- | --------------------------------------------------------- |
| vTokens | address\[] | The list of addresses of the vToken markets to be enabled |

**Return Values**

| Name | Type       | Description                                                         |
| ---- | ---------- | ------------------------------------------------------------------- |
| \[0] | uint256\[] | Success indicator for whether each corresponding market was entered |

***

#### enterMarketBehalf

Add assets to be included in account liquidity calculation

```solidity
function enterMarketBehalf(address onBehalf, address vToken) external returns (uint256)
```

**Parameters**

| Name     | Type    | Description                                                |
| -------- | ------- | ---------------------------------------------------------- |
| onBehalf | address | The address of the account entering the market             |
| vToken   | address | The address of the vToken market to enable for the account |

**Return Values**

| Name | Type    | Description                                                     |
| ---- | ------- | --------------------------------------------------------------- |
| \[0] | uint256 | uint256 indicating the result (0 = success, non-zero = failure) |

**❌ Errors**

* NotAnApprovedDelegate thrown if `msg.sender` is not the account itself or an approved delegate

***

#### unlistMarket

Unlists the given vToken market from the Core Pool (`poolId = 0`) by setting `isListed` to false

```solidity
function unlistMarket(address market) external returns (uint256)
```

**Parameters**

| Name   | Type    | Description                                  |
| ------ | ------- | -------------------------------------------- |
| market | address | The address of the market (vToken) to unlist |

**Return Values**

| Name | Type    | Description                                                         |
| ---- | ------- | ------------------------------------------------------------------- |
| \[0] | uint256 | uint256 0=success, otherwise a failure (See enum Error for details) |

***

#### exitMarket

Removes asset from sender's account liquidity calculation

```solidity
function exitMarket(address vTokenAddress) external returns (uint256)
```

**Parameters**

| Name          | Type    | Description                            |
| ------------- | ------- | -------------------------------------- |
| vTokenAddress | address | The address of the asset to be removed |

**Return Values**

| Name | Type    | Description                                               |
| ---- | ------- | --------------------------------------------------------- |
| \[0] | uint256 | Whether or not the account successfully exited the market |

***

#### supportMarket

Alias to \_supportMarket to support the Isolated Lending Comptroller Interface

```solidity
function supportMarket(contract VToken vToken) external returns (uint256)
```

**Parameters**

| Name   | Type            | Description                               |
| ------ | --------------- | ----------------------------------------- |
| vToken | contract VToken | The address of the market (token) to list |

**Return Values**

| Name | Type    | Description                                                          |
| ---- | ------- | -------------------------------------------------------------------- |
| \[0] | uint256 | uint256 0=success, otherwise a failure. (See enum Error for details) |

***

#### \_supportMarket

Adds the given vToken market to the Core Pool (`poolId = 0`) and marks it as listed

```solidity
function _supportMarket(contract VToken vToken) external returns (uint256)
```

**Parameters**

| Name   | Type            | Description                                               |
| ------ | --------------- | --------------------------------------------------------- |
| vToken | contract VToken | The address of the vToken market to list in the Core Pool |

**Return Values**

| Name | Type    | Description                                                          |
| ---- | ------- | -------------------------------------------------------------------- |
| \[0] | uint256 | uint256 0=success, otherwise a failure. (See enum Error for details) |

***

#### updateDelegate

Grants or revokes the borrowing or redeeming delegate rights to / from an account If allowed, the delegate will be able to borrow funds on behalf of the sender Upon a delegated borrow, the delegate will receive the funds, and the borrower will see the debt on their account Upon a delegated redeem, the delegate will receive the redeemed amount and the approver will see a deduction in his vToken balance

```solidity
function updateDelegate(address delegate, bool approved) external
```

**Parameters**

| Name     | Type    | Description                                                                 |
| -------- | ------- | --------------------------------------------------------------------------- |
| delegate | address | The address to update the rights for                                        |
| approved | bool    | Whether to grant (true) or revoke (false) the borrowing or redeeming rights |

***

#### enterPool

Allows a user to switch to a new pool (e.g., e-mode).

```solidity
function enterPool(uint96 poolId) external
```

**Parameters**

| Name   | Type   | Description                                 |
| ------ | ------ | ------------------------------------------- |
| poolId | uint96 | The ID of the pool the user wants to enter. |

**📅 Events**

* PoolSelected Emitted after a successful pool switch.

**❌ Errors**

* PoolDoesNotExist The specified pool ID does not exist.
* AlreadyInSelectedPool The user is already in the target pool.
* IncompatibleBorrowedAssets The user's current borrows are incompatible with the new pool.
* LiquidityCheckFailed The user's liquidity is insufficient after switching pools.
* InactivePool The user is trying to enter inactive pool.

***

#### createPool

Creates a new pool with the given label.

```solidity
function createPool(string label) external returns (uint96)
```

**Parameters**

| Name  | Type   | Description                            |
| ----- | ------ | -------------------------------------- |
| label | string | name for the pool (must be non-empty). |

**Return Values**

| Name | Type   | Description                                                         |
| ---- | ------ | ------------------------------------------------------------------- |
| \[0] | uint96 | poolId The incremental unique identifier of the newly created pool. |

**📅 Events**

* PoolCreated Emitted after successfully creating a new pool.

**❌ Errors**

* EmptyPoolLabel Reverts if the provided label is an empty string.

***

#### addPoolMarkets

Batch initializes market entries with basic config.

```solidity
function addPoolMarkets(uint96[] poolIds, address[] vTokens) external
```

**Parameters**

| Name    | Type       | Description                         |
| ------- | ---------- | ----------------------------------- |
| poolIds | uint96\[]  | Array of pool IDs.                  |
| vTokens | address\[] | Array of market (vToken) addresses. |

**📅 Events**

* PoolMarketInitialized Emitted after successfully initializing a market in a pool.

**❌ Errors**

* ArrayLengthMismatch Reverts if `poolIds` and `vTokens` arrays have different lengths or if the length is zero.
* InvalidOperationForCorePool Reverts when attempting to call pool-specific methods on the Core Pool.
* PoolDoesNotExist Reverts if the target pool ID does not exist.
* MarketNotListedInCorePool Reverts if the market is not listed in the core pool.
* MarketAlreadyListed Reverts if the given market is already listed in the specified pool.
* InactivePool Reverts if attempted to add markets to an inactive pool.

***

#### removePoolMarket

Removes a market (vToken) from the specified pool.

```solidity
function removePoolMarket(uint96 poolId, address vToken) external
```

**Parameters**

| Name   | Type    | Description                                                 |
| ------ | ------- | ----------------------------------------------------------- |
| poolId | uint96  | The ID of the pool from which the market should be removed. |
| vToken | address | The address of the market token to remove.                  |

**📅 Events**

* PoolMarketRemoved Emitted after a market is successfully removed from a pool.

**❌ Errors**

* InvalidOperationForCorePool Reverts if called on the Core Pool.
* PoolMarketNotFound Reverts if the market is not listed in the pool.

***

#### getCollateralFactor

Get the core pool collateral factor for a vToken

```solidity
function getCollateralFactor(address vToken) external view returns (uint256)
```

**Parameters**

| Name   | Type    | Description                                                |
| ------ | ------- | ---------------------------------------------------------- |
| vToken | address | The address of the vToken to get the collateral factor for |

**Return Values**

| Name | Type    | Description                                          |
| ---- | ------- | ---------------------------------------------------- |
| \[0] | uint256 | The collateral factor for the vToken, scaled by 1e18 |

***

#### getLiquidationThreshold

Get the core pool liquidation threshold for a vToken

```solidity
function getLiquidationThreshold(address vToken) external view returns (uint256)
```

**Parameters**

| Name   | Type    | Description                                                    |
| ------ | ------- | -------------------------------------------------------------- |
| vToken | address | The address of the vToken to get the liquidation threshold for |

**Return Values**

| Name | Type    | Description                                              |
| ---- | ------- | -------------------------------------------------------- |
| \[0] | uint256 | The liquidation threshold for the vToken, scaled by 1e18 |

***

#### getLiquidationIncentive

Get the core pool liquidation Incentive for a vToken

```solidity
function getLiquidationIncentive(address vToken) external view returns (uint256)
```

**Parameters**

| Name   | Type    | Description                                                    |
| ------ | ------- | -------------------------------------------------------------- |
| vToken | address | The address of the vToken to get the liquidation Incentive for |

**Return Values**

| Name | Type    | Description                                                                   |
| ---- | ------- | ----------------------------------------------------------------------------- |
| \[0] | uint256 | liquidationIncentive The liquidation incentive for the vToken, scaled by 1e18 |

***

#### getEffectiveLtvFactor

Get the effective loan-to-value factor (collateral factor or liquidation threshold) for a given account and market.

```solidity
function getEffectiveLtvFactor(address account, address vToken, enum WeightFunction weightingStrategy) external view returns (uint256)
```

**Parameters**

| Name              | Type                | Description                                                                                                                                                                |
| ----------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| account           | address             | The account whose pool is used to determine the market's risk parameters.                                                                                                  |
| vToken            | address             | The address of the vToken market.                                                                                                                                          |
| weightingStrategy | enum WeightFunction | The weighting strategy to use: - `WeightFunction.USE_COLLATERAL_FACTOR` to use collateral factor - `WeightFunction.USE_LIQUIDATION_THRESHOLD` to use liquidation threshold |

**Return Values**

| Name | Type    | Description                                                |
| ---- | ------- | ---------------------------------------------------------- |
| \[0] | uint256 | factor The effective loan-to-value factor, scaled by 1e18. |

***

#### getEffectiveLiquidationIncentive

Get the Effective Liquidation Incentive for a given account and market

```solidity
function getEffectiveLiquidationIncentive(address account, address vToken) external view returns (uint256)
```

**Parameters**

| Name    | Type    | Description                                                              |
| ------- | ------- | ------------------------------------------------------------------------ |
| account | address | The account whose pool is used to determine the market's risk parameters |
| vToken  | address | The address of the vToken market                                         |

**Return Values**

| Name | Type    | Description                                              |
| ---- | ------- | -------------------------------------------------------- |
| \[0] | uint256 | The liquidation Incentive for the vToken, scaled by 1e18 |

***

#### getPoolVTokens

Returns the full list of vTokens for a given pool ID.

```solidity
function getPoolVTokens(uint96 poolId) external view returns (address[])
```

**Parameters**

| Name   | Type   | Description                                         |
| ------ | ------ | --------------------------------------------------- |
| poolId | uint96 | The ID of the pool whose vTokens are being queried. |

**Return Values**

| Name | Type       | Description                                            |
| ---- | ---------- | ------------------------------------------------------ |
| \[0] | address\[] | An array of vToken addresses associated with the pool. |

**❌ Errors**

* PoolDoesNotExist Reverts if the given pool ID do not exist.
* InvalidOperationForCorePool Reverts if called on the Core Pool.

***

#### markets

Returns the market configuration for a vToken in the core pool (poolId = 0).

```solidity
function markets(address vToken) external view returns (bool isListed, uint256 collateralFactorMantissa, bool isVenus, uint256 liquidationThresholdMantissa, uint256 liquidationIncentiveMantissa, uint96 marketPoolId, bool isBorrowAllowed)
```

**Parameters**

| Name   | Type    | Description                                                            |
| ------ | ------- | ---------------------------------------------------------------------- |
| vToken | address | The address of the vToken whose market configuration is to be fetched. |

**Return Values**

| Name                         | Type    | Description                                                         |
| ---------------------------- | ------- | ------------------------------------------------------------------- |
| isListed                     | bool    | Whether the market is listed and enabled.                           |
| collateralFactorMantissa     | uint256 | The maximum borrowable percentage of collateral, in mantissa.       |
| isVenus                      | bool    | Whether this market is eligible for VENUS rewards.                  |
| liquidationThresholdMantissa | uint256 | The threshold at which liquidation is triggered, in mantissa.       |
| liquidationIncentiveMantissa | uint256 | The max liquidation incentive allowed for this market, in mantissa. |
| marketPoolId                 | uint96  | The pool ID this market belongs to.                                 |
| isBorrowAllowed              | bool    | Whether borrowing is allowed in this market.                        |

***

#### poolMarkets

Returns the market configuration for a vToken from \_poolMarkets.

```solidity
function poolMarkets(uint96 poolId, address vToken) public view returns (bool isListed, uint256 collateralFactorMantissa, bool isVenus, uint256 liquidationThresholdMantissa, uint256 liquidationIncentiveMantissa, uint96 marketPoolId, bool isBorrowAllowed)
```

**Parameters**

| Name   | Type    | Description                                                            |
| ------ | ------- | ---------------------------------------------------------------------- |
| poolId | uint96  | The ID of the pool whose market configuration is being queried.        |
| vToken | address | The address of the vToken whose market configuration is to be fetched. |

**Return Values**

| Name                         | Type    | Description                                                     |
| ---------------------------- | ------- | --------------------------------------------------------------- |
| isListed                     | bool    | Whether the market is listed and enabled.                       |
| collateralFactorMantissa     | uint256 | The maximum borrowable percentage of collateral, in mantissa.   |
| isVenus                      | bool    | Whether this market is eligible for XVS rewards.                |
| liquidationThresholdMantissa | uint256 | The threshold at which liquidation is triggered, in mantissa.   |
| liquidationIncentiveMantissa | uint256 | The liquidation incentive allowed for this market, in mantissa. |
| marketPoolId                 | uint96  | The pool ID this market belongs to.                             |
| isBorrowAllowed              | bool    | Whether borrowing is allowed in this market.                    |

**❌ Errors**

* PoolDoesNotExist Reverts if the given pool ID do not exist.

***

#### hasValidPoolBorrows

Returns true if the user can switch to the given target pool, i.e., all markets they have borrowed from are also borrowable in the target pool.

```solidity
function hasValidPoolBorrows(address account, uint96 targetPoolId) public view returns (bool)
```

**Parameters**

| Name         | Type    | Description                                         |
| ------------ | ------- | --------------------------------------------------- |
| account      | address | The address of the user attempting to switch pools. |
| targetPoolId | uint96  | The pool ID the user wants to switch into.          |

**Return Values**

| Name | Type | Description                                          |
| ---- | ---- | ---------------------------------------------------- |
| \[0] | bool | bool True if the switch is allowed, otherwise False. |

***


# PolicyFacet

## PolicyFacet

This facet contract contains all the external pre-hook functions related to vToken

{% hint style="warning" %}
BNB Core Pool only. At block `118,363,255`, this facet was routed to `0x8930B02c69EDd37464B50991680D306Bb9B8FDBD`. vTokens call policy selectors through the Unitroller. Verify the live selector assignment and use the [v10.3.0 source](https://github.com/VenusProtocol/venus-protocol/blob/v10.3.0/contracts/Comptroller/Diamond/facets/PolicyFacet.sol) for the exact pool, liquidity, Prime, and protection-mode behavior.
{% endhint %}

## Solidity API

#### mintAllowed

Checks if the account should be allowed to mint tokens in the given market

```solidity
function mintAllowed(address vToken, address minter, uint256 mintAmount) external returns (uint256)
```

**Parameters**

| Name       | Type    | Description                                                                  |
| ---------- | ------- | ---------------------------------------------------------------------------- |
| vToken     | address | The market to verify the mint against                                        |
| minter     | address | The account which would get the minted tokens                                |
| mintAmount | uint256 | The amount of underlying being supplied to the market in exchange for tokens |

**Return Values**

| Name | Type    | Description                                                                          |
| ---- | ------- | ------------------------------------------------------------------------------------ |
| \[0] | uint256 | 0 if the mint is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) |

***

#### mintVerify

Validates mint, accrues interest and updates score in prime. Reverts on rejection. May emit logs.

```solidity
function mintVerify(address vToken, address minter, uint256 actualMintAmount, uint256 mintTokens) external
```

**Parameters**

| Name             | Type    | Description                                     |
| ---------------- | ------- | ----------------------------------------------- |
| vToken           | address | Asset being minted                              |
| minter           | address | The address minting the tokens                  |
| actualMintAmount | uint256 | The amount of the underlying asset being minted |
| mintTokens       | uint256 | The number of tokens being minted               |

***

#### redeemAllowed

Checks if the account should be allowed to redeem tokens in the given market

```solidity
function redeemAllowed(address vToken, address redeemer, uint256 redeemTokens) external returns (uint256)
```

**Parameters**

| Name         | Type    | Description                                                              |
| ------------ | ------- | ------------------------------------------------------------------------ |
| vToken       | address | The market to verify the redeem against                                  |
| redeemer     | address | The account which would redeem the tokens                                |
| redeemTokens | uint256 | The number of vTokens to exchange for the underlying asset in the market |

**Return Values**

| Name | Type    | Description                                                                            |
| ---- | ------- | -------------------------------------------------------------------------------------- |
| \[0] | uint256 | 0 if the redeem is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) |

***

#### redeemVerify

Validates redeem, accrues interest and updates score in prime. Reverts on rejection. May emit logs.

```solidity
function redeemVerify(address vToken, address redeemer, uint256 redeemAmount, uint256 redeemTokens) external
```

**Parameters**

| Name         | Type    | Description                                       |
| ------------ | ------- | ------------------------------------------------- |
| vToken       | address | Asset being redeemed                              |
| redeemer     | address | The address redeeming the tokens                  |
| redeemAmount | uint256 | The amount of the underlying asset being redeemed |
| redeemTokens | uint256 | The number of tokens being redeemed               |

***

#### borrowAllowed

Checks if the account should be allowed to borrow the underlying asset of the given market

```solidity
function borrowAllowed(address vToken, address borrower, uint256 borrowAmount) external returns (uint256)
```

**Parameters**

| Name         | Type    | Description                                       |
| ------------ | ------- | ------------------------------------------------- |
| vToken       | address | The market to verify the borrow against           |
| borrower     | address | The account which would borrow the asset          |
| borrowAmount | uint256 | The amount of underlying the account would borrow |

**Return Values**

| Name | Type    | Description                                                                            |
| ---- | ------- | -------------------------------------------------------------------------------------- |
| \[0] | uint256 | 0 if the borrow is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) |

***

#### borrowVerify

Validates borrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.

```solidity
function borrowVerify(address vToken, address borrower, uint256 borrowAmount) external
```

**Parameters**

| Name         | Type    | Description                                            |
| ------------ | ------- | ------------------------------------------------------ |
| vToken       | address | Asset whose underlying is being borrowed               |
| borrower     | address | The address borrowing the underlying                   |
| borrowAmount | uint256 | The amount of the underlying asset requested to borrow |

***

#### repayBorrowAllowed

Checks if the account should be allowed to repay a borrow in the given market

```solidity
function repayBorrowAllowed(address vToken, address payer, address borrower, uint256 repayAmount) external returns (uint256)
```

**Parameters**

| Name        | Type    | Description                                                |
| ----------- | ------- | ---------------------------------------------------------- |
| vToken      | address | The market to verify the repay against                     |
| payer       | address | The account which would repay the asset                    |
| borrower    | address | The account which borrowed the asset                       |
| repayAmount | uint256 | The amount of the underlying asset the account would repay |

**Return Values**

| Name | Type    | Description                                                                           |
| ---- | ------- | ------------------------------------------------------------------------------------- |
| \[0] | uint256 | 0 if the repay is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) |

***

#### repayBorrowVerify

Validates repayBorrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.

```solidity
function repayBorrowVerify(address vToken, address payer, address borrower, uint256 actualRepayAmount, uint256 borrowerIndex) external
```

**Parameters**

| Name              | Type    | Description                           |
| ----------------- | ------- | ------------------------------------- |
| vToken            | address | Asset being repaid                    |
| payer             | address | The address repaying the borrow       |
| borrower          | address | The address of the borrower           |
| actualRepayAmount | uint256 | The amount of underlying being repaid |
| borrowerIndex     | uint256 |                                       |

***

#### liquidateBorrowAllowed

Checks if the liquidation should be allowed to occur

```solidity
function liquidateBorrowAllowed(address vTokenBorrowed, address vTokenCollateral, address liquidator, address borrower, uint256 repayAmount) external view returns (uint256)
```

**Parameters**

| Name             | Type    | Description                                                |
| ---------------- | ------- | ---------------------------------------------------------- |
| vTokenBorrowed   | address | Asset which was borrowed by the borrower                   |
| vTokenCollateral | address | Asset which was used as collateral and will be seized      |
| liquidator       | address | The address repaying the borrow and seizing the collateral |
| borrower         | address | The address of the borrower                                |
| repayAmount      | uint256 | The amount of underlying being repaid                      |

***

#### liquidateBorrowVerify

Validates liquidateBorrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.

```solidity
function liquidateBorrowVerify(address vTokenBorrowed, address vTokenCollateral, address liquidator, address borrower, uint256 actualRepayAmount, uint256 seizeTokens) external
```

**Parameters**

| Name              | Type    | Description                                                |
| ----------------- | ------- | ---------------------------------------------------------- |
| vTokenBorrowed    | address | Asset which was borrowed by the borrower                   |
| vTokenCollateral  | address | Asset which was used as collateral and will be seized      |
| liquidator        | address | The address repaying the borrow and seizing the collateral |
| borrower          | address | The address of the borrower                                |
| actualRepayAmount | uint256 | The amount of underlying being repaid                      |
| seizeTokens       | uint256 | The amount of collateral token that will be seized         |

***

#### seizeAllowed

Checks if the seizing of assets should be allowed to occur

```solidity
function seizeAllowed(address vTokenCollateral, address vTokenBorrowed, address liquidator, address borrower, uint256 seizeTokens) external returns (uint256)
```

**Parameters**

| Name             | Type    | Description                                                |
| ---------------- | ------- | ---------------------------------------------------------- |
| vTokenCollateral | address | Asset which was used as collateral and will be seized      |
| vTokenBorrowed   | address | Asset which was borrowed by the borrower                   |
| liquidator       | address | The address repaying the borrow and seizing the collateral |
| borrower         | address | The address of the borrower                                |
| seizeTokens      | uint256 | The number of collateral tokens to seize                   |

***

#### seizeVerify

Validates seize, accrues interest and updates score in prime. Reverts on rejection. May emit logs.

```solidity
function seizeVerify(address vTokenCollateral, address vTokenBorrowed, address liquidator, address borrower, uint256 seizeTokens) external
```

**Parameters**

| Name             | Type    | Description                                                |
| ---------------- | ------- | ---------------------------------------------------------- |
| vTokenCollateral | address | Asset which was used as collateral and will be seized      |
| vTokenBorrowed   | address | Asset which was borrowed by the borrower                   |
| liquidator       | address | The address repaying the borrow and seizing the collateral |
| borrower         | address | The address of the borrower                                |
| seizeTokens      | uint256 | The number of collateral tokens to seize                   |

***

#### transferAllowed

Checks if the account should be allowed to transfer tokens in the given market

```solidity
function transferAllowed(address vToken, address src, address dst, uint256 transferTokens) external returns (uint256)
```

**Parameters**

| Name           | Type    | Description                               |
| -------------- | ------- | ----------------------------------------- |
| vToken         | address | The market to verify the transfer against |
| src            | address | The account which sources the tokens      |
| dst            | address | The account which receives the tokens     |
| transferTokens | uint256 | The number of vTokens to transfer         |

**Return Values**

| Name | Type    | Description                                                                              |
| ---- | ------- | ---------------------------------------------------------------------------------------- |
| \[0] | uint256 | 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) |

***

#### transferVerify

Validates transfer, accrues interest and updates score in prime. Reverts on rejection. May emit logs.

```solidity
function transferVerify(address vToken, address src, address dst, uint256 transferTokens) external
```

**Parameters**

| Name           | Type    | Description                           |
| -------------- | ------- | ------------------------------------- |
| vToken         | address | Asset being transferred               |
| src            | address | The account which sources the tokens  |
| dst            | address | The account which receives the tokens |
| transferTokens | uint256 | The number of vTokens to transfer     |

***

#### getBorrowingPower

Alias to getAccountLiquidity to support the Isolated Lending Comptroller Interface

```solidity
function getBorrowingPower(address account) external view returns (uint256, uint256, uint256)
```

**Parameters**

| Name    | Type    | Description                   |
| ------- | ------- | ----------------------------- |
| account | address | The account get liquidity for |

**Return Values**

| Name | Type    | Description                                                                                                                                  |
| ---- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| \[0] | uint256 | (possible error code (semi-opaque), account liquidity in excess of collateral requirements, account shortfall below collateral requirements) |
| \[1] | uint256 |                                                                                                                                              |
| \[2] | uint256 |                                                                                                                                              |

***

#### getAccountLiquidity

Determine the current account liquidity wrt liquidation threshold requirements

```solidity
function getAccountLiquidity(address account) external view returns (uint256, uint256, uint256)
```

**Parameters**

| Name    | Type    | Description                   |
| ------- | ------- | ----------------------------- |
| account | address | The account get liquidity for |

**Return Values**

| Name | Type    | Description                                                                                                                                                        |
| ---- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| \[0] | uint256 | (possible error code (semi-opaque), account liquidity in excess of liquidation threshold requirements, account shortfall below liquidation threshold requirements) |
| \[1] | uint256 |                                                                                                                                                                    |
| \[2] | uint256 |                                                                                                                                                                    |

***

#### getHypotheticalAccountLiquidity

Determine what the account liquidity would be if the given amounts were redeemed/borrowed

```solidity
function getHypotheticalAccountLiquidity(address account, address vTokenModify, uint256 redeemTokens, uint256 borrowAmount) external view returns (uint256, uint256, uint256)
```

**Parameters**

| Name         | Type    | Description                                       |
| ------------ | ------- | ------------------------------------------------- |
| account      | address | The account to determine liquidity for            |
| vTokenModify | address | The market to hypothetically redeem/borrow in     |
| redeemTokens | uint256 | The number of tokens to hypothetically redeem     |
| borrowAmount | uint256 | The amount of underlying to hypothetically borrow |

**Return Values**

| Name | Type    | Description                                                                                                                                                            |
| ---- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| \[0] | uint256 | (possible error code (semi-opaque), hypothetical account liquidity in excess of collateral requirements, hypothetical account shortfall below collateral requirements) |
| \[1] | uint256 |                                                                                                                                                                        |
| \[2] | uint256 |                                                                                                                                                                        |

***

#### \_setVenusSpeeds

Set XVS speed for a single market

```solidity
function _setVenusSpeeds(contract VToken[] vTokens, uint256[] supplySpeeds, uint256[] borrowSpeeds) external
```

**Parameters**

| Name         | Type               | Description                          |
| ------------ | ------------------ | ------------------------------------ |
| vTokens      | contract VToken\[] | The market whose XVS speed to update |
| supplySpeeds | uint256\[]         | New XVS speed for supply             |
| borrowSpeeds | uint256\[]         | New XVS speed for borrow             |

***


# RewardFacet

## RewardFacet

This facet contract provides the external functions related to all claims and rewards of the protocol

{% hint style="warning" %}
BNB Core Pool only. At block `118,363,255`, this facet was routed to `0x9e0CCD70b5E0030472D5013bbBd37B6E868d416f`. Call its selectors through the Unitroller and check the live route. The [v10.3.0 source](https://github.com/VenusProtocol/venus-protocol/blob/v10.3.0/contracts/Comptroller/Diamond/facets/RewardFacet.sol) also exposes market-filtered `claimVenusAsCollateral` and `seizeVenus` overloads that this generated page may omit; the latter is permissioned through the Access Control Manager.
{% endhint %}

## Solidity API

#### claimVenus

Claim all the xvs accrued by holder in all markets and VAI

```solidity
function claimVenus(address holder) public
```

**Parameters**

| Name   | Type    | Description                  |
| ------ | ------- | ---------------------------- |
| holder | address | The address to claim XVS for |

***

#### claimVenus

Claim all the xvs accrued by holder in the specified markets

```solidity
function claimVenus(address holder, contract VToken[] vTokens) public
```

**Parameters**

| Name    | Type               | Description                         |
| ------- | ------------------ | ----------------------------------- |
| holder  | address            | The address to claim XVS for        |
| vTokens | contract VToken\[] | The list of markets to claim XVS in |

***

#### claimVenus

Claim all xvs accrued by the holders

```solidity
function claimVenus(address[] holders, contract VToken[] vTokens, bool borrowers, bool suppliers) public
```

**Parameters**

| Name      | Type               | Description                                     |
| --------- | ------------------ | ----------------------------------------------- |
| holders   | address\[]         | The addresses to claim XVS for                  |
| vTokens   | contract VToken\[] | The list of markets to claim XVS in             |
| borrowers | bool               | Whether or not to claim XVS earned by borrowing |
| suppliers | bool               | Whether or not to claim XVS earned by supplying |

***

#### claimVenusAsCollateral

Claim all the xvs accrued by holder in all markets, a shorthand for `claimVenus` with collateral set to `true`

```solidity
function claimVenusAsCollateral(address holder) external
```

**Parameters**

| Name   | Type    | Description                  |
| ------ | ------- | ---------------------------- |
| holder | address | The address to claim XVS for |

***

#### \_grantXVS

Transfer XVS to the recipient

```solidity
function _grantXVS(address recipient, uint256 amount) external
```

**Parameters**

| Name      | Type    | Description                                     |
| --------- | ------- | ----------------------------------------------- |
| recipient | address | The address of the recipient to transfer XVS to |
| amount    | uint256 | The amount of XVS to (possibly) transfer        |

***

#### seizeVenus

Seize XVS rewards allocated to holders

```solidity
function seizeVenus(address[] holders, address recipient) external returns (uint256)
```

**Parameters**

| Name      | Type       | Description                        |
| --------- | ---------- | ---------------------------------- |
| holders   | address\[] | Addresses of the XVS holders       |
| recipient | address    | Address of the XVS token recipient |

**Return Values**

| Name | Type    | Description                                                        |
| ---- | ------- | ------------------------------------------------------------------ |
| \[0] | uint256 | The total amount of XVS tokens seized and transferred to recipient |

***

#### claimVenus

Claim all xvs accrued by the holders

```solidity
function claimVenus(address[] holders, contract VToken[] vTokens, bool borrowers, bool suppliers, bool collateral) public
```

**Parameters**

| Name       | Type               | Description                                                                                       |
| ---------- | ------------------ | ------------------------------------------------------------------------------------------------- |
| holders    | address\[]         | The addresses to claim XVS for                                                                    |
| vTokens    | contract VToken\[] | The list of markets to claim XVS in                                                               |
| borrowers  | bool               | Whether or not to claim XVS earned by borrowing                                                   |
| suppliers  | bool               | Whether or not to claim XVS earned by supplying                                                   |
| collateral | bool               | Whether or not to use XVS earned as collateral, only takes effect when the holder has a shortfall |

***

#### getXVSVTokenAddress

Returns the XVS vToken address

```solidity
function getXVSVTokenAddress() external view returns (address)
```

**Return Values**

| Name | Type    | Description               |
| ---- | ------- | ------------------------- |
| \[0] | address | The address of XVS vToken |

***


# SetterFacet

## SetterFacet

This facet contract contains all the configurational setter functions

{% hint style="danger" %}
BNB Core Pool only. At block `118,363,255`, this facet was routed to `0xbc4885e5A27050E321d094503597aC6734AB1871`. Call its selectors through the Unitroller. Access is function-specific and can use admin-only, admin-or-guardian, or Access Control Manager checks; do not infer permission from this generated page. Verify the live selector and permission, then use the [v10.3.0 source](https://github.com/VenusProtocol/venus-protocol/blob/v10.3.0/contracts/Comptroller/Diamond/facets/SetterFacet.sol) for the exact ABI.
{% endhint %}

## Solidity API

#### setPriceOracle

Alias to \_setPriceOracle to support the Isolated Lending Comptroller Interface

```solidity
function setPriceOracle(contract ResilientOracleInterface newOracle) external returns (uint256)
```

**Parameters**

| Name      | Type                              | Description                 |
| --------- | --------------------------------- | --------------------------- |
| newOracle | contract ResilientOracleInterface | The new price oracle to set |

**Return Values**

| Name | Type    | Description                                                                |
| ---- | ------- | -------------------------------------------------------------------------- |
| \[0] | uint256 | uint256 0=success, otherwise a failure (see ErrorReporter.sol for details) |

***

#### \_setPriceOracle

Sets a new price oracle for the comptroller

```solidity
function _setPriceOracle(contract ResilientOracleInterface newOracle) external returns (uint256)
```

**Parameters**

| Name      | Type                              | Description                 |
| --------- | --------------------------------- | --------------------------- |
| newOracle | contract ResilientOracleInterface | The new price oracle to set |

**Return Values**

| Name | Type    | Description                                                                |
| ---- | ------- | -------------------------------------------------------------------------- |
| \[0] | uint256 | uint256 0=success, otherwise a failure (see ErrorReporter.sol for details) |

***

#### setCloseFactor

Alias to \_setCloseFactor to support the Isolated Lending Comptroller Interface

```solidity
function setCloseFactor(uint256 newCloseFactorMantissa) external returns (uint256)
```

**Parameters**

| Name                   | Type    | Description                      |
| ---------------------- | ------- | -------------------------------- |
| newCloseFactorMantissa | uint256 | New close factor, scaled by 1e18 |

**Return Values**

| Name | Type    | Description                              |
| ---- | ------- | ---------------------------------------- |
| \[0] | uint256 | uint256 0=success, otherwise will revert |

***

#### \_setCloseFactor

Sets the closeFactor used when liquidating borrows

```solidity
function _setCloseFactor(uint256 newCloseFactorMantissa) external returns (uint256)
```

**Parameters**

| Name                   | Type    | Description                      |
| ---------------------- | ------- | -------------------------------- |
| newCloseFactorMantissa | uint256 | New close factor, scaled by 1e18 |

**Return Values**

| Name | Type    | Description                              |
| ---- | ------- | ---------------------------------------- |
| \[0] | uint256 | uint256 0=success, otherwise will revert |

***

#### \_setAccessControl

Sets the address of the access control of this contract

```solidity
function _setAccessControl(address newAccessControlAddress) external returns (uint256)
```

**Parameters**

| Name                    | Type    | Description                        |
| ----------------------- | ------- | ---------------------------------- |
| newAccessControlAddress | address | New address for the access control |

**Return Values**

| Name | Type    | Description                              |
| ---- | ------- | ---------------------------------------- |
| \[0] | uint256 | uint256 0=success, otherwise will revert |

***

#### setCollateralFactor

Sets the collateral factor and liquidation threshold for a market in the Core Pool only.

```solidity
function setCollateralFactor(contract VToken vToken, uint256 newCollateralFactorMantissa, uint256 newLiquidationThresholdMantissa) external returns (uint256)
```

**Parameters**

| Name                            | Type            | Description                                   |
| ------------------------------- | --------------- | --------------------------------------------- |
| vToken                          | contract VToken | The market to set the factor on               |
| newCollateralFactorMantissa     | uint256         | The new collateral factor, scaled by 1e18     |
| newLiquidationThresholdMantissa | uint256         | The new liquidation threshold, scaled by 1e18 |

**Return Values**

| Name | Type    | Description                                                             |
| ---- | ------- | ----------------------------------------------------------------------- |
| \[0] | uint256 | uint256 0=success, otherwise a failure. (See ErrorReporter for details) |

***

#### setLiquidationIncentive

Sets the liquidation incentive for a market in the Core Pool only.

```solidity
function setLiquidationIncentive(address vToken, uint256 newLiquidationIncentiveMantissa) external returns (uint256)
```

**Parameters**

| Name                            | Type    | Description                                    |
| ------------------------------- | ------- | ---------------------------------------------- |
| vToken                          | address | The market to set the liquidationIncentive for |
| newLiquidationIncentiveMantissa | uint256 | New liquidationIncentive scaled by 1e18        |

**Return Values**

| Name | Type    | Description                                                             |
| ---- | ------- | ----------------------------------------------------------------------- |
| \[0] | uint256 | uint256 0=success, otherwise a failure. (See ErrorReporter for details) |

***

#### setCollateralFactor

Sets the collateral factor and liquidation threshold for a market in the specified pool.

```solidity
function setCollateralFactor(uint96 poolId, contract VToken vToken, uint256 newCollateralFactorMantissa, uint256 newLiquidationThresholdMantissa) external returns (uint256)
```

**Parameters**

| Name                            | Type            | Description                                   |
| ------------------------------- | --------------- | --------------------------------------------- |
| poolId                          | uint96          | The ID of the pool.                           |
| vToken                          | contract VToken | The market to set the factor on               |
| newCollateralFactorMantissa     | uint256         | The new collateral factor, scaled by 1e18     |
| newLiquidationThresholdMantissa | uint256         | The new liquidation threshold, scaled by 1e18 |

**Return Values**

| Name | Type    | Description                                                             |
| ---- | ------- | ----------------------------------------------------------------------- |
| \[0] | uint256 | uint256 0=success, otherwise a failure. (See ErrorReporter for details) |

***

#### setLiquidationIncentive

Sets the liquidation incentive for a market in the specified pool.

```solidity
function setLiquidationIncentive(uint96 poolId, address vToken, uint256 newLiquidationIncentiveMantissa) external returns (uint256)
```

**Parameters**

| Name                            | Type    | Description                                    |
| ------------------------------- | ------- | ---------------------------------------------- |
| poolId                          | uint96  | The ID of the pool.                            |
| vToken                          | address | The market to set the liquidationIncentive for |
| newLiquidationIncentiveMantissa | uint256 | New liquidationIncentive scaled by 1e18        |

**Return Values**

| Name | Type    | Description                                                             |
| ---- | ------- | ----------------------------------------------------------------------- |
| \[0] | uint256 | uint256 0=success, otherwise a failure. (See ErrorReporter for details) |

***

#### \_setLiquidatorContract

Update the address of the liquidator contract

```solidity
function _setLiquidatorContract(address newLiquidatorContract_) external
```

**Parameters**

| Name                    | Type    | Description                                |
| ----------------------- | ------- | ------------------------------------------ |
| newLiquidatorContract\_ | address | The new address of the liquidator contract |

***

#### \_setPauseGuardian

Admin function to change the Pause Guardian

```solidity
function _setPauseGuardian(address newPauseGuardian) external returns (uint256)
```

**Parameters**

| Name             | Type    | Description                           |
| ---------------- | ------- | ------------------------------------- |
| newPauseGuardian | address | The address of the new Pause Guardian |

**Return Values**

| Name | Type    | Description                                                          |
| ---- | ------- | -------------------------------------------------------------------- |
| \[0] | uint256 | uint256 0=success, otherwise a failure. (See enum Error for details) |

***

#### setMarketBorrowCaps

Alias to \_setMarketBorrowCaps to support the Isolated Lending Comptroller Interface

```solidity
function setMarketBorrowCaps(contract VToken[] vTokens, uint256[] newBorrowCaps) external
```

**Parameters**

| Name          | Type               | Description                                                                                       |
| ------------- | ------------------ | ------------------------------------------------------------------------------------------------- |
| vTokens       | contract VToken\[] | The addresses of the markets (tokens) to change the borrow caps for                               |
| newBorrowCaps | uint256\[]         | The new borrow cap values in underlying to be set. A value of 0 corresponds to Borrow not allowed |

***

#### \_setMarketBorrowCaps

Set the given borrow caps for the given vToken market Borrowing that brings total borrows to or above borrow cap will revert

```solidity
function _setMarketBorrowCaps(contract VToken[] vTokens, uint256[] newBorrowCaps) external
```

**Parameters**

| Name          | Type               | Description                                                                                       |
| ------------- | ------------------ | ------------------------------------------------------------------------------------------------- |
| vTokens       | contract VToken\[] | The addresses of the markets (tokens) to change the borrow caps for                               |
| newBorrowCaps | uint256\[]         | The new borrow cap values in underlying to be set. A value of 0 corresponds to Borrow not allowed |

***

#### setMarketSupplyCaps

Alias to \_setMarketSupplyCaps to support the Isolated Lending Comptroller Interface

```solidity
function setMarketSupplyCaps(contract VToken[] vTokens, uint256[] newSupplyCaps) external
```

**Parameters**

| Name          | Type               | Description                                                                                       |
| ------------- | ------------------ | ------------------------------------------------------------------------------------------------- |
| vTokens       | contract VToken\[] | The addresses of the markets (tokens) to change the supply caps for                               |
| newSupplyCaps | uint256\[]         | The new supply cap values in underlying to be set. A value of 0 corresponds to Minting NotAllowed |

***

#### \_setMarketSupplyCaps

Set the given supply caps for the given vToken market Supply that brings total Supply to or above supply cap will revert

```solidity
function _setMarketSupplyCaps(contract VToken[] vTokens, uint256[] newSupplyCaps) external
```

**Parameters**

| Name          | Type               | Description                                                                                       |
| ------------- | ------------------ | ------------------------------------------------------------------------------------------------- |
| vTokens       | contract VToken\[] | The addresses of the markets (tokens) to change the supply caps for                               |
| newSupplyCaps | uint256\[]         | The new supply cap values in underlying to be set. A value of 0 corresponds to Minting NotAllowed |

***

#### \_setProtocolPaused

Set whole protocol pause/unpause state

```solidity
function _setProtocolPaused(bool state) external returns (bool)
```

**Parameters**

| Name  | Type | Description                                 |
| ----- | ---- | ------------------------------------------- |
| state | bool | The new state (true=paused, false=unpaused) |

**Return Values**

| Name | Type | Description                            |
| ---- | ---- | -------------------------------------- |
| \[0] | bool | bool The updated state of the protocol |

***

#### setActionsPaused

Alias to \_setActionsPaused to support the Isolated Lending Comptroller Interface

```solidity
function setActionsPaused(address[] markets_, enum Action[] actions_, bool paused_) external
```

**Parameters**

| Name      | Type           | Description                                        |
| --------- | -------------- | -------------------------------------------------- |
| markets\_ | address\[]     | Markets to pause/unpause the actions on            |
| actions\_ | enum Action\[] | List of action ids to pause/unpause                |
| paused\_  | bool           | The new paused state (true=paused, false=unpaused) |

***

#### \_setActionsPaused

Pause/unpause certain actions

```solidity
function _setActionsPaused(address[] markets_, enum Action[] actions_, bool paused_) external
```

**Parameters**

| Name      | Type           | Description                                        |
| --------- | -------------- | -------------------------------------------------- |
| markets\_ | address\[]     | Markets to pause/unpause the actions on            |
| actions\_ | enum Action\[] | List of action ids to pause/unpause                |
| paused\_  | bool           | The new paused state (true=paused, false=unpaused) |

***

#### \_setVAIController

Sets a new VAI controller

```solidity
function _setVAIController(contract VAIControllerInterface vaiController_) external returns (uint256)
```

**Return Values**

| Name | Type    | Description                                                                |
| ---- | ------- | -------------------------------------------------------------------------- |
| \[0] | uint256 | uint256 0=success, otherwise a failure (see ErrorReporter.sol for details) |

***

#### \_setVAIMintRate

Set the VAI mint rate

```solidity
function _setVAIMintRate(uint256 newVAIMintRate) external returns (uint256)
```

**Parameters**

| Name           | Type    | Description                     |
| -------------- | ------- | ------------------------------- |
| newVAIMintRate | uint256 | The new VAI mint rate to be set |

**Return Values**

| Name | Type    | Description                                                                |
| ---- | ------- | -------------------------------------------------------------------------- |
| \[0] | uint256 | uint256 0=success, otherwise a failure (see ErrorReporter.sol for details) |

***

#### setMintedVAIOf

Set the minted VAI amount of the `owner`

```solidity
function setMintedVAIOf(address owner, uint256 amount) external returns (uint256)
```

**Parameters**

| Name   | Type    | Description                             |
| ------ | ------- | --------------------------------------- |
| owner  | address | The address of the account to set       |
| amount | uint256 | The amount of VAI to set to the account |

**Return Values**

| Name | Type    | Description                         |
| ---- | ------- | ----------------------------------- |
| \[0] | uint256 | The number of minted VAI by `owner` |

***

#### \_setTreasuryData

Set the treasury data.

```solidity
function _setTreasuryData(address newTreasuryGuardian, address newTreasuryAddress, uint256 newTreasuryPercent) external returns (uint256)
```

**Parameters**

| Name                | Type    | Description                                        |
| ------------------- | ------- | -------------------------------------------------- |
| newTreasuryGuardian | address | The new address of the treasury guardian to be set |
| newTreasuryAddress  | address | The new address of the treasury to be set          |
| newTreasuryPercent  | uint256 | The new treasury percent to be set                 |

**Return Values**

| Name | Type    | Description                                                                |
| ---- | ------- | -------------------------------------------------------------------------- |
| \[0] | uint256 | uint256 0=success, otherwise a failure (see ErrorReporter.sol for details) |

***

#### \_setVenusVAIVaultRate

Set the amount of XVS distributed per block to VAI Vault

```solidity
function _setVenusVAIVaultRate(uint256 venusVAIVaultRate_) external
```

**Parameters**

| Name                | Type    | Description                                                |
| ------------------- | ------- | ---------------------------------------------------------- |
| venusVAIVaultRate\_ | uint256 | The amount of XVS wei per block to distribute to VAI Vault |

***

#### \_setVAIVaultInfo

Set the VAI Vault infos

```solidity
function _setVAIVaultInfo(address vault_, uint256 releaseStartBlock_, uint256 minReleaseAmount_) external
```

**Parameters**

| Name                | Type    | Description                             |
| ------------------- | ------- | --------------------------------------- |
| vault\_             | address | The address of the VAI Vault            |
| releaseStartBlock\_ | uint256 | The start block of release to VAI Vault |
| minReleaseAmount\_  | uint256 | The minimum release amount to VAI Vault |

***

#### setPrimeToken

Alias to \_setPrimeToken to support the Isolated Lending Comptroller Interface

```solidity
function setPrimeToken(contract IPrime _prime) external returns (uint256)
```

**Parameters**

| Name    | Type            | Description                            |
| ------- | --------------- | -------------------------------------- |
| \_prime | contract IPrime | The new prime token contract to be set |

**Return Values**

| Name | Type    | Description                                                             |
| ---- | ------- | ----------------------------------------------------------------------- |
| \[0] | uint256 | uint 0=success, otherwise a failure (see ErrorReporter.sol for details) |

***

#### \_setPrimeToken

Sets the prime token contract for the comptroller

```solidity
function _setPrimeToken(contract IPrime _prime) external returns (uint256)
```

**Parameters**

| Name    | Type            | Description                            |
| ------- | --------------- | -------------------------------------- |
| \_prime | contract IPrime | The new prime token contract to be set |

**Return Values**

| Name | Type    | Description                                                             |
| ---- | ------- | ----------------------------------------------------------------------- |
| \[0] | uint256 | uint 0=success, otherwise a failure (see ErrorReporter.sol for details) |

***

#### setForcedLiquidation

Alias to \_setForcedLiquidation to support the Isolated Lending Comptroller Interface

```solidity
function setForcedLiquidation(address vTokenBorrowed, bool enable) external
```

**Parameters**

| Name           | Type    | Description                           |
| -------------- | ------- | ------------------------------------- |
| vTokenBorrowed | address | Borrowed vToken                       |
| enable         | bool    | Whether to enable forced liquidations |

***

#### \_setForcedLiquidation

Enables forced liquidations for a market. If forced liquidation is enabled, borrows in the market may be liquidated regardless of the account liquidity

```solidity
function _setForcedLiquidation(address vTokenBorrowed, bool enable) external
```

**Parameters**

| Name           | Type    | Description                           |
| -------------- | ------- | ------------------------------------- |
| vTokenBorrowed | address | Borrowed vToken                       |
| enable         | bool    | Whether to enable forced liquidations |

***

#### \_setForcedLiquidationForUser

Enables forced liquidations for user's borrows in a certain market. If forced liquidation is enabled, user's borrows in the market may be liquidated regardless of the account liquidity. Forced liquidation may be enabled for a user even if it is not enabled for the entire market.

```solidity
function _setForcedLiquidationForUser(address borrower, address vTokenBorrowed, bool enable) external
```

**Parameters**

| Name           | Type    | Description                           |
| -------------- | ------- | ------------------------------------- |
| borrower       | address | The address of the borrower           |
| vTokenBorrowed | address | Borrowed vToken                       |
| enable         | bool    | Whether to enable forced liquidations |

***

#### setWhiteListFlashLoanAccount

```solidity
function setWhiteListFlashLoanAccount(address account, bool _isWhiteListed) external 
```

**Explanation:**\
This function grants or revokes an account's permission to use the protocol's flash loan feature, enforcing access control and address validation.

**Parameters**

| Name            | Type    | Description                        |
| --------------- | ------- | ---------------------------------- |
| account         | address | The account to whitelist or remove |
| \_isWhiteListed | bool    | True to whitelist, false to remove |

**Return Values**

| Name | Type | Description |
| ---- | ---- | ----------- |
| None |      |             |

***

#### \_setXVSToken

Set the address of the XVS token

```solidity
function _setXVSToken(address xvs_) external
```

**Parameters**

| Name  | Type    | Description                  |
| ----- | ------- | ---------------------------- |
| xvs\_ | address | The address of the XVS token |

***

#### \_setXVSVToken

Set the address of the XVS vToken

```solidity
function _setXVSVToken(address xvsVToken_) external
```

**Parameters**

| Name        | Type    | Description                   |
| ----------- | ------- | ----------------------------- |
| xvsVToken\_ | address | The address of the XVS vToken |

***

#### setPoolActive

updates active status for a specific pool (excluding the Core Pool)

```solidity
function setPoolActive(uint96 poolId, bool active) external
```

**Parameters**

| Name   | Type   | Description                      |
| ------ | ------ | -------------------------------- |
| poolId | uint96 | id of the pool to update         |
| active | bool   | true to enable, false to disable |

**📅 Events**

* PoolActiveStatusUpdated Emitted after the pool active status is updated.

**❌ Errors**

* InvalidOperationForCorePool Reverts when attempting to call pool-specific methods on the Core Pool.
* PoolDoesNotExist Reverts if the target pool ID does not exist.

***

#### setIsBorrowAllowed

Updates the `isBorrowAllowed` flag for a market in a pool.

```solidity
function setIsBorrowAllowed(uint96 poolId, address vToken, bool borrowAllowed) external
```

**Parameters**

| Name          | Type    | Description                         |
| ------------- | ------- | ----------------------------------- |
| poolId        | uint96  | The ID of the pool.                 |
| vToken        | address | The address of the market (vToken). |
| borrowAllowed | bool    | The new borrow allowed status.      |

**📅 Events**

* BorrowAllowedUpdated Emitted after the borrow permission for a market is updated.

**❌ Errors**

* PoolDoesNotExist Reverts if the pool ID is invalid.
* MarketConfigNotFound Reverts if the market is not listed in the pool.

***


# FlashLoanFacet

## FlashLoanFacet

This facet contract contains functions for flash loan operations

{% hint style="warning" %}
BNB Core Pool only. At block `118,363,255`, this facet was routed to `0xAC54A4D148690b7FDA22B1D29c4439aCBF668fb2`. Call its selectors through the Unitroller, verify the live selector assignment, authorized caller, pause state, and market implementation, and treat the [v10.3.0 source](https://github.com/VenusProtocol/venus-protocol/blob/v10.3.0/contracts/Comptroller/Diamond/facets/FlashLoanFacet.sol) as the source reference rather than a permanent deployed ABI.
{% endhint %}

## Solidity API

#### executeFlashLoan

Executes a flashLoan operation with the requested assets

```solidity
function executeFlashLoan(
    address payable onBehalf,
    address payable receiver,
    VToken[] memory vTokens,
    uint256[] memory underlyingAmounts,
    bytes memory param
) external
```

Transfers the specified assets to the receiver contract and handles repayment. Supports both full repayment and partial repayment where unpaid amounts become ongoing debt positions.

**Parameters**

| Name              | Type            | Description                                                                                  |
| ----------------- | --------------- | -------------------------------------------------------------------------------------------- |
| onBehalf          | address payable | The address of the user whose debt position will be created in case of partial repayment     |
| receiver          | address payable | The address of the contract that will receive the flashLoan amount and execute the operation |
| vTokens           | VToken\[]       | The addresses of the vToken assets to be loaned                                              |
| underlyingAmounts | uint256\[]      | The amounts of each underlying assets to be loaned                                           |
| param             | bytes           | The bytes passed in the executeOperation call                                                |

***


# VToken

This page applies only to markets in the BNB Chain Core Pool. It replaces the previous unscoped reference to the abstract `VToken` base, which is not a complete deployable market API.

{% hint style="danger" %}
Core Pools on Ethereum, opBNB, Arbitrum, ZKsync, Optimism, Base, and Unichain use the BeaconProxy VToken architecture from the `isolated-pools` repository. Use the [BeaconProxy VToken reference](/technical-reference/reference-isolated-pools/vtoken/vtoken) for those deployments.
{% endhint %}

## BNB market types

| Underlying          | Runtime pattern                            | Concrete API                                                                                                                                                                                                                              |
| ------------------- | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ERC-20/BEP-20 token | `VBep20Delegator` proxy → `VBep20Delegate` | [VBep20.sol](https://github.com/VenusProtocol/venus-protocol/blob/v10.3.0/contracts/Tokens/VTokens/VBep20.sol) plus shared [VToken.sol](https://github.com/VenusProtocol/venus-protocol/blob/v10.3.0/contracts/Tokens/VTokens/VToken.sol) |
| Native BNB          | Standalone `VBNB` contract                 | [VBNB.sol](https://github.com/VenusProtocol/venus-protocol/blob/v10.3.0/contracts/Tokens/VTokens/VBNB.sol) plus shared `VToken.sol`                                                                                                       |

At BNB Chain block `118,363,255`, the vUSDC delegator reported implementation `0xCDfea50f7CECCB24Fe804657DB8E6c93b689941e`, the delegate installed for the BNB Core ERC-20 markets upgraded by [VIP-640](https://venus.io/governance/proposal/640?chainId=56). Resolve `implementation()` on the exact market instead of assuming every market points to the same delegate.

Current market addresses are listed in [Deployed Markets](/deployed-contracts/markets).

## User entry points

The native-value convention is the most important ABI difference:

| Operation             | ERC-20 underlying market                                                           | Native BNB market                            |
| --------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------- |
| Supply                | `mint(uint256 mintAmount)` after underlying-token approval                         | `mint()` payable, or send BNB to `receive()` |
| Redeem vTokens        | `redeem(uint256 redeemTokens)`                                                     | `redeem(uint256 redeemTokens)`               |
| Redeem underlying     | `redeemUnderlying(uint256 redeemAmount)`                                           | `redeemUnderlying(uint256 redeemAmount)`     |
| Borrow                | `borrow(uint256 borrowAmount)`                                                     | `borrow(uint256 borrowAmount)`               |
| Repay own debt        | `repayBorrow(uint256 repayAmount)` after underlying-token approval                 | `repayBorrow()` payable                      |
| Repay another account | `repayBorrowBehalf(address,uint256)` after underlying-token approval               | `repayBorrowBehalf(address)` payable         |
| Liquidate             | `liquidateBorrow(address,uint256,VTokenInterface)` after underlying-token approval | `liquidateBorrow(address,VToken)` payable    |

For ERC-20 markets, approve the **underlying token** for supply, repay, and liquidation. Calling `approve` on the vToken instead grants permission to transfer the receipt token; it does not approve movement of the underlying asset.

Several ERC-20 methods return `0` on success and a nonzero protocol error code on failure. A successful EVM transaction alone is therefore not sufficient; clients must decode the return value and relevant events. Native BNB entry points generally revert when their internal operation fails.

## Shared accounting and reads

The abstract `VToken` base supplies receipt-token accounting, interest accrual, reserves, borrows, exchange-rate calculations, seizure, and administrative hooks. Important integration boundaries include:

* `balanceOfUnderlying`, `borrowBalanceCurrent`, `totalBorrowsCurrent`, and `exchangeRateCurrent` accrue interest and are not `view` calls.
* Snapshot-style alternatives such as `borrowBalanceStored` and `exchangeRateStored` do not accrue the market first.
* The exchange rate and interest calculations depend on the market's exact implementation and rate-time basis. See [Protocol Math](/guides/protocol-math).
* Borrowing and redeeming are subject to Comptroller policy, market listing, liquidity, caps, and pause state.
* Delegated `mintBehalf`, `borrowBehalf`, and redeem-on-behalf methods require the account to authorize the caller through the BNB Core Comptroller.

## Implementation and administration

ERC-20 market calls go to the delegator address. The delegator stores balances and configuration and delegates execution to its current implementation. Read `implementation()` and generate the ABI from that implementation at the relevant block.

VBNB is not a VBep20 delegator. Some privileged operations are performed through the separate VBNBAdmin wrapper; do not infer the ERC-20 proxy upgrade or approval model for vBNB.

Governance can upgrade delegates, change interest-rate models and Comptroller configuration, or pause actions. Verify the live proxy, Comptroller, Access Control Manager, and pause state before constructing an operator transaction.


# Prime

Venus Prime has two deployed contract generations. BNB Chain uses PrimeV2 with a leaderboard; the other current mainnet rows still use Prime V1.

## Mainnet version map

The following state was read on 2026-08-27:

| Network   | Prime consumer                               | Generation | PLP mode                | PLP paused |
| --------- | -------------------------------------------- | ---------- | ----------------------- | ---------- |
| BNB Chain | `0x059EabA8676b03e4e8f009eFb7F587C28450F50f` | PrimeV2    | block, 70,080,000/year  | No         |
| Ethereum  | `0x14C4525f47A7f7C984474979c57a2Dccb8EACB39` | Prime V1   | block, 2,628,000/year   | No         |
| Arbitrum  | `0xFE69720424C954A2da05648a0FAC84f9bf11Ef49` | Prime V1   | second, 31,536,000/year | Yes        |
| ZKsync    | `0xdFe62Dcba3Ce0A827439390d7d45Af8baE599978` | Prime V1   | second, 31,536,000/year | Yes        |
| Optimism  | `0xE76d2173546Be97Fa6E18358027BdE9742a649f7` | Prime V1   | second, 31,536,000/year | Yes        |
| Base      | `0xD2e84244f1e9Fca03Ff024af35b8f9612D5d7a30` | Prime V1   | second, 31,536,000/year | Yes        |
| Unichain  | `0x600aFf613d40D87C8Fe90Cb2e78e8e6667c0C872` | Prime V1   | second, 31,536,000/year | Yes        |

PLP means PrimeLiquidityProvider. A paused PLP does not by itself prove that the Prime deployment is deprecated; it proves only that PLP transfers were paused at the observed block. Check current product support, the Prime contract, and all funding paths before assigning a lifecycle state.

{% hint style="danger" %}
The child PrimeV2 and PrimeLeaderboard pages apply only to BNB Chain. Do not use their ABI, leaderboard eligibility, or permissionless minting model for a Prime V1 deployment.
{% endhint %}

## BNB PrimeV2 components

At BNB Chain block `118,364,540`, PrimeV2 was unpaused and resolved to:

| Component                                                                                         | Proxy or address                             | Implementation                               |
| ------------------------------------------------------------------------------------------------- | -------------------------------------------- | -------------------------------------------- |
| [PrimeV2](/technical-reference/reference-core-pool/prime/prime)                                   | `0x059EabA8676b03e4e8f009eFb7F587C28450F50f` | `0x18cb7198cbb6d6e94001458cf3cf47c106d83a1b` |
| [PrimeLeaderboard](/technical-reference/reference-core-pool/prime/prime-leaderboard)              | `0x55e2ccF68B7A276dc28AfA107997b8B1Be932c0b` | `0xd80de9ecb6596df95dd67af73b67122054c2d1a1` |
| [PrimeLiquidityProvider](/technical-reference/reference-core-pool/prime/prime-liquidity-provider) | `0x23c4F844ffDdC6161174eB32c770D4D8C07833F2` | `0x46bed43b29d73835ff075bba1a0002a1ed1e4de8` |
| [PrimeLens](/technical-reference/reference-core-pool/prime/prime-lens)                            | `0x2f8c5e4562E22DB7908C56Bf99961C053436473c` | Non-proxy lens                               |

Storage references: [PrimeV2Storage](/technical-reference/reference-core-pool/prime/prime-storage) and [PrimeLeaderboardStorage](/technical-reference/reference-core-pool/prime/prime-leaderboard-storage). See the [Venus Prime technical article](/technical-reference/reference-technical-articles/prime) for the BNB V2 flow.

Current addresses are listed in [Deployed Funds](/deployed-contracts/funds).


# Prime token

## PrimeV2

PrimeV2 is the Prime token contract with leaderboard-based distribution. Prime status is decided by the `PrimeLeaderboard` contract based on time-weighted XVS staking, and boosted rewards are distributed to Prime holders across supported markets.

{% hint style="warning" %}
BNB Chain only. At block `118,364,540`, proxy `0x059EabA8676b03e4e8f009eFb7F587C28450F50f` used implementation `0x18cb7198cbb6d6e94001458cf3cf47c106d83a1b` and was unpaused. Other mainnets currently use Prime V1; see [Prime Contract Versions](/technical-reference/reference-core-pool/prime).
{% endhint %}

## Solidity API

#### WRAPPED\_NATIVE\_TOKEN

Address of wrapped native token

```solidity
address WRAPPED_NATIVE_TOKEN
```

***

#### NATIVE\_MARKET

Address of native market vToken

```solidity
address NATIVE_MARKET
```

***

#### xvsVault

Address of XVSVault contract

```solidity
address xvsVault
```

***

#### xvsVaultRewardToken

Reward token address in XVSVault

```solidity
address xvsVaultRewardToken
```

***

#### xvsVaultPoolId

Pool ID in XVSVault

```solidity
uint256 xvsVaultPoolId
```

***

#### constructor

PrimeV2 constructor. Sets the immutable references above and disables further initialization of the implementation contract.

```solidity
constructor(address wrappedNativeToken_, address nativeMarket_, address xvsVault_, address xvsVaultRewardToken_, uint256 xvsVaultPoolId_, bool timeBased_, uint256 blocksPerYear_) public
```

**Parameters**

| Name                  | Type    | Description                                                         |
| --------------------- | ------- | ------------------------------------------------------------------- |
| wrappedNativeToken\_  | address | Address of wrapped native token                                     |
| nativeMarket\_        | address | Address of native market                                            |
| xvsVault\_            | address | Address of XVSVault contract                                        |
| xvsVaultRewardToken\_ | address | Reward token address in XVSVault                                    |
| xvsVaultPoolId\_      | uint256 | Pool ID in XVSVault                                                 |
| timeBased\_           | bool    | A boolean indicating whether the contract is based on time or block |
| blocksPerYear\_       | uint256 | Total blocks per year                                               |

**❌ Errors**

* Throw InvalidAddress if xvsVault\_ or xvsVaultRewardToken\_ is the zero address

***

#### initialize

PrimeV2 initializer

```solidity
function initialize(uint128 alphaNumerator_, uint128 alphaDenominator_, address accessControlManager_, address primeLiquidityProvider_, address corePoolComptroller_, address oracle_, uint256 loopsLimit_) external
```

**Parameters**

| Name                     | Type    | Description                                                 |
| ------------------------ | ------- | ----------------------------------------------------------- |
| alphaNumerator\_         | uint128 | numerator of alpha. If alpha is 0.5 then numerator is 1     |
| alphaDenominator\_       | uint128 | denominator of alpha. If alpha is 0.5 then denominator is 2 |
| accessControlManager\_   | address | Address of AccessControlManager                             |
| primeLiquidityProvider\_ | address | Address of PrimeLiquidityProvider                           |
| corePoolComptroller\_    | address | Address of core pool comptroller                            |
| oracle\_                 | address | Address of Oracle                                           |
| loopsLimit\_             | uint256 | Maximum number of loops allowed in a single transaction     |

**❌ Errors**

* Throw InvalidAddress if any of the address is zero
* Throw InvalidAlphaArguments if alpha arguments are invalid

***

#### claimPrime

Mint a Prime token for a user in a permissionless way. Checks the user's Prime Score (their effective stake, read via `PrimeLeaderboard.getEffectiveStake`) against `mintThreshold`. Anyone can call this on behalf of an eligible user; no ACM required. Reverts while the contract is paused.

```solidity
function claimPrime(address user) external
```

**Parameters**

| Name | Type    | Description              |
| ---- | ------- | ------------------------ |
| user | address | User address to mint for |

**📅 Events**

* Emits Mint event on new token issuance

**❌ Errors**

* Throw InvalidAddress if user is the zero address
* Throw ScoreUpdateInProgress if a score update round is active
* Throw UserAlreadyHasPrimeToken if user already has a token
* Throw LeaderboardNotSet if primeLeaderboard address is not configured
* Throw MintThresholdNotSet if mintThreshold is zero
* Throw EligibilityBelowThreshold if user's Prime Score < mintThreshold
* Throw InvalidLimit if mint limit would be exceeded
* Throw MintWindowClosed if the minting deadline has passed

***

#### claimPrimeBatch

Mint Prime tokens for multiple users in a permissionless way. Non-holders below `mintThreshold` are skipped with a `SkippedIneligibleUser` event (not reverted). Existing Prime holders are silently skipped. Anyone can call this; no ACM required. Reverts while the contract is paused.

```solidity
function claimPrimeBatch(address[] users) external
```

**Parameters**

| Name  | Type       | Description                         |
| ----- | ---------- | ----------------------------------- |
| users | address\[] | Array of user addresses to mint for |

**📅 Events**

* Emits Mint event for each new token issuance
* Emits SkippedIneligibleUser for each non-holder below threshold

**❌ Errors**

* Throw ScoreUpdateInProgress if a score update round is active
* Throw LeaderboardNotSet if primeLeaderboard address is not configured
* Throw MintThresholdNotSet if mintThreshold is zero
* Throw MintWindowClosed if the minting deadline has passed
* Throw InvalidLimit if mint limit would be exceeded
* Throw MaxLoopsLimitExceeded if the batch is larger than loopsLimit

***

#### issue

Issue a Prime token to a single user (admin function)

```solidity
function issue(address user) external
```

**Parameters**

| Name | Type    | Description  |
| ---- | ------- | ------------ |
| user | address | User address |

**📅 Events**

* Emits Mint event on new token issuance

**⛔️ Access Requirements**

* Controlled by ACM

**❌ Errors**

* Throw InvalidAddress if user is zero address
* Throw InvalidLimit if mint limit would be exceeded
* Throw UserAlreadyHasPrimeToken if user already has a token
* Throw ScoreUpdateInProgress if a score update round is active

***

#### issueBatch

Issue Prime tokens to multiple users (admin function)

```solidity
function issueBatch(address[] users) external
```

**Parameters**

| Name  | Type       | Description             |
| ----- | ---------- | ----------------------- |
| users | address\[] | Array of user addresses |

**📅 Events**

* Emits Mint event on new token issuance

**⛔️ Access Requirements**

* Controlled by ACM

**❌ Errors**

* Throw InvalidAddress if any user in the batch is zero address
* Throw InvalidLimit if mint limit would be exceeded
* Throw ScoreUpdateInProgress if a score update round is active
* Throw MaxLoopsLimitExceeded if the batch is larger than loopsLimit

***

#### burn

Burn a user's Prime token (admin function)

```solidity
function burn(address user) external
```

**Parameters**

| Name | Type    | Description  |
| ---- | ------- | ------------ |
| user | address | User address |

**📅 Events**

* Emits Burn event

**⛔️ Access Requirements**

* Controlled by ACM

**❌ Errors**

* Throw UserHasNoPrimeToken if user has no prime token
* Throw ScoreUpdateInProgress if a score update round is active

***

#### burnBatch

Burn Prime tokens for multiple users (admin function)

```solidity
function burnBatch(address[] users) external
```

**Parameters**

| Name  | Type       | Description             |
| ----- | ---------- | ----------------------- |
| users | address\[] | Array of user addresses |

**📅 Events**

* Emits Burn event for each user

**⛔️ Access Requirements**

* Controlled by ACM

**❌ Errors**

* Throw ScoreUpdateInProgress if a score update round is active
* Throw MaxLoopsLimitExceeded if the batch is larger than loopsLimit

***

#### isUserPrimeHolder

Check if a user has a Prime token

```solidity
function isUserPrimeHolder(address user) external view returns (bool)
```

**Parameters**

| Name | Type    | Description  |
| ---- | ------- | ------------ |
| user | address | User address |

**Return Values**

| Name | Type | Description                        |
| ---- | ---- | ---------------------------------- |
| \[0] | bool | whether the user has a Prime token |

***

#### claimInterest

Claim accrued interest for a market (to msg.sender). If the PrimeV2 balance is insufficient, funds are pulled from the PrimeLiquidityProvider in the same transaction; any remaining shortfall stays recorded as accrued and claimable later (partial claim, no revert). Residual accrued interest remains claimable even after a market is removed. Reverts while the contract is paused.

```solidity
function claimInterest(address vToken) external returns (uint256)
```

**Parameters**

| Name   | Type    | Description    |
| ------ | ------- | -------------- |
| vToken | address | Market address |

**Return Values**

| Name | Type    | Description    |
| ---- | ------- | -------------- |
| \[0] | uint256 | amount claimed |

**📅 Events**

* Emits InterestClaimed event

**❌ Errors**

* Throw MarketNotSupported if market is not supported (only when the vToken is not a current Prime market — never added, or since removed — and the user has no residual accrued balance)

***

#### claimInterest

Claim accrued interest for a market to a specific address. Permissionless: anyone can trigger a claim on behalf of a user. Tokens are always sent to the user address, never to msg.sender. Same shortfall behavior as the single-argument overload: partial claim with the remainder kept as accrued, no revert. Reverts while the contract is paused.

```solidity
function claimInterest(address vToken, address user) external returns (uint256)
```

**Parameters**

| Name   | Type    | Description       |
| ------ | ------- | ----------------- |
| vToken | address | Market address    |
| user   | address | Recipient address |

**Return Values**

| Name | Type    | Description    |
| ---- | ------- | -------------- |
| \[0] | uint256 | amount claimed |

**📅 Events**

* Emits InterestClaimed event

**❌ Errors**

* Throw MarketNotSupported if market is not supported (only when the vToken is not a current Prime market — never added, or since removed — and the user has no residual accrued balance)

***

#### accrueInterest

Accrue interest for a market. Intentionally not gated by the pause to ensure fair reward distribution during pauses.

```solidity
function accrueInterest(address vToken) public
```

**Parameters**

| Name   | Type    | Description    |
| ------ | ------- | -------------- |
| vToken | address | Market address |

**❌ Errors**

* Throw MarketNotSupported if market is not supported

***

#### accrueInterestAndUpdateScore

Accrue interest and update score for a user in a specific market. Called by the Comptroller hooks.

```solidity
function accrueInterestAndUpdateScore(address user, address market) external
```

**Parameters**

| Name   | Type    | Description    |
| ------ | ------- | -------------- |
| user   | address | User address   |
| market | address | Market address |

***

#### accrueInterestAndUpdateScore

Accrue interest and update score for a user across all markets. Called by `PrimeLeaderboard` when a user's XVS stake changes, so rewards are accrued at the old score before the score is recalculated.

```solidity
function accrueInterestAndUpdateScore(address user) external
```

**Parameters**

| Name | Type    | Description  |
| ---- | ------- | ------------ |
| user | address | User address |

**❌ Errors**

* Throw OnlyPrimeLeaderboard if caller is not the PrimeLeaderboard contract

***

#### getPendingRewards

Get pending rewards for a user (accrues first)

```solidity
function getPendingRewards(address user) external returns (struct PrimeV2StorageV1.PendingReward[] pendingRewards)
```

**Parameters**

| Name | Type    | Description  |
| ---- | ------- | ------------ |
| user | address | User address |

**Return Values**

| Name           | Type                                     | Description                         |
| -------------- | ---------------------------------------- | ----------------------------------- |
| pendingRewards | struct PrimeV2StorageV1.PendingReward\[] | Array of pending rewards per market |

***

#### getPendingRewardsStatic

Get pending rewards for a user (view-only, does not accrue). Returns rewards based on the last accrued state without triggering new accrual.

```solidity
function getPendingRewardsStatic(address user) external view returns (struct PrimeV2StorageV1.PendingReward[] pendingRewards)
```

**Parameters**

| Name | Type    | Description  |
| ---- | ------- | ------------ |
| user | address | User address |

**Return Values**

| Name           | Type                                     | Description                         |
| -------------- | ---------------------------------------- | ----------------------------------- |
| pendingRewards | struct PrimeV2StorageV1.PendingReward\[] | Array of pending rewards per market |

***

#### getLifetimeAccruedByMarket

Lifetime accrued rewards for many users in a single market. Pure view over the `lifetimeAccrued` field; intended for the off-chain cycle pipeline to snapshot per-(market, user) earnings without indexing events.

```solidity
function getLifetimeAccruedByMarket(address market, address[] users) external view returns (uint256[] amounts)
```

**Parameters**

| Name   | Type       | Description             |
| ------ | ---------- | ----------------------- |
| market | address    | vToken address          |
| users  | address\[] | Array of user addresses |

**Return Values**

| Name    | Type       | Description                                           |
| ------- | ---------- | ----------------------------------------------------- |
| amounts | uint256\[] | Lifetime accrued amounts, indexed parallel to `users` |

***

#### getLifetimeAccruedByUser

Lifetime accrued rewards for one user across many markets.

```solidity
function getLifetimeAccruedByUser(address user, address[] markets_) external view returns (uint256[] amounts)
```

**Parameters**

| Name      | Type       | Description               |
| --------- | ---------- | ------------------------- |
| user      | address    | User address              |
| markets\_ | address\[] | Array of vToken addresses |

**Return Values**

| Name    | Type       | Description                                              |
| ------- | ---------- | -------------------------------------------------------- |
| amounts | uint256\[] | Lifetime accrued amounts, indexed parallel to `markets_` |

***

#### updateScores

Update scores for a batch of users. Intentionally not gated by the pause — the keeper must complete rounds even during pauses.

```solidity
function updateScores(address[] users) external
```

**Parameters**

| Name  | Type       | Description             |
| ----- | ---------- | ----------------------- |
| users | address\[] | Array of user addresses |

**📅 Events**

* Emits UserScoreUpdated event

**❌ Errors**

* Throw NoScoreUpdatesRequired if no score updates are required
* Throw MaxLoopsLimitExceeded if the batch is larger than loopsLimit

***

#### getAllMarkets

Get all Prime markets

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

**Return Values**

| Name | Type       | Description               |
| ---- | ---------- | ------------------------- |
| \[0] | address\[] | Array of market addresses |

***

#### xvsBalanceOfUser

Get a user's XVS balance from the vault (net of pending withdrawals)

```solidity
function xvsBalanceOfUser(address user) external view returns (uint256)
```

**Parameters**

| Name | Type    | Description  |
| ---- | ------- | ------------ |
| user | address | User address |

**Return Values**

| Name | Type    | Description        |
| ---- | ------- | ------------------ |
| \[0] | uint256 | User's XVS balance |

***

#### addMarket

Add a market to Prime. Opens a new score-update round: all Prime holders' scores must be recomputed via `updateScores` before issuing, burning and claiming Prime tokens are unblocked.

```solidity
function addMarket(address market, uint256 supplyMultiplier, uint256 borrowMultiplier) external
```

**Parameters**

| Name             | Type    | Description                       |
| ---------------- | ------- | --------------------------------- |
| market           | address | Market address                    |
| supplyMultiplier | uint256 | Supply multiplier, scaled by 1e18 |
| borrowMultiplier | uint256 | Borrow multiplier, scaled by 1e18 |

**📅 Events**

* Emits MarketAdded event
* Emits IncompleteRoundDiscarded if a previous score-update round was still in progress

**⛔️ Access Requirements**

* Controlled by ACM

**❌ Errors**

* Throw MarketAlreadyExists if market already exists
* Throw InvalidMultipliers if both multipliers are zero
* Throw InvalidVToken if market is not listed
* Throw AssetAlreadyExists if asset already has a market
* Throw MaxLoopsLimitExceeded if listing this market would exceed loopsLimit
* Throw UnsupportedUnderlyingDecimals if underlying token has decimals > 18

***

#### removeMarket

Remove a market from the Prime program

```solidity
function removeMarket(address market) external
```

**Parameters**

| Name   | Type    | Description                     |
| ------ | ------- | ------------------------------- |
| market | address | Market vToken address to remove |

**📅 Events**

* Emits MarketRemoved event

**⛔️ Access Requirements**

* Controlled by ACM

**❌ Errors**

* Throw MarketNotSupported if market doesn't exist
* Throw MarketHasActiveMembers if market still has members with scores

***

#### setLimit

Update mint limit (maximum Prime tokens)

```solidity
function setLimit(uint256 tokenLimit_) external
```

**Parameters**

| Name         | Type    | Description     |
| ------------ | ------- | --------------- |
| tokenLimit\_ | uint256 | New token limit |

**📅 Events**

* Emits MintLimitUpdated event

**⛔️ Access Requirements**

* Controlled by ACM

**❌ Errors**

* Throw InvalidLimit if limit is less than current count

***

#### updateAlpha

Update alpha parameter. Opens a new score-update round: all Prime holders' scores must be recomputed via `updateScores` before issuing, burning and claiming Prime tokens are unblocked.

```solidity
function updateAlpha(uint128 alphaNumerator_, uint128 alphaDenominator_) external
```

**Parameters**

| Name               | Type    | Description           |
| ------------------ | ------- | --------------------- |
| alphaNumerator\_   | uint128 | New alpha numerator   |
| alphaDenominator\_ | uint128 | New alpha denominator |

**📅 Events**

* Emits AlphaUpdated event
* Emits IncompleteRoundDiscarded if a previous score-update round was still in progress

**⛔️ Access Requirements**

* Controlled by ACM

**❌ Errors**

* Throw InvalidAlphaArguments if alpha arguments are invalid

***

#### updateMultipliers

Update market multipliers. Opens a new score-update round: all Prime holders' scores must be recomputed via `updateScores` before issuing, burning and claiming Prime tokens are unblocked.

```solidity
function updateMultipliers(address market, uint256 supplyMultiplier, uint256 borrowMultiplier) external
```

**Parameters**

| Name             | Type    | Description           |
| ---------------- | ------- | --------------------- |
| market           | address | Market address        |
| supplyMultiplier | uint256 | New supply multiplier |
| borrowMultiplier | uint256 | New borrow multiplier |

**📅 Events**

* Emits MultiplierUpdated event
* Emits IncompleteRoundDiscarded if a previous score-update round was still in progress

**⛔️ Access Requirements**

* Controlled by ACM

**❌ Errors**

* Throw MarketNotSupported if market is not supported
* Throw InvalidMultipliers if both multipliers are zero

***

#### pause

Pause the contract

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

**📅 Events**

* Emits Paused event

**⛔️ Access Requirements**

* Controlled by ACM

***

#### unpause

Unpause the contract

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

**📅 Events**

* Emits Unpaused event

**⛔️ Access Requirements**

* Controlled by ACM

***

#### setMaxLoopsLimit

Set the max loops limit

```solidity
function setMaxLoopsLimit(uint256 loopsLimit) external
```

**Parameters**

| Name       | Type    | Description     |
| ---------- | ------- | --------------- |
| loopsLimit | uint256 | New loops limit |

**📅 Events**

* Emits MaxLoopsLimitUpdated event

**⛔️ Access Requirements**

* Controlled by ACM

***

#### setPrimeLeaderboard

Set the `PrimeLeaderboard` contract address used for permissionless mint eligibility

```solidity
function setPrimeLeaderboard(address primeLeaderboard_) external
```

**Parameters**

| Name               | Type    | Description                          |
| ------------------ | ------- | ------------------------------------ |
| primeLeaderboard\_ | address | Address of PrimeLeaderboard contract |

**📅 Events**

* Emits PrimeLeaderboardSet event

**⛔️ Access Requirements**

* Controlled by ACM

**❌ Errors**

* Throw InvalidAddress if address is zero

***

#### setMintThreshold

Set the minimum Prime Score threshold and minting deadline for permissionless Prime minting. Each epoch is one calendar month; governance typically calls this at end-of-epoch with the #500 user's Prime Score as the threshold. Pass `mintThreshold_ = 0` to disable the permissionless minting window immediately. Pass `mintDeadline_ = 0` for no expiry; otherwise the window auto-closes once `block.timestamp` exceeds it.

```solidity
function setMintThreshold(uint256 mintThreshold_, uint256 mintDeadline_) external
```

**Parameters**

| Name            | Type    | Description                                                    |
| --------------- | ------- | -------------------------------------------------------------- |
| mintThreshold\_ | uint256 | New mint threshold (set to 0 to close the window)              |
| mintDeadline\_  | uint256 | Unix timestamp after which minting is closed (0 = no deadline) |

**📅 Events**

* Emits MintThresholdUpdated event

**⛔️ Access Requirements**

* Controlled by ACM

**❌ Errors**

* Throw InvalidDeadline if mintDeadline\_ is non-zero and not strictly in the future

***

#### recordCycleSnapshot

Emit a cycle-start anchor event so the off-chain reward pipeline can recover cycle boundaries by indexing the event log. Operational hook, not a policy lever. Not idempotent on-chain: duplicate `cycleId`s emit duplicate events and must be de-duplicated by the indexer.

```solidity
function recordCycleSnapshot(uint256 cycleId) external
```

**Parameters**

| Name    | Type    | Description                                           |
| ------- | ------- | ----------------------------------------------------- |
| cycleId | uint256 | Identifier of the cycle whose start is being recorded |

**📅 Events**

* Emits CycleSnapshotRecorded(cycleId, block.number, block.timestamp)

**⛔️ Access Requirements**

* Controlled by ACM. Check the live grantees and current operations runbook; this reference does not prescribe an EOA, bot, or Timelock as the caller.

***

#### sweepUndistributed

Reclaim PLP income that accrued for a market while no scored members existed in it. Flushes any pending PLP delta via `accrueInterest` first, then transfers the recorded slice to the recipient.

```solidity
function sweepUndistributed(address vToken, address to) external
```

**Parameters**

| Name   | Type    | Description                                           |
| ------ | ------- | ----------------------------------------------------- |
| vToken | address | Market address whose underlying slice should be swept |
| to     | address | Recipient of the swept tokens                         |

**📅 Events**

* Emits UndistributedSwept on a non-zero transfer

**⛔️ Access Requirements**

* Controlled by ACM

**❌ Errors**

* Throw InvalidAddress if to is the zero address

Note: for a removed Prime market the call does not revert; it returns without transferring once the market's `undistributedReward` slice is zero. Passing an address that is not a vToken at all reverts when resolving its underlying token.

***


# Prime leaderboard

## PrimeLeaderboard

PrimeLeaderboard manages Prime V2 eligibility with time-weighted scoring. It tracks per-deposit timestamps for LIFO withdrawals and computes each user's Prime Score. It is called by the `XVSVault` via the existing `xvsUpdated()` callback. Governance reads `getEffectiveStakeBatch()` off-chain, ranks users by Prime Score, and either sets a mint threshold on PrimeV2 (for permissionless minting) or calls `PrimeV2.issue()` / `burn()` directly.

{% hint style="warning" %}
BNB PrimeV2 only. At block `118,364,540`, proxy `0x55e2ccF68B7A276dc28AfA107997b8B1Be932c0b` used implementation `0xd80de9ecb6596df95dd67af73b67122054c2d1a1`. Resolve the live proxy and Access Control Manager before using an administrative function.
{% endhint %}

## Solidity API

#### constructor

Sets immutable XVS Vault references and disables initialization on the implementation.

```solidity
constructor(address xvsVault_, address xvsVaultRewardToken_, uint256 xvsVaultPoolId_)
```

***

#### xvsVault

Address of XVSVault contract

```solidity
address xvsVault
```

***

#### xvsVaultRewardToken

Reward token address in XVSVault

```solidity
address xvsVaultRewardToken
```

***

#### xvsVaultPoolId

Pool ID in XVSVault

```solidity
uint256 xvsVaultPoolId
```

***

#### initialize

PrimeLeaderboard initializer. Initializes the default multiplier tiers: 30 days → 1.3x, 60 days → 1.6x, 90 days → 2.0x (cap).

```solidity
function initialize(address accessControlManager_, uint256 loopsLimit_) external
```

**Parameters**

| Name                   | Type    | Description                                             |
| ---------------------- | ------- | ------------------------------------------------------- |
| accessControlManager\_ | address | Address of AccessControlManager                         |
| loopsLimit\_           | uint256 | Maximum number of loops allowed in a single transaction |

***

#### initializeStakers

Batch-seed existing stakers' deposit data during migration. Idempotent: skips users with `totalStaked[user] != 0` (already seeded). Caller must batch appropriately to avoid exceeding block gas limits.

```solidity
function initializeStakers(address[] users, uint256[] amounts, uint64[] timestamps) external
```

**Parameters**

| Name       | Type       | Description                           |
| ---------- | ---------- | ------------------------------------- |
| users      | address\[] | Array of user addresses to initialize |
| amounts    | uint256\[] | Array of staked amounts (in XVS wei)  |
| timestamps | uint64\[]  | Array of deposit timestamps           |

**📅 Events**

* Emits StakerInitialized for each user successfully seeded

**⛔️ Access Requirements**

* Controlled by ACM

**❌ Errors**

* Throw StakersAlreadyInitialized if finalizeInitialization was already called
* Throw LengthMismatch if array lengths don't match
* Throw InvalidTimestamp if a seeded timestamp is zero or in the future

***

#### finalizeInitialization

Finalize staker initialization, preventing further seeding. Must be called after all `initializeStakers` batches are complete.

```solidity
function finalizeInitialization() external
```

**📅 Events**

* Emits StakersInitializationFinalized

**⛔️ Access Requirements**

* Controlled by ACM

**❌ Errors**

* Throw StakersAlreadyInitialized if already finalized

***

#### xvsUpdated

Called by the XVSVault (via `primeToken.xvsUpdated`) when a user's stake changes. Reads the user's current vault balance, diffs it against `totalStaked`, and records the appropriate deposit or LIFO withdrawal internally. Then forwards `accrueInterestAndUpdateScore(user)` to PrimeV2 if set.

```solidity
function xvsUpdated(address user) external
```

**Parameters**

| Name | Type    | Description                  |
| ---- | ------- | ---------------------------- |
| user | address | The user whose stake changed |

**📅 Events**

* Emits DepositRecorded event on deposit
* Emits WithdrawalRecorded event on withdrawal
* Emits DepositsCompacted event if deposits are compacted

**⛔️ Access Requirements**

* Only callable by XVSVault

**❌ Errors**

* Throw OnlyXVSVaultAllowed if caller is not XVSVault
* Throw ZeroAddress if user address is zero

***

#### getEffectiveStake

Get a user's current Prime Score (time-weighted score)

```solidity
function getEffectiveStake(address user) public view returns (uint256 effectiveStake)
```

**Parameters**

| Name | Type    | Description        |
| ---- | ------- | ------------------ |
| user | address | The user's address |

**Return Values**

| Name           | Type    | Description                            |
| -------------- | ------- | -------------------------------------- |
| effectiveStake | uint256 | The user's Prime Score (time-weighted) |

***

#### getEffectiveStakeBatch

Batch view to get Prime Scores for multiple users

```solidity
function getEffectiveStakeBatch(address[] users) external view returns (uint256[] scores)
```

**Parameters**

| Name  | Type       | Description             |
| ----- | ---------- | ----------------------- |
| users | address\[] | Array of user addresses |

**Return Values**

| Name   | Type       | Description           |
| ------ | ---------- | --------------------- |
| scores | uint256\[] | Array of Prime Scores |

***

#### getTotalStaked

Get a user's total staked amount

```solidity
function getTotalStaked(address user) external view returns (uint256)
```

**Parameters**

| Name | Type    | Description        |
| ---- | ------- | ------------------ |
| user | address | The user's address |

**Return Values**

| Name | Type    | Description          |
| ---- | ------- | -------------------- |
| \[0] | uint256 | The total XVS staked |

***

#### getDeposits

Get a user's deposit stack

```solidity
function getDeposits(address user) external view returns (struct IPrimeLeaderboard.Deposit[] deposits)
```

**Parameters**

| Name | Type    | Description        |
| ---- | ------- | ------------------ |
| user | address | The user's address |

**Return Values**

| Name     | Type                                | Description                          |
| -------- | ----------------------------------- | ------------------------------------ |
| deposits | struct IPrimeLeaderboard.Deposit\[] | Array of deposits (index 0 = oldest) |

***

#### getDepositCount

Get the number of deposit tranches for a user

```solidity
function getDepositCount(address user) external view returns (uint256 count)
```

**Parameters**

| Name | Type    | Description        |
| ---- | ------- | ------------------ |
| user | address | The user's address |

**Return Values**

| Name  | Type    | Description                |
| ----- | ------- | -------------------------- |
| count | uint256 | Number of deposit tranches |

***

#### getMultiplier

Calculate the multiplier for a given holding duration

```solidity
function getMultiplier(uint256 holdingDuration) external view returns (uint256 multiplier)
```

**Parameters**

| Name            | Type    | Description         |
| --------------- | ------- | ------------------- |
| holdingDuration | uint256 | Duration in seconds |

**Return Values**

| Name       | Type    | Description                     |
| ---------- | ------- | ------------------------------- |
| multiplier | uint256 | The multiplier (scaled by 1e18) |

***

#### getMultiplierTiers

Get the multiplier tier configuration

```solidity
function getMultiplierTiers() external view returns (uint256[] durations, uint256[] multipliers)
```

**Return Values**

| Name        | Type       | Description                  |
| ----------- | ---------- | ---------------------------- |
| durations   | uint256\[] | Array of duration thresholds |
| multipliers | uint256\[] | Array of multiplier values   |

***

#### setMultiplierTiers

Set the multiplier tiers. Durations and multipliers must be strictly ascending, and each multiplier must be ≥ the base multiplier (1e18).

```solidity
function setMultiplierTiers(uint256[] durations, uint256[] multipliers) external
```

**Parameters**

| Name        | Type       | Description                                 |
| ----------- | ---------- | ------------------------------------------- |
| durations   | uint256\[] | Array of duration thresholds in seconds     |
| multipliers | uint256\[] | Array of multiplier values (scaled by 1e18) |

**📅 Events**

* Emits MultiplierTiersUpdated event

**⛔️ Access Requirements**

* Controlled by ACM

**❌ Errors**

* Throw LengthMismatch if durations and multipliers arrays have different lengths
* Throw InvalidValue if durations array is empty
* Throw InvalidMultiplierTiers if tiers are not in ascending order or multipliers below base

***

#### setPrimeV2

Set the PrimeV2 contract address

```solidity
function setPrimeV2(address primeV2_) external
```

**Parameters**

| Name      | Type    | Description                 |
| --------- | ------- | --------------------------- |
| primeV2\_ | address | Address of PrimeV2 contract |

**📅 Events**

* Emits PrimeV2Set event

**⛔️ Access Requirements**

* Controlled by ACM

**❌ Errors**

* Throw ZeroAddress if address is zero

***

#### setMaxLoopsLimit

Set the limit for the loops that can iterate to avoid DOS

```solidity
function setMaxLoopsLimit(uint256 loopsLimit) external
```

**Parameters**

| Name       | Type    | Description                                        |
| ---------- | ------- | -------------------------------------------------- |
| loopsLimit | uint256 | Limit for the max loops that can execute at a time |

**📅 Events**

* Emits MaxLoopsLimitUpdated event

**⛔️ Access Requirements**

* Controlled by ACM

***


# Prime leaderboard storage

## PrimeLeaderboardStorageV1

Storage layout for the PrimeLeaderboard contract

{% hint style="warning" %}
BNB PrimeLeaderboard only. Upgrade review must include inherited storage and the proxy's exact implementation. Source: [PrimeLeaderboardStorage.sol](https://github.com/VenusProtocol/venus-protocol/blob/v10.3.0/contracts/Tokens/Prime/PrimeLeaderboardStorage.sol).
{% endhint %}

## Solidity API

```solidity
// Individual deposit record for LIFO tracking (declared in IPrimeLeaderboard)
struct Deposit {
  uint128 amount;    // Amount deposited
  uint64 timestamp;  // Deposit timestamp
  uint64 _reserved;  // Reserved for future use
}
```

#### BASE\_MULTIPLIER

Base multiplier (1.0x) scaled by 1e18

```solidity
uint256 BASE_MULTIPLIER
```

***

#### MAX\_DEPOSITS\_PER\_USER

Maximum deposits per user (DoS protection). Constant, set to 30.

```solidity
uint256 MAX_DEPOSITS_PER_USER
```

***

#### EXP\_SCALE

Scaling factor for calculations (1e18)

```solidity
uint256 EXP_SCALE
```

***

#### \_depositStacks

User's deposit stack (LIFO order, index 0 = oldest). Holds the `Deposit` records described above.

```solidity
mapping(address => IPrimeLeaderboard.Deposit[]) _depositStacks
```

***

#### totalStaked

User's total staked XVS

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

***

#### \_multiplierDurations

Multiplier tier duration thresholds, in seconds. Default `[30 days, 60 days, 90 days]`.

```solidity
uint256[] _multiplierDurations
```

***

#### \_multiplierValues

Multiplier values corresponding to each tier (scaled by 1e18). Default `[1.3e18, 1.6e18, 2.0e18]`.

```solidity
uint256[] _multiplierValues
```

***

#### primeV2

Address of the PrimeV2 contract

```solidity
address primeV2
```

***

#### stakersInitialized

Whether staker initialization is complete (prevents further initialization calls)

```solidity
bool stakersInitialized
```

***

#### \_\_gap

Reserved storage for future upgrades.

```solidity
uint256[45] __gap
```

***


# Prime lens

## PrimeLens

PrimeLens is a read-only helper contract for off-chain APR queries against PrimeV2. It is kept separate from PrimeV2 to stay within the EVM contract-size limit. It holds an immutable reference to a PrimeV2 instance and derives values from PrimeV2 and PrimeLiquidityProvider state.

{% hint style="info" %}
BNB PrimeV2 only. The non-proxy lens `0x2f8c5e4562E22DB7908C56Bf99961C053436473c` points to BNB PrimeV2. It is not the lens or ABI for Prime V1 deployments on other networks.
{% endhint %}

## Solidity API

#### primeV2

The PrimeV2 instance this lens reads from (set in the constructor)

```solidity
contract IPrimeV2WithStorage primeV2
```

***

#### constructor

```solidity
constructor(address primeV2_)
```

**Parameters**

| Name      | Type    | Description                     |
| --------- | ------- | ------------------------------- |
| primeV2\_ | address | Address of the PrimeV2 contract |

**❌ Errors**

* Throw InvalidAddress if primeV2\_ is the zero address

***

#### calculateAPR

Returns supply and borrow APR for a user in a given market, based on current state.

```solidity
function calculateAPR(address market, address user) external view returns (struct IPrimeV2.APRInfo aprInfo)
```

**Parameters**

| Name   | Type    | Description                           |
| ------ | ------- | ------------------------------------- |
| market | address | The market for which to fetch the APR |
| user   | address | The account for which to get the APR  |

**Return Values**

| Name    | Type                    | Description                                      |
| ------- | ----------------------- | ------------------------------------------------ |
| aprInfo | struct IPrimeV2.APRInfo | APR information for the user in the given market |

The `APRInfo` struct contains:

```solidity
struct APRInfo {
  uint256 supplyAPR;          // supply APR of the user in BPS
  uint256 borrowAPR;          // borrow APR of the user in BPS
  uint256 totalScore;         // total score of the market
  uint256 userScore;          // score of the user
  uint256 xvsBalanceForScore; // XVS balance of the user used for score
  uint256 capital;            // capital of the user
  uint256 cappedSupply;       // capped supply of the user
  uint256 cappedBorrow;       // capped borrow of the user
  uint256 supplyCapUSD;       // supply cap of the user in USD
  uint256 borrowCapUSD;       // borrow cap of the user in USD
}
```

***

#### estimateAPR

Returns supply and borrow APR for a user in a given market based on hypothetical supply, borrow and XVS staked amounts. The user's score is recomputed from the supplied inputs (subtracting their existing market score and re-adding the estimated one), so the result reflects what the APR would be under the provided position.

```solidity
function estimateAPR(address market, address user, uint256 borrow, uint256 supply, uint256 xvsStaked) external view returns (struct IPrimeV2.APRInfo aprInfo)
```

**Parameters**

| Name      | Type    | Description                           |
| --------- | ------- | ------------------------------------- |
| market    | address | The market for which to fetch the APR |
| user      | address | The account for which to get the APR  |
| borrow    | uint256 | Hypothetical borrow amount            |
| supply    | uint256 | Hypothetical supply amount            |
| xvsStaked | uint256 | Hypothetical staked XVS amount        |

**Return Values**

| Name    | Type                    | Description                                                                                |
| ------- | ----------------------- | ------------------------------------------------------------------------------------------ |
| aprInfo | struct IPrimeV2.APRInfo | APR information for the user in the given market (same `APRInfo` struct as `calculateAPR`) |

***

#### incomeDistributionYearly

The total income that will be distributed to Prime token holders for a market over a year. Computed as the PrimeLiquidityProvider effective distribution speed for the market's underlying multiplied by `blocksOrSecondsPerYear`.

```solidity
function incomeDistributionYearly(address vToken) public view returns (uint256 amount)
```

**Parameters**

| Name   | Type    | Description                                    |
| ------ | ------- | ---------------------------------------------- |
| vToken | address | The market for which to fetch the total income |

**Return Values**

| Name   | Type    | Description                        |
| ------ | ------- | ---------------------------------- |
| amount | uint256 | The annualized distribution income |

***


# Prime liquidity provider

## PrimeLiquidityProvider

PrimeLiquidityProvider is used to fund Prime

PrimeLiquidityProvider deployments can accrue per block or per second and can fund either Prime V1 or PrimeV2. See the [Prime version map](/technical-reference/reference-core-pool/prime) for current consumer, clock, and pause state by network.

{% hint style="warning" %}
Distribution speeds use the deployment's `isTimeBased()` clock. Read `blocksOrSecondsPerYear()` and `paused()` from the exact proxy before annualizing a speed or assuming funds can be released.
{% endhint %}

## Solidity API

#### constructor

Configures the immutable time basis on the implementation.

```solidity
constructor(bool timeBased_, uint256 blocksPerYear_)
```

When `timeBased_` is true, the contract uses seconds and the annualization constant is 31,536,000; otherwise it uses the supplied block count.

***

#### DEFAULT\_MAX\_DISTRIBUTION\_SPEED

The default max token distribution speed

```solidity
uint256 DEFAULT_MAX_DISTRIBUTION_SPEED
```

***

#### prime

Address of the Prime contract

```solidity
address prime
```

***

#### tokenDistributionSpeeds

The rate at which a token is distributed per block or second

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

***

#### maxTokenDistributionSpeeds

The max token distribution speed for token

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

***

#### lastAccruedBlock

The last block or second up to which rewards were accrued for the token (view getter over the `lastAccruedBlockOrSecond` storage mapping)

```solidity
function lastAccruedBlock(address token_) external view returns (uint256)
```

***

#### tokenAmountAccrued

The token accrued but not yet transferred to prime contract (view getter over the internal `_tokenAmountAccrued` mapping)

```solidity
function tokenAmountAccrued(address token_) external view returns (uint256)
```

***

#### initialize

PrimeLiquidityProvider initializer

```solidity
function initialize(address accessControlManager_, address[] tokens_, uint256[] distributionSpeeds_, uint256[] maxDistributionSpeeds_, uint256 loopsLimit_) external
```

**Parameters**

| Name                    | Type       | Description                                             |
| ----------------------- | ---------- | ------------------------------------------------------- |
| accessControlManager\_  | address    | AccessControlManager contract address                   |
| tokens\_                | address\[] | Array of addresses of the tokens                        |
| distributionSpeeds\_    | uint256\[] | New distribution speeds for tokens                      |
| maxDistributionSpeeds\_ | uint256\[] |                                                         |
| loopsLimit\_            | uint256    | Maximum number of loops allowed in a single transaction |

**❌ Errors**

* Throw InvalidArguments on different length of tokens and speeds array

***

#### initializeTokens

Initialize the distribution of the token

```solidity
function initializeTokens(address[] tokens_) external
```

**Parameters**

| Name     | Type       | Description                                       |
| -------- | ---------- | ------------------------------------------------- |
| tokens\_ | address\[] | Array of addresses of the tokens to be intialized |

**⛔️ Access Requirements**

* Only Governance

***

#### pauseFundsTransfer

Pause fund transfer of tokens to Prime contract

```solidity
function pauseFundsTransfer() external
```

**⛔️ Access Requirements**

* Controlled by ACM

***

#### resumeFundsTransfer

Resume fund transfer of tokens to Prime contract

```solidity
function resumeFundsTransfer() external
```

**⛔️ Access Requirements**

* Controlled by ACM

***

#### setTokensDistributionSpeed

Set distribution speed per block or second

```solidity
function setTokensDistributionSpeed(address[] tokens_, uint256[] distributionSpeeds_) external
```

**Parameters**

| Name                 | Type       | Description                        |
| -------------------- | ---------- | ---------------------------------- |
| tokens\_             | address\[] | Array of addresses of the tokens   |
| distributionSpeeds\_ | uint256\[] | New distribution speeds for tokens |

**⛔️ Access Requirements**

* Controlled by ACM

**❌ Errors**

* Throw InvalidArguments on different length of tokens and speeds array

***

#### setMaxTokensDistributionSpeed

Set maximum distribution speed per block or second

```solidity
function setMaxTokensDistributionSpeed(address[] tokens_, uint256[] maxDistributionSpeeds_) external
```

**Parameters**

| Name                    | Type       | Description                        |
| ----------------------- | ---------- | ---------------------------------- |
| tokens\_                | address\[] | Array of addresses of the tokens   |
| maxDistributionSpeeds\_ | uint256\[] | New distribution speeds for tokens |

**⛔️ Access Requirements**

* Controlled by ACM

**❌ Errors**

* Throw InvalidArguments on different length of tokens and speeds array

***

#### setPrimeToken

Set the prime token contract address

```solidity
function setPrimeToken(address prime_) external
```

**Parameters**

| Name    | Type    | Description                                 |
| ------- | ------- | ------------------------------------------- |
| prime\_ | address | The new address of the prime token contract |

**📅 Events**

* Emits PrimeTokenUpdated event

**⛔️ Access Requirements**

* Only owner

***

#### setMaxLoopsLimit

Set the limit for the loops can iterate to avoid the DOS

```solidity
function setMaxLoopsLimit(uint256 loopsLimit) external
```

**Parameters**

| Name       | Type    | Description                                   |
| ---------- | ------- | --------------------------------------------- |
| loopsLimit | uint256 | Limit for the max loops can execute at a time |

**📅 Events**

* Emits MaxLoopsLimitUpdated event on success

**⛔️ Access Requirements**

* Controlled by ACM

***

#### releaseFunds

Release the token amount accrued through the latest block or second

```solidity
function releaseFunds(address token_) external
```

**Parameters**

| Name    | Type    | Description                                |
| ------- | ------- | ------------------------------------------ |
| token\_ | address | The token to release to the Prime contract |

**📅 Events**

* Emits TokenTransferredToPrime event

**❌ Errors**

* Throw InvalidArguments on Zero address(token)
* Throw FundsTransferIsPaused is paused
* Throw InvalidCaller if the sender is not the Prime contract

***

#### sweepToken

A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to user

```solidity
function sweepToken(contract IERC20Upgradeable token_, address to_, uint256 amount_) external
```

**Parameters**

| Name     | Type                       | Description                              |
| -------- | -------------------------- | ---------------------------------------- |
| token\_  | contract IERC20Upgradeable | The address of the ERC-20 token to sweep |
| to\_     | address                    | The address of the recipient             |
| amount\_ | uint256                    | The amount of tokens needs to transfer   |

**📅 Events**

* Emits SweepToken event

**⛔️ Access Requirements**

* Only Governance

**❌ Errors**

* Throw InsufficientBalance if amount\_ is greater than the available balance of the token in the contract

***

#### getEffectiveDistributionSpeed

Get the effective reward speed per block or second for a token

```solidity
function getEffectiveDistributionSpeed(address token_) external view returns (uint256)
```

**Parameters**

| Name    | Type    | Description          |
| ------- | ------- | -------------------- |
| token\_ | address | Address of the token |

**Return Values**

| Name | Type    | Description                                                     |
| ---- | ------- | --------------------------------------------------------------- |
| \[0] | uint256 | Effective reward speed in the deployment's configured time unit |

***

#### accrueTokens

Accrue token by updating the distribution state

```solidity
function accrueTokens(address token_) public
```

**Parameters**

| Name    | Type    | Description          |
| ------- | ------- | -------------------- |
| token\_ | address | Address of the token |

**📅 Events**

* Emits TokensAccrued event

***

#### getBlockNumberOrTimestamp

Get the current block number (block-based chains) or timestamp (time-based chains). Inherited from `TimeManagerV8`.

```solidity
function getBlockNumberOrTimestamp() public view virtual returns (uint256)
```

**Return Values**

| Name | Type    | Description                                                       |
| ---- | ------- | ----------------------------------------------------------------- |
| \[0] | uint256 | blockNumberOrSecond returns the current block number or timestamp |

***


# Prime storage

## PrimeV2StorageV1

Storage layout for the PrimeV2 contract with leaderboard-based Prime token distribution

{% hint style="warning" %}
BNB PrimeV2 only. This is the direct `PrimeV2StorageV1` layout from the implementation used at block `118,364,540`; upgrade review must also include inherited storage and the proxy's exact implementation. Source: [PrimeV2Storage.sol](https://github.com/VenusProtocol/venus-protocol/blob/v10.3.0/contracts/Tokens/Prime/PrimeV2Storage.sol).
{% endhint %}

## Solidity API

```solidity
struct Market {
  uint256 supplyMultiplier;
  uint256 borrowMultiplier;
  uint256 rewardIndex;
  uint256 sumOfMembersScore;
  bool exists;
}
```

```solidity
struct Interest {
  uint256 accrued;          // Accrued rewards pending claim (reset to 0 on claim)
  uint256 score;            // User's score in this market
  uint256 rewardIndex;      // Last recorded reward index
  uint256 lifetimeAccrued;  // Monotonic running total of all rewards ever accrued to this
                            // user in this market; never decremented on claim. Lets the
                            // off-chain cycle pipeline compute per-cycle earnings as the
                            // diff between two snapshots.
}
```

```solidity
struct PendingReward {
  address vToken;
  address rewardToken;
  uint256 amount;
}
```

#### isPrimeHolder

Whether a user holds a Prime token

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

***

#### totalTokens

Total count of Prime tokens

```solidity
uint256 totalTokens
```

***

#### tokenLimit

Maximum number of Prime tokens allowed

```solidity
uint256 tokenLimit
```

***

#### markets

Mapping of vToken to its market configuration

```solidity
mapping(address => struct PrimeV2StorageV1.Market) markets
```

***

#### interests

Mapping of vToken -> user -> interest info

```solidity
mapping(address => mapping(address => struct PrimeV2StorageV1.Interest)) interests
```

***

#### \_allMarkets

Internal array of all Prime-participating markets. This field is stored between `interests` and `vTokenForAsset`.

```solidity
address[] _allMarkets
```

***

#### vTokenForAsset

Mapping of underlying token to vToken address (used to check if an asset is part of Prime markets)

```solidity
mapping(address => address) vTokenForAsset
```

***

#### alphaNumerator

numerator of alpha. Ex: if alpha is 0.5 then this will be 1

```solidity
uint128 alphaNumerator
```

***

#### alphaDenominator

denominator of alpha. Ex: if alpha is 0.5 then this will be 2

```solidity
uint128 alphaDenominator
```

***

#### primeLiquidityProvider

The address of the PrimeLiquidityProvider contract

```solidity
address primeLiquidityProvider
```

***

#### oracle

The address of the ResilientOracle contract

```solidity
contract ResilientOracleInterface oracle
```

***

#### corePoolComptroller

Address of the core pool comptroller contract

```solidity
address corePoolComptroller
```

***

#### unreleasedPLPIncome

Unreleased income from the PLP per token that has already been accounted for — either indexed into `rewardIndex` for Prime holders, or recorded in `undistributedReward` while the market had no scored members

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

***

#### undistributedReward

Income accrued while no scored members existed in the market. Tracked per underlying so governance can reclaim the slice via `sweepUndistributed` without touching user-owed funds.

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

***

#### isScoreUpdated

Mapping to check if an account's score was updated in a round

```solidity
mapping(uint256 => mapping(address => bool)) isScoreUpdated
```

***

#### nextScoreUpdateRoundId

Current score update round ID

```solidity
uint256 nextScoreUpdateRoundId
```

***

#### pendingScoreUpdates

Number of pending score updates in the current round

```solidity
uint256 pendingScoreUpdates
```

***

#### primeLeaderboard

Address of the PrimeLeaderboard contract for permissionless eligibility checks

```solidity
address primeLeaderboard
```

***

#### mintThreshold

Minimum Prime Score required for permissionless Prime minting

```solidity
uint256 mintThreshold
```

***

#### mintDeadline

Unix timestamp after which permissionless minting is closed (0 = no deadline)

```solidity
uint256 mintDeadline
```

***

#### \_\_gap

Reserved storage for future upgrades.

```solidity
uint256[40] __gap
```

***


# Vaults

Venus has two distinct vault families:

| Vault                                                            | Network scope                                                    | Proxy model                                         | Purpose                                                           |
| ---------------------------------------------------------------- | ---------------------------------------------------------------- | --------------------------------------------------- | ----------------------------------------------------------------- |
| [VAI Vault](/technical-reference/reference-core-pool/vaults/vai) | BNB Chain only                                                   | Custom `VAIVaultProxy` delegate proxy               | Stake VAI and claim XVS rewards                                   |
| [XVS Vault](/technical-reference/reference-core-pool/vaults/xvs) | BNB, Ethereum, opBNB, Arbitrum, ZKsync, Optimism, Base, Unichain | Custom `XVSVaultProxy` delegate proxy plus XVSStore | Stake XVS, earn configured rewards, and delegate governance votes |

The proxy address is the public entry point. Resolve the custom proxy's implementation getter at the relevant block before choosing an ABI. Vault reward speed, pause state, time basis, pools, permissions, and funding are all configurable.

Current proxy and store addresses are listed in [Deployed Funds](/deployed-contracts/funds).


# XVS

The XVS Vault is a multi-chain custom delegate-proxy family. Each deployment has a separate implementation, time basis, pause state, pool configuration, XVSStore, and permission set.

## Mainnet deployments

State observed on 2026-08-27:

| Network   | Proxy                                        | Implementation                               | Reward/vote clock       | Paused |
| --------- | -------------------------------------------- | -------------------------------------------- | ----------------------- | ------ |
| BNB Chain | `0x051100480289e704d20e9DB4804837068f3f9204` | `0x74c8a97BE672db3e9a224648bE566AdA5F43B378` | block, 70,080,000/year  | No     |
| Ethereum  | `0xA0882C2D5DF29233A092d2887A258C2b90e9b994` | `0x437042777255A1f25BE60eD25C814Dea6E43bC28` | block, 2,628,000/year   | No     |
| opBNB     | `0x7dc969122450749A8B0777c0e324522d67737988` | `0x785BEF8B6dB40E86fA3749b44cD67C14945E2a71` | block, 126,144,000/year | Yes    |
| Arbitrum  | `0x8b79692AAB2822Be30a6382Eb04763A74752d5B4` | `0x4C4BedC003e4E2f3A057DeC35aeF26F64Cb07384` | second, 31,536,000/year | No     |
| ZKsync    | `0xbbB3C88192a5B0DB759229BeF49DcD1f168F326F` | `0x513323f8bd847Bd4C7C73DD69098B38789Ae0590` | second, 31,536,000/year | No     |
| Optimism  | `0x133120607C018c949E91AE333785519F6d947e01` | `0x8B8651EEB002a7991F2287500B17a395E8cfe7d9` | second, 31,536,000/year | No     |
| Base      | `0x708B54F2C3f3606ea48a8d94dab88D9Ab22D7fCd` | `0x322F1a2E03F089F8ce510855e793970D6f0EFcF9` | second, 31,536,000/year | No     |
| Unichain  | `0x5ECa0FBBc5e7bf49dbFb1953a92784F8e4248eF6` | `0x2ba0F45f7368d2A56d0c9e5a29af363987BE1d02` | second, 31,536,000/year | No     |

Observed blocks were BNB `118,364,446`, Ethereum `25,845,840`, opBNB `178,832,524`, Arbitrum `498,882,806`, ZKsync `71,738,207`, Optimism `156,113,142`, Base `50,517,857`, and Unichain `57,076,698`.

{% hint style="warning" %}
A paused vault is not automatically deprecated. Pause status only establishes whether pause-gated actions could execute at the observed block. Check balances, pending withdrawals, claimable rewards, governance duties, and formal support before assigning a lifecycle state.
{% endhint %}

## Components

* [XVSVaultProxy](/technical-reference/reference-core-pool/vaults/xvs/xvs-vault-proxy) — stores state and delegates calls to the current implementation.
* [XVS Vault](/technical-reference/reference-core-pool/vaults/xvs/xvs-vault) — staking, withdrawals, rewards, governance delegation, and Prime callback logic.
* [XVSStore](/technical-reference/reference-core-pool/vaults/xvs/xvs-store) — custody contract from which the vault transfers configured rewards.
* [XVSVaultTreasury](/technical-reference/reference-core-pool/vaults/xvs/xvs-vault-treasury) — funds the store with governance-approved XVS allocations.

Current proxy and store addresses are listed in [Deployed Funds](/deployed-contracts/funds).


# XVSVault

The XVS Vault lets users stake configured tokens, accrue rewards, request and execute withdrawals, and delegate voting power. Deployments can be block-based or time-based; use the [mainnet version map](/technical-reference/reference-core-pool/vaults/xvs) to select the correct units and implementation.

{% hint style="danger" %}
Call the chain's proxy, not the implementation. Verify `implementation()`, `vaultPaused()`, `isTimeBased()`, `blocksOrSecondsPerYear()`, pool configuration, reward funding, and live permissions before constructing a transaction.
{% endhint %}

## User staking and rewards

```solidity
function deposit(address rewardToken, uint256 pid, uint256 amount) external
function claim(address account, address rewardToken, uint256 pid) external
function requestWithdrawal(address rewardToken, uint256 pid, uint256 amount) external
function executeWithdrawal(address rewardToken, uint256 pid) external
function pendingReward(address rewardToken, uint256 pid, address user) external view returns (uint256)
function getEligibleWithdrawalAmount(address rewardToken, uint256 pid, address user) external view returns (uint256)
function getRequestedAmount(address rewardToken, uint256 pid, address user) external view returns (uint256)
function getWithdrawalRequests(address rewardToken, uint256 pid, address user) external view returns (WithdrawalRequest[])
```

The user must approve the vault to transfer the staked token before depositing. A withdrawal has two stages: request it, wait until the pool's locking period has elapsed, then execute it. `claim(address,...)` pays the named account.

If the XVSStore cannot fully pay a reward, the vault transfers the available amount and records the remainder in `pendingRewardTransfers` rather than treating the entire claim as paid.

## Reward clock and configuration

The canonical speed setter and getter use block-or-second terminology:

```solidity
function setRewardAmountPerBlockOrSecond(address rewardToken, uint256 rewardAmount) external
function rewardTokenAmountsPerBlockOrSecond(address rewardToken) external view returns (uint256)
function isTimeBased() external view returns (bool)
function blocksOrSecondsPerYear() external view returns (uint256)
```

The old `rewardTokenAmountsPerBlock(address)` compatibility getter returns the same speed mapping, but its name does not describe time-based deployments. The previously documented `setRewardAmountPerBlock(address,uint256)` selector is not part of the current implementation; operator tooling must use `setRewardAmountPerBlockOrSecond` after verifying the deployed ABI.

Pool addition, allocation updates, speed changes, withdrawal-lock changes, pause/resume, and `setBlocksPerYear` use signature-specific Access Control Manager checks. `setXvsStore`, `setPrimeToken`, `initializeTimeManager`, and `setAccessControl` are restricted to the proxy admin.

## Governance delegation

```solidity
function delegate(address delegatee) external
function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external
function delegates(address account) external view returns (address)
function getCurrentVotes(address account) external view returns (uint96)
function getPriorVotes(address account, uint256 blockNumberOrSecond) external view returns (uint96)
```

Voting checkpoints use the deployment's configured clock. On time-based chains, the historical parameter is a timestamp; on block-based chains, it is a block number. Governance proposal voting on BNB Chain uses the BNB vault snapshots.

## Prime integration

The vault can call the configured Prime token when an eligible XVS stake changes. Prime generation and support differ by network; see [Prime Contract Versions](/technical-reference/reference-core-pool/prime). Do not assume the BNB PrimeV2 leaderboard callback exists on a Prime V1 deployment.

Source: [XVSVault.sol in venus-protocol v10.3.0](https://github.com/VenusProtocol/venus-protocol/blob/v10.3.0/contracts/XVSVault/XVSVault.sol).


# XVSVaultProxy

`XVSVaultProxy` is a custom delegate proxy used on every XVS Vault deployment. Users and integrations call the proxy; state is stored at the proxy while `implementation()` supplies the logic.

The constructor sets the initial admin. The current admin can call `_setPendingImplementation(address)` and `_setPendingAdmin(address)`. A pending implementation or admin accepts through `_acceptImplementation()` or `_acceptAdmin()`.

These functions return protocol error codes: `0` means success. Decode the return value and emitted events instead of relying only on EVM transaction success.

{% hint style="warning" %}
Implementation and admin are chain-specific. Resolve `implementation()`, `admin()`, pending values, pause state, and time mode on the exact proxy. See the [mainnet XVS Vault version map](/technical-reference/reference-core-pool/vaults/xvs).
{% endhint %}

Source: [XVSVaultProxy.sol in venus-protocol v10.3.0](https://github.com/VenusProtocol/venus-protocol/blob/v10.3.0/contracts/XVSVault/XVSVaultProxy.sol).


# XVSStore

XVSStore holds reward tokens and transfers them on behalf of its configured owner, normally an XVS Vault. It is a non-proxy contract with separate `admin` and `owner` roles.

{% hint style="warning" %}
Every network has its own store, roles, balances, and active reward-token list. Read those values from the exact address in [Deployed Funds](/deployed-contracts/funds).
{% endhint %}

## Roles and API

| Function                                      | Caller requirement | Effect                                                                    |
| --------------------------------------------- | ------------------ | ------------------------------------------------------------------------- |
| `safeRewardTransfer(address,address,uint256)` | Owner              | Transfers an active reward token, capped by the store's available balance |
| `setPendingAdmin(address)`                    | Admin              | Starts an admin transfer                                                  |
| `acceptAdmin()`                               | Pending admin      | Accepts the admin role                                                    |
| `setNewOwner(address)`                        | Admin              | Replaces the owner that can distribute or withdraw rewards                |
| `setRewardToken(address,bool)`                | Admin **or owner** | Enables or disables a reward token                                        |
| `emergencyRewardWithdraw(address,uint256)`    | Owner              | Withdraws a reward token from the store                                   |

The owner can therefore both distribute active rewards and change which tokens are active. The admin can replace the owner. Permission reviews must account for both roles rather than treating the vault as the only privileged actor.

Source: [XVSStore.sol in venus-protocol v10.3.0](https://github.com/VenusProtocol/venus-protocol/blob/v10.3.0/contracts/XVSVault/XVSStore.sol).


# XVSVaultTreasury

`XVSVaultTreasury` receives XVS allocated by the protocol-reserve conversion flow and funds the XVSStore used by an XVS Vault.

## BNB deployment

At BNB Chain block `118,364,540`:

| Field                  | Value                                        |
| ---------------------- | -------------------------------------------- |
| Proxy                  | `0x269ff7818DB317f60E386D2be0B259e1a324a40a` |
| Implementation         | `0xA95a4f34337d8fAC283c3e3D2A605b95dA916cD6` |
| Owner                  | `0x939bD8d64c0A9583A7Dcea9933f7b21697ab6396` |
| Access Control Manager | `0x4788629ABc6cFCA10F9f969efdEAa1cF70c23555` |
| XVS Vault              | `0x051100480289e704d20e9DB4804837068f3f9204` |
| XVS                    | `0xcF6BB5389c92Bdda8a3747Ddb454cB7a64626C63` |

Other network deployments can use different proxies, implementations, owners, and vaults.

## API and permissions

```solidity
constructor(address xvsAddress_)
function initialize(address accessControlManager_, address xvsVault_) public
function setXVSVault(address xvsVault_) external
function fundXVSVault(uint256 amountMantissa) external
function sweepToken(address tokenAddress, address to, uint256 amount) external
```

* `setXVSVault` is `onlyOwner`.
* `fundXVSVault` checks `fundXVSVault(uint256)` through the Access Control Manager and sends XVS directly to the vault's current XVSStore.
* `sweepToken` is `onlyOwner`; it is **not** gated by the Access Control Manager.

Because this contract holds funds and can change its destination vault, resolve the live proxy implementation, owner, ACM permissions, and `xvsVault()` value before an operator transaction.

Source: [XVSVaultTreasury.sol](https://github.com/VenusProtocol/protocol-reserve/blob/main/contracts/ProtocolReserve/XVSVaultTreasury.sol).


# VAI

The VAI Vault exists only on BNB Chain. Its public entry point is the custom proxy at [`0x0667Eed0a0aAb930af74a3dfeDD263A73994f216`](https://bscscan.com/address/0x0667Eed0a0aAb930af74a3dfeDD263A73994f216).

At BNB Chain block `118,364,540`, the proxy was unpaused, used implementation `0xA52f2a56aBb7cbDD378bC36c6088fAfEaf9AC423`, and was administered by the Normal Timelock `0x939bD8d64c0A9583A7Dcea9933f7b21697ab6396`.

The vault still holds user VAI and reward XVS, so its user and recovery reference must remain available regardless of whether new deposits are promoted in the interface.

* [VAIVaultProxy administration](/technical-reference/reference-core-pool/vaults/vai/vai-vault-proxy)
* [VAI Vault user and operator API](/technical-reference/reference-core-pool/vaults/vai/vai-vault)
* [Deployed Funds](/deployed-contracts/funds)


# VAIVault

This reference applies to the BNB Chain VAI Vault proxy `0x0667Eed0a0aAb930af74a3dfeDD263A73994f216`, which used implementation `0xA52f2a56aBb7cbDD378bC36c6088fAfEaf9AC423` at block `118,364,540`.

{% hint style="warning" %}
Call the proxy, not the implementation. Verify `vaiVaultImplementation()`, `vaultPaused()`, token addresses, reward funding, and live permissions at the block relevant to the transaction.
{% endhint %}

## User functions

```solidity
function deposit(uint256 amount) external
function withdraw(uint256 amount) external
function claim() external
function claim(address account) external
function pendingXVS(address user) public view returns (uint256)
function updatePendingRewards() public
```

`deposit` transfers VAI from the caller and therefore requires an underlying VAI allowance. `withdraw` returns staked VAI. `claim(address)` pays the named account; it does not redirect that account's rewards to the caller.

The vault can record rewards that have not yet been distributed. Check both `pendingXVS` and current reward funding before presenting a claim as guaranteed.

## Administration

* `pause()` and `resume()` are signature-gated through the Access Control Manager.
* `_become(VAIVaultProxy)` can accept an implementation only when called by the proxy admin.
* `setAccessControl(address)` is restricted to the proxy admin.
* `setVenusInfo(address xvs, address vai)` updates the token references, in that order, and is restricted to the proxy admin.

Permission assignments and token addresses can change. Query the proxy and ACM instead of relying on a static caller label.

Source: [VAIVault.sol in venus-protocol v10.3.0](https://github.com/VenusProtocol/venus-protocol/blob/v10.3.0/contracts/VAIVault/VAIVault.sol).


# VAIVaultProxy

`VAIVaultProxy` is a custom delegate proxy. Users and integrations call the proxy; state is stored at the proxy while the implementation supplies logic.

## BNB deployment

At block `118,364,540`:

| Field                             | Address                                      |
| --------------------------------- | -------------------------------------------- |
| Proxy                             | `0x0667Eed0a0aAb930af74a3dfeDD263A73994f216` |
| `vaiVaultImplementation()`        | `0xA52f2a56aBb7cbDD378bC36c6088fAfEaf9AC423` |
| `admin()`                         | `0x939bD8d64c0A9583A7Dcea9933f7b21697ab6396` |
| `pendingVAIVaultImplementation()` | Read live before an upgrade                  |
| `pendingAdmin()`                  | Read live before an admin transfer           |

The constructor initially sets `admin` to the deployer. The current admin can call `_setPendingImplementation(address)` and `_setPendingAdmin(address)`. A pending implementation or admin must accept its role through `_acceptImplementation()` or `_acceptAdmin()` respectively.

These methods return protocol error codes: `0` means success. Decode the return value and emitted events instead of relying only on EVM transaction success.

Source: [VAIVaultProxy.sol in venus-protocol v10.3.0](https://github.com/VenusProtocol/venus-protocol/blob/v10.3.0/contracts/VAIVault/VAIVaultProxy.sol).


# InterestRateModels

Each BNB Core market stores its own interest-rate-model address. Model families and annualization constants have changed over time, so the market address and block determine the applicable implementation.

## Model selection

1. Read `interestRateModel()` from the vToken.
2. Identify whether the returned address is a model or a `CheckpointView` wrapper.
3. For a wrapper, resolve the active target at the relevant checkpoint.
4. Read `blocksPerYear` or `BLOCKS_PER_YEAR` from the effective model before annualizing a per-block rate.
5. Use the exact deployed ABI and parameters; do not infer them from the model family name.

At BNB Chain block `118,364,540`, the active vUSDC and vUSDT markets both pointed to `CheckpointView` `0x2CF0e211c99dFd28892cf80D142aA27a9042Dbf4`, whose active TwoKinks target exposed `BLOCKS_PER_YEAR = 70,080,000`.

{% hint style="warning" %}
Checkpoint wrappers can switch their underlying model at a configured timestamp while keeping the same address in the vToken. Historical rate reconstruction must resolve the target for the historical block, not only inspect the wrapper's current result.
{% endhint %}

## Model families

* [JumpRateModel](/technical-reference/reference-core-pool/interestratemodels/jumpmodel) — one kink and a jump slope.
* [TwoKinksInterestRateModel](/technical-reference/reference-core-pool/interestratemodels/twokinksinterestratemodel) — three curve segments separated by two kinks.
* [WhitePaperInterestRateModel](/technical-reference/reference-core-pool/interestratemodels/whitepapermodel) — older linear model; some unlisted markets retain balances or debt, so its legacy reference must remain available.

The previously published [InterestRateModelLens page](/technical-reference/reference-core-pool/interestratemodels/interestratemodellens) has no resolved canonical source or deployment and must not be treated as a supported integration address.

Current market addresses are listed in [Deployed Markets](/deployed-contracts/markets). For interest, exchange-rate, and APR/APY equations, see [Protocol Math](/guides/protocol-math).


# JumpModel

## JumpRateModel

This page describes the BNB Core `JumpRateModel` source in venus-protocol v10.3.0. A market can point to this model directly or through a `CheckpointView` wrapper.

{% hint style="warning" %}
Resolve `interestRateModel()` on the target vToken and read the effective model's `blocksPerYear` before annualizing. Model address, parameters, and annualization basis are market- and block-specific.
{% endhint %}

## Solidity API

#### blocksPerYear

Approximate blocks per year used to convert annual constructor parameters to per-block rates.

```solidity
uint256 blocksPerYear
```

***

#### constructor

Construct an interest rate model

```solidity
constructor(uint256 baseRatePerYear, uint256 multiplierPerYear, uint256 jumpMultiplierPerYear, uint256 kink_, uint256 blocksPerYear_) public
```

**Parameters**

| Name                  | Type    | Description                                                            |
| --------------------- | ------- | ---------------------------------------------------------------------- |
| baseRatePerYear       | uint256 | The approximate target base APR, as a mantissa (scaled by 1e18)        |
| multiplierPerYear     | uint256 | The rate of increase in interest rate wrt utilization (scaled by 1e18) |
| jumpMultiplierPerYear | uint256 | The multiplierPerBlock after hitting a specified utilization point     |
| kink\_                | uint256 | The utilization point at which the jump multiplier is applied          |
| blocksPerYear\_       | uint256 | The approximate number of blocks per year assumed by this model        |

***

#### utilizationRate

Calculates the utilization rate of the market: `borrows / (cash + borrows - reserves)`

```solidity
function utilizationRate(uint256 cash, uint256 borrows, uint256 reserves) public pure returns (uint256)
```

**Parameters**

| Name     | Type    | Description                                             |
| -------- | ------- | ------------------------------------------------------- |
| cash     | uint256 | The amount of cash in the market                        |
| borrows  | uint256 | The amount of borrows in the market                     |
| reserves | uint256 | The amount of reserves in the market (currently unused) |

**Return Values**

| Name | Type    | Description                                           |
| ---- | ------- | ----------------------------------------------------- |
| \[0] | uint256 | The utilization rate as a mantissa between \[0, 1e18] |

***

#### getBorrowRate

Calculates the current borrow rate per block, with the error code expected by the market

```solidity
function getBorrowRate(uint256 cash, uint256 borrows, uint256 reserves) public view returns (uint256)
```

**Parameters**

| Name     | Type    | Description                          |
| -------- | ------- | ------------------------------------ |
| cash     | uint256 | The amount of cash in the market     |
| borrows  | uint256 | The amount of borrows in the market  |
| reserves | uint256 | The amount of reserves in the market |

**Return Values**

| Name | Type    | Description                                                         |
| ---- | ------- | ------------------------------------------------------------------- |
| \[0] | uint256 | The borrow rate percentage per block as a mantissa (scaled by 1e18) |

***

#### getSupplyRate

Calculates the current supply rate per block

```solidity
function getSupplyRate(uint256 cash, uint256 borrows, uint256 reserves, uint256 reserveFactorMantissa) public view returns (uint256)
```

**Parameters**

| Name                  | Type    | Description                               |
| --------------------- | ------- | ----------------------------------------- |
| cash                  | uint256 | The amount of cash in the market          |
| borrows               | uint256 | The amount of borrows in the market       |
| reserves              | uint256 | The amount of reserves in the market      |
| reserveFactorMantissa | uint256 | The current reserve factor for the market |

**Return Values**

| Name | Type    | Description                                                         |
| ---- | ------- | ------------------------------------------------------------------- |
| \[0] | uint256 | The supply rate percentage per block as a mantissa (scaled by 1e18) |

***


# TwoKinksInterestRateModel

## TwoKinksInterestRateModel

An interest rate model with two different slope increase or decrease each after a certain utilization threshold called **kink** is reached.

{% hint style="warning" %}
BNB Core model reference. Resolve the vToken's live model and any `CheckpointView` wrapper before using this ABI. Read `BLOCKS_PER_YEAR` from the effective target; the value is not a universal BNB constant.
{% endhint %}

## Solidity API

#### BLOCKS\_PER\_YEAR

Approximate blocks per year used to convert annual constructor parameters to per-block rates.

```solidity
int256 BLOCKS_PER_YEAR
```

***

#### MULTIPLIER\_PER\_BLOCK

The multiplier of utilization rate per block that gives the slope 1 of the interest rate scaled by EXP\_SCALE

```solidity
int256 MULTIPLIER_PER_BLOCK
```

***

#### BASE\_RATE\_PER\_BLOCK

The base interest rate per block which is the y-intercept when utilization rate is 0 scaled by EXP\_SCALE

```solidity
int256 BASE_RATE_PER_BLOCK
```

***

#### KINK\_1

The utilization point at which the multiplier2 is applied

```solidity
int256 KINK_1
```

***

#### MULTIPLIER\_2\_PER\_BLOCK

The multiplier of utilization rate per block that gives the slope 2 of the interest rate scaled by EXP\_SCALE

```solidity
int256 MULTIPLIER_2_PER_BLOCK
```

***

#### BASE\_RATE\_2\_PER\_BLOCK

The base interest rate per block which is the y-intercept when utilization rate hits KINK\_1 scaled by EXP\_SCALE

```solidity
int256 BASE_RATE_2_PER_BLOCK
```

***

#### RATE\_1

The maximum kink interest rate scaled by EXP\_SCALE

```solidity
int256 RATE_1
```

***

#### KINK\_2

The utilization point at which the jump multiplier is applied

```solidity
int256 KINK_2
```

***

#### JUMP\_MULTIPLIER\_PER\_BLOCK

The multiplier of utilization rate per block that gives the slope 3 of interest rate scaled by EXP\_SCALE

```solidity
int256 JUMP_MULTIPLIER_PER_BLOCK
```

***

#### RATE\_2

The maximum kink interest rate scaled by EXP\_SCALE

```solidity
int256 RATE_2
```

***

#### constructor

Construct an interest rate model

```solidity
constructor(int256 baseRatePerYear_, int256 multiplierPerYear_, int256 kink1_, int256 multiplier2PerYear_, int256 baseRate2PerYear_, int256 kink2_, int256 jumpMultiplierPerYear_, int256 blocksPerYear_) public
```

**Parameters**

| Name                    | Type   | Description                                                                                                    |
| ----------------------- | ------ | -------------------------------------------------------------------------------------------------------------- |
| baseRatePerYear\_       | int256 | The approximate target base APR, as a mantissa (scaled by EXP\_SCALE)                                          |
| multiplierPerYear\_     | int256 | The rate of increase or decrease in interest rate wrt utilization (scaled by EXP\_SCALE)                       |
| kink1\_                 | int256 | The utilization point at which the multiplier2 is applied                                                      |
| multiplier2PerYear\_    | int256 | The rate of increase or decrease in interest rate wrt utilization after hitting KINK\_1 (scaled by EXP\_SCALE) |
| baseRate2PerYear\_      | int256 | The additional base APR after hitting KINK\_1, as a mantissa (scaled by EXP\_SCALE)                            |
| kink2\_                 | int256 | The utilization point at which the jump multiplier is applied                                                  |
| jumpMultiplierPerYear\_ | int256 | The multiplier after hitting KINK\_2                                                                           |
| blocksPerYear\_         | int256 | The approximate number of blocks per year assumed by this model                                                |

***

#### getBorrowRate

Calculates the current borrow rate per slot (block)

```solidity
function getBorrowRate(uint256 cash, uint256 borrows, uint256 reserves) external view returns (uint256)
```

**Parameters**

| Name     | Type    | Description                          |
| -------- | ------- | ------------------------------------ |
| cash     | uint256 | The amount of cash in the market     |
| borrows  | uint256 | The amount of borrows in the market  |
| reserves | uint256 | The amount of reserves in the market |

**Return Values**

| Name | Type    | Description                                                                      |
| ---- | ------- | -------------------------------------------------------------------------------- |
| \[0] | uint256 | The borrow rate percentage per slot (block) as a mantissa (scaled by EXP\_SCALE) |

***

#### getSupplyRate

Calculates the current supply rate per slot (block)

```solidity
function getSupplyRate(uint256 cash, uint256 borrows, uint256 reserves, uint256 reserveFactorMantissa) public view virtual returns (uint256)
```

**Parameters**

| Name                  | Type    | Description                               |
| --------------------- | ------- | ----------------------------------------- |
| cash                  | uint256 | The amount of cash in the market          |
| borrows               | uint256 | The amount of borrows in the market       |
| reserves              | uint256 | The amount of reserves in the market      |
| reserveFactorMantissa | uint256 | The current reserve factor for the market |

**Return Values**

| Name | Type    | Description                                                                      |
| ---- | ------- | -------------------------------------------------------------------------------- |
| \[0] | uint256 | The supply rate percentage per slot (block) as a mantissa (scaled by EXP\_SCALE) |

***

#### utilizationRate

Calculates the utilization rate of the market: `borrows / (cash + borrows - reserves)`

```solidity
function utilizationRate(uint256 cash, uint256 borrows, uint256 reserves) public pure returns (uint256)
```

**Parameters**

| Name     | Type    | Description                          |
| -------- | ------- | ------------------------------------ |
| cash     | uint256 | The amount of cash in the market     |
| borrows  | uint256 | The amount of borrows in the market  |
| reserves | uint256 | The amount of reserves in the market |

**Return Values**

| Name | Type    | Description                                                 |
| ---- | ------- | ----------------------------------------------------------- |
| \[0] | uint256 | The utilization rate as a mantissa between \[0, EXP\_SCALE] |

***


# WhitePaperModel

## WhitePaperInterestRateModel

The parameterized model described in section 2.4 of the original Venus Protocol whitepaper

{% hint style="warning" %}
Legacy BNB Core model reference. Some unlisted markets still retain supply or debt, so the ABI remains operationally relevant. Resolve the market's exact model and `blocksPerYear` at the historical block; do not use this page to infer that a market is active or deprecated.
{% endhint %}

## Solidity API

#### blocksPerYear

Approximate blocks per year used to convert annual constructor parameters to per-block rates.

```solidity
uint256 blocksPerYear
```

***

#### constructor

Construct an interest rate model

```solidity
constructor(uint256 baseRatePerYear, uint256 multiplierPerYear, uint256 blocksPerYear_) public
```

**Parameters**

| Name              | Type    | Description                                                            |
| ----------------- | ------- | ---------------------------------------------------------------------- |
| baseRatePerYear   | uint256 | The approximate target base APR, as a mantissa (scaled by 1e18)        |
| multiplierPerYear | uint256 | The rate of increase in interest rate wrt utilization (scaled by 1e18) |
| blocksPerYear\_   | uint256 | The approximate number of blocks per year assumed by this model        |

***

#### utilizationRate

Calculates the utilization rate of the market: `borrows / (cash + borrows - reserves)`

```solidity
function utilizationRate(uint256 cash, uint256 borrows, uint256 reserves) public pure returns (uint256)
```

**Parameters**

| Name     | Type    | Description                                             |
| -------- | ------- | ------------------------------------------------------- |
| cash     | uint256 | The amount of cash in the market                        |
| borrows  | uint256 | The amount of borrows in the market                     |
| reserves | uint256 | The amount of reserves in the market (currently unused) |

**Return Values**

| Name | Type    | Description                                           |
| ---- | ------- | ----------------------------------------------------- |
| \[0] | uint256 | The utilization rate as a mantissa between \[0, 1e18] |

***

#### getBorrowRate

Calculates the current borrow rate per block, with the error code expected by the market

```solidity
function getBorrowRate(uint256 cash, uint256 borrows, uint256 reserves) public view returns (uint256)
```

**Parameters**

| Name     | Type    | Description                          |
| -------- | ------- | ------------------------------------ |
| cash     | uint256 | The amount of cash in the market     |
| borrows  | uint256 | The amount of borrows in the market  |
| reserves | uint256 | The amount of reserves in the market |

**Return Values**

| Name | Type    | Description                                                         |
| ---- | ------- | ------------------------------------------------------------------- |
| \[0] | uint256 | The borrow rate percentage per block as a mantissa (scaled by 1e18) |

***

#### getSupplyRate

Calculates the current supply rate per block

```solidity
function getSupplyRate(uint256 cash, uint256 borrows, uint256 reserves, uint256 reserveFactorMantissa) public view returns (uint256)
```

**Parameters**

| Name                  | Type    | Description                               |
| --------------------- | ------- | ----------------------------------------- |
| cash                  | uint256 | The amount of cash in the market          |
| borrows               | uint256 | The amount of borrows in the market       |
| reserves              | uint256 | The amount of reserves in the market      |
| reserveFactorMantissa | uint256 | The current reserve factor for the market |

**Return Values**

| Name | Type    | Description                                                         |
| ---- | ------- | ------------------------------------------------------------------- |
| \[0] | uint256 | The supply rate percentage per block as a mantissa (scaled by 1e18) |

***


# InterestRateModelLens

The former page described this function:

```solidity
function getNextInterestRates(
    uint256 cash,
    uint256 borrows,
    uint256 reserves,
    uint256 reserveFactorMantissa,
    uint256 badDebt,
    InterestRateModel interestRateModel
) external view returns (uint256 borrowRate, uint256 supplyRate)
```

No canonical source file, deployed address, supported network, or current consumer for `InterestRateModelLens` has been identified in the stable Venus repositories or deployment registries.

{% hint style="danger" %}
Do not assume this contract is deployed or supported. Simulate rates by calling the exact model selected by the target market, after resolving any `CheckpointView` wrapper. See [BNB Core Interest Rate Models](/technical-reference/reference-core-pool/interestratemodels).
{% endhint %}

This page is retained only to prevent old links from silently implying that the unverified ABI is current. It can be archived after integrations and ownership are conclusively ruled out.




---

[Next Page](https://docs-v4.venus.io/llms-full.txt/1)

