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
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
- FolioVersionRegistry: Central registry of approved versions (1 per ecosystem)
- FolioDeployer: Version-specific deployer (1 per version)
- FolioImplementation: Shared logic contract (1 per version)
- FolioProxy: Individual Folio proxy (1 per Folio)
- 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);