RolesReceiver

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": {
    "src/RolesReceiver.sol": {
      "content": "// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.24;

import {OApp, Origin, MessagingFee, MessagingReceipt} from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol";
import {VaultConfig} from "./types/StrategyTypes.sol";
import {StrategyManager} from "./libraries/StrategyManager.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

/**
 * @title RolesReceiver
 * @author Variable Logic Labs, Corp (hello@blend.money)
 *
 * @notice A LayerZero OApp that receives role update messages from a trusted broadcaster and executes them on a Zodiac role modifier
 * @dev This contract acts as a bridge between LayerZero cross-chain messaging and Zodiac role management.
 *      It validates incoming messages from a trusted source chain and broadcaster before executing role updates
 *      on a Zodiac role modifier contract. The contract inherits from OApp for LayerZero functionality,
 *      Ownable for access control, and StrategyManager for strategy configuration management.
 *
 * @notice Security Model: Owner/Delegate Privilege Mitigation
 * @dev This contract's security relies on restricting the `delegate`'s privileges. While several functions are
 *      overridden to prevent misuse by the `delegate` (e.g., `setPeer`, `_lzSend`), a significant trust assumption remains.
 *      The `delegate` of this OApp can call `setDefaultReceiveLibrary()` on the LayerZero endpoint, potentially
 *      installing a malicious library that could bypass this contract's validation logic in `_lzReceive` and spoof
 *      messages from the trusted broadcaster and chain.
 *
 *      To mitigate this, the `delegate` address (set as `_owner` in the constructor) MUST be a Timelock contract
 *      or a secure multi-sig wallet. This ensures that any change to the endpoint configuration is subject to a
 *      time delay and/or a multi-party approval process, providing an opportunity to detect and prevent malicious actions.
 *
 *      Hardened security measures in this contract include:
 *      - `setPeer()` always reverts - prevents changing trusted peers after deployment.
 *      - `_lzReceive()` only accepts messages from `TRUSTED_BROADCASTER` on `TRUSTED_CHAIN_ID`.
 *      - `_lzSend()` is overridden to revert - prevents sending messages.
 */
contract RolesReceiver is OApp, StrategyManager {
    /*//////////////////////////////////////////////////////////////
                                CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /// @notice Keccak256 selector for updateStrategyConfig
    /// @dev precomputed at compile time to avoid repeated keccak256 at runtime
    bytes4 private constant _UPDATE_VAULT_CONFIG_SELECTOR =
        bytes4(keccak256("updateVaultConfig(address,(address,(bytes32,address,uint256,bool,bytes)[],(address)[]))"));

    /// @notice Keccak256 selector for setExecutor
    /// @dev precomputed at compile time to avoid repeated keccak256 at runtime
    bytes4 private constant _SET_EXECUTOR_SELECTOR = bytes4(keccak256("setExecutor(address)"));

    /*//////////////////////////////////////////////////////////////
                            STATE VARIABLES
    //////////////////////////////////////////////////////////////*/

    /// @notice The bytes32 representation of the trusted broadcaster that can send role update messages
    /// @dev This bytes32 value must match the sender of incoming LayerZero messages for validation
    bytes32 public immutable TRUSTED_BROADCASTER;

    /// @notice The LayerZero endpoint ID of the trusted source chain
    /// @dev Messages from other chains will be rejected during validation
    uint32 public immutable TRUSTED_CHAIN_ID;

    /// @notice The address of the trusted broadcaster on the same chain for direct calls
    /// @dev Used to validate same-chain direct calls from RolesBroadcaster
    address public immutable TRUSTED_BROADCASTER_ADDRESS;

    /*//////////////////////////////////////////////////////////////
                                ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @notice Thrown when a message is received from an untrusted source chain
    /// @dev Occurs when origin.srcEid doesn't match trustedChainId
    error InvalidSourceChain();

    /// @notice Thrown when a message is received from an untrusted broadcaster
    /// @dev Occurs when the sender address doesn't match trustedBroadcaster
    error InvalidBroadcaster();

    /// @notice Thrown when a message is received that is not a valid selector
    /// @dev Occurs when the selector is not a valid selector
    error InvalidSelector();

    /// @notice Thrown when a message is received that is formed incorrectly
    /// @dev Occurs when the message is not formed correctly
    error InvalidMessagePayload();

    /// @notice Thrown when a message is received that is not supported
    /// @dev Occurs when the selector is not a valid selector
    error NOT_SUPPORTED();

    /// @notice Thrown when same-chain call is made by unauthorized caller
    /// @dev Occurs when processSameChainCall is called by non-trusted broadcaster
    error UnauthorizedSameChainCall();

    /*//////////////////////////////////////////////////////////////
                                EVENTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Emitted when a LayerZero message is successfully received and processed
     * @param messageId The unique identifier of the received message
     * @param message The raw message data that was processed
     */
    event MessageReceived(bytes32 indexed messageId, bytes message);

    /*//////////////////////////////////////////////////////////////
                              CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Initializes the RolesReceiver contract with LayerZero and trusted source configuration
     * @param _lzEndpoint The address of the LayerZero endpoint for this chain
     * @param _owner The address that will own this contract and also be set as the OApp delegate
     * @param _trustedBroadcaster The address of the trusted broadcaster that can send role updates
     * @param _trustedChainId The LayerZero endpoint ID of the trusted source chain
     * @param _minSecondsBetweenOperations The minimum time in seconds that must elapse between operations on the same safe
     * @dev The constructor sets up the OApp with the LayerZero endpoint and establishes the trusted source parameters.
     *      It is critical that the `_owner` is a Timelock or a secure multi-sig wallet to mitigate risks associated
     *      with the OApp delegate role. See the contract-level security notice for more details.
     */
    constructor(
        address _lzEndpoint,
        address _owner,
        address _trustedBroadcaster,
        uint32 _trustedChainId,
        uint256 _minSecondsBetweenOperations
    ) OApp(_lzEndpoint, _owner) Ownable(_owner) StrategyManager(_minSecondsBetweenOperations) {
        TRUSTED_BROADCASTER = bytes32(uint256(uint160(_trustedBroadcaster)));
        TRUSTED_BROADCASTER_ADDRESS = _trustedBroadcaster;
        TRUSTED_CHAIN_ID = _trustedChainId;

        _setPeer(_trustedChainId, TRUSTED_BROADCASTER);
    }

    /*//////////////////////////////////////////////////////////////
                            EXTERNAL FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Reverts when a peer is set
     * @dev This function is overridden to prevent setting peers
     */
    function setPeer(uint32, bytes32) public pure override {
        revert NOT_SUPPORTED();
    }

    /**
     * @notice Reverts when a message is sent
     * @dev This function is overridden to prevent sending messages
     */
    function _lzSend(uint32, bytes memory, bytes memory, MessagingFee memory, address)
        internal
        virtual
        override
        returns (MessagingReceipt memory)
    {
        revert NOT_SUPPORTED();
    }

    /**
     * @notice Processes a same-chain role update call directly from the trusted broadcaster
     * @param message The encoded message data containing the role update call
     * @dev This function allows the trusted broadcaster on the same chain to directly call
     *      role update functions without going through LayerZero messaging
     * @dev Only the trusted broadcaster address can call this function
     * @dev Uses the same validation and processing logic as _lzReceive but for same-chain calls
     */
    function processSameChainCall(bytes calldata message) external {
        // Validate that the caller is the trusted broadcaster on the same chain
        require(msg.sender == TRUSTED_BROADCASTER_ADDRESS, UnauthorizedSameChainCall());
        // Process the message using the same logic as _lzReceive
        _processRoleUpdateMessage(message, bytes32(0)); // Use empty guid for same-chain calls
    }

    /*//////////////////////////////////////////////////////////////
                           INTERNAL FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Internal function called by LayerZero when a message is received
     * @param origin The origin information including source chain ID and sender address
     * @param guid The globally unique identifier for this message
     * @param message The encoded message data containing the role update call for the Zodiac role modifier
     * @dev This function validates the source chain and broadcaster, then executes the role update on the Zodiac role modifier.
     *      The message should contain the encoded function call data for the role modifier contract (e.g., assignRoles, revokeRoles).
     *      The function is protected by whenNotPaused modifier to allow emergency stops.
     * @dev Reverts with InvalidSourceChain if the message doesn't come from the trusted chain
     * @dev Reverts with InvalidBroadcaster if the sender is not the trusted broadcaster
     * @dev Reverts with InvalidSelector if the selector is not a valid selector
     * @custom:security Only the trusted broadcaster can send messages to this contract
     */
    function _lzReceive(
        Origin calldata origin,
        bytes32 guid,
        bytes calldata message,
        address, // executor - unused
        bytes calldata // extraData - unused
    ) internal override {
        // Validate source chain
        require(origin.srcEid == TRUSTED_CHAIN_ID, InvalidSourceChain());
        // Validate broadcaster
        require(origin.sender == TRUSTED_BROADCASTER, InvalidBroadcaster());
        // Process the message using shared logic
        _processRoleUpdateMessage(message, guid);
    }

    /**
     * @notice Internal function to process role update messages from both LayerZero and same-chain calls
     * @param message The encoded message data containing the role update call
     * @param guid The globally unique identifier for this message (empty for same-chain calls)
     * @dev This function contains the shared logic for processing role update messages regardless of source
     * @dev Reverts with InvalidSelector if the selector is not supported
     * @dev Reverts with InvalidMessagePayload if the message format is incorrect
     */
    function _processRoleUpdateMessage(bytes calldata message, bytes32 guid) internal {
        bytes4 selector = bytes4(message);

        if (selector == _UPDATE_VAULT_CONFIG_SELECTOR) {
            // Decode the market configurations update
            (address vaultAddress, VaultConfig memory configs) = abi.decode(message[4:], (address, VaultConfig));
            // Update strategy with market configs
            _updateVaultConfig(vaultAddress, configs);
        } else if (selector == _SET_EXECUTOR_SELECTOR) {
            // There must be exactly 1 address (20 bytes, padded out to 32 due to function encoding) + 4 bytes
            require(message.length == 36, InvalidMessagePayload());
            // Decode the executor address
            (address executor) = abi.decode(message[4:], (address));
            // Set the executor
            _setExecutor(executor);
        } else {
            // We don't support any other selectors
            revert InvalidSelector();
        }

        emit MessageReceived(guid, message);
    }
}
"
    },
    "lib/devtools/packages/oapp-evm/contracts/oapp/OApp.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

// @dev Import the 'MessagingFee' and 'MessagingReceipt' so it's exposed to OApp implementers
// solhint-disable-next-line no-unused-import
import { OAppSender, MessagingFee, MessagingReceipt } from "./OAppSender.sol";
// @dev Import the 'Origin' so it's exposed to OApp implementers
// solhint-disable-next-line no-unused-import
import { OAppReceiver, Origin } from "./OAppReceiver.sol";
import { OAppCore } from "./OAppCore.sol";

/**
 * @title OApp
 * @dev Abstract contract serving as the base for OApp implementation, combining OAppSender and OAppReceiver functionality.
 */
abstract contract OApp is OAppSender, OAppReceiver {
    /**
     * @dev Constructor to initialize the OApp with the provided endpoint and owner.
     * @param _endpoint The address of the LOCAL LayerZero endpoint.
     * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.
     */
    constructor(address _endpoint, address _delegate) OAppCore(_endpoint, _delegate) {}

    /**
     * @notice Retrieves the OApp version information.
     * @return senderVersion The version of the OAppSender.sol implementation.
     * @return receiverVersion The version of the OAppReceiver.sol implementation.
     */
    function oAppVersion()
        public
        pure
        virtual
        override(OAppSender, OAppReceiver)
        returns (uint64 senderVersion, uint64 receiverVersion)
    {
        return (SENDER_VERSION, RECEIVER_VERSION);
    }
}
"
    },
    "src/types/StrategyTypes.sol": {
      "content": "// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.24;

import {IVaultController} from "../interfaces/controllers/IVaultController.sol";
import {IMarketAdapterController} from "../interfaces/controllers/IMarketAdapterController.sol";
import {IVaultActionController} from "../interfaces/controllers/IVaultActionController.sol";

/// @notice Configuration parameters for a specific market within a strategy
/// @dev Contains market identifier, adapter contract, and leverage settings
struct MarketConfig {
    /// @notice Unique identifier for the market
    /// @dev Usually a hash of market parameters like tokens, oracles etc
    bytes32 marketId;
    /// @notice Controller contract for interacting with the market adapter
    /// @dev Handles protocol-specific market interactions and position management
    IMarketAdapterController adapter;
    /// @notice Target leverage ratio for positions in this market
    /// @dev Expressed as a fixed-point number where 1e18 = 1x leverage
    uint256 leverage;
    /// @notice Boolean indicating if the market is a wrapper
    /// @dev If true, the market is a wrapper and the adapter will use the underlying asset
    bool isWrapped;
    /// @notice Specific bytecode to be deconstructed
    bytes data;
}

struct ActionConfig {
    /// @notice Controller contract for interacting with the action
    IVaultActionController controller;
    /// @notice Specific bytecode to be deconstructed
    bytes data;
}

/// @notice Configuration parameters for a specific vault within a strategy
/// @dev Contains control contract and series of market configs
struct VaultConfig {
    /// @notice Control contract for the lending protocol
    IVaultController control;
    /// @notice Array of adapters that is designed to be run inside of a flashloan context
    MarketConfig[] markets;
    /// @notice Set of hooks that are designed to make targeted changes to user positions
    ActionConfig[] actions;
}
"
    },
    "src/libraries/StrategyManager.sol": {
      "content": "// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.24;

import {VaultConfig} from "../types/StrategyTypes.sol";
import {RebalanceData} from "../types/RebalanceTypes.sol";
import {IVaultActionController} from "../interfaces/controllers/IVaultActionController.sol";
import {IVaultController} from "../interfaces/controllers/IVaultController.sol";
import {IModuleManager} from "safe-contracts/interfaces/IModuleManager.sol";
import {Enum} from "safe-contracts/libraries/Enum.sol";

/**
 * @title StrategyManager
 * @author Variable Logic Labs, Corp (hello@blend.money)
 * @notice Abstract contract responsible for managing strategy configurations
 * @dev This contract provides storage and management functions for strategy configurations,
 *      separating the concern of strategy management from other contract responsibilities.
 *      Security: Reentrancy protection is provided by the onlyExecutor modifier and per-vault rate limiting.
 */
abstract contract StrategyManager {
    /*//////////////////////////////////////////////////////////////
                            STATE VARIABLES
    //////////////////////////////////////////////////////////////*/

    /// @notice Mapping to store market configurations for each vault
    /// @dev Maps VaultId to their corresponding array of MarketConfigs
    mapping(address => VaultConfig) public strategyConfig;

    /// @notice A mapping of (safe, vault) pairs to the timestamp of their last completed operation
    /// @dev Key is keccak256(abi.encodePacked(safe, vault)). Used to enforce per-vault rate limiting and prevent rapid-fire operations
    mapping(bytes32 => uint256) public lastOperationTimestamp;

    /// @notice Boolean flag that the `executeRebalance` function has been entered
    /// @dev In practice, this will only be set to true if inside the `executeRebalance` function
    bool public isRebalanceInitiated;

    /// @notice Boolean flag that the `executeVaultAction` function has been entered
    /// @dev In practice, this will only be set to true if inside the `executeVaultAction` function
    bool public isVaultActionInitiated;

    /// @notice This is the address that is able to execute transactions on the strategy manager
    address public executor;

    /// @notice The minimum time between operations in seconds
    uint256 public immutable MIN_SECONDS_BETWEEN_OPERATIONS;

    /// @notice Storage slot to avoid collisions for temporary seen items mapping
    bytes32 private constant TEMP_SEEN_SLOT = keccak256("blend.temp.seen.items");

    /*//////////////////////////////////////////////////////////////
                                EVENTS
    //////////////////////////////////////////////////////////////*/

    /// @notice Emitted when the executor is set
    /// @param executor The address of the executor
    event ExecutorSet(address indexed executor);

    /// @notice Emitted when a rebalance is executed
    /// @param safe The address of the safe that executed the rebalance
    /// @param vault The address of the vault that was rebalanced
    event RebalanceExecuted(address indexed safe, address indexed vault);

    /// @notice Emitted when a vault action is executed
    /// @param safe The address of the safe that executed the vault action
    /// @param vault The address of the vault that was acted on
    event VaultActionExecuted(address indexed safe, address indexed vault);

    /*//////////////////////////////////////////////////////////////
                            ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @notice Thrown when the caller is not the executor
    error NotExecutor();

    /// @notice Thrown when the vault is not valid
    error InvalidVault();

    /// @notice Thrown when the VaultConfig is invalid
    error InvalidVaultConfig();

    /// @notice Thrown when the safe throws an error
    /// @dev This is a generic error that occurs when a safe `execTransactionFromModule` fails
    error SafeExecutionError();

    /// @notice Thrown when the specified action controller is not approved for the given vault.
    error InvalidActionController();

    /// @notice Thrown when the `execBalance` function has already been entered
    error AlreadyInitiated();

    /// @notice Thrown when an operation is performed too frequently
    error RateLimited();

    /// @notice Thrown when the minimum seconds between operations specified in constructor is zero
    error InvalidMinSecondsBetweenOperations();

    /*//////////////////////////////////////////////////////////////
                            MODIFIERS
    //////////////////////////////////////////////////////////////*/

    /// @notice Modifier to ensure the caller is the executor
    modifier onlyExecutor() {
        require(msg.sender == executor, NotExecutor());
        _;
    }

    /// @notice Modifier to ensure the vault is valid
    modifier validVault(address vault) {
        require(address(strategyConfig[vault].control) != address(0), InvalidVault());
        _;
    }

    /**
     * @notice Ensures that the action controller is valid and approved for the specified vault.
     * @dev It checks that the vault is valid and then verifies that the `actionController`
     * is present in the list of approved action controllers for that vault.
     * @param vault The address of the vault.
     * @param actionController The action controller to validate.
     */
    modifier validActionController(address vault, IVaultActionController actionController) {
        require(vault != address(0), InvalidVault());

        VaultConfig memory vaultConfig = strategyConfig[vault];
        require(address(vaultConfig.control) != address(0), InvalidVault());

        // Check if the action controller is one of the allowed action controllers
        bool found = false;
        for (uint256 i = 0; i < vaultConfig.actions.length; ++i) {
            if (vaultConfig.actions[i].controller == actionController) {
                found = true;
                break;
            }
        }
        require(found, InvalidActionController());
        _;
    }

    /// @notice Modifier to ensure the `executeRebalance` function has not been entered
    modifier initiateRebalance() {
        require(!isRebalanceInitiated, AlreadyInitiated());
        isRebalanceInitiated = true;
        _;
        isRebalanceInitiated = false;
    }

    /// @notice Modifier to ensure the `executeVaultAction` function has not been entered
    modifier initiateVaultAction() {
        require(!isVaultActionInitiated, AlreadyInitiated());
        isVaultActionInitiated = true;
        _;
        isVaultActionInitiated = false;
    }

    /*//////////////////////////////////////////////////////////////
                            CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Constructor for the StrategyManager
     * @param _minSecondsBetweenOperations The minimum time in seconds that must elapse between operations on the same safe
     * @dev Reverts if _minSecondsBetweenOperations is zero or greater than 300 seconds
     */
    constructor(uint256 _minSecondsBetweenOperations) {
        require(
            _minSecondsBetweenOperations > 0 && _minSecondsBetweenOperations <= 300,
            InvalidMinSecondsBetweenOperations()
        );
        MIN_SECONDS_BETWEEN_OPERATIONS = _minSecondsBetweenOperations;
    }

    /*//////////////////////////////////////////////////////////////
                         INTERNAL FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Gets the temporary seen items mapping from a specific storage slot
     * @return m The mapping at the designated storage slot
     * @dev Uses assembly to access a specific storage slot to avoid collisions
     */
    function _tempSeen() internal pure returns (mapping(bytes32 => bool) storage m) {
        bytes32 slot = TEMP_SEEN_SLOT;
        assembly {
            m.slot := slot
        }
    }

    /**
     * @notice Clears the temporary seen items mapping
     * @param config The vault configuration containing the items to clear
     * @dev Deletes entries from the temporary mapping to avoid storage bloat
     */
    function _clearTempSeen(VaultConfig memory config) internal {
        mapping(bytes32 => bool) storage seen = _tempSeen();
        for (uint256 i = 0; i < config.markets.length; i++) {
            delete seen[config.markets[i].marketId];
        }
        for (uint256 i = 0; i < config.actions.length; i++) {
            delete seen[bytes32(uint256(uint160(address(config.actions[i].controller))))];
        }
    }

    /**
     * @notice Internal function to set the executor
     * @param _executor The address of the executor
     * @dev *BE CAREFUL* this function gives the executor the ability to execute txns on behalf of user safes
     */
    function _setExecutor(address _executor) internal {
        executor = _executor;
        emit ExecutorSet(_executor);
    }

    /**
     * @notice Internal function to update a vault configuration
     * @param vaultId The vault identifier to update the vault config for
     * @param newConfig The vault configuration to set
     * @dev This function replaces the entire vault config for a vault.
     *      It checks for duplicate market IDs, lower bound leverage, non-zero market controller,
     *      duplicate action controllers, and non-zero action controller.
     *      It then updates the vault config.
     */
    function _updateVaultConfig(address vaultId, VaultConfig memory newConfig) internal {
        require(address(newConfig.control) != address(0), InvalidVaultConfig());

        mapping(bytes32 => bool) storage seen = _tempSeen();

        // Check for duplicate market IDs using O(n) approach
        for (uint256 i = 0; i < newConfig.markets.length; i++) {
            bytes32 marketId = newConfig.markets[i].marketId;
            require(!seen[marketId], InvalidVaultConfig());
            seen[marketId] = true;

            // Check for lower bound leverage
            require(newConfig.markets[i].leverage >= 1e18, InvalidVaultConfig());
            // Check to ensure that the market address is not zero
            require(address(newConfig.markets[i].adapter) != address(0), InvalidVaultConfig());
        }

        // Check for duplicate action controllers using O(n) approach
        for (uint256 i = 0; i < newConfig.actions.length; i++) {
            bytes32 actionKey = bytes32(uint256(uint160(address(newConfig.actions[i].controller))));
            require(!seen[actionKey], InvalidVaultConfig());
            seen[actionKey] = true;

            // Check to ensure that the action controller is not zero
            require(address(newConfig.actions[i].controller) != address(0), InvalidVaultConfig());
        }
        // Clean up temporary storage to avoid storage bloat
        _clearTempSeen(newConfig);

        // Update the vault config
        strategyConfig[vaultId] = newConfig;
    }

    /**
     * @notice Internal function to verify the rate limit for a safe-vault pair
     * @param safe The address of the safe
     * @param vault The address of the vault
     * @dev This function checks if the minimum seconds between operations has elapsed since the last operation on this safe-vault pair
     * @custom:reverts RateLimited if insufficient time has passed since the last operation on this safe-vault pair
     */
    function _verifyRateLimit(address safe, address vault) internal {
        bytes32 safeVaultKey = keccak256(abi.encodePacked(safe, vault));
        require(block.timestamp - lastOperationTimestamp[safeVaultKey] > MIN_SECONDS_BETWEEN_OPERATIONS, RateLimited());
        lastOperationTimestamp[safeVaultKey] = block.timestamp;
    }

    /*//////////////////////////////////////////////////////////////
                            EXTERNAL FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Executes a rebalance on a vault
     * @param safe The address of the safe that will execute the rebalance
     * @param vault The address of the vault that will be rebalanced
     * @param rebalanceData The data for the rebalance
     * @dev Enforces rate limiting per (safe, vault) pair by checking that MIN_SECONDS_BETWEEN_OPERATIONS has elapsed since the last operation
     * @custom:reverts RateLimited if insufficient time has passed since the last operation on this safe-vault pair
     * @custom:reverts SafeExecutionError if the safe's execTransactionFromModule call fails
     */
    function executeRebalance(address safe, address vault, RebalanceData[] calldata rebalanceData)
        public
        onlyExecutor
        validVault(vault)
        initiateRebalance
    {
        _verifyRateLimit(safe, vault);

        bool success = IModuleManager(safe).execTransactionFromModule(
            address(strategyConfig[vault].control),
            0,
            abi.encodeWithSelector(IVaultController.executeRebalance.selector, vault, rebalanceData),
            Enum.Operation.DelegateCall
        );
        require(success, SafeExecutionError());
        emit RebalanceExecuted(safe, vault);
    }

    /**
     * @notice Executes a generic vault action through a designated controller.
     * @dev This function enables the executor to trigger a vault action on behalf of a user's Gnosis Safe.
     *      It constructs a call to the safe's `execTransactionFromModule`, targeting the specified
     *      `actionController`. This ensures actions are executed within the safe's context.
     *      The function is protected by modifiers to ensure the caller is the executor, the vault is valid,
     *      and the action controller is approved for the given vault.
     *      Enforces rate limiting per (safe, vault) pair by checking that MIN_SECONDS_BETWEEN_OPERATIONS has elapsed since the last operation.
     * @param safe The address of the user's Gnosis Safe that will execute the transaction.
     * @param vault The address of the vault being acted upon. Used for validation purposes.
     * @param actionController The controller contract responsible for executing the specific action.
     * @param data The ABI-encoded calldata for the action to be executed by the `actionController`.
     * @custom:reverts RateLimited if insufficient time has passed since the last operation on this safe-vault pair
     * @custom:reverts SafeExecutionError if the safe's execTransactionFromModule call fails
     */
    function executeVaultAction(
        address safe,
        address vault,
        IVaultActionController actionController,
        bytes calldata data
    ) external onlyExecutor validVault(vault) validActionController(vault, actionController) initiateVaultAction {
        _verifyRateLimit(safe, vault);

        VaultConfig memory vaultConfig = strategyConfig[vault];
        bytes memory strategyData;
        for (uint256 i = 0; i < vaultConfig.actions.length; i++) {
            if (vaultConfig.actions[i].controller == actionController) {
                strategyData = vaultConfig.actions[i].data;
                break;
            }
        }
        bool success = IModuleManager(safe).execTransactionFromModule(
            address(actionController),
            0,
            abi.encodeWithSelector(IVaultActionController.executeAction.selector, vault, strategyData, data),
            Enum.Operation.DelegateCall
        );
        require(success, SafeExecutionError());
        emit VaultActionExecuted(safe, vault);
    }

    /**
     * @notice Returns the vault configuration for a given vault
     * @param vault The address of the vault to get the vault configuration for
     * @return The vault configuration for the given vault
     */
    function getVaultConfig(address vault) public view returns (VaultConfig memory) {
        return strategyConfig[vault];
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/access/Ownable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
"
    },
    "lib/devtools/packages/oapp-evm/contracts/oapp/OAppSender.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import { SafeERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { MessagingParams, MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { OAppCore } from "./OAppCore.sol";

/**
 * @title OAppSender
 * @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.
 */
abstract contract OAppSender is OAppCore {
    using SafeERC20 for IERC20;

    // Custom error messages
    error NotEnoughNative(uint256 msgValue);
    error LzTokenUnavailable();

    // @dev The version of the OAppSender implementation.
    // @dev Version is bumped when changes are made to this contract.
    uint64 internal constant SENDER_VERSION = 1;

    /**
     * @notice Retrieves the OApp version information.
     * @return senderVersion The version of the OAppSender.sol contract.
     * @return receiverVersion The version of the OAppReceiver.sol contract.
     *
     * @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.
     * ie. this is a SEND only OApp.
     * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions
     */
    function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {
        return (SENDER_VERSION, 0);
    }

    /**
     * @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.
     * @param _dstEid The destination endpoint ID.
     * @param _message The message payload.
     * @param _options Additional options for the message.
     * @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.
     * @return fee The calculated MessagingFee for the message.
     *      - nativeFee: The native fee for the message.
     *      - lzTokenFee: The LZ token fee for the message.
     */
    function _quote(
        uint32 _dstEid,
        bytes memory _message,
        bytes memory _options,
        bool _payInLzToken
    ) internal view virtual returns (MessagingFee memory fee) {
        return
            endpoint.quote(
                MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),
                address(this)
            );
    }

    /**
     * @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.
     * @param _dstEid The destination endpoint ID.
     * @param _message The message payload.
     * @param _options Additional options for the message.
     * @param _fee The calculated LayerZero fee for the message.
     *      - nativeFee: The native fee.
     *      - lzTokenFee: The lzToken fee.
     * @param _refundAddress The address to receive any excess fee values sent to the endpoint.
     * @return receipt The receipt for the sent message.
     *      - guid: The unique identifier for the sent message.
     *      - nonce: The nonce of the sent message.
     *      - fee: The LayerZero fee incurred for the message.
     */
    function _lzSend(
        uint32 _dstEid,
        bytes memory _message,
        bytes memory _options,
        MessagingFee memory _fee,
        address _refundAddress
    ) internal virtual returns (MessagingReceipt memory receipt) {
        // @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.
        uint256 messageValue = _payNative(_fee.nativeFee);
        if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);

        return
            // solhint-disable-next-line check-send-result
            endpoint.send{ value: messageValue }(
                MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0),
                _refundAddress
            );
    }

    /**
     * @dev Internal function to pay the native fee associated with the message.
     * @param _nativeFee The native fee to be paid.
     * @return nativeFee The amount of native currency paid.
     *
     * @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,
     * this will need to be overridden because msg.value would contain multiple lzFees.
     * @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.
     * @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.
     * @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.
     */
    function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {
        if (msg.value != _nativeFee) revert NotEnoughNative(msg.value);
        return _nativeFee;
    }

    /**
     * @dev Internal function to pay the LZ token fee associated with the message.
     * @param _lzTokenFee The LZ token fee to be paid.
     *
     * @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.
     * @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().
     */
    function _payLzToken(uint256 _lzTokenFee) internal virtual {
        // @dev Cannot cache the token because it is not immutable in the endpoint.
        address lzToken = endpoint.lzToken();
        if (lzToken == address(0)) revert LzTokenUnavailable();

        // Pay LZ token fee by sending tokens to the endpoint.
        IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);
    }
}
"
    },
    "lib/devtools/packages/oapp-evm/contracts/oapp/OAppReceiver.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import { IOAppReceiver, Origin } from "./interfaces/IOAppReceiver.sol";
import { OAppCore } from "./OAppCore.sol";

/**
 * @title OAppReceiver
 * @dev Abstract contract implementing the ILayerZeroReceiver interface and extending OAppCore for OApp receivers.
 */
abstract contract OAppReceiver is IOAppReceiver, OAppCore {
    // Custom error message for when the caller is not the registered endpoint/
    error OnlyEndpoint(address addr);

    // @dev The version of the OAppReceiver implementation.
    // @dev Version is bumped when changes are made to this contract.
    uint64 internal constant RECEIVER_VERSION = 2;

    /**
     * @notice Retrieves the OApp version information.
     * @return senderVersion The version of the OAppSender.sol contract.
     * @return receiverVersion The version of the OAppReceiver.sol contract.
     *
     * @dev Providing 0 as the default for OAppSender version. Indicates that the OAppSender is not implemented.
     * ie. this is a RECEIVE only OApp.
     * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions.
     */
    function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {
        return (0, RECEIVER_VERSION);
    }

    /**
     * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.
     * @dev _origin The origin information containing the source endpoint and sender address.
     *  - srcEid: The source chain endpoint ID.
     *  - sender: The sender address on the src chain.
     *  - nonce: The nonce of the message.
     * @dev _message The lzReceive payload.
     * @param _sender The sender address.
     * @return isSender Is a valid sender.
     *
     * @dev Applications can optionally choose to implement separate composeMsg senders that are NOT the bridging layer.
     * @dev The default sender IS the OAppReceiver implementer.
     */
    function isComposeMsgSender(
        Origin calldata /*_origin*/,
        bytes calldata /*_message*/,
        address _sender
    ) public view virtual returns (bool) {
        return _sender == address(this);
    }

    /**
     * @notice Checks if the path initialization is allowed based on the provided origin.
     * @param origin The origin information containing the source endpoint and sender address.
     * @return Whether the path has been initialized.
     *
     * @dev This indicates to the endpoint that the OApp has enabled msgs for this particular path to be received.
     * @dev This defaults to assuming if a peer has been set, its initialized.
     * Can be overridden by the OApp if there is other logic to determine this.
     */
    function allowInitializePath(Origin calldata origin) public view virtual returns (bool) {
        return peers[origin.srcEid] == origin.sender;
    }

    /**
     * @notice Retrieves the next nonce for a given source endpoint and sender address.
     * @dev _srcEid The source endpoint ID.
     * @dev _sender The sender address.
     * @return nonce The next nonce.
     *
     * @dev The path nonce starts from 1. If 0 is returned it means that there is NO nonce ordered enforcement.
     * @dev Is required by the off-chain executor to determine the OApp expects msg execution is ordered.
     * @dev This is also enforced by the OApp.
     * @dev By default this is NOT enabled. ie. nextNonce is hardcoded to return 0.
     */
    function nextNonce(uint32 /*_srcEid*/, bytes32 /*_sender*/) public view virtual returns (uint64 nonce) {
        return 0;
    }

    /**
     * @dev Entry point for receiving messages or packets from the endpoint.
     * @param _origin The origin information containing the source endpoint and sender address.
     *  - srcEid: The source chain endpoint ID.
     *  - sender: The sender address on the src chain.
     *  - nonce: The nonce of the message.
     * @param _guid The unique identifier for the received LayerZero message.
     * @param _message The payload of the received message.
     * @param _executor The address of the executor for the received message.
     * @param _extraData Additional arbitrary data provided by the corresponding executor.
     *
     * @dev Entry point for receiving msg/packet from the LayerZero endpoint.
     */
    function lzReceive(
        Origin calldata _origin,
        bytes32 _guid,
        bytes calldata _message,
        address _executor,
        bytes calldata _extraData
    ) public payable virtual {
        // Ensures that only the endpoint can attempt to lzReceive() messages to this OApp.
        if (address(endpoint) != msg.sender) revert OnlyEndpoint(msg.sender);

        // Ensure that the sender matches the expected peer for the source endpoint.
        if (_getPeerOrRevert(_origin.srcEid) != _origin.sender) revert OnlyPeer(_origin.srcEid, _origin.sender);

        // Call the internal OApp implementation of lzReceive.
        _lzReceive(_origin, _guid, _message, _executor, _extraData);
    }

    /**
     * @dev Internal function to implement lzReceive logic without needing to copy the basic parameter validation.
     */
    function _lzReceive(
        Origin calldata _origin,
        bytes32 _guid,
        bytes calldata _message,
        address _executor,
        bytes calldata _extraData
    ) internal virtual;
}
"
    },
    "lib/devtools/packages/oapp-evm/contracts/oapp/OAppCore.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IOAppCore, ILayerZeroEndpointV2 } from "./interfaces/IOAppCore.sol";

/**
 * @title OAppCore
 * @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.
 */
abstract contract OAppCore is IOAppCore, Ownable {
    // The LayerZero endpoint associated with the given OApp
    ILayerZeroEndpointV2 public immutable endpoint;

    // Mapping to store peers associated with corresponding endpoints
    mapping(uint32 eid => bytes32 peer) public peers;

    /**
     * @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.
     * @param _endpoint The address of the LOCAL Layer Zero endpoint.
     * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.
     *
     * @dev The delegate typically should be set as the owner of the contract.
     */
    constructor(address _endpoint, address _delegate) {
        endpoint = ILayerZeroEndpointV2(_endpoint);

        if (_delegate == address(0)) revert InvalidDelegate();
        endpoint.setDelegate(_delegate);
    }

    /**
     * @notice Sets the peer address (OApp instance) for a corresponding endpoint.
     * @param _eid The endpoint ID.
     * @param _peer The address of the peer to be associated with the corresponding endpoint.
     *
     * @dev Only the owner/admin of the OApp can call this function.
     * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.
     * @dev Set this to bytes32(0) to remove the peer address.
     * @dev Peer is a bytes32 to accommodate non-evm chains.
     */
    function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOwner {
        _setPeer(_eid, _peer);
    }

    /**
     * @notice Sets the peer address (OApp instance) for a corresponding endpoint.
     * @param _eid The endpoint ID.
     * @param _peer The address of the peer to be associated with the corresponding endpoint.
     *
     * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.
     * @dev Set this to bytes32(0) to remove the peer address.
     * @dev Peer is a bytes32 to accommodate non-evm chains.
     */
    function _setPeer(uint32 _eid, bytes32 _peer) internal virtual {
        peers[_eid] = _peer;
        emit PeerSet(_eid, _peer);
    }

    /**
     * @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.
     * ie. the peer is set to bytes32(0).
     * @param _eid The endpoint ID.
     * @return peer The address of the peer associated with the specified endpoint.
     */
    function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {
        bytes32 peer = peers[_eid];
        if (peer == bytes32(0)) revert NoPeer(_eid);
        return peer;
    }

    /**
     * @notice Sets the delegate address for the OApp.
     * @param _delegate The address of the delegate to be set.
     *
     * @dev Only the owner/admin of the OApp can call this function.
     * @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.
     */
    function setDelegate(address _delegate) public onlyOwner {
        endpoint.setDelegate(_delegate);
    }
}
"
    },
    "src/interfaces/controllers/IVaultController.sol": {
      "content": "// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.24;

import {RebalanceData} from "../../types/RebalanceTypes.sol";

/**
 * @title IVaultController
 * @author Variable Logic Labs, Corp (hello@blend.money)
 * @notice Defines the interface for a vault controller, which is responsible for executing actions on a vault.
 * @dev This interface is intended to be implemented by a contract that can manage and interact with various vault functionalities,
 * such as rebalancing and other vault-specific actions.
 */
interface IVaultController {
    /**
     * @notice Executes a rebalance action on the specified vault.
     * @dev This function is called to trigger a rebalancing of the vault's assets according to the provided data.
     * @param vault The address of the vault to be rebalanced.
     * @param rebalanceData An array of `RebalanceData` structs, each containing the necessary information for a rebalance operation.
     */
    function executeRebalance(address vault, RebalanceData[] calldata rebalanceData) external;
}
"
    },
    "src/interfaces/controllers/IMarketAdapterController.sol": {
      "content": "// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.24;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
 * @title IMarketAdapterController
 * @author Blend.Money (hello@blend.money)
 * @notice This interface defines the functions for swapping between loan and collateral tokens.
 */
interface IMarketAdapterController {
    /**
     * @notice Swaps a given amount of loan token for collateral token.
     * @dev The balance amount will be the entire balance of the loan token on the adapter contract.
     * @param loanToken The loan token to swap from.
     * @param collateralToken The collateral token to swap to.
     * @param recipient The recipient of the swapped collateral token.
     * @param strategyData Additional data for the strategy.
     * @param extraData Additional data for the swap.
     */
    function swapToCollateral(
        IERC20 loanToken,
        IERC20 collateralToken,
        address recipient,
        bytes calldata strategyData,
        bytes calldata extraData
    ) external;

    /**
     * @notice Swaps a given amount of collateral token for loan token.
     * @dev The balance amount will be the entire balance of the collateral token on the adapter contract.
     * @param loanToken The loan token to swap to.
     * @param collateralToken The collateral token to swap from.
     * @param recipient The recipient of the swapped loan token.
     * @param strategyData Additional data for the strategy.
     * @param extraData Additional data for the swap.
     */
    function swapToLoanToken(
        IERC20 loanToken,
        IERC20 collateralToken,
        address recipient,
        bytes calldata strategyData,
        bytes calldata extraData
    ) external;
}
"
    },
    "src/interfaces/controllers/IVaultActionController.sol": {
      "content": "// SPDX-License-Identifier: BSL-1.1
pragma solidity ^0.8.24;

import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";

/**
 * @title IVaultActionController
 * @author Variable Logic Labs, Corp (hello@blend.money)
 * @notice This interface allows an authorized address to perform targeted actions against user accounts.
 */
interface IVaultActionController {
    /**
     * @notice Performs a targeted action on behalf of a user.
     * @param vault The vault to be acted upon.
     * @param strategyData The encoded data for the strategy to be executed (dictated by the `RolesReceiver`)
     * @param extraData The encoded data for the action to be performed.
     */
    function executeAction(IERC4626 vault, bytes calldata strategyData, bytes calldata extraData) external;
}
"
    },
    "src/types/RebalanceTypes.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/**
 * @title RebalanceData
 * @notice Struct containing data required for rebalancing operations in vault strategies
 * @dev This struct is used to encapsulate all necessary information for executing
 *      rebalance operations, including whether to increase or decrease positions,
 *      the amount to rebalance, and any additional data needed for the operation.
 */
struct RebalanceData {
    /**
     * @notice The expected market ID to rebalance
     * @dev This will be used as a sanity check to ensure that
     *      the rebalance is being executed on the correct market
     */
    bytes32 marketId;
    /**
     * @notice Flag indicating whether this is an increase or decrease operation
     * @dev true = increase position, false = decrease position
     */
    bool isIncrease;
    /**
     * @notice The amount to rebalance in base units (e.g., wei for ETH)
     * @dev Must be greater than 0 for valid operations
     */
    uint256 amount;
    /**
     * @notice Additional data required for the rebalance operation
     * @dev This field can contain protocol-specific parameters, slippage settings,
     *      or any other data needed by the target adapter or strategy
     */
    bytes extraData;
}

/**
 * @title RebalanceAssetsPerShare
 * @notice Struct containing assets per share data required for rebalancing operations
 * @dev This struct is used to encapsulate all necessary assets per share data for executing
 *      rebalance operations, including the initial, final, and withdraw assets per share for the vault
 *      and collateral tokens.
 */
struct RebalanceAssetsPerShare {
    uint256 vaultDepositAssetsPerShare;
    uint256 vaultWithdrawAssetsPerShare;
    uint256 morphoBorrowAssetsPerShare;
    uint256 morphoRepayAssetsPerShare;
    uint256 wrappedCollateralAssetsPerShare;
}

/**
 * @title StoredRebalanceAssetsPerShare
 * @notice Parameters for vault and morpho market assets per share
 * @param vaultDepositAssetsPerShare Assets per share of the vault after Safe deposit
 * @param vaultWithdrawAssetsPerShare Assets per share of the vault after the withdraw
 * @param morphoBorrowAssetsPerShare Assets per share of the morpho market after the borrow
 * @param morphoRepayAssetsPerShare Assets per share of the morpho market after the repay
 * @param wrappedCollateralAssetsPerShare Assets per share of the collateral
 */
struct StoredRebalanceAssetsPerShare {
    uint256 vaultDepositAssetsPerShare;
    uint256 vaultWithdrawAssetsPerShare;
    uint256 morphoBorrowAssetsPerShare;
    uint256 morphoRepayAssetsPerShare;
    uint256 wrappedCollateralAssetsPerShare;
}
"
    },
    "lib/safe-smart-account/contracts/interfaces/IModuleManager.sol": {
      "content": "// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
import {Enum} from "../libraries/Enum.sol";

/**
 * @title IModuleManager - An interface of contract managing Safe modules
 * @notice Modules are extensions with unlimited access to a Safe that can be added to a Safe by its owners.
           ⚠️ WARNING: Modules are a security risk since they can execute arbitrary transactions, 
           so only trusted and audited modules should be added to a Safe. A malicious module can
           completely takeover a Safe.
 * @author @safe-global/safe-protocol
 */
interface IModuleManager {
    event EnabledModule(address indexed module);
    event DisabledModule(address indexed module);
    event ExecutionFromModuleSuccess(address indexed module);
    event ExecutionFromModuleFailure(address indexed module);
    event ChangedModuleGuard(address indexed moduleGuard);

    /**
     * @notice Enables the module `module` for the Safe.
     * @dev This can only be done via a Safe transaction.
     * @param module Module to be whitelisted.
     */
    function enableModule(address module) external;

    /**
     * @notice Disables the module `module` for the Safe.
     * @dev This can only be done via a Safe transaction.
     * @param prevModule Previous module in the modules linked list.
     * @param module Module to be removed.
     */
    function disableModule(address prevModule, address module) external;

    /**
     * @notice Execute `operation` (0: Call, 1: DelegateCall) to `to` with `value` (Native Token)
     * @param to Destination address of module transaction.
     * @param value Ether value of module transaction.
     * @param data Data payload of module transaction.
     * @param operation Operation type of module transaction.
     * @return success Boolean flag indicating if the call succeeded.
     */
    function execTransactionFromModule(
        address to,
        uint256 value,
        bytes memory data,
        Enum.Operation operation
    ) external returns (bool success);

    /**
     * @notice Execute `operation` (0: Call, 1: DelegateCall) to `to` with `value` (Native Token) and return data
     * @param to Destination address of module transaction.
     * @param value Ether value of module transaction.
     * @param data Data payload of module transaction.
     * @param operation Operation type of module transaction.
     * @return success Boolean flag indicating if the call succeeded.
     * @return returnData Data returned by the call.
     */
    function execTransactionFromModuleReturnData(
        address to,
        uint256 value,
        bytes memory data,
        Enum.Operation operation
    ) external returns (bool success, bytes memory returnData);

    /**
     * @notice Returns if a module is enabled
     * @return True if the module is enabled
     */
    function isModuleEnabled(address module) external view returns (bool);

    /**
     * @notice Returns an array of modules.
     *         If all entries fit into a single page, the next pointer will be 0x1.
     *         If another page is present, next will be the last element of the returned array.
     * @param start Start of the page. Has to be a module or start pointer (0x1 address)
     * @param pageSize Maximum number of modules that should be returned. Has to be > 0
     * @return array Array of modules.
     * @return next Start of the next page.
     */
    function getModulesPaginated(address start, uint256 pageSize) external view returns (address[] memory array, address next);

    /**
     * @dev Set a module guard that checks transactions initiated by the module before execution
     *      This can only be done via a Safe transaction.
     *      ⚠️ IMPORTANT: Since a module guard has full power to block Safe transaction execution initiated via a module,
     *        a broken module guard can cause a denial of service for the Safe modules. Make sure to carefully
     *        audit the module guard code and design recovery mechanisms.
     * @notice Set Module Guard `moduleGuard` for the Safe. Make sure you trust the module guard.
     * @param moduleGuard The address of the module guard to be used or the zero address to disable the module guard.
     */
    function setModuleGuard(address moduleGuard) external;
}
"
    },
    "lib/safe-smart-account/contracts/libraries/Enum.sol": {
      "content": "// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;

/**
 * @title Enum - Collection of enums used in Safe Smart Account contracts.
 * @author @safe-global/safe-protocol
 */
library Enum {
    enum Operation {
        Call,
        DelegateCall
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/utils/Context.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token`

Tags:
ERC20, ERC165, Multisig, Mintable, Burnable, Swap, Yield, Upgradeable, Multi-Signature, Factory, Oracle|addr:0x05824db4609ec96c77e99bc61c8fe0acf3269b24|verified:true|block:23555828|tx:0xda4cefaae71ade2d20a7e43386a8942d8afc4ae0e8645b60be6ccbd6b62d39db|first_check:1760280363

Submitted on: 2025-10-12 16:46:07

Comments

Log in to comment.

No comments yet.