Documentation Index
Fetch the complete documentation index at: https://mintlify.com/reserve-protocol/reserve-index-dtf/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Reserve Folio implements multiple security mechanisms to protect against common vulnerabilities while supporting a wide range of ERC20 tokens. Understanding these protections is critical for both governance and consuming protocols.
Reentrancy Protection
Folio uses OpenZeppelin’s ReentrancyGuardUpgradeable to prevent reentrancy attacks:
contract Folio is ReentrancyGuardUpgradeable {
// All state-changing external functions are protected
function mint(...) external nonReentrant { ... }
function redeem(...) external nonReentrant { ... }
function bid(...) external nonReentrant { ... }
function createTrustedFill(...) external nonReentrant { ... }
}
Protected Functions
All mutating external functions use the nonReentrant modifier:
mint(), redeem()
bid(), createTrustedFill()
openAuction(), openAuctionUnrestricted(), closeAuction()
startRebalance(), endRebalance()
distributeFees(), poke()
- Governance functions:
addToBasket(), removeFromBasket(), etc.
Read-Only Reentrancy
While Folio itself is protected from reentrancy, read-only reentrancy is still possible for consuming protocols. View functions can be called during state changes and return inconsistent data.
Checking for Active State Changes
Consuming protocols must check stateChangeActive() before trusting view data:
function getSafeData() external view returns (uint256) {
(bool syncActive, bool asyncActive) = folio.stateChangeActive();
// Synchronous state change (reentrancy guard entered)
require(!syncActive, "Sync state change active");
// Asynchronous state change (trusted fill in progress)
require(!asyncActive, "Async state change active");
// Safe to read view functions
return folio.totalSupply();
}
When state changes are active:
syncActive = true: Folio is in the middle of a transaction (reentrancy)
asyncActive = true: A trusted fill swap is ongoing (CoW Swap order pending)
The asyncActive check can be DoS’d for the current block if a malicious actor repeatedly creates and cancels trusted fills. Implement appropriate safeguards in consuming protocols.
Weird ERC20 Support
Folio supports most ERC20 tokens with the following compatibility matrix:
Folio Compatibility
| Token Behavior | Folio | StakingVault | Notes |
|---|
| Multiple Entrypoints | ❌ | ❌ | Tokens like old TUSD with transferProxy() |
| Pausable / Blocklist | ❌ | ❌ | USDC, USDT pause functionality |
| Fee-on-transfer | ❌ | ❌ | Breaks balance accounting |
| ERC777 / Callback | ❌ | ❌ | Reentrancy risk via hooks |
| Upward-rebasing | ✅ | ❌ | stETH, but accounting may be off |
| Downward-rebasing | ✅ | ❌ | Accounting may be off |
| Revert on zero-value transfers | ✅ | ✅ | No issue with SafeERC20 |
| Flash mint | ✅ | ✅ | Not a problem for Folio |
| Missing return values | ✅ | ✅ | SafeERC20 handles this |
| No revert on failure | ✅ | ✅ | SafeERC20 handles this |
Unsupported Token Types
Multiple Entrypoints
Tokens with non-standard transfer functions:
// ❌ Not supported
contract TUSDOld {
function transfer(address to, uint amount) external;
function transferProxy(address from, address to, uint amount) external;
}
Pausable / Blocklist
Tokens that can be paused or have blocklists:
// ❌ Not supported
contract USDC {
bool public paused;
mapping(address => bool) public isBlacklisted;
function transfer(address to, uint amount) external {
require(!paused, "Paused");
require(!isBlacklisted[msg.sender], "Blacklisted");
// ...
}
}
Risk: Folio could become stuck if tokens are paused or the Folio address is blacklisted.
Fee-on-Transfer
Tokens that charge a fee on transfer:
// ❌ Not supported
contract FeeToken {
uint256 public transferFee = 100; // 1%
function transfer(address to, uint amount) external {
uint256 fee = amount * transferFee / 10000;
balances[to] += amount - fee;
balances[feeRecipient] += fee;
}
}
Risk: Balance accounting breaks as actual received amount differs from transfer amount.
ERC777 / Callback Tokens
Tokens with transfer hooks:
// ❌ Not supported
contract ERC777Token {
function transfer(address to, uint amount) external {
// Calls tokensToSend hook on sender
IERC777Sender(msg.sender).tokensToSend(...);
// Transfer
balances[to] += amount;
// Calls tokensReceived hook on recipient
IERC777Recipient(to).tokensReceived(...);
}
}
Risk: Reentrancy attacks via callback hooks.
Supported with Caveats
Rebasing Tokens
Tokens where balances change automatically:
// ⚠️ Supported but accounting may be off
contract RebaseToken {
uint256 public rebaseMultiplier = 1e18;
function balanceOf(address account) external view returns (uint256) {
return (sharesOf[account] * rebaseMultiplier) / 1e18;
}
}
Risk: Folio’s auction accounting relies on balance deltas. Large rebases between auctions can cause misreporting of bought/sold amounts.
Avoid using rebasing tokens with non-incremental rebases (large jumps). Daily incremental rebases like stETH are generally acceptable, but governance should understand the accounting implications.
SafeERC20 Wrapper
Folio uses OpenZeppelin’s SafeERC20 for all token operations:
using SafeERC20 for IERC20;
// Handles:
// - Missing return values
// - False return values (converts to revert)
// - Zero-value transfers that revert
SafeERC20.safeTransfer(token, to, amount);
SafeERC20.safeTransferFrom(token, from, to, amount);
SafeERC20.forceApprove(token, spender, amount);
Trusted Filler Token Restrictions
If trusted fillers are enabled, tokens must be supported by both the Folio and the external filler (e.g., CoW Swap). Check the trusted filler’s documentation for their token compatibility requirements.
For CoW Swap specifically:
- Token must be listed on CoW Protocol
- Must have liquidity routing available
- Cannot be pausable or have callbacks
- Should have reasonable slippage characteristics
Chain Assumptions
The protocol assumes specific chain characteristics:
Block Time
// Assumed maximum block time
require(blockTime <= 30 seconds, "Chain not supported");
Auction timing mechanisms assume block times ≤ 30 seconds. Chains with longer block times may experience:
- Less precise auction pricing
- Larger time gaps in exponential decay curve
- Reduced warmup period effectiveness
Supported Chains
- Ethereum Mainnet (12s blocks) ✅
- Base (2s blocks) ✅
- Arbitrum (0.25s blocks) ✅
- Optimism (2s blocks) ✅
Value Range Assumptions
The protocol has defined bounds for all numeric values:
Token Supplies
// Maximum token supply: 1e36
// Folio collateral: 27 decimals max
// StakingVault underlying: 21 decimals max
Governance must ensure the Folio supply never grows beyond 1e36. This is a hard limit to prevent overflow in calculations.
Exchange Rates & Prices
// Rebalance limits: D18{BU/share} up to 1e27
require(limits.low > 0 && limits.high <= 1e27, "Invalid limits");
// Basket weights: D27{tok/BU} up to 1e54
require(weight.high <= 1e54, "Weight too high");
// Prices: D27{UoA/tok} up to 1e45
require(price.low > 0 && price.high <= 1e45, "Invalid price");
// Price range: Maximum 100x spread
require(price.high / price.low <= 100, "Price range too wide");
Governance Safety Guidelines
Token Removal
When removing a token from the basket via removeFromBasket(), users have limited time to redeem before the token becomes inaccessible. Only remove tokens if they have become malicious or compromised.
// Safe removal process:
1. Announce removal with sufficient warning (days/weeks)
2. Set token weight to zero in next rebalance
3. Complete rebalance to sell all tokens
4. Call removeFromBasket() after balance reaches zero
Rebalance Price Monitoring
If prices move outside the initially-provided price ranges during a rebalance, MEV searchers can extract value from the Folio. The AUCTION_LAUNCHER must actively monitor markets and end dangerous rebalances.
// AUCTION_LAUNCHER responsibility
if (currentPrice < initialPriceRange.low ||
currentPrice > initialPriceRange.high) {
// Value leakage imminent!
folio.endRebalance();
}
MEV Considerations
Auction MEV
Dutch auctions are inherently MEV-prone:
// Price decays over time
// First bidder at profitable price wins
// → Gas war / priority auction
Mitigations:
- Use
PriceControl.ATOMIC_SWAP to eliminate public MEV
- Use trusted fillers (CoW Swap) for MEV-protected execution
- Set narrow price ranges to limit extractable value
- Use 30-second warmup period to enable competition
Mint/Redeem MEV
Permissionless mint/redeem can be exploited:
// Sandwich attack pattern:
1. Detect profitable rebalance completion
2. Mint shares at old basket composition
3. Rebalance completes
4. Redeem at new basket composition
5. Profit from composition change
Mitigations:
- Governance should rebalance gradually (multiple small auctions)
- Large rebalances should use trusted fillers or atomic execution
- Consider mint/redeem fees to make attacks unprofitable
Denial of Service Vectors
Dust Donations
Governance can be griefed by dust token donations:
// Permissionless removal requires:
// 1. Token weight set to zero
// 2. Token balance is exactly zero
// Attacker can donate 1 wei to prevent removal
dustToken.transfer(address(folio), 1);
Mitigation: Use DEFAULT_ADMIN_ROLE to forcibly remove tokens.
Async Fill DoS
// Attacker can DoS asyncActive check:
while (inCurrentBlock) {
folio.createTrustedFill(...);
folio.closeTrustedFill();
}
// asyncActive = true for the entire block
Mitigation: Consuming protocols should implement rate limiting or use synchronous checks only.
Best Practices for Integrators
1. Always Check State Changes
function safeRead() external view returns (uint256) {
(bool syncActive, bool asyncActive) = folio.stateChangeActive();
require(!syncActive && !asyncActive, "State changing");
return folio.totalSupply();
}
2. Use Slippage Protection
// Minting
folio.mint(shares, receiver, minSharesOut);
// Redeeming
folio.redeem(shares, receiver, assets, minAmountsOut);
3. Understand Token Risks
// Check token compatibility before adding to Folio
if (tokenHasPause || tokenHasBlocklist || tokenIsFeeOnTransfer) {
revert("Incompatible token");
}
4. Monitor Deprecation
Before relying on a Folio:
bool deprecated = folio.isDeprecated();
require(!deprecated, "Folio deprecated");
Audit History
Reserve Folio has undergone multiple security audits:
Review audit reports before integrating or upgrading.
Emergency Procedures
Folio Deprecation
In case of critical vulnerability:
// DEFAULT_ADMIN_ROLE can deprecate
folio.deprecateFolio();
// Effects:
// - Minting disabled
// - Auctions cannot be opened/bid
// - Rebalancing disabled
// - Redemption still works (users can exit)
Version Deprecation
DAO or emergency council can deprecate a Folio version:
versionRegistry.deprecateVersion(versionHash);
// Effects:
// - Cannot upgrade TO this version
// - Existing Folios continue working
// - Folio admins should upgrade to new version