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));