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 uses the ERC1967 proxy pattern to enable upgradeability while maintaining security and decentralization. All upgrades must go through the FolioVersionRegistry to ensure only approved implementations can be used.

Proxy Architecture

FolioProxy

Each Folio is deployed as a proxy that delegates calls to a shared implementation:
contract FolioProxy is ERC1967Proxy {
    constructor(address _logic, address _admin) 
        ERC1967Proxy(_logic, "") 
    {
        ERC1967Utils.changeAdmin(_admin);
    }
}
Key characteristics:
  • Uses ERC1967 storage slots to avoid collisions
  • Admin can only call upgradeToAndCall()
  • All other calls are delegated to the implementation
  • Follows transparent proxy pattern

FolioProxyAdmin

The admin contract validates upgrades against the version registry:
function upgradeToVersion(
    address proxyTarget,
    bytes32 versionHash,
    bytes memory data
) external onlyOwner {
    IFolioVersionRegistry folioRegistry = IFolioVersionRegistry(versionRegistry);
    
    // Verify version is approved and not deprecated
    require(!folioRegistry.isDeprecated(versionHash), VersionDeprecated());
    require(
        address(folioRegistry.deployments(versionHash)) != address(0),
        InvalidVersion()
    );
    
    // Get approved implementation
    address folioImpl = folioRegistry.getImplementationForVersion(versionHash);
    
    // Execute upgrade
    ITransparentUpgradeableProxy(proxyTarget).upgradeToAndCall(folioImpl, data);
}

Version Registry

Registration

The DAO registers new Folio versions through FolioVersionRegistry:
function registerVersion(IFolioDeployer folioDeployer) external {
    require(roleRegistry.isOwner(msg.sender), InvalidCaller());
    
    string memory version = Versioned(address(folioDeployer)).version();
    bytes32 versionHash = keccak256(abi.encodePacked(version));
    
    require(
        address(deployments[versionHash]) == address(0),
        InvalidRegistration()
    );
    
    deployments[versionHash] = folioDeployer;
    latestVersion = versionHash;
    
    emit VersionRegistered(versionHash, folioDeployer);
}

Deprecation

Versions can be marked deprecated by the DAO or emergency council:
function deprecateVersion(bytes32 versionHash) external {
    require(
        roleRegistry.isOwnerOrEmergencyCouncil(msg.sender),
        InvalidCaller()
    );
    
    require(!isDeprecated[versionHash], AlreadyDeprecated());
    
    isDeprecated[versionHash] = true;
    
    emit VersionDeprecated(versionHash);
}
Deprecated versions cannot be upgraded to, but existing Folios running deprecated versions continue to function. Folio owners should upgrade to the latest version when deprecations occur.

Upgrade Process

1. DAO Registers New Version

// Deploy new FolioDeployer with updated implementation
FolioDeployer newDeployer = new FolioDeployer(
    newFolioImpl,
    daoFeeRegistry,
    trustedFillerRegistry,
    roleRegistry
);

// Register through governance
versionRegistry.registerVersion(newDeployer);

2. Folio Admin Proposes Upgrade

// Calculate version hash
string memory version = "5.0.0";
bytes32 versionHash = keccak256(abi.encodePacked(version));

// Prepare upgrade data (if needed)
bytes memory upgradeData = "";

// Submit governance proposal
governor.propose(
    targets,    // [proxyAdmin]
    values,     // [0]
    calldatas,  // [upgradeToVersion(folio, versionHash, upgradeData)]
    "Upgrade Folio to v5.0.0"
);

3. Governance Approval

// After timelock delay, execute upgrade
proxyAdmin.upgradeToVersion(
    address(folioProxy),
    versionHash,
    upgradeData
);

4. Post-Upgrade Verification

// Verify new implementation
address newImpl = ERC1967Utils.getImplementation(address(folioProxy));
assert(newImpl == expectedNewImplementation);

// Verify version string
string memory currentVersion = Versioned(address(folio)).version();
assert(keccak256(bytes(currentVersion)) == versionHash);

Storage Layout

Folio uses OpenZeppelin’s upgradeable contracts with careful storage management:
contract Folio is
    Initializable,
    ERC20Upgradeable,
    AccessControlEnumerableUpgradeable,
    ReentrancyGuardUpgradeable,
    Versioned
{
    // Storage slots follow upgrade-safe patterns:
    
    // === 1.0.0 ===
    IFolioDAOFeeRegistry public daoFeeRegistry;
    string public mandate;
    EnumerableSet.AddressSet private basket;
    // ...
    
    // === 2.0.0 ===
    mapping(uint256 => AuctionDetails) private auctionDetails;
    // ...
    
    // === 3.0.0 ===
    ITrustedFillerRegistry public trustedFillerRegistry;
    bool public trustedFillerEnabled;
    // ...
    
    // === 4.0.0 ===
    RebalanceControl public rebalanceControl;
    Rebalance private rebalance;
    // ...
    
    // === 5.0.0 ===
    bool public bidsEnabled;
    
    // === 6.0.0 ===
    bool public tradeAllowlistEnabled;
    EnumerableSet.AddressSet private tradeTokenAllowlist;
    uint256 public folioFeeForSelf;
}
Storage safety rules:
  • Never remove or reorder existing storage variables
  • Only append new variables at the end
  • Use storage gaps for future expansion (if needed)
  • Mark deprecated storage with _DEPRECATED suffix

Version Strings

Folio versions follow semantic versioning:
abstract contract Versioned {
    function version() external pure virtual returns (string memory) {
        return "6.0.0";  // major.minor.patch
    }
}
Version components:
  • Major: Breaking storage layout changes
  • Minor: New features, backward compatible
  • Patch: Bug fixes, no storage changes

Querying Version Information

Current Folio Version

string memory currentVersion = Versioned(address(folio)).version();
// Returns: "6.0.0"

Latest Available Version

(
    bytes32 versionHash,
    string memory version,
    IFolioDeployer deployer,
    bool deprecated
) = versionRegistry.getLatestVersion();

if (!deprecated) {
    // Safe to upgrade to this version
}

Check Version Status

bytes32 versionHash = keccak256(abi.encodePacked("5.0.0"));

// Check if deprecated
bool isDeprecated = versionRegistry.isDeprecated(versionHash);

// Get deployer for version
IFolioDeployer deployer = versionRegistry.deployments(versionHash);
if (address(deployer) != address(0)) {
    // Version exists
}

Security Considerations

Access Control

  • Version registration: Only DAO owner
  • Version deprecation: DAO owner or emergency council
  • Folio upgrades: Folio’s DEFAULT_ADMIN_ROLE (typically governance timelock)

Upgrade Validation

// ProxyAdmin enforces:
1. Version must be registered in version registry
2. Version must not be deprecated
3. Implementation address must be non-zero
4. Caller must be proxy admin owner

Time Delays

Upgrades typically go through governance timelocks:
// Example: 2-day minimum delay
timelock.queueTransaction(
    target: proxyAdmin,
    value: 0,
    signature: "upgradeToVersion(address,bytes32,bytes)",
    data: abi.encode(folio, versionHash, upgradeData),
    eta: block.timestamp + 2 days
);
Never skip timelock delays for upgrades. This gives users time to exit if they disagree with the upgrade.

Deployment Architecture

FolioVersionRegistry
    ↓ (registers)
FolioDeployer (per version)
    ↓ (deploys)
[FolioProxy → FolioImplementation]

    | (upgrades via)
    |
FolioProxyAdmin

    | (validates with)
    |
FolioVersionRegistry

Components

  1. FolioVersionRegistry: Central registry of approved versions (1 per ecosystem)
  2. FolioDeployer: Version-specific deployer (1 per version)
  3. FolioImplementation: Shared logic contract (1 per version)
  4. FolioProxy: Individual Folio proxy (1 per Folio)
  5. FolioProxyAdmin: Upgrade executor with registry validation (1 per Folio)

Example: Full Upgrade Flow

// 1. DAO deploys new version
Folio newImpl = new Folio();
FolioDeployer newDeployer = new FolioDeployer(
    address(newImpl),
    daoFeeRegistry,
    trustedFillerRegistry,
    roleRegistry
);

// 2. Register version
versionRegistry.registerVersion(newDeployer);

// 3. Folio governance proposes upgrade
string memory version = newImpl.version();
bytes32 versionHash = keccak256(abi.encodePacked(version));

governor.propose(
    [address(proxyAdmin)],
    [0],
    [abi.encodeCall(
        proxyAdmin.upgradeToVersion,
        (address(folio), versionHash, "")
    )],
    "Upgrade to 7.0.0: Add feature X"
);

// 4. After voting + timelock, execute
governor.execute(proposalId);

// 5. Verify upgrade
assert(keccak256(bytes(folio.version())) == versionHash);

Build docs developers (and LLMs) love