Skip to main content

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 BehaviorFolioStakingVaultNotes
Multiple EntrypointsTokens like old TUSD with transferProxy()
Pausable / BlocklistUSDC, USDT pause functionality
Fee-on-transferBreaks balance accounting
ERC777 / CallbackReentrancy risk via hooks
Upward-rebasingstETH, but accounting may be off
Downward-rebasingAccounting may be off
Revert on zero-value transfersNo issue with SafeERC20
Flash mintNot a problem for Folio
Missing return valuesSafeERC20 handles this
No revert on failureSafeERC20 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:
  1. Use PriceControl.ATOMIC_SWAP to eliminate public MEV
  2. Use trusted fillers (CoW Swap) for MEV-protected execution
  3. Set narrow price ranges to limit extractable value
  4. 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

Build docs developers (and LLMs) love