Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Supply and Borrow Cap Encoding

Supply and borrow caps in the Euler Vault Kit (EVK) are not stored as plain integers, but are instead encoded using a compact 16-bit decimal floating point format. This allows vaults to efficiently store and process cap values on-chain, while still supporting a wide range of possible limits.

What Are Supply and Borrow Caps?

  • Supply cap: The maximum amount of the underlying asset that can be deposited into a vault.
  • Borrow cap: The maximum amount that can be borrowed from a vault.

Both are denominated in the underlying asset (with its native decimals), but are stored in a special encoded format for efficiency.

The Encoding Format

Caps are stored as a 16-bit value using a decimal floating point scheme:

  • Mantissa: The most significant 10 bits (bits 6-15), scaled by 100
  • Exponent: The least significant 6 bits (bits 0-5)
  • Special value: 0 means "no cap set" (unlimited)

The decoded value is:

cap = 10 ** exponent * mantissa / 100
  • If the encoded value is 0, the cap is considered unlimited (type(uint256).max).
  • For an actual cap of zero, use a zero mantissa and non-zero exponent.

Reference: AmountCap.sol

Encoding a Cap

If you need to encode a cap off-chain (for example, in a deployment script or frontend), you can use the following logic, which matches the on-chain encoding scheme:

// Maximum uint256 value as BigInt
const MAX_UINT = (2n ** 256n) - 1n;
 
// Encode a cap value (supplyCap should be a BigInt)
function encodeCap(supplyCap) {
    if (supplyCap === MAX_UINT) return 0; // Unlimited cap (special value)
    if (supplyCap === 0n) return 1;      // Actual zero cap (rarely used)
 
    const base = 10n;
    let exponent = 0n;
    let mantissa = supplyCap;
 
    // Adjust mantissa and exponent so that mantissa fits into 10 bits
    while (mantissa >= 1024n) {
        mantissa /= base;
        exponent++;
    }
 
    // Add 2 to the exponent to account for the scaling by 100 in the decoding function
    exponent += 2n;
 
    // Ensure mantissa fits into 10 bits
    if (mantissa >= 1024n) {
        throw new Error("Mantissa too large for AmountCap encoding");
    }
 
    // Ensure the exponent fits into 6 bits
    if (exponent > 63n) {
        throw new Error("Exponent too large for AmountCap encoding");
    }
 
    // Combine mantissa and exponent into a single 16-bit number
    const encodedCapNumber = Number((mantissa << 6n) | exponent);
    return encodedCapNumber;
}
 
// Example usage:
const cap = encodeCap(1000000n);

Decoding a Cap in JavaScript

To decode a cap value (for example, when reading from a contract):

const MAX_UINT = (2n ** 256n) - 1n;
 
function decodeCap(cap) {
    if (cap === 0) return MAX_UINT; // Unlimited
    const exponent = BigInt(cap & 63);
    const mantissa = BigInt(cap >> 6);
    return (10n ** exponent) * mantissa / 100n;
}
 
// Example usage:
const decoded = decodeCap(encodedCap);

Decoding a Cap on-chain

To decode a cap value in Solidity:

import {AmountCap, AmountCapLib} from "evk/EVault/shared/types/AmountCap.sol";
 
// To resolve (decode) a cap:
uint256 decoded = AmountCapLib.resolve(AmountCap.wrap(encoded));