MultiSigStrategyV1

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": {
    "node_modules/@blueprint-finance/earn-v1/src/strategies/MultiSigStrat/MultiSigStrategy.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.24;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {StrategyBase, RewardToken} from "../StrategyBase.sol";
import {Errors} from "../../interfaces/Errors.sol";

abstract contract MultiSigStrategyEvents {
    /// @notice Emitted when assets are forwarded to the multi-sig
    /// @param asset The address of the asset forwarded
    /// @param amount The amount of assets forwarded
    /// @param multiSig The address of the multi-sig wallet
    event AssetsForwarded(address indexed asset, uint256 amount, address multiSig);

    /// @notice Emitted when assets are retrieved from the multi-sig
    /// @param asset The address of the asset retrieved
    /// @param amount The amount of assets retrieved
    /// @param multiSig The address of the multi-sig wallet
    event AssetsRetrieved(address indexed asset, uint256 amount, address multiSig);
}

/**
 * @title MultiSigStrategy
 * @author Blueprint Finance
 * @notice ⚠️ IMPORTANT ⚠️ multiSig must approve this contract to pull funds for withdrawals
 * @notice A strategy that forwards assets to a designated multi-signature wallet
 * @dev Implements StrategyBase for integration with the Blueprint vault system
 *      This strategy simply forwards deposits to a multi-sig wallet and retrieves
 *      them on withdrawal. It does not generate any rewards.
 *
 */
contract MultiSigStrategyV1 is StrategyBase, MultiSigStrategyEvents {
    using SafeERC20 for IERC20;
    using Math for uint256;

    /// @notice The address of the multi-signature wallet that will hold the assets
    address public multiSig;

    /// @notice The amount of assets deposited into this strategy.
    uint256 private _vaultDepositedAmount;

    /// @notice Indicates if the multi-sig is allowed to withdraw assets
    bool public withdrawEnabled;

    /**
     *                                     CONSTRUCTOR                                  **
     */

    /**
     * @notice Initializes the strategy with required parameters
     * @param asset_ The ERC20 token this strategy will manage
     * @param feeRecipient_ Address that will receive strategy fees
     * @param admin_ Address that will have admin control of the strategy
     * @param multiSig_ Address of the multi-signature wallet that will hold assets
     * @param vault_ Address of the vault this strategy is attached to
     * @dev The multiSig must approve this contract to pull funds for withdrawals
     */
    constructor(IERC20 asset_, address feeRecipient_, address admin_, address multiSig_, address vault_) {
        if (multiSig_ == address(0)) revert InvalidMultiSigAddress();
        multiSig = multiSig_;

        string memory assetSymbol = IERC20Metadata(address(asset_)).symbol();
        RewardToken[] memory rewardTokenEmptyArray = new RewardToken[](0);
        __StrategyBase_init(
            asset_,
            string.concat("Concrete ", assetSymbol, " MultiSig Strategy"),
            string.concat("ctMS", assetSymbol),
            feeRecipient_,
            type(uint256).max,
            admin_,
            rewardTokenEmptyArray,
            vault_
        );
    }

    /**
     *                                     MUTATIVE                                    **
     */

    /**
     * @notice Sets the address of the multi-signature wallet
     * @param multiSig_ The address of the multi-signature wallet
     * @dev The multiSig must approve this contract to pull funds for withdrawals
     */
    function setMultiSig(address multiSig_) external onlyOwner {
        if (multiSig_ == address(0)) revert InvalidMultiSigAddress();
        multiSig = multiSig_;
    }

    /**
     * @notice Rescues any assets held by this strategy
     * @param asset_ The address of the asset to rescue
     * @dev Transfers the asset to the owner
     */
    function rescueFunds(address asset_) external onlyOwner {
        IERC20(asset_).safeTransfer(owner(), IERC20(asset()).balanceOf(address(this)));
    }

    /**
     * @notice Toggles the ability to withdraw assets from the multi-sig
     * @dev Used to prevent withdrawals in case of emergency
     */
    function toggleWithdraw() external onlyOwner {
        withdrawEnabled = !withdrawEnabled;
    }

    /**
     * @notice Handles the accounting for the amount of underlying assets that were claimed or failed to be claimed on the remote chain.
     * @dev This function MUST be called once the corresponding amounts of shares for the underlying have been locked or burned by the Redemption Helper (i.e. Owner). In this case outgoing has to be set to true.
     * @dev If the shares for the underlying were not burned here, but locked until claimed there, the redemption process can still be undone by the Redemption Handler (i.e. Owner). In this case outgoing has to be set to false, i.e. the shares are bridged back (incoming).
     * @param underlyingAmount The amount of underlying assets whose shares have been burned or locked by the Redemption Helper
     * @param outgoing Indicates if the corresponding shares are burned (outgoing) or bridged back (incoming)
     */
    function assetsMigrationToTargetByUserRedemption(uint256 underlyingAmount, bool outgoing) external onlyOwner {
        if (outgoing) {
            // Can only redeem shares (burn them here, claim them there) for underlying funds that have been bridged
            if (underlyingAmount > _vaultDepositedAmount) revert Errors.InsufficientUnderlyingBalance();
            // clearing that amount from the strategy accounting
            _vaultDepositedAmount -= underlyingAmount;
        } else {
            // open up the possibility for shares to be bridged back (unlocked here, locked and eventually burned there)
            // NOTE: This only works together with a mechanism where shares are locked instead of burned. Burned shares are irretrievable.
            _vaultDepositedAmount += underlyingAmount;
        }
    }

    /**
     *                                     GETTERS                                     **
     */

    /**
     * @notice Indicates if this is a protect strategy
     * @return false This is not a protect strategy
     */
    function isProtectStrategy() external pure override returns (bool) {
        return false;
    }

    /**
     * @notice Returns the address of the multi-signature wallet
     * @return The address of the multi-signature wallet
     */
    function getMultiSig() external view returns (address) {
        return multiSig;
    }

    /**
     * @notice Returns the amount of assets available for withdrawal
     * @return The balance of the underlying asset in the multi-sig
     */
    function getAvailableAssetsForWithdrawal() public view override returns (uint256) {
        //this will allow the vault to execute _protocolWithdraw and the fail reaching the revert
        if (!withdrawEnabled) return _vaultDepositedAmount;

        uint256 balance = IERC20(asset()).balanceOf(multiSig);
        return balance > _vaultDepositedAmount ? _vaultDepositedAmount : balance;
    }

    /**
     * @notice Returns the addresses of reward tokens
     * @return An empty array as this strategy has no reward tokens
     */
    function getRewardTokenAddresses() public pure override returns (address[] memory) {
        // No reward tokens
        return new address[](0);
    }

    /**
     * @notice Returns the amount of assets deposited in the vault
     * @return The amount of assets deposited in the vault
     */
    function viewVaultDepositedAmount() external view returns (uint256) {
        return _vaultDepositedAmount;
    }

    /**
     *                                     INTERNAL                                    **
     */

    /**
     * @notice Returns the total assets managed by this strategy
     * @return The balance of the underlying asset in the multi-sig
     * @dev Used by the vault to calculate total assets under management
     */
    function _totalAssets() internal view override returns (uint256) {
        return _vaultDepositedAmount;
    }

    /**
     * @notice Deposits assets into the multi-sig
     * @param assets_ Amount of assets to deposit
     * @dev Transfers assets directly to the multi-sig wallet
     */
    function _protocolDeposit(uint256 assets_, uint256) internal virtual override {
        _vaultDepositedAmount += assets_;
        address assetAddr = asset();
        IERC20(assetAddr).safeTransfer(multiSig, assets_);
        emit AssetsForwarded(assetAddr, assets_, multiSig);
    }

    /**
     * @notice Withdraws assets from the multi-sig
     * @dev Requires the multi-sig to have pre-approved this contract
     */
    function _protocolWithdraw(uint256 assets_, uint256) internal virtual override {
        if (!withdrawEnabled) revert WithdrawDisabled();
        _vaultDepositedAmount -= assets_;
        address assetAddr = asset();
        IERC20(assetAddr).safeTransferFrom(multiSig, address(this), assets_);
        emit AssetsRetrieved(assetAddr, assets_, multiSig);
    }

    /**
     * @notice Placeholder for reward collection (unused)
     * @dev This strategy does not generate rewards
     */
    function _getRewardsToStrategy(bytes memory) internal pure override {
        // No rewards for this strategy
    }

    /**
     * @notice Placeholder for reward handling on withdrawal (unused)
     * @dev This strategy does not generate rewards
     */
    function _handleRewardsOnWithdraw() internal pure override {
        // No rewards to handle
    }
}
"
    },
    "node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
    },
    "node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
"
    },
    "node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 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 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 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` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}
"
    },
    "node_modules/@openzeppelin/contracts/utils/math/Math.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}
"
    },
    "node_modules/@blueprint-finance/earn-v1/src/strategies/StrategyBase.sol": {
      "content": "//SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.24;

import {
    ERC4626Upgradeable,
    IERC20,
    IERC20Metadata,
    IERC4626
} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {Errors} from "../interfaces/Errors.sol";
import {IStrategy, ReturnedRewards} from "../interfaces/IStrategy.sol";
import {TokenHelper} from "@blueprint-finance/hub-and-spokes-libraries/src/libraries/TokenHelper.sol";

struct RewardToken {
    IERC20 token;
    uint256 fee;
    uint256 accumulatedFeeAccounted;
}

struct RewardWindow {
    uint256 start;
    uint256 end;
    uint256 lastHarvested;
    uint256 rewardsPerSecond;
}

abstract contract StrategyBase is
    IERC4626,
    IStrategy,
    ERC4626Upgradeable,
    ReentrancyGuard,
    OwnableUpgradeable,
    Errors
{
    using Math for uint256;
    using SafeERC20 for IERC20;

    uint256 private constant DEFAULT_REWARD_WINDOW_DURATION = 30 minutes;
    address internal _vault;

    // Array to store the reward tokens associated with the strategy
    RewardToken[] public rewardTokens;
    // Address where fees collected by the strategy are sent
    address public feeRecipient;
    // Maximum amount of the base asset that can be deposited into the strategy
    uint256 public depositLimit;
    // Number of decimals the strategy's shares will have, derived from the base asset's decimals
    //slither-disable-next-line naming-convention
    uint8 public _decimals;
    // Offset to adjust the decimals of the strategy's shares
    uint8 public constant DECIMAL_OFFSET = 9;

    // Mapping to track which reward tokens have been approved for use in the strategy
    mapping(address => bool) public rewardTokenApproved;

    // Mapping to track the reward distribution info for each reward token
    mapping(address rewardToken => RewardWindow) public rewardWindows;

    event Harvested(address indexed harvester, uint256 tvl);

    modifier onlyVault() {
        if (msg.sender != _vault) revert OnlyVault(msg.sender);
        _;
    }

    /**
     * @dev Initializes the StrategyBase contract with necessary parameters and setups.
     * This includes initializing inherited contracts, setting up reward tokens, fee recipient, deposit limit, and share decimals.
     * @param baseAsset_ The base asset of the strategy.
     * @param shareName_ The name of the strategy's share token.
     * @param shareSymbol_ The symbol of the strategy's share token.
     * @param feeRecipient_ The address where collected fees will be sent.
     * @param depositLimit_ The maximum amount of the base asset that can be deposited.
     * @param owner_ The owner of the strategy.
     * @param rewardTokens_ An array of reward tokens associated with the strategy.
     */
    // slither didn't detect the nonReentrant modifier
    // slither-disable-next-line reentrancy-no-eth,reentrancy-benign,naming-convention
    function __StrategyBase_init(
        IERC20 baseAsset_,
        string memory shareName_,
        string memory shareSymbol_,
        address feeRecipient_,
        uint256 depositLimit_,
        address owner_,
        RewardToken[] memory rewardTokens_,
        address vault_
    ) internal initializer nonReentrant {
        // Initialize inherited contracts
        __ERC4626_init(IERC20Metadata(address(baseAsset_)));
        __ERC20_init(shareName_, shareSymbol_);
        __Ownable_init(owner_);

        // Iterate through the provided reward tokens to set them up
        if (rewardTokens_.length != 0) {
            for (uint256 i; i < rewardTokens_.length;) {
                // Validate reward token address, current fee accounted, and high watermark
                if (address(rewardTokens_[i].token) == address(0)) {
                    revert InvalidRewardTokenAddress();
                }
                if (rewardTokens_[i].accumulatedFeeAccounted != 0) {
                    revert AccumulatedFeeAccountedMustBeZero();
                }

                // Approve the strategy to spend the reward token without limit
                if (!rewardTokens_[i].token.approve(address(this), type(uint256).max)) revert ERC20ApproveFail();
                // Add the reward token to the strategy's list and mark it as approved
                rewardTokens.push(rewardTokens_[i]);
                rewardTokenApproved[address(rewardTokens_[i].token)] = true;
                unchecked {
                    i++;
                }
            }
        }

        // Validate and set the fee recipient address
        if (feeRecipient_ == address(0)) revert InvalidFeeRecipient();
        feeRecipient = feeRecipient_;

        // Set the deposit limit for the strategy
        if (depositLimit_ == 0) revert InvalidDepositLimit();

        depositLimit = depositLimit_;
        // Calculate and set the decimals for the strategy's shares based on the base asset's decimals
        _decimals = IERC20Metadata(address(baseAsset_)).decimals() + DECIMAL_OFFSET;
        _vault = vault_;
    }

    /**
     * @dev Internal function to deposit assets into the strategy.
     * @param caller_ The address initiating the deposit.
     * @param receiver_ The address receiving the deposited shares.
     * @param assets_ The amount of assets to deposit.
     * @param shares_ The amount of shares to mint.
     */
    function _deposit(address caller_, address receiver_, uint256 assets_, uint256 shares_)
        internal
        virtual
        override(ERC4626Upgradeable)
        onlyVault
    {
        if (_totalAssets() + assets_ > depositLimit) revert MaxError();
        //slither-disable-next-line incorrect-equality
        if (shares_ == 0 || assets_ == 0) revert ZeroAmount();
        IERC20(asset()).safeTransferFrom(caller_, address(this), assets_);

        _protocolDeposit(assets_, shares_);

        _mint(receiver_, shares_);

        emit Deposit(caller_, receiver_, assets_, shares_);
    }

    /**
     * @dev Internal function to withdraw assets from the strategy.
     * @param caller_ The address initiating the withdrawal.
     * @param receiver_ The address receiving the withdrawn assets.
     * @param owner_ The owner of the strategy.
     * @param assets_ The amount of assets to withdraw.
     * @param shares_ The amount of shares to burn.
     */
    //It can only be called by the vault
    //slither-disable-next-line reentrancy-events
    function _withdraw(address caller_, address receiver_, address owner_, uint256 assets_, uint256 shares_)
        internal
        virtual
        override(ERC4626Upgradeable)
        onlyVault
    {
        //slither-disable-next-line incorrect-equality
        if (shares_ == 0 || assets_ == 0) revert ZeroAmount();

        _handleRewardsOnWithdraw();
        _protocolWithdraw(assets_, shares_);

        _burn(owner_, shares_);
        IERC20(asset()).safeTransfer(receiver_, assets_);

        emit Withdraw(caller_, receiver_, owner_, assets_, shares_);
    }

    /**
     * @dev Public function to get the total assets held by the strategy.
     * @return The total assets held by the strategy.
     */
    function totalAssets() public view override(IERC4626, ERC4626Upgradeable) returns (uint256) {
        // use the balance of this address + allow for custom totalAssets logic inside the strategy
        // This is because some strategies will just have funds inside them, and others will have funds in third parties.
        // The strategies with funds in third parties will need to show the actual balance, taking that into account
        return _totalAssets();
    }

    /**
     * @notice Adds a new reward token to the strategy.
     * @dev This function allows the owner to add a new reward token to the strategy's list of reward tokens.
     * It checks for several conditions to ensure the integrity of the reward token being added.
     * @param rewardToken_ The reward token to be added, encapsulated in a RewardToken struct.
     */
    function addRewardToken(RewardToken calldata rewardToken_) external onlyOwner nonReentrant {
        // Ensure the reward token address is not zero, not already approved, and its parameters are correctly initialized.
        if (address(rewardToken_.token) == address(0)) {
            revert InvalidRewardTokenAddress();
        }
        if (rewardTokenApproved[address(rewardToken_.token)]) {
            revert RewardTokenAlreadyApproved();
        }
        if (rewardToken_.accumulatedFeeAccounted != 0) {
            revert AccumulatedFeeAccountedMustBeZero();
        }

        // Add the reward token to the list and approve it for unlimited spending by the strategy.
        rewardTokens.push(rewardToken_);
        rewardTokenApproved[address(rewardToken_.token)] = true;
    }

    /**
     * @notice Removes a reward token from the strategy.
     * @dev This function allows the owner to remove a reward token from the strategy's list of reward tokens.
     * It shifts the elements in the array to maintain a compact array after removal.
     * @param rewardToken_ The reward token to be removed, encapsulated in a RewardToken struct.
     */
    function removeRewardToken(RewardToken calldata rewardToken_) external onlyOwner {
        // Ensure the reward token is approved before attempting removal.
        if (!rewardTokenApproved[address(rewardToken_.token)]) {
            revert RewardTokenNotApproved();
        }

        rewardTokens[_getIndex(address(rewardToken_.token))] = rewardTokens[rewardTokens.length - 1];
        rewardTokens.pop();

        // Mark the reward token as not approved.
        rewardTokenApproved[address(rewardToken_.token)] = false;
    }

    /**
     * @notice Modifies the fee associated with a reward token.
     * @dev This function allows the owner to modify the fee percentage for a specific reward token.
     * @param newFee_ The new fee percentage to be applied.
     * @param rewardToken_ The reward token whose fee is being modified, encapsulated in a RewardToken struct.
     */
    function modifyRewardFeeForRewardToken(uint256 newFee_, RewardToken calldata rewardToken_) external onlyOwner {
        // Ensure the reward token is approved before attempting to modify its fee.
        if (!rewardTokenApproved[address(rewardToken_.token)]) {
            revert RewardTokenNotApproved();
        }

        // Find the index of the reward token to modify.
        uint256 index = _getIndex(address(rewardToken_.token));

        // Update the fee for the specified reward token.
        rewardTokens[index].fee = newFee_;
    }

    /**
     * @notice Handles the distribution of accrued rewards upon withdrawal.
     * @dev This function is called during a withdrawal operation to distribute any accrued rewards to the withdrawing vault.
     * It first accrues user rewards, then iterates through all reward tokens to distribute the accrued rewards.
     */
    function _handleRewardsOnWithdraw() internal virtual;

    /**
     * @notice Sets the recipient address for protocol fees.
     * @dev Can only be called by the contract owner.
     * @param feeRecipient_ The address to which protocol fees will be sent.
     */
    function setFeeRecipient(address feeRecipient_) external onlyOwner {
        if (feeRecipient_ == address(0)) revert InvalidFeeRecipient();
        feeRecipient = feeRecipient_;
    }

    /**
     * @notice Sets the maximum limit for deposits into the strategy.
     * @dev Can only be called by the contract owner.
     * @param depositLimit_ The maximum amount that can be deposited.
     */
    //slither-disable-next-line events-maths
    function setDepositLimit(uint256 depositLimit_) external onlyOwner {
        if (depositLimit_ == 0) revert InvalidDepositLimit();
        depositLimit = depositLimit_;
    }

    // VIEWS

    /**
     * @notice Retrieves the user's accrued rewards for a specific token.
     * @dev Accrues rewards for the user before returning the reward balance.
     * @param user_ The address of the user whose rewards are being queried.
     * @param token_ The address of the token for which rewards are being queried.
     * @return UserRewardBalance The accrued rewards for the user for the specified token.
     */

    /**
     * @notice Returns the list of reward tokens configured in the strategy.
     * @dev Provides a view function to retrieve all reward tokens.
     * @return RewardToken[] An array of reward token configurations.
     */
    function getRewardTokens() external view returns (RewardToken[] memory) {
        return rewardTokens; // Return the array of configured reward tokens.
    }

    // Internal

    /**
     * @notice Finds the index of a given token in the reward tokens array.
     * @dev Iterates through the reward tokens array to find the index of the specified token.
     * @param token_ The address of the token to find.
     * @return index The index of the token in the reward tokens array, if found.
     */
    function _getIndex(address token_) internal view returns (uint256 index) {
        uint256 len = rewardTokens.length;
        for (uint256 i; i < len;) {
            if (address(rewardTokens[i].token) == token_) {
                index = i; // Set the index if the token is found.
                break; // Exit the loop once the token is found.
            }
            unchecked {
                ++i;
            }
        }
    }

    /**
     * @dev Generates an array of RewardToken structs for the strategy.
     * @param rewardFee_ The fee percentage for the reward tokens.
     * @return An array of RewardToken structs.
     */
    function _getRewardTokens(uint256 rewardFee_) internal view returns (RewardToken[] memory) {
        address[] memory rewards = getRewardTokenAddresses();
        RewardToken[] memory r = new RewardToken[](rewards.length);
        for (uint256 i = 0; i < rewards.length;) {
            r[i] = RewardToken({token: IERC20(rewards[i]), fee: rewardFee_, accumulatedFeeAccounted: 0});
            unchecked {
                ++i;
            }
        }
        return r;
    }

    //no reentrancy possible since the only one able to call this function is the vault
    //slither-disable-next-line reentrancy-no-eth,reentrancy-events
    function harvestRewards(bytes memory data)
        public
        virtual
        override(IStrategy)
        onlyVault
        returns (ReturnedRewards[] memory)
    {
        _getRewardsToStrategy(data);
        uint256 len = rewardTokens.length;
        ReturnedRewards[] memory returnedRewards = new ReturnedRewards[](len);
        for (uint256 i = 0; i < len;) {
            IERC20 rewardAddress = rewardTokens[i].token;

            RewardWindow storage rewardWindow = rewardWindows[address(rewardAddress)];
            uint256 rewardsToVault;
            uint256 effectiveRewardsToVault;
            uint256 remainingRewards;
            uint256 lastHarvested = rewardWindow.lastHarvested;
            if (rewardWindow.end >= block.timestamp || lastHarvested < rewardWindow.end) {
                uint256 newBlockTimestamp = block.timestamp > rewardWindow.end && lastHarvested < rewardWindow.end
                    ? rewardWindow.end
                    : block.timestamp;
                rewardsToVault = (newBlockTimestamp - rewardWindow.lastHarvested) * rewardWindow.rewardsPerSecond;
                rewardWindow.lastHarvested = block.timestamp;
                remainingRewards = rewardWindow.rewardsPerSecond * (rewardWindow.end - newBlockTimestamp);
            }
            if (rewardsToVault > 0) {
                // rewardAddress.safeTransfer(_vault, rewardsToVault);
                uint256 vaultBalanceBefore = rewardAddress.balanceOf(_vault);
                TokenHelper.attemptSafeTransfer(address(rewardAddress), _vault, rewardsToVault, false);
                uint256 vaultBalanceAfter = rewardAddress.balanceOf(_vault);
                effectiveRewardsToVault = vaultBalanceAfter - vaultBalanceBefore;
            }
            if (address(rewardAddress) == address(asset())) {
                //With this we avoid the vault considering the underlying token as a reward
                effectiveRewardsToVault = 0;
            }
            returnedRewards[i] = ReturnedRewards(address(rewardAddress), effectiveRewardsToVault);

            uint256 claimedBalance = rewardAddress.balanceOf(address(this));
            if (claimedBalance > remainingRewards) {
                claimedBalance = claimedBalance - remainingRewards;
            } else {
                unchecked {
                    ++i;
                }
                continue;
            }
            //if the rewardAddress matches the underlying asset, only transfer the tokens to the vault, don't account them
            uint256 netReward = claimedBalance;
            if (claimedBalance > 0) {
                uint256 collectedFee = claimedBalance.mulDiv(rewardTokens[i].fee, 10000, Math.Rounding.Ceil);
                if (TokenHelper.attemptSafeTransfer(address(rewardAddress), feeRecipient, collectedFee, false)) {
                    rewardTokens[i].accumulatedFeeAccounted += collectedFee;
                    netReward = claimedBalance - collectedFee;
                    emit Harvested(_vault, netReward);
                }
                if (netReward > 0) {
                    uint256 rewardBalance = rewardAddress.balanceOf(address(this));
                    uint256 duration = _getRewardWindowDuration();
                    rewardWindow.start = block.timestamp;
                    rewardWindow.end = block.timestamp + duration;
                    rewardWindow.lastHarvested = block.timestamp;
                    rewardWindow.rewardsPerSecond = rewardBalance / duration;
                }
            }
            unchecked {
                ++i;
            }
        }
        return returnedRewards;
    }

    function getRewardTokenAddresses() public view virtual returns (address[] memory) {
        //Each strategy should avoid returning the token considered in the _totalAssets function as a reward token
        uint256 len = rewardTokens.length;
        address[] memory rT = new address[](len);
        for (uint256 i = 0; i < len;) {
            rT[i] = address(rewardTokens[i].token);
            unchecked {
                ++i;
            }
        }
        return rT;
    }

    function _getRewardWindowDuration() internal pure virtual returns (uint256) {
        return DEFAULT_REWARD_WINDOW_DURATION;
    }

    function _protocolDeposit(uint256 assets, uint256 shares) internal virtual {}
    function _protocolWithdraw(uint256 assets, uint256 shares) internal virtual {}
    function _totalAssets() internal view virtual returns (uint256);
    function _getRewardsToStrategy(bytes memory data) internal virtual;
}
"
    },
    "node_modules/@blueprint-finance/earn-v1/src/interfaces/Errors.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.0;

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

/// @title Errors Interface
/// @notice Defines custom errors for the smart contract operations.
interface Errors {
    /// @notice Error for invalid asset address.
    error InvalidAssetAddress();

    /// @notice Error for mismatch between vault's asset and expected asset.
    error VaultAssetMismatch();

    /// @notice Error for invalid vault fees configuration.
    error InvalidVaultFees();

    /// @notice Error for invalid fee recipient address.
    error InvalidFeeRecipient();

    /// @notice Error for invalid parking lot address.
    error InvalidParkingLot();

    /// @notice Error for operations involving a zero amount.
    error ZeroAmount();

    /// @notice Error for operations involving a zero amount.
    error InvalidAmount();

    /// @notice Error for invalid recipient address.
    error InvalidRecipient();

    /// @notice Error for exceeding maximum allowed value or count.
    error MaxError();

    /// @notice Error for exceeding substraction.
    error InvalidSubstraction();

    error WithdrawalsPaused();

    /// @notice Error for insufficient funds in a strategy.
    /// @param strategy The strategy contract with insufficient funds.
    /// @param amount The amount attempted to be withdrawn.
    /// @param available The available amount in the strategy.
    error InsufficientFunds(IERC4626 strategy, uint256 amount, uint256 available);

    error QueueNotSet();

    error InsufficientQueueRequest(uint256 assets, uint256 minRequest);

    error InsufficientVaultFunds(address vault, uint256 amount, uint256 available);
    /// @notice Error for total allotment exceeding allowed maximum.
    error AllotmentTotalTooHigh();

    /// @notice Error for expired permit deadline.
    /// @param deadline The deadline timestamp that has been exceeded.
    error PermitDeadlineExpired(uint256 deadline);

    /// @notice Error for invalid signer address.
    /// @param signer The address of the invalid signer.
    error InvalidSigner(address signer);

    /// @notice Error for vault being in an idle state when an active state is required.
    error VaultIsIdle();

    /// @notice Error for invalid implementation identifier.
    /// @param id The bytes32 identifier of the implementation.
    error InvalidImplementation(bytes32 id);

    /// @notice Error for failed initialization of a vault deployment.
    error VaultDeployInitFailed();

    /// @notice Error for an implementation identifier that already exists.
    /// @param id The bytes32 identifier of the existing implementation.
    error ImplementationAlreadyExists(bytes32 id);

    /// @notice Error for a non-existent implementation identifier.
    /// @param id The bytes32 identifier of the non-existent implementation.
    error ImplementationDoesNotExist(bytes32 id);

    /// @notice Error for attempting to add a vault that already exists.
    error VaultAlreadyExists();

    /// @notice error when the vault address is the zero address
    error VaultZeroAddress();

    error VaultNotUpgradeable();

    /// @notice error when the vaultAddress does not exist
    /// @param vault the address of the vault
    error VaultDoesNotExist(address vault);

    /// @notice error for  the total vaults allowed is exceeded
    /// @param total the total number of vaults if the addVault function is successful
    error TotalVaultsAllowedExceeded(uint256 total);

    /// @notice error for vault to token limit check
    /// @param token the address of the token to add to vault mapping
    /// @param total the total number of vaults if the addVault function is successful
    error VaultByTokenLimitExceeded(address token, uint256 total);

    /// @notice error for invalid withdrawl queue address (zero address)
    error InvalidWithdrawlQueue();

    /// @notice error for beneficiary address check is not zero address
    error InvalidBeneficiary();

    /// @notice error for zero deposit limit check
    error InvalidDepositLimit();

    /// @notice error when setting new withdrawl queue to check if the old withdrawl queue is unfinalized
    /// @param queue the address of the withdrawl queue
    error UnfinalizedWithdrawl(address queue);

    /// @notice error for invalid token address
    error InvalidToken();

    /// @notice error for invalid rescuer address
    error InvalidRescuer();

    /// @notice error for ERC20 approve fail
    error ERC20ApproveFail();

    /// @notice error for vesting period check
    error NotPassedYear();

    /// @notice error for reward token address is zero address
    error InvalidRewardTokenAddress();

    /// @notice error for reward token already approved
    error RewardTokenAlreadyApproved();

    /// @notice error for reward token not approved
    error RewardTokenNotApproved();

    /// @notice error for checking a new reward token to be added to the strategy
    /// @notice a new reward token must have accumulated fee accounted as zero
    error AccumulatedFeeAccountedMustBeZero();

    /// @notice error for checking the vault must have only one protect strategy
    error MultipleProtectStrat();

    /// @notice error for checking the strategy has no locked assets (before removing strategy)
    /// @param strategy the address of the strategy
    error StrategyHasLockedAssets(address strategy);

    /// @notice error for checking the strategy index is out of bounds
    /// @param index the index of the strategy
    error InvalidIndex(uint256 index);

    /// @notice error for checking the length of the arrays
    /// @param argLength the length of the allocations array
    /// @param expectedLength the expected length of the strategies array
    error InvalidLength(uint256 argLength, uint256 expectedLength);
    // TokenRegistry errors /////////////////////////////////////////////////

    /// @notice Error for a token already being registered.
    /// @param tokenAddress The address of the token.
    error TokenAlreadyRegistered(address tokenAddress);

    /// @notice Error for a token not being registered.
    /// @param tokenAddress The address of the token.
    error TokenNotRegistered(address tokenAddress);

    /// @notice Error for a token not being a reward token.
    /// @param tokenAddress The address of the token.
    error NotValidRewardToken(address tokenAddress);

    /// @notice Treasury on the TokenRegistry is already set.
    error TreasuryAlreadySet(address attacker);

    /// @notice Unregistered tokens cannot be rewards.
    /// @param tokenAddress The address of the token.
    error UnregisteredTokensCannotBeRewards(address tokenAddress);

    /// @notice Error for a the treasury to be set to the zero address on constructor.
    error InvalidTreasuryAddress();

    // Swapper errors //////////////////////////////////////////////////////

    /// @notice The amount of a reward token is not available for withdrawal.
    /// @param token The address of the reward token.
    /// @param amount The amount required.
    error NotAvailableForWithdrawal(address token, uint256 amount);

    /// @notice The treasury change request cooldown has not elapsed.
    /// @param sender The address of the sender.
    error TreasuryChangeRequestCooldownNotElapsed(address sender);

    // RewardManager errors /////////////////////////////////////////////////

    /// @notice The base reward rate must be less than 100%.
    error SwapperBaseRewardrate();

    /// @notice The maximum progression factor must be less than 100%.
    error SwapperMaxProgressionFactor();

    /// @notice The bonus reward rate for the user must be less than 100%.
    error SwapperBonusRewardrateUser();

    /// @notice The bonus reward rate for the ctToken must be less than 100%.
    error SwapperBonusRewardrateCtToken();

    /// @notice The bonus reward rate for the swap token must be less than 100%.
    error SwapperBonusRewardrateSwapToken();

    /// @notice Invalid Address
    error InvalidUserAddress();

    //Oracle plug
    /// @notice Invalid Token Registry Address
    error InvalidTokenRegistry();

    //Claim Router errors //////////////////////////////////////////////////

    /// @notice error for invalid vault registry address
    error InvalidVaultRegistry();

    /// @notice error for unauthorized account
    error BlueprintUnauthorizedAccount(address account);

    /// @notice error for invalid default admin address
    error InvalidDefaultAdminAddress();

    /// @notice error for no protection strategies found
    error NoProtectionStrategiesFound();

    /// @notice error for only vault caller
    error OnlyVault(address caller);

    //Protect strategy errors ///////////////////////////////////////////////

    /// @notice error for unauthorized account
    error ProtectUnauthorizedAccount(address account);

    /// @notice error for claim router is unauthorized account
    error ClaimRouterUnauthorizedAccount(address account);

    /// @notice error for claim router address is zero address
    error InvalidClaimRouterAddress();

    //MultiSigStrategy
    /// @notice error for unauthorized multi-sig account
    error MultiSigUnauthorizedAccount(address account);

    /// @notice Thrown when attempting to initialize with zero address for multi-sig
    error InvalidMultiSigAddress();

    /// @notice Thrown when attempting to withdraw when withdraw is disabled
    error WithdrawDisabled();

    /// @notice Thrown when deposit fails
    error DepositFailed();

    /// @notice Thrown when attempting to initialize with zero address for target vault
    error InvalidTargetVaultAddress();
    /// @notice Thrown when attempting to withdraw more than the available balance
    error InsufficientUnderlyingBalance();
}
"
    },
    "node_modules/@o

Tags:
ERC20, Multisig, Mintable, Swap, Yield, Upgradeable, Multi-Signature, Factory, Oracle|addr:0x63b52f2ac8b0976d81ceaed41aac129c87f92f24|verified:true|block:23469858|tx:0x4b6a34f9be4f3fd3f8f4eac3b1ee5885aedc8fe048578166eba0db2cde2a6afe|first_check:1759163500

Submitted on: 2025-09-29 18:31:42

Comments

Log in to comment.

No comments yet.