Access policies in Flux Protocol control who can interact with vaults and under what conditions. They provide fine-grained access control for vault deposits, withdrawals, and borrowing operations.
Overview
Access policies enable vault creators to:
Restrict vault access to specific addresses or roles
Implement KYC/compliance requirements for regulated use cases
Create private vaults for specific user groups
Enforce deposit/withdrawal limits per user
Control manager permissions separately from LP permissions
Policy Types
1. Open Access (Default)
Anyone can deposit, withdraw, or borrow from the vault.
// No access policy set - fully permissionless
IFluxVault vault = IFluxVault(vaultAddress);
Use Cases:
Public lending pools
Permissionless DeFi protocols
Maximum composability
2. Allowlist Policy
Only approved addresses can interact with the vault.
Use Cases:
Private funds
Institutional vaults
Compliance requirements
Testing/beta access
3. Role-Based Access
Different permissions for different roles (LPs vs Managers).
Use Cases:
Separate LP and manager lists
Professional manager programs
Tiered access levels
4. Cap-Based Policy
Enforce per-user deposit limits.
Use Cases:
Fair launch mechanisms
Prevent whale concentration
Regulatory limits
5. Time-Locked Policy
Restrict access based on time windows.
Use Cases:
Vesting schedules
Lock-up periods
Scheduled access
Implementing Access Policies
Creating a Custom Policy
Attaching Policy to Vault
Access policies are set during vault creation:
Policy Upgrades
Important: Access policies in Flux are immutable by default to protect depositors.
If you need updatable policies:
Implement a proxy pattern in your policy contract
Clearly communicate upgrade rights to users
Consider timelock mechanisms for policy changes
Policy Best Practices
For Public Vaults
Pros:
Maximum DeFi composability
Lowest integration friction
Largest potential user base
Cons:
No compliance controls
Cannot restrict bad actors
No preferential access
For Private Vaults
Pros:
Full control over participants
Compliance-friendly
Can meet KYC requirements
Cons:
Manual user management
Reduced composability
Ongoing maintenance
For Hybrid Models
Pros:
Open LP access for liquidity
Curated manager selection
Balanced approach
Cons:
More complex policy logic
Two allowlists to maintain
Security Considerations
Policy Immutability
Default behavior: Policies cannot be changed after vault creation
Rationale: Protects LPs from rug pulls
Trade-off: Cannot adapt to new requirements
Upgradeability Risks
If implementing upgradeable policies:
Mitigation:
Use timelocks for policy changes
Emit events for all policy updates
Consider multi-sig ownership
Document upgrade rights clearly
Gas Optimization
Policy Examples
Example 1: Allowlist with Max Users
Example 2: Deposit Cap Per User
Example 3: Time-Gated Access
Integration Guide
For Vault Creators
Determine requirements: Do you need access control?
Choose policy type: Select from standard policies or build custom
Deploy policy: Deploy your policy contract
Create vault: Pass policy address during vault creation
Manage access: Update allowlists if using mutable policies
contract CustomAccessPolicy is IAccessPolicy {
mapping(address => bool) public allowedLPs;
mapping(address => bool) public allowedManagers;
function canDeposit(address user) external view returns (bool) {
return allowedLPs[user];
}
function canBorrow(address user) external view returns (bool) {
return allowedManagers[user];
}
function canWithdraw(address user) external view returns (bool) {
return allowedLPs[user];
}
}
IFluxVault vault = factory.createVault(
baseAsset,
strategy,
accessPolicy, // Your policy contract
name,
symbol
);
// No policy = maximum composability
address accessPolicy = address(0);
// Allowlist policy with owner controls
AllowlistPolicy policy = new AllowlistPolicy(owner);
policy.addToAllowlist(trustedUser1);
policy.addToAllowlist(trustedUser2);
// Separate LP and manager access
RoleBasedPolicy policy = new RoleBasedPolicy();
policy.setLPAccess(PUBLIC);
policy.setManagerAccess(ALLOWLIST);
// ⚠️ DANGEROUS: Owner can change rules
contract UpgradeablePolicy {
address public owner;
function setAccess(address user, bool allowed) external {
require(msg.sender == owner);
// Owner can rug pull by revoking LP access
}
}
// BAD: External call on every deposit
function canDeposit(address user) external view returns (bool) {
return expensiveExternalCall(user);
}
// GOOD: Cached allowlist
mapping(address => bool) public allowed;
contract CappedAllowlist is IAccessPolicy {
mapping(address => bool) public allowlist;
uint256 public maxUsers;
uint256 public currentUsers;
function addToAllowlist(address user) external onlyOwner {
require(currentUsers < maxUsers, "Max users reached");
require(!allowlist[user], "Already allowed");
allowlist[user] = true;
currentUsers++;
}
function canDeposit(address user) external view returns (bool) {
return allowlist[user];
}
}
contract PerUserCap is IAccessPolicy {
uint256 public maxDepositPerUser;
mapping(address => uint256) public userDeposits;
function canDeposit(address user, uint256 amount)
external
view
returns (bool)
{
return userDeposits[user] + amount <= maxDepositPerUser;
}
function recordDeposit(address user, uint256 amount) external {
userDeposits[user] += amount;
}
}
contract TimeLocked is IAccessPolicy {
uint256 public startTime;
uint256 public endTime;
function canDeposit(address) external view returns (bool) {
return block.timestamp >= startTime && block.timestamp <= endTime;
}
}
IAccessPolicy policy = vault.accessPolicy();
if (address(policy) != address(0)) {
// Vault has access control
bool canDeposit = policy.canDeposit(user);
require(canDeposit, "User not authorized");
}
// Check if you can deposit
try vault.accessPolicy().canDeposit(msg.sender) returns (bool allowed) {
require(allowed, "Not authorized to deposit");
} catch {
// No policy = open access
}
vault.deposit(amount, msg.sender);
interface IAccessPolicy {
/// @notice Check if user can deposit
function canDeposit(address user) external view returns (bool);
/// @notice Check if user can withdraw
function canWithdraw(address user) external view returns (bool);
/// @notice Check if user can borrow
function canBorrow(address user) external view returns (bool);
/// @notice Check if user can liquidate
function canLiquidate(address user) external view returns (bool);
}
contract MultiSigPolicy is IAccessPolicy {
uint256 public requiredSignatures;
mapping(address => bool) public signers;
function addToAllowlist(
address user,
bytes[] memory signatures
) external {
require(signatures.length >= requiredSignatures);
// Verify signatures from signers
// Add user to allowlist
}
}
contract OraclePolicy is IAccessPolicy {
IOracle public creditScoreOracle;
uint256 public minCreditScore;
function canDeposit(address user) external view returns (bool) {
uint256 score = creditScoreOracle.getCreditScore(user);
return score >= minCreditScore;
}
}
contract NFTGatedPolicy is IAccessPolicy {
IERC721 public membershipNFT;
function canDeposit(address user) external view returns (bool) {
return membershipNFT.balanceOf(user) > 0;
}
}