Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"lib/ipor-fusion/contracts/fuses/liquity/LiquityBalanceFuse.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import {IMarketBalanceFuse} from "../IMarketBalanceFuse.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {PlasmaVaultConfigLib} from "../../libraries/PlasmaVaultConfigLib.sol";
import {IporMath} from "../../libraries/math/IporMath.sol";
import {IStabilityPool} from "./ext/IStabilityPool.sol";
import {IAddressesRegistry} from "./ext/IAddressesRegistry.sol";
import {IPriceOracleMiddleware} from "../../price_oracle/IPriceOracleMiddleware.sol";
import {PlasmaVaultLib} from "../../libraries/PlasmaVaultLib.sol";
/**
* @title Fuse for Liquity protocol responsible for calculating the balance of the Plasma Vault in Liquity protocol based on preconfigured market substrates
* @dev Substrates in this fuse are the address registries of Liquity protocol that are used in the Liquity protocol for a given MARKET_ID
*/
contract LiquityBalanceFuse is IMarketBalanceFuse {
uint256 public immutable MARKET_ID;
error InvalidMarketId();
constructor(uint256 marketId_) {
MARKET_ID = marketId_;
}
/**
* @notice Calculates the USD-denominated balance of the Plasma Vault in the Liquity protocol.
* The balance includes:
* - Current BOLD deposits (compounded after liquidations)
* - Stashed collateral (claimed but not sent)
* - Unrealized collateral gains from liquidations
* - Unrealized BOLD yield gains from interest
* It is assumed that BOLD token is the same across all registries
* @return The total balance of the Plasma Vault in USD (scaled to 18 decimals, that are the BOLD decimals).
*/
function balanceOf() external view override returns (uint256) {
bytes32[] memory registriesRaw = PlasmaVaultConfigLib.getMarketSubstrates(MARKET_ID);
uint256 len = registriesRaw.length;
if (len == 0) return 0;
address priceOracleMiddleware = PlasmaVaultLib.getPriceOracleMiddleware();
address plasmaVault = address(this);
uint256 totalBoldValue;
uint256 totalCollateralValue;
/// @dev Get BOLD token info from first registry (assumed same across all)
IAddressesRegistry firstRegistry = IAddressesRegistry(PlasmaVaultConfigLib.bytes32ToAddress(registriesRaw[0]));
address boldToken = firstRegistry.boldToken();
(uint256 boldPrice, uint256 boldPriceDecimals) = IPriceOracleMiddleware(priceOracleMiddleware).getAssetPrice(
boldToken
);
uint256 boldDecimals = IERC20Metadata(boldToken).decimals();
IAddressesRegistry registry;
IStabilityPool stabilityPool;
for (uint256 i; i < len; ++i) {
registry = IAddressesRegistry(PlasmaVaultConfigLib.bytes32ToAddress(registriesRaw[i]));
stabilityPool = IStabilityPool(registry.stabilityPool());
/// @dev Calculate collateral value for this registry
totalCollateralValue += _calculateCollateralValue(
stabilityPool,
registry.collToken(),
plasmaVault,
priceOracleMiddleware
);
/// @dev Calculate BOLD value for this registry
totalBoldValue += _calculateBoldValue(
stabilityPool,
plasmaVault,
boldPrice,
boldDecimals,
boldPriceDecimals
);
}
return totalCollateralValue + totalBoldValue;
}
/**
* @notice Calculates the total collateral value (stashed + unrealized gains) for a single registry
*/
function _calculateCollateralValue(
IStabilityPool stabilityPool_,
address collToken_,
address plasmaVault_,
address priceOracleMiddleware_
) private view returns (uint256) {
(uint256 collTokenPrice, uint256 collTokenPriceDecimals) = IPriceOracleMiddleware(priceOracleMiddleware_)
.getAssetPrice(collToken_);
uint256 collTokenDecimals = IERC20Metadata(collToken_).decimals();
uint256 decimalsSum = collTokenDecimals + collTokenPriceDecimals;
uint256 stashedValue = IporMath.convertToWad(
stabilityPool_.stashedColl(plasmaVault_) * collTokenPrice,
decimalsSum
);
uint256 unrealizedGainValue = IporMath.convertToWad(
stabilityPool_.getDepositorCollGain(plasmaVault_) * collTokenPrice,
decimalsSum
);
return stashedValue + unrealizedGainValue;
}
/**
* @dev Calculates the total BOLD value (compounded deposits + unrealized yield gains) for a single registry
*/
function _calculateBoldValue(
IStabilityPool stabilityPool_,
address plasmaVault_,
uint256 boldPrice_,
uint256 boldDecimals_,
uint256 boldPriceDecimals_
) private view returns (uint256) {
uint256 decimalsSum = boldDecimals_ + boldPriceDecimals_;
uint256 depositValue = IporMath.convertToWad(
stabilityPool_.getCompoundedBoldDeposit(plasmaVault_) * boldPrice_,
decimalsSum
);
uint256 yieldGainValue = IporMath.convertToWad(
stabilityPool_.getDepositorYieldGainWithPending(plasmaVault_) * boldPrice_,
decimalsSum
);
return depositValue + yieldGainValue;
}
}
"
},
"lib/ipor-fusion/contracts/fuses/IMarketBalanceFuse.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
/// @title Interface for Fuses responsible for providing the balance of the PlasmaVault address in the market in USD
interface IMarketBalanceFuse {
/// @notice Get the balance of the Plasma Vault in the market in USD
/// @dev Notice! Every Balance Fuse have to implement this exact function signature, because it is used by Plasma Vault engine
/// @return balanceValue The balance of the Plasma Vault in the market in USD, represented in 18 decimals
function balanceOf() external returns (uint256 balanceValue);
}
"
},
"lib/ipor-fusion/node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
* ```
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}
"
},
"lib/ipor-fusion/contracts/libraries/PlasmaVaultConfigLib.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import {PlasmaVaultStorageLib} from "./PlasmaVaultStorageLib.sol";
/// @title Plasma Vault Configuration Library responsible for managing the configuration of the Plasma Vault
library PlasmaVaultConfigLib {
event MarketSubstratesGranted(uint256 marketId, bytes32[] substrates);
/// @notice Checks if a given asset address is granted as a substrate for a specific market
/// @dev This function is part of the Plasma Vault's substrate management system that controls which assets can be used in specific markets
///
/// @param marketId_ The ID of the market to check
/// @param substrateAsAsset The address of the asset to verify as a substrate
/// @return bool True if the asset is granted as a substrate for the market, false otherwise
///
/// @custom:security-notes
/// - Substrates are stored internally as bytes32 values
/// - Asset addresses are converted to bytes32 for storage efficiency
/// - Part of the vault's asset distribution protection system
///
/// @custom:context The function is used in conjunction with:
/// - PlasmaVault's execute() function for validating market operations
/// - PlasmaVaultGovernance's grantMarketSubstrates() for configuration
/// - Asset distribution protection system for market limit enforcement
///
/// @custom:example
/// ```solidity
/// // Check if USDC is granted for market 1
/// bool isGranted = isSubstrateAsAssetGranted(1, USDC_ADDRESS);
/// ```
///
/// @custom:permissions
/// - View function, no special permissions required
/// - Substrate grants are managed by ATOMIST_ROLE through PlasmaVaultGovernance
///
/// @custom:related-functions
/// - grantMarketSubstrates(): For granting substrates to markets
/// - isMarketSubstrateGranted(): For checking non-asset substrates
/// - getMarketSubstrates(): For retrieving all granted substrates
function isSubstrateAsAssetGranted(uint256 marketId_, address substrateAsAsset) internal view returns (bool) {
PlasmaVaultStorageLib.MarketSubstratesStruct storage marketSubstrates = _getMarketSubstrates(marketId_);
return marketSubstrates.substrateAllowances[addressToBytes32(substrateAsAsset)] == 1;
}
/// @notice Validates if a substrate is granted for a specific market
/// @dev Part of the Plasma Vault's substrate management system that enables flexible market configurations
///
/// @param marketId_ The ID of the market to check
/// @param substrate_ The bytes32 identifier of the substrate to verify
/// @return bool True if the substrate is granted for the market, false otherwise
///
/// @custom:security-notes
/// - Substrates are stored and compared as raw bytes32 values
/// - Used for both asset and non-asset substrates (e.g., vaults, parameters)
/// - Critical for market access control and security
///
/// @custom:context The function is used for:
/// - Validating market operations in PlasmaVault.execute()
/// - Checking substrate permissions before market interactions
/// - Supporting various substrate types:
/// * Asset addresses (converted to bytes32)
/// * Protocol-specific vault identifiers
/// * Market parameters and configuration values
///
/// @custom:example
/// ```solidity
/// // Check if a compound vault substrate is granted
/// bytes32 vaultId = keccak256(abi.encode("compound-vault-1"));
/// bool isGranted = isMarketSubstrateGranted(1, vaultId);
///
/// // Check if a market parameter is granted
/// bytes32 param = bytes32("max-leverage");
/// bool isParamGranted = isMarketSubstrateGranted(1, param);
/// ```
///
/// @custom:permissions
/// - View function, no special permissions required
/// - Substrate grants are managed by ATOMIST_ROLE through PlasmaVaultGovernance
///
/// @custom:related-functions
/// - isSubstrateAsAssetGranted(): For checking asset-specific substrates
/// - grantMarketSubstrates(): For granting substrates to markets
/// - getMarketSubstrates(): For retrieving all granted substrates
function isMarketSubstrateGranted(uint256 marketId_, bytes32 substrate_) internal view returns (bool) {
PlasmaVaultStorageLib.MarketSubstratesStruct storage marketSubstrates = _getMarketSubstrates(marketId_);
return marketSubstrates.substrateAllowances[substrate_] == 1;
}
/// @notice Retrieves all granted substrates for a specific market
/// @dev Part of the Plasma Vault's substrate management system that provides visibility into market configurations
///
/// @param marketId_ The ID of the market to query
/// @return bytes32[] Array of all granted substrate identifiers for the market
///
/// @custom:security-notes
/// - Returns raw bytes32 values that may represent different substrate types
/// - Order of substrates in array is preserved from grant operations
/// - Empty array indicates no substrates are granted
///
/// @custom:context The function is used for:
/// - Auditing market configurations
/// - Validating substrate grants during governance operations
/// - Supporting UI/external systems that need market configuration data
/// - Debugging and monitoring market setups
///
/// @custom:substrate-types The returned array may contain:
/// - Asset addresses (converted to bytes32)
/// - Protocol-specific vault identifiers
/// - Market parameters and configuration values
/// - Any other substrate type granted to the market
///
/// @custom:example
/// ```solidity
/// // Get all substrates for market 1
/// bytes32[] memory substrates = getMarketSubstrates(1);
///
/// // Process different substrate types
/// for (uint256 i = 0; i < substrates.length; i++) {
/// if (isSubstrateAsAssetGranted(1, bytes32ToAddress(substrates[i]))) {
/// // Handle asset substrate
/// } else {
/// // Handle other substrate type
/// }
/// }
/// ```
///
/// @custom:permissions
/// - View function, no special permissions required
/// - Useful for both governance and user interfaces
///
/// @custom:related-functions
/// - isMarketSubstrateGranted(): For checking individual substrate grants
/// - grantMarketSubstrates(): For modifying substrate grants
/// - bytes32ToAddress(): For converting asset substrates back to addresses
function getMarketSubstrates(uint256 marketId_) internal view returns (bytes32[] memory) {
return _getMarketSubstrates(marketId_).substrates;
}
/// @notice Grants or updates substrate permissions for a specific market
/// @dev Core function for managing market substrate configurations in the Plasma Vault system
///
/// @param marketId_ The ID of the market to configure
/// @param substrates_ Array of substrate identifiers to grant to the market
///
/// @custom:security-notes
/// - Revokes all existing substrate grants before applying new ones
/// - Atomic operation - either all substrates are granted or none
/// - Emits MarketSubstratesGranted event for tracking changes
/// - Critical for market security and access control
///
/// @custom:context The function is used for:
/// - Initial market setup by governance
/// - Updating market configurations
/// - Managing protocol integrations
/// - Controlling asset access per market
///
/// @custom:substrate-handling
/// - Accepts both asset and non-asset substrates:
/// * Asset addresses (converted to bytes32)
/// * Protocol-specific vault identifiers
/// * Market parameters
/// * Configuration values
/// - Maintains a list of active substrates
/// - Updates allowance mapping for each substrate
///
/// @custom:example
/// ```solidity
/// // Grant multiple substrates to market 1
/// bytes32[] memory substrates = new bytes32[](2);
/// substrates[0] = addressToBytes32(USDC_ADDRESS);
/// substrates[1] = keccak256(abi.encode("compound-vault-1"));
/// grantMarketSubstrates(1, substrates);
/// ```
///
/// @custom:permissions
/// - Should only be called by authorized governance functions
/// - Typically restricted to ATOMIST_ROLE
/// - Critical for vault security
///
/// @custom:related-functions
/// - isMarketSubstrateGranted(): For checking granted substrates
/// - getMarketSubstrates(): For viewing current grants
/// - grantSubstratesAsAssetsToMarket(): For asset-specific grants
///
/// @custom:events
/// - Emits MarketSubstratesGranted(marketId, substrates)
function grantMarketSubstrates(uint256 marketId_, bytes32[] memory substrates_) internal {
PlasmaVaultStorageLib.MarketSubstratesStruct storage marketSubstrates = _getMarketSubstrates(marketId_);
_revokeMarketSubstrates(marketSubstrates);
bytes32[] memory list = new bytes32[](substrates_.length);
for (uint256 i; i < substrates_.length; ++i) {
marketSubstrates.substrateAllowances[substrates_[i]] = 1;
list[i] = substrates_[i];
}
marketSubstrates.substrates = list;
emit MarketSubstratesGranted(marketId_, substrates_);
}
/// @notice Grants asset-specific substrates to a market
/// @dev Specialized function for managing asset-type substrates in the Plasma Vault system
///
/// @param marketId_ The ID of the market to configure
/// @param substratesAsAssets_ Array of asset addresses to grant as substrates
///
/// @custom:security-notes
/// - Revokes all existing substrate grants before applying new ones
/// - Converts addresses to bytes32 for storage efficiency
/// - Atomic operation - either all assets are granted or none
/// - Emits MarketSubstratesGranted event with converted addresses
/// - Critical for market asset access control
///
/// @custom:context The function is used for:
/// - Setting up asset permissions for markets
/// - Managing DeFi protocol integrations
/// - Controlling which tokens can be used in specific markets
/// - Implementing asset-based strategies
///
/// @custom:implementation-details
/// - Converts each address to bytes32 using addressToBytes32()
/// - Updates both allowance mapping and substrate list
/// - Maintains consistency between address and bytes32 representations
/// - Ensures proper event emission with converted values
///
/// @custom:example
/// ```solidity
/// // Grant USDC and DAI access to market 1
/// address[] memory assets = new address[](2);
/// assets[0] = USDC_ADDRESS;
/// assets[1] = DAI_ADDRESS;
/// grantSubstratesAsAssetsToMarket(1, assets);
/// ```
///
/// @custom:permissions
/// - Should only be called by authorized governance functions
/// - Typically restricted to ATOMIST_ROLE
/// - Critical for vault security and asset management
///
/// @custom:related-functions
/// - grantMarketSubstrates(): For granting general substrates
/// - isSubstrateAsAssetGranted(): For checking asset grants
/// - addressToBytes32(): For address conversion
///
/// @custom:events
/// - Emits MarketSubstratesGranted(marketId, convertedSubstrates)
function grantSubstratesAsAssetsToMarket(uint256 marketId_, address[] calldata substratesAsAssets_) internal {
PlasmaVaultStorageLib.MarketSubstratesStruct storage marketSubstrates = _getMarketSubstrates(marketId_);
_revokeMarketSubstrates(marketSubstrates);
bytes32[] memory list = new bytes32[](substratesAsAssets_.length);
for (uint256 i; i < substratesAsAssets_.length; ++i) {
marketSubstrates.substrateAllowances[addressToBytes32(substratesAsAssets_[i])] = 1;
list[i] = addressToBytes32(substratesAsAssets_[i]);
}
marketSubstrates.substrates = list;
emit MarketSubstratesGranted(marketId_, list);
}
/// @notice Converts an Ethereum address to its bytes32 representation for substrate storage
/// @dev Core utility function for substrate address handling in the Plasma Vault system
///
/// @param address_ The Ethereum address to convert
/// @return bytes32 The bytes32 representation of the address
///
/// @custom:security-notes
/// - Performs unchecked conversion from address to bytes32
/// - Pads the address (20 bytes) with zeros to fill bytes32 (32 bytes)
/// - Used for storage efficiency in substrate mappings
/// - Critical for consistent substrate identifier handling
///
/// @custom:context The function is used for:
/// - Converting asset addresses for substrate storage
/// - Maintaining consistent substrate identifier format
/// - Supporting the substrate allowance system
/// - Enabling efficient storage and comparison operations
///
/// @custom:implementation-details
/// - Uses uint160 casting to handle address bytes
/// - Follows standard Solidity type conversion patterns
/// - Zero-pads the upper bytes implicitly
/// - Maintains compatibility with bytes32ToAddress()
///
/// @custom:example
/// ```solidity
/// // Convert USDC address to substrate identifier
/// bytes32 usdcSubstrate = addressToBytes32(USDC_ADDRESS);
///
/// // Use in substrate allowance mapping
/// marketSubstrates.substrateAllowances[usdcSubstrate] = 1;
/// ```
///
/// @custom:permissions
/// - Pure function, no state modifications
/// - Can be called by any function
/// - Used internally for substrate management
///
/// @custom:related-functions
/// - bytes32ToAddress(): Complementary conversion function
/// - grantSubstratesAsAssetsToMarket(): Uses this for address conversion
/// - isSubstrateAsAssetGranted(): Uses converted values for comparison
function addressToBytes32(address address_) internal pure returns (bytes32) {
return bytes32(uint256(uint160(address_)));
}
/// @notice Converts a bytes32 substrate identifier to its corresponding address representation
/// @dev Core utility function for substrate address handling in the Plasma Vault system
///
/// @param substrate_ The bytes32 substrate identifier to convert
/// @return address The resulting Ethereum address
///
/// @custom:security-notes
/// - Performs unchecked conversion from bytes32 to address
/// - Only the last 20 bytes (160 bits) are used
/// - Should only be used for known substrate conversions
/// - Critical for proper asset substrate handling
///
/// @custom:context The function is used for:
/// - Converting stored substrate identifiers back to asset addresses
/// - Processing asset-type substrates in market operations
/// - Interfacing with external protocols using addresses
/// - Validating asset substrate configurations
///
/// @custom:implementation-details
/// - Uses uint160 casting to ensure proper address size
/// - Follows standard Solidity address conversion pattern
/// - Maintains compatibility with addressToBytes32()
/// - Zero-pads the upper bytes implicitly
///
/// @custom:example
/// ```solidity
/// // Convert a stored substrate back to an asset address
/// bytes32 storedSubstrate = marketSubstrates.substrates[0];
/// address assetAddress = bytes32ToAddress(storedSubstrate);
///
/// // Use in asset validation
/// if (assetAddress == USDC_ADDRESS) {
/// // Handle USDC-specific logic
/// }
/// ```
///
/// @custom:related-functions
/// - addressToBytes32(): Complementary conversion function
/// - isSubstrateAsAssetGranted(): Uses this for address comparison
/// - getMarketSubstrates(): Returns values that may need conversion
function bytes32ToAddress(bytes32 substrate_) internal pure returns (address) {
return address(uint160(uint256(substrate_)));
}
/// @notice Gets the market substrates configuration for a specific market
function _getMarketSubstrates(
uint256 marketId_
) private view returns (PlasmaVaultStorageLib.MarketSubstratesStruct storage) {
return PlasmaVaultStorageLib.getMarketSubstrates().value[marketId_];
}
function _revokeMarketSubstrates(PlasmaVaultStorageLib.MarketSubstratesStruct storage marketSubstrates) private {
uint256 length = marketSubstrates.substrates.length;
for (uint256 i; i < length; ++i) {
marketSubstrates.substrateAllowances[marketSubstrates.substrates[i]] = 0;
}
}
}
"
},
"lib/ipor-fusion/contracts/libraries/math/IporMath.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
/// @title Ipor Math library with math functions
library IporMath {
uint256 private constant WAD_DECIMALS = 18;
uint256 public constant BASIS_OF_POWER = 10;
/// @dev The index of the most significant bit in a 256-bit signed integer
uint256 private constant MSB = 255;
function min(uint256 a_, uint256 b_) internal pure returns (uint256) {
return a_ < b_ ? a_ : b_;
}
/// @notice Converts the value to WAD decimals, WAD decimals are 18
/// @param value_ The value to convert
/// @param assetDecimals_ The decimals of the asset
/// @return The value in WAD decimals
function convertToWad(uint256 value_, uint256 assetDecimals_) internal pure returns (uint256) {
if (value_ > 0) {
if (assetDecimals_ == WAD_DECIMALS) {
return value_;
} else if (assetDecimals_ > WAD_DECIMALS) {
return division(value_, BASIS_OF_POWER ** (assetDecimals_ - WAD_DECIMALS));
} else {
return value_ * BASIS_OF_POWER ** (WAD_DECIMALS - assetDecimals_);
}
} else {
return value_;
}
}
/// @notice Converts the value to WAD decimals, WAD decimals are 18
/// @param value_ The value to convert
/// @param assetDecimals_ The decimals of the asset
/// @return The value in WAD decimals
function convertWadToAssetDecimals(uint256 value_, uint256 assetDecimals_) internal pure returns (uint256) {
if (assetDecimals_ == WAD_DECIMALS) {
return value_;
} else if (assetDecimals_ > WAD_DECIMALS) {
return value_ * BASIS_OF_POWER ** (assetDecimals_ - WAD_DECIMALS);
} else {
return division(value_, BASIS_OF_POWER ** (WAD_DECIMALS - assetDecimals_));
}
}
/// @notice Converts the int value to WAD decimals, WAD decimals are 18
/// @param value_ The int value to convert
/// @param assetDecimals_ The decimals of the asset
/// @return The value in WAD decimals, int
function convertToWadInt(int256 value_, uint256 assetDecimals_) internal pure returns (int256) {
if (value_ == 0) {
return 0;
}
if (assetDecimals_ == WAD_DECIMALS) {
return value_;
} else if (assetDecimals_ > WAD_DECIMALS) {
return divisionInt(value_, int256(BASIS_OF_POWER ** (assetDecimals_ - WAD_DECIMALS)));
} else {
return value_ * int256(BASIS_OF_POWER ** (WAD_DECIMALS - assetDecimals_));
}
}
/// @notice Divides two int256 numbers and rounds the result to the nearest integer
/// @param x_ The numerator
/// @param y_ The denominator
/// @return z The result of the division
function divisionInt(int256 x_, int256 y_) internal pure returns (int256 z) {
uint256 absX_ = uint256(x_ < 0 ? -x_ : x_);
uint256 absY_ = uint256(y_ < 0 ? -y_ : y_);
// Use bitwise XOR to get the sign on MBS bit then shift to LSB
// sign == 0x0000...0000 == 0 if the number is non-negative
// sign == 0xFFFF...FFFF == -1 if the number is negative
int256 sign = (x_ ^ y_) >> MSB;
uint256 divAbs;
uint256 remainder;
unchecked {
divAbs = absX_ / absY_;
remainder = absX_ % absY_;
}
// Check if we need to round
if (sign < 0) {
// remainder << 1 left shift is equivalent to multiplying by 2
if (remainder << 1 > absY_) {
++divAbs;
}
} else {
if (remainder << 1 >= absY_) {
++divAbs;
}
}
// (sign | 1) is cheaper than (sign < 0) ? -1 : 1;
unchecked {
z = int256(divAbs) * (sign | 1);
}
}
/// @notice Divides two uint256 numbers and rounds the result to the nearest integer
/// @param x_ The numerator
/// @param y_ The denominator
/// @return z_ The result of the division
function division(uint256 x_, uint256 y_) internal pure returns (uint256 z_) {
z_ = x_ / y_;
}
}
"
},
"lib/ipor-fusion/contracts/fuses/liquity/ext/IStabilityPool.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
interface IStabilityPool {
function provideToSP(uint256 amount_, bool doClaim_) external;
function withdrawFromSP(uint256 amount_, bool doClaim_) external;
function claimAllCollGains() external;
function stashedColl(address depositor_) external view returns (uint256);
function deposits(address depositor_) external view returns (uint256);
function troveManager() external view returns (address);
function offset(uint256 debtToOffset_, uint256 collToAdd_) external;
/// @notice Get the current compounded BOLD deposit for a depositor (after accounting for liquidations)
/// @param depositor_ The address of the depositor
/// @return The compounded deposit amount
function getCompoundedBoldDeposit(address depositor_) external view returns (uint256);
/// @notice Get the unrealized collateral gain for a depositor from liquidations
/// @param depositor_ The address of the depositor
/// @return The collateral gain amount
function getDepositorCollGain(address depositor_) external view returns (uint256);
/// @notice Get the unrealized BOLD yield gain for a depositor from interest
/// @param depositor_ The address of the depositor
/// @return The yield gain amount
function getDepositorYieldGainWithPending(address depositor_) external view returns (uint256);
}
"
},
"lib/ipor-fusion/contracts/fuses/liquity/ext/IAddressesRegistry.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
interface IAddressesRegistry {
function stabilityPool() external view returns (address);
function collToken() external view returns (address);
function priceFeed() external view returns (address);
function boldToken() external view returns (address);
}
"
},
"lib/ipor-fusion/contracts/price_oracle/IPriceOracleMiddleware.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
/// @title Interface to an aggregator of price feeds for assets, responsible for providing the prices of assets in a given quote currency
interface IPriceOracleMiddleware {
error EmptyArrayNotSupported();
error ArrayLengthMismatch();
error UnexpectedPriceResult();
error UnsupportedAsset();
error ZeroAddress(string variableName);
error WrongDecimals();
error InvalidExpectedPrice();
error PriceDeltaTooHigh();
error AssetPriceSourceAlreadySet();
/// @notice Returns the price of the given asset in given decimals
/// @return assetPrice price in QUOTE_CURRENCY of the asset
/// @return decimals number of decimals of the asset price
function getAssetPrice(address asset) external view returns (uint256 assetPrice, uint256 decimals);
/// @notice Returns the prices of the given assets in given decimals
/// @return assetPrices prices in QUOTE_CURRENCY of the assets represented in defined decimals QUOTE_CURRENCY_DECIMALS
/// @return decimalsList number of decimals of the asset prices
function getAssetsPrices(
address[] calldata assets
) external view returns (uint256[] memory assetPrices, uint256[] memory decimalsList);
/// @notice Returns address of source of the asset price - it could be IPOR Price Feed or Chainlink Aggregator or any other source of price for a given asset
/// @param asset address of the asset
/// @return address of the source of the asset price
function getSourceOfAssetPrice(address asset) external view returns (address);
/// @notice Sets the sources of the asset prices
/// @param assets array of addresses of the assets
function setAssetsPricesSources(address[] calldata assets, address[] calldata sources) external;
/// @notice Returns the address of the quote currency to which all the prices are relative, in IPOR Fusion it is the USD
//solhint-disable-next-line
function QUOTE_CURRENCY() external view returns (address);
/// @notice Returns the number of decimals of the quote currency, can be different for other types of Price Oracles Middlewares
//solhint-disable-next-line
function QUOTE_CURRENCY_DECIMALS() external view returns (uint256);
}
"
},
"lib/ipor-fusion/contracts/libraries/PlasmaVaultLib.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {Errors} from "./errors/Errors.sol";
import {PlasmaVaultStorageLib} from "./PlasmaVaultStorageLib.sol";
import {FusesLib} from "./FusesLib.sol";
/// @title InstantWithdrawalFusesParamsStruct
/// @notice A technical struct used to configure instant withdrawal fuses and their parameters in the Plasma Vault system
/// @dev This struct is used primarily in configureInstantWithdrawalFuses function to set up withdrawal paths
struct InstantWithdrawalFusesParamsStruct {
/// @notice The address of the fuse contract that handles a specific withdrawal path
/// @dev Must be a valid and supported fuse contract address that implements instant withdrawal logic
address fuse;
/// @notice Array of parameters specific to the fuse's withdrawal logic
/// @dev Parameter structure:
/// - params[0]: Always represents the withdrawal amount in underlying token decimals (set during withdrawal, not during configuration)
/// - params[1+]: Additional fuse-specific parameters such as:
/// - Asset addresses
/// - Market IDs
/// - Slippage tolerances
/// - Protocol-specific parameters
/// @dev The same fuse can appear multiple times with different params for different withdrawal paths
bytes32[] params;
}
/// @title Plasma Vault Library
/// @notice Core library responsible for managing the Plasma Vault's state and operations
/// @dev Provides centralized management of vault operations, fees, configuration and state updates
///
/// Key responsibilities:
/// - Asset management and accounting
/// - Fee configuration and calculations
/// - Market balance tracking and updates
/// - Withdrawal system configuration
/// - Access control and execution state
/// - Price oracle integration
/// - Rewards claim management
library PlasmaVaultLib {
using SafeCast for uint256;
using SafeCast for int256;
/// @dev Hard CAP for the performance fee in percentage - 50%
uint256 public constant PERFORMANCE_MAX_FEE_IN_PERCENTAGE = 5000;
/// @dev Hard CAP for the management fee in percentage - 5%
uint256 public constant MANAGEMENT_MAX_FEE_IN_PERCENTAGE = 500;
/// @dev The offset for the underlying asset decimals in the Plasma Vault
uint8 public constant DECIMALS_OFFSET = 2;
error InvalidPerformanceFee(uint256 feeInPercentage);
error InvalidManagementFee(uint256 feeInPercentage);
event InstantWithdrawalFusesConfigured(InstantWithdrawalFusesParamsStruct[] fuses);
event PriceOracleMiddlewareChanged(address newPriceOracleMiddleware);
event PerformanceFeeDataConfigured(address feeAccount, uint256 feeInPercentage);
event ManagementFeeDataConfigured(address feeAccount, uint256 feeInPercentage);
event RewardsClaimManagerAddressChanged(address newRewardsClaimManagerAddress);
event DependencyBalanceGraphChanged(uint256 marketId, uint256[] newDependenceGraph);
event WithdrawManagerChanged(address newWithdrawManager);
event TotalSupplyCapChanged(uint256 newTotalSupplyCap);
/// @notice Gets the total assets in the vault for all markets
/// @dev Retrieves the total value of assets across all integrated markets and protocols
/// @return uint256 The total assets in the vault, represented in decimals of the underlying asset
///
/// This function:
/// - Returns the raw total of assets without considering:
/// - Unrealized management fees
/// - Unrealized performance fees
/// - Pending rewards
/// - Current vault balance
///
/// Used by:
/// - PlasmaVault.totalAssets() for share price calculations
/// - Fee calculations and accrual
/// - Asset distribution checks
/// - Market limit validations
///
/// @dev Important: This value represents only the tracked assets in markets,
/// for full vault assets see PlasmaVault._getGrossTotalAssets()
function getTotalAssetsInAllMarkets() internal view returns (uint256) {
return PlasmaVaultStorageLib.getTotalAssets().value;
}
/// @notice Gets the total assets in the vault for a specific market
/// @param marketId_ The ID of the market to query
/// @return uint256 The total assets in the vault for the market, represented in decimals of the underlying asset
///
/// @dev This function provides market-specific asset tracking and is used for:
/// - Market balance validation
/// - Asset distribution checks
/// - Market limit enforcement
/// - Balance dependency resolution
///
/// Important considerations:
/// - Returns raw balance without considering fees
/// - Value is updated by balance fuses during market interactions
/// - Used in conjunction with market dependency graphs
/// - Critical for maintaining proper asset distribution across markets
///
/// Integration points:
/// - Balance Fuses: Update market balances
/// - Asset Distribution Protection: Check market limits
/// - Withdrawal System: Verify available assets
/// - Market Dependencies: Track related market updates
function getTotalAssetsInMarket(uint256 marketId_) internal view returns (uint256) {
return PlasmaVaultStorageLib.getMarketTotalAssets().value[marketId_];
}
/// @notice Gets the dependency balance graph for a specific market
/// @param marketId_ The ID of the market to query
/// @return uint256[] Array of market IDs that depend on the queried market
///
/// @dev The dependency balance graph is critical for maintaining consistent state across related markets:
/// - Ensures atomic balance updates across dependent markets
/// - Prevents inconsistent states in interconnected protocols
/// - Manages complex market relationships
///
/// Use cases:
/// - Market balance updates
/// - Withdrawal validations
/// - Asset rebalancing
/// - Protocol integrations
///
/// Example dependencies:
/// - Lending markets depending on underlying asset markets
/// - LP token markets depending on constituent token markets
/// - Derivative markets depending on base asset markets
///
/// Important considerations:
/// - Dependencies are unidirectional (A->B doesn't imply B->A)
/// - Empty array means no dependencies
/// - Order of dependencies may matter for some operations
/// - Used by _checkBalanceFusesDependencies() during balance updates
function getDependencyBalanceGraph(uint256 marketId_) internal view returns (uint256[] memory) {
return PlasmaVaultStorageLib.getDependencyBalanceGraph().dependencyGraph[marketId_];
}
/// @notice Updates the dependency balance graph for a specific market
/// @param marketId_ The ID of the market to update
/// @param newDependenceGraph_ Array of market IDs that should depend on this market
/// @dev Updates the market dependency relationships and emits an event
///
/// This function:
/// - Overwrites existing dependencies for the market
/// - Establishes new dependency relationships
/// - Triggers event for dependency tracking
///
/// Security considerations:
/// - Only callable by authorized governance functions
/// - Critical for maintaining market balance consistency
/// - Must prevent circular dependencies
/// - Should validate market existence
///
/// Common update scenarios:
/// - Adding new market dependencies
/// - Removing obsolete dependencies
/// - Modifying existing dependency chains
/// - Protocol integration changes
///
/// @dev Important: Changes to dependency graph affect:
/// - Balance update order
/// - Withdrawal validations
/// - Market rebalancing operations
/// - Protocol interaction flows
function updateDependencyBalanceGraph(uint256 marketId_, uint256[] memory newDependenceGraph_) internal {
PlasmaVaultStorageLib.getDependencyBalanceGraph().dependencyGraph[marketId_] = newDependenceGraph_;
emit DependencyBalanceGraphChanged(marketId_, newDependenceGraph_);
}
/// @notice Adds or subtracts an amount from the total assets in the Plasma Vault
/// @param amount_ The signed amount to adjust total assets by, represented in decimals of the underlying asset
/// @dev Updates the global total assets tracker based on market operations
///
/// Function behavior:
/// - Positive amount: Increases total assets
/// - Negative amount: Decreases total assets
/// - Zero amount: No effect
///
/// Used during:
/// - Market balance updates
/// - Fee realizations
/// - Asset rebalancing
/// - Withdrawal processing
///
/// Security considerations:
/// - Handles signed integers safely using SafeCast
/// - Only called during validated operations
/// - Must maintain accounting consistency
/// - Critical for share price calculations
///
/// @dev Important: This function affects:
/// - Total vault valuation
/// - Share price calculations
/// - Fee calculations
/// - Asset distribution checks
function addToTotalAssetsInAllMarkets(int256 amount_) internal {
if (amount_ < 0) {
PlasmaVaultStorageLib.getTotalAssets().value -= (-amount_).toUint256();
} else {
PlasmaVaultStorageLib.getTotalAssets().value += amount_.toUint256();
}
}
/// @notice Updates the total assets in the Plasma Vault for a specific market
/// @param marketId_ The ID of the market to update
/// @param newTotalAssetsInUnderlying_ The new total assets value for the market
/// @return deltaInUnderlying The net change in assets (positive or negative), represented in underlying decimals
/// @dev Updates market-specific asset tracking and calculates the change in total assets
///
/// Function behavior:
/// - Stores new total assets for the market
/// - Calculates delta between old and new values
/// - Returns signed delta for total asset updates
///
/// Used during:
/// - Balance fuse updates
/// - Market rebalancing
/// - Protocol interactions
/// - Asset redistribution
///
/// Security considerations:
/// - Handles asset value transitions safely
/// - Uses SafeCast for integer conversions
/// - Must be called within proper market context
/// - Critical for maintaining accurate balances
///
/// Integration points:
/// - Called by balance fuses after market operations
/// - Used in _updateMarketsBalances for batch updates
/// - Triggers market limit validations
/// - Affects total asset calculations
///
/// @dev Important: The returned delta is used by:
/// - addToTotalAssetsInAllMarkets
/// - Asset distribution protection checks
/// - Market balance event emissions
function updateTotalAssetsInMarket(
uint256 marketId_,
uint256 newTotalAssetsInUnderlying_
) internal returns (int256 deltaInUnderlying) {
uint256 oldTotalAssetsInUnderlying = PlasmaVaultStorageLib.getMarketTotalAssets().value[marketId_];
PlasmaVaultStorageLib.getMarketTotalAssets().value[marketId_] = newTotalAssetsInUnderlying_;
deltaInUnderlying = newTotalAssetsInUnderlying_.toInt256() - oldTotalAssetsInUnderlying.toInt256();
}
/// @notice Gets the management fee configuration data
/// @return managementFeeData The current management fee configuration containing:
/// - feeAccount: Address receiving management fees
/// - feeInPercentage: Current fee rate (basis points, 1/10000)
/// - lastUpdateTimestamp: Last time fees were realized
/// @dev Retrieves the current management fee settings from storage
///
/// Fee structure:
/// - Continuous time-based fee on assets under management (AUM)
/// - Fee percentage limited by MANAGEMENT_MAX_FEE_IN_PERCENTAGE (5%)
/// - Fees accrue linearly over time
/// - Realized during vault operations
///
/// Used for:
/// - Fee calculations in totalAssets()
/// - Fee realization during operations
/// - Management fee distribution
/// - Governance fee adjustments
///
/// Integration points:
/// - PlasmaVault._realizeManagementFee()
/// - PlasmaVault.totalAssets()
/// - FeeManager contract
/// - Governance configuration
///
/// @dev Important: Management fees:
/// - Are calculated based on total vault assets
/// - Affect share price calculations
/// - Must be realized before major vault operations
/// - Are distributed to configured fee recipients
function getManagementFeeData()
internal
view
returns (PlasmaVaultStorageLib.ManagementFeeData memory managementFeeData)
{
return PlasmaVaultStorageLib.getManagementFeeData();
}
/// @notice Configures the management fee settings for the vault
/// @param feeAccount_ The address that will receive management fees
/// @param feeInPercentage_ The management fee rate in basis points (100 = 1%)
/// @dev Updates fee configuration and emits event
///
/// Parameter requirements:
/// - feeAccount_: Must be non-zero address. The address of the technical Management Fee Account that will receive the management fee collected by the Plasma Vault and later on distributed to IPOR DAO and recipients by FeeManager
/// - feeInPercentage_: Must not exceed MANAGEMENT_MAX_FEE_IN_PERCENTAGE (5%)
///
/// Fee account types:
/// - FeeManager contract: Distributes fees to IPOR DAO and other recipients
/// - EOA/MultiSig: Receives fees directly without distribution
/// - Technical account: Temporary fee collection before distribution
///
/// Fee percentage format:
/// - Uses 2 decimal places (basis points)
/// - Examples:
/// - 10000 = 100%
/// - 100 = 1%
/// - 1 = 0.01%
///
/// Security considerations:
/// - Only callable by authorized governance functions
/// - Validates fee percentage against maximum limit
/// - Emits event for tracking changes
/// - Critical for vault economics
///
/// @dev Important: Changes affect:
/// - Future fee calculations
/// - Share price computations
/// - Vault revenue distribution
/// - Total asset calculations
function configureManagementFee(address feeAccount_, uint256 feeInPercentage_) internal {
if (feeAccount_ == address(0)) {
revert Errors.WrongAddress();
}
if (feeInPercentage_ > MANAGEMENT_MAX_FEE_IN_PERCENTAGE) {
revert InvalidManagementFee(feeInPercentage_);
}
PlasmaVaultStorageLib.ManagementFeeData storage managementFeeData = PlasmaVaultStorageLib
.getManagementFeeData();
managementFeeData.feeAccount = feeAccount_;
managementFeeData.feeInPercentage = feeInPercentage_.toUint16();
emit ManagementFeeDataConfigured(feeAccount_, feeInPercentage_);
}
/// @notice Gets the performance fee configuration data
/// @return performanceFeeData The current performance fee configuration containing:
/// - feeAccount: The address of the technical Performance Fee Account that will receive the performance fee collected by the Plasma Vault and later on distributed to IPOR DAO and recipients by FeeManager
/// - feeInPercentage: Current fee rate (basis points, 1/10000)
/// @dev Retrieves the current performance fee settings from storage
///
/// Fee structure:
/// - Charged on positive vault performance
/// - Fee percentage limited by PERFORMANCE_MAX_FEE_IN_PERCENTAGE (50%)
/// - Calculated on realized gains only
/// - Applied during execute() operations
///
/// Used for:
/// - Performance fee calculations
/// - Fee realization during profitable operations
/// - Performance fee distribution
/// - Governance fee adjustments
///
/// Integration points:
/// - PlasmaVault._addPerformanceFee()
/// - PlasmaVault.execute()
/// - FeeManager contract
/// - Governance configuration
///
/// @dev Important: Performance fees:
/// - Only charged on positive performance
/// - Calculated based on profit since last fee realization
/// - Minted as new vault shares
/// - Distributed to configured fee recipients
function getPerformanceFeeData()
internal
view
returns (PlasmaVaultStorageLib.PerformanceFeeData memory performanceFeeData)
{
return PlasmaVaultStorageLib.getPerformanceFeeData();
}
/// @notice Configures the performance fee settings for the vault
/// @param feeAccount_ The address that will receive performance fees
/// @param feeInPercentage_ The performance fee rate in basis points (100 = 1%)
/// @dev Updates fee configuration and emits event
///
/// Parameter requirements:
/// - feeAccount_: Must be non-zero address. The address of the technical Performance Fee Account that will receive the performance fee collected by the Plasma Vault and later on distributed to IPOR DAO and recipients by FeeManager
/// - feeInPercentage_: Must not exceed PERFORMANCE_MAX_FEE_IN_PERCENTAGE (50%)
///
/// Fee account types:
/// - FeeManager contract: Distributes fees to IPOR DAO and other recipients
/// - EOA/MultiSig: Receives fees directly without distribution
/// - Technical account: Temporary fee collection before distribution
///
/// Fee percentage format:
/// - Uses 2 decimal places (basis points)
/// - Examples:
/// - 10000 = 100%
/// - 100 = 1%
/// - 1 = 0.01%
///
/// Security considerations:
/// - Only callable by authorized governance functions
/// - Validates fee percentage against maximum limit
/// - Emits event for tracking changes
/// - Critical for vault incentive structure
///
/// @dev Important: Changes affect:
/// - Profit sharing calculations
/// - Alpha incentive alignment
/// - Vault performance metrics
/// - Revenue distribution model
function configurePerformanceFee(address feeAccount_, uint256 feeInPercentage_) internal {
if (feeAccount_ == address(0)) {
revert Errors.WrongAddress();
}
if (feeInPercentage_ > PERFORMANCE_MAX_FEE_IN_PERCENTAGE) {
revert InvalidPerformanceFee(feeInPercentage_);
}
PlasmaVaultStorageLib.PerformanceFeeData storage performanceFeeData = PlasmaVaultStorageLib
.getPerformanceFeeData();
performanceFeeData.feeAccount = feeAccount_;
performanceFeeData.feeInPercentage = feeInPercentage_.toUint16();
emit PerformanceFeeDataConfigured(feeAccount_, feeInPercentage_);
}
/// @notice Updates the management fee timestamp for fee accrual tracking
/// @dev Updates lastUpdateTimestamp to current block timestamp for fee calculations
///
/// Function behavior:
/// - Sets lastUpdateTimestamp to current block.timestamp
/// - Used to mark points of fee realization
/// - Critical for time-based fee calculations
///
/// Called during:
/// - Fee realization operations
/// - Deposit transactions
/// - Withdrawal transactions
/// - Share minting/burning
///
/// Integration points:
/// - PlasmaVault._realizeManagementFee()
/// - PlasmaVault.deposit()
/// - PlasmaVault.withdraw()
/// - PlasmaVault.mint()
///
/// @dev Important considerations:
/// - Must be called after fee realization
/// - Affects future fee calculations
/// - Uses uint32 for timestamp storage
/// - Critical for fee accounting accuracy
function updateManagementFeeData() internal {
PlasmaVaultStorageLib.ManagementFeeData storage feeData = PlasmaVaultStorageLib.getManagementFeeData();
feeData.lastUpdateTimestamp = block.timestamp.toUint32();
}
/// @notice Gets the ordered list of instant withdrawal fuses
/// @return address[] Array of fuse addresses in withdrawal priority order
/// @dev Retrieves the configured withdrawal path sequence
///
/// Function behavior:
/// - Returns ordered array of fuse addresses
/// - Empty array if no withdrawal paths configured
/// - Order determines withdrawal attempt sequence
/// - Same fuse can appear multiple times with different params
///
/// Used during:
/// - Withdrawal operations
/// - Instant withdrawal processing
/// - Withdrawal path validation
/// - Withdrawal strategy execution
///
/// Integration p
Submitted on: 2025-11-07 12:07:47
Comments
Log in to comment.
No comments yet.