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

The Folio protocol uses an upgradeable proxy pattern with two contracts:
  1. FolioProxy: ERC1967-compliant transparent proxy
  2. FolioProxyAdmin: Admin contract managing upgrades with version control
This architecture enables protocol upgrades while maintaining state and user balances.

FolioProxyAdmin

The FolioProxyAdmin contract manages upgrades for Folio proxies with built-in version registry integration.

Features

  • Version-controlled upgrades
  • Ownership-based access control
  • Deprecation protection
  • Single admin per proxy

Constructor

FolioProxy.sol
constructor(address initialOwner, address _versionRegistry) Ownable(initialOwner)
initialOwner
address
Initial owner of the ProxyAdmin (typically a timelock)
_versionRegistry
address
FolioVersionRegistry address for version validation

Upgrade to Version

Upgrade a proxy to a specific version from the registry.
proxyTarget
address
Address of the FolioProxy to upgrade
versionHash
bytes32
Hash of the version to upgrade to
data
bytes
Calldata to execute after upgrade (typically for re-initialization)
FolioProxy.sol
function upgradeToVersion(
    address proxyTarget,
    bytes32 versionHash,
    bytes memory data
) external onlyOwner
The function will revert if:
  • Version is deprecated in the registry
  • Version doesn’t exist in the registry
  • Caller is not the owner

Version Validation

The upgrade process includes automatic validation:
Upgrade Flow
// 1. Check version is not deprecated
require(!folioRegistry.isDeprecated(versionHash), VersionDeprecated());

// 2. Verify version exists
require(
    address(folioRegistry.deployments(versionHash)) != address(0),
    InvalidVersion()
);

// 3. Get implementation address
address folioImpl = folioRegistry.getImplementationForVersion(versionHash);

// 4. Perform upgrade
ITransparentUpgradeableProxy(proxyTarget).upgradeToAndCall(folioImpl, data);

FolioProxy

The FolioProxy contract is a minimal transparent proxy implementation following ERC1967.

Features

  • Transparent proxy pattern
  • Immutable admin (cannot change after deployment)
  • Restricted admin interface
  • Fallback delegation to implementation

Constructor

FolioProxy.sol
constructor(address _logic, address _admin) ERC1967Proxy(_logic, "")
_logic
address
Initial implementation address (Folio contract)
_admin
address
ProxyAdmin address (immutable after deployment)
The admin is stored in the ERC1967 admin slot and cannot be changed after deployment.

Proxy Behavior

The proxy uses a custom _fallback() implementation:
FolioProxy.sol
function _fallback() internal virtual override {
    if (msg.sender == ERC1967Utils.getAdmin()) {
        // Admin can only call upgradeToAndCall
        require(
            msg.sig == ITransparentUpgradeableProxy.upgradeToAndCall.selector,
            ProxyDeniedAdminAccess()
        );

        (address newImplementation, bytes memory data) = abi.decode(
            msg.data[4:],
            (address, bytes)
        );

        ERC1967Utils.upgradeToAndCall(newImplementation, data);
    } else {
        // All other callers are delegated to implementation
        super._fallback();
    }
}

Access Control

Admin
address
Can only call upgradeToAndCall() - no access to implementation functions
Users
address
All calls are delegated to the implementation contract

Upgrade Process

Step-by-Step Upgrade

  1. Deploy New Implementation
    Folio newImplementation = new Folio();
    
  2. Register Version (via FolioVersionRegistry)
    versionRegistry.registerVersion(newDeployer);
    
  3. Prepare Upgrade Data (if needed)
    bytes memory initData = abi.encodeWithSignature(
        "reinitialize(uint256)",
        newVersion
    );
    
  4. Execute Upgrade (via ProxyAdmin)
    bytes32 versionHash = keccak256(abi.encodePacked("4.0.0"));
    proxyAdmin.upgradeToVersion(folioProxy, versionHash, initData);
    

Governance Upgrade Example

Timelock Upgrade
// Proposal to upgrade Folio
address[] memory targets = new address[](1);
targets[0] = address(proxyAdmin);

uint256[] memory values = new uint256[](1);
values[0] = 0;

bytes[] memory calldatas = new bytes[](1);
calldatas[0] = abi.encodeWithSignature(
    "upgradeToVersion(address,bytes32,bytes)",
    folioProxy,
    versionHash,
    ""
);

governor.propose(
    targets,
    values,
    calldatas,
    "Upgrade Folio to v4.0.0"
);

Storage Layout

ERC1967 Storage Slots

The proxy follows ERC1967 standard storage slots:
Storage Slots
// Implementation slot
bytes32 IMPLEMENTATION_SLOT = 
    bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1);

// Admin slot
bytes32 ADMIN_SLOT = 
    bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1);
Never use these slots in the implementation contract to avoid storage collisions.

Security Considerations

Transparent Proxy Pattern

The transparent proxy ensures:
  • Admin cannot call implementation functions
  • Users cannot call admin functions
  • No function selector collisions

Admin Immutability

The admin address is set once during deployment and cannot be changed. This ensures:
  • Predictable upgrade permissions
  • No admin takeover attacks
  • Clear governance structure

Version Control

Integration with FolioVersionRegistry provides:
  • Deprecation Protection: Prevents upgrades to deprecated versions
  • Version Validation: Ensures implementation exists before upgrade
  • Audit Trail: All versions registered on-chain

Events

The proxy emits standard ERC1967 events:
Upgraded
event
Emitted when implementation is upgradedParameters:
  • implementation - New implementation address
AdminChanged
event
Emitted when admin changes (only during deployment)Parameters:
  • previousAdmin - Previous admin (address(0) initially)
  • newAdmin - New admin address

Errors

ProxyDeniedAdminAccess
error
Thrown when admin tries to call non-upgrade functions
VersionDeprecated
error
Thrown when trying to upgrade to a deprecated version
InvalidVersion
error
Thrown when version doesn’t exist in registry

Best Practices

Upgrade Safety
  • Always test upgrades on testnet first
  • Use initialization functions for new storage variables
  • Follow the upgrade pattern for storage layout
  • Verify version registration before upgrade proposals
Admin Management
  • Use timelock contracts as ProxyAdmin owner
  • Implement multi-sig or governance for upgrade decisions
  • Monitor version registry for deprecations
  • Keep upgrade proposals transparent

Build docs developers (and LLMs) love