Skip to main content

Overview

PoolQuotaKeeperV3 manages the quota system for risky assets in Gearbox Protocol V3. Quotas limit system exposure to volatile tokens by requiring credit accounts to “purchase” quotas with two types of fees:
  1. Quota interest: Accrues over time based on rates from the gauge (suited for leveraged farming)
  2. Quota increase fee: One-time fee when purchasing additional quota (suited for leveraged trading)
Contract Location: contracts/pool/PoolQuotaKeeperV3.sol

Key Features

  • Per-Account Quota Tracking: Tracks quotas for each credit account and token
  • Interest Accrual: Accumulates quota interest using cumulative indexes
  • Total Quota Limits: Enforces system-wide limits per token
  • Multi-Credit Manager: Supports multiple credit managers from the same pool
  • Quota Revenue Updates: Keeps pool’s quota revenue synchronized
  • Gauge Integration: Gets quota rates from the gauge contract

Architecture

contract PoolQuotaKeeperV3 is 
    IPoolQuotaKeeperV3,
    ACLTrait,
    ContractsRegisterTrait,
    SanityCheckTrait

State Variables

Immutable

version
uint256
Contract version: 3_10
contractType
bytes32
Contract type identifier: "POOL_QUOTA_KEEPER"
underlying
address
Address of the pool’s underlying token
pool
address
Address of the pool this quota keeper is connected to

Configuration

gauge
address
Address of the gauge contract that determines quota rates
lastQuotaRateUpdate
uint40
Timestamp of the last quota rate update

Data Structures

TokenQuotaParams

struct TokenQuotaParams {
    uint16 rate;                    // Annual quota interest rate in bps
    uint192 cumulativeIndexLU;      // Cumulative index at last update
    uint16 quotaIncreaseFee;        // One-time increase fee in bps
    uint96 totalQuoted;             // Total quota amount across all accounts
    uint96 limit;                   // Maximum total quota for this token
}

AccountQuota

struct AccountQuota {
    uint96 quota;                   // Account's quota amount
    uint192 cumulativeIndexLU;      // Cumulative index at last account update
}

Core Functions

Quota Management

updateQuota

function updateQuota(
    address creditAccount,
    address token,
    int96 requestedChange,
    uint96 minQuota,
    uint96 maxQuota
) external returns (
    uint128 caQuotaInterestChange,
    uint128 fees,
    bool enableToken,
    bool disableToken
)
Updates a credit account’s quota for a token. Only callable by authorized credit managers.
creditAccount
address
Credit account to update quota for
token
address
Token to update quota for
requestedChange
int96
Requested change in quota (negative to decrease, type(int96).min to remove all)
minQuota
uint96
Minimum acceptable quota after update
maxQuota
uint96
Maximum acceptable quota after update
Returns:
  • caQuotaInterestChange: Accrued quota interest since last update
  • fees: Quota increase fees charged (if increasing)
  • enableToken: Whether token should be enabled as collateral
  • disableToken: Whether token should be disabled as collateral
Logic:
  1. Updates account’s cumulative index
  2. Calculates accrued interest
  3. Adjusts quota by requested amount (subject to total limit)
  4. Charges increase fee if quota increased
  5. Checks min/max bounds
  6. Updates pool’s quota revenue
// Example: Increase quota by 5000 USDC, require at least 4000
(uint128 interest, uint128 fees, bool enable, bool disable) = 
    quotaKeeper.updateQuota(
        creditAccount,
        token,
        5000e6,      // Request +5000
        4000e6,      // Min 4000 (allows less if limit hit)
        10000e6      // Max 10000
    );

removeQuotas

function removeQuotas(
    address creditAccount,
    address[] calldata tokens,
    bool setLimitsToZero
) external
Removes all quotas for a credit account. Only callable by credit managers.
creditAccount
address
Credit account to remove quotas from
tokens
address[]
Array of tokens to remove quotas for
setLimitsToZero
bool
Whether to also set token limits to zero (used in extreme cases like liquidations with loss)
// Example: Remove all quotas on liquidation
address[] memory tokens = new address[](2);
tokens[0] = WETH;
tokens[1] = WBTC;
quotaKeeper.removeQuotas(creditAccount, tokens, false);

accrueQuotaInterest

function accrueQuotaInterest(
    address creditAccount,
    address[] calldata tokens
) external
Updates cumulative indexes for an account’s quoted tokens without changing quotas.
tokens
address[]
Array of tokens to accrue interest for

View Functions

getQuotaAndOutstandingInterest

function getQuotaAndOutstandingInterest(
    address creditAccount,
    address token
) external view returns (
    uint96 quoted,
    uint128 outstandingInterest
)
Returns account’s quota and accrued interest for a token.
// Check quota and interest owed
(uint96 quota, uint128 interest) = 
    quotaKeeper.getQuotaAndOutstandingInterest(creditAccount, WETH);

cumulativeIndex

function cumulativeIndex(address token) external view returns (uint192)
Returns the current cumulative quota interest index for a token (in ray).

getQuotaRate

function getQuotaRate(address token) external view returns (uint16)
Returns the current quota interest rate for a token in basis points.

getTokenQuotaParams

function getTokenQuotaParams(address token) external view returns (
    uint16 rate,
    uint192 cumulativeIndexLU,
    uint16 quotaIncreaseFee,
    uint96 totalQuoted,
    uint96 limit,
    bool isActive
)
Returns all quota parameters for a token.
// Check WETH quota params
(
    uint16 rate,              // e.g., 200 = 2% annual
    uint192 index,
    uint16 increaseFee,       // e.g., 100 = 1% one-time
    uint96 totalQuoted,       // Total across all accounts
    uint96 limit,             // System-wide limit
    bool isActive
) = quotaKeeper.getTokenQuotaParams(WETH);

poolQuotaRevenue

function poolQuotaRevenue() external view returns (uint256)
Returns the pool’s total annual quota revenue in underlying tokens.

quotedTokens

function quotedTokens() external view returns (address[] memory)
Returns array of all tokens with quotas enabled.

isQuotedToken

function isQuotedToken(address token) external view returns (bool)
Checks if a token has quotas enabled.

Configuration

setGauge

function setGauge(address _gauge) external
Sets the gauge contract used to determine quota rates. Only callable by configurator. Requirements:
  • Gauge must be compatible with the pool
  • All currently quoted tokens must be added in the new gauge

addQuotaToken

function addQuotaToken(address token) external
Adds a new token to the quota system. Only callable by gauge.
The gauge is responsible for ensuring the token is not the pool’s underlying.

updateRates

function updateRates() external
Updates quota rates for all tokens from the gauge. Only callable by gauge. Process:
  1. Updates cumulative indexes for all tokens using old rates
  2. Queries new rates from gauge
  3. Sets new rates
  4. Updates pool’s total quota revenue
  5. Updates timestamp

addCreditManager

function addCreditManager(address _creditManager) external
Authorizes a new credit manager to call quota functions. Only callable by configurator.

setTokenLimit

function setTokenLimit(address token, uint96 limit) external
Sets the total quota limit for a token. Only callable by configurator.
limit
uint96
New limit (must be ≤ type(int96).max)

setTokenQuotaIncreaseFee

function setTokenQuotaIncreaseFee(address token, uint16 fee) external
Sets the one-time increase fee for a token in basis points.

Events

event UpdateQuota(address indexed creditAccount, address indexed token, int96 quotaChange)
event UpdateTokenQuotaRate(address indexed token, uint16 rate)
event SetGauge(address indexed newGauge)
event AddCreditManager(address indexed creditManager)
event AddQuotaToken(address indexed token)
event SetTokenLimit(address indexed token, uint96 limit)
event SetQuotaIncreaseFee(address indexed token, uint16 fee)

Example Usage

Updating Account Quota

// Credit manager requests quota increase for WETH
(uint128 interest, uint128 fees, bool enable, bool disable) = 
    quotaKeeper.updateQuota(
        creditAccount,
        WETH,
        int96(5 ether),      // Add 5 WETH worth of quota
        uint96(4 ether),     // Min 4 WETH (allows less if limit hit)
        uint96(20 ether)     // Max 20 WETH
    );

if (enable) {
    // Enable WETH as collateral in credit manager
}

if (interest > 0 || fees > 0) {
    // Charge account for interest and fees
    totalDebt += interest + fees;
}

Checking Quota Status

// Get account's current quota and interest
(uint96 quota, uint128 interest) = 
    quotaKeeper.getQuotaAndOutstandingInterest(creditAccount, WETH);

console.log("Current quota:", quota);
console.log("Interest owed:", interest);

// Check token parameters
(
    uint16 rate,
    ,
    uint16 increaseFee,
    uint96 totalQuoted,
    uint96 limit,
    bool isActive
) = quotaKeeper.getTokenQuotaParams(WETH);

console.log("Rate:", rate, "bps");
console.log("Increase fee:", increaseFee, "bps");
console.log("Total quoted:", totalQuoted, "/", limit);

Removing Quotas on Liquidation

// Get all quoted tokens for the account
address[] memory tokens = getAccountQuotedTokens(creditAccount);

// Remove all quotas
quotaKeeper.removeQuotas(
    creditAccount,
    tokens,
    false  // Don't set limits to zero (normal liquidation)
);

// For liquidations with loss, optionally set limits to zero
// to prevent further exposure
if (hasLoss) {
    quotaKeeper.removeQuotas(creditAccount, tokens, true);
}

Quota Interest Calculation

Quota interest uses a cumulative index system similar to lending protocols:
// Interest accrued since last update:
interest = quota * (cumulativeIndexNow - cumulativeIndexLU) / RAY

// Cumulative index grows linearly:
cumulativeIndexNow = cumulativeIndexLU * (1 + rate * timeElapsed / YEAR)
Example:
  • Account has 10,000 USDC quota
  • Rate is 5% (500 bps)
  • 30 days passed since last update
  • Interest ≈ 10,000 * 0.05 * (30/365) ≈ 41 USDC

Security Considerations

Access Control: Only authorized credit managers can update quotas for their credit accounts.
Quota Limits: Total quoted amount across all accounts is capped per token to limit system exposure.
Interest Accrual: Indexes are updated before any quota changes to ensure accurate interest calculations.
Revenue Synchronization: Pool’s quota revenue is kept in sync whenever quotas or rates change.

Build docs developers (and LLMs) love