Custom Wrapper Development
Table of Contents
Architecture Overview
Asset wrappers in Flux provide a unified interface for managing heterogeneous assets. They enable vaults to support:
Standard fungible tokens (ERC20)
Yield-bearing tokens (ERC4626 vaults like Aave, Compound)
NFT positions (Uniswap V3 LP NFTs)
LP tokens (Aerodrome, Curve, Balancer)
Any custom asset type you can imagine
Key Principles
Three-Layer Position Model:
account (vault) → subaccount (manager) → positionIdIsolated Namespaces: Each vault operates independently
Permissionless: One wrapper deployment serves unlimited vaults
Oracle Pricing: Wrappers query the Oracle Registry for valuations
Access Control: Only the vault can modify its positions
Understanding the IAsset Interface
All wrappers must implement IAsset (src/interfaces/IAsset.sol:9):
Function Responsibilities
deposit()
Transfer assets from manager to wrapper
Vault during locked_depositToWrapper()
claim()
Claim pre-received assets (airdrops, rewards)
Vault during locked_claimFromWrapper()
withdraw()
Transfer assets from wrapper to recipient
Vault during locked_withdrawFromWrapper()
transfer()
Move position between accounts
Vault during internal transfers
getValue()
Get USD value of position
Strategy during health checks
getBalance()
Get raw balance (shares, NFT count, etc.)
Vault for accounting
underlyingAsset()
Get underlying token address
Oracle registry for pricing
ledgerTransferSubaccount()
Transfer all positions during liquidation
Vault during liquidation
customInteraction()
Execute custom logic (optional)
Vault during locked_interactWithWrapper()
Base Contracts
Flux provides two base contracts to simplify wrapper development:
1. BaseAssetWrapper (src/base/BaseAssetWrapper.sol:22)
Provides access control and security enforcement:
Benefits:
Access control handled automatically
Focus on business logic in
_internalfunctionsSecure by default
2. SimpleFungibleWrapper (src/base/SimpleFungibleWrapper.sol:18)
Extends BaseAssetWrapper for simple 2-position model:
Use SimpleFungibleWrapper for:
ERC20 tokens
ERC4626 vaults
Any fungible asset with working capital + bond positions
Use BaseAssetWrapper for:
NFT positions (Uniswap V3, etc.)
Complex multi-position assets
Assets requiring custom position enumeration
Wrapper Development Patterns
Pattern 1: Simple Fungible Token Wrapper
Best for: ERC20 tokens, simple yield-bearing tokens
Pattern 2: NFT Position Wrapper
Best for: Uniswap V3 positions, ERC721-based assets
Pattern 3: LP Token Wrapper with Rewards
Best for: Curve, Balancer, Aerodrome LP positions with reward claiming
Position ID Semantics
Position IDs serve different purposes depending on asset type:
Fungible Assets (ERC20/ERC4626)
Standard 2-position model:
Strict enforcement in ERC20AssetWrapper and ERC4626AssetWrapper:
Why? Prevents "ghost collateral" - assets deposited to positions not valued by strategies.
NFT Assets (Uniswap V3, etc.)
Use token ID as position ID:
Multi-Position Assets
Use semantic identifiers:
Best Practices
Document position ID semantics clearly in your wrapper
Validate position IDs if using restricted model
Use descriptive IDs for multi-position wrappers
Ensure strategies understand your position ID scheme
Oracle Integration
Wrappers don't store prices - they query the Oracle Registry:
Standard Pattern
Multi-Asset Valuation
For positions with multiple underlying assets:
ERC4626 Pattern
Convert shares to underlying before pricing:
Security Model
Namespace Isolation
Each vault operates in isolated namespace:
Access Control Pattern
Use BaseAssetWrapper to get this right automatically!
Claim Validation
Prevent fake collateral attacks:
Reentrancy Protection
For complex interactions:
Example Implementations
Example 1: Rebasing Token Wrapper (stETH)
Example 2: Synthetic Asset Wrapper (Synthetix sUSD)
Testing Your Wrapper
Unit Test Template
Integration Test with Real Vault
Deployment & Integration
Step 1: Deploy Wrapper
For vanilla wrappers, use AssetWrapperFactory:
For custom wrappers, deploy directly:
Step 2: Whitelist with Governance
Step 3: Include in Strategy
Step 4: Test with Real Vault
Best Practices
DO
Extend
BaseAssetWrapper- Don't reimplement access controlUse
SimpleFungibleWrapperfor standard fungible assetsValidate position IDs if using restricted model
Handle
type(uint256).maxin withdraw for "withdraw all"Measure actual balance changes for fee-on-transfer tokens
Track shares for rebasing tokens (stETH, aTokens)
Validate claim amounts against actual unclaimed balance
Document position ID semantics clearly
Test thoroughly with unit and integration tests
Gas optimize - wrappers are called frequently
DON'T
Don't skip access control - use
onlyAuthorizedmodifierDon't store prices - always query Oracle Registry
Don't allow cross-account transfers without authorization
Don't assume token balances - always measure actual transfers
Don't ignore rebasing - use shares for rebasing tokens
Don't hardcode decimals - query from token contract
Don't skip edge cases - test zero amounts, max withdrawals
Don't forget events - emit for all state changes
Don't implement custom oracles in wrapper - use registry
Don't make upgradeable - wrappers should be immutable
Security Checklist
Gas Optimization Tips
Common Pitfalls & Solutions
Pitfall 1: Ghost Collateral
Problem: Manager deposits to non-standard position ID, strategy doesn't value it
Solution: Validate position IDs in fungible wrappers
Pitfall 2: Rebasing Token Accounting
Problem: Token balance changes without transfers (stETH, aTokens)
Solution: Track shares, not balances
Pitfall 3: Fee-on-Transfer Tokens
Problem: safeTransferFrom(amount) doesn't guarantee amount received
Solution: Measure actual balance change
Pitfall 4: Unimplemented ledgerTransferSubaccount
ledgerTransferSubaccountProblem: Liquidations fail because subaccount can't be transferred
Solution: Implement proper subaccount enumeration
Further Reading
Last updated