Skip to main 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));