ETH Price: $1,958.09 (+0.81%)
 

More Info

Private Name Tags

TokenTracker

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Swap And Report430953052026-03-08 14:25:5712 hrs ago1772979957IN
0x67b93f66...f35E14dCd
0 ETH0.00003010.005625
Swap And Report430952932026-03-08 14:25:3312 hrs ago1772979933IN
0x67b93f66...f35E14dCd
0 ETH0.00002920.005625
Swap And Report430952782026-03-08 14:25:0312 hrs ago1772979903IN
0x67b93f66...f35E14dCd
0 ETH0.000028710.005625
Swap And Report430827172026-03-08 7:26:2119 hrs ago1772954781IN
0x67b93f66...f35E14dCd
0 ETH0.000030640.005625
Swap And Report430599062026-03-07 18:45:5932 hrs ago1772909159IN
0x67b93f66...f35E14dCd
0 ETH0.000030750.00572229
Swap And Report430444972026-03-07 10:12:2140 hrs ago1772878341IN
0x67b93f66...f35E14dCd
0 ETH0.000028120.00525
Swap And Report430444892026-03-07 10:12:0540 hrs ago1772878325IN
0x67b93f66...f35E14dCd
0 ETH0.000026840.00525
Swap And Report430393382026-03-07 7:20:2343 hrs ago1772868023IN
0x67b93f66...f35E14dCd
0 ETH0.000028160.005625
Swap And Report429982832026-03-06 8:31:532 days ago1772785913IN
0x67b93f66...f35E14dCd
0 ETH0.00002770.0052509
Swap And Report429786822026-03-05 21:38:313 days ago1772746711IN
0x67b93f66...f35E14dCd
0 ETH0.000030160.00615325
Swap And Report429786682026-03-05 21:38:033 days ago1772746683IN
0x67b93f66...f35E14dCd
0 ETH0.000032810.005999
Swap And Report429519812026-03-05 6:48:293 days ago1772693309IN
0x67b93f66...f35E14dCd
0 ETH0.000050730.00995781
Swap And Report429310742026-03-04 19:11:354 days ago1772651495IN
0x67b93f66...f35E14dCd
0 ETH0.000038780.00744459
Swap And Report429310552026-03-04 19:10:574 days ago1772651457IN
0x67b93f66...f35E14dCd
0 ETH0.000040670.00772114
Swap And Report429207012026-03-04 13:25:494 days ago1772630749IN
0x67b93f66...f35E14dCd
0 ETH0.000035940.00725417
Swap And Report429206842026-03-04 13:25:154 days ago1772630715IN
0x67b93f66...f35E14dCd
0 ETH0.000041310.00757113
Swap And Report429132242026-03-04 9:16:354 days ago1772615795IN
0x67b93f66...f35E14dCd
0 ETH0.0000350.00697063
Swap And Report429132152026-03-04 9:16:174 days ago1772615777IN
0x67b93f66...f35E14dCd
0 ETH0.000038130.00702022
Swap And Report429112952026-03-04 8:12:174 days ago1772611937IN
0x67b93f66...f35E14dCd
0 ETH0.000037930.00731863
Swap And Report429112862026-03-04 8:11:594 days ago1772611919IN
0x67b93f66...f35E14dCd
0 ETH0.000036780.0072034
Swap And Report428873062026-03-03 18:52:395 days ago1772563959IN
0x67b93f66...f35E14dCd
0 ETH0.000035160.00666622
Swap And Report428798162026-03-03 14:42:595 days ago1772548979IN
0x67b93f66...f35E14dCd
0 ETH0.000061790.01183552
Swap And Report428652342026-03-03 6:36:555 days ago1772519815IN
0x67b93f66...f35E14dCd
0 ETH0.000030170.00600538
Swap And Report428652232026-03-03 6:36:335 days ago1772519793IN
0x67b93f66...f35E14dCd
0 ETH0.000032870.00575
Swap And Report428493182026-03-02 21:46:236 days ago1772487983IN
0x67b93f66...f35E14dCd
0 ETH0.000031080.00607808
View all transactions

Latest 1 internal transaction

Parent Transaction Hash Block From To
409327822026-01-17 13:01:5150 days ago1768654911  Contract Creation0 ETH

Cross-Chain Transactions
Loading...
Loading

Minimal Proxy Contract for 0xea7975c2fec1ae9e3058bb5f99d8e26dbc816811

Contract Name:
PermissionedDynaVault

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 250 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "./DynaVault.sol";
import "./VaultConfigLib.sol";

/**
 * @title Permissioned Vault
 * @notice Wraps DynaVault to only allow permitted users to deposit and withdraw
 */
contract PermissionedDynaVault is DynaVault {
	bytes32 private constant PERMISSION_ADMIN = keccak256("PERMISSION_ADMIN");
	bytes32 private constant PERMITTED_USER = keccak256("PERMITTED_USER");

	bool public permissionDisabled;

	event UpdatedPermissionDisabled(address caller, bool newState);

	/**
	 * @notice Initializes the permissioned vault
	 * @param nameOverride The vault name
	 * @param symbolOverride The vault symbol
	 * @param managerAddress The address of the vault manager
	 * @param referenceAssetOracleAddress The address of the reference asset oracle
	 * @param dynaRouterRegistryAddress The address of the DynaRouter registry
	 * @param ownerAddress The address of the owner
	 * @param vaultSimulatorAddress The address of the vault simulator
	 */
	function initialize(
		string memory nameOverride,
		string memory symbolOverride,
		address managerAddress,
		address referenceAssetOracleAddress,
		address dynaRouterRegistryAddress,
		address ownerAddress,
		address vaultSimulatorAddress
	) public override {
		DynaVault.initialize(
			nameOverride,
			symbolOverride,
			managerAddress,
			referenceAssetOracleAddress,
			dynaRouterRegistryAddress,
			ownerAddress,
			vaultSimulatorAddress
		);
		_setRoleAdmin(PERMITTED_USER, PERMISSION_ADMIN);
	}

	/**
	 * @notice Toggles if the vault requires permission
	 * @param newState The new permission state
	 */
	function setPermissionDisabled(bool newState) external {
		VaultConfigLib.onlyGovernance();
		permissionDisabled = newState;
		emit UpdatedPermissionDisabled(msg.sender, newState);
	}

	/**
	 * @notice Checks if a vault requires permission
	 */
	function checkPermission() private view {
		if (!permissionDisabled) _checkRole(PERMITTED_USER);
	}

	/** @dev See {IERC5143-mint} */
	function mint(uint256 sharesNotIncludingFees, address receiver) public virtual override returns (uint256 assetsIncludingFees) {
		checkPermission();
		return DynaVault.mint(sharesNotIncludingFees, receiver);
	}

	/** @dev See {IERC5143-mint} */
	function mintCheckSlippage(uint256 shares, address receiver, uint256 maxAssets) public virtual override returns (uint256) {
		checkPermission();
		return DynaVault.mintCheckSlippage(shares, receiver, maxAssets);
	}

	/** @dev See {IERC5143-mint} */
	function deposit(uint256 assetsIncludingFees, address receiver) public virtual override returns (uint256 sharesNotIncludingFees) {
		checkPermission();
		return DynaVault.deposit(assetsIncludingFees, receiver);
	}

	/** @dev See {IERC5143-mint} */
	function depositCheckSlippage(uint256 assets, address receiver, uint256 minShares) public virtual override returns (uint256) {
		checkPermission();
		return DynaVault.depositCheckSlippage(assets, receiver, minShares);
	}

	/** @dev See {IERC5143-mint} */
	function withdraw(uint256 assetsNotIncludingFees, address receiver, address owner) public virtual override returns (uint256 sharesIncludingFees) {
		checkPermission();
		return DynaVault.withdraw(assetsNotIncludingFees, receiver, owner);
	}

	/** @dev See {IERC5143-mint} */
	function withdrawCheckSlippage(uint256 assets, address receiver, address owner, uint256 maxShares) public virtual override returns (uint256) {
		checkPermission();
		return DynaVault.withdrawCheckSlippage(assets, receiver, owner, maxShares);
	}

	/** @dev See {IERC5143-mint} */
	function redeem(uint256 sharesIncludingFees, address receiver, address owner) public virtual override returns (uint256 assetsNotIncludingFees) {
		checkPermission();
		return DynaVault.redeem(sharesIncludingFees, receiver, owner);
	}

	/** @dev See {IERC5143-mint} */
	function redeemCheckSlippage(uint256 shares, address receiver, address owner, uint256 minAssets) public virtual override returns (uint256) {
		checkPermission();
		return DynaVault.redeemCheckSlippage(shares, receiver, owner, minAssets);
	}

	/**
	 * @notice Redeems an amount of shares paid out in proportional amounts of reserve tokens
	 * @param sharesIncludingFees The amount of shares to redeem
	 * @param receiver The address of the receiver
	 * @param owner The address of the owner
	 * @return assetsIncludingFees Array with proportional amounts of reserve tokens to be paid out
	 */
	function redeemProportional(uint256 sharesIncludingFees, address receiver, address owner) public virtual override returns (uint256[] memory) {
		checkPermission();
		return DynaVault.redeemProportional(sharesIncludingFees, receiver, owner);
	}

	/**
	 * @notice Redeems an amount of shares paid out in proportional amounts of reserve tokens with slippage checking
	 * @param shares The amount of shares to redeem
	 * @param receiver The address of the receiver
	 * @param owner The address of the owner
	 * @param minAssets An array with min amounts of assets
	 * @return assets An array with proportional amounts of reserve tokens to be paid out
	 */
	function redeemProportionalCheckSlippage(
		uint256 shares,
		address receiver,
		address owner,
		uint256[] memory minAssets
	) public virtual override returns (uint256[] memory) {
		checkPermission();
		return DynaVault.redeemProportionalCheckSlippage(shares, receiver, owner, minAssets);
	}
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(account),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * May emit a {RoleGranted} event.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @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 amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` 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 amount) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../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 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.encodeWithSelector(token.transfer.selector, 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.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 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);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @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.encodeWithSelector(token.approve.selector, spender, value);

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

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @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, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @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.isContract(address(token));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @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;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @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 up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (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; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                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.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 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.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            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 (rounding == Rounding.Up && 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 down.
     *
     * 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 + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * 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 + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * 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 + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * 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 + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

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

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 15 of 36 : DynaVault.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "./interfaces/IDynaVaultAPI.sol";
import "./interfaces/IDynaRouterAPI.sol";
import "./interfaces/IVaultSimulatorAPI.sol";
import "./VaultConfigLib.sol";
import "./DynaVaultLib.sol";
import "./VaultRouterLib.sol";
import "./DynaVaultErrors.sol";
import "./utils/ERC20.sol";
import "./utils/ReentrancyGuard.sol";
import "./utils/Checks.sol";
import "./utils/Clonable.sol";

/**
 * @dev "DynaVault" vault using Implementation of the ERC4626 "Tokenized Vault Standard" as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626].
 *
 * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for
 * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends
 * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this
 * contract and not the "assets" token which is an independent contract.
 *
 * @notice We do not support fee-on-transfer tokens and they should not be used as deposit or reserve tokens!
 *
 * CAUTION: Deposits and withdrawals may incur unexpected slippage. Users should verify that the amount received of
 * shares or assets is as expected. For this reason we implement EIP-5143 to have check slippage.
 */
contract DynaVault is ERC20, IDynaVaultAPI, Clonable, ReentrancyGuard {
	using Checks for address;

	address public simulator;

	/**
	 * @notice Initializes the vault parameters
	 * @param nameOverride The vault name
	 * @param symbolOverride The vault symbol
	 * @param managerAddress The address of the vault manager
	 * @param referenceAssetOracleAddress The address of the reference asset oracle
	 * @param dynaRouterRegistryAddress The address of the DynaRouter registry
	 * @param ownerAddress The address of the owner
	 */
	function initialize(
		string memory nameOverride,
		string memory symbolOverride,
		address managerAddress,
		address referenceAssetOracleAddress,
		address dynaRouterRegistryAddress,
		address ownerAddress,
		address vaultSimulatorAddress
	) public virtual {
		ownerAddress.requireNonZeroAddress();
		// grantRole is reverting when executed by user without admin role
		_grantRole(DEFAULT_ADMIN_ROLE, ownerAddress);
		initializeERC20(nameOverride, symbolOverride);
		VaultConfigLib.initialize(managerAddress, dynaRouterRegistryAddress, referenceAssetOracleAddress);
		DynaVaultLib.initialize();
		simulator = vaultSimulatorAddress;
		IVaultSimulatorAPI(vaultSimulatorAddress).initialize(address(this), managerAddress, uint8(VaultConfigLib.depositDecimals()));
	}

	/**
	 * @notice Takes a snapshot of the vault that can be used for simulating vault actions such as reporting
	 * @return snapshot A vault snapshot that can be used for simulation
	 */
	function takeSnapshot() public view returns (IVaultSimulatorAPI.VaultSnapshot memory) {
		return IVaultSimulatorAPI(simulator).takeSnapshot();
	}

	/** @dev See {IERC4626-asset} */
	function asset() external view override returns (address) {
		return VaultConfigLib.asset();
	}

	/** @dev See {IERC4626-totalAssets} */
	function totalAssets() external view virtual override returns (uint256 total) {
		total = DynaVaultLib.totalAssets();
	}

	/**
	 * @notice Returns the value of one share in deposit token
	 * @return assetsPerShare The value of one share in deposit token
	 */
	function assetsPerShare() public view virtual returns (uint256) {
		return IVaultSimulatorAPI(simulator).assetsPerShare();
	}

	/** @dev See {IERC4626-convertToShares} */
	function convertToShares(uint256 assets) external view override returns (uint256 shares) {
		return IVaultSimulatorAPI(simulator)._convertToShares(assets);
	}

	/** @dev See {IERC4626-convertToAssets} */
	function convertToAssets(uint256 shares) external view override returns (uint256 assets) {
		return IVaultSimulatorAPI(simulator)._convertToAssets(shares);
	}

	/** @dev See {IERC4626-maxDeposit} */
	function maxDeposit(address) external view virtual override returns (uint256) {
		return DynaVaultLib.maxDepositLimit();
	}

	/** @dev See {IERC4626-maxMint} */
	function maxMint(address) external view virtual override returns (uint256) {
		return DynaVaultLib.maxMint();
	}

	/** @dev See {IERC4626-maxWithdraw} */
	function maxWithdraw(address owner) external view virtual override returns (uint256) {
		return IVaultSimulatorAPI(simulator).simulatedMaxWithdraw(owner, takeSnapshot());
	}

	/**
	 * @notice Simulates max withdraw
	 * @param owner The address of owner
	 * @param snapshot The vault snapshot used in the simulation
	 * @return amount Max amount of tokens that can be withdrawn
	 */
	function simulatedMaxWithdraw(address owner, IVaultSimulatorAPI.VaultSnapshot memory snapshot) external view returns (uint256) {
		return IVaultSimulatorAPI(simulator).simulatedMaxWithdraw(owner, snapshot);
	}

	/**
	 * @notice Wraps balanceOf
	 * @param user The address of user
	 * @return shares The amount of shares owned by user
	 */
	function sharesOf(address user) external view virtual returns (uint256) {
		return DynaVaultLib.sharesOf(user);
	}

	/** @dev See {IERC4626-maxRedeem} */
	function maxRedeem(address owner) external view virtual override returns (uint256) {
		return DynaVaultLib.maxRedeem(owner);
	}

	/** @dev See {IERC4626-previewDeposit} */
	function previewDeposit(uint256 assets) public view virtual override returns (uint256) {
		return IVaultSimulatorAPI(simulator).simulatedDeposit(assets, takeSnapshot());
	}

	/**
	 * @dev Simulates the calculation of how many shares should be minted for a deposit
	 * @param assets The amount of fees
	 * @param snapshot The current vault snapshot
	 * @return shares The simulated amount of shares
	 */
	function simulatedDeposit(uint256 assets, IVaultSimulatorAPI.VaultSnapshot memory snapshot) public view returns (uint256) {
		return IVaultSimulatorAPI(simulator).simulatedDeposit(assets, snapshot);
	}

	/** @dev See {IERC4626-previewMint} */
	function previewMint(uint256 shares) public view virtual override returns (uint256) {
		return IVaultSimulatorAPI(simulator).simulatedMint(shares, takeSnapshot());
	}

	/** @dev See {IERC4626-previewWithdraw} */
	function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {
		return IVaultSimulatorAPI(simulator).simulatedWithdraw(assets, takeSnapshot());
	}

	/** @dev See {IERC4626-previewRedeem} */
	function previewRedeem(uint256 shares) public view virtual override returns (uint256) {
		return IVaultSimulatorAPI(simulator).simulatedRedeem(shares, takeSnapshot());
	}

	/** @dev See {IERC4626-deposit} */
	function deposit(uint256 assetsIncludingFees, address receiver) public virtual override returns (uint256 sharesNotIncludingFees) {
		receiver.requireNonZeroAddress();
		before_nonReentrant();
		uint256 reportedFreeFunds = DynaVaultLib.reportAllReserves();
		DynaVaultLib.checkMaxDeposit(assetsIncludingFees);
		DynaVaultLib.checkMinDeposit(assetsIncludingFees);
		sharesNotIncludingFees = DynaVaultLib.previewDeposit(assetsIncludingFees, reportedFreeFunds);
		_deposit(msg.sender, receiver, assetsIncludingFees, sharesNotIncludingFees);
		after_nonReentrant();
	}

	/** @dev See {IERC4626-mint} */
	function mint(uint256 sharesNotIncludingFees, address receiver) public virtual override returns (uint256 assetsIncludingFees) {
		receiver.requireNonZeroAddress();
		before_nonReentrant();
		uint256 reportedFreeFunds = DynaVaultLib.reportAllReserves();
		DynaVaultLib.checkMaxMint(sharesNotIncludingFees);
		assetsIncludingFees = DynaVaultLib.previewMint(sharesNotIncludingFees, reportedFreeFunds);
		DynaVaultLib.checkMinDeposit(assetsIncludingFees);
		_deposit(msg.sender, receiver, assetsIncludingFees, sharesNotIncludingFees);
		after_nonReentrant();
	}

	/** @dev See {IERC4626-withdraw} */
	function withdraw(uint256 assetsNotIncludingFees, address receiver, address owner) public virtual override returns (uint256 sharesIncludingFees) {
		owner.requireNonZeroAddress();
		receiver.requireNonZeroAddress();
		before_nonReentrant();
		(sharesIncludingFees, assetsNotIncludingFees) = DynaVaultLib.reportAndCalculateWithdraw(assetsNotIncludingFees, owner);
		_withdraw(msg.sender, receiver, owner, assetsNotIncludingFees, sharesIncludingFees);
		after_nonReentrant();
	}

	/** @dev See {IERC4626-redeem} */
	function redeem(uint256 sharesIncludingFees, address receiver, address owner) public virtual override returns (uint256 assetsNotIncludingFees) {
		owner.requireNonZeroAddress();
		receiver.requireNonZeroAddress();
		before_nonReentrant();
		assetsNotIncludingFees = DynaVaultLib.reportAndCalculateRedeem(sharesIncludingFees, owner);
		_withdraw(msg.sender, receiver, owner, assetsNotIncludingFees, sharesIncludingFees);
		after_nonReentrant();
	}

	/** @dev See {IERC5143-deposit} */
	function depositCheckSlippage(uint256 assets, address receiver, uint256 minShares) public virtual override returns (uint256 shares) {
		shares = deposit(assets, receiver);
		DynaVaultErrors.checkSlippageAbove(shares, minShares);
	}

	/** @dev See {IERC5143-mint} */
	function mintCheckSlippage(uint256 shares, address receiver, uint256 maxAssets) public virtual override returns (uint256 assets) {
		assets = mint(shares, receiver);
		DynaVaultErrors.checkSlippageBelow(assets, maxAssets);
	}

	/** @dev See {IERC5143-withdraw} */
	function withdrawCheckSlippage(uint256 assets, address receiver, address owner, uint256 maxShares) public virtual override returns (uint256 shares) {
		shares = withdraw(assets, receiver, owner);
		DynaVaultErrors.checkSlippageBelow(shares, maxShares);
	}

	/** @dev See {IERC5143-redeem} */
	function redeemCheckSlippage(uint256 shares, address receiver, address owner, uint256 minAssets) public virtual override returns (uint256 assets) {
		assets = redeem(shares, receiver, owner);
		DynaVaultErrors.checkSlippageAbove(assets, minAssets);
	}

	/**
	 * @notice Redeems an amount of shares paid out in proportional amounts of reserve tokens
	 * @param sharesIncludingFees The amount of shares to redeem
	 * @param receiver The address of the receiver
	 * @param owner The address of the owner
	 * @return assetsIncludingFees Array with proportional amounts of reserve tokens to be paid out
	 */
	function redeemProportional(uint256 sharesIncludingFees, address receiver, address owner) public virtual override returns (uint256[] memory) {
		owner.requireNonZeroAddress();
		receiver.requireNonZeroAddress();
		before_nonReentrant();
		if (msg.sender != owner) _spendAllowance(owner, msg.sender, sharesIncludingFees);
		uint256 reportedFreeFunds = DynaVaultLib.reportAllReserves();
		DynaVaultLib.checkRedeem(sharesIncludingFees, owner, reportedFreeFunds);
		uint256[] memory toRedeem = DynaVaultLib.calcRedeemProportional(sharesIncludingFees);
		_burn(owner, sharesIncludingFees);
		DynaVaultLib.transferProportional(receiver, toRedeem);
		after_nonReentrant();
		return toRedeem;
	}

	/**
	 * @notice Preview of redeem proportional
	 * @param sharesIncludingFees Amount of shares to redeem
	 * @param _snapshot Snapshot used for the simulation
	 * @return assets Array with proportional amounts of reserve tokens
	 */
	function previewRedeemProportional(
		uint256 sharesIncludingFees,
		IVaultSimulatorAPI.VaultSnapshot memory _snapshot
	) external view virtual override returns (uint256[] memory) {
		return IVaultSimulatorAPI(simulator).simulatedRedeemProportional(sharesIncludingFees, _snapshot);
	}

	/**
	 * @notice Redeems an amount of shares paid out in proportional amounts of reserve tokens with slippage checking
	 * @param shares The amount of shares to redeem
	 * @param receiver The address of the receiver
	 * @param owner The address of the owner
	 * @param minAssets An array with min amounts of assets
	 * @return assets An array with proportional amounts of reserve tokens to be paid out
	 */
	function redeemProportionalCheckSlippage(
		uint256 shares,
		address receiver,
		address owner,
		uint256[] memory minAssets
	) public virtual override returns (uint256[] memory) {
		owner.requireNonZeroAddress();
		receiver.requireNonZeroAddress();
		uint256[] memory assets = redeemProportional(shares, receiver, owner);
		DynaVaultErrors.checkSlippageAbove(assets, minAssets);
		return assets;
	}

	/**
	 * @notice Returns the address of the vault manager
	 * @return manager The address of the vault manager
	 */
	function manager() public view returns (address) {
		return VaultConfigLib.manager();
	}

	/**
	 * @notice Returns the address of the router registry
	 * @return routerRegistryAddress The address of the  router registry
	 */
	function routerRegistry() public view override returns (IDynaRouterRegistryAPI) {
		return IDynaRouterRegistryAPI(VaultConfigLib.routerRegistry());
	}

	/**
	 * @notice Issues shares for fees
	 * @notice deltaTotalAssets is in reference asset, used when there is profit in strategies to compensate balances in calculations
	 * @param to The address of fee receiver
	 * @param feeAmount The amount of fee in feeToken
	 * @param feeToken The address of fee token
	 * @param deltaTotalAssets The delta of total assets
	 * @return shares The amount of shares minted
	 */
	function issueSharesForFeeAmount(address to, uint256 feeAmount, address feeToken, uint256 deltaTotalAssets) external override returns (uint256) {
		if (address(msg.sender) != manager()) revert DynaVaultErrors.NotAuthorized();
		uint256 shares = DynaVaultLib.calcSharesForFeeAmount(feeAmount, feeToken, deltaTotalAssets);
		_mint(to, shares);
		return shares;
	}

	/**
	 * @dev Deposit/mint common workflow.
	 * @notice Private function called during deposit
	 * @param caller The address of caller
	 * @param receiver The address of receiver
	 * @param assetsIncludingFees The assets that receiver will get
	 * @param _sharesWithoutFees The shares that are burned
	 */
	function _deposit(address caller, address receiver, uint256 assetsIncludingFees, uint256 _sharesWithoutFees) private {
		uint256 fee = DynaVaultLib.beforeMint(caller, assetsIncludingFees);
		_mint(receiver, _sharesWithoutFees);
		DynaVaultLib.afterMint(caller, receiver, assetsIncludingFees, _sharesWithoutFees, fee);
	}

	/**
	 * @dev Withdraw/redeem common workflow.
	 * @notice Private function called during withdraw
	 * @param caller The address of caller
	 * @param receiver The address of receiver
	 * @param owner The address of owner
	 * @param assetsNotIncludingFees The assets that receiver will get
	 * @param sharesIncludingFees The amount of shares to burn
	 */
	function _withdraw(address caller, address receiver, address owner, uint256 assetsNotIncludingFees, uint256 sharesIncludingFees) private {
		if (caller != owner) _spendAllowance(owner, caller, sharesIncludingFees);
		uint256 assetsToWithdraw = DynaVaultLib.beforeBurn(assetsNotIncludingFees, sharesIncludingFees);
		_burn(owner, sharesIncludingFees);
		DynaVaultLib.afterBurn(caller, receiver, owner, assetsToWithdraw, sharesIncludingFees);
	}

	/**
	 * @notice Returns the value of one share in deposit token
	 * @return pricePerShare value of one share in deposit token
	 */
	function pricePerShare() external view returns (uint256) {
		return IVaultSimulatorAPI(simulator).assetsPerShare();
	}

	/**
	 * @notice Returns the minimum amount of tokens that can be deposited
	 * @return minDepositLimit The minimum amount possible to deposit
	 */
	function minDepositLimit() external view returns (uint256) {
		return DynaVaultLib.minDepositLimit();
	}

	/**
	 * @notice Returns the max value of the tokens in the vault
	 * @return maxTotalAssets The max amount of assets in vault
	 */
	function maxTotalAssets() external view returns (uint256) {
		return DynaVaultLib.maxTotalAssets();
	}

	/**
	 * @notice Returns the max amount possible to deposit
	 * @return maxDepositLimit The max amount of tokens that can be deposited
	 */
	function maxDepositLimit() external view returns (uint256) {
		return DynaVaultLib.maxDepositLimit();
	}

	/**
	 * @notice Set deposit limits
	 * @param newMinDepositAssets The new minimum deposit amount limit
	 * @param newMaxTotalAssets The new max total assets limit
	 */
	function setDepositLimits(uint256 newMinDepositAssets, uint256 newMaxTotalAssets) external {
		DynaVaultLib.setDepositLimits(newMinDepositAssets, newMaxTotalAssets);
	}

	/**
	 * @notice Transfers fees to a receiver
	 * @param to The address of the fee receiver
	 * @param amount The amount of shares to send
	 */
	function feeTransfer(address to, uint256 amount) external override {
		VaultConfigLib.onlyManager();
		_transfer(address(this), to, amount);
	}

	/**
	 * @notice Set the DynaRouterRegistry address
	 * @param routerRegistryAddress The address of the new router registry
	 */
	function setRouterRegistry(address routerRegistryAddress) external {
		VaultConfigLib.setRouterRegistry(routerRegistryAddress);
	}

	/**
	 * @notice Used to fetch swap data used when calling swap
	 * @param tokenIn The address of the input token
	 * @param amountIn The amount to swap
	 * @param tokenOut The address of the output token
	 * @return amountOut The expected amount out from swap
	 * @return selectedRouter The address of router to use
	 * @return swapData The data used in swap
	 */
	function previewSwap(
		address tokenIn,
		uint256 amountIn,
		address tokenOut
	) external view returns (uint256 amountOut, address selectedRouter, bytes32[] memory swapData) {
		return VaultRouterLib.previewSwap(tokenIn, amountIn, tokenOut);
	}

	/**
	 * @notice Swap function to be called by vault management to swap and change target weights
	 * @param tokenIn The address of the input token
	 * @param amountIn The amount to swap
	 * @param tokenOut The address of the output token
	 * @param minAmountOut The min expected amount out from swap
	 * @param selectedRouter The address of router to use
	 * @param swapData The data used for the swap
	 */
	function swap(address tokenIn, uint256 amountIn, address tokenOut, uint256 minAmountOut, address selectedRouter, bytes32[] memory swapData) external {
		before_nonReentrant();
		VaultRouterLib.swap(tokenIn, amountIn, tokenOut, minAmountOut, selectedRouter, swapData);
		after_nonReentrant();
	}

	/**
	 * @notice Swap with reporting of the tokens swapped
	 * @param tokenIn The address of the input token
	 * @param amountIn The amount of input token to swap
	 * @param tokenOut The address of the output token
	 * @param minAmountOut The min expected amount out from swap
	 * @param selectedRouter The address of router to use
	 * @param swapData The swapData from previewSwap
	 */
	function swapAndReport(
		address tokenIn,
		uint256 amountIn,
		address tokenOut,
		uint256 minAmountOut,
		address selectedRouter,
		bytes32[] memory swapData
	) external {
		before_nonReentrant();
		VaultRouterLib.swapAndReport(tokenIn, amountIn, tokenOut, minAmountOut, selectedRouter, swapData);
		after_nonReentrant();
	}

	/**
	 * @notice Sets the reference asset oracle address
	 * @param referenceAssetOracleAddress The address of the new reference asset oracle
	 */
	function setReferenceAssetOracle(address referenceAssetOracleAddress) external {
		VaultConfigLib.setReferenceAssetOracle(referenceAssetOracleAddress);
	}

	/**
	 * @notice Returns the address of the configured reference oracle
	 * @return referenceAssetOracle The address of the reference oracle
	 */
	function referenceAssetOracle() external view returns (address) {
		return VaultConfigLib.referenceAssetOracle();
	}

	/**
	 * @notice Returns the reference asset address
	 * @return referenceAsset The address of the reference assets
	 */
	function referenceAsset() external view returns (address) {
		return VaultConfigLib.referenceAsset();
	}

	/**
	 * @notice Returns the current max loss limit
	 * @return maxLoss The current max loss limit
	 */

	function maxLoss() external view returns (uint256) {
		return VaultConfigLib.maxLoss();
	}

	/**
	 * @notice Sets the max loss limit
	 * @param _maxLoss The new max loss limit
	 */
	function setMaxLoss(uint256 _maxLoss) external {
		VaultConfigLib.setMaxLoss(_maxLoss);
	}

	/**
	 * @notice Approve manager for swapping token
	 * @param tokenAddress Address of the token to approve
	 */
	function approveAddedToken(address tokenAddress) external {
		VaultConfigLib.approveAddedToken(tokenAddress);
	}

	/**
	 * @notice Reset allowance of manager for swapping token
	 * @param tokenAddress The address of the token to approve
	 */
	function resetRemovedTokenAllowance(address tokenAddress) external {
		VaultConfigLib.resetRemovedTokenAllowance(tokenAddress);
	}

	/**
	 * @notice This a swap function to be called by the vault manager contract to rebalance, which does not change target depositDebtRatio weights
	 * @param tokenIn The address of the input token
	 * @param amountIn The amount to swap
	 * @param tokenOut The address of the output token
	 * @param minAmountOut The min expected amount out from swap
	 * @return amountOut The amount of tokenOut from swap
	 */
	function doSwap(address tokenIn, uint256 amountIn, address tokenOut, uint256 minAmountOut) external returns (uint256 amountOut) {
		return VaultRouterLib.doSwap(tokenIn, amountIn, tokenOut, minAmountOut);
	}

	/**
	 * @notice Calculates shares for fees with given values for total supply and total assets
	 * @param feeAmount The amount for fees
	 * @param feeToken The address of the fee token
	 * @param deltaTotalAssets Updates total assets in fee shares calculation
	 * @param givenTotalSupply The amount of total supply to use in calculation
	 * @param givenTotalAssets The amount of total assets to use in calculation
	 * @return feeShares The amount of shares to mint for fees
	 */
	function calcSharesForFeeAmountUsingGivenTotalSupplyAndTotalAssets(
		uint256 feeAmount,
		address feeToken,
		uint256 deltaTotalAssets,
		uint256 givenTotalSupply,
		uint256 givenTotalAssets
	) external view returns (uint256 feeShares) {
		return
			DynaVaultLib.calcSharesForFeeAmountUsingGivenTotalSupplyAndTotalAssets(feeAmount, feeToken, deltaTotalAssets, givenTotalSupply, givenTotalAssets);
	}

	/**
	 * @notice Calculates shares for fees with given values for total supply and free funds
	 * @param feeAmount The amount for fee
	 * @param feeToken The address of the fee token
	 * @param givenTotalSupply The amount of total supply to use in calculation
	 * @param givenFreeFunds The amount of free funds to use in calculation
	 * @return feeShares The amount of shares to mint for fees
	 */
	function calcSharesForFeeAmountUsingGivenTotalSupplyAndFreeFunds(
		uint256 feeAmount,
		address feeToken,
		uint256 givenTotalSupply,
		uint256 givenFreeFunds
	) external view returns (uint256 feeShares) {
		return DynaVaultLib.calcSharesForFeeAmountUsingGivenTotalSupplyAndFreeFunds(feeAmount, feeToken, givenTotalSupply, givenFreeFunds);
	}

	/**
	 * @notice Returns value of token in quote asset
	 * @param base The address of the base token
	 * @param amount The amount of base token
	 * @param quote The address of the quote token
	 * @return value The value of the amount of base token in quote token
	 */
	function tokenValueInQuoteAsset(address base, uint256 amount, address quote) external view returns (uint256 value) {
		return DynaVaultLib.tokenValueInQuoteAsset(base, amount, quote);
	}

	/**
	 * @dev Simulates the calculation of how many shares should be minted for fees
	 * @param feeAmount The amount of fees
	 * @param feeToken The token used to calculate fees
	 * @param deltaTotalAssets The vault profit
	 * @param snapshot The current vault snapshot
	 * @return Updated snapshot with new total supply
	 */
	function simulatedIssueSharesForFeeAmount(
		uint256 feeAmount,
		address feeToken,
		uint256 deltaTotalAssets,
		IVaultSimulatorAPI.VaultSnapshot memory snapshot
	) external view returns (IVaultSimulatorAPI.VaultSnapshot memory) {
		return IVaultSimulatorAPI(simulator).simulatedIssueSharesForFeeAmount(feeAmount, feeToken, deltaTotalAssets, snapshot);
	}

	/**
	 * @notice Withdraw token debt from strategies
	 * @param tokenAddress The address of the token
	 * @param valueToWithdraw The amount to withdraw
	 */
	function withdrawTokenDebtFromStrategies(address tokenAddress, uint256 valueToWithdraw) external returns (uint256 totalLoss, uint256 totalWithdrawn) {
		return DynaVaultLib.withdrawTokenDebtFromStrategies(tokenAddress, valueToWithdraw);
	}
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

library DynaVaultErrors {
	// Vault Lib
	error MaxMint();
	error MaxDeposit();
	error MinDeposit();
	error MaxRedeem();
	error MinRedeem();
	error MaxWithdraw();
	error MinWithdraw();
	error StrategyLossProtection(uint256 toWithdraw, uint256 totalLoss, uint256 maxLoss);
	error ArrayMismatch();
	error ZeroShares();

	// Vault Token Lib
	error MaxTokens();
	error AmountInMoreThanTokenIdle(uint256 amountIn, address tokenIn, uint256 tokenIdle);
	error TokenIdle();
	error TokenDebt();
	error ERC20InsufficientBalance();
	error ERC20InsufficientAllowance();
	error InvalidToken();
	error MaxWatermarkDuration();

	// Vault Strategies Lib
	error UnableToChangeDebtRatioOnRevokedStrategy();

	// Vault Lib
	error MinAboveMax();
	error MaxTotalAssets();

	// Vault Config Lib
	error MaxLossLimit();
	error NotSameReferenceAsset();

	// Registry
	error NotRegistered(); // liq reg, vault reg
	error AlreadyRegistered(); // liq reg, vault reg , tokens lib

	// Vault Manager Lib
	error UpdateFeeOverTimeLimit();
	error IncorrectStrategyReport(uint256 strategyBalance, uint256 reportedGain, uint256 reportedDebtPayment);
	error LockedProfitDegradationCoefficient(uint256 newCoefficient, uint256 minimumCoefficient, uint256 maximumCoefficient);

	// Vault Router Lib
	error DynaVaultSwapUnsupportedToken(address token);
	error DynaVaultSwapLackingAmountIn(address tokenIn, uint256 amountIn, uint256 tokenInBalance);
	error DynaVaultSwapSlippageProtection(address tokenIn, uint256 amountIn, address tokenOut, uint256 amountOut, uint256 minAmountOut);
	error DynaRouterInactive(address router);

	// Config Lib, Gov Lib, Base Strategy, Vault
	error NotAuthorized();

	// Errors Lib
	error ERC5143_SlippageProtection();

	function checkSlippageBelow(uint256 value, uint256 maxValue) internal pure {
		if (value > maxValue) revert ERC5143_SlippageProtection();
	}

	function checkSlippageAbove(uint256 value, uint256 minValue) internal pure {
		if (value < minValue) revert ERC5143_SlippageProtection();
	}

	function checkSlippageAbove(uint256[] memory values, uint256[] memory minValues) internal pure {
		if (values.length != minValues.length) revert ArrayMismatch();
		for (uint256 i = 0; i < values.length; ++i) {
			checkSlippageAbove(values[i], minValues[i]);
		}
	}
}

File 17 of 36 : DynaVaultLib.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.26;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "solady/src/utils/FixedPointMathLib.sol";
import "./interfaces/IDynaStrategyAPI.sol";
import "./interfaces/IDynaRouterAPI.sol";
import "./interfaces/IVaultManagerAPI.sol";
import "./interfaces/IReferenceAssetOracle.sol";
import "./utils/Checks.sol";
import "./VaultConfigLib.sol";
import "./VaultRouterLib.sol";
import "./DynaVaultErrors.sol";

/**
 * @title DynaVault library
 * @notice Contains logic to implement EIP4626, EIP5143 vault standards including our redeemProportional extensions.
 * https://eips.ethereum.org/EIPS/eip-4626
 * https://eips.ethereum.org/EIPS/eip-5143
 */
library DynaVaultLib {
	using Checks for address;
	using FixedPointMathLib for uint256;
	using SafeERC20 for IERC20;

	/// @dev The storage slot follows EIP1967 to avoid storage collision
	bytes32 private constant VAULT_STORAGE_POSITION = bytes32(uint256(keccak256("DynaVault.VaultStorage")) - 1);
	address private constant ZERO_ADDRESS = address(0);
	uint256 private constant DEFAULT_MAX_TOTAL_ASSETS = type(uint256).max >> 64;
	uint256 private constant MAX_BPS = 100e2;
	uint256 private constant PRECISION = 1e18;
	uint8 private constant FEE_CALC_ITERATIONS = 10;

	struct VaultStorage {
		uint256 depositPrecision;
		uint256 minDepositAssets /* Minimum depositAmount in assets a user can deposit */;
		uint256 maxTotalAssets /* Limit for totalAssets the Vault can hold */;
	}

	event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);
	event Withdraw(address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares);
	event WithdrawFromStrategy(address strategy, uint256 strategyTotalDebt, uint256 loss);
	event DepositLimits(uint256 minDepositAssets, uint256 maxTotalAssets);

	/**
	 * @notice Returns the vault storage
	 * @return vs Storage pointer for accessing the state variables
	 */
	function vaultStorage() private pure returns (VaultStorage storage vs) {
		bytes32 position = VAULT_STORAGE_POSITION;
		assembly {
			vs.slot := position
		}
	}

	/** @notice Initializes the dyna vault library */
	function initialize() external {
		VaultStorage storage _storage = vaultStorage();
		if (_storage.maxTotalAssets != 0) {
			revert Checks.AlreadyInitialized();
		}
		_storage.depositPrecision = 10 ** VaultConfigLib.depositDecimals();
		_storage.maxTotalAssets = DEFAULT_MAX_TOTAL_ASSETS;
	}

	/**
	 * @notice Returns the vault manager API for interactions
	 * @return vaultManagerAPI Vault manager API instance that can be used for interactions
	 */
	function _manager() private view returns (IVaultManagerAPI) {
		return IVaultManagerAPI(VaultConfigLib.manager());
	}

	/**
	 * @notice Returns the address of the deposit token
	 * @return asset The address of the deposit token
	 */
	function _asset() private view returns (address) {
		return VaultConfigLib.asset();
	}

	/**
	 * @notice Returns the total value of the vault tokens in deposit token
	 * @return total The total value of vault tokens in deposit token
	 */
	function totalAssets() internal view returns (uint256 total) {
		total = _manager().totalAssets();
	}

	/**
	 * @notice Private function that calculates the value of shares in assets given total supply
	 * @param shares The amount of shares
	 * @param givenTotalSupply  The total shares supply to use in calculation
	 * @param rounding Specify to round up or down
	 * @return assets value of shares in assets
	 */
	function _convertToAssetsGivenTotalSupply(uint256 shares, uint256 givenTotalSupply, Math.Rounding rounding) private view returns (uint256 assets) {
		if (shares == 0) return 0;
		uint256 _freeFunds = _manager().freeFunds();
		if (givenTotalSupply == 0) {
			return _fullMulDiv(shares, vaultStorage().depositPrecision, PRECISION, rounding);
		} else {
			return _fullMulDiv(shares, _freeFunds, givenTotalSupply, rounding);
		}
	}

	/**
	 * @notice Private function that calculates the value of shares in assets given total supply
	 * @param shares The amount of shares
	 * @param givenTotalSupply  The total shares supply to use in calculation
	 * @param givenFreeFunds The total free funds
	 * @param rounding Specify to round up or down
	 * @return assets value of shares in assets
	 */
	function _convertToAssetsGivenTotalSupplyAndFreeFunds(
		uint256 shares,
		uint256 givenTotalSupply,
		uint256 givenFreeFunds,
		Math.Rounding rounding
	) private view returns (uint256 assets) {
		if (shares == 0) return 0;
		if (givenTotalSupply == 0) return _fullMulDiv(shares, vaultStorage().depositPrecision, PRECISION, rounding);
		return _fullMulDiv(shares, givenFreeFunds, givenTotalSupply, rounding);
	}

	/**
	 * @dev Do high precision multiplication and division with a specified rounding direction
	 * @notice Always round down towards the user and up towards the protocol to avoid exploitation
	 */
	function _fullMulDiv(uint256 x, uint256 y, uint256 z, Math.Rounding rounding) private pure returns (uint256) {
		if (rounding == Math.Rounding.Up) {
			return x.fullMulDivUp(y, z);
		} else {
			return x.fullMulDiv(y, z);
		}
	}

	/**
	 * @notice Converts assets to shares
	 * @param shares The amount of shares to convert
	 * @param rounding Specify to round up or down
	 * @return assets value of shares in assets
	 */
	function convertToAssets(uint256 shares, Math.Rounding rounding) internal view returns (uint256 assets) {
		uint256 givenTotalSupply = totalSupply();
		assets = _convertToAssetsGivenTotalSupply(shares, givenTotalSupply, rounding);
	}

	/**
	 * @notice Private function that calculates the value of assets in shares given total supply
	 * @param assets The amount of assets
	 * @param givenTotalSupply The total shares supply to use in calculation
	 * @param rounding Specify to round up or down
	 * @return shares The value of assets in shares
	 */
	function _convertToSharesGivenTotalSupply(uint256 assets, uint256 givenTotalSupply, Math.Rounding rounding) private view returns (uint256 shares) {
		if (assets == 0) return 0;
		uint256 _freeFunds = _manager().freeFunds();
		if (givenTotalSupply == 0 || _freeFunds == 0) {
			return _fullMulDiv(assets, PRECISION, vaultStorage().depositPrecision, rounding);
		} else {
			return _fullMulDiv(assets, givenTotalSupply, _freeFunds, rounding);
		}
	}

	/**
	 * @notice Private function that calculates the value of assets in shares given total supply
	 * @param assets The amount of assets
	 * @param givenTotalSupply The total shares supply to use in calculation
	 * @param givenFreeFunds The total free funds
	 * @param rounding Specify to round up or down
	 * @return shares The value of assets in shares
	 */
	function _convertToSharesGivenTotalSupplyAndFreeFunds(
		uint256 assets,
		uint256 givenTotalSupply,
		uint256 givenFreeFunds,
		Math.Rounding rounding
	) private view returns (uint256 shares) {
		if (assets == 0) return 0;
		if (givenTotalSupply == 0 || givenFreeFunds == 0) return _fullMulDiv(assets, PRECISION, vaultStorage().depositPrecision, rounding);
		return _fullMulDiv(assets, givenTotalSupply, givenFreeFunds, rounding);
	}

	/**
	 * @notice Converts assets to shares
	 * @param assets The amount of assets to convert
	 * @return shares The value of assets in shares
	 */
	function convertToShares(uint256 assets) internal view returns (uint256 shares) {
		return _convertToSharesGivenTotalSupply(assets, totalSupply(), Math.Rounding.Up);
	}

	/** @notice Returns the total supply of shares */
	function totalSupply() private view returns (uint256) {
		return IERC20(address(this)).totalSupply();
	}

	/**
	 * @notice Set deposit limits
	 * @param newMinDepositAssets The new minimum deposit amount limit
	 * @param newMaxTotalAssets The new max total assets limit
	 */
	function setDepositLimits(uint256 newMinDepositAssets, uint256 newMaxTotalAssets) external {
		IVaultManagerAPI manager = _manager();
		manager.checkGovernance(msg.sender);
		// avoid maxMint from overflowing on convertToAssets
		if (newMaxTotalAssets > DEFAULT_MAX_TOTAL_ASSETS || newMaxTotalAssets == 0) revert DynaVaultErrors.MaxTotalAssets();
		if (newMinDepositAssets >= newMaxTotalAssets) revert DynaVaultErrors.MinAboveMax();
		vaultStorage().minDepositAssets = newMinDepositAssets;
		vaultStorage().maxTotalAssets = newMaxTotalAssets;
		emit DepositLimits(newMinDepositAssets, newMaxTotalAssets);
	}

	/** @notice Returns the minimum amount possible to deposit */
	function minDepositLimit() external view returns (uint256) {
		return vaultStorage().minDepositAssets;
	}

	/** @notice Returns the max amount possible to deposit */
	function maxDepositLimit() internal view returns (uint256 limit) {
		if (vaultStorage().maxTotalAssets > totalAssets()) {
			return vaultStorage().maxTotalAssets - totalAssets();
		}
	}

	/** @notice Returns the max amount of total assets possible */
	function maxTotalAssets() external view returns (uint256) {
		return vaultStorage().maxTotalAssets;
	}

	/**
	 * @notice Checks if an amount of assets are above the max amount possible to deposit
	 * @param assets The amount of assets to check
	 */
	function checkMaxDeposit(uint256 assets) internal view {
		if (assets > maxDepositLimit()) {
			revert DynaVaultErrors.MaxDeposit();
		}
	}

	/**
	 * @notice Checks if an amount of assets is below the min deposit limit
	 * @param assets The amount of assets to check
	 */
	function checkMinDeposit(uint256 assets) internal view {
		if (assets <= vaultStorage().minDepositAssets) {
			revert DynaVaultErrors.MinDeposit();
		}
	}

	/** @notice Returns the max amount of shares that can be minted */
	function maxMint() internal view returns (uint256) {
		uint256 maxDepositAssets = maxDepositLimit();
		return (maxDepositAssets == type(uint256).max) ? type(uint256).max : convertToShares(maxDepositAssets);
	}

	/**
	 * @notice Checks if an amount of shares exceeds the max amount possible
	 * @param shares amount of shares to check
	 */
	function checkMaxMint(uint256 shares) external view {
		if (shares > maxMint()) {
			revert DynaVaultErrors.MaxMint();
		}
	}

	/**
	 * Maximum amount of the underlying asset that can be withdrawn from the owner balance in the Vault,
	 * through a withdraw call.
	 * @notice This does not include fees to allow exiting the vault withdrawing the asset value of the entire balance.
	 * @param owner The address of the owner of the shares to withdraw.
	 * @return The maximum amount of assets that can be withdrawn.
	 */
	function maxWithdraw(address owner) internal view returns (uint256) {
		return _convertToAssetsGivenTotalSupply(sharesOf(owner), totalSupply(), Math.Rounding.Down);
	}

	/**
	 * @dev Check if withdrawal of assets is within limits.
	 * @param assets The amount of assets to withdraw.
	 * @param owner The address of the owner.
	 */
	function checkWithdraw(uint256 assets, address owner) internal view returns (uint256 maxAssets) {
		maxAssets = maxWithdraw(owner);
		if (assets > maxAssets) {
			revert DynaVaultErrors.MaxWithdraw();
		}
		uint256 remainingAssets = maxAssets - assets;
		if (remainingAssets != 0 && remainingAssets < vaultStorage().minDepositAssets) {
			revert DynaVaultErrors.MinWithdraw();
		}
	}

	/**
	 * @notice Wraps erc20 balanceOf
	 * @param user The address of the user.
	 * @return Shares balance of user.
	 */
	function sharesOf(address user) internal view returns (uint256) {
		return IERC20(address(this)).balanceOf(user);
	}

	/** @notice Allows vault to report reserves during withdraw and redeem */
	function reportAllReserves() internal returns (uint256 reportedFreeFunds) {
		reportedFreeFunds = _manager().reportAllReservesFromVault();
	}

	/**
	 * Maximum amount of shares that can be withdrawn from the owner balance in the Vault,
	 * through a redeem call.
	 * @notice This does not include fees to allow exiting the vault redeeming entire balance.
	 * @param owner The address of the owner of the shares to redeem.
	 * @return The maximum amount of assets that can be withdrawn.
	 */
	function maxRedeem(address owner) internal view returns (uint256) {
		return sharesOf(owner);
	}

	/**
	 * @dev Check if redeem of shares is within limits.
	 * @param shares The amount of assets to withdraw.
	 * @param owner The address of the owner.
	 * @param reportedFreeFunds The reported free funds in the vault
	 */
	function checkRedeem(uint256 shares, address owner, uint256 reportedFreeFunds) external view {
		uint256 maxShares = maxRedeem(owner);
		if (shares > maxShares) {
			revert DynaVaultErrors.MaxRedeem();
		}
		uint256 remainingShares = maxShares - shares;
		if (
			remainingShares != 0 &&
			_convertToAssetsGivenTotalSupplyAndFreeFunds(remainingShares, totalSupply(), reportedFreeFunds, Math.Rounding.Down) <
			vaultStorage().minDepositAssets
		) {
			revert DynaVaultErrors.MinRedeem();
		}
	}

	/**
	 * @notice Return preview of deposit
	 * @param assets The amount of assets to deposit
	 * @param reportedFreeFunds The reported free funds in vault
	 */
	function previewDeposit(uint256 assets, uint256 reportedFreeFunds) external view returns (uint256) {
		uint256 depositFee = _manager().getFees().depositFee;
		return _convertToSharesGivenTotalSupplyAndFreeFunds(assets - _feeOnTotal(assets, depositFee), totalSupply(), reportedFreeFunds, Math.Rounding.Down);
	}

	/**
	 * @notice previews value of amount of shares to mint
	 * @param shares amount of shares minted
	 * @param reportedFreeFunds The reported free funds in vault
	 * @return assets value of shares including fees
	 */
	function previewMint(uint256 shares, uint256 reportedFreeFunds) internal view returns (uint256) {
		uint256 depositFee = _manager().getFees().depositFee;
		uint256 assets = _convertToAssetsGivenTotalSupplyAndFreeFunds(shares, totalSupply(), reportedFreeFunds, Math.Rounding.Up);
		return assets + _feeOnRaw(assets, depositFee);
	}

	/**
	 * @notice Return preview of withdraw
	 * @param assets The amount of assets to withdraw
	 */
	function previewWithdraw(uint256 assets) internal view returns (uint256) {
		return _convertToSharesGivenTotalSupply(assets + _calculateRedemptionFee(assets), totalSupply(), Math.Rounding.Up);
	}

	/**
	 * @notice Return preview of redeem
	 * @param shares The amount of shares to redeem
	 */
	function previewRedeem(uint256 shares) public view returns (uint256) {
		uint256 redemptionFee = _manager().getFees().redemptionFee;
		uint256 assets = _convertToAssetsGivenTotalSupply(shares, totalSupply(), Math.Rounding.Down);
		return assets - _feeOnTotal(assets, redemptionFee);
	}

	/**
	 * @notice Returns shares ratio based on unlocked funds and of total supply
	 * @param shares The amount of shares to convert
	 * @return ratio Ratio in PRECISION decimals
	 */
	function _convertToRatio(uint256 shares) private view returns (uint256 ratio) {
		ratio = FixedPointMathLib.fullMulDiv(shares, _manager().unlockedFundsRatio(), totalSupply());
	}

	/**
	 * @notice Calculates amounts for redeem proportional
	 * @param shares The amount of shares to redeem
	 * @return toRedeem An array of proportional amounts to redeem
	 */
	function calcRedeemProportional(uint256 shares) external returns (uint256[] memory) {
		IVaultManagerAPI manager = _manager();
		uint256 ratio = _convertToRatio(shares);
		uint256 nrOfTokens = manager.nrOfTokens();
		uint256[] memory toRedeem = new uint256[](nrOfTokens);
		for (uint256 i = 0; i < nrOfTokens; ++i) {
			address tokenAddress = manager.tokens(i);
			TokenStats memory stats = manager.tokenStats(tokenAddress);
			uint256 tokenTotal = stats.tokenIdle + stats.tokenDebt;
			toRedeem[i] = FixedPointMathLib.fullMulDiv(tokenTotal, ratio, PRECISION);
			if (toRedeem[i] > stats.tokenIdle) {
				// fetch from strategies
				(uint256 totalLoss, uint256 totalWithdrawn) = _withdrawTokenDebtFromStrategies(tokenAddress, toRedeem[i] - stats.tokenIdle);
				// adjust toRedeem based on loss incurred during withdrawal
				if (totalLoss > 0) {
					toRedeem[i] -= Math.min(toRedeem[i], totalLoss);
				}
				// update amountIdle after withdrawal
				manager.depositIdle(tokenAddress, totalWithdrawn);
			}
		}
		return toRedeem;
	}

	/**
	 * @notice Transfers proportional amounts of reserve tokens
	 * @param receiver The address of receiver
	 * @param toRedeem Array of token amounts to redeem
	 */
	function transferProportional(address receiver, uint256[] memory toRedeem) external {
		IVaultManagerAPI manager = _manager();
		uint256 nrOfTokens = manager.nrOfTokens();
		if (toRedeem.length != nrOfTokens) {
			revert DynaVaultErrors.ArrayMismatch();
		}
		uint256 redemptionFee = manager.getFees().redemptionFee;
		address feeRecipient = manager.getFees().redemptionFeeWallet;
		for (uint256 i = 0; i < nrOfTokens; ++i) {
			address token = manager.tokens(i);
			uint256 fee = 0;
			manager.withdrawIdle(token, toRedeem[i]);
			if (toRedeem[i] != 0 && redemptionFee != 0) fee = _feeOnTotal(toRedeem[i], redemptionFee);
			IERC20(token).safeTransfer(receiver, toRedeem[i] - fee);
			if (fee != 0 && feeRecipient != address(this)) {
				IERC20(token).safeTransfer(feeRecipient, fee);
			}
		}
	}

	/**
	 * @notice Returns value of token in quote asset
	 * @param base The address of base token
	 * @param amount The amount of token
	 * @param quote The address of quote token
	 * @return value The value of amount in quote token
	 */
	function tokenValueInQuoteAsset(address base, uint256 amount, address quote) internal view returns (uint256 value) {
		IReferenceAssetOracle _referenceAssetOracle = IReferenceAssetOracle(VaultConfigLib.referenceAssetOracle());
		(uint256 price, ) = _referenceAssetOracle.getPrice(base, quote);
		return FixedPointMathLib.fullMulDiv(price, amount, (10 ** IERC20Metadata(base).decimals()));
	}

	/**
	 * @notice feeShares is approximated instead of being computed with the formula:
	 * amount * supply / (assets - amount)
	 * Minting shares increases the supply, so if you don't correct for this dilution during the calculation,
	 * your shares would be worth a lot less than the intended fees.
	 * @param feeAmount The amount of fees
	 * @param feeToken The address of the fee token
	 * @param deltaTotalAssets Updates total assets in fee shares calculation
	 * @return feeShares The amount of shares to mint for fees
	 */
	function calcSharesForFeeAmount(uint256 feeAmount, address feeToken, uint256 deltaTotalAssets) internal view returns (uint256 feeShares) {
		feeShares = calcSharesForFeeAmountUsingGivenTotalSupplyAndTotalAssets(feeAmount, feeToken, deltaTotalAssets, totalSupply(), totalAssets());
	}

	/**
	 * @notice Calculates shares for fees with given values for total supply and total assets
	 * @param feeAmount The amount for fees
	 * @param feeToken The address of fee token
	 * @param deltaTotalAssets Updates total assets in fee shares calculation
	 * @param givenTotalSupply The amount of total supply to use in calculation
	 * @param givenTotalAssets The amount of total assets to use in calculation
	 * @return feeShares The amount of shares to mint for fees
	 */
	function calcSharesForFeeAmountUsingGivenTotalSupplyAndTotalAssets(
		uint256 feeAmount,
		address feeToken,
		uint256 deltaTotalAssets,
		uint256 givenTotalSupply,
		uint256 givenTotalAssets
	) internal view returns (uint256 feeShares) {
		address depositToken = _asset();
		uint256 feeAmountInDepositToken = (feeToken == depositToken) ? feeAmount : tokenValueInQuoteAsset(feeToken, feeAmount, depositToken);
		uint256 _freeFunds = givenTotalAssets;
		uint256 unlockedRatio = _manager().unlockedFundsRatio();
		if (deltaTotalAssets != 0) {
			// calculate free amount with updated total assets when reporting from strategy
			uint256 deltaTotalAssetsInDeposit = (feeToken == depositToken)
				? deltaTotalAssets
				: tokenValueInQuoteAsset(feeToken, deltaTotalAssets, depositToken);
			_freeFunds = FixedPointMathLib.fullMulDiv(_freeFunds + deltaTotalAssetsInDeposit, unlockedRatio, PRECISION);
		} else {
			_freeFunds = FixedPointMathLib.fullMulDiv(_freeFunds, unlockedRatio, PRECISION);
		}
		if (_freeFunds != 0) {
			uint256 lastFeeSharesApproximation;
			for (uint8 i = 0; i < FEE_CALC_ITERATIONS; ++i) {
				// Calculate the Error Term and refine the approximation
				feeShares += ((feeAmountInDepositToken * (givenTotalSupply + feeShares)) / _freeFunds) - feeShares;
				if (feeShares == lastFeeSharesApproximation) break;
				lastFeeSharesApproximation = feeShares;
			}
		}
	}

	/**
	 * @notice Calculates shares for fees with given values for total supply and free funds
	 * @param feeAmount The amount for fees
	 * @param feeToken The address of fee token
	 * @param givenTotalSupply The amount of total supply to use in calculation
	 * @param givenFreeFunds The amount of free funds to use in calculation
	 * @return feeShares The amount of shares to mint for fees
	 */
	function calcSharesForFeeAmountUsingGivenTotalSupplyAndFreeFunds(
		uint256 feeAmount,
		address feeToken,
		uint256 givenTotalSupply,
		uint256 givenFreeFunds
	) internal view returns (uint256 feeShares) {
		address depositToken = _asset();
		uint256 feeAmountInDepositToken = (feeToken == depositToken) ? feeAmount : tokenValueInQuoteAsset(feeToken, feeAmount, depositToken);
		if (givenFreeFunds != 0) {
			uint256 lastFeeSharesApproximation;
			for (uint8 i = 0; i < FEE_CALC_ITERATIONS; ++i) {
				// Calculate the Error Term and refine the approximation
				feeShares += ((feeAmountInDepositToken * (givenTotalSupply + feeShares)) / givenFreeFunds) - feeShares;
				if (feeShares == lastFeeSharesApproximation) break;
				lastFeeSharesApproximation = feeShares;
			}
		}
	}

	/**
	 * @dev Deposit/mint common workflow.
	 * @param caller The address of caller
	 * @param assetsIncludingFees The amount of assets including fees
	 * @return fee The fee amount
	 */
	function beforeMint(address caller, uint256 assetsIncludingFees) external returns (uint256 fee) {
		// If token is ERC777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the
		// `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer,
		// calls the vault, which is assumed not malicious.
		//
		// Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the
		// assets are transferred and before the shares are minted, which is a valid state.
		IVaultManagerAPI manager = _manager();
		address token = _asset();
		if (IERC20(token).balanceOf(caller) < assetsIncludingFees) {
			revert DynaVaultErrors.ERC20InsufficientBalance();
		}
		if (IERC20(token).allowance(caller, address(this)) < assetsIncludingFees) {
			revert DynaVaultErrors.ERC20InsufficientAllowance();
		}
		uint256 depositFee = manager.getFees().depositFee;
		fee = _feeOnTotal(assetsIncludingFees, depositFee);
		IERC20(token).safeTransferFrom(caller, address(this), assetsIncludingFees);
		manager.depositDepositToken(assetsIncludingFees, fee);
	}

	/**
	 * @notice Transfers fee to fee recipient
	 * @param caller The address of the caller
	 * @param receiver The address of the receiver
	 * @param assetsIncludingFees The amount of assets including amount for fees
	 * @param _sharesWithoutFees The amount of shares not including fees
	 * @param fee The fee amount
	 */
	function afterMint(address caller, address receiver, uint256 assetsIncludingFees, uint256 _sharesWithoutFees, uint256 fee) external {
		emit Deposit(caller, receiver, assetsIncludingFees, _sharesWithoutFees);
		address feeRecipient = _manager().getFees().depositFeeWallet;
		if (fee > 0 && feeRecipient != address(this)) {
			SafeERC20.safeTransfer(IERC20(_asset()), feeRecipient, fee);
		}
	}

	/**
	 * @notice Withdraw debt from strategy
	 * @param strategy The address of strategy
	 * @param tokenAddress The address of token
	 * @param amountNeeded The amount wanted to withdraw
	 */
	function _withdrawStrategyDebt(
		address strategy,
		address tokenAddress,
		uint256 amountNeeded
	) private returns (uint256 strategyDebt, uint256 loss, uint256 withdrawn) {
		// NOTE: Don't withdraw more than the debt so that Strategy can still
		//       continue to work based on the profits it has
		// NOTE: This means that user will lose out on any profits that each
		//       Strategy in the queue would return on next harvest, benefiting others
		strategyDebt = _manager().strategyDebt(strategy);
		if (amountNeeded > strategyDebt) amountNeeded = strategyDebt;
		// Withdraw amount
		uint256 preBalance = IERC20(tokenAddress).balanceOf(address(this));
		loss = IDynaStrategyAPI(strategy).withdraw(amountNeeded);
		withdrawn = IERC20(tokenAddress).balanceOf(address(this)) - preBalance;
	}

	/**
	 * @notice Withdraw token debt from strategies
	 * @param tokenAddress The address of token to withdraw
	 * @param valueToWithdraw The amount of tokens to withdraw
	 */
	function withdrawTokenDebtFromStrategies(address tokenAddress, uint256 valueToWithdraw) internal returns (uint256 totalLoss, uint256 totalWithdrawn) {
		VaultConfigLib.onlyManager();
		return _withdrawTokenDebtFromStrategies(tokenAddress, valueToWithdraw);
	}

	/**
	 * @dev Withdraw/redeem common workflow.
	 * @param assetsNotIncludingFees The assets that receiver will get
	 * @param sharesIncludingFees The shares that are burned
	 * @return assetsToWithdraw The amount of assets to withdraw
	 */
	function beforeBurn(uint256 assetsNotIncludingFees, uint256 sharesIncludingFees) external returns (uint256 assetsToWithdraw) {
		IVaultManagerAPI manager = _manager();
		uint256 redemptionFee = manager.getFees().redemptionFee;
		uint256 fee = _feeOnRaw(assetsNotIncludingFees, redemptionFee);
		(uint256 tokenIdle, uint256 tokenDebt) = manager.tokenIdleDebt(_asset());
		// we must have enough tokens to send to both the withdrawer and the fee recipient
		if (tokenIdle >= assetsNotIncludingFees + fee) {
			assetsToWithdraw = assetsNotIncludingFees + fee;
		} else {
			// if deposit token vault idle balance is not sufficient,
			// start swapping reserve assets based on shares/totalSupply ratio
			uint256 nrOfTokens = manager.nrOfTokens();
			uint256 ratio = FixedPointMathLib.fullMulDiv(sharesIncludingFees, PRECISION, totalSupply());
			uint256 totalLoss;
			uint256 depositTokensAllocatedForWithdraw;
			address asset = _asset();
			for (uint256 t = 0; t < nrOfTokens; ++t) {
				address tokenAddress = manager.tokens(t);
				(tokenIdle, tokenDebt) = manager.tokenIdleDebt(tokenAddress);
				uint256 toRedeem = FixedPointMathLib.fullMulDiv((tokenIdle + tokenDebt), ratio, PRECISION);
				if (toRedeem == 0) continue;
				if (tokenIdle < toRedeem) {
					// fetch from strategies
					(uint256 tokenLoss, uint256 tokenWithdrawn) = _withdrawTokenDebtFromStrategies(tokenAddress, toRedeem - tokenIdle);
					// adjust toRedeem based on loss incurred during withdrawal
					if (tokenLoss != 0) {
						toRedeem = (tokenLoss < toRedeem) ? toRedeem - tokenLoss : 0;
						totalLoss += (t > 0 && tokenLoss > 0) ? tokenValueInQuoteAsset(tokenAddress, tokenLoss, asset) : tokenLoss;
					}
					// update tokenIdle after withdrawal
					manager.depositIdle(tokenAddress, tokenWithdrawn);
				}
				// no need to swap for deposit token
				if (t == 0) {
					depositTokensAllocatedForWithdraw = toRedeem;
					continue;
				}
				// swap reserve to deposit
				uint256 amountOut;
				{
					(, address selectedRouter, bytes32[] memory swapData) = VaultRouterLib.previewSwap(tokenAddress, toRedeem, asset);
					uint256 allowed = IERC20(tokenAddress).allowance(address(this), selectedRouter);
					if (allowed < toRedeem) IERC20(tokenAddress).safeIncreaseAllowance(selectedRouter, toRedeem);
					uint256 tokenOutInitialBalance = IERC20(asset).balanceOf(address(this));
					IDynaRouterAPI(selectedRouter).swap(tokenAddress, toRedeem, asset, 0, address(this), swapData);
					amountOut = IERC20(asset).balanceOf(address(this)) - tokenOutInitialBalance;
				}
				depositTokensAllocatedForWithdraw += amountOut;
				manager.updateDebtAfterSwap(tokenAddress, toRedeem, asset, amountOut, false);
			}
			manager.setTotalIdle(asset, IERC20(asset).balanceOf(address(this)));
			// NOTE: This loss protection is put in place to revert if losses from
			//       withdrawing from strategies are more than what is considered acceptable.
			{
				// fix stack too deep
				uint256 depositTokensToWithdraw = assetsNotIncludingFees + fee;
				uint256 maxLoss = VaultConfigLib.maxLoss(); // max loss BPS for loss protection
				if (totalLoss > FixedPointMathLib.fullMulDiv(maxLoss, depositTokensToWithdraw, MAX_BPS)) {
					revert DynaVaultErrors.StrategyLossProtection(depositTokensToWithdraw, totalLoss, maxLoss);
				}
				assetsToWithdraw = Math.min(depositTokensAllocatedForWithdraw, depositTokensToWithdraw);
			}
		}
		// withdraw from tokenIdle and update locked profit ratio based on assetsToWithdraw including redemption fees
		_manager().withdrawDepositToken(assetsToWithdraw);
		address feeRecipient = manager.getFees().redemptionFeeWallet;
		// we send to the fee recipient when needed
		if (fee > 0 && feeRecipient != address(this)) {
			SafeERC20.safeTransfer(IERC20(_asset()), feeRecipient, fee);
		}
		// always subtract fee from assetsToWithdraw amount to adjust the amount returned
		assetsToWithdraw = assetsToWithdraw > fee ? assetsToWithdraw - fee : 0;
	}

	/**
	 * @notice Called during withdraw after burning of the users shares, is responsible to send assets to the receiver
	 * @param caller the address of the caller
	 * @param receiver The address of the receiver
	 * @param owner The address of the owner
	 * @param assetsNotIncludingFees The assets that receiver will get
	 * @param sharesIncludingFeesBurned The shares that are burned
	 */
	function afterBurn(address caller, address receiver, address owner, uint256 assetsNotIncludingFees, uint256 sharesIncludingFeesBurned) external {
		IERC20(_asset()).safeTransfer(receiver, assetsNotIncludingFees);
		emit Withdraw(caller, receiver, owner, assetsNotIncludingFees, sharesIncludingFeesBurned);
	}

	/**
	 * @notice Internal function returns a calculated redemption fee amount
	 * @param assets The amount of assets to calculate amount on
	 * @return fees The amount of fees on assets
	 */
	function _calculateRedemptionFee(uint256 assets) private view returns (uint256 fees) {
		fees = _feeOnRaw(assets, _manager().getFees().redemptionFee);
	}

	/**
	 * @notice Internal function returns max redemption without fee amount
	 * @param shares The amount of shares to calculate amount on
	 * @return assetsNotIncludingFees The assets without fees
	 */
	function _calculateMaxAssetsNotIncludingFees(uint256 shares) private view returns (uint256 assetsNotIncludingFees) {
		uint256 maxAssetsIncludingFees = convertToAssets(shares, Math.Rounding.Down);
		return maxAssetsIncludingFees - _calculateRedemptionFee(maxAssetsIncludingFees);
	}

	/**
	 * @notice Reports all reserves and calculates shares to burn when withdrawing assets
	 * @param assetsNotIncludingFees The amount of assets not including  fees
	 * @param owner The address of the owner
	 * @return sharesToBurn The amount of shares to burn
	 * @return assetsExcludingFees The amount of assets excluding fees
	 */
	function reportAndCalculateWithdraw(uint256 assetsNotIncludingFees, address owner) internal returns (uint256 sharesToBurn, uint256 assetsExcludingFees) {
		uint256 freeFunds = reportAllReserves();
		uint256 sharesIncludingFees;
		if (assetsNotIncludingFees == type(uint256).max) {
			sharesIncludingFees = sharesOf(owner);
			assetsNotIncludingFees = _calculateMaxAssetsNotIncludingFees(sharesIncludingFees);
		} else {
			uint256 totalShares = totalSupply();
			// check withdraw
			uint256 maxAssets = _convertToAssetsGivenTotalSupplyAndFreeFunds(sharesOf(owner), totalShares, freeFunds, Math.Rounding.Down);
			if (assetsNotIncludingFees > maxAssets) revert DynaVaultErrors.MaxWithdraw();
			uint256 remainingAssets = maxAssets - assetsNotIncludingFees;
			if (remainingAssets != 0 && remainingAssets < vaultStorage().minDepositAssets) {
				revert DynaVaultErrors.MinWithdraw();
			}
			// preview withdraw
			sharesIncludingFees = _convertToSharesGivenTotalSupplyAndFreeFunds(
				assetsNotIncludingFees + _calculateRedemptionFee(assetsNotIncludingFees),
				totalShares,
				freeFunds,
				Math.Rounding.Up
			);
		}
		if (sharesIncludingFees == 0) {
			revert DynaVaultErrors.ZeroShares();
		}
		return (sharesIncludingFees, assetsNotIncludingFees);
	}

	/**
	 * @notice Reports all reserves and calculates assets to withdraw
	 * @param sharesIncludingFees The amount of shares including  fees
	 * @param owner The address of the owner
	 * @return assetsExcludingFees The amount of assets excluding fees
	 */
	function reportAndCalculateRedeem(uint256 sharesIncludingFees, address owner) internal returns (uint256 assetsExcludingFees) {
		uint256 freeFunds = reportAllReserves();
		uint256 totalShares = totalSupply();

		// checkRedeem
		uint256 maxShares = maxRedeem(owner);
		if (sharesIncludingFees > maxShares) revert DynaVaultErrors.MaxRedeem();
		uint256 remainingShares = maxShares - sharesIncludingFees;
		if (
			remainingShares != 0 &&
			_convertToAssetsGivenTotalSupplyAndFreeFunds(remainingShares, totalShares, freeFunds, Math.Rounding.Down) < vaultStorage().minDepositAssets
		) {
			revert DynaVaultErrors.MinRedeem();
		}

		// previewRedeem
		uint256 redemptionFee = _manager().getFees().redemptionFee;
		uint256 assets = _convertToAssetsGivenTotalSupplyAndFreeFunds(sharesIncludingFees, totalShares, freeFunds, Math.Rounding.Down);
		assetsExcludingFees = assets - _feeOnTotal(assets, redemptionFee);
	}

	/**
	 * @notice Returns fee amount on the raw assets amount
	 * @param assets The amount of assets
	 * @param feeBasePoint The fee base points
	 */
	function _feeOnRaw(uint256 assets, uint256 feeBasePoint) private pure returns (uint256) {
		return feeBasePoint > 0 ? FixedPointMathLib.fullMulDivUp(assets, feeBasePoint, MAX_BPS) : 0;
	}

	/**
	 * @notice Returns fee amount on total
	 * @param assets The amount of assets
	 * @param feeBasePoint The fee base points
	 */
	function _feeOnTotal(uint256 assets, uint256 feeBasePoint) private pure returns (uint256) {
		return feeBasePoint > 0 ? FixedPointMathLib.fullMulDivUp(assets, feeBasePoint, feeBasePoint + MAX_BPS) : 0;
	}

	/**
	 * @notice Withdraw token debt from strategies
	 * @param tokenAddress The address of token to withdraw
	 * @param valueToWithdraw The amount of tokens to withdraw
	 */
	function _withdrawTokenDebtFromStrategies(address tokenAddress, uint256 valueToWithdraw) private returns (uint256 totalLoss, uint256 totalWithdrawn) {
		IVaultManagerAPI manager = _manager();
		// We need to go get some from our strategies in the withdrawal queue
		// NOTE: This performs forced withdrawals from each Strategy. During
		//       forced withdrawal, a Strategy may realize a loss. That loss
		//       is reported back to the Vault, and the will affect the amount
		//       of tokens that the withdrawer receives for their shares. They
		//       can optionally specify the maximum acceptable loss (in BPS)
		//       to prevent excessive losses on their withdrawals (which may
		//       happen in certain edge cases where Strategies realize a loss)
		uint256 valueAllocatedForWithdraw;
		address[] memory tokenStrategies = manager.getTokenStrategies(tokenAddress);
		for (uint256 s = 0; s < tokenStrategies.length; s++) {
			address strategy = tokenStrategies[s];
			if (strategy == ZERO_ADDRESS) break; // We've exhausted the queue
			if (valueToWithdraw <= valueAllocatedForWithdraw) break; // We're done withdrawing
			uint256 amountNeeded = valueToWithdraw - valueAllocatedForWithdraw;
			(uint256 strategyDebt, uint256 loss, uint256 withdrawn) = _withdrawStrategyDebt(strategy, tokenAddress, amountNeeded);
			totalWithdrawn += withdrawn;
			valueAllocatedForWithdraw += withdrawn;
			// NOTE: Withdrawer incurs any losses from liquidation
			if (loss != 0) {
				valueToWithdraw -= loss;
				totalLoss += loss;
				manager.reportLoss(strategy, loss);
			}
			// Reduce the Strategy's debt by the amount withdrawn ("realized returns")
			// NOTE: This doesn't add to returns as it's not earned by "normal means"
			manager.decreaseStrategyDebt(strategy, withdrawn);
			emit WithdrawFromStrategy(strategy, strategyDebt, loss);
		}
	}
}

// SPDX-License-Identifier: MIT
// Forked OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity 0.8.26;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
	/**
	 * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
	 * @param sender Address whose tokens are being transferred.
	 * @param balance Current balance for the interacting account.
	 * @param needed Minimum amount required to perform a transfer.
	 */
	error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

	/**
	 * @dev Indicates a failure with the token `sender`. Used in transfers.
	 * @param sender Address whose tokens are being transferred.
	 */
	error ERC20InvalidSender(address sender);

	/**
	 * @dev Indicates a failure with the token `receiver`. Used in transfers.
	 * @param receiver Address to which tokens are being transferred.
	 */
	error ERC20InvalidReceiver(address receiver);

	/**
	 * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
	 * @param spender Address that may be allowed to operate on tokens without being their owner.
	 * @param allowance Amount of tokens a `spender` is allowed to operate with.
	 * @param needed Minimum amount required to perform a transfer.
	 */
	error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

	/**
	 * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
	 * @param approver Address initiating an approval operation.
	 */
	error ERC20InvalidApprover(address approver);

	/**
	 * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
	 * @param spender Address that may be allowed to operate on tokens without being their owner.
	 */
	error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
	/**
	 * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
	 * Used in balance queries.
	 * @param owner Address of the current owner of a token.
	 */
	error ERC721InvalidOwner(address owner);

	/**
	 * @dev Indicates a `tokenId` whose `owner` is the zero address.
	 * @param tokenId Identifier number of a token.
	 */
	error ERC721NonexistentToken(uint256 tokenId);

	/**
	 * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
	 * @param sender Address whose tokens are being transferred.
	 * @param tokenId Identifier number of a token.
	 * @param owner Address of the current owner of a token.
	 */
	error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

	/**
	 * @dev Indicates a failure with the token `sender`. Used in transfers.
	 * @param sender Address whose tokens are being transferred.
	 */
	error ERC721InvalidSender(address sender);

	/**
	 * @dev Indicates a failure with the token `receiver`. Used in transfers.
	 * @param receiver Address to which tokens are being transferred.
	 */
	error ERC721InvalidReceiver(address receiver);

	/**
	 * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
	 * @param operator Address that may be allowed to operate on tokens without being their owner.
	 * @param tokenId Identifier number of a token.
	 */
	error ERC721InsufficientApproval(address operator, uint256 tokenId);

	/**
	 * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
	 * @param approver Address initiating an approval operation.
	 */
	error ERC721InvalidApprover(address approver);

	/**
	 * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
	 * @param operator Address that may be allowed to operate on tokens without being their owner.
	 */
	error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
	/**
	 * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
	 * @param sender Address whose tokens are being transferred.
	 * @param balance Current balance for the interacting account.
	 * @param needed Minimum amount required to perform a transfer.
	 * @param tokenId Identifier number of a token.
	 */
	error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

	/**
	 * @dev Indicates a failure with the token `sender`. Used in transfers.
	 * @param sender Address whose tokens are being transferred.
	 */
	error ERC1155InvalidSender(address sender);

	/**
	 * @dev Indicates a failure with the token `receiver`. Used in transfers.
	 * @param receiver Address to which tokens are being transferred.
	 */
	error ERC1155InvalidReceiver(address receiver);

	/**
	 * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
	 * @param operator Address that may be allowed to operate on tokens without being their owner.
	 * @param owner Address of the current owner of a token.
	 */
	error ERC1155MissingApprovalForAll(address operator, address owner);

	/**
	 * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
	 * @param approver Address initiating an approval operation.
	 */
	error ERC1155InvalidApprover(address approver);

	/**
	 * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
	 * @param operator Address that may be allowed to operate on tokens without being their owner.
	 */
	error ERC1155InvalidOperator(address operator);

	/**
	 * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
	 * Used in batch transfers.
	 * @param idsLength Length of the array of token identifiers
	 * @param valuesLength Length of the array of token amounts
	 */
	error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "./IDynaRouterAPI.sol";

interface IDynaRouter is IDynaRouterAPI {
	function getRegistry() external view returns (address);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

/**
 * @title DynaRouter API is a generalized router API for converting one ERC20 token into another
 * @notice each DynaRouter needs to implement these functions
 * there is an abstract BaseDynaRouter to facilitate this
 * The basic usage flow for executing a swap in any DynaRouter is:
 * 1. If the dynarouter supports encoding preview routes the user can optionally call an encodePreviewRoute function
 *    This encodePreviewRoute function is not part of this interface, because the arguments are dependant on the specific AMM
 *    This can encode a route of pools, fee of pool, volatile/stable pair or any data required to calculate a quote in the AMM
 *    The output of the encodePreviewRoute should however always be encoded as an array of bytes32
 * 2. call previewSwapRoute (with given encoded preview route) or previewSwap (which tries to build a default route for given tokens) which returns:
 *    - an expected amount out
 *    - a router (in most cases this return its own address, unless it is an aggregate router)
 *    - encoded swap data (typically this encodes the route of pools, fee of pool, voltatile/stable pair or any data required to swap)
 * 3. call spenderAllowance using the input token which returns:
 *    - the current allowed amount of input tokens that can be spend by router
 * 4. call to increases allowance of the router, when the current allowed amount is less than the amount you want to swap
 * 5. calculate the minimum amount out by applying the slippage to the expected amount out
 *    and call swap giving tokens, (minimum) amounts, selected router, source & destination of assets, encoded swapdata
 */
interface IDynaRouterAPI {
	function getSpender() external view returns (address);

	function spenderAllowance(address token) external view returns (uint256 allowed);

	function estimateConversion(address tokenIn, uint256 amountIn, address tokenOut) external view returns (uint256 amountOut);

	function previewSwap(
		address tokenIn,
		uint256 amountIn,
		address tokenOut
	) external view returns (uint256 amountOut, address router, bytes32[] memory swapData);

	function previewSwapRoute(
		address tokenIn,
		uint256 amountIn,
		address tokenOut,
		bytes32[] memory previewRoute
	) external view returns (uint256 amountOut, address router, bytes32[] memory swapData);

	function swap(address tokenIn, uint256 amountIn, address tokenOut, uint256 minAmountOut, address to, bytes32[] memory swapData) external;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

interface IDynaRouterRegistryAPI {
	struct Route {
		address router;
		bytes32[] route;
	}

	function allRouters(uint256 index) external view returns (address);

	function enabledRouters(uint256 index) external view returns (address);

	function getAllRouters() external view returns (address[] memory selectedRouters);

	function getEnabledRouter(address router) external view returns (bool isEnabled);

	function getEnabledRouters() external view returns (address[] memory selectedRouters);

	function getNativeRouters() external view returns (address[] memory selectedRouters);

	function getTokenRouter(address token, uint256 index) external view returns (address selectedRouter);

	function getTokenRouters(address token) external view returns (address[] memory selectedRouters);

	function getMultiTokenRouters() external view returns (address[] memory selectedRouters);

	function getPairRouter(address tokenIn, address tokenOut, uint256 index) external view returns (address selectedRouter);

	function getPairRouters(address tokenIn, address tokenOut) external view returns (address[] memory selectedRouters);

	function getPairRoutes(address tokenIn, address tokenOut) external view returns (Route[] memory selectedRoutes);

	function getDefaultNativeRouter() external view returns (address defaultRouter);

	function getDefaultTokenRouter(address token) external view returns (address defaultRouter);

	function getDefaultMultiTokenRouter() external view returns (address defaultRouter);

	function getDefaultPairRoute(address tokenIn, address tokenOut) external view returns (Route memory defaultRoute);

	function getDefaultPairRouter(address tokenIn, address tokenOut) external view returns (address defaultRouter);

	function setTokenRouters(address token, address[] memory newRouters) external;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

interface IDynaStrategyAPI {
	function want() external view returns (address);

	function vault() external view returns (address);

	function strategist() external view returns (address);

	function isActive() external view returns (bool);

	function delegatedAssets() external view returns (uint256);

	function estimatedTotalAssets() external view returns (uint256);

	function withdraw(uint256 _amount) external returns (uint256);

	function migrate(address _newStrategy) external;

	function harvest() external;

	function tend() external;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "./IERC5143.sol";
import "./IDynaRouterRegistryAPI.sol";
import "./IVaultSimulatorAPI.sol";

interface IDynaVaultAPI is IERC5143 {
	function initialize(
		string memory _nameOverride,
		string memory _symbolOverride,
		address _manager,
		address _referenceAssetOracle,
		address _dynaRouter,
		address _owner,
		address _simulator
	) external;

	function manager() external view returns (address);

	function routerRegistry() external view returns (IDynaRouterRegistryAPI);

	function referenceAssetOracle() external view returns (address);

	function referenceAsset() external view returns (address);

	function previewRedeemProportional(uint256 shares, IVaultSimulatorAPI.VaultSnapshot memory snapshot) external view returns (uint256[] memory);

	function redeemProportional(uint256 shares, address receiver, address owner) external returns (uint256[] memory);

	function issueSharesForFeeAmount(address to, uint256 feeAmount, address feeToken, uint256 deltaTotalAssets) external returns (uint256);

	function feeTransfer(address _to, uint256 _amount) external;

	function approveAddedToken(address tokenAddress) external;

	function resetRemovedTokenAllowance(address tokenAddress) external;

	function doSwap(address _tokenIn, uint256 _amountIn, address _tokenOut, uint256 _minAmountOut) external returns (uint256 amountOut);

	function calcSharesForFeeAmountUsingGivenTotalSupplyAndTotalAssets(
		uint256 feeAmount,
		address feeToken,
		uint256 deltaTotalAssets,
		uint256 givenTotalSupply,
		uint256 givenTotalAssets
	) external view returns (uint256 feeShares);

	function calcSharesForFeeAmountUsingGivenTotalSupplyAndFreeFunds(
		uint256 feeAmount,
		address feeToken,
		uint256 givenTotalSupply,
		uint256 givenFreeFunds
	) external view returns (uint256 feeShares);

	function tokenValueInQuoteAsset(address _base, uint256 _amount, address _quote) external view returns (uint256 value);

	function withdrawTokenDebtFromStrategies(address tokenAddress, uint256 valueToWithdraw) external returns (uint256 totalLoss, uint256 totalWithdrawn);

	function takeSnapshot() external view returns (IVaultSimulatorAPI.VaultSnapshot memory);

	function simulatedIssueSharesForFeeAmount(
		uint256 feeAmount,
		address feeToken,
		uint256 deltaTotalAssets,
		IVaultSimulatorAPI.VaultSnapshot memory snapshot
	) external view returns (IVaultSimulatorAPI.VaultSnapshot memory);

	function swapAndReport(
		address tokenIn,
		uint256 amountIn,
		address tokenOut,
		uint256 minAmountOut,
		address selectedRouter,
		bytes32[] memory swapData
	) external;

	function previewSwap(
		address tokenIn,
		uint256 amountIn,
		address tokenOut
	) external view returns (uint256 amountOut, address selectedRouter, bytes32[] memory swapData);
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.26;

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

interface IERC4626 is IERC20 {
	/*//////////////////////////////////////////////////////////////
                               IMMUTABLES
    //////////////////////////////////////////////////////////////*/

	function asset() external view returns (address);

	/*//////////////////////////////////////////////////////////////
                        DEPOSIT/WITHDRAWAL LOGIC
    //////////////////////////////////////////////////////////////*/

	function deposit(uint256 assets, address receiver) external returns (uint256 shares);

	function mint(uint256 shares, address receiver) external returns (uint256 assets);

	function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

	function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);

	/*//////////////////////////////////////////////////////////////
                            ACCOUNTING LOGIC
    //////////////////////////////////////////////////////////////*/

	function totalAssets() external view returns (uint256);

	function convertToShares(uint256 assets) external view returns (uint256);

	function convertToAssets(uint256 shares) external view returns (uint256);

	function previewDeposit(uint256 assets) external view returns (uint256);

	function previewMint(uint256 shares) external view returns (uint256);

	function previewWithdraw(uint256 assets) external view returns (uint256);

	function previewRedeem(uint256 shares) external view returns (uint256);

	/*//////////////////////////////////////////////////////////////
                     DEPOSIT/WITHDRAWAL LIMIT LOGIC
    //////////////////////////////////////////////////////////////*/

	function maxDeposit(address) external view returns (uint256);

	function maxMint(address) external view returns (uint256);

	function maxWithdraw(address owner) external view returns (uint256);

	function maxRedeem(address owner) external view returns (uint256);
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.26;

import "./IERC4626.sol";

// @note we had to add the postfix "CheckSlippage" to the standard IERC5143 function names,
// because type chain still has issues disambiguating overloaded functions
interface IERC5143 is IERC4626 {
	function depositCheckSlippage(uint256 assets, address receiver, uint256 minShares) external returns (uint256 shares);

	function mintCheckSlippage(uint256 shares, address receiver, uint256 maxAssets) external returns (uint256 assets);

	function withdrawCheckSlippage(uint256 assets, address receiver, address owner, uint256 maxShares) external returns (uint256 shares);

	function redeemCheckSlippage(uint256 shares, address receiver, address owner, uint256 minAssets) external returns (uint256 assets);

	// NOTE: redeemProportional is not part of the original ERC4626 standard,
	//       so it's an extension we provide on our part to ERC5143
	function redeemProportionalCheckSlippage(
		uint256 shares,
		address receiver,
		address owner,
		uint256[] memory minAssets
	) external returns (uint256[] memory assets);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

interface IMetaDynaRouterAPI {
	struct Connector {
		address tokenOut;
		address router;
		bytes32[] route;
	}

	function routerRegistry() external view returns (address);

	function estimateConversion(address tokenIn, uint256 amountIn, address tokenOut) external view returns (uint256 amountOut);

	function estimateConversionNative(address tokenIn, uint256 amountIn, address tokenOut) external view returns (uint256 amountOut);

	function estimateConversionMulti(
		address[] memory tokensIn,
		uint256[] memory amountsIn,
		address[] memory tokensOut
	) external view returns (uint256[] memory amountsOut);

	function estimateConversionConnectors(address tokenIn, uint256 amountIn, Connector[] memory connectors) external view returns (uint256 amountOut);

	function previewSwap(
		address tokenIn,
		uint256 amountIn,
		address tokenOut
	) external view returns (uint256 amountOut, address router, bytes32[] memory swapData);

	function previewSwapNative(
		address tokenIn,
		uint256 amountIn,
		address tokenOut
	) external view returns (uint256 amountOut, address router, bytes32[] memory swapData);

	function previewSwapMulti(
		address[] memory tokensIn,
		uint256[] memory amountsIn,
		address[] memory tokensOut
	) external view returns (uint256[] memory amountsOut, address router, bytes32[] memory swapData);

	function previewSwapConnectors(
		address tokenIn,
		uint256 amountIn,
		Connector[] memory connectors
	) external view returns (uint256 amountOut, bytes32[][] memory swapDataList);

	function swap(address tokenIn, uint256 amountIn, address tokenOut, uint256 minAmountOut, address router, address to, bytes32[] memory swapData) external;

	function swapNative(
		address tokenIn,
		uint256 amountIn,
		address tokenOut,
		uint256 minAmountOut,
		address router,
		address to,
		bytes32[] memory swapData
	) external payable;

	function swapMulti(
		address[] memory tokensIn,
		uint256[] memory amountsIn,
		address[] memory tokensOut,
		uint256[] memory minAmountsOut,
		address router,
		address to,
		bytes32[] memory swapData
	) external;

	function swapConnectors(
		address tokenIn,
		uint256 amountIn,
		Connector[] memory connectors,
		uint256[] memory minAmountsOut,
		address to,
		bytes32[][] memory swapDataList
	) external payable;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

interface IReferenceAssetOracle {
	function referenceAsset() external view returns (address token);

	function tokenReferenceValue(address token, uint256 amount) external view returns (uint256 referenceValue, uint256 oldestObservation);

	function getPrice(address base, address quote) external view returns (uint256 value, uint256 oldestObservation);
}

File 28 of 36 : IVaultManagerAPI.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "./IVaultSimulatorAPI.sol";

struct Fees {
	uint256 managementFee;
	uint256 performanceFee;
	uint256 depositFee;
	uint256 redemptionFee;
	address managementFeeWallet;
	address performanceFeeWallet;
	address depositFeeWallet;
	address redemptionFeeWallet;
}

struct TokenStats {
	uint256 tokenIdle /* Amount of tokens that are in the vault (reserves) */;
	uint256 tokenDebt /* Amount of tokens that all strategies have borrowed  */;
	uint256 depositDebt; // Amount of deposit tokens used
	uint256 depositDebtRatio; // Target ratio of deposit tokens
	uint256 totalProfit;
	uint256 totalLoss;
	uint256 lastReport;
	uint256 lastReportedValue;
	uint256 watermark;
}

struct StrategyParams {
	address want;
	uint256 performanceFee;
	uint256 activation;
	uint256 debtRatio;
	uint256 minDebtPerHarvest;
	uint256 maxDebtPerHarvest;
	uint256 lastReport;
	uint256 totalDebt;
	uint256 totalGain;
	uint256 totalLoss;
}

interface IVaultManagerAPI {
	function apiVersion() external pure returns (string memory);

	function token() external view returns (address);

	function nrOfTokens() external view returns (uint256);

	function tokens(uint256 index) external view returns (address);

	function tokenIndex(address token) external view returns (uint256);

	function totalIdle(uint256 index) external view returns (uint256);

	function totalDebt(uint256 index) external view returns (uint256);

	function strategies(address _strategy) external view returns (StrategyParams memory);

	function tokenStats(address tokenAddress) external view returns (TokenStats memory);

	function tokenIdleDebt(address tokenAddress) external view returns (uint256 tokenIdle, uint256 tokenDebt);

	function getFees() external view returns (Fees memory);

	function totalTokenAssets() external view returns (uint256 total);

	function totalAssets() external view returns (uint256 total);

	function minDepositLimit() external view returns (uint256);

	function maxDepositLimit() external view returns (uint256);

	/** calculate new strategy debt ratio based on investment amount
	 *  and call strategy harvest to take fees and adjust position */
	function investStrategy(address strategy, uint256 amount) external;

	/**
	 * View how much the Vault would increase this Strategy's borrow limit,
	 * based on its present performance (since its last report). Can be used to
	 * determine expectedReturn in your Strategy.
	 */
	function creditAvailable() external view returns (uint256);

	function creditAvailable(address strategy) external view returns (uint256);

	/**
	 * View how much the Vault would like to pull back from the Strategy,
	 * based on its present performance (since its last report). Can be used to
	 * determine expectedReturn in your Strategy.
	 */
	function debtOutstanding() external view returns (uint256);

	function debtOutstanding(address strategy) external view returns (uint256);

	/**
	 * View how much the Vault expect this Strategy to return at the current
	 * block, based on its present performance (since its last report). Can be
	 * used to determine expectedReturn in your Strategy.
	 */
	function expectedReturn(address _strategy) external view returns (uint256);

	/**
	 * This is the main contact point where the Strategy interacts with the
	 * Vault. It is critical that this call is handled as intended by the
	 * Strategy. Therefore, this function will be called by BaseStrategy to
	 * make sure the integration is correct.
	 */
	function reportStrategy(uint256 _gain, uint256 _loss, uint256 _debtPayment) external returns (uint256);

	/**
	 * This function should only be used in the scenario where the Strategy is
	 * being retired but no migration of the positions are possible, or in the
	 * extreme scenario that the Strategy needs to be put into "Emergency Exit"
	 * mode in order for it to exit as quickly as possible. The latter scenario
	 * could be for any reason that is considered "critical" that the Strategy
	 * exits its position as fast as possible, such as a sudden change in
	 * market conditions leading to losses, or an imminent failure in an
	 * external dependency.
	 */
	function revokeStrategy(address strategy) external;

	/**
	 * This will resurrect a strategy when a revoked strategy should be brought back
	 */
	function resurrectStrategy(address strategy) external;

	/**
	 * View the governance address of the Vault to assert privileged functions
	 * can only be called by governance. The Strategy serves the Vault, so it
	 * is subject to governance defined by the Vault.
	 */
	function governance() external view returns (address);

	/**
	 * View the management address of the Vault to assert privileged functions
	 * can only be called by management. The Strategy serves the Vault, so it
	 * is subject to management defined by the Vault.
	 */
	function management() external view returns (address);

	function vault() external view returns (address);

	/**
	 * View the guardian address of the Vault to assert privileged functions
	 * can only be called by guardian. The Strategy serves the Vault, so it
	 * is subject to guardian defined by the Vault.
	 */
	function guardian() external view returns (address);

	function depositIdle(address tokenAddress, uint256 _assets) external;

	function withdrawIdle(address tokenAddress, uint256 _assets) external;

	function setTotalIdle(address tokenAddress, uint256 _assets) external;

	function depositDepositToken(uint256 _depositAmount, uint256 _feeAmount) external;

	function withdrawDepositToken(uint256 _withdrawAmount) external;

	function freeFunds() external view returns (uint256);

	function strategyDebt(address strategy) external view returns (uint256);

	function getTokenStrategies(address tokenAddress) external view returns (address[] memory queue);

	function reportReserve(address tokenAddress) external;

	function reportReserveFromVault(address token) external;

	function reportAllReservesFromVault() external returns (uint256 reportedFreeFunds);

	function reportLoss(address strategy, uint256 loss) external;

	function decreaseStrategyDebt(address strategy, uint256 withdrawn) external;

	function updateDebtAfterSwap(address _tokenIn, uint256 _amountIn, address _tokenOut, uint256 _amountOut, bool updateRatio) external;

	function initialize(
		address _vault,
		address tokenAddress,
		address _governance,
		address _management,
		address _guardian,
		address _managementFeeWallet,
		address _performanceFeeWallet,
		address _owner
	) external;

	function checkManagementOrGovernance(address user) external;

	function checkGovernance(address user) external;

	function onlyVaultOrManagement() external view;

	function lastLockedProfitRatio() external view returns (uint256);

	function lastLockedProfitDegradation() external view returns (uint256);

	function lockedProfit() external view returns (uint256);

	function lockedProfitDegradationRates() external view returns (uint256, uint256);

	function unlockedFundsRatio() external view returns (uint256);

	function ethToWant(uint256 _amount, address _weth) external view returns (uint256);

	function tokenExists(address tokenAddress) external view returns (bool);

	function simulatedReportAllReserves(IVaultSimulatorAPI.VaultSnapshot memory snapshot) external view returns (IVaultSimulatorAPI.VaultSnapshot memory);

	function simulateUnlockedFundsRatio(IVaultSimulatorAPI.VaultSnapshot memory snapshot) external view returns (uint256);

	function simulatedFreeFunds(IVaultSimulatorAPI.VaultSnapshot memory snapshot) external view returns (uint256);

	function takeStrategiesSnapshot(address tokenAddress) external view returns (IVaultSimulatorAPI.VaultStrategySnapshot[] memory snapshots);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

interface IVaultSimulatorAPI {
	struct VaultStrategySnapshot {
		uint256 debtRatio;
		uint256 totalDebt;
	}

	struct VaultTokenSnapshot {
		address tokenAddress;
		uint256 balance;
		uint256 tokenIdle;
		uint256 tokenDebt;
		uint256 totalProfit;
		uint256 totalLoss;
		uint256 depositDebt;
		uint256 depositDebtRatio;
		uint256 lastReport;
		uint256 lastReportedValue;
		uint256 watermark;
		uint256 lastWatermark;
		uint256 watermarkDuration;
		VaultStrategySnapshot[] strategies;
	}

	struct VaultSnapshot {
		address vault;
		address manager;
		VaultTokenSnapshot[] tokens;
		uint256 reserveDebt;
		uint256 totalDepositDebt;
		uint256 totalFeeShares;
		uint256 totalFees;
		uint256 totalAssets;
		uint256 totalSupply;
		uint256 totalProfit;
		uint256 lockedProfit;
		uint256 lastLockedProfitRatio;
		uint256 lastLockedProfitDegradation;
		uint256 lockedProfitDegradationRate;
		uint256 extraLockedProfitDegradationRate;
		uint256 timestamp;
	}

	struct ReserveFees {
		uint256 managementFee;
		uint256 totalFees;
		uint256 profit;
	}

	struct StrategyFees {
		uint256 vaultManagementFee;
		uint256 vaultPerformanceFee;
		uint256 strategyManagementFee;
		uint256 strategistFee;
		uint256 totalFees;
	}

	function initialize(address vaultAddress, address managerAddress, uint8 depositTokenDecimals) external;

	function takeSnapshot() external view returns (IVaultSimulatorAPI.VaultSnapshot memory snapshot);

	function simulatedDeposit(uint256 assets, IVaultSimulatorAPI.VaultSnapshot memory snapshot) external view returns (uint256 shares);

	function simulatedMint(
		uint256 sharesNotIncludingFees,
		IVaultSimulatorAPI.VaultSnapshot memory snapshot
	) external view returns (uint256 assetsIncludingFees);

	function simulatedWithdraw(uint256 assets, IVaultSimulatorAPI.VaultSnapshot memory snapshot) external view returns (uint256 shares);

	function simulatedMaxWithdraw(address owner, IVaultSimulatorAPI.VaultSnapshot memory snapshot) external view returns (uint256 maxAssetsNotIncludingFees);

	function simulatedRedeem(
		uint256 sharesIncludingFees,
		IVaultSimulatorAPI.VaultSnapshot memory snapshot
	) external view returns (uint256 assetsNotIncludingFees);

	function simulatedRedeemProportional(
		uint256 sharesIncludingFees,
		IVaultSimulatorAPI.VaultSnapshot memory snapshot
	) external view returns (uint256[] memory toRedeem);

	function simulatedIssueSharesForFeeAmount(
		uint256 feeAmount,
		address feeToken,
		uint256 deltaTotalAssets,
		IVaultSimulatorAPI.VaultSnapshot memory snapshot
	) external view returns (IVaultSimulatorAPI.VaultSnapshot memory);

	function assetsPerShare() external view returns (uint256);

	function _convertToAssets(uint256 shares) external view returns (uint256 assets);

	function _convertToShares(uint256 assets) external view returns (uint256 shares);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

library Checks {
	error MaxFee();
	error ZeroAddress();
	error AlreadyInitialized();

	function isNotAlreadyInitialized(address _address) internal pure {
		if (_address != address(0)) revert AlreadyInitialized();
	}

	function requireNonZeroAddress(address _address) internal pure {
		if (_address == address(0)) revert ZeroAddress();
	}

	function requireMaxFee(uint256 _fee, uint256 _maxFee) internal pure {
		if (_fee > _maxFee) revert MaxFee();
	}
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "@openzeppelin/contracts/access/AccessControl.sol";

/*
 * @title a contract which implements EIP-1167 minimal proxy pattern and adds role based access control
 * @notice grants DEFAULT_ADMIN_ROLE when deploying the implementation contract and after cloning a new instance
 */
contract Clonable is AccessControl {
	bool private initializedRoleBasedAccessControl;

	event Cloned(address newInstance);

	error AlreadyInitializedRBAC();

	/*
	 * @notice marked the constructor function as payable, because it costs less gas to execute,
	 * since the compiler does not have to add extra checks to ensure that a payment wasn't provided.
	 * A constructor can safely be marked as payable, since only the deployer would be able to pass funds,
	 * and the project itself would not pass any funds.
	 */
	constructor() payable {
		setDefaultAdmin(msg.sender);
	}

	/*
	 * @notice clones a new instance of this contract and grant default admin roler to given defaultAdmin
	 */
	function clone(address defaultAdmin) public returns (address newInstance) {
		// Copied from https://github.com/optionality/clone-factory/blob/master/contracts/CloneFactory.sol
		bytes20 addressBytes = bytes20(address(this));
		assembly {
			// EIP-1167 bytecode
			let clone_code := mload(0x40)
			mstore(clone_code, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
			mstore(add(clone_code, 0x14), addressBytes)
			mstore(add(clone_code, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
			newInstance := create(0, clone_code, 0x37)
		}
		emit Cloned(newInstance);
		Clonable(newInstance).setDefaultAdmin(defaultAdmin);
	}

	/*
	 * @notice clones a new instance of this contract and grant default admin roler to caller
	 */
	function getClone() external returns (address) {
		return clone(msg.sender);
	}

	/*
	 * @dev initializes DEFAULT_ADMIN_ROLE (can be initialized only once)
	 * @notice this function does emit a RoleGranted event in AccessControl._grantRole
	 */
	function setDefaultAdmin(address initialAdmin) public {
		if (initializedRoleBasedAccessControl) revert AlreadyInitializedRBAC();
		_grantRole(DEFAULT_ADMIN_ROLE, initialAdmin);
		initializedRoleBasedAccessControl = true;
	}
}

// SPDX-License-Identifier: MIT
// Forked OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
pragma solidity 0.8.26;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {IERC20Errors} from "../interfaces/draft-IERC6093.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
	mapping(address => uint256) private _balances; // account => balance

	mapping(address => mapping(address => uint256)) private _allowances; // account => spender => allowance

	uint256 private _totalSupply;

	string private _name;
	string private _symbol;

	/**
	 * @dev Initializes the values for {name} and {symbol}.
	 */
	function initializeERC20(string memory name_, string memory symbol_) internal {
		require(bytes(_symbol).length == 0 && bytes(symbol_).length != 0, "ERR_(RE)INIT");
		_name = name_;
		_symbol = symbol_;
	}

	/**
	 * @dev Returns the name of the token.
	 */
	function name() public view virtual returns (string memory) {
		return _name;
	}

	/**
	 * @dev Returns the symbol of the token, usually a shorter version of the
	 * name.
	 */
	function symbol() public view virtual returns (string memory) {
		return _symbol;
	}

	/**
	 * @dev Returns the number of decimals used to get its user representation.
	 * For example, if `decimals` equals `2`, a balance of `505` tokens should
	 * be displayed to a user as `5.05` (`505 / 10 ** 2`).
	 *
	 * Tokens usually opt for a value of 18, imitating the relationship between
	 * Ether and Wei. This is the default value returned by this function, unless
	 * it's overridden.
	 *
	 * NOTE: This information is only used for _display_ purposes: it in
	 * no way affects any of the arithmetic of the contract, including
	 * {IERC20-balanceOf} and {IERC20-transfer}.
	 */
	function decimals() public view virtual returns (uint8) {
		return 18;
	}

	/**
	 * @dev See {IERC20-totalSupply}.
	 */
	function totalSupply() public view virtual returns (uint256) {
		return _totalSupply;
	}

	/**
	 * @dev See {IERC20-balanceOf}.
	 */
	function balanceOf(address account) public view virtual returns (uint256) {
		return _balances[account];
	}

	/**
	 * @dev See {IERC20-transfer}.
	 *
	 * Requirements:
	 *
	 * - `to` cannot be the zero address.
	 * - the caller must have a balance of at least `value`.
	 */
	function transfer(address to, uint256 value) public virtual returns (bool) {
		address owner = _msgSender();
		_transfer(owner, to, value);
		return true;
	}

	/**
	 * @dev See {IERC20-allowance}.
	 */
	function allowance(address owner, address spender) public view virtual returns (uint256) {
		return _allowances[owner][spender];
	}

	/**
	 * @dev See {IERC20-approve}.
	 *
	 * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
	 * `transferFrom`. This is semantically equivalent to an infinite approval.
	 *
	 * Requirements:
	 *
	 * - `spender` cannot be the zero address.
	 */
	function approve(address spender, uint256 value) public virtual returns (bool) {
		address owner = _msgSender();
		_approve(owner, spender, value);
		return true;
	}

	/**
	 * @dev See {IERC20-transferFrom}.
	 *
	 * Emits an {Approval} event indicating the updated allowance. This is not
	 * required by the EIP. See the note at the beginning of {ERC20}.
	 *
	 * NOTE: Does not update the allowance if the current allowance
	 * is the maximum `uint256`.
	 *
	 * Requirements:
	 *
	 * - `from` and `to` cannot be the zero address.
	 * - `from` must have a balance of at least `value`.
	 * - the caller must have allowance for ``from``'s tokens of at least
	 * `value`.
	 */
	function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
		address spender = _msgSender();
		_spendAllowance(from, spender, value);
		_transfer(from, to, value);
		return true;
	}

	/**
	 * @dev Moves a `value` amount of tokens from `from` to `to`.
	 *
	 * This internal function is equivalent to {transfer}, and can be used to
	 * e.g. implement automatic token fees, slashing mechanisms, etc.
	 *
	 * Emits a {Transfer} event.
	 *
	 * NOTE: This function is not virtual, {_update} should be overridden instead.
	 */
	function _transfer(address from, address to, uint256 value) internal {
		if (from == address(0)) {
			revert ERC20InvalidSender(address(0));
		}
		if (to == address(0)) {
			revert ERC20InvalidReceiver(address(0));
		}
		_update(from, to, value);
	}

	/**
	 * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
	 * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
	 * this function.
	 *
	 * Emits a {Transfer} event.
	 */
	function _update(address from, address to, uint256 value) internal virtual {
		if (from == address(0)) {
			// Overflow check required: The rest of the code assumes that totalSupply never overflows
			_totalSupply += value;
		} else {
			uint256 fromBalance = _balances[from];
			if (fromBalance < value) {
				revert ERC20InsufficientBalance(from, fromBalance, value);
			}
			unchecked {
				// Overflow not possible: value <= fromBalance <= totalSupply.
				_balances[from] = fromBalance - value;
			}
		}

		if (to == address(0)) {
			unchecked {
				// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
				_totalSupply -= value;
			}
		} else {
			unchecked {
				// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
				_balances[to] += value;
			}
		}

		emit Transfer(from, to, value);
	}

	/**
	 * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
	 * Relies on the `_update` mechanism
	 *
	 * Emits a {Transfer} event with `from` set to the zero address.
	 *
	 * NOTE: This function is not virtual, {_update} should be overridden instead.
	 */
	function _mint(address account, uint256 value) internal {
		if (account == address(0)) {
			revert ERC20InvalidReceiver(address(0));
		}
		_update(address(0), account, value);
	}

	/**
	 * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
	 * Relies on the `_update` mechanism.
	 *
	 * Emits a {Transfer} event with `to` set to the zero address.
	 *
	 * NOTE: This function is not virtual, {_update} should be overridden instead
	 */
	function _burn(address account, uint256 value) internal {
		if (account == address(0)) {
			revert ERC20InvalidSender(address(0));
		}
		_update(account, address(0), value);
	}

	/**
	 * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
	 *
	 * This internal function is equivalent to `approve`, and can be used to
	 * e.g. set automatic allowances for certain subsystems, etc.
	 *
	 * Emits an {Approval} event.
	 *
	 * Requirements:
	 *
	 * - `owner` cannot be the zero address.
	 * - `spender` cannot be the zero address.
	 *
	 * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
	 */
	function _approve(address owner, address spender, uint256 value) internal {
		_approve(owner, spender, value, true);
	}

	/**
	 * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
	 *
	 * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
	 * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
	 * `Approval` event during `transferFrom` operations.
	 *
	 * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
	 * true using the following override:
	 * ```
	 * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
	 *     super._approve(owner, spender, value, true);
	 * }
	 * ```
	 *
	 * Requirements are the same as {_approve}.
	 */
	function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
		if (owner == address(0)) {
			revert ERC20InvalidApprover(address(0));
		}
		if (spender == address(0)) {
			revert ERC20InvalidSpender(address(0));
		}
		_allowances[owner][spender] = value;
		if (emitEvent) {
			emit Approval(owner, spender, value);
		}
	}

	/**
	 * @dev Updates `owner` s allowance for `spender` based on spent `value`.
	 *
	 * Does not update the allowance value in case of infinite allowance.
	 * Revert if not enough allowance is available.
	 *
	 * Does not emit an {Approval} event.
	 */
	function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
		uint256 currentAllowance = allowance(owner, spender);
		if (currentAllowance != type(uint256).max) {
			if (currentAllowance < value) {
				revert ERC20InsufficientAllowance(spender, currentAllowance, value);
			}
			unchecked {
				_approve(owner, spender, currentAllowance - value, false);
			}
		}
	}
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

// we use internal functions instead modifiers to save gas
abstract contract ReentrancyGuard {
	uint256 private constant _NOT_ENTERED = 1;
	uint256 private constant _ENTERED = 2;

	uint256 private _status;

	error Reentrancy();

	function before_nonReentrant() internal {
		if (!(_status != _ENTERED)) {
			revert Reentrancy();
		}
		_status = _ENTERED;
	}

	function after_nonReentrant() internal {
		_status = _NOT_ENTERED;
	}
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "./DynaVaultErrors.sol";
import "./interfaces/IVaultManagerAPI.sol";
import "./interfaces/IReferenceAssetOracle.sol";
import "./utils/Checks.sol";

/**
 * @title VaultConfigLib is part of DynaVault and explicitly handles the storage of contract addresses.
 * @notice It includes several necessary setter functions which allow governance to adjust max loss/deviation variables.
 */
library VaultConfigLib {
	using Checks for address;
	using Math for uint256;
	using SafeERC20 for IERC20;

	/// @dev The storage slot follows EIP1967 to avoid storage collision
	bytes32 private constant CONFIG_STORAGE_POSITION = bytes32(uint256(keccak256("DynaVault.configStorage")) - 1);
	uint256 private constant DEFAULT_MAX_LOSS = 100; // default 1.00% max loss protection
	uint256 private constant MAX_LOSS_LIMIT = 2000; // 20.00% max loss protection

	struct ConfigStorage {
		address token;
		address manager;
		address routerRegistry;
		address referenceAssetOracle;
		address referenceAsset;
		uint256 depositDecimals;
		uint256 maxLoss; // max loss BPS for loss protection
	}

	event UpdatedRouterRegistry(address newDynaRouterRegistry);
	event UpdatedReferenceAssetOracle(address referenceAssetOracle);
	event UpdatedMaxLoss(uint256 newMaxLoss);

	/**
	 * @notice Returns the library storage
	 * @return cs Storage pointer for accessing the state variables
	 */
	function configStorage() private pure returns (ConfigStorage storage cs) {
		bytes32 position = CONFIG_STORAGE_POSITION;
		assembly {
			cs.slot := position
		}
	}

	/**
	 * @notice Returns the address of the vault manager
	 * @return manager The address of the vault manager
	 */
	function manager() external view returns (address) {
		return configStorage().manager;
	}

	/**
	 * @notice Returns the address of the deposit token
	 * @return asset The address of the deposit token
	 */
	function asset() external view returns (address) {
		return configStorage().token;
	}

	/**
	 * @notice Returns the address of the DynaRouter registry
	 * @return routerRegistry The address of the DynaRouter registry
	 */
	function routerRegistry() external view returns (address) {
		return configStorage().routerRegistry;
	}

	/**
	 * @notice Returns the address of the reference oracle
	 * @return referenceAssetOracle The address of the reference oracle
	 */
	function referenceAssetOracle() external view returns (address) {
		return configStorage().referenceAssetOracle;
	}

	/**
	 * @notice Returns the decimals of the deposit token
	 * @return decimals The decimals of the deposit token
	 */
	function depositDecimals() external view returns (uint256) {
		return configStorage().depositDecimals;
	}

	/**
	 * @notice Initializes the config library
	 * @param managerAddress The address of the manager
	 * @param routerRegistryAddress The address of the DynaRouter registry
	 * @param referenceAssetOracleAddress The address of the reference asset oracle
	 */
	function initialize(address managerAddress, address routerRegistryAddress, address referenceAssetOracleAddress) external {
		managerAddress.requireNonZeroAddress();
		routerRegistryAddress.requireNonZeroAddress();
		referenceAssetOracleAddress.requireNonZeroAddress();
		ConfigStorage storage _storage = configStorage();
		_storage.manager.isNotAlreadyInitialized();
		_storage.manager = managerAddress;
		_storage.token = IVaultManagerAPI(managerAddress).token();
		_storage.depositDecimals = IERC20Metadata(_storage.token).decimals();
		IERC20(_storage.token).safeIncreaseAllowance(managerAddress, type(uint256).max); // set max allowance assuming it was 0
		_storage.routerRegistry = routerRegistryAddress;
		_storage.referenceAssetOracle = referenceAssetOracleAddress;
		address _referenceAsset = IReferenceAssetOracle(referenceAssetOracleAddress).referenceAsset();
		_storage.referenceAsset = _referenceAsset;
		_storage.maxLoss = DEFAULT_MAX_LOSS;
	}

	/**
	 * @notice checks if caller is manager
	 */
	function onlyManager() external view {
		if (msg.sender != configStorage().manager) {
			revert DynaVaultErrors.NotAuthorized();
		}
	}

	/**
	 * @notice checks if caller has governance role
	 */
	function onlyGovernance() external {
		IVaultManagerAPI(configStorage().manager).checkGovernance(msg.sender);
	}

	/**
	 * @notice Sets the DynaRouter registry address
	 * @param routerRegistryAddress The address of the new DynaRouter registry
	 */
	function setRouterRegistry(address routerRegistryAddress) external {
		IVaultManagerAPI(configStorage().manager).checkGovernance(msg.sender);
		routerRegistryAddress.requireNonZeroAddress();
		configStorage().routerRegistry = routerRegistryAddress;
		emit UpdatedRouterRegistry(routerRegistryAddress);
	}

	/**
	 * @notice Sets the reference asset oracle address
	 * @param upgradedReferenceAssetOracleAddress The address of the new reference asset oracle
	 */
	function setReferenceAssetOracle(address upgradedReferenceAssetOracleAddress) external {
		IVaultManagerAPI(configStorage().manager).checkGovernance(msg.sender);
		upgradedReferenceAssetOracleAddress.requireNonZeroAddress();
		ConfigStorage storage _storage = configStorage();
		_storage.referenceAssetOracle = upgradedReferenceAssetOracleAddress;
		address _referenceAsset = IReferenceAssetOracle(upgradedReferenceAssetOracleAddress).referenceAsset();
		if (_storage.referenceAsset != _referenceAsset) {
			revert DynaVaultErrors.NotSameReferenceAsset();
		}
		_storage.referenceAsset = _referenceAsset;
		emit UpdatedReferenceAssetOracle(upgradedReferenceAssetOracleAddress);
	}

	/**
	 * @notice Returns the reference asset address
	 * @return referenceAsset The address of the reference assets
	 */
	function referenceAsset() external view returns (address) {
		return configStorage().referenceAsset;
	}

	/**
	 * @notice Returns the current max loss limit
	 * @return maxLoss The current max loss limit
	 */
	function maxLoss() external view returns (uint256) {
		return configStorage().maxLoss;
	}

	/**
	 * @notice Sets max loss
	 * @param newMaxLoss The new max loss limit
	 */
	function setMaxLoss(uint256 newMaxLoss) external {
		IVaultManagerAPI(configStorage().manager).checkGovernance(msg.sender);
		if (newMaxLoss > MAX_LOSS_LIMIT) {
			revert DynaVaultErrors.MaxLossLimit();
		}
		configStorage().maxLoss = newMaxLoss;
		emit UpdatedMaxLoss(newMaxLoss);
	}

	/**
	 * @notice Approve manager for swapping token
	 * @param tokenAddress The address of the token to approve
	 */
	function approveAddedToken(address tokenAddress) external {
		address _manager = configStorage().manager;
		if (msg.sender != _manager) {
			revert DynaVaultErrors.NotAuthorized();
		}
		if (IERC20(tokenAddress).allowance(address(this), _manager) == 0) IERC20(tokenAddress).safeIncreaseAllowance(_manager, type(uint256).max);
	}

	/**
	 * @notice Reset manager allowance for swapping token to 0
	 * @param tokenAddress The address of the token to approve
	 */
	function resetRemovedTokenAllowance(address tokenAddress) external {
		address _manager = configStorage().manager;
		if (msg.sender != _manager) {
			revert DynaVaultErrors.NotAuthorized();
		}
		IERC20(tokenAddress).safeApprove(_manager, 0);
	}
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "./DynaVaultErrors.sol";
import "./interfaces/IMetaDynaRouterAPI.sol";
import "./interfaces/IDynaRouter.sol";
import "./interfaces/IDynaRouterRegistryAPI.sol";
import "./interfaces/IVaultManagerAPI.sol";
import "./utils/Checks.sol";
import "./VaultConfigLib.sol";

/**
 * @title DynaVault library
 * @notice VaultRouterLib is part of DynaVault
 * and is responsible for the estimating and executing token conversions by using a ReferenceAssetOracle and DynaRouterRegistry.
 * It contains logic to check oracle deviation when doing token conversion estimations to avoid flash-loan/sandwich style attacks.
 */
library VaultRouterLib {
	using Checks for address;
	using Math for uint256;
	using SafeERC20 for IERC20;

	event VaultSwapped(address indexed caller, address indexed tokenIn, address indexed tokenOut, uint256 amountIn, uint256 amountOut);

	/**
	 * @notice Used to fetch swap data used when calling swap
	 * @param tokenIn The address of the input token
	 * @param amountIn The amount of input token to swap
	 * @param tokenOut The address fo the output token
	 * @return amountOut The expected amount out from the swap
	 * @return selectedRouter The address of the router to use
	 * @return swapData The swap data from the preview
	 */
	function previewSwap(
		address tokenIn,
		uint256 amountIn,
		address tokenOut
	) public view returns (uint256 amountOut, address selectedRouter, bytes32[] memory swapData) {
		address registry = VaultConfigLib.routerRegistry();
		IDynaRouterRegistryAPI.Route memory route = IDynaRouterRegistryAPI(registry).getDefaultPairRoute(tokenIn, tokenOut);
		(amountOut, selectedRouter, swapData) = IDynaRouterAPI(route.router).previewSwapRoute(tokenIn, amountIn, tokenOut, route.route);
	}

	/**
	 * @notice This a swap function to be called by vault management to swap and change target weights
	 * @param tokenIn The address of the input token
	 * @param amountIn The amount of input token to swap
	 * @param tokenOut The address of the output token
	 * @param minAmountOut The min expected amount out from swap
	 * @param swapData The swap data from the preview
	 */
	function _swap(address tokenIn, uint256 amountIn, address tokenOut, uint256 minAmountOut, address selectedRouter, bytes32[] memory swapData) private {
		address manager = VaultConfigLib.manager();
		IVaultManagerAPI(manager).checkManagementOrGovernance(msg.sender);
		address routerRegistry = VaultConfigLib.routerRegistry();
		if (!IDynaRouterRegistryAPI(routerRegistry).getEnabledRouter(selectedRouter)) {
			revert DynaVaultErrors.DynaRouterInactive(selectedRouter);
		}
		if (!IVaultManagerAPI(manager).tokenExists(tokenIn)) {
			revert DynaVaultErrors.DynaVaultSwapUnsupportedToken(tokenIn);
		}
		if (!IVaultManagerAPI(manager).tokenExists(tokenOut)) {
			revert DynaVaultErrors.DynaVaultSwapUnsupportedToken(tokenOut);
		}
		uint256 tokenInBalance = IERC20(tokenIn).balanceOf(address(this));
		if (tokenInBalance < amountIn) {
			revert DynaVaultErrors.DynaVaultSwapLackingAmountIn(tokenIn, amountIn, tokenInBalance);
		}
		uint256 allowed = IERC20(tokenIn).allowance(address(this), selectedRouter);
		if (allowed < amountIn) IERC20(tokenIn).safeIncreaseAllowance(selectedRouter, amountIn);
		uint256 tokenOutInitialBalance = IERC20(tokenOut).balanceOf(address(this));
		IDynaRouterAPI(selectedRouter).swap(tokenIn, amountIn, tokenOut, minAmountOut, address(this), swapData);
		uint256 _amountOut = IERC20(tokenOut).balanceOf(address(this)) - tokenOutInitialBalance;
		if (_amountOut < minAmountOut) {
			revert DynaVaultErrors.DynaVaultSwapSlippageProtection(tokenIn, amountIn, tokenOut, _amountOut, minAmountOut);
		}
		IVaultManagerAPI(manager).updateDebtAfterSwap(tokenIn, amountIn, tokenOut, _amountOut, true);
		emit VaultSwapped(msg.sender, tokenIn, tokenOut, amountIn, _amountOut);
	}

	/**
	 * @notice wrapper for the private swap function
	 * @notice this a swap function to be called by vault management to swap and change target weights
	 * @param tokenIn The address of the input token
	 * @param amountIn The amount of input token to swap
	 * @param tokenOut The address of the output token
	 * @param minAmountOut The min amount out expected from swap
	 * @param selectedRouter The address of router to use
	 * @param swapData The swap data from the preview
	 */
	function swap(address tokenIn, uint256 amountIn, address tokenOut, uint256 minAmountOut, address selectedRouter, bytes32[] memory swapData) external {
		_swap(tokenIn, amountIn, tokenOut, minAmountOut, selectedRouter, swapData);
	}

	/**
	 * @notice Swap with reporting of all the tokens to ensure consistency
	 * @param tokenIn The address of the input token
	 * @param amountIn The amount of input token to swap
	 * @param tokenOut The address of the output token
	 * @param minAmountOut The min amount out expected from swap
	 * @param selectedRouter The address of router to use
	 * @param swapData The swap data from the preview
	 */
	function swapAndReport(
		address tokenIn,
		uint256 amountIn,
		address tokenOut,
		uint256 minAmountOut,
		address selectedRouter,
		bytes32[] memory swapData
	) external {
		IVaultManagerAPI(VaultConfigLib.manager()).reportAllReservesFromVault();
		_swap(tokenIn, amountIn, tokenOut, minAmountOut, selectedRouter, swapData);
	}

	/**
	 * @notice this a swap function to be called by the vault manager contract to rebalance, which does not change target depositDebtRatio weights
	 * @param tokenIn The address of the input token
	 * @param amountIn The amount of input token to swap
	 * @param tokenOut The address of the output token
	 * @param minAmountOut The min amount out expected from swap
	 * @return amountOut The amount of output token from swap
	 */
	function doSwap(address tokenIn, uint256 amountIn, address tokenOut, uint256 minAmountOut) external returns (uint256 amountOut) {
		VaultConfigLib.onlyManager();
		(, address router, bytes32[] memory swapData) = previewSwap(tokenIn, amountIn, tokenOut);
		uint256 allowed = IDynaRouterAPI(router).spenderAllowance(tokenIn);
		if (allowed < amountIn) IERC20(tokenIn).safeIncreaseAllowance(router, amountIn);
		uint256 tokenOutInitialBalance = IERC20(tokenOut).balanceOf(address(this));
		IDynaRouterAPI(router).swap(tokenIn, amountIn, tokenOut, minAmountOut, address(this), swapData);
		amountOut = IERC20(tokenOut).balanceOf(address(this)) - tokenOutInitialBalance;
		if (amountOut < minAmountOut) {
			revert DynaVaultErrors.DynaVaultSwapSlippageProtection(tokenIn, amountIn, tokenOut, amountOut, minAmountOut);
		}
		IVaultManagerAPI(VaultConfigLib.manager()).updateDebtAfterSwap(tokenIn, amountIn, tokenOut, amountOut, false);
	}
}

File 36 of 36 : FixedPointMathLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The operation failed, as the output exceeds the maximum value of uint256.
    error ExpOverflow();

    /// @dev The operation failed, as the output exceeds the maximum value of uint256.
    error FactorialOverflow();

    /// @dev The operation failed, due to an overflow.
    error RPowOverflow();

    /// @dev The mantissa is too big to fit.
    error MantissaOverflow();

    /// @dev The operation failed, due to an multiplication overflow.
    error MulWadFailed();

    /// @dev The operation failed, due to an multiplication overflow.
    error SMulWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error DivWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error SDivWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error MulDivFailed();

    /// @dev The division failed, as the denominator is zero.
    error DivFailed();

    /// @dev The full precision multiply-divide operation failed, either due
    /// to the result being larger than 256 bits, or a division by a zero.
    error FullMulDivFailed();

    /// @dev The output is undefined, as the input is less-than-or-equal to zero.
    error LnWadUndefined();

    /// @dev The input outside the acceptable domain.
    error OutOfDomain();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The scalar of ETH and most ERC20s.
    uint256 internal constant WAD = 1e18;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*              SIMPLIFIED FIXED POINT OPERATIONS             */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `(x * y) / WAD` rounded down.
    function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
            if gt(x, div(not(0), y)) {
                if y {
                    mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            z := div(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down.
    function sMulWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require((x == 0 || z / x == y) && !(x == -1 && y == type(int256).min))`.
            if iszero(gt(or(iszero(x), eq(sdiv(z, x), y)), lt(not(x), eq(y, shl(255, 1))))) {
                mstore(0x00, 0xedcd4dd4) // `SMulWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := sdiv(z, WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
    function rawMulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
    function rawSMulWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded up.
    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
            if iszero(eq(div(z, y), x)) {
                if y {
                    mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            z := add(iszero(iszero(mod(z, WAD))), div(z, WAD))
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded up, but without overflow checks.
    function rawMulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down.
    function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
            if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
                mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down.
    function sDivWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, WAD)
            // Equivalent to `require(y != 0 && ((x * WAD) / WAD == x))`.
            if iszero(mul(y, eq(sdiv(z, WAD), x))) {
                mstore(0x00, 0x5c43740d) // `SDivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := sdiv(z, y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
    function rawDivWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
    function rawSDivWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded up.
    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
            if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
                mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded up, but without overflow and divide by zero checks.
    function rawDivWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
        }
    }

    /// @dev Equivalent to `x` to the power of `y`.
    /// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`.
    /// Note: This function is an approximation.
    function powWad(int256 x, int256 y) internal pure returns (int256) {
        // Using `ln(x)` means `x` must be greater than 0.
        return expWad((lnWad(x) * y) / int256(WAD));
    }

    /// @dev Returns `exp(x)`, denominated in `WAD`.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
    /// Note: This function is an approximation. Monotonically increasing.
    function expWad(int256 x) internal pure returns (int256 r) {
        unchecked {
            // When the result is less than 0.5 we return zero.
            // This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`.
            if (x <= -41446531673892822313) return r;

            /// @solidity memory-safe-assembly
            assembly {
                // When the result is greater than `(2**255 - 1) / 1e18` we can not represent it as
                // an int. This happens when `x >= floor(log((2**255 - 1) / 1e18) * 1e18) ≈ 135`.
                if iszero(slt(x, 135305999368893231589)) {
                    mstore(0x00, 0xa37bfec9) // `ExpOverflow()`.
                    revert(0x1c, 0x04)
                }
            }

            // `x` is now in the range `(-42, 136) * 1e18`. Convert to `(-42, 136) * 2**96`
            // for more intermediate precision and a binary basis. This base conversion
            // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
            x = (x << 78) / 5 ** 18;

            // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
            // of two such that exp(x) = exp(x') * 2**k, where k is an integer.
            // Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
            int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96;
            x = x - k * 54916777467707473351141471128;

            // `k` is in the range `[-61, 195]`.

            // Evaluate using a (6, 7)-term rational approximation.
            // `p` is made monic, we'll multiply by a scale factor later.
            int256 y = x + 1346386616545796478920950773328;
            y = ((y * x) >> 96) + 57155421227552351082224309758442;
            int256 p = y + x - 94201549194550492254356042504812;
            p = ((p * y) >> 96) + 28719021644029726153956944680412240;
            p = p * x + (4385272521454847904659076985693276 << 96);

            // We leave `p` in `2**192` basis so we don't need to scale it back up for the division.
            int256 q = x - 2855989394907223263936484059900;
            q = ((q * x) >> 96) + 50020603652535783019961831881945;
            q = ((q * x) >> 96) - 533845033583426703283633433725380;
            q = ((q * x) >> 96) + 3604857256930695427073651918091429;
            q = ((q * x) >> 96) - 14423608567350463180887372962807573;
            q = ((q * x) >> 96) + 26449188498355588339934803723976023;

            /// @solidity memory-safe-assembly
            assembly {
                // Div in assembly because solidity adds a zero check despite the unchecked.
                // The q polynomial won't have zeros in the domain as all its roots are complex.
                // No scaling is necessary because p is already `2**96` too large.
                r := sdiv(p, q)
            }

            // r should be in the range `(0.09, 0.25) * 2**96`.

            // We now need to multiply r by:
            // - The scale factor `s ≈ 6.031367120`.
            // - The `2**k` factor from the range reduction.
            // - The `1e18 / 2**96` factor for base conversion.
            // We do this all at once, with an intermediate result in `2**213`
            // basis, so the final right shift is always by a positive amount.
            r = int256(
                (uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k)
            );
        }
    }

    /// @dev Returns `ln(x)`, denominated in `WAD`.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
    /// Note: This function is an approximation. Monotonically increasing.
    function lnWad(int256 x) internal pure returns (int256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // We want to convert `x` from `10**18` fixed point to `2**96` fixed point.
            // We do this by multiplying by `2**96 / 10**18`. But since
            // `ln(x * C) = ln(x) + ln(C)`, we can simply do nothing here
            // and add `ln(2**96 / 10**18)` at the end.

            // Compute `k = log2(x) - 96`, `r = 159 - k = 255 - log2(x) = 255 ^ log2(x)`.
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // We place the check here for more optimal stack operations.
            if iszero(sgt(x, 0)) {
                mstore(0x00, 0x1615e638) // `LnWadUndefined()`.
                revert(0x1c, 0x04)
            }
            // forgefmt: disable-next-item
            r := xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff))

            // Reduce range of x to (1, 2) * 2**96
            // ln(2^k * x) = k * ln(2) + ln(x)
            x := shr(159, shl(r, x))

            // Evaluate using a (8, 8)-term rational approximation.
            // `p` is made monic, we will multiply by a scale factor later.
            // forgefmt: disable-next-item
            let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir.
                sar(96, mul(add(43456485725739037958740375743393,
                sar(96, mul(add(24828157081833163892658089445524,
                sar(96, mul(add(3273285459638523848632254066296,
                    x), x))), x))), x)), 11111509109440967052023855526967)
            p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857)
            p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526)
            p := sub(mul(p, x), shl(96, 795164235651350426258249787498))
            // We leave `p` in `2**192` basis so we don't need to scale it back up for the division.

            // `q` is monic by convention.
            let q := add(5573035233440673466300451813936, x)
            q := add(71694874799317883764090561454958, sar(96, mul(x, q)))
            q := add(283447036172924575727196451306956, sar(96, mul(x, q)))
            q := add(401686690394027663651624208769553, sar(96, mul(x, q)))
            q := add(204048457590392012362485061816622, sar(96, mul(x, q)))
            q := add(31853899698501571402653359427138, sar(96, mul(x, q)))
            q := add(909429971244387300277376558375, sar(96, mul(x, q)))

            // `p / q` is in the range `(0, 0.125) * 2**96`.

            // Finalization, we need to:
            // - Multiply by the scale factor `s = 5.549…`.
            // - Add `ln(2**96 / 10**18)`.
            // - Add `k * ln(2)`.
            // - Multiply by `10**18 / 2**96 = 5**18 >> 78`.

            // The q polynomial is known not to have zeros in the domain.
            // No scaling required because p is already `2**96` too large.
            p := sdiv(p, q)
            // Multiply by the scaling factor: `s * 5**18 * 2**96`, base is now `5**18 * 2**192`.
            p := mul(1677202110996718588342820967067443963516166, p)
            // Add `ln(2) * k * 5**18 * 2**192`.
            // forgefmt: disable-next-item
            p := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, r)), p)
            // Add `ln(2**96 / 10**18) * 5**18 * 2**192`.
            p := add(600920179829731861736702779321621459595472258049074101567377883020018308, p)
            // Base conversion: mul `2**18 / 2**192`.
            r := sar(174, p)
        }
    }

    /// @dev Returns `W_0(x)`, denominated in `WAD`.
    /// See: https://en.wikipedia.org/wiki/Lambert_W_function
    /// a.k.a. Product log function. This is an approximation of the principal branch.
    /// Note: This function is an approximation. Monotonically increasing.
    function lambertW0Wad(int256 x) internal pure returns (int256 w) {
        // forgefmt: disable-next-item
        unchecked {
            if ((w = x) <= -367879441171442322) revert OutOfDomain(); // `x` less than `-1/e`.
            (int256 wad, int256 p) = (int256(WAD), x);
            uint256 c; // Whether we need to avoid catastrophic cancellation.
            uint256 i = 4; // Number of iterations.
            if (w <= 0x1ffffffffffff) {
                if (-0x4000000000000 <= w) {
                    i = 1; // Inputs near zero only take one step to converge.
                } else if (w <= -0x3ffffffffffffff) {
                    i = 32; // Inputs near `-1/e` take very long to converge.
                }
            } else if (uint256(w >> 63) == uint256(0)) {
                /// @solidity memory-safe-assembly
                assembly {
                    // Inline log2 for more performance, since the range is small.
                    let v := shr(49, w)
                    let l := shl(3, lt(0xff, v))
                    l := add(or(l, byte(and(0x1f, shr(shr(l, v), 0x8421084210842108cc6318c6db6d54be)),
                        0x0706060506020504060203020504030106050205030304010505030400000000)), 49)
                    w := sdiv(shl(l, 7), byte(sub(l, 31), 0x0303030303030303040506080c13))
                    c := gt(l, 60)
                    i := add(2, add(gt(l, 53), c))
                }
            } else {
                int256 ll = lnWad(w = lnWad(w));
                /// @solidity memory-safe-assembly
                assembly {
                    // `w = ln(x) - ln(ln(x)) + b * ln(ln(x)) / ln(x)`.
                    w := add(sdiv(mul(ll, 1023715080943847266), w), sub(w, ll))
                    i := add(3, iszero(shr(68, x)))
                    c := iszero(shr(143, x))
                }
                if (c == uint256(0)) {
                    do { // If `x` is big, use Newton's so that intermediate values won't overflow.
                        int256 e = expWad(w);
                        /// @solidity memory-safe-assembly
                        assembly {
                            let t := mul(w, div(e, wad))
                            w := sub(w, sdiv(sub(t, x), div(add(e, t), wad)))
                        }
                        if (p <= w) break;
                        p = w;
                    } while (--i != uint256(0));
                    /// @solidity memory-safe-assembly
                    assembly {
                        w := sub(w, sgt(w, 2))
                    }
                    return w;
                }
            }
            do { // Otherwise, use Halley's for faster convergence.
                int256 e = expWad(w);
                /// @solidity memory-safe-assembly
                assembly {
                    let t := add(w, wad)
                    let s := sub(mul(w, e), mul(x, wad))
                    w := sub(w, sdiv(mul(s, wad), sub(mul(e, t), sdiv(mul(add(t, wad), s), add(t, t)))))
                }
                if (p <= w) break;
                p = w;
            } while (--i != c);
            /// @solidity memory-safe-assembly
            assembly {
                w := sub(w, sgt(w, 2))
            }
            // For certain ranges of `x`, we'll use the quadratic-rate recursive formula of
            // R. Iacono and J.P. Boyd for the last iteration, to avoid catastrophic cancellation.
            if (c == uint256(0)) return w;
            int256 t = w | 1;
            /// @solidity memory-safe-assembly
            assembly {
                x := sdiv(mul(x, wad), t)
            }
            x = (t * (wad + lnWad(x)));
            /// @solidity memory-safe-assembly
            assembly {
                w := sdiv(x, add(wad, t))
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  GENERAL NUMBER UTILITIES                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `a * b == x * y`, with full precision.
    function fullMulEq(uint256 a, uint256 b, uint256 x, uint256 y)
        internal
        pure
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := and(eq(mul(a, b), mul(x, y)), eq(mulmod(x, y, not(0)), mulmod(a, b, not(0))))
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision.
    /// Throws if result overflows a uint256 or when `d` is zero.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv
    function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // 512-bit multiply `[p1 p0] = x * y`.
            // Compute the product mod `2**256` and mod `2**256 - 1`
            // then use the Chinese Remainder Theorem to reconstruct
            // the 512 bit result. The result is stored in two 256
            // variables such that `product = p1 * 2**256 + p0`.

            // Temporarily use `z` as `p0` to save gas.
            z := mul(x, y) // Lower 256 bits of `x * y`.
            for {} 1 {} {
                // If overflows.
                if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
                    let mm := mulmod(x, y, not(0))
                    let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.

                    /*------------------- 512 by 256 division --------------------*/

                    // Make division exact by subtracting the remainder from `[p1 p0]`.
                    let r := mulmod(x, y, d) // Compute remainder using mulmod.
                    let t := and(d, sub(0, d)) // The least significant bit of `d`. `t >= 1`.
                    // Make sure `z` is less than `2**256`. Also prevents `d == 0`.
                    // Placing the check here seems to give more optimal stack operations.
                    if iszero(gt(d, p1)) {
                        mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                        revert(0x1c, 0x04)
                    }
                    d := div(d, t) // Divide `d` by `t`, which is a power of two.
                    // Invert `d mod 2**256`
                    // Now that `d` is an odd number, it has an inverse
                    // modulo `2**256` such that `d * inv = 1 mod 2**256`.
                    // Compute the inverse by starting with a seed that is correct
                    // correct for four bits. That is, `d * inv = 1 mod 2**4`.
                    let inv := xor(2, mul(3, d))
                    // Now use 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.
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128
                    z :=
                        mul(
                            // Divide [p1 p0] by the factors of two.
                            // Shift in bits from `p1` into `p0`. For this we need
                            // to flip `t` such that it is `2**256 / t`.
                            or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
                            mul(sub(2, mul(d, inv)), inv) // inverse mod 2**256
                        )
                    break
                }
                z := div(z, d)
                break
            }
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision.
    /// Behavior is undefined if `d` is zero or the final result cannot fit in 256 bits.
    /// Performs the full 512 bit calculation regardless.
    function fullMulDivUnchecked(uint256 x, uint256 y, uint256 d)
        internal
        pure
        returns (uint256 z)
    {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            let mm := mulmod(x, y, not(0))
            let p1 := sub(mm, add(z, lt(mm, z)))
            let t := and(d, sub(0, d))
            let r := mulmod(x, y, d)
            d := div(d, t)
            let inv := xor(2, mul(3, d))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            z :=
                mul(
                    or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
                    mul(sub(2, mul(d, inv)), inv)
                )
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision, rounded up.
    /// Throws if result overflows a uint256 or when `d` is zero.
    /// Credit to Uniswap-v3-core under MIT license:
    /// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol
    function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        z = fullMulDiv(x, y, d);
        /// @solidity memory-safe-assembly
        assembly {
            if mulmod(x, y, d) {
                z := add(z, 1)
                if iszero(z) {
                    mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /// @dev Calculates `floor(x * y / 2 ** n)` with full precision.
    /// Throws if result overflows a uint256.
    /// Credit to Philogy under MIT license:
    /// https://github.com/SorellaLabs/angstrom/blob/main/contracts/src/libraries/X128MathLib.sol
    function fullMulDivN(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Temporarily use `z` as `p0` to save gas.
            z := mul(x, y) // Lower 256 bits of `x * y`. We'll call this `z`.
            for {} 1 {} {
                if iszero(or(iszero(x), eq(div(z, x), y))) {
                    let k := and(n, 0xff) // `n`, cleaned.
                    let mm := mulmod(x, y, not(0))
                    let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.
                    //         |      p1     |      z     |
                    // Before: | p1_0 ¦ p1_1 | z_0  ¦ z_1 |
                    // Final:  |   0  ¦ p1_0 | p1_1 ¦ z_0 |
                    // Check that final `z` doesn't overflow by checking that p1_0 = 0.
                    if iszero(shr(k, p1)) {
                        z := add(shl(sub(256, k), p1), shr(k, z))
                        break
                    }
                    mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                    revert(0x1c, 0x04)
                }
                z := shr(and(n, 0xff), z)
                break
            }
        }
    }

    /// @dev Returns `floor(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
            if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
                mstore(0x00, 0xad251c27) // `MulDivFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(z, d)
        }
    }

    /// @dev Returns `ceil(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
            if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
                mstore(0x00, 0xad251c27) // `MulDivFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(z, d))), div(z, d))
        }
    }

    /// @dev Returns `x`, the modular multiplicative inverse of `a`, such that `(a * x) % n == 1`.
    function invMod(uint256 a, uint256 n) internal pure returns (uint256 x) {
        /// @solidity memory-safe-assembly
        assembly {
            let g := n
            let r := mod(a, n)
            for { let y := 1 } 1 {} {
                let q := div(g, r)
                let t := g
                g := r
                r := sub(t, mul(r, q))
                let u := x
                x := y
                y := sub(u, mul(y, q))
                if iszero(r) { break }
            }
            x := mul(eq(g, 1), add(x, mul(slt(x, 0), n)))
        }
    }

    /// @dev Returns `ceil(x / d)`.
    /// Reverts if `d` is zero.
    function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(d) {
                mstore(0x00, 0x65244e4e) // `DivFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(x, d))), div(x, d))
        }
    }

    /// @dev Returns `max(0, x - y)`. Alias for `saturatingSub`.
    function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }

    /// @dev Returns `max(0, x - y)`.
    function saturatingSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }

    /// @dev Returns `min(2 ** 256 - 1, x + y)`.
    function saturatingAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(sub(0, lt(add(x, y), x)), add(x, y))
        }
    }

    /// @dev Returns `min(2 ** 256 - 1, x * y)`.
    function saturatingMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(sub(or(iszero(x), eq(div(mul(x, y), x), y)), 1), mul(x, y))
        }
    }

    /// @dev Returns `condition ? x : y`, without branching.
    function ternary(bool condition, uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), iszero(condition)))
        }
    }

    /// @dev Returns `condition ? x : y`, without branching.
    function ternary(bool condition, bytes32 x, bytes32 y) internal pure returns (bytes32 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), iszero(condition)))
        }
    }

    /// @dev Returns `condition ? x : y`, without branching.
    function ternary(bool condition, address x, address y) internal pure returns (address z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), iszero(condition)))
        }
    }

    /// @dev Exponentiate `x` to `y` by squaring, denominated in base `b`.
    /// Reverts if the computation overflows.
    function rpow(uint256 x, uint256 y, uint256 b) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(b, iszero(y)) // `0 ** 0 = 1`. Otherwise, `0 ** n = 0`.
            if x {
                z := xor(b, mul(xor(b, x), and(y, 1))) // `z = isEven(y) ? scale : x`
                let half := shr(1, b) // Divide `b` by 2.
                // Divide `y` by 2 every iteration.
                for { y := shr(1, y) } y { y := shr(1, y) } {
                    let xx := mul(x, x) // Store x squared.
                    let xxRound := add(xx, half) // Round to the nearest number.
                    // Revert if `xx + half` overflowed, or if `x ** 2` overflows.
                    if or(lt(xxRound, xx), shr(128, x)) {
                        mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
                        revert(0x1c, 0x04)
                    }
                    x := div(xxRound, b) // Set `x` to scaled `xxRound`.
                    // If `y` is odd:
                    if and(y, 1) {
                        let zx := mul(z, x) // Compute `z * x`.
                        let zxRound := add(zx, half) // Round to the nearest number.
                        // If `z * x` overflowed or `zx + half` overflowed:
                        if or(xor(div(zx, x), z), lt(zxRound, zx)) {
                            // Revert if `x` is non-zero.
                            if x {
                                mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
                                revert(0x1c, 0x04)
                            }
                        }
                        z := div(zxRound, b) // Return properly scaled `zxRound`.
                    }
                }
            }
        }
    }

    /// @dev Returns the square root of `x`, rounded down.
    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`.
            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // Let `y = x / 2**r`. We check `y >= 2**(k + 8)`
            // but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`.
            let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffffff, shr(r, x))))
            z := shl(shr(1, r), z)

            // Goal was to get `z*z*y` within a small factor of `x`. More iterations could
            // get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`.
            // We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small.
            // That's not possible if `x < 256` but we can just verify those cases exhaustively.

            // Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`.
            // Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`.
            // Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps.

            // For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)`
            // is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`,
            // with largest error when `s = 1` and when `s = 256` or `1/256`.

            // Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`.
            // Then we can estimate `sqrt(y)` using
            // `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`.

            // There is no overflow risk here since `y < 2**136` after the first branch above.
            z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If `x+1` is a perfect square, the Babylonian method cycles between
            // `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            z := sub(z, lt(div(x, z), z))
        }
    }

    /// @dev Returns the cube root of `x`, rounded down.
    /// Credit to bout3fiddy and pcaversaccio under AGPLv3 license:
    /// https://github.com/pcaversaccio/snekmate/blob/main/src/utils/Math.vy
    /// Formally verified by xuwinnie:
    /// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
    function cbrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // Makeshift lookup table to nudge the approximate log2 result.
            z := div(shl(div(r, 3), shl(lt(0xf, shr(r, x)), 0xf)), xor(7, mod(r, 3)))
            // Newton-Raphson's.
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            // Round down.
            z := sub(z, lt(div(x, mul(z, z)), z))
        }
    }

    /// @dev Returns the square root of `x`, denominated in `WAD`, rounded down.
    function sqrtWad(uint256 x) internal pure returns (uint256 z) {
        unchecked {
            if (x <= type(uint256).max / 10 ** 18) return sqrt(x * 10 ** 18);
            z = (1 + sqrt(x)) * 10 ** 9;
            z = (fullMulDivUnchecked(x, 10 ** 18, z) + z) >> 1;
        }
        /// @solidity memory-safe-assembly
        assembly {
            z := sub(z, gt(999999999999999999, sub(mulmod(z, z, x), 1))) // Round down.
        }
    }

    /// @dev Returns the cube root of `x`, denominated in `WAD`, rounded down.
    /// Formally verified by xuwinnie:
    /// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
    function cbrtWad(uint256 x) internal pure returns (uint256 z) {
        unchecked {
            if (x <= type(uint256).max / 10 ** 36) return cbrt(x * 10 ** 36);
            z = (1 + cbrt(x)) * 10 ** 12;
            z = (fullMulDivUnchecked(x, 10 ** 36, z * z) + z + z) / 3;
        }
        /// @solidity memory-safe-assembly
        assembly {
            let p := x
            for {} 1 {} {
                if iszero(shr(229, p)) {
                    if iszero(shr(199, p)) {
                        p := mul(p, 100000000000000000) // 10 ** 17.
                        break
                    }
                    p := mul(p, 100000000) // 10 ** 8.
                    break
                }
                if iszero(shr(249, p)) { p := mul(p, 100) }
                break
            }
            let t := mulmod(mul(z, z), z, p)
            z := sub(z, gt(lt(t, shr(1, p)), iszero(t))) // Round down.
        }
    }

    /// @dev Returns the factorial of `x`.
    function factorial(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := 1
            if iszero(lt(x, 58)) {
                mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`.
                revert(0x1c, 0x04)
            }
            for {} x { x := sub(x, 1) } { z := mul(z, x) }
        }
    }

    /// @dev Returns the log2 of `x`.
    /// Equivalent to computing the index of the most significant bit (MSB) of `x`.
    /// Returns 0 if `x` is zero.
    function log2(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // forgefmt: disable-next-item
            r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0x0706060506020504060203020504030106050205030304010505030400000000))
        }
    }

    /// @dev Returns the log2 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log2Up(uint256 x) internal pure returns (uint256 r) {
        r = log2(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(shl(r, 1), x))
        }
    }

    /// @dev Returns the log10 of `x`.
    /// Returns 0 if `x` is zero.
    function log10(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(lt(x, 100000000000000000000000000000000000000)) {
                x := div(x, 100000000000000000000000000000000000000)
                r := 38
            }
            if iszero(lt(x, 100000000000000000000)) {
                x := div(x, 100000000000000000000)
                r := add(r, 20)
            }
            if iszero(lt(x, 10000000000)) {
                x := div(x, 10000000000)
                r := add(r, 10)
            }
            if iszero(lt(x, 100000)) {
                x := div(x, 100000)
                r := add(r, 5)
            }
            r := add(r, add(gt(x, 9), add(gt(x, 99), add(gt(x, 999), gt(x, 9999)))))
        }
    }

    /// @dev Returns the log10 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log10Up(uint256 x) internal pure returns (uint256 r) {
        r = log10(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(exp(10, r), x))
        }
    }

    /// @dev Returns the log256 of `x`.
    /// Returns 0 if `x` is zero.
    function log256(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(shr(3, r), lt(0xff, shr(r, x)))
        }
    }

    /// @dev Returns the log256 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log256Up(uint256 x) internal pure returns (uint256 r) {
        r = log256(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(shl(shl(3, r), 1), x))
        }
    }

    /// @dev Returns the scientific notation format `mantissa * 10 ** exponent` of `x`.
    /// Useful for compressing prices (e.g. using 25 bit mantissa and 7 bit exponent).
    function sci(uint256 x) internal pure returns (uint256 mantissa, uint256 exponent) {
        /// @solidity memory-safe-assembly
        assembly {
            mantissa := x
            if mantissa {
                if iszero(mod(mantissa, 1000000000000000000000000000000000)) {
                    mantissa := div(mantissa, 1000000000000000000000000000000000)
                    exponent := 33
                }
                if iszero(mod(mantissa, 10000000000000000000)) {
                    mantissa := div(mantissa, 10000000000000000000)
                    exponent := add(exponent, 19)
                }
                if iszero(mod(mantissa, 1000000000000)) {
                    mantissa := div(mantissa, 1000000000000)
                    exponent := add(exponent, 12)
                }
                if iszero(mod(mantissa, 1000000)) {
                    mantissa := div(mantissa, 1000000)
                    exponent := add(exponent, 6)
                }
                if iszero(mod(mantissa, 10000)) {
                    mantissa := div(mantissa, 10000)
                    exponent := add(exponent, 4)
                }
                if iszero(mod(mantissa, 100)) {
                    mantissa := div(mantissa, 100)
                    exponent := add(exponent, 2)
                }
                if iszero(mod(mantissa, 10)) {
                    mantissa := div(mantissa, 10)
                    exponent := add(exponent, 1)
                }
            }
        }
    }

    /// @dev Convenience function for packing `x` into a smaller number using `sci`.
    /// The `mantissa` will be in bits [7..255] (the upper 249 bits).
    /// The `exponent` will be in bits [0..6] (the lower 7 bits).
    /// Use `SafeCastLib` to safely ensure that the `packed` number is small
    /// enough to fit in the desired unsigned integer type:
    /// ```
    ///     uint32 packed = SafeCastLib.toUint32(FixedPointMathLib.packSci(777 ether));
    /// ```
    function packSci(uint256 x) internal pure returns (uint256 packed) {
        (x, packed) = sci(x); // Reuse for `mantissa` and `exponent`.
        /// @solidity memory-safe-assembly
        assembly {
            if shr(249, x) {
                mstore(0x00, 0xce30380c) // `MantissaOverflow()`.
                revert(0x1c, 0x04)
            }
            packed := or(shl(7, x), packed)
        }
    }

    /// @dev Convenience function for unpacking a packed number from `packSci`.
    function unpackSci(uint256 packed) internal pure returns (uint256 unpacked) {
        unchecked {
            unpacked = (packed >> 7) * 10 ** (packed & 0x7f);
        }
    }

    /// @dev Returns the average of `x` and `y`. Rounds towards zero.
    function avg(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = (x & y) + ((x ^ y) >> 1);
        }
    }

    /// @dev Returns the average of `x` and `y`. Rounds towards negative infinity.
    function avg(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = (x >> 1) + (y >> 1) + (x & y & 1);
        }
    }

    /// @dev Returns the absolute value of `x`.
    function abs(int256 x) internal pure returns (uint256 z) {
        unchecked {
            z = (uint256(x) + uint256(x >> 255)) ^ uint256(x >> 255);
        }
    }

    /// @dev Returns the absolute distance between `x` and `y`.
    function dist(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(xor(sub(0, gt(x, y)), sub(y, x)), gt(x, y))
        }
    }

    /// @dev Returns the absolute distance between `x` and `y`.
    function dist(int256 x, int256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(xor(sub(0, sgt(x, y)), sub(y, x)), sgt(x, y))
        }
    }

    /// @dev Returns the minimum of `x` and `y`.
    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), lt(y, x)))
        }
    }

    /// @dev Returns the minimum of `x` and `y`.
    function min(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), slt(y, x)))
        }
    }

    /// @dev Returns the maximum of `x` and `y`.
    function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), gt(y, x)))
        }
    }

    /// @dev Returns the maximum of `x` and `y`.
    function max(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), sgt(y, x)))
        }
    }

    /// @dev Returns `x`, bounded to `minValue` and `maxValue`.
    function clamp(uint256 x, uint256 minValue, uint256 maxValue)
        internal
        pure
        returns (uint256 z)
    {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, minValue), gt(minValue, x)))
            z := xor(z, mul(xor(z, maxValue), lt(maxValue, z)))
        }
    }

    /// @dev Returns `x`, bounded to `minValue` and `maxValue`.
    function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, minValue), sgt(minValue, x)))
            z := xor(z, mul(xor(z, maxValue), slt(maxValue, z)))
        }
    }

    /// @dev Returns greatest common divisor of `x` and `y`.
    function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            for { z := x } y {} {
                let t := y
                y := mod(z, y)
                z := t
            }
        }
    }

    /// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`,
    /// with `t` clamped between `begin` and `end` (inclusive).
    /// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
    /// If `begins == end`, returns `t <= begin ? a : b`.
    function lerp(uint256 a, uint256 b, uint256 t, uint256 begin, uint256 end)
        internal
        pure
        returns (uint256)
    {
        if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
        if (t <= begin) return a;
        if (t >= end) return b;
        unchecked {
            if (b >= a) return a + fullMulDiv(b - a, t - begin, end - begin);
            return a - fullMulDiv(a - b, t - begin, end - begin);
        }
    }

    /// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`.
    /// with `t` clamped between `begin` and `end` (inclusive).
    /// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
    /// If `begins == end`, returns `t <= begin ? a : b`.
    function lerp(int256 a, int256 b, int256 t, int256 begin, int256 end)
        internal
        pure
        returns (int256)
    {
        if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
        if (t <= begin) return a;
        if (t >= end) return b;
        // forgefmt: disable-next-item
        unchecked {
            if (b >= a) return int256(uint256(a) + fullMulDiv(uint256(b - a),
                uint256(t - begin), uint256(end - begin)));
            return int256(uint256(a) - fullMulDiv(uint256(a - b),
                uint256(t - begin), uint256(end - begin)));
        }
    }

    /// @dev Returns if `x` is an even number. Some people may need this.
    function isEven(uint256 x) internal pure returns (bool) {
        return x & uint256(1) == uint256(0);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   RAW NUMBER OPERATIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `x + y`, without checking for overflow.
    function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x + y;
        }
    }

    /// @dev Returns `x + y`, without checking for overflow.
    function rawAdd(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x + y;
        }
    }

    /// @dev Returns `x - y`, without checking for underflow.
    function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x - y;
        }
    }

    /// @dev Returns `x - y`, without checking for underflow.
    function rawSub(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x - y;
        }
    }

    /// @dev Returns `x * y`, without checking for overflow.
    function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x * y;
        }
    }

    /// @dev Returns `x * y`, without checking for overflow.
    function rawMul(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x * y;
        }
    }

    /// @dev Returns `x / y`, returning 0 if `y` is zero.
    function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(x, y)
        }
    }

    /// @dev Returns `x / y`, returning 0 if `y` is zero.
    function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(x, y)
        }
    }

    /// @dev Returns `x % y`, returning 0 if `y` is zero.
    function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mod(x, y)
        }
    }

    /// @dev Returns `x % y`, returning 0 if `y` is zero.
    function rawSMod(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := smod(x, y)
        }
    }

    /// @dev Returns `(x + y) % d`, return 0 if `d` if zero.
    function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := addmod(x, y, d)
        }
    }

    /// @dev Returns `(x * y) % d`, return 0 if `d` if zero.
    function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mulmod(x, y, d)
        }
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 250
  },
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {
    "contracts/dynavaults/PermissionedDynaVault.sol": {
      "DynaVaultLib": "0x4c3df500989df0f6670b99018811b94d591cc5a0",
      "VaultConfigLib": "0x3ce426bb2fcb9e1300bbe03e2d34d7b3b7347db5",
      "VaultRouterLib": "0xd3109f87bd553b83f64a2972effb034bfed8d2a0"
    }
  }
}

Contract ABI

API
[{"inputs":[],"name":"AlreadyInitializedRBAC","type":"error"},{"inputs":[],"name":"ArrayMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[],"name":"ERC5143_SlippageProtection","type":"error"},{"inputs":[],"name":"MaxDeposit","type":"error"},{"inputs":[],"name":"MaxRedeem","type":"error"},{"inputs":[],"name":"MaxWithdraw","type":"error"},{"inputs":[],"name":"MinDeposit","type":"error"},{"inputs":[],"name":"MinRedeem","type":"error"},{"inputs":[],"name":"MinWithdraw","type":"error"},{"inputs":[],"name":"NotAuthorized","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroShares","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newInstance","type":"address"}],"name":"Cloned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"bool","name":"newState","type":"bool"}],"name":"UpdatedPermissionDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"strategyTotalDebt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"loss","type":"uint256"}],"name":"WithdrawFromStrategy","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"approveAddedToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"assetsPerShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"address","name":"feeToken","type":"address"},{"internalType":"uint256","name":"givenTotalSupply","type":"uint256"},{"internalType":"uint256","name":"givenFreeFunds","type":"uint256"}],"name":"calcSharesForFeeAmountUsingGivenTotalSupplyAndFreeFunds","outputs":[{"internalType":"uint256","name":"feeShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"address","name":"feeToken","type":"address"},{"internalType":"uint256","name":"deltaTotalAssets","type":"uint256"},{"internalType":"uint256","name":"givenTotalSupply","type":"uint256"},{"internalType":"uint256","name":"givenTotalAssets","type":"uint256"}],"name":"calcSharesForFeeAmountUsingGivenTotalSupplyAndTotalAssets","outputs":[{"internalType":"uint256","name":"feeShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"defaultAdmin","type":"address"}],"name":"clone","outputs":[{"internalType":"address","name":"newInstance","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetsIncludingFees","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"sharesNotIncludingFees","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"minShares","type":"uint256"}],"name":"depositCheckSlippage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"name":"doSwap","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"feeTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getClone","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"nameOverride","type":"string"},{"internalType":"string","name":"symbolOverride","type":"string"},{"internalType":"address","name":"managerAddress","type":"address"},{"internalType":"address","name":"referenceAssetOracleAddress","type":"address"},{"internalType":"address","name":"dynaRouterRegistryAddress","type":"address"},{"internalType":"address","name":"ownerAddress","type":"address"},{"internalType":"address","name":"vaultSimulatorAddress","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"address","name":"feeToken","type":"address"},{"internalType":"uint256","name":"deltaTotalAssets","type":"uint256"}],"name":"issueSharesForFeeAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"manager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxDepositLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxLoss","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTotalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minDepositLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"sharesNotIncludingFees","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"assetsIncludingFees","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"maxAssets","type":"uint256"}],"name":"mintCheckSlippage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"permissionDisabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"sharesIncludingFees","type":"uint256"},{"components":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"address","name":"manager","type":"address"},{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"tokenIdle","type":"uint256"},{"internalType":"uint256","name":"tokenDebt","type":"uint256"},{"internalType":"uint256","name":"totalProfit","type":"uint256"},{"internalType":"uint256","name":"totalLoss","type":"uint256"},{"internalType":"uint256","name":"depositDebt","type":"uint256"},{"internalType":"uint256","name":"depositDebtRatio","type":"uint256"},{"internalType":"uint256","name":"lastReport","type":"uint256"},{"internalType":"uint256","name":"lastReportedValue","type":"uint256"},{"internalType":"uint256","name":"watermark","type":"uint256"},{"internalType":"uint256","name":"lastWatermark","type":"uint256"},{"internalType":"uint256","name":"watermarkDuration","type":"uint256"},{"components":[{"internalType":"uint256","name":"debtRatio","type":"uint256"},{"internalType":"uint256","name":"totalDebt","type":"uint256"}],"internalType":"struct IVaultSimulatorAPI.VaultStrategySnapshot[]","name":"strategies","type":"tuple[]"}],"internalType":"struct IVaultSimulatorAPI.VaultTokenSnapshot[]","name":"tokens","type":"tuple[]"},{"internalType":"uint256","name":"reserveDebt","type":"uint256"},{"internalType":"uint256","name":"totalDepositDebt","type":"uint256"},{"internalType":"uint256","name":"totalFeeShares","type":"uint256"},{"internalType":"uint256","name":"totalFees","type":"uint256"},{"internalType":"uint256","name":"totalAssets","type":"uint256"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"uint256","name":"totalProfit","type":"uint256"},{"internalType":"uint256","name":"lockedProfit","type":"uint256"},{"internalType":"uint256","name":"lastLockedProfitRatio","type":"uint256"},{"internalType":"uint256","name":"lastLockedProfitDegradation","type":"uint256"},{"internalType":"uint256","name":"lockedProfitDegradationRate","type":"uint256"},{"internalType":"uint256","name":"extraLockedProfitDegradationRate","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"internalType":"struct IVaultSimulatorAPI.VaultSnapshot","name":"_snapshot","type":"tuple"}],"name":"previewRedeemProportional","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"}],"name":"previewSwap","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address","name":"selectedRouter","type":"address"},{"internalType":"bytes32[]","name":"swapData","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pricePerShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"sharesIncludingFees","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"assetsNotIncludingFees","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"minAssets","type":"uint256"}],"name":"redeemCheckSlippage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"sharesIncludingFees","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"redeemProportional","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256[]","name":"minAssets","type":"uint256[]"}],"name":"redeemProportionalCheckSlippage","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"referenceAsset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"referenceAssetOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"resetRemovedTokenAllowance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"routerRegistry","outputs":[{"internalType":"contract IDynaRouterRegistryAPI","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"initialAdmin","type":"address"}],"name":"setDefaultAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMinDepositAssets","type":"uint256"},{"internalType":"uint256","name":"newMaxTotalAssets","type":"uint256"}],"name":"setDepositLimits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxLoss","type":"uint256"}],"name":"setMaxLoss","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"newState","type":"bool"}],"name":"setPermissionDisabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"referenceAssetOracleAddress","type":"address"}],"name":"setReferenceAssetOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"routerRegistryAddress","type":"address"}],"name":"setRouterRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"sharesOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"components":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"address","name":"manager","type":"address"},{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"tokenIdle","type":"uint256"},{"internalType":"uint256","name":"tokenDebt","type":"uint256"},{"internalType":"uint256","name":"totalProfit","type":"uint256"},{"internalType":"uint256","name":"totalLoss","type":"uint256"},{"internalType":"uint256","name":"depositDebt","type":"uint256"},{"internalType":"uint256","name":"depositDebtRatio","type":"uint256"},{"internalType":"uint256","name":"lastReport","type":"uint256"},{"internalType":"uint256","name":"lastReportedValue","type":"uint256"},{"internalType":"uint256","name":"watermark","type":"uint256"},{"internalType":"uint256","name":"lastWatermark","type":"uint256"},{"internalType":"uint256","name":"watermarkDuration","type":"uint256"},{"components":[{"internalType":"uint256","name":"debtRatio","type":"uint256"},{"internalType":"uint256","name":"totalDebt","type":"uint256"}],"internalType":"struct IVaultSimulatorAPI.VaultStrategySnapshot[]","name":"strategies","type":"tuple[]"}],"internalType":"struct IVaultSimulatorAPI.VaultTokenSnapshot[]","name":"tokens","type":"tuple[]"},{"internalType":"uint256","name":"reserveDebt","type":"uint256"},{"internalType":"uint256","name":"totalDepositDebt","type":"uint256"},{"internalType":"uint256","name":"totalFeeShares","type":"uint256"},{"internalType":"uint256","name":"totalFees","type":"uint256"},{"internalType":"uint256","name":"totalAssets","type":"uint256"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"uint256","name":"totalProfit","type":"uint256"},{"internalType":"uint256","name":"lockedProfit","type":"uint256"},{"internalType":"uint256","name":"lastLockedProfitRatio","type":"uint256"},{"internalType":"uint256","name":"lastLockedProfitDegradation","type":"uint256"},{"internalType":"uint256","name":"lockedProfitDegradationRate","type":"uint256"},{"internalType":"uint256","name":"extraLockedProfitDegradationRate","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"internalType":"struct IVaultSimulatorAPI.VaultSnapshot","name":"snapshot","type":"tuple"}],"name":"simulatedDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"address","name":"feeToken","type":"address"},{"internalType":"uint256","name":"deltaTotalAssets","type":"uint256"},{"components":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"address","name":"manager","type":"address"},{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"tokenIdle","type":"uint256"},{"internalType":"uint256","name":"tokenDebt","type":"uint256"},{"internalType":"uint256","name":"totalProfit","type":"uint256"},{"internalType":"uint256","name":"totalLoss","type":"uint256"},{"internalType":"uint256","name":"depositDebt","type":"uint256"},{"internalType":"uint256","name":"depositDebtRatio","type":"uint256"},{"internalType":"uint256","name":"lastReport","type":"uint256"},{"internalType":"uint256","name":"lastReportedValue","type":"uint256"},{"internalType":"uint256","name":"watermark","type":"uint256"},{"internalType":"uint256","name":"lastWatermark","type":"uint256"},{"internalType":"uint256","name":"watermarkDuration","type":"uint256"},{"components":[{"internalType":"uint256","name":"debtRatio","type":"uint256"},{"internalType":"uint256","name":"totalDebt","type":"uint256"}],"internalType":"struct IVaultSimulatorAPI.VaultStrategySnapshot[]","name":"strategies","type":"tuple[]"}],"internalType":"struct IVaultSimulatorAPI.VaultTokenSnapshot[]","name":"tokens","type":"tuple[]"},{"internalType":"uint256","name":"reserveDebt","type":"uint256"},{"internalType":"uint256","name":"totalDepositDebt","type":"uint256"},{"internalType":"uint256","name":"totalFeeShares","type":"uint256"},{"internalType":"uint256","name":"totalFees","type":"uint256"},{"internalType":"uint256","name":"totalAssets","type":"uint256"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"uint256","name":"totalProfit","type":"uint256"},{"internalType":"uint256","name":"lockedProfit","type":"uint256"},{"internalType":"uint256","name":"lastLockedProfitRatio","type":"uint256"},{"internalType":"uint256","name":"lastLockedProfitDegradation","type":"uint256"},{"internalType":"uint256","name":"lockedProfitDegradationRate","type":"uint256"},{"internalType":"uint256","name":"extraLockedProfitDegradationRate","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"internalType":"struct IVaultSimulatorAPI.VaultSnapshot","name":"snapshot","type":"tuple"}],"name":"simulatedIssueSharesForFeeAmount","outputs":[{"components":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"address","name":"manager","type":"address"},{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"tokenIdle","type":"uint256"},{"internalType":"uint256","name":"tokenDebt","type":"uint256"},{"internalType":"uint256","name":"totalProfit","type":"uint256"},{"internalType":"uint256","name":"totalLoss","type":"uint256"},{"internalType":"uint256","name":"depositDebt","type":"uint256"},{"internalType":"uint256","name":"depositDebtRatio","type":"uint256"},{"internalType":"uint256","name":"lastReport","type":"uint256"},{"internalType":"uint256","name":"lastReportedValue","type":"uint256"},{"internalType":"uint256","name":"watermark","type":"uint256"},{"internalType":"uint256","name":"lastWatermark","type":"uint256"},{"internalType":"uint256","name":"watermarkDuration","type":"uint256"},{"components":[{"internalType":"uint256","name":"debtRatio","type":"uint256"},{"internalType":"uint256","name":"totalDebt","type":"uint256"}],"internalType":"struct IVaultSimulatorAPI.VaultStrategySnapshot[]","name":"strategies","type":"tuple[]"}],"internalType":"struct IVaultSimulatorAPI.VaultTokenSnapshot[]","name":"tokens","type":"tuple[]"},{"internalType":"uint256","name":"reserveDebt","type":"uint256"},{"internalType":"uint256","name":"totalDepositDebt","type":"uint256"},{"internalType":"uint256","name":"totalFeeShares","type":"uint256"},{"internalType":"uint256","name":"totalFees","type":"uint256"},{"internalType":"uint256","name":"totalAssets","type":"uint256"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"uint256","name":"totalProfit","type":"uint256"},{"internalType":"uint256","name":"lockedProfit","type":"uint256"},{"internalType":"uint256","name":"lastLockedProfitRatio","type":"uint256"},{"internalType":"uint256","name":"lastLockedProfitDegradation","type":"uint256"},{"internalType":"uint256","name":"lockedProfitDegradationRate","type":"uint256"},{"internalType":"uint256","name":"extraLockedProfitDegradationRate","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"internalType":"struct IVaultSimulatorAPI.VaultSnapshot","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"components":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"address","name":"manager","type":"address"},{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"tokenIdle","type":"uint256"},{"internalType":"uint256","name":"tokenDebt","type":"uint256"},{"internalType":"uint256","name":"totalProfit","type":"uint256"},{"internalType":"uint256","name":"totalLoss","type":"uint256"},{"internalType":"uint256","name":"depositDebt","type":"uint256"},{"internalType":"uint256","name":"depositDebtRatio","type":"uint256"},{"internalType":"uint256","name":"lastReport","type":"uint256"},{"internalType":"uint256","name":"lastReportedValue","type":"uint256"},{"internalType":"uint256","name":"watermark","type":"uint256"},{"internalType":"uint256","name":"lastWatermark","type":"uint256"},{"internalType":"uint256","name":"watermarkDuration","type":"uint256"},{"components":[{"internalType":"uint256","name":"debtRatio","type":"uint256"},{"internalType":"uint256","name":"totalDebt","type":"uint256"}],"internalType":"struct IVaultSimulatorAPI.VaultStrategySnapshot[]","name":"strategies","type":"tuple[]"}],"internalType":"struct IVaultSimulatorAPI.VaultTokenSnapshot[]","name":"tokens","type":"tuple[]"},{"internalType":"uint256","name":"reserveDebt","type":"uint256"},{"internalType":"uint256","name":"totalDepositDebt","type":"uint256"},{"internalType":"uint256","name":"totalFeeShares","type":"uint256"},{"internalType":"uint256","name":"totalFees","type":"uint256"},{"internalType":"uint256","name":"totalAssets","type":"uint256"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"uint256","name":"totalProfit","type":"uint256"},{"internalType":"uint256","name":"lockedProfit","type":"uint256"},{"internalType":"uint256","name":"lastLockedProfitRatio","type":"uint256"},{"internalType":"uint256","name":"lastLockedProfitDegradation","type":"uint256"},{"internalType":"uint256","name":"lockedProfitDegradationRate","type":"uint256"},{"internalType":"uint256","name":"extraLockedProfitDegradationRate","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"internalType":"struct IVaultSimulatorAPI.VaultSnapshot","name":"snapshot","type":"tuple"}],"name":"simulatedMaxWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"simulator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"address","name":"selectedRouter","type":"address"},{"internalType":"bytes32[]","name":"swapData","type":"bytes32[]"}],"name":"swap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"address","name":"selectedRouter","type":"address"},{"internalType":"bytes32[]","name":"swapData","type":"bytes32[]"}],"name":"swapAndReport","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"takeSnapshot","outputs":[{"components":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"address","name":"manager","type":"address"},{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"tokenIdle","type":"uint256"},{"internalType":"uint256","name":"tokenDebt","type":"uint256"},{"internalType":"uint256","name":"totalProfit","type":"uint256"},{"internalType":"uint256","name":"totalLoss","type":"uint256"},{"internalType":"uint256","name":"depositDebt","type":"uint256"},{"internalType":"uint256","name":"depositDebtRatio","type":"uint256"},{"internalType":"uint256","name":"lastReport","type":"uint256"},{"internalType":"uint256","name":"lastReportedValue","type":"uint256"},{"internalType":"uint256","name":"watermark","type":"uint256"},{"internalType":"uint256","name":"lastWatermark","type":"uint256"},{"internalType":"uint256","name":"watermarkDuration","type":"uint256"},{"components":[{"internalType":"uint256","name":"debtRatio","type":"uint256"},{"internalType":"uint256","name":"totalDebt","type":"uint256"}],"internalType":"struct IVaultSimulatorAPI.VaultStrategySnapshot[]","name":"strategies","type":"tuple[]"}],"internalType":"struct IVaultSimulatorAPI.VaultTokenSnapshot[]","name":"tokens","type":"tuple[]"},{"internalType":"uint256","name":"reserveDebt","type":"uint256"},{"internalType":"uint256","name":"totalDepositDebt","type":"uint256"},{"internalType":"uint256","name":"totalFeeShares","type":"uint256"},{"internalType":"uint256","name":"totalFees","type":"uint256"},{"internalType":"uint256","name":"totalAssets","type":"uint256"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"uint256","name":"totalProfit","type":"uint256"},{"internalType":"uint256","name":"lockedProfit","type":"uint256"},{"internalType":"uint256","name":"lastLockedProfitRatio","type":"uint256"},{"internalType":"uint256","name":"lastLockedProfitDegradation","type":"uint256"},{"internalType":"uint256","name":"lockedProfitDegradationRate","type":"uint256"},{"internalType":"uint256","name":"extraLockedProfitDegradationRate","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"internalType":"struct IVaultSimulatorAPI.VaultSnapshot","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"quote","type":"address"}],"name":"tokenValueInQuoteAsset","outputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"total","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetsNotIncludingFees","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"sharesIncludingFees","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"maxShares","type":"uint256"}],"name":"withdrawCheckSlippage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"valueToWithdraw","type":"uint256"}],"name":"withdrawTokenDebtFromStrategies","outputs":[{"internalType":"uint256","name":"totalLoss","type":"uint256"},{"internalType":"uint256","name":"totalWithdrawn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.