Flux Protocol's factory system enables permissionless deployment of vaults, strategies, and asset wrappers. Anyone can create these components, fostering innovation while maintaining safety through validated parameters and governance whitelisting.
interface IFluxVaultFactory {
// ========== Vault Creation ==========
function createVault(
address asset, // Base asset (USDC, WETH, etc.)
address baseAssetWrapper, // Wrapper for base asset
address strategy, // Strategy contract
address accessPolicy, // Access control (address(0) = permissionless)
string memory name, // Vault name
string memory symbol // Share symbol
) external returns (address vault);
function createVaultWithDepositPolicy(
address asset,
address baseAssetWrapper,
address strategy,
address accessPolicy,
GraduatedCapDepositPolicyTier[] memory tiers,
uint256 minTotalAssets,
string memory name,
string memory symbol
) external returns (address vault, address policy);
// ========== Executor Management ==========
function deployWeirollExecutor() external returns (address executor);
function registerExecutor(address manager, address executor) external;
function getExecutor(address manager) external view returns (address);
// ========== Asset Whitelisting (Governance Only) ==========
function whitelistAsset(address asset) external;
function removeAssetWhitelist(address asset) external;
function isAssetWhitelisted(address asset) external view returns (bool);
// ========== Protocol Fees (Governance Only) ==========
function protocolFeeRate() external view returns (uint256);
function setProtocolFeeRate(uint256 newRate) external;
function protocolFeeCollector() external view returns (address);
function setProtocolFeeCollector(address collector) external;
function collectFeesFromVaults(address[] calldata vaults) external;
// ========== Oracle Registry (Governance Only) ==========
function oracleRegistry() external view returns (IOracleRegistry);
function setOracleRegistry(IOracleRegistry registry) external;
// ========== Vault Registry ==========
function isVault(address vault) external view returns (bool);
}
// Step 1: Deploy or choose strategy
address strategy = strategyFactory.deployImmutableStrategy(
allowedAssets,
StrategyParams({
minBondRatio: 0.2e18, // 20% bond
liquidationBuffer: 0.1e18, // 10% buffer
adlBuffer: 0.1e18, // 10% ADL buffer
adlUtilizationThreshold: 0.95e18, // 95% utilization for ADL
annualRate: 0.1e18, // 10% APR
curatorFeeRate: 1000, // 10% curator fee
liquidationProfitMargin: 0.01e18 // 1% liquidator profit
})
);
// Step 2: Deploy vault
address vault = fluxFactory.createVault(
USDC, // Base asset
usdcWrapper, // Base asset wrapper (must be whitelisted)
strategy, // Strategy from step 1
address(0), // No access policy = permissionless
"Flux USDC Vault", // Name
"fUSDC" // Symbol
);
emit VaultCreated(vault);
// Deploy access policy
WhitelistAccessPolicy accessPolicy = new WhitelistAccessPolicy(msg.sender);
accessPolicy.setWhitelistedLP(trustedLP, true);
accessPolicy.setWhitelistedManager(trustedManager, true);
// Create vault with access control
address vault = fluxFactory.createVault(
WETH,
wethWrapper,
strategy,
address(accessPolicy), // Only whitelisted users
"Private WETH Vault",
"pWETH"
);
// Define tiers based on utilization
GraduatedCapDepositPolicyTier[] memory tiers = new GraduatedCapDepositPolicyTier[](3);
// Tier 1: Low utilization (0-50%) → No cap
tiers[0] = GraduatedCapDepositPolicyTier({
minUtilizationBps: 0, // 0% utilization
maxDepositBps: type(uint64).max // Unlimited deposits
});
// Tier 2: Medium utilization (50-80%) → 10% of vault size per deposit
tiers[1] = GraduatedCapDepositPolicyTier({
minUtilizationBps: 5000, // 50% utilization
maxDepositBps: 1000 // Max 10% of vault per deposit
});
// Tier 3: High utilization (80%+) → 5% of vault size per deposit
tiers[2] = GraduatedCapDepositPolicyTier({
minUtilizationBps: 8000, // 80% utilization
maxDepositBps: 500 // Max 5% of vault per deposit
});
// Create vault with deposit policy
(address vault, address policy) = fluxFactory.createVaultWithDepositPolicy(
USDC,
usdcWrapper,
strategy,
address(0), // Permissionless
tiers, // Graduated caps
100_000e6, // $100K bootstrap threshold (no caps until reached)
"Graduated USDC Vault",
"gUSDC"
);
// Later: Adjust tiers if needed
GraduatedCapDepositPolicy(policy).updateTiers(newTiers);
// Deploy a fresh Weiroll executor
address executor = fluxFactory.deployWeirollExecutor();
// Automatically registered to msg.sender
// Can now use vault.unlock() from this executor
// Deploy your custom executor
MyCustomExecutor executor = new MyCustomExecutor();
// Register it
fluxFactory.registerExecutor(msg.sender, address(executor));
contract MyCustomExecutor is IFluxUnlockCallback {
address public immutable manager;
IFluxVault public immutable vault;
constructor(address _vault) {
manager = msg.sender;
vault = IFluxVault(_vault);
}
function executeStrategy(bytes calldata data) external {
require(msg.sender == manager, "Only manager");
// Trigger vault unlock
vault.unlock(data);
}
function fluxUnlockCallback(bytes calldata data) external {
require(msg.sender == address(vault), "Only vault");
// Decode strategy
(uint256 borrowAmount, address[] memory wrappers) =
abi.decode(data, (uint256, address[]));
// Execute your custom logic
vault.locked_borrow(borrowAmount);
for (uint256 i = 0; i < wrappers.length; i++) {
vault.locked_registerWrapper(wrappers[i]);
// ... custom trading logic ...
}
}
}
bool isFluxVault = fluxFactory.isVault(vaultAddress);
if (isFluxVault) {
// Safe to cast to IFluxVault
IFluxVault vault = IFluxVault(vaultAddress);
}
// Set protocol fee rate (max 10%)
fluxFactory.setProtocolFeeRate(1000); // 10% of interest
// Set fee collector
fluxFactory.setProtocolFeeCollector(treasuryAddress);
// Collect fees from specific vaults
address[] memory vaults = new address[](2);
vaults[0] = vault1;
vaults[1] = vault2;
fluxFactory.collectFeesFromVaults(vaults);
// Initial allowed assets (can add more later)
address[] memory initialAssets = new address[](2);
initialAssets[0] = wethWrapper;
initialAssets[1] = usdcWrapper;
// Initial parameters (can be updated via timelock)
StrategyParams memory params = StrategyParams({
minBondRatio: 0.2e18, // 20% bond (5x leverage)
liquidationBuffer: 0.1e18, // 10% buffer
adlBuffer: 0.1e18, // 10% ADL buffer
adlUtilizationThreshold: 0.95e18, // 95% utilization for ADA
annualRate: 0.1e18, // 10% APR
curatorFeeRate: 1000, // 10% curator fee
liquidationProfitMargin: 0.02e18 // 2% liquidator profit
});
// Deploy with owner (typically multisig)
address strategy = strategyFactory.deployMutableStrategy(
initialAssets,
params,
multisigOwner // Can update params via timelock
);
// As strategy owner (via timelock contract)
IMutableStrategy(strategy).setAnnualRate(0.12e18); // Increase to 12% APR
// LPs have 7 days to exit if they disagree
// After 7 days, new rate takes effect
// Add newly whitelisted wrapper
IMutableStrategy(strategy).addAllowedAsset(newWrapperAddress);
// Cannot remove assets (managers may have positions)
// Initial allowed assets
address[] memory initialAssets = new address[](5);
initialAssets[0] = wethWrapper;
initialAssets[1] = wbtcWrapper;
initialAssets[2] = usdcWrapper;
initialAssets[3] = stethWrapper;
initialAssets[4] = daiWrapper;
// Flexible margin parameters
FlexibleMarginParams memory params = FlexibleMarginParams({
liquidationThreshold: 0.9e18, // Liquidate at 90% (stop-loss at -10%)
minBondRatio: 0.001e18, // 0.1% bond (minimal since self-managed)
adlThreshold: 0, // Not used (ADL disabled)
adlUtilizationThreshold: 0, // Not used (ADL disabled)
annualRate: 0, // 0% (self-funding)
curatorFeeRate: 0, // 0% (self-managed)
liquidationProfitMargin: 0.02e18 // 2% profit for liquidators
});
// Deploy
address strategy = strategyFactory.deployFlexibleMarginStrategy(
initialAssets,
params,
selfManagedOwner // Typically the only LP and manager
);
liquidationThreshold = 0.95e18 // Auto-exit at -5% loss
liquidationThreshold = 0.98e18 // Liquidate at -2% to protect capital
// Get total count
uint256 count = strategyFactory.getStrategyCount();
// Get strategies in pages
address[] memory strategies = strategyFactory.getStrategiesRange(0, 100);
for (uint256 i = 0; i < strategies.length; i++) {
IStrategy strategy = IStrategy(strategies[i]);
// Query parameters
uint256 annualRate = strategy.annualRate();
uint256 minBond = strategy.minBondRatio();
console.log("Strategy", i, "APR:", annualRate / 1e18);
}
Developer Deploys → Governance Reviews → Governance Whitelists → Vaults Can Use
(permissionless) (audit/test) (FluxVaultFactory) (strategy config)
contract AssetWrapperFactory {
// ========== Deployment ==========
function deployERC20Wrapper(address asset)
external returns (address wrapper);
function deployERC4626Wrapper(address vault)
external returns (address wrapper);
// ========== Lookups ==========
function getERC20Wrapper(address asset)
external view returns (address wrapper);
function getERC4626Wrapper(address vault)
external view returns (address wrapper);
function hasERC20Wrapper(address asset)
external view returns (bool);
function hasERC4626Wrapper(address vault)
external view returns (bool);
// ========== Enumeration ==========
function getERC20WrapperCount() external view returns (uint256);
function getERC20WrapperAtIndex(uint256 index) external view returns (address);
function getERC20WrappersRange(uint256 offset, uint256 limit)
external view returns (address[] memory);
function getERC4626WrapperCount() external view returns (uint256);
function getERC4626WrapperAtIndex(uint256 index) external view returns (address);
function getERC4626WrappersRange(uint256 offset, uint256 limit)
external view returns (address[] memory);
}
// Check if wrapper already exists
address existingWrapper = assetWrapperFactory.getERC20Wrapper(WETH);
if (existingWrapper == address(0)) {
// Deploy new wrapper
address wethWrapper = assetWrapperFactory.deployERC20Wrapper(WETH);
emit WrapperDeployed(WETH, wethWrapper, "ERC20");
// Now request governance whitelisting
// submitGovernanceProposal(wethWrapper);
} else {
// Wrapper already exists, use it
wethWrapper = existingWrapper;
}
// Attempting to deploy duplicate will revert
try assetWrapperFactory.deployERC20Wrapper(WETH) {
// Won't reach here
} catch {
// Reverts with WrapperAlreadyExists()
}
// Deploy wrapper for Aave USDC (aUSDC)
address aUSDC = 0x...; // Aave USDC vault
address aUsdcWrapper = assetWrapperFactory.deployERC4626Wrapper(aUSDC);
// Deploy wrapper for Compound USDC (cUSDCv3)
address cUSDCv3 = 0x...; // Compound v3 USDC vault
address cUsdcWrapper = assetWrapperFactory.deployERC4626Wrapper(cUSDCv3);
// Both wrap USDC underneath, but different yield strategies
// Flux vault managers can choose which to use
// 1. Developer identifies need for new wrapper
address USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
// 2. Deploy wrapper (permissionless)
address usdtWrapper = assetWrapperFactory.deployERC20Wrapper(USDT);
// 3. Test wrapper thoroughly
// - Verify IAsset implementation
// - Test deposit/withdraw
// - Verify oracle integration
// - Check edge cases (reentrant tokens, fee-on-transfer, etc.)
// 4. Submit governance proposal
GovernanceProposal memory proposal = GovernanceProposal({
action: "Whitelist USDT Wrapper",
wrapper: usdtWrapper,
auditReport: "ipfs://...",
testResults: "ipfs://...",
reasoning: "USDT is 3rd largest stablecoin, high demand from LPs and managers"
});
// 5. Governance reviews and approves
// After passing vote:
fluxFactory.whitelistAsset(usdtWrapper);
// 6. Vault creators can now use it
address[] memory allowedAssets = new address[](1);
allowedAssets[0] = usdtWrapper;
address strategy = strategyFactory.deployImmutableStrategy(
allowedAssets,
params
);
// 7. Managers can use USDT as collateral
vault.locked_registerWrapper(usdtWrapper);
USDT.approve(usdtWrapper, amount);
vault.locked_depositToWrapper(usdtWrapper, positionId, amount);
// Custom Uniswap V3 wrapper (not via factory)
UniswapV3Wrapper customWrapper = new UniswapV3Wrapper(
nonfungiblePositionManager,
fluxFactory
);
// Submit to governance for whitelisting
// If approved:
fluxFactory.whitelistAsset(address(customWrapper));
// Now usable in strategies