Smart Contracts
This document provides a comprehensive, in-depth overview of the IPOR-Fusion architecture, its core concepts, modular components, and operational mechanics, structured for developers.
1. Core Architecture: The Polymorphic Vault
At its heart, IPOR-Fusion is a polymorphic vault architecture. This design allows the vault's core strategy and capabilities to be dynamically extended without upgrading the central PlasmaVault contract.
PlasmaVault: This is the central, stateful contract that holds all user-deposited funds. It acts as a secure container that executes operations on various external protocols but does not contain complex strategy logic itself.
Markets: A "Market" represents an external protocol or a specific strategy within a protocol with which the PlasmaVault integrates. It's the destination for the funds that the vault manages (e.g., a lending pool on Aave). The Market ID is an abstract entity. While in most cases a single ID corresponds to a single market in an external protocol, the architecture allows for defining more than one Market ID for a single external protocol integration (via a Fuse). This is particularly useful when one Fuse needs to handle different assets or sub-strategies within that same protocol, treating each as a distinct market from the vault's perspective.
The Alpha: This refers to the authorized strategist or automated entity responsible for managing the vault's funds by rebalancing assets between different Markets.
2. The Building Blocks: Modular Components
The polymorphic nature of the vault is enabled by a set of pluggable, modular components that handle specific tasks. These components are invoked by the PlasmaVault using delegatecall, which means their logic executes within the context of the vault's storage, address, and balance.
Fuses: Non-upgradeable, stateless contracts that act as standardized adapters between the PlasmaVault and external Markets. They contain the logic for specific interactions.
Core Operations: enter(market, amount) to send assets to a market and exit(market, amount) to withdraw them.
Reward-Bearing Fuses: Include a claim(market) function to collect rewards from a Market. See folder: https://github.com/IPOR-Labs/ipor-fusion/tree/main/contracts/fuses Examples: https://github.com/IPOR-Labs/ipor-fusion/tree/main/test/fuses
Pre-hooks: Configurable contracts that execute logic before a specific restricted method is called on the PlasmaVault, allowing for custom validation or checks. See folder: https://github.com/IPOR-Labs/ipor-fusion/tree/main/contracts/handlers/pre_hooks Examples: https://github.com/IPOR-Labs/ipor-fusion/tree/main/test/pre_hooks
Callback Handlers: Contracts that allow for executing custom logic after an operation on an external protocol has been completed via a Fuse. This is conceptually similar to the callback mechanism in flash loans. See folder: https://github.com/IPOR-Labs/ipor-fusion/tree/main/contracts/handlers/callbacks Examples: PlasmaVaultCallbackHandler.t.sol, MorphoFlashLoanFuseTest.t.sol
Universal Readers: Read-only contracts that provide a flexible and efficient way to query and aggregate data from the PlasmaVault's storage in a single on-chain call. See folder: https://github.com/IPOR-Labs/ipor-fusion/tree/main/contracts/readers Examples: https://github.com/IPOR-Labs/ipor-fusion/tree/main/test/readers
3. Manager Contracts: Specialized Logic
Manager contracts are specialized contracts that encapsulate and offload specific areas of logic from the PlasmaVault, making the system more modular and maintainable. (See folder: https://github.com/IPOR-Labs/ipor-fusion/tree/main/contracts/managers)
AccessManager: Responsible for the Role-Based Access Control (RBAC) system, defining roles and permissions.
PriceManager: Acts as the single source of truth for asset pricing for a specific vault. It interfaces with a common, upgradeable PriceOracleMiddleware (or PriceOracleMiddlewareWithRoles) contract that manages a shared set of price feeds. Crucially, the PriceManager allows for overriding these shared price feeds. This enables a given PlasmaVault to use its own custom price feeds and configuration, independent of the global setup in the PriceOracleMiddleware, providing significant flexibility. Examples: PriceOracleMiddlewareManagerTest.t.sol
FeeManager: Handles performance and management fee accounting and distribution. Examples: FeeManager.t.sol
WithdrawManager: Manages the withdrawal process, especially for Scheduled Withdraw Vaults. It also handles withdrawal-related fees. Examples: ContextManagerWithdrawManagerTest.t.sol
RewardsManager: Responsible for claiming rewards on behalf of the vault and linearly replenishing the vault's balance with them. Examples: RewardsClaimManagerTest.t.sol
ContextManager: Enables the execution of a sequence of actions atomically or within a specific "context" with the correct permissions. Examples: ContextManagerPlasmaVaultTest.t.sol
4. Lifecycle Part 1: Vault Creation via Factories
The IPOR-Fusion ecosystem is designed to be extensible through a system of Factory contracts. These factories are responsible for deploying and initializing different parts of the system. (See folder: https://github.com/IPOR-Labs/ipor-fusion/tree/main/contracts/factory )
Types of Factories:
Ecosystem Factories: Contracts designed to deploy an entire, self-contained IPOR-Fusion ecosystem, including the PlasmaVault and all its associated Manager contracts and other required contracts.
Component Factories: More specialized factories for creating individual components. Examples include:
BeefyVaultV7PriceFeedFactory: Creates a price feed for calculating the USD value of Beefy Vault V7 LP tokens. Examples: BeefyVaultV7PriceFeedFactory.t.sol
CollateralTokenOnMorphoMarketPriceFeedFactory: Creates a price feed for a collateral token on a Morpho market, calculating its USD price based on the loan token's known USD price.
CurveStableSwapNGPriceFeedFactory: Creates a price feed for calculating the USD value of Curve StableSwap NG LP tokens.
DualCrossReferencePriceFeedFactory: Creates a price feed for a token X by using the price of X relative to token Y, and the price of token Y relative to USD. Examples: DualCrossReferencePriceFeedFactoryTest.t.sol
WrappedPlasmaVaultFactory: This factory is used to create a wrapper around any existing PlasmaVault. This is a powerful concept for third parties who wish to build a business on top of a deployed vault by adding their own layer of fees. Examples: WrappedPlasmaVaultFactory.t.sol
The FusionFactory: This is a primary factory for creating new vault instances.
create() method: This standard creation method takes five parameters: assetName, assetSymbol, underlyingToken, redemptionDelayInSeconds, and owner. Examples: FusionFactory.t.sol
createSupervised() method: This method creates a vault with an enhanced permissions model. It establishes an admin role that has greater authority than the owner. The admin can add or remove other roles and assign or revoke roles from users, providing an additional layer of operational security and management, separating ownership from administration. Examples: FusionFactory.t.sol
5. Lifecycle Part 2: Post-Deployment Vault Configuration
Once a vault is deployed via a factory, its behavior is fine-tuned using the following methods:
Price Feed Configuration:
A critical step is configuring price feeds in the PriceManager. Price feeds must be added for all assets that are either tracked by the vault or are used in substrates by Fuses during strategy execution (especially when Fuses need to calculate the USD value of those assets). This ensures accurate valuation for performance tracking and fee calculation. Examples: PriceOracleMiddlewareWithRolesTest.t.sol
Fuse Configuration:
addFuses (on PlasmaVault): Registers general-purpose Fuses that enable interaction with external markets. Examples: PlasmaVaultBasic.t.sol
addBalanceFuse (on PlasmaVault): Registers a special Fuse used specifically for balance checking. Examples: PlasmaVaultBasic.t.sol
addRewardFuses (on RewardsManager): Registers Fuses that can claim rewards, linking them to the rewards management system. Examples: RewardsClaimManagerTest.t.sol
Substrate Configuration:
grantMarketSubstrates (on PlasmaVault): Grants specific permissions ("substrates") to a Market. Substrates are a list of assets, sub-markets within a specific protocol, or any other IDs required by a Fuse to correctly calculate a balance or perform an operation in the external market. This method essentially tells the vault which specific underlying components of a market a Fuse is allowed to interact with. Examples: PlasmaVaultBasic.t.sol, MorphoSupplyFuseTest.t.sol
Instant Withdraw Order:
configureInstantWithdrawalFuses (on PlasmaVault): For Instant and Hybrid Vaults, this method sets the specific order and combination of Fuses to be used. This configuration is necessary to define the sequence in which the vault automatically exits markets to fulfill a withdraw or redeem request when there are insufficient idle funds. Vaults that operate exclusively in scheduled mode do not require this configuration. Examples: PlasmaVaultWithdraw.t.sol
Dependency Graph:
updateDependencyBalanceGraphs (on PlasmaVault): Configures the dependency graph, defining which market balances should be updated together to ensure data consistency. Examples: MorphoCreditMarketTest.t.sol, Erc20BalanceArbitrum.t.sol
Additional Asset Tracking:
To track assets other than the vault's primary underlying asset (e.g., reward tokens), a special erc20balance Fuse is used. This involves configuring a specific market type (ERC20_VAULT_BALANCE = 7) and granting it the necessary substrates to check the balance of the desired ERC20 token held by the vault. Examples: Erc20BalanceArbitrum.t.sol, MorphoCreditMarketTest.t.sol
6. Core Operational Mechanics
Strategy Execution (execute()): The Alpha executes strategies by calling the execute() function on the PlasmaVault. This function allows for executing a sequence of operations (e.g., exiting one market, entering another) through one or more Fuses within a single transaction.
Simple Strategy: A single enter call to deposit assets into a lending pool. Examples: MorphoSupplyFuseTest.t.sol
Complex Strategy (Leverage Looping): A multi-step process involving depositing collateral, borrowing against it, swapping the borrowed asset, and re-depositing to increase leverage. Examples: LoopingBorrowSupplyMorphoFlashLoanMorpho.t.sol or any tests in folder https://github.com/IPOR-Labs/ipor-fusion/tree/main/test/looping
Balance Management:
Cached Balances: To save gas, the vault's balance on external Markets is cached and only updated upon direct interaction. Examples: PlasmaVaultBasic.t.sol and for example testShouldUpdateBalanceWhenOneFuse(), Examples: PlasmaVaultUpdateMarketsBalances.t.sol
On-Demand Updates: To get the most current balance, cached values can be updated manually for any list of markets. This can also be automated before a critical operation by using the updateMarketsBalances pre-hook. Examples: PlasmaVaultUpdateMarketsBalances.t.sol Examples: UpdateBalancesIgnoreDustPreHook.t.sol Other tests in folder: https://github.com/IPOR-Labs/ipor-fusion/tree/main/test/pre_hooks
Dependency Graph: This is a configurable graph that defines relationships between Markets. Its configuration is not always necessary and depends on the nature of the external protocol and how the Fuse integration is implemented. The primary use case is to ensure data consistency when an action in one market affects the balance of another. The most common dependency is the ERC20_VAULT_BALANCE market (which tracks tokens held directly by the vault) depending on other markets where swaps or reward claims occur.
ERC20_VAULT_BALANCE always depends on: UNIVERSAL_TOKEN_SWAPPER, UNISWAP_SWAP_V3_POSITIONS, UNISWAP_SWAP_V3, UNISWAP_SWAP_V2, RAMSES_V2, MORPHO_FLASH_LOAN.
ERC20_VAULT_BALANCE conditionally depends on (when borrow/swap fuses are used): AAVE_V3, COMPOUND_V3_xxx, MORPHO, MOONWELL. Examples: MorphoCreditMarketTest.t.sol, Erc20BalanceArbitrum.t.sol Examples: Various examples in folder https://github.com/IPOR-Labs/ipor-fusion/tree/main/test/fuses
Fee Mechanics: Fees are split between two managers to separate profit-based fees from transactional fees.
FeeManager (Profit-Based Fees):
Performance Fee: A percentage fee on generated profits.
Management Fee: An ongoing fee based on total assets under management (AUM).
Configuration: The FeeManager allows for setting multiple recipients and their respective shares for both performance and management fees. The fee share designated for the DAO is set during initialization and cannot be changed later.
Collection: Fees are collected by calling harvestAllFees(), harvestPerformanceFee(), or harvestManagementFee(). Examples: FeeManagerTest.s.sol
WithdrawManager (Transactional Fees):
Request Fee: A fee charged when a user requests a withdrawal in a Scheduled Withdraw Vault. It is set using the updateRequestFee method.
Withdraw Fee: A fee charged at the moment of withdrawal. It is set using the updateWithdrawFee method. Examples: PlasmaVaultScheduledWithdraw.t.sol
Withdrawal Processes:
Instant Withdraw: Available in Instant and Hybrid Vaults. Requires the instant withdraw order to be configured (method configureInstantWithdrawalFuses). This order determines the sequence in which the vault automatically exits markets to fulfill a user's withdraw or redeem request when there are not enough idle funds in the vault. Examples: PlasmaVaultWithdraw.t.sol
Scheduled Withdraw: Available in Scheduled and Hybrid Vaults. The process follows a specific sequence:
User requests withdrawal: The user calls requestShares on the WithdrawManager.
Alpha prepares funds: The Alpha calls execute on the PlasmaVault to free up liquidity.
Alpha releases funds: The Alpha calls releaseFunds on the WithdrawManager.
User redeems funds: The user calls redeemFromRequest on the PlasmaVault to receive their assets.
Examples: PlasmaVaultScheduledWithdraw.t.sol
7. Key Code-Level Definitions
To fully understand the system's operation, it is essential to be familiar with several key definitions found directly in the codebase.
Roles (Roles.sol): This file defines the bytes32 constants for all roles used within the AccessManager. Key roles include ADMIN, OWNER, ATOMIST, ALPHA Examples: IporPlasmaVaultRolesTest.t.sol, PlasmaVaultMaintenanceTest.t.sol
Markets (IporFusionMarkets.sol): This file contains the uint256 constants that serve as unique identifiers for each Market type. These are the IDs used in functions like grantMarketSubstrates and in the dependency graph. In addition to standard protocol integrations, there are special-purpose markets:
ERC20_VAULT_BALANCE: This market was created to track the balance of any ERC20 token held directly by the vault, other than the vault's primary underlying asset. Examples: Erc20BalanceArbitrum.t.sol, MorphoCreditMarketTest.t.sol
ZERO_BALANCE_MARKET: A special market used in cases where a Fuse does not need to maintain any balance, and there are no dependent balances to track. A prime example is the BurnRequestFeeFuse. Examples: PlasmaVaultScheduledWithdraw.t.sol
Initial Role Structure (IporFusionAccessManagerInitializerLibV1.sol): This library is crucial as it defines the default hierarchy and permissions structure when a new vault is initialized. It establishes which roles can grant or revoke other roles, creating a secure and logical separation of duties from the moment the vault is created.
8. Example DeFi Strategies for Alpha
The tests within the IPOR-Fusion repository serve as excellent, practical examples of the types of DeFi strategies the Alpha can execute via the PlasmaVault. These strategies range from simple, single-action operations to complex, multi-step leveraged positions.
Fundamental Operations (Supply, Borrow, Claim):
Supply Fuse, Borrow Fuse and Claim Fuse are the core building blocks of any vault strategy, representing depositing assets, taking loans, and collecting rewards, respectively. They can be combined with market entry/exit strategies. Examples: see folder https://github.com/IPOR-Labs/ipor-fusion/tree/main/test/fuses
Leveraged Looping on Aave V3, Morpho, Euler with Flash Loan:
More advanced strategies where Alpha can execute looping on Aave V3 / Euler / Morpho or any credit market with Flash Loan Examples: see folder https://github.com/IPOR-Labs/ipor-fusion/tree/main/test/looping
9. Developer Resources: Locating Addresses and ABIs
Contract addresses and their Application Binary Interfaces (ABIs) are essential for interacting with the protocol. You can find these crucial details in our dedicated IPOR ABI GitHub Repository: https://github.com/IPOR-Labs/ipor-abi.
Within this repository, navigate to the abi/ folder to access JSON files containing the ABIs for individual contracts. You will also find contract addresses for various networks, such as Ethereum Mainnet and Arbitrum. Common addresses for the Fusion ecosystem are typically located in folders with a "-fusion" postfix, and specific deployment addresses are often in files like deployment-addresses.json.
Last updated