ETH Price: $2,516.88 (-0.33%)

Transaction Decoder

Block:
21495233 at Dec-27-2024 05:17:11 PM +UTC
Transaction Fee:
0.001101175264107264 ETH $2.77
Gas Used:
138,496 Gas / 7.950953559 Gwei

Emitted Events:

125 WETH9.Deposit( dst=[Receiver] DepositWrapper, wad=3000000000000000 )
126 WETH9.Approval( src=[Receiver] DepositWrapper, guy=StoneBeraVault, wad=3000000000000000 )
127 WETH9.Transfer( src=[Receiver] DepositWrapper, dst=StoneBeraVault, wad=3000000000000000 )
128 Token.Transfer( from=0x0000000000000000000000000000000000000000, to=[Sender] 0x5ff570067aaa86b406930a49149b70bde18e6fe7, value=2999892258701076 )
129 StoneBeraVault.Deposit( caller=[Receiver] DepositWrapper, owner=[Sender] 0x5ff570067aaa86b406930a49149b70bde18e6fe7, asset=WETH9, amount=3000000000000000, shares=2999892258701076 )

Account State Difference:

  Address   Before After State Difference Code
0x5Ff57006...dE18E6Fe7
0.005048674078781545 Eth
Nonce: 6
0.000947498814674281 Eth
Nonce: 7
0.004101175264107264
(beaverbuild)
6.890170221712549326 Eth6.890298795383098574 Eth0.000128573670549248
0x97Ad7506...7F007a706
0xC02aaA39...83C756Cc2 2,913,101.014188570940609111 Eth2,913,101.017188570940609111 Eth0.003

Execution Trace

ETH 0.003 DepositWrapper.depositETH( _receiver=0x5Ff570067AAA86B406930A49149b70bdE18E6Fe7 ) => ( minted=2999892258701076 )
  • ETH 0.003 WETH9.CALL( )
  • WETH9.balanceOf( 0x2aCA0C7ED4d5EB4a2116A3bc060A2F264a343357 ) => ( 3000000000000000 )
  • WETH9.approve( guy=0x8f88aE3798E8fF3D0e0DE7465A0863C9bbB577f0, wad=3000000000000000 ) => ( True )
  • StoneBeraVault.deposit( _asset=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, _amount=3000000000000000, _receiver=0x5Ff570067AAA86B406930A49149b70bdE18E6Fe7 ) => ( shares=2999892258701076 )
    • OracleConfigurator.getPrice( _token=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 ) => ( price=1000000000000000000 )
      • WETHOracle.STATICCALL( )
      • Token.STATICCALL( )
      • Token.STATICCALL( )
      • OracleConfigurator.getPrice( _token=0x7122985656e38BDC0302Db86685bb972b145bD3C ) => ( price=1034712400518153981 )
        • StoneOracle.STATICCALL( )
        • WETH9.balanceOf( 0x8f88aE3798E8fF3D0e0DE7465A0863C9bbB577f0 ) => ( 39151170237885619092706 )
        • OracleConfigurator.getPrice( _token=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 ) => ( price=1000000000000000000 )
          • WETHOracle.STATICCALL( )
          • Stone.balanceOf( account=0x8f88aE3798E8fF3D0e0DE7465A0863C9bbB577f0 ) => ( 20814951408118337716038 )
          • OracleConfigurator.getPrice( _token=0x7122985656e38BDC0302Db86685bb972b145bD3C ) => ( price=1034712400518153981 )
            • StoneOracle.STATICCALL( )
            • Token.STATICCALL( )
            • WETH9.transferFrom( src=0x2aCA0C7ED4d5EB4a2116A3bc060A2F264a343357, dst=0x8f88aE3798E8fF3D0e0DE7465A0863C9bbB577f0, wad=3000000000000000 ) => ( True )
            • Token.mint( _to=0x5Ff570067AAA86B406930A49149b70bdE18E6Fe7, _amount=2999892258701076 )
              File 1 of 8: DepositWrapper
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.26;
              import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
              interface IStoneBeraVault {
                  function lpToken() external returns (address);
                  function deposit(
                      address _asset,
                      uint256 _amount,
                      address _receiver
                  ) external returns (uint256 shares);
              }
              interface IWETH {
                  function deposit() external payable;
                  function balanceOf(address _user) external returns (uint256);
              }
              contract DepositWrapper {
                  address public immutable weth;
                  address public immutable vault;
                  address public immutable token;
                  constructor(address _weth, address _vault) {
                      require(_weth != address(0) && _vault != address(0), "ZERO ADDRESS");
                      weth = _weth;
                      vault = _vault;
                      token = IStoneBeraVault(vault).lpToken();
                  }
                  function depositETH(
                      address _receiver
                  ) external payable returns (uint256 minted) {
                      require(msg.value > 0, "zero value");
                      IWETH wETH = IWETH(weth);
                      wETH.deposit{value: msg.value}();
                      uint256 bal = wETH.balanceOf(address(this));
                      TransferHelper.safeApprove(weth, vault, bal);
                      minted = IStoneBeraVault(vault).deposit(weth, bal, _receiver);
                  }
                  receive() external payable {
                      revert("Forbidden");
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.6.0;
              import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
              library TransferHelper {
                  /// @notice Transfers tokens from the targeted address to the given destination
                  /// @notice Errors with 'STF' if transfer fails
                  /// @param token The contract address of the token to be transferred
                  /// @param from The originating address from which the tokens will be transferred
                  /// @param to The destination address of the transfer
                  /// @param value The amount to be transferred
                  function safeTransferFrom(
                      address token,
                      address from,
                      address to,
                      uint256 value
                  ) internal {
                      (bool success, bytes memory data) =
                          token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
                      require(success && (data.length == 0 || abi.decode(data, (bool))), 'STF');
                  }
                  /// @notice Transfers tokens from msg.sender to a recipient
                  /// @dev Errors with ST if transfer fails
                  /// @param token The contract address of the token which will be transferred
                  /// @param to The recipient of the transfer
                  /// @param value The value of the transfer
                  function safeTransfer(
                      address token,
                      address to,
                      uint256 value
                  ) internal {
                      (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
                      require(success && (data.length == 0 || abi.decode(data, (bool))), 'ST');
                  }
                  /// @notice Approves the stipulated contract to spend the given allowance in the given token
                  /// @dev Errors with 'SA' if transfer fails
                  /// @param token The contract address of the token to be approved
                  /// @param to The target of the approval
                  /// @param value The amount of the given token the target will be allowed to spend
                  function safeApprove(
                      address token,
                      address to,
                      uint256 value
                  ) internal {
                      (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
                      require(success && (data.length == 0 || abi.decode(data, (bool))), 'SA');
                  }
                  /// @notice Transfers ETH to the recipient address
                  /// @dev Fails with `STE`
                  /// @param to The destination of the transfer
                  /// @param value The value to be transferred
                  function safeTransferETH(address to, uint256 value) internal {
                      (bool success, ) = to.call{value: value}(new bytes(0));
                      require(success, 'STE');
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Interface of the ERC-20 standard as defined in the ERC.
               */
              interface IERC20 {
                  /**
                   * @dev Emitted when `value` tokens are moved from one account (`from`) to
                   * another (`to`).
                   *
                   * Note that `value` may be zero.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  /**
                   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                   * a call to {approve}. `value` is the new allowance.
                   */
                  event Approval(address indexed owner, address indexed spender, uint256 value);
                  /**
                   * @dev Returns the value of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
                  /**
                   * @dev Returns the value of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
                  /**
                   * @dev Moves a `value` amount of tokens from the caller's account to `to`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address to, uint256 value) external returns (bool);
                  /**
                   * @dev Returns the remaining number of tokens that `spender` will be
                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                   * zero by default.
                   *
                   * This value changes when {approve} or {transferFrom} are called.
                   */
                  function allowance(address owner, address spender) external view returns (uint256);
                  /**
                   * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
                   * caller's tokens.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                   * that someone may use both the old and the new allowance by unfortunate
                   * transaction ordering. One possible solution to mitigate this race
                   * condition is to first reduce the spender's allowance to 0 and set the
                   * desired value afterwards:
                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address spender, uint256 value) external returns (bool);
                  /**
                   * @dev Moves a `value` amount of tokens from `from` to `to` using the
                   * allowance mechanism. `value` is then deducted from the caller's
                   * allowance.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(address from, address to, uint256 value) external returns (bool);
              }
              

              File 2 of 8: WETH9
              // Copyright (C) 2015, 2016, 2017 Dapphub
              
              // This program is free software: you can redistribute it and/or modify
              // it under the terms of the GNU General Public License as published by
              // the Free Software Foundation, either version 3 of the License, or
              // (at your option) any later version.
              
              // This program is distributed in the hope that it will be useful,
              // but WITHOUT ANY WARRANTY; without even the implied warranty of
              // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
              // GNU General Public License for more details.
              
              // You should have received a copy of the GNU General Public License
              // along with this program.  If not, see <http://www.gnu.org/licenses/>.
              
              pragma solidity ^0.4.18;
              
              contract WETH9 {
                  string public name     = "Wrapped Ether";
                  string public symbol   = "WETH";
                  uint8  public decimals = 18;
              
                  event  Approval(address indexed src, address indexed guy, uint wad);
                  event  Transfer(address indexed src, address indexed dst, uint wad);
                  event  Deposit(address indexed dst, uint wad);
                  event  Withdrawal(address indexed src, uint wad);
              
                  mapping (address => uint)                       public  balanceOf;
                  mapping (address => mapping (address => uint))  public  allowance;
              
                  function() public payable {
                      deposit();
                  }
                  function deposit() public payable {
                      balanceOf[msg.sender] += msg.value;
                      Deposit(msg.sender, msg.value);
                  }
                  function withdraw(uint wad) public {
                      require(balanceOf[msg.sender] >= wad);
                      balanceOf[msg.sender] -= wad;
                      msg.sender.transfer(wad);
                      Withdrawal(msg.sender, wad);
                  }
              
                  function totalSupply() public view returns (uint) {
                      return this.balance;
                  }
              
                  function approve(address guy, uint wad) public returns (bool) {
                      allowance[msg.sender][guy] = wad;
                      Approval(msg.sender, guy, wad);
                      return true;
                  }
              
                  function transfer(address dst, uint wad) public returns (bool) {
                      return transferFrom(msg.sender, dst, wad);
                  }
              
                  function transferFrom(address src, address dst, uint wad)
                      public
                      returns (bool)
                  {
                      require(balanceOf[src] >= wad);
              
                      if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
                          require(allowance[src][msg.sender] >= wad);
                          allowance[src][msg.sender] -= wad;
                      }
              
                      balanceOf[src] -= wad;
                      balanceOf[dst] += wad;
              
                      Transfer(src, dst, wad);
              
                      return true;
                  }
              }
              
              
              /*
                                  GNU GENERAL PUBLIC LICENSE
                                     Version 3, 29 June 2007
              
               Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
               Everyone is permitted to copy and distribute verbatim copies
               of this license document, but changing it is not allowed.
              
                                          Preamble
              
                The GNU General Public License is a free, copyleft license for
              software and other kinds of works.
              
                The licenses for most software and other practical works are designed
              to take away your freedom to share and change the works.  By contrast,
              the GNU General Public License is intended to guarantee your freedom to
              share and change all versions of a program--to make sure it remains free
              software for all its users.  We, the Free Software Foundation, use the
              GNU General Public License for most of our software; it applies also to
              any other work released this way by its authors.  You can apply it to
              your programs, too.
              
                When we speak of free software, we are referring to freedom, not
              price.  Our General Public Licenses are designed to make sure that you
              have the freedom to distribute copies of free software (and charge for
              them if you wish), that you receive source code or can get it if you
              want it, that you can change the software or use pieces of it in new
              free programs, and that you know you can do these things.
              
                To protect your rights, we need to prevent others from denying you
              these rights or asking you to surrender the rights.  Therefore, you have
              certain responsibilities if you distribute copies of the software, or if
              you modify it: responsibilities to respect the freedom of others.
              
                For example, if you distribute copies of such a program, whether
              gratis or for a fee, you must pass on to the recipients the same
              freedoms that you received.  You must make sure that they, too, receive
              or can get the source code.  And you must show them these terms so they
              know their rights.
              
                Developers that use the GNU GPL protect your rights with two steps:
              (1) assert copyright on the software, and (2) offer you this License
              giving you legal permission to copy, distribute and/or modify it.
              
                For the developers' and authors' protection, the GPL clearly explains
              that there is no warranty for this free software.  For both users' and
              authors' sake, the GPL requires that modified versions be marked as
              changed, so that their problems will not be attributed erroneously to
              authors of previous versions.
              
                Some devices are designed to deny users access to install or run
              modified versions of the software inside them, although the manufacturer
              can do so.  This is fundamentally incompatible with the aim of
              protecting users' freedom to change the software.  The systematic
              pattern of such abuse occurs in the area of products for individuals to
              use, which is precisely where it is most unacceptable.  Therefore, we
              have designed this version of the GPL to prohibit the practice for those
              products.  If such problems arise substantially in other domains, we
              stand ready to extend this provision to those domains in future versions
              of the GPL, as needed to protect the freedom of users.
              
                Finally, every program is threatened constantly by software patents.
              States should not allow patents to restrict development and use of
              software on general-purpose computers, but in those that do, we wish to
              avoid the special danger that patents applied to a free program could
              make it effectively proprietary.  To prevent this, the GPL assures that
              patents cannot be used to render the program non-free.
              
                The precise terms and conditions for copying, distribution and
              modification follow.
              
                                     TERMS AND CONDITIONS
              
                0. Definitions.
              
                "This License" refers to version 3 of the GNU General Public License.
              
                "Copyright" also means copyright-like laws that apply to other kinds of
              works, such as semiconductor masks.
              
                "The Program" refers to any copyrightable work licensed under this
              License.  Each licensee is addressed as "you".  "Licensees" and
              "recipients" may be individuals or organizations.
              
                To "modify" a work means to copy from or adapt all or part of the work
              in a fashion requiring copyright permission, other than the making of an
              exact copy.  The resulting work is called a "modified version" of the
              earlier work or a work "based on" the earlier work.
              
                A "covered work" means either the unmodified Program or a work based
              on the Program.
              
                To "propagate" a work means to do anything with it that, without
              permission, would make you directly or secondarily liable for
              infringement under applicable copyright law, except executing it on a
              computer or modifying a private copy.  Propagation includes copying,
              distribution (with or without modification), making available to the
              public, and in some countries other activities as well.
              
                To "convey" a work means any kind of propagation that enables other
              parties to make or receive copies.  Mere interaction with a user through
              a computer network, with no transfer of a copy, is not conveying.
              
                An interactive user interface displays "Appropriate Legal Notices"
              to the extent that it includes a convenient and prominently visible
              feature that (1) displays an appropriate copyright notice, and (2)
              tells the user that there is no warranty for the work (except to the
              extent that warranties are provided), that licensees may convey the
              work under this License, and how to view a copy of this License.  If
              the interface presents a list of user commands or options, such as a
              menu, a prominent item in the list meets this criterion.
              
                1. Source Code.
              
                The "source code" for a work means the preferred form of the work
              for making modifications to it.  "Object code" means any non-source
              form of a work.
              
                A "Standard Interface" means an interface that either is an official
              standard defined by a recognized standards body, or, in the case of
              interfaces specified for a particular programming language, one that
              is widely used among developers working in that language.
              
                The "System Libraries" of an executable work include anything, other
              than the work as a whole, that (a) is included in the normal form of
              packaging a Major Component, but which is not part of that Major
              Component, and (b) serves only to enable use of the work with that
              Major Component, or to implement a Standard Interface for which an
              implementation is available to the public in source code form.  A
              "Major Component", in this context, means a major essential component
              (kernel, window system, and so on) of the specific operating system
              (if any) on which the executable work runs, or a compiler used to
              produce the work, or an object code interpreter used to run it.
              
                The "Corresponding Source" for a work in object code form means all
              the source code needed to generate, install, and (for an executable
              work) run the object code and to modify the work, including scripts to
              control those activities.  However, it does not include the work's
              System Libraries, or general-purpose tools or generally available free
              programs which are used unmodified in performing those activities but
              which are not part of the work.  For example, Corresponding Source
              includes interface definition files associated with source files for
              the work, and the source code for shared libraries and dynamically
              linked subprograms that the work is specifically designed to require,
              such as by intimate data communication or control flow between those
              subprograms and other parts of the work.
              
                The Corresponding Source need not include anything that users
              can regenerate automatically from other parts of the Corresponding
              Source.
              
                The Corresponding Source for a work in source code form is that
              same work.
              
                2. Basic Permissions.
              
                All rights granted under this License are granted for the term of
              copyright on the Program, and are irrevocable provided the stated
              conditions are met.  This License explicitly affirms your unlimited
              permission to run the unmodified Program.  The output from running a
              covered work is covered by this License only if the output, given its
              content, constitutes a covered work.  This License acknowledges your
              rights of fair use or other equivalent, as provided by copyright law.
              
                You may make, run and propagate covered works that you do not
              convey, without conditions so long as your license otherwise remains
              in force.  You may convey covered works to others for the sole purpose
              of having them make modifications exclusively for you, or provide you
              with facilities for running those works, provided that you comply with
              the terms of this License in conveying all material for which you do
              not control copyright.  Those thus making or running the covered works
              for you must do so exclusively on your behalf, under your direction
              and control, on terms that prohibit them from making any copies of
              your copyrighted material outside their relationship with you.
              
                Conveying under any other circumstances is permitted solely under
              the conditions stated below.  Sublicensing is not allowed; section 10
              makes it unnecessary.
              
                3. Protecting Users' Legal Rights From Anti-Circumvention Law.
              
                No covered work shall be deemed part of an effective technological
              measure under any applicable law fulfilling obligations under article
              11 of the WIPO copyright treaty adopted on 20 December 1996, or
              similar laws prohibiting or restricting circumvention of such
              measures.
              
                When you convey a covered work, you waive any legal power to forbid
              circumvention of technological measures to the extent such circumvention
              is effected by exercising rights under this License with respect to
              the covered work, and you disclaim any intention to limit operation or
              modification of the work as a means of enforcing, against the work's
              users, your or third parties' legal rights to forbid circumvention of
              technological measures.
              
                4. Conveying Verbatim Copies.
              
                You may convey verbatim copies of the Program's source code as you
              receive it, in any medium, provided that you conspicuously and
              appropriately publish on each copy an appropriate copyright notice;
              keep intact all notices stating that this License and any
              non-permissive terms added in accord with section 7 apply to the code;
              keep intact all notices of the absence of any warranty; and give all
              recipients a copy of this License along with the Program.
              
                You may charge any price or no price for each copy that you convey,
              and you may offer support or warranty protection for a fee.
              
                5. Conveying Modified Source Versions.
              
                You may convey a work based on the Program, or the modifications to
              produce it from the Program, in the form of source code under the
              terms of section 4, provided that you also meet all of these conditions:
              
                  a) The work must carry prominent notices stating that you modified
                  it, and giving a relevant date.
              
                  b) The work must carry prominent notices stating that it is
                  released under this License and any conditions added under section
                  7.  This requirement modifies the requirement in section 4 to
                  "keep intact all notices".
              
                  c) You must license the entire work, as a whole, under this
                  License to anyone who comes into possession of a copy.  This
                  License will therefore apply, along with any applicable section 7
                  additional terms, to the whole of the work, and all its parts,
                  regardless of how they are packaged.  This License gives no
                  permission to license the work in any other way, but it does not
                  invalidate such permission if you have separately received it.
              
                  d) If the work has interactive user interfaces, each must display
                  Appropriate Legal Notices; however, if the Program has interactive
                  interfaces that do not display Appropriate Legal Notices, your
                  work need not make them do so.
              
                A compilation of a covered work with other separate and independent
              works, which are not by their nature extensions of the covered work,
              and which are not combined with it such as to form a larger program,
              in or on a volume of a storage or distribution medium, is called an
              "aggregate" if the compilation and its resulting copyright are not
              used to limit the access or legal rights of the compilation's users
              beyond what the individual works permit.  Inclusion of a covered work
              in an aggregate does not cause this License to apply to the other
              parts of the aggregate.
              
                6. Conveying Non-Source Forms.
              
                You may convey a covered work in object code form under the terms
              of sections 4 and 5, provided that you also convey the
              machine-readable Corresponding Source under the terms of this License,
              in one of these ways:
              
                  a) Convey the object code in, or embodied in, a physical product
                  (including a physical distribution medium), accompanied by the
                  Corresponding Source fixed on a durable physical medium
                  customarily used for software interchange.
              
                  b) Convey the object code in, or embodied in, a physical product
                  (including a physical distribution medium), accompanied by a
                  written offer, valid for at least three years and valid for as
                  long as you offer spare parts or customer support for that product
                  model, to give anyone who possesses the object code either (1) a
                  copy of the Corresponding Source for all the software in the
                  product that is covered by this License, on a durable physical
                  medium customarily used for software interchange, for a price no
                  more than your reasonable cost of physically performing this
                  conveying of source, or (2) access to copy the
                  Corresponding Source from a network server at no charge.
              
                  c) Convey individual copies of the object code with a copy of the
                  written offer to provide the Corresponding Source.  This
                  alternative is allowed only occasionally and noncommercially, and
                  only if you received the object code with such an offer, in accord
                  with subsection 6b.
              
                  d) Convey the object code by offering access from a designated
                  place (gratis or for a charge), and offer equivalent access to the
                  Corresponding Source in the same way through the same place at no
                  further charge.  You need not require recipients to copy the
                  Corresponding Source along with the object code.  If the place to
                  copy the object code is a network server, the Corresponding Source
                  may be on a different server (operated by you or a third party)
                  that supports equivalent copying facilities, provided you maintain
                  clear directions next to the object code saying where to find the
                  Corresponding Source.  Regardless of what server hosts the
                  Corresponding Source, you remain obligated to ensure that it is
                  available for as long as needed to satisfy these requirements.
              
                  e) Convey the object code using peer-to-peer transmission, provided
                  you inform other peers where the object code and Corresponding
                  Source of the work are being offered to the general public at no
                  charge under subsection 6d.
              
                A separable portion of the object code, whose source code is excluded
              from the Corresponding Source as a System Library, need not be
              included in conveying the object code work.
              
                A "User Product" is either (1) a "consumer product", which means any
              tangible personal property which is normally used for personal, family,
              or household purposes, or (2) anything designed or sold for incorporation
              into a dwelling.  In determining whether a product is a consumer product,
              doubtful cases shall be resolved in favor of coverage.  For a particular
              product received by a particular user, "normally used" refers to a
              typical or common use of that class of product, regardless of the status
              of the particular user or of the way in which the particular user
              actually uses, or expects or is expected to use, the product.  A product
              is a consumer product regardless of whether the product has substantial
              commercial, industrial or non-consumer uses, unless such uses represent
              the only significant mode of use of the product.
              
                "Installation Information" for a User Product means any methods,
              procedures, authorization keys, or other information required to install
              and execute modified versions of a covered work in that User Product from
              a modified version of its Corresponding Source.  The information must
              suffice to ensure that the continued functioning of the modified object
              code is in no case prevented or interfered with solely because
              modification has been made.
              
                If you convey an object code work under this section in, or with, or
              specifically for use in, a User Product, and the conveying occurs as
              part of a transaction in which the right of possession and use of the
              User Product is transferred to the recipient in perpetuity or for a
              fixed term (regardless of how the transaction is characterized), the
              Corresponding Source conveyed under this section must be accompanied
              by the Installation Information.  But this requirement does not apply
              if neither you nor any third party retains the ability to install
              modified object code on the User Product (for example, the work has
              been installed in ROM).
              
                The requirement to provide Installation Information does not include a
              requirement to continue to provide support service, warranty, or updates
              for a work that has been modified or installed by the recipient, or for
              the User Product in which it has been modified or installed.  Access to a
              network may be denied when the modification itself materially and
              adversely affects the operation of the network or violates the rules and
              protocols for communication across the network.
              
                Corresponding Source conveyed, and Installation Information provided,
              in accord with this section must be in a format that is publicly
              documented (and with an implementation available to the public in
              source code form), and must require no special password or key for
              unpacking, reading or copying.
              
                7. Additional Terms.
              
                "Additional permissions" are terms that supplement the terms of this
              License by making exceptions from one or more of its conditions.
              Additional permissions that are applicable to the entire Program shall
              be treated as though they were included in this License, to the extent
              that they are valid under applicable law.  If additional permissions
              apply only to part of the Program, that part may be used separately
              under those permissions, but the entire Program remains governed by
              this License without regard to the additional permissions.
              
                When you convey a copy of a covered work, you may at your option
              remove any additional permissions from that copy, or from any part of
              it.  (Additional permissions may be written to require their own
              removal in certain cases when you modify the work.)  You may place
              additional permissions on material, added by you to a covered work,
              for which you have or can give appropriate copyright permission.
              
                Notwithstanding any other provision of this License, for material you
              add to a covered work, you may (if authorized by the copyright holders of
              that material) supplement the terms of this License with terms:
              
                  a) Disclaiming warranty or limiting liability differently from the
                  terms of sections 15 and 16 of this License; or
              
                  b) Requiring preservation of specified reasonable legal notices or
                  author attributions in that material or in the Appropriate Legal
                  Notices displayed by works containing it; or
              
                  c) Prohibiting misrepresentation of the origin of that material, or
                  requiring that modified versions of such material be marked in
                  reasonable ways as different from the original version; or
              
                  d) Limiting the use for publicity purposes of names of licensors or
                  authors of the material; or
              
                  e) Declining to grant rights under trademark law for use of some
                  trade names, trademarks, or service marks; or
              
                  f) Requiring indemnification of licensors and authors of that
                  material by anyone who conveys the material (or modified versions of
                  it) with contractual assumptions of liability to the recipient, for
                  any liability that these contractual assumptions directly impose on
                  those licensors and authors.
              
                All other non-permissive additional terms are considered "further
              restrictions" within the meaning of section 10.  If the Program as you
              received it, or any part of it, contains a notice stating that it is
              governed by this License along with a term that is a further
              restriction, you may remove that term.  If a license document contains
              a further restriction but permits relicensing or conveying under this
              License, you may add to a covered work material governed by the terms
              of that license document, provided that the further restriction does
              not survive such relicensing or conveying.
              
                If you add terms to a covered work in accord with this section, you
              must place, in the relevant source files, a statement of the
              additional terms that apply to those files, or a notice indicating
              where to find the applicable terms.
              
                Additional terms, permissive or non-permissive, may be stated in the
              form of a separately written license, or stated as exceptions;
              the above requirements apply either way.
              
                8. Termination.
              
                You may not propagate or modify a covered work except as expressly
              provided under this License.  Any attempt otherwise to propagate or
              modify it is void, and will automatically terminate your rights under
              this License (including any patent licenses granted under the third
              paragraph of section 11).
              
                However, if you cease all violation of this License, then your
              license from a particular copyright holder is reinstated (a)
              provisionally, unless and until the copyright holder explicitly and
              finally terminates your license, and (b) permanently, if the copyright
              holder fails to notify you of the violation by some reasonable means
              prior to 60 days after the cessation.
              
                Moreover, your license from a particular copyright holder is
              reinstated permanently if the copyright holder notifies you of the
              violation by some reasonable means, this is the first time you have
              received notice of violation of this License (for any work) from that
              copyright holder, and you cure the violation prior to 30 days after
              your receipt of the notice.
              
                Termination of your rights under this section does not terminate the
              licenses of parties who have received copies or rights from you under
              this License.  If your rights have been terminated and not permanently
              reinstated, you do not qualify to receive new licenses for the same
              material under section 10.
              
                9. Acceptance Not Required for Having Copies.
              
                You are not required to accept this License in order to receive or
              run a copy of the Program.  Ancillary propagation of a covered work
              occurring solely as a consequence of using peer-to-peer transmission
              to receive a copy likewise does not require acceptance.  However,
              nothing other than this License grants you permission to propagate or
              modify any covered work.  These actions infringe copyright if you do
              not accept this License.  Therefore, by modifying or propagating a
              covered work, you indicate your acceptance of this License to do so.
              
                10. Automatic Licensing of Downstream Recipients.
              
                Each time you convey a covered work, the recipient automatically
              receives a license from the original licensors, to run, modify and
              propagate that work, subject to this License.  You are not responsible
              for enforcing compliance by third parties with this License.
              
                An "entity transaction" is a transaction transferring control of an
              organization, or substantially all assets of one, or subdividing an
              organization, or merging organizations.  If propagation of a covered
              work results from an entity transaction, each party to that
              transaction who receives a copy of the work also receives whatever
              licenses to the work the party's predecessor in interest had or could
              give under the previous paragraph, plus a right to possession of the
              Corresponding Source of the work from the predecessor in interest, if
              the predecessor has it or can get it with reasonable efforts.
              
                You may not impose any further restrictions on the exercise of the
              rights granted or affirmed under this License.  For example, you may
              not impose a license fee, royalty, or other charge for exercise of
              rights granted under this License, and you may not initiate litigation
              (including a cross-claim or counterclaim in a lawsuit) alleging that
              any patent claim is infringed by making, using, selling, offering for
              sale, or importing the Program or any portion of it.
              
                11. Patents.
              
                A "contributor" is a copyright holder who authorizes use under this
              License of the Program or a work on which the Program is based.  The
              work thus licensed is called the contributor's "contributor version".
              
                A contributor's "essential patent claims" are all patent claims
              owned or controlled by the contributor, whether already acquired or
              hereafter acquired, that would be infringed by some manner, permitted
              by this License, of making, using, or selling its contributor version,
              but do not include claims that would be infringed only as a
              consequence of further modification of the contributor version.  For
              purposes of this definition, "control" includes the right to grant
              patent sublicenses in a manner consistent with the requirements of
              this License.
              
                Each contributor grants you a non-exclusive, worldwide, royalty-free
              patent license under the contributor's essential patent claims, to
              make, use, sell, offer for sale, import and otherwise run, modify and
              propagate the contents of its contributor version.
              
                In the following three paragraphs, a "patent license" is any express
              agreement or commitment, however denominated, not to enforce a patent
              (such as an express permission to practice a patent or covenant not to
              sue for patent infringement).  To "grant" such a patent license to a
              party means to make such an agreement or commitment not to enforce a
              patent against the party.
              
                If you convey a covered work, knowingly relying on a patent license,
              and the Corresponding Source of the work is not available for anyone
              to copy, free of charge and under the terms of this License, through a
              publicly available network server or other readily accessible means,
              then you must either (1) cause the Corresponding Source to be so
              available, or (2) arrange to deprive yourself of the benefit of the
              patent license for this particular work, or (3) arrange, in a manner
              consistent with the requirements of this License, to extend the patent
              license to downstream recipients.  "Knowingly relying" means you have
              actual knowledge that, but for the patent license, your conveying the
              covered work in a country, or your recipient's use of the covered work
              in a country, would infringe one or more identifiable patents in that
              country that you have reason to believe are valid.
              
                If, pursuant to or in connection with a single transaction or
              arrangement, you convey, or propagate by procuring conveyance of, a
              covered work, and grant a patent license to some of the parties
              receiving the covered work authorizing them to use, propagate, modify
              or convey a specific copy of the covered work, then the patent license
              you grant is automatically extended to all recipients of the covered
              work and works based on it.
              
                A patent license is "discriminatory" if it does not include within
              the scope of its coverage, prohibits the exercise of, or is
              conditioned on the non-exercise of one or more of the rights that are
              specifically granted under this License.  You may not convey a covered
              work if you are a party to an arrangement with a third party that is
              in the business of distributing software, under which you make payment
              to the third party based on the extent of your activity of conveying
              the work, and under which the third party grants, to any of the
              parties who would receive the covered work from you, a discriminatory
              patent license (a) in connection with copies of the covered work
              conveyed by you (or copies made from those copies), or (b) primarily
              for and in connection with specific products or compilations that
              contain the covered work, unless you entered into that arrangement,
              or that patent license was granted, prior to 28 March 2007.
              
                Nothing in this License shall be construed as excluding or limiting
              any implied license or other defenses to infringement that may
              otherwise be available to you under applicable patent law.
              
                12. No Surrender of Others' Freedom.
              
                If conditions are imposed on you (whether by court order, agreement or
              otherwise) that contradict the conditions of this License, they do not
              excuse you from the conditions of this License.  If you cannot convey a
              covered work so as to satisfy simultaneously your obligations under this
              License and any other pertinent obligations, then as a consequence you may
              not convey it at all.  For example, if you agree to terms that obligate you
              to collect a royalty for further conveying from those to whom you convey
              the Program, the only way you could satisfy both those terms and this
              License would be to refrain entirely from conveying the Program.
              
                13. Use with the GNU Affero General Public License.
              
                Notwithstanding any other provision of this License, you have
              permission to link or combine any covered work with a work licensed
              under version 3 of the GNU Affero General Public License into a single
              combined work, and to convey the resulting work.  The terms of this
              License will continue to apply to the part which is the covered work,
              but the special requirements of the GNU Affero General Public License,
              section 13, concerning interaction through a network will apply to the
              combination as such.
              
                14. Revised Versions of this License.
              
                The Free Software Foundation may publish revised and/or new versions of
              the GNU General Public License from time to time.  Such new versions will
              be similar in spirit to the present version, but may differ in detail to
              address new problems or concerns.
              
                Each version is given a distinguishing version number.  If the
              Program specifies that a certain numbered version of the GNU General
              Public License "or any later version" applies to it, you have the
              option of following the terms and conditions either of that numbered
              version or of any later version published by the Free Software
              Foundation.  If the Program does not specify a version number of the
              GNU General Public License, you may choose any version ever published
              by the Free Software Foundation.
              
                If the Program specifies that a proxy can decide which future
              versions of the GNU General Public License can be used, that proxy's
              public statement of acceptance of a version permanently authorizes you
              to choose that version for the Program.
              
                Later license versions may give you additional or different
              permissions.  However, no additional obligations are imposed on any
              author or copyright holder as a result of your choosing to follow a
              later version.
              
                15. Disclaimer of Warranty.
              
                THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
              APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
              HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
              OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
              THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
              PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
              IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
              ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
              
                16. Limitation of Liability.
              
                IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
              WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
              THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
              GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
              USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
              DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
              PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
              EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
              SUCH DAMAGES.
              
                17. Interpretation of Sections 15 and 16.
              
                If the disclaimer of warranty and limitation of liability provided
              above cannot be given local legal effect according to their terms,
              reviewing courts shall apply local law that most closely approximates
              an absolute waiver of all civil liability in connection with the
              Program, unless a warranty or assumption of liability accompanies a
              copy of the Program in return for a fee.
              
                                   END OF TERMS AND CONDITIONS
              
                          How to Apply These Terms to Your New Programs
              
                If you develop a new program, and you want it to be of the greatest
              possible use to the public, the best way to achieve this is to make it
              free software which everyone can redistribute and change under these terms.
              
                To do so, attach the following notices to the program.  It is safest
              to attach them to the start of each source file to most effectively
              state the exclusion of warranty; and each file should have at least
              the "copyright" line and a pointer to where the full notice is found.
              
                  <one line to give the program's name and a brief idea of what it does.>
                  Copyright (C) <year>  <name of author>
              
                  This program is free software: you can redistribute it and/or modify
                  it under the terms of the GNU General Public License as published by
                  the Free Software Foundation, either version 3 of the License, or
                  (at your option) any later version.
              
                  This program is distributed in the hope that it will be useful,
                  but WITHOUT ANY WARRANTY; without even the implied warranty of
                  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                  GNU General Public License for more details.
              
                  You should have received a copy of the GNU General Public License
                  along with this program.  If not, see <http://www.gnu.org/licenses/>.
              
              Also add information on how to contact you by electronic and paper mail.
              
                If the program does terminal interaction, make it output a short
              notice like this when it starts in an interactive mode:
              
                  <program>  Copyright (C) <year>  <name of author>
                  This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
                  This is free software, and you are welcome to redistribute it
                  under certain conditions; type `show c' for details.
              
              The hypothetical commands `show w' and `show c' should show the appropriate
              parts of the General Public License.  Of course, your program's commands
              might be different; for a GUI interface, you would use an "about box".
              
                You should also get your employer (if you work as a programmer) or school,
              if any, to sign a "copyright disclaimer" for the program, if necessary.
              For more information on this, and how to apply and follow the GNU GPL, see
              <http://www.gnu.org/licenses/>.
              
                The GNU General Public License does not permit incorporating your program
              into proprietary programs.  If your program is a subroutine library, you
              may consider it more useful to permit linking proprietary applications with
              the library.  If this is what you want to do, use the GNU Lesser General
              Public License instead of this License.  But first, please read
              <http://www.gnu.org/philosophy/why-not-lgpl.html>.
              
              */

              File 3 of 8: StoneBeraVault
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.26;
              import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
              import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
              import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
              import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
              import {Token} from "./Token.sol";
              import {OracleConfigurator} from "./oracle/OracleConfigurator.sol";
              import "./Errors.sol";
              contract StoneBeraVault is AccessControl {
                  using Math for uint256;
                  bytes32 public constant VAULT_OPERATOR_ROLE =
                      keccak256("VAULT_OPERATOR_ROLE");
                  bytes32 public constant ASSETS_MANAGEMENT_ROLE =
                      keccak256("ASSETS_MANAGEMENT_ROLE");
                  uint256 public constant D18 = 1e18;
                  uint256 public constant D6 = 1e6;
                  Token public immutable lpToken;
                  ERC20 public immutable withdrawToken;
                  OracleConfigurator public immutable oracleConfigurator;
                  address[] public underlyingAssets;
                  mapping(address => bool) public isUnderlyingAssets;
                  mapping(address => RedeemRequest) public redeemRequests;
                  mapping(uint256 => uint256) public roundPricePerShare;
                  mapping(uint256 => uint256) public withdrawTokenPrice;
                  mapping(address => bool) public depositPaused;
                  mapping(address => uint256) public feeRate;
                  uint256 public latestRoundID;
                  uint256 public cap;
                  uint256 public assetsBorrowed;
                  uint256 public redeemableAmountInPast; // calculated as withdrawToken
                  uint256 public requestingSharesInPast; // calculated as share
                  uint256 public requestingSharesInRound; // calculated as share
                  address public feeRecipient;
                  struct RedeemRequest {
                      uint256 requestRound;
                      uint256 requestShares;
                  }
                  event Deposit(
                      address indexed caller,
                      address indexed owner,
                      address indexed asset,
                      uint256 amount,
                      uint256 shares
                  );
                  event RedeemRequested(address indexed owner, uint256 shares, uint256 round);
                  event RedeemCancelled(address indexed owner, uint256 shares, uint256 round);
                  event RedeemClaimed(address indexed owner, uint256 amount);
                  event RollToNextRound(
                      uint256 round,
                      uint256 share,
                      uint256 withdrawTokenAmount,
                      uint256 sharePrice,
                      uint256 withdrawTokenPrice
                  );
                  event FeeCharged(address recipient, uint256 fee);
                  event SetCap(uint256 oldValue, uint256 newValue);
                  event SetDepositPause(address indexed asset, bool flag);
                  event SetFeeRate(address indexed asset, uint256 feeRate);
                  event SetFeeRecipient(address oldValue, address newValue);
                  event AddUnderlyingAsset(address indexed asset);
                  event RemoveUnderlyingAsset(address indexed asset);
                  event AssetsWithdrawn(address indexed asset, uint256 amount, uint256 value);
                  event AssetsRepaid(address indexed asset, uint256 amount, uint256 value);
                  constructor(
                      address _lpToken,
                      address _withdrawToken,
                      address _oracleConfigurator,
                      uint256 _cap
                  ) {
                      if (
                          _lpToken == address(0) ||
                          _withdrawToken == address(0) ||
                          _oracleConfigurator == address(0)
                      ) revert ZeroAddress();
                      _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
                      lpToken = Token(_lpToken);
                      withdrawToken = Token(_withdrawToken);
                      cap = _cap;
                      oracleConfigurator = OracleConfigurator(_oracleConfigurator);
                      if (oracleConfigurator.oracles(address(_withdrawToken)) == address(0))
                          revert InvalidOracle();
                  }
                  function deposit(
                      address _asset,
                      uint256 _amount,
                      address _receiver
                  ) public returns (uint256 shares) {
                      if (depositPaused[_asset]) revert DepositPaused();
                      if ((shares = previewDeposit(_asset, _amount)) == 0)
                          revert ZeroShares();
                      if (lpToken.totalSupply() + shares > cap) revert DepositCapped();
                      TransferHelper.safeTransferFrom(
                          _asset,
                          msg.sender,
                          address(this),
                          _amount
                      );
                      uint256 fee;
                      uint256 rate = feeRate[_asset];
                      if (rate != 0) {
                          fee = shares.mulDiv(rate, D6);
                      }
                      if (fee == 0) {
                          lpToken.mint(_receiver, shares);
                      } else {
                          shares -= fee;
                          lpToken.mint(_receiver, shares);
                          lpToken.mint(feeRecipient, fee);
                          emit FeeCharged(feeRecipient, fee);
                      }
                      emit Deposit(msg.sender, _receiver, _asset, _amount, shares);
                  }
                  function mint(
                      address _asset,
                      uint256 _shares,
                      address _receiver
                  ) external returns (uint256 assets) {
                      if (depositPaused[_asset]) revert DepositPaused();
                      if (_shares == 0) revert ZeroShares();
                      if (lpToken.totalSupply() + _shares > cap) revert DepositCapped();
                      assets = previewMint(_asset, _shares);
                      TransferHelper.safeTransferFrom(
                          _asset,
                          msg.sender,
                          address(this),
                          assets
                      );
                      uint256 fee;
                      uint256 rate = feeRate[_asset];
                      if (rate != 0) {
                          fee = _shares.mulDiv(rate, D6);
                      }
                      if (fee == 0) {
                          lpToken.mint(_receiver, _shares);
                      } else {
                          _shares -= fee;
                          lpToken.mint(_receiver, _shares);
                          lpToken.mint(feeRecipient, fee);
                          emit FeeCharged(feeRecipient, fee);
                      }
                      emit Deposit(msg.sender, _receiver, _asset, assets, _shares);
                  }
                  function requestRedeem(uint256 _shares) external {
                      if (_shares == 0) revert ZeroShares();
                      if (_shares > lpToken.balanceOf(msg.sender))
                          revert InsufficientBalance();
                      TransferHelper.safeTransferFrom(
                          address(lpToken),
                          msg.sender,
                          address(this),
                          _shares
                      );
                      RedeemRequest storage redeemRequest = redeemRequests[msg.sender];
                      if (
                          redeemRequest.requestShares > 0 &&
                          redeemRequest.requestRound < latestRoundID
                      ) {
                          claimRedeemRequest();
                      }
                      if (redeemRequest.requestRound == latestRoundID) {
                          redeemRequest.requestShares += _shares;
                      } else {
                          redeemRequest.requestRound = latestRoundID;
                          redeemRequest.requestShares = _shares;
                      }
                      requestingSharesInRound += _shares;
                      emit RedeemRequested(msg.sender, _shares, latestRoundID);
                  }
                  function cancelRequest() external {
                      if (pendingRedeemRequest() == 0) revert NoRequestingShares();
                      RedeemRequest storage redeemRequest = redeemRequests[msg.sender];
                      uint256 requestingShares = redeemRequest.requestShares;
                      redeemRequest.requestShares = 0;
                      requestingSharesInRound -= requestingShares;
                      TransferHelper.safeTransfer(
                          address(lpToken),
                          msg.sender,
                          requestingShares
                      );
                      emit RedeemCancelled(msg.sender, requestingShares, latestRoundID);
                  }
                  function claimRedeemRequest() public {
                      RedeemRequest storage redeemRequest = redeemRequests[msg.sender];
                      uint256 requestShares = redeemRequest.requestShares;
                      uint256 claimable;
                      uint256 round = redeemRequest.requestRound;
                      if (round < latestRoundID && redeemRequest.requestShares != 0) {
                          claimable = redeemRequest.requestShares.mulDiv(
                              roundPricePerShare[round],
                              withdrawTokenPrice[round],
                              Math.Rounding.Floor
                          );
                      } else {
                          revert NoClaimableRedeem();
                      }
                      lpToken.burn(address(this), requestShares);
                      redeemRequest.requestShares = 0;
                      redeemableAmountInPast -= claimable;
                      requestingSharesInPast -= requestShares;
                      if (claimable > 0)
                          TransferHelper.safeTransfer(
                              address(withdrawToken),
                              msg.sender,
                              claimable
                          );
                      emit RedeemClaimed(msg.sender, claimable);
                  }
                  function pendingRedeemRequest() public view returns (uint256 shares) {
                      RedeemRequest memory redeemRequest = redeemRequests[msg.sender];
                      return
                          redeemRequest.requestRound == latestRoundID
                              ? redeemRequest.requestShares
                              : 0;
                  }
                  function claimableRedeemRequest() external view returns (uint256 assets) {
                      RedeemRequest memory redeemRequest = redeemRequests[msg.sender];
                      uint256 round = redeemRequest.requestRound;
                      if (round < latestRoundID && redeemRequest.requestShares != 0) {
                          assets = redeemRequest.requestShares.mulDiv(
                              roundPricePerShare[round],
                              withdrawTokenPrice[round],
                              Math.Rounding.Floor
                          );
                      }
                  }
                  function totalAssets() public view returns (uint256 totalManagedAssets) {
                      uint256 length = underlyingAssets.length;
                      uint256 i;
                      address _this = address(this);
                      for (i; i < length; i++) {
                          address tokenAddr = underlyingAssets[i];
                          ERC20 token = ERC20(tokenAddr);
                          uint256 balance = token.balanceOf(_this);
                          if (balance != 0) {
                              uint256 price = oracleConfigurator.getPrice(tokenAddr);
                              uint256 value = price.mulDiv(balance, D18, Math.Rounding.Floor);
                              totalManagedAssets += value;
                          }
                      }
                      totalManagedAssets += assetsBorrowed;
                  }
                  function activeAssets() public view returns (uint256 assets) {
                      uint256 price = oracleConfigurator.getPrice(address(withdrawToken));
                      uint256 reservedValue = redeemableAmountInPast.mulDiv(
                          price,
                          D18,
                          Math.Rounding.Floor
                      );
                      return totalAssets() - reservedValue;
                  }
                  function activeShares() public view returns (uint256 shares) {
                      return lpToken.totalSupply() - requestingSharesInPast;
                  }
                  function convertToShares(
                      uint256 _assets
                  ) public view returns (uint256 shares) {
                      uint256 supply = lpToken.totalSupply();
                      return
                          supply == 0
                              ? _assets
                              : _assets.mulDiv(
                                  activeShares(),
                                  activeAssets(),
                                  Math.Rounding.Floor
                              );
                  }
                  function convertToAssets(
                      uint256 _shares
                  ) public view returns (uint256 assets) {
                      uint256 supply = lpToken.totalSupply();
                      return
                          supply == 0
                              ? _shares
                              : _shares.mulDiv(
                                  activeAssets(),
                                  activeShares(),
                                  Math.Rounding.Floor
                              );
                  }
                  function previewDeposit(
                      address _asset,
                      uint256 _amount
                  ) public view returns (uint256 shares) {
                      if (!isUnderlyingAssets[_asset]) revert InvalidAsset();
                      uint256 price = oracleConfigurator.getPrice(_asset);
                      uint256 value = _amount.mulDiv(price, D18, Math.Rounding.Floor);
                      return convertToShares(value);
                  }
                  function previewMint(
                      address _asset,
                      uint256 _shares
                  ) public view returns (uint256 assets) {
                      if (!isUnderlyingAssets[_asset]) revert InvalidAsset();
                      uint256 price = oracleConfigurator.getPrice(_asset);
                      uint256 amount = _shares.mulDiv(D18, price, Math.Rounding.Ceil);
                      uint256 supply = lpToken.totalSupply();
                      return
                          supply == 0
                              ? amount
                              : amount.mulDiv(
                                  activeAssets(),
                                  activeShares(),
                                  Math.Rounding.Ceil
                              );
                  }
                  function getRate() public view returns (uint256 rate) {
                      return activeAssets().mulDiv(D18, activeShares(), Math.Rounding.Floor);
                  }
                  function getUnderlyings()
                      external
                      view
                      returns (address[] memory underlyings)
                  {
                      return underlyingAssets;
                  }
                  function rollToNextRound() external onlyRole(VAULT_OPERATOR_ROLE) {
                      uint256 price = oracleConfigurator.getPrice(address(withdrawToken));
                      uint256 rate = getRate();
                      uint256 requestingShares = requestingSharesInRound;
                      uint256 withdrawTokenAmount = requestingShares.mulDiv(
                          rate,
                          price,
                          Math.Rounding.Ceil
                      );
                      if (
                          withdrawToken.balanceOf(address(this)) <
                          redeemableAmountInPast + withdrawTokenAmount
                      ) revert InsufficientBalance();
                      redeemableAmountInPast += withdrawTokenAmount;
                      requestingSharesInPast += requestingShares;
                      requestingSharesInRound = 0;
                      roundPricePerShare[latestRoundID] = rate;
                      withdrawTokenPrice[latestRoundID] = price;
                      emit RollToNextRound(
                          latestRoundID,
                          requestingShares,
                          withdrawTokenAmount,
                          rate,
                          price
                      );
                      latestRoundID++;
                  }
                  function withdrawAssets(
                      address _asset,
                      uint256 _amount
                  ) external onlyRole(ASSETS_MANAGEMENT_ROLE) {
                      if (!isUnderlyingAssets[_asset]) revert InvalidAsset();
                      uint256 balance = ERC20(_asset).balanceOf(address(this));
                      if (balance < _amount) revert InsufficientBalance();
                      if (
                          _asset == address(withdrawToken) &&
                          balance < redeemableAmountInPast + _amount
                      ) revert InsufficientBalance();
                      uint256 price = oracleConfigurator.getPrice(_asset);
                      uint256 value = _amount.mulDiv(price, D18, Math.Rounding.Ceil);
                      assetsBorrowed += value;
                      TransferHelper.safeTransfer(_asset, msg.sender, _amount);
                      emit AssetsWithdrawn(_asset, _amount, value);
                  }
                  function repayAssets(
                      address _asset,
                      uint256 _amount
                  ) external onlyRole(ASSETS_MANAGEMENT_ROLE) {
                      if (!isUnderlyingAssets[_asset]) revert InvalidAsset();
                      TransferHelper.safeTransferFrom(
                          _asset,
                          msg.sender,
                          address(this),
                          _amount
                      );
                      uint256 price = oracleConfigurator.getPrice(_asset);
                      uint256 value = _amount.mulDiv(price, D18, Math.Rounding.Floor);
                      if (value > assetsBorrowed) {
                          assetsBorrowed = 0;
                      } else {
                          assetsBorrowed -= value;
                      }
                      emit AssetsRepaid(_asset, _amount, value);
                  }
                  function setCap(uint256 _cap) external onlyRole(VAULT_OPERATOR_ROLE) {
                      emit SetCap(cap, _cap);
                      cap = _cap;
                  }
                  function addUnderlyingAsset(
                      address _asset
                  ) external onlyRole(VAULT_OPERATOR_ROLE) {
                      if (_asset == address(0) || isUnderlyingAssets[_asset])
                          revert InvalidAsset();
                      if (oracleConfigurator.oracles(_asset) == address(0))
                          revert InvalidOracle();
                      isUnderlyingAssets[_asset] = true;
                      underlyingAssets.push(_asset);
                      emit AddUnderlyingAsset(_asset);
                  }
                  function removeUnderlyingAsset(
                      address _asset
                  ) external onlyRole(VAULT_OPERATOR_ROLE) {
                      if (!isUnderlyingAssets[_asset]) revert InvalidAsset();
                      address[] memory assets = underlyingAssets;
                      uint256 length = assets.length;
                      uint256 i;
                      for (i; i < length; i++) {
                          if (assets[i] == _asset) {
                              underlyingAssets[i] = underlyingAssets[length - 1];
                              underlyingAssets.pop();
                              break;
                          }
                      }
                      isUnderlyingAssets[_asset] = false;
                      emit RemoveUnderlyingAsset(_asset);
                  }
                  function setDepositPause(
                      address _token,
                      bool _pause
                  ) external onlyRole(VAULT_OPERATOR_ROLE) {
                      depositPaused[_token] = _pause;
                      emit SetDepositPause(_token, _pause);
                  }
                  function setFeeRate(
                      address _token,
                      uint256 _feeRate
                  ) external onlyRole(VAULT_OPERATOR_ROLE) {
                      if (feeRecipient == address(0)) revert NoFeeRecipient();
                      if (!isUnderlyingAssets[_token]) revert InvalidAsset();
                      if (_feeRate > D6) revert InvalidFeeRate();
                      feeRate[_token] = _feeRate;
                      emit SetFeeRate(_token, _feeRate);
                  }
                  function setFeeRecipient(
                      address _feeRecipient
                  ) external onlyRole(VAULT_OPERATOR_ROLE) {
                      if (_feeRecipient == address(0)) revert ZeroAddress();
                      emit SetFeeRecipient(feeRecipient, _feeRecipient);
                      feeRecipient = _feeRecipient;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
              pragma solidity ^0.8.20;
              import {IAccessControl} from "./IAccessControl.sol";
              import {Context} from "../utils/Context.sol";
              import {ERC165} from "../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 account => bool) hasRole;
                      bytes32 adminRole;
                  }
                  mapping(bytes32 role => RoleData) private _roles;
                  bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                  /**
                   * @dev Modifier that checks that an account has a specific role. Reverts
                   * with an {AccessControlUnauthorizedAccount} error including the required role.
                   */
                  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 returns (bool) {
                      return _roles[role].hasRole[account];
                  }
                  /**
                   * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
                   * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
                   */
                  function _checkRole(bytes32 role) internal view virtual {
                      _checkRole(role, _msgSender());
                  }
                  /**
                   * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
                   * is missing `role`.
                   */
                  function _checkRole(bytes32 role, address account) internal view virtual {
                      if (!hasRole(role, account)) {
                          revert AccessControlUnauthorizedAccount(account, role);
                      }
                  }
                  /**
                   * @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 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 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 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 `callerConfirmation`.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function renounceRole(bytes32 role, address callerConfirmation) public virtual {
                      if (callerConfirmation != _msgSender()) {
                          revert AccessControlBadConfirmation();
                      }
                      _revokeRole(role, callerConfirmation);
                  }
                  /**
                   * @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 Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
                   *
                   * Internal function without access restriction.
                   *
                   * May emit a {RoleGranted} event.
                   */
                  function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
                      if (!hasRole(role, account)) {
                          _roles[role].hasRole[account] = true;
                          emit RoleGranted(role, account, _msgSender());
                          return true;
                      } else {
                          return false;
                      }
                  }
                  /**
                   * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
                   *
                   * Internal function without access restriction.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
                      if (hasRole(role, account)) {
                          _roles[role].hasRole[account] = false;
                          emit RoleRevoked(role, account, _msgSender());
                          return true;
                      } else {
                          return false;
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol)
              pragma solidity ^0.8.20;
              import {IERC20} from "./IERC20.sol";
              import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
              import {Context} from "../../utils/Context.sol";
              import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
              /**
               * @dev Implementation of the {IERC20} interface.
               *
               * This implementation is agnostic to the way tokens are created. This means
               * that a supply mechanism has to be added in a derived contract using {_mint}.
               *
               * TIP: For a detailed writeup see our guide
               * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
               * to implement supply mechanisms].
               *
               * The default value of {decimals} is 18. To change this, you should override
               * this function so it returns a different value.
               *
               * We have followed general OpenZeppelin Contracts guidelines: functions revert
               * instead returning `false` on failure. This behavior is nonetheless
               * conventional and does not conflict with the expectations of ERC-20
               * applications.
               */
              abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
                  mapping(address account => uint256) private _balances;
                  mapping(address account => mapping(address spender => uint256)) private _allowances;
                  uint256 private _totalSupply;
                  string private _name;
                  string private _symbol;
                  /**
                   * @dev Sets the values for {name} and {symbol}.
                   *
                   * All two of these values are immutable: they can only be set once during
                   * construction.
                   */
                  constructor(string memory name_, string memory symbol_) {
                      _name = name_;
                      _symbol = symbol_;
                  }
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() public view virtual returns (string memory) {
                      return _name;
                  }
                  /**
                   * @dev Returns the symbol of the token, usually a shorter version of the
                   * name.
                   */
                  function symbol() public view virtual returns (string memory) {
                      return _symbol;
                  }
                  /**
                   * @dev Returns the number of decimals used to get its user representation.
                   * For example, if `decimals` equals `2`, a balance of `505` tokens should
                   * be displayed to a user as `5.05` (`505 / 10 ** 2`).
                   *
                   * Tokens usually opt for a value of 18, imitating the relationship between
                   * Ether and Wei. This is the default value returned by this function, unless
                   * it's overridden.
                   *
                   * NOTE: This information is only used for _display_ purposes: it in
                   * no way affects any of the arithmetic of the contract, including
                   * {IERC20-balanceOf} and {IERC20-transfer}.
                   */
                  function decimals() public view virtual returns (uint8) {
                      return 18;
                  }
                  /**
                   * @dev See {IERC20-totalSupply}.
                   */
                  function totalSupply() public view virtual returns (uint256) {
                      return _totalSupply;
                  }
                  /**
                   * @dev See {IERC20-balanceOf}.
                   */
                  function balanceOf(address account) public view virtual returns (uint256) {
                      return _balances[account];
                  }
                  /**
                   * @dev See {IERC20-transfer}.
                   *
                   * Requirements:
                   *
                   * - `to` cannot be the zero address.
                   * - the caller must have a balance of at least `value`.
                   */
                  function transfer(address to, uint256 value) public virtual returns (bool) {
                      address owner = _msgSender();
                      _transfer(owner, to, value);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-allowance}.
                   */
                  function allowance(address owner, address spender) public view virtual returns (uint256) {
                      return _allowances[owner][spender];
                  }
                  /**
                   * @dev See {IERC20-approve}.
                   *
                   * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
                   * `transferFrom`. This is semantically equivalent to an infinite approval.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   */
                  function approve(address spender, uint256 value) public virtual returns (bool) {
                      address owner = _msgSender();
                      _approve(owner, spender, value);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-transferFrom}.
                   *
                   * Skips emitting an {Approval} event indicating an allowance update. This is not
                   * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
                   *
                   * 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:
                   *
                   * ```solidity
                   * 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
              // OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)
              pragma solidity ^0.8.20;
              import {Panic} from "../Panic.sol";
              import {SafeCast} from "./SafeCast.sol";
              /**
               * @dev Standard math utilities missing in the Solidity language.
               */
              library Math {
                  enum Rounding {
                      Floor, // Toward negative infinity
                      Ceil, // Toward positive infinity
                      Trunc, // Toward zero
                      Expand // Away from zero
                  }
                  /**
                   * @dev Returns the addition of two unsigned integers, with an success flag (no overflow).
                   */
                  function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
                      unchecked {
                          uint256 c = a + b;
                          if (c < a) return (false, 0);
                          return (true, c);
                      }
                  }
                  /**
                   * @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow).
                   */
                  function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
                      unchecked {
                          if (b > a) return (false, 0);
                          return (true, a - b);
                      }
                  }
                  /**
                   * @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).
                   */
                  function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
                      unchecked {
                          // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                          // benefit is lost if 'b' is also tested.
                          // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                          if (a == 0) return (true, 0);
                          uint256 c = a * b;
                          if (c / a != b) return (false, 0);
                          return (true, c);
                      }
                  }
                  /**
                   * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
                   */
                  function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
                      unchecked {
                          if (b == 0) return (false, 0);
                          return (true, a / b);
                      }
                  }
                  /**
                   * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
                   */
                  function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
                      unchecked {
                          if (b == 0) return (false, 0);
                          return (true, a % b);
                      }
                  }
                  /**
                   * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
                   *
                   * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
                   * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
                   * one branch when needed, making this function more expensive.
                   */
                  function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
                      unchecked {
                          // branchless ternary works because:
                          // b ^ (a ^ b) == a
                          // b ^ 0 == b
                          return b ^ ((a ^ b) * SafeCast.toUint(condition));
                      }
                  }
                  /**
                   * @dev Returns the largest of two numbers.
                   */
                  function max(uint256 a, uint256 b) internal pure returns (uint256) {
                      return ternary(a > b, a, b);
                  }
                  /**
                   * @dev Returns the smallest of two numbers.
                   */
                  function min(uint256 a, uint256 b) internal pure returns (uint256) {
                      return ternary(a < b, a, b);
                  }
                  /**
                   * @dev Returns the average of two numbers. The result is rounded towards
                   * zero.
                   */
                  function average(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b) / 2 can overflow.
                      return (a & b) + (a ^ b) / 2;
                  }
                  /**
                   * @dev Returns the ceiling of the division of two numbers.
                   *
                   * This differs from standard division with `/` in that it rounds towards infinity instead
                   * of rounding towards zero.
                   */
                  function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                      if (b == 0) {
                          // Guarantee the same behavior as in a regular Solidity division.
                          Panic.panic(Panic.DIVISION_BY_ZERO);
                      }
                      // The following calculation ensures accurate ceiling division without overflow.
                      // Since a is non-zero, (a - 1) / b will not overflow.
                      // The largest possible result occurs when (a - 1) / b is type(uint256).max,
                      // but the largest value we can obtain is type(uint256).max - 1, which happens
                      // when a = type(uint256).max and b = 1.
                      unchecked {
                          return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
                      }
                  }
                  /**
                   * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
                   * denominator == 0.
                   *
                   * 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²⁵⁶ and mod 2²⁵⁶ - 1, then use
                          // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                          // variables such that product = prod1 * 2²⁵⁶ + prod0.
                          uint256 prod0 = x * y; // Least significant 256 bits of the product
                          uint256 prod1; // Most significant 256 bits of the product
                          assembly {
                              let mm := mulmod(x, y, not(0))
                              prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                          }
                          // Handle non-overflow cases, 256 by 256 division.
                          if (prod1 == 0) {
                              // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                              // The surrounding unchecked block does not change this fact.
                              // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                              return prod0 / denominator;
                          }
                          // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
                          if (denominator <= prod1) {
                              Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_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.
                          uint256 twos = denominator & (0 - denominator);
                          assembly {
                              // Divide denominator by twos.
                              denominator := div(denominator, twos)
                              // Divide [prod1 prod0] by twos.
                              prod0 := div(prod0, twos)
                              // Flip twos such that it is 2²⁵⁶ / 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²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
                          // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
                          // four bits. That is, denominator * inv ≡ 1 mod 2⁴.
                          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⁸
                          inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
                          inverse *= 2 - denominator * inverse; // inverse mod 2³²
                          inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
                          inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
                          inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
                          // 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²⁵⁶. Since the preconditions guarantee that the outcome is
                          // less than 2²⁵⁶, 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;
                      }
                  }
                  /**
                   * @dev 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) {
                      return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
                  }
                  /**
                   * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
                   *
                   * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
                   * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
                   *
                   * If the input value is not inversible, 0 is returned.
                   *
                   * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
                   * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
                   */
                  function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
                      unchecked {
                          if (n == 0) return 0;
                          // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
                          // Used to compute integers x and y such that: ax + ny = gcd(a, n).
                          // When the gcd is 1, then the inverse of a modulo n exists and it's x.
                          // ax + ny = 1
                          // ax = 1 + (-y)n
                          // ax ≡ 1 (mod n) # x is the inverse of a modulo n
                          // If the remainder is 0 the gcd is n right away.
                          uint256 remainder = a % n;
                          uint256 gcd = n;
                          // Therefore the initial coefficients are:
                          // ax + ny = gcd(a, n) = n
                          // 0a + 1n = n
                          int256 x = 0;
                          int256 y = 1;
                          while (remainder != 0) {
                              uint256 quotient = gcd / remainder;
                              (gcd, remainder) = (
                                  // The old remainder is the next gcd to try.
                                  remainder,
                                  // Compute the next remainder.
                                  // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
                                  // where gcd is at most n (capped to type(uint256).max)
                                  gcd - remainder * quotient
                              );
                              (x, y) = (
                                  // Increment the coefficient of a.
                                  y,
                                  // Decrement the coefficient of n.
                                  // Can overflow, but the result is casted to uint256 so that the
                                  // next value of y is "wrapped around" to a value between 0 and n - 1.
                                  x - y * int256(quotient)
                              );
                          }
                          if (gcd != 1) return 0; // No inverse exists.
                          return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
                      }
                  }
                  /**
                   * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
                   *
                   * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
                   * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
                   * `a**(p-2)` is the modular multiplicative inverse of a in Fp.
                   *
                   * NOTE: this function does NOT check that `p` is a prime greater than `2`.
                   */
                  function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
                      unchecked {
                          return Math.modExp(a, p - 2, p);
                      }
                  }
                  /**
                   * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
                   *
                   * Requirements:
                   * - modulus can't be zero
                   * - underlying staticcall to precompile must succeed
                   *
                   * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
                   * sure the chain you're using it on supports the precompiled contract for modular exponentiation
                   * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
                   * the underlying function will succeed given the lack of a revert, but the result may be incorrectly
                   * interpreted as 0.
                   */
                  function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
                      (bool success, uint256 result) = tryModExp(b, e, m);
                      if (!success) {
                          Panic.panic(Panic.DIVISION_BY_ZERO);
                      }
                      return result;
                  }
                  /**
                   * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
                   * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
                   * to operate modulo 0 or if the underlying precompile reverted.
                   *
                   * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
                   * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
                   * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
                   * of a revert, but the result may be incorrectly interpreted as 0.
                   */
                  function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
                      if (m == 0) return (false, 0);
                      assembly ("memory-safe") {
                          let ptr := mload(0x40)
                          // | Offset    | Content    | Content (Hex)                                                      |
                          // |-----------|------------|--------------------------------------------------------------------|
                          // | 0x00:0x1f | size of b  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
                          // | 0x20:0x3f | size of e  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
                          // | 0x40:0x5f | size of m  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
                          // | 0x60:0x7f | value of b | 0x<.............................................................b> |
                          // | 0x80:0x9f | value of e | 0x<.............................................................e> |
                          // | 0xa0:0xbf | value of m | 0x<.............................................................m> |
                          mstore(ptr, 0x20)
                          mstore(add(ptr, 0x20), 0x20)
                          mstore(add(ptr, 0x40), 0x20)
                          mstore(add(ptr, 0x60), b)
                          mstore(add(ptr, 0x80), e)
                          mstore(add(ptr, 0xa0), m)
                          // Given the result < m, it's guaranteed to fit in 32 bytes,
                          // so we can use the memory scratch space located at offset 0.
                          success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
                          result := mload(0x00)
                      }
                  }
                  /**
                   * @dev Variant of {modExp} that supports inputs of arbitrary length.
                   */
                  function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
                      (bool success, bytes memory result) = tryModExp(b, e, m);
                      if (!success) {
                          Panic.panic(Panic.DIVISION_BY_ZERO);
                      }
                      return result;
                  }
                  /**
                   * @dev Variant of {tryModExp} that supports inputs of arbitrary length.
                   */
                  function tryModExp(
                      bytes memory b,
                      bytes memory e,
                      bytes memory m
                  ) internal view returns (bool success, bytes memory result) {
                      if (_zeroBytes(m)) return (false, new bytes(0));
                      uint256 mLen = m.length;
                      // Encode call args in result and move the free memory pointer
                      result = abi.encodePacked(b.length, e.length, mLen, b, e, m);
                      assembly ("memory-safe") {
                          let dataPtr := add(result, 0x20)
                          // Write result on top of args to avoid allocating extra memory.
                          success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
                          // Overwrite the length.
                          // result.length > returndatasize() is guaranteed because returndatasize() == m.length
                          mstore(result, mLen)
                          // Set the memory pointer after the returned data.
                          mstore(0x40, add(dataPtr, mLen))
                      }
                  }
                  /**
                   * @dev Returns whether the provided byte array is zero.
                   */
                  function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
                      for (uint256 i = 0; i < byteArray.length; ++i) {
                          if (byteArray[i] != 0) {
                              return false;
                          }
                      }
                      return true;
                  }
                  /**
                   * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
                   * towards zero.
                   *
                   * This method is based on Newton's method for computing square roots; the algorithm is restricted to only
                   * using integer operations.
                   */
                  function sqrt(uint256 a) internal pure returns (uint256) {
                      unchecked {
                          // Take care of easy edge cases when a == 0 or a == 1
                          if (a <= 1) {
                              return a;
                          }
                          // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
                          // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
                          // the current value as `ε_n = | x_n - sqrt(a) |`.
                          //
                          // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
                          // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
                          // bigger than any uint256.
                          //
                          // By noticing that
                          // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
                          // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
                          // to the msb function.
                          uint256 aa = a;
                          uint256 xn = 1;
                          if (aa >= (1 << 128)) {
                              aa >>= 128;
                              xn <<= 64;
                          }
                          if (aa >= (1 << 64)) {
                              aa >>= 64;
                              xn <<= 32;
                          }
                          if (aa >= (1 << 32)) {
                              aa >>= 32;
                              xn <<= 16;
                          }
                          if (aa >= (1 << 16)) {
                              aa >>= 16;
                              xn <<= 8;
                          }
                          if (aa >= (1 << 8)) {
                              aa >>= 8;
                              xn <<= 4;
                          }
                          if (aa >= (1 << 4)) {
                              aa >>= 4;
                              xn <<= 2;
                          }
                          if (aa >= (1 << 2)) {
                              xn <<= 1;
                          }
                          // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
                          //
                          // We can refine our estimation by noticing that the middle of that interval minimizes the error.
                          // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
                          // This is going to be our x_0 (and ε_0)
                          xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)
                          // From here, Newton's method give us:
                          // x_{n+1} = (x_n + a / x_n) / 2
                          //
                          // One should note that:
                          // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
                          //              = ((x_n² + a) / (2 * x_n))² - a
                          //              = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
                          //              = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
                          //              = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
                          //              = (x_n² - a)² / (2 * x_n)²
                          //              = ((x_n² - a) / (2 * x_n))²
                          //              ≥ 0
                          // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
                          //
                          // This gives us the proof of quadratic convergence of the sequence:
                          // ε_{n+1} = | x_{n+1} - sqrt(a) |
                          //         = | (x_n + a / x_n) / 2 - sqrt(a) |
                          //         = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
                          //         = | (x_n - sqrt(a))² / (2 * x_n) |
                          //         = | ε_n² / (2 * x_n) |
                          //         = ε_n² / | (2 * x_n) |
                          //
                          // For the first iteration, we have a special case where x_0 is known:
                          // ε_1 = ε_0² / | (2 * x_0) |
                          //     ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
                          //     ≤ 2**(2*e-4) / (3 * 2**(e-1))
                          //     ≤ 2**(e-3) / 3
                          //     ≤ 2**(e-3-log2(3))
                          //     ≤ 2**(e-4.5)
                          //
                          // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
                          // ε_{n+1} = ε_n² / | (2 * x_n) |
                          //         ≤ (2**(e-k))² / (2 * 2**(e-1))
                          //         ≤ 2**(2*e-2*k) / 2**e
                          //         ≤ 2**(e-2*k)
                          xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5)  -- special case, see above
                          xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9)    -- general case with k = 4.5
                          xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18)   -- general case with k = 9
                          xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36)   -- general case with k = 18
                          xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72)   -- general case with k = 36
                          xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144)  -- general case with k = 72
                          // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
                          // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
                          // sqrt(a) or sqrt(a) + 1.
                          return xn - SafeCast.toUint(xn > a / xn);
                      }
                  }
                  /**
                   * @dev 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
                      }
                  }
                  /**
                   * @dev Return the log in base 2 of a positive value rounded towards zero.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      uint256 exp;
                      unchecked {
                          exp = 128 * SafeCast.toUint(value > (1 << 128) - 1);
                          value >>= exp;
                          result += exp;
                          exp = 64 * SafeCast.toUint(value > (1 << 64) - 1);
                          value >>= exp;
                          result += exp;
                          exp = 32 * SafeCast.toUint(value > (1 << 32) - 1);
                          value >>= exp;
                          result += exp;
                          exp = 16 * SafeCast.toUint(value > (1 << 16) - 1);
                          value >>= exp;
                          result += exp;
                          exp = 8 * SafeCast.toUint(value > (1 << 8) - 1);
                          value >>= exp;
                          result += exp;
                          exp = 4 * SafeCast.toUint(value > (1 << 4) - 1);
                          value >>= exp;
                          result += exp;
                          exp = 2 * SafeCast.toUint(value > (1 << 2) - 1);
                          value >>= exp;
                          result += exp;
                          result += SafeCast.toUint(value > 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
                      }
                  }
                  /**
                   * @dev Return the log in base 10 of a positive value rounded towards zero.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >= 10 ** 64) {
                              value /= 10 ** 64;
                              result += 64;
                          }
                          if (value >= 10 ** 32) {
                              value /= 10 ** 32;
                              result += 32;
                          }
                          if (value >= 10 ** 16) {
                              value /= 10 ** 16;
                              result += 16;
                          }
                          if (value >= 10 ** 8) {
                              value /= 10 ** 8;
                              result += 8;
                          }
                          if (value >= 10 ** 4) {
                              value /= 10 ** 4;
                              result += 4;
                          }
                          if (value >= 10 ** 2) {
                              value /= 10 ** 2;
                              result += 2;
                          }
                          if (value >= 10 ** 1) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log10(value);
                          return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
                      }
                  }
                  /**
                   * @dev Return the log in base 256 of a positive value rounded towards zero.
                   * Returns 0 if given 0.
                   *
                   * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                   */
                  function log256(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      uint256 isGt;
                      unchecked {
                          isGt = SafeCast.toUint(value > (1 << 128) - 1);
                          value >>= isGt * 128;
                          result += isGt * 16;
                          isGt = SafeCast.toUint(value > (1 << 64) - 1);
                          value >>= isGt * 64;
                          result += isGt * 8;
                          isGt = SafeCast.toUint(value > (1 << 32) - 1);
                          value >>= isGt * 32;
                          result += isGt * 4;
                          isGt = SafeCast.toUint(value > (1 << 16) - 1);
                          value >>= isGt * 16;
                          result += isGt * 2;
                          result += SafeCast.toUint(value > (1 << 8) - 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
                      }
                  }
                  /**
                   * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
                   */
                  function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
                      return uint8(rounding) % 2 == 1;
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.6.0;
              import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
              library TransferHelper {
                  /// @notice Transfers tokens from the targeted address to the given destination
                  /// @notice Errors with 'STF' if transfer fails
                  /// @param token The contract address of the token to be transferred
                  /// @param from The originating address from which the tokens will be transferred
                  /// @param to The destination address of the transfer
                  /// @param value The amount to be transferred
                  function safeTransferFrom(
                      address token,
                      address from,
                      address to,
                      uint256 value
                  ) internal {
                      (bool success, bytes memory data) =
                          token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
                      require(success && (data.length == 0 || abi.decode(data, (bool))), 'STF');
                  }
                  /// @notice Transfers tokens from msg.sender to a recipient
                  /// @dev Errors with ST if transfer fails
                  /// @param token The contract address of the token which will be transferred
                  /// @param to The recipient of the transfer
                  /// @param value The value of the transfer
                  function safeTransfer(
                      address token,
                      address to,
                      uint256 value
                  ) internal {
                      (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
                      require(success && (data.length == 0 || abi.decode(data, (bool))), 'ST');
                  }
                  /// @notice Approves the stipulated contract to spend the given allowance in the given token
                  /// @dev Errors with 'SA' if transfer fails
                  /// @param token The contract address of the token to be approved
                  /// @param to The target of the approval
                  /// @param value The amount of the given token the target will be allowed to spend
                  function safeApprove(
                      address token,
                      address to,
                      uint256 value
                  ) internal {
                      (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
                      require(success && (data.length == 0 || abi.decode(data, (bool))), 'SA');
                  }
                  /// @notice Transfers ETH to the recipient address
                  /// @dev Fails with `STE`
                  /// @param to The destination of the transfer
                  /// @param value The value to be transferred
                  function safeTransferETH(address to, uint256 value) internal {
                      (bool success, ) = to.call{value: value}(new bytes(0));
                      require(success, 'STE');
                  }
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.26;
              import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
              import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
              contract Token is ERC20, AccessControl {
                  bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
                  bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
                  constructor(
                      string memory _name,
                      string memory _symbol
                  ) ERC20(_name, _symbol) {
                      _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
                  }
                  function mint(address _to, uint256 _amount) external onlyRole(MINTER_ROLE) {
                      _mint(_to, _amount);
                  }
                  function burn(
                      address _from,
                      uint256 _amount
                  ) external onlyRole(BURNER_ROLE) {
                      _burn(_from, _amount);
                  }
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.26;
              import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
              import {Oracle} from "./Oracle.sol";
              import "../Errors.sol";
              contract OracleConfigurator is AccessControl {
                  bytes32 public constant ORACLE_MANAGER_ROLE =
                      keccak256("ORACLE_MANAGER_ROLE");
                  mapping(address => address) public oracles;
                  event OracleUpdated(address oldOracle, address newOracle);
                  constructor() {
                      _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
                  }
                  function updateOracle(
                      address _token,
                      address _oracle
                  ) external onlyRole(ORACLE_MANAGER_ROLE) {
                      if (_token == address(0)) revert InvalidToken();
                      if (_oracle == address(0)) revert InvalidOracle();
                      emit OracleUpdated(oracles[_token], _oracle);
                      oracles[_token] = _oracle;
                  }
                  function getPrice(address _token) external view returns (uint256 price) {
                      address oracle = oracles[_token];
                      if (_token == address(0) || oracle == address(0)) revert InvalidToken();
                      price = Oracle(oracle).getPrice();
                  }
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.26;
              error InvalidToken();
              error InvalidAsset();
              error InsufficientBalance();
              error InvalidOracle();
              error InvalidPrice();
              error InvalidArrayLength();
              error DepositCapped();
              error DepositPaused();
              error ZeroShares();
              error NoRequestingShares();
              error NoClaimableRedeem();
              error ZeroAddress();
              error InvalidRequest();
              error InvalidRequestToken();
              error CannotRemove();
              error InvalidDecimals();
              error InvalidFeeRate();
              error NoFeeRecipient();
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev External interface of AccessControl declared to support ERC-165 detection.
               */
              interface IAccessControl {
                  /**
                   * @dev The `account` is missing a role.
                   */
                  error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
                  /**
                   * @dev The caller of a function is not the expected one.
                   *
                   * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
                   */
                  error AccessControlBadConfirmation();
                  /**
                   * @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.
                   */
                  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. This account bears the admin role (for the granted role).
                   * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
                   */
                  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 `callerConfirmation`.
                   */
                  function renounceRole(bytes32 role, address callerConfirmation) external;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
                  function _contextSuffixLength() internal view virtual returns (uint256) {
                      return 0;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)
              pragma solidity ^0.8.20;
              import {IERC165} from "./IERC165.sol";
              /**
               * @dev Implementation of the {IERC165} interface.
               *
               * Contracts that want to implement ERC-165 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);
               * }
               * ```
               */
              abstract contract ERC165 is IERC165 {
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
                      return interfaceId == type(IERC165).interfaceId;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Interface of the ERC-20 standard as defined in the ERC.
               */
              interface IERC20 {
                  /**
                   * @dev Emitted when `value` tokens are moved from one account (`from`) to
                   * another (`to`).
                   *
                   * Note that `value` may be zero.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  /**
                   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                   * a call to {approve}. `value` is the new allowance.
                   */
                  event Approval(address indexed owner, address indexed spender, uint256 value);
                  /**
                   * @dev Returns the value of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
                  /**
                   * @dev Returns the value of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
                  /**
                   * @dev Moves a `value` amount of tokens from the caller's account to `to`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address to, uint256 value) external returns (bool);
                  /**
                   * @dev Returns the remaining number of tokens that `spender` will be
                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                   * zero by default.
                   *
                   * This value changes when {approve} or {transferFrom} are called.
                   */
                  function allowance(address owner, address spender) external view returns (uint256);
                  /**
                   * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
                   * caller's tokens.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                   * that someone may use both the old and the new allowance by unfortunate
                   * transaction ordering. One possible solution to mitigate this race
                   * condition is to first reduce the spender's allowance to 0 and set the
                   * desired value afterwards:
                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address spender, uint256 value) external returns (bool);
                  /**
                   * @dev Moves a `value` amount of tokens from `from` to `to` using the
                   * allowance mechanism. `value` is then deducted from the caller's
                   * allowance.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(address from, address to, uint256 value) external returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
              pragma solidity ^0.8.20;
              import {IERC20} from "../IERC20.sol";
              /**
               * @dev Interface for the optional metadata functions from the ERC-20 standard.
               */
              interface IERC20Metadata is IERC20 {
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() external view returns (string memory);
                  /**
                   * @dev Returns the symbol of the token.
                   */
                  function symbol() external view returns (string memory);
                  /**
                   * @dev Returns the decimals places of the token.
                   */
                  function decimals() external view returns (uint8);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Standard ERC-20 Errors
               * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 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 ERC-721 Errors
               * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
               */
              interface IERC721Errors {
                  /**
                   * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-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 ERC-1155 Errors
               * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 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
              // OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Helper library for emitting standardized panic codes.
               *
               * ```solidity
               * contract Example {
               *      using Panic for uint256;
               *
               *      // Use any of the declared internal constants
               *      function foo() { Panic.GENERIC.panic(); }
               *
               *      // Alternatively
               *      function foo() { Panic.panic(Panic.GENERIC); }
               * }
               * ```
               *
               * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
               *
               * _Available since v5.1._
               */
              // slither-disable-next-line unused-state
              library Panic {
                  /// @dev generic / unspecified error
                  uint256 internal constant GENERIC = 0x00;
                  /// @dev used by the assert() builtin
                  uint256 internal constant ASSERT = 0x01;
                  /// @dev arithmetic underflow or overflow
                  uint256 internal constant UNDER_OVERFLOW = 0x11;
                  /// @dev division or modulo by zero
                  uint256 internal constant DIVISION_BY_ZERO = 0x12;
                  /// @dev enum conversion error
                  uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
                  /// @dev invalid encoding in storage
                  uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
                  /// @dev empty array pop
                  uint256 internal constant EMPTY_ARRAY_POP = 0x31;
                  /// @dev array out of bounds access
                  uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
                  /// @dev resource error (too large allocation or too large array)
                  uint256 internal constant RESOURCE_ERROR = 0x41;
                  /// @dev calling invalid internal function
                  uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;
                  /// @dev Reverts with a panic code. Recommended to use with
                  /// the internal constants with predefined codes.
                  function panic(uint256 code) internal pure {
                      assembly ("memory-safe") {
                          mstore(0x00, 0x4e487b71)
                          mstore(0x20, code)
                          revert(0x1c, 0x24)
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
              // This file was procedurally generated from scripts/generate/templates/SafeCast.js.
              pragma solidity ^0.8.20;
              /**
               * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
               * checks.
               *
               * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
               * easily result in undesired exploitation or bugs, since developers usually
               * assume that overflows raise errors. `SafeCast` restores this intuition by
               * reverting the transaction when such an operation overflows.
               *
               * Using this library instead of the unchecked operations eliminates an entire
               * class of bugs, so it's recommended to use it always.
               */
              library SafeCast {
                  /**
                   * @dev Value doesn't fit in an uint of `bits` size.
                   */
                  error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
                  /**
                   * @dev An int value doesn't fit in an uint of `bits` size.
                   */
                  error SafeCastOverflowedIntToUint(int256 value);
                  /**
                   * @dev Value doesn't fit in an int of `bits` size.
                   */
                  error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
                  /**
                   * @dev An uint value doesn't fit in an int of `bits` size.
                   */
                  error SafeCastOverflowedUintToInt(uint256 value);
                  /**
                   * @dev Returns the downcasted uint248 from uint256, reverting on
                   * overflow (when the input is greater than largest uint248).
                   *
                   * Counterpart to Solidity's `uint248` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 248 bits
                   */
                  function toUint248(uint256 value) internal pure returns (uint248) {
                      if (value > type(uint248).max) {
                          revert SafeCastOverflowedUintDowncast(248, value);
                      }
                      return uint248(value);
                  }
                  /**
                   * @dev Returns the downcasted uint240 from uint256, reverting on
                   * overflow (when the input is greater than largest uint240).
                   *
                   * Counterpart to Solidity's `uint240` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 240 bits
                   */
                  function toUint240(uint256 value) internal pure returns (uint240) {
                      if (value > type(uint240).max) {
                          revert SafeCastOverflowedUintDowncast(240, value);
                      }
                      return uint240(value);
                  }
                  /**
                   * @dev Returns the downcasted uint232 from uint256, reverting on
                   * overflow (when the input is greater than largest uint232).
                   *
                   * Counterpart to Solidity's `uint232` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 232 bits
                   */
                  function toUint232(uint256 value) internal pure returns (uint232) {
                      if (value > type(uint232).max) {
                          revert SafeCastOverflowedUintDowncast(232, value);
                      }
                      return uint232(value);
                  }
                  /**
                   * @dev Returns the downcasted uint224 from uint256, reverting on
                   * overflow (when the input is greater than largest uint224).
                   *
                   * Counterpart to Solidity's `uint224` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 224 bits
                   */
                  function toUint224(uint256 value) internal pure returns (uint224) {
                      if (value > type(uint224).max) {
                          revert SafeCastOverflowedUintDowncast(224, value);
                      }
                      return uint224(value);
                  }
                  /**
                   * @dev Returns the downcasted uint216 from uint256, reverting on
                   * overflow (when the input is greater than largest uint216).
                   *
                   * Counterpart to Solidity's `uint216` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 216 bits
                   */
                  function toUint216(uint256 value) internal pure returns (uint216) {
                      if (value > type(uint216).max) {
                          revert SafeCastOverflowedUintDowncast(216, value);
                      }
                      return uint216(value);
                  }
                  /**
                   * @dev Returns the downcasted uint208 from uint256, reverting on
                   * overflow (when the input is greater than largest uint208).
                   *
                   * Counterpart to Solidity's `uint208` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 208 bits
                   */
                  function toUint208(uint256 value) internal pure returns (uint208) {
                      if (value > type(uint208).max) {
                          revert SafeCastOverflowedUintDowncast(208, value);
                      }
                      return uint208(value);
                  }
                  /**
                   * @dev Returns the downcasted uint200 from uint256, reverting on
                   * overflow (when the input is greater than largest uint200).
                   *
                   * Counterpart to Solidity's `uint200` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 200 bits
                   */
                  function toUint200(uint256 value) internal pure returns (uint200) {
                      if (value > type(uint200).max) {
                          revert SafeCastOverflowedUintDowncast(200, value);
                      }
                      return uint200(value);
                  }
                  /**
                   * @dev Returns the downcasted uint192 from uint256, reverting on
                   * overflow (when the input is greater than largest uint192).
                   *
                   * Counterpart to Solidity's `uint192` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 192 bits
                   */
                  function toUint192(uint256 value) internal pure returns (uint192) {
                      if (value > type(uint192).max) {
                          revert SafeCastOverflowedUintDowncast(192, value);
                      }
                      return uint192(value);
                  }
                  /**
                   * @dev Returns the downcasted uint184 from uint256, reverting on
                   * overflow (when the input is greater than largest uint184).
                   *
                   * Counterpart to Solidity's `uint184` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 184 bits
                   */
                  function toUint184(uint256 value) internal pure returns (uint184) {
                      if (value > type(uint184).max) {
                          revert SafeCastOverflowedUintDowncast(184, value);
                      }
                      return uint184(value);
                  }
                  /**
                   * @dev Returns the downcasted uint176 from uint256, reverting on
                   * overflow (when the input is greater than largest uint176).
                   *
                   * Counterpart to Solidity's `uint176` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 176 bits
                   */
                  function toUint176(uint256 value) internal pure returns (uint176) {
                      if (value > type(uint176).max) {
                          revert SafeCastOverflowedUintDowncast(176, value);
                      }
                      return uint176(value);
                  }
                  /**
                   * @dev Returns the downcasted uint168 from uint256, reverting on
                   * overflow (when the input is greater than largest uint168).
                   *
                   * Counterpart to Solidity's `uint168` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 168 bits
                   */
                  function toUint168(uint256 value) internal pure returns (uint168) {
                      if (value > type(uint168).max) {
                          revert SafeCastOverflowedUintDowncast(168, value);
                      }
                      return uint168(value);
                  }
                  /**
                   * @dev Returns the downcasted uint160 from uint256, reverting on
                   * overflow (when the input is greater than largest uint160).
                   *
                   * Counterpart to Solidity's `uint160` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 160 bits
                   */
                  function toUint160(uint256 value) internal pure returns (uint160) {
                      if (value > type(uint160).max) {
                          revert SafeCastOverflowedUintDowncast(160, value);
                      }
                      return uint160(value);
                  }
                  /**
                   * @dev Returns the downcasted uint152 from uint256, reverting on
                   * overflow (when the input is greater than largest uint152).
                   *
                   * Counterpart to Solidity's `uint152` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 152 bits
                   */
                  function toUint152(uint256 value) internal pure returns (uint152) {
                      if (value > type(uint152).max) {
                          revert SafeCastOverflowedUintDowncast(152, value);
                      }
                      return uint152(value);
                  }
                  /**
                   * @dev Returns the downcasted uint144 from uint256, reverting on
                   * overflow (when the input is greater than largest uint144).
                   *
                   * Counterpart to Solidity's `uint144` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 144 bits
                   */
                  function toUint144(uint256 value) internal pure returns (uint144) {
                      if (value > type(uint144).max) {
                          revert SafeCastOverflowedUintDowncast(144, value);
                      }
                      return uint144(value);
                  }
                  /**
                   * @dev Returns the downcasted uint136 from uint256, reverting on
                   * overflow (when the input is greater than largest uint136).
                   *
                   * Counterpart to Solidity's `uint136` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 136 bits
                   */
                  function toUint136(uint256 value) internal pure returns (uint136) {
                      if (value > type(uint136).max) {
                          revert SafeCastOverflowedUintDowncast(136, value);
                      }
                      return uint136(value);
                  }
                  /**
                   * @dev Returns the downcasted uint128 from uint256, reverting on
                   * overflow (when the input is greater than largest uint128).
                   *
                   * Counterpart to Solidity's `uint128` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 128 bits
                   */
                  function toUint128(uint256 value) internal pure returns (uint128) {
                      if (value > type(uint128).max) {
                          revert SafeCastOverflowedUintDowncast(128, value);
                      }
                      return uint128(value);
                  }
                  /**
                   * @dev Returns the downcasted uint120 from uint256, reverting on
                   * overflow (when the input is greater than largest uint120).
                   *
                   * Counterpart to Solidity's `uint120` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 120 bits
                   */
                  function toUint120(uint256 value) internal pure returns (uint120) {
                      if (value > type(uint120).max) {
                          revert SafeCastOverflowedUintDowncast(120, value);
                      }
                      return uint120(value);
                  }
                  /**
                   * @dev Returns the downcasted uint112 from uint256, reverting on
                   * overflow (when the input is greater than largest uint112).
                   *
                   * Counterpart to Solidity's `uint112` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 112 bits
                   */
                  function toUint112(uint256 value) internal pure returns (uint112) {
                      if (value > type(uint112).max) {
                          revert SafeCastOverflowedUintDowncast(112, value);
                      }
                      return uint112(value);
                  }
                  /**
                   * @dev Returns the downcasted uint104 from uint256, reverting on
                   * overflow (when the input is greater than largest uint104).
                   *
                   * Counterpart to Solidity's `uint104` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 104 bits
                   */
                  function toUint104(uint256 value) internal pure returns (uint104) {
                      if (value > type(uint104).max) {
                          revert SafeCastOverflowedUintDowncast(104, value);
                      }
                      return uint104(value);
                  }
                  /**
                   * @dev Returns the downcasted uint96 from uint256, reverting on
                   * overflow (when the input is greater than largest uint96).
                   *
                   * Counterpart to Solidity's `uint96` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 96 bits
                   */
                  function toUint96(uint256 value) internal pure returns (uint96) {
                      if (value > type(uint96).max) {
                          revert SafeCastOverflowedUintDowncast(96, value);
                      }
                      return uint96(value);
                  }
                  /**
                   * @dev Returns the downcasted uint88 from uint256, reverting on
                   * overflow (when the input is greater than largest uint88).
                   *
                   * Counterpart to Solidity's `uint88` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 88 bits
                   */
                  function toUint88(uint256 value) internal pure returns (uint88) {
                      if (value > type(uint88).max) {
                          revert SafeCastOverflowedUintDowncast(88, value);
                      }
                      return uint88(value);
                  }
                  /**
                   * @dev Returns the downcasted uint80 from uint256, reverting on
                   * overflow (when the input is greater than largest uint80).
                   *
                   * Counterpart to Solidity's `uint80` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 80 bits
                   */
                  function toUint80(uint256 value) internal pure returns (uint80) {
                      if (value > type(uint80).max) {
                          revert SafeCastOverflowedUintDowncast(80, value);
                      }
                      return uint80(value);
                  }
                  /**
                   * @dev Returns the downcasted uint72 from uint256, reverting on
                   * overflow (when the input is greater than largest uint72).
                   *
                   * Counterpart to Solidity's `uint72` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 72 bits
                   */
                  function toUint72(uint256 value) internal pure returns (uint72) {
                      if (value > type(uint72).max) {
                          revert SafeCastOverflowedUintDowncast(72, value);
                      }
                      return uint72(value);
                  }
                  /**
                   * @dev Returns the downcasted uint64 from uint256, reverting on
                   * overflow (when the input is greater than largest uint64).
                   *
                   * Counterpart to Solidity's `uint64` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 64 bits
                   */
                  function toUint64(uint256 value) internal pure returns (uint64) {
                      if (value > type(uint64).max) {
                          revert SafeCastOverflowedUintDowncast(64, value);
                      }
                      return uint64(value);
                  }
                  /**
                   * @dev Returns the downcasted uint56 from uint256, reverting on
                   * overflow (when the input is greater than largest uint56).
                   *
                   * Counterpart to Solidity's `uint56` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 56 bits
                   */
                  function toUint56(uint256 value) internal pure returns (uint56) {
                      if (value > type(uint56).max) {
                          revert SafeCastOverflowedUintDowncast(56, value);
                      }
                      return uint56(value);
                  }
                  /**
                   * @dev Returns the downcasted uint48 from uint256, reverting on
                   * overflow (when the input is greater than largest uint48).
                   *
                   * Counterpart to Solidity's `uint48` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 48 bits
                   */
                  function toUint48(uint256 value) internal pure returns (uint48) {
                      if (value > type(uint48).max) {
                          revert SafeCastOverflowedUintDowncast(48, value);
                      }
                      return uint48(value);
                  }
                  /**
                   * @dev Returns the downcasted uint40 from uint256, reverting on
                   * overflow (when the input is greater than largest uint40).
                   *
                   * Counterpart to Solidity's `uint40` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 40 bits
                   */
                  function toUint40(uint256 value) internal pure returns (uint40) {
                      if (value > type(uint40).max) {
                          revert SafeCastOverflowedUintDowncast(40, value);
                      }
                      return uint40(value);
                  }
                  /**
                   * @dev Returns the downcasted uint32 from uint256, reverting on
                   * overflow (when the input is greater than largest uint32).
                   *
                   * Counterpart to Solidity's `uint32` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 32 bits
                   */
                  function toUint32(uint256 value) internal pure returns (uint32) {
                      if (value > type(uint32).max) {
                          revert SafeCastOverflowedUintDowncast(32, value);
                      }
                      return uint32(value);
                  }
                  /**
                   * @dev Returns the downcasted uint24 from uint256, reverting on
                   * overflow (when the input is greater than largest uint24).
                   *
                   * Counterpart to Solidity's `uint24` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 24 bits
                   */
                  function toUint24(uint256 value) internal pure returns (uint24) {
                      if (value > type(uint24).max) {
                          revert SafeCastOverflowedUintDowncast(24, value);
                      }
                      return uint24(value);
                  }
                  /**
                   * @dev Returns the downcasted uint16 from uint256, reverting on
                   * overflow (when the input is greater than largest uint16).
                   *
                   * Counterpart to Solidity's `uint16` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 16 bits
                   */
                  function toUint16(uint256 value) internal pure returns (uint16) {
                      if (value > type(uint16).max) {
                          revert SafeCastOverflowedUintDowncast(16, value);
                      }
                      return uint16(value);
                  }
                  /**
                   * @dev Returns the downcasted uint8 from uint256, reverting on
                   * overflow (when the input is greater than largest uint8).
                   *
                   * Counterpart to Solidity's `uint8` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 8 bits
                   */
                  function toUint8(uint256 value) internal pure returns (uint8) {
                      if (value > type(uint8).max) {
                          revert SafeCastOverflowedUintDowncast(8, value);
                      }
                      return uint8(value);
                  }
                  /**
                   * @dev Converts a signed int256 into an unsigned uint256.
                   *
                   * Requirements:
                   *
                   * - input must be greater than or equal to 0.
                   */
                  function toUint256(int256 value) internal pure returns (uint256) {
                      if (value < 0) {
                          revert SafeCastOverflowedIntToUint(value);
                      }
                      return uint256(value);
                  }
                  /**
                   * @dev Returns the downcasted int248 from int256, reverting on
                   * overflow (when the input is less than smallest int248 or
                   * greater than largest int248).
                   *
                   * Counterpart to Solidity's `int248` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 248 bits
                   */
                  function toInt248(int256 value) internal pure returns (int248 downcasted) {
                      downcasted = int248(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(248, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int240 from int256, reverting on
                   * overflow (when the input is less than smallest int240 or
                   * greater than largest int240).
                   *
                   * Counterpart to Solidity's `int240` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 240 bits
                   */
                  function toInt240(int256 value) internal pure returns (int240 downcasted) {
                      downcasted = int240(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(240, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int232 from int256, reverting on
                   * overflow (when the input is less than smallest int232 or
                   * greater than largest int232).
                   *
                   * Counterpart to Solidity's `int232` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 232 bits
                   */
                  function toInt232(int256 value) internal pure returns (int232 downcasted) {
                      downcasted = int232(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(232, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int224 from int256, reverting on
                   * overflow (when the input is less than smallest int224 or
                   * greater than largest int224).
                   *
                   * Counterpart to Solidity's `int224` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 224 bits
                   */
                  function toInt224(int256 value) internal pure returns (int224 downcasted) {
                      downcasted = int224(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(224, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int216 from int256, reverting on
                   * overflow (when the input is less than smallest int216 or
                   * greater than largest int216).
                   *
                   * Counterpart to Solidity's `int216` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 216 bits
                   */
                  function toInt216(int256 value) internal pure returns (int216 downcasted) {
                      downcasted = int216(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(216, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int208 from int256, reverting on
                   * overflow (when the input is less than smallest int208 or
                   * greater than largest int208).
                   *
                   * Counterpart to Solidity's `int208` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 208 bits
                   */
                  function toInt208(int256 value) internal pure returns (int208 downcasted) {
                      downcasted = int208(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(208, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int200 from int256, reverting on
                   * overflow (when the input is less than smallest int200 or
                   * greater than largest int200).
                   *
                   * Counterpart to Solidity's `int200` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 200 bits
                   */
                  function toInt200(int256 value) internal pure returns (int200 downcasted) {
                      downcasted = int200(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(200, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int192 from int256, reverting on
                   * overflow (when the input is less than smallest int192 or
                   * greater than largest int192).
                   *
                   * Counterpart to Solidity's `int192` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 192 bits
                   */
                  function toInt192(int256 value) internal pure returns (int192 downcasted) {
                      downcasted = int192(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(192, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int184 from int256, reverting on
                   * overflow (when the input is less than smallest int184 or
                   * greater than largest int184).
                   *
                   * Counterpart to Solidity's `int184` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 184 bits
                   */
                  function toInt184(int256 value) internal pure returns (int184 downcasted) {
                      downcasted = int184(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(184, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int176 from int256, reverting on
                   * overflow (when the input is less than smallest int176 or
                   * greater than largest int176).
                   *
                   * Counterpart to Solidity's `int176` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 176 bits
                   */
                  function toInt176(int256 value) internal pure returns (int176 downcasted) {
                      downcasted = int176(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(176, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int168 from int256, reverting on
                   * overflow (when the input is less than smallest int168 or
                   * greater than largest int168).
                   *
                   * Counterpart to Solidity's `int168` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 168 bits
                   */
                  function toInt168(int256 value) internal pure returns (int168 downcasted) {
                      downcasted = int168(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(168, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int160 from int256, reverting on
                   * overflow (when the input is less than smallest int160 or
                   * greater than largest int160).
                   *
                   * Counterpart to Solidity's `int160` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 160 bits
                   */
                  function toInt160(int256 value) internal pure returns (int160 downcasted) {
                      downcasted = int160(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(160, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int152 from int256, reverting on
                   * overflow (when the input is less than smallest int152 or
                   * greater than largest int152).
                   *
                   * Counterpart to Solidity's `int152` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 152 bits
                   */
                  function toInt152(int256 value) internal pure returns (int152 downcasted) {
                      downcasted = int152(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(152, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int144 from int256, reverting on
                   * overflow (when the input is less than smallest int144 or
                   * greater than largest int144).
                   *
                   * Counterpart to Solidity's `int144` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 144 bits
                   */
                  function toInt144(int256 value) internal pure returns (int144 downcasted) {
                      downcasted = int144(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(144, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int136 from int256, reverting on
                   * overflow (when the input is less than smallest int136 or
                   * greater than largest int136).
                   *
                   * Counterpart to Solidity's `int136` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 136 bits
                   */
                  function toInt136(int256 value) internal pure returns (int136 downcasted) {
                      downcasted = int136(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(136, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int128 from int256, reverting on
                   * overflow (when the input is less than smallest int128 or
                   * greater than largest int128).
                   *
                   * Counterpart to Solidity's `int128` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 128 bits
                   */
                  function toInt128(int256 value) internal pure returns (int128 downcasted) {
                      downcasted = int128(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(128, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int120 from int256, reverting on
                   * overflow (when the input is less than smallest int120 or
                   * greater than largest int120).
                   *
                   * Counterpart to Solidity's `int120` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 120 bits
                   */
                  function toInt120(int256 value) internal pure returns (int120 downcasted) {
                      downcasted = int120(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(120, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int112 from int256, reverting on
                   * overflow (when the input is less than smallest int112 or
                   * greater than largest int112).
                   *
                   * Counterpart to Solidity's `int112` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 112 bits
                   */
                  function toInt112(int256 value) internal pure returns (int112 downcasted) {
                      downcasted = int112(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(112, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int104 from int256, reverting on
                   * overflow (when the input is less than smallest int104 or
                   * greater than largest int104).
                   *
                   * Counterpart to Solidity's `int104` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 104 bits
                   */
                  function toInt104(int256 value) internal pure returns (int104 downcasted) {
                      downcasted = int104(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(104, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int96 from int256, reverting on
                   * overflow (when the input is less than smallest int96 or
                   * greater than largest int96).
                   *
                   * Counterpart to Solidity's `int96` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 96 bits
                   */
                  function toInt96(int256 value) internal pure returns (int96 downcasted) {
                      downcasted = int96(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(96, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int88 from int256, reverting on
                   * overflow (when the input is less than smallest int88 or
                   * greater than largest int88).
                   *
                   * Counterpart to Solidity's `int88` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 88 bits
                   */
                  function toInt88(int256 value) internal pure returns (int88 downcasted) {
                      downcasted = int88(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(88, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int80 from int256, reverting on
                   * overflow (when the input is less than smallest int80 or
                   * greater than largest int80).
                   *
                   * Counterpart to Solidity's `int80` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 80 bits
                   */
                  function toInt80(int256 value) internal pure returns (int80 downcasted) {
                      downcasted = int80(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(80, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int72 from int256, reverting on
                   * overflow (when the input is less than smallest int72 or
                   * greater than largest int72).
                   *
                   * Counterpart to Solidity's `int72` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 72 bits
                   */
                  function toInt72(int256 value) internal pure returns (int72 downcasted) {
                      downcasted = int72(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(72, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int64 from int256, reverting on
                   * overflow (when the input is less than smallest int64 or
                   * greater than largest int64).
                   *
                   * Counterpart to Solidity's `int64` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 64 bits
                   */
                  function toInt64(int256 value) internal pure returns (int64 downcasted) {
                      downcasted = int64(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(64, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int56 from int256, reverting on
                   * overflow (when the input is less than smallest int56 or
                   * greater than largest int56).
                   *
                   * Counterpart to Solidity's `int56` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 56 bits
                   */
                  function toInt56(int256 value) internal pure returns (int56 downcasted) {
                      downcasted = int56(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(56, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int48 from int256, reverting on
                   * overflow (when the input is less than smallest int48 or
                   * greater than largest int48).
                   *
                   * Counterpart to Solidity's `int48` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 48 bits
                   */
                  function toInt48(int256 value) internal pure returns (int48 downcasted) {
                      downcasted = int48(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(48, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int40 from int256, reverting on
                   * overflow (when the input is less than smallest int40 or
                   * greater than largest int40).
                   *
                   * Counterpart to Solidity's `int40` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 40 bits
                   */
                  function toInt40(int256 value) internal pure returns (int40 downcasted) {
                      downcasted = int40(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(40, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int32 from int256, reverting on
                   * overflow (when the input is less than smallest int32 or
                   * greater than largest int32).
                   *
                   * Counterpart to Solidity's `int32` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 32 bits
                   */
                  function toInt32(int256 value) internal pure returns (int32 downcasted) {
                      downcasted = int32(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(32, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int24 from int256, reverting on
                   * overflow (when the input is less than smallest int24 or
                   * greater than largest int24).
                   *
                   * Counterpart to Solidity's `int24` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 24 bits
                   */
                  function toInt24(int256 value) internal pure returns (int24 downcasted) {
                      downcasted = int24(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(24, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int16 from int256, reverting on
                   * overflow (when the input is less than smallest int16 or
                   * greater than largest int16).
                   *
                   * Counterpart to Solidity's `int16` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 16 bits
                   */
                  function toInt16(int256 value) internal pure returns (int16 downcasted) {
                      downcasted = int16(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(16, value);
                      }
                  }
                  /**
                   * @dev Returns the downcasted int8 from int256, reverting on
                   * overflow (when the input is less than smallest int8 or
                   * greater than largest int8).
                   *
                   * Counterpart to Solidity's `int8` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 8 bits
                   */
                  function toInt8(int256 value) internal pure returns (int8 downcasted) {
                      downcasted = int8(value);
                      if (downcasted != value) {
                          revert SafeCastOverflowedIntDowncast(8, value);
                      }
                  }
                  /**
                   * @dev Converts an unsigned uint256 into a signed int256.
                   *
                   * Requirements:
                   *
                   * - input must be less than or equal to maxInt256.
                   */
                  function toInt256(uint256 value) internal pure returns (int256) {
                      // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
                      if (value > uint256(type(int256).max)) {
                          revert SafeCastOverflowedUintToInt(value);
                      }
                      return int256(value);
                  }
                  /**
                   * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
                   */
                  function toUint(bool b) internal pure returns (uint256 u) {
                      assembly ("memory-safe") {
                          u := iszero(iszero(b))
                      }
                  }
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.26;
              abstract contract Oracle {
                  address public immutable token;
                  string public name;
                  constructor(address _token, string memory _name) {
                      token = _token;
                      name = _name;
                  }
                  function getPrice() external view virtual returns (uint256 price) {}
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Interface of the ERC-165 standard, as defined in the
               * https://eips.ethereum.org/EIPS/eip-165[ERC].
               *
               * 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[ERC 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);
              }
              

              File 4 of 8: Token
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.26;
              import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
              import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
              contract Token is ERC20, AccessControl {
                  bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
                  bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
                  constructor(
                      string memory _name,
                      string memory _symbol
                  ) ERC20(_name, _symbol) {
                      _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
                  }
                  function mint(address _to, uint256 _amount) external onlyRole(MINTER_ROLE) {
                      _mint(_to, _amount);
                  }
                  function burn(
                      address _from,
                      uint256 _amount
                  ) external onlyRole(BURNER_ROLE) {
                      _burn(_from, _amount);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol)
              pragma solidity ^0.8.20;
              import {IERC20} from "./IERC20.sol";
              import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
              import {Context} from "../../utils/Context.sol";
              import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
              /**
               * @dev Implementation of the {IERC20} interface.
               *
               * This implementation is agnostic to the way tokens are created. This means
               * that a supply mechanism has to be added in a derived contract using {_mint}.
               *
               * TIP: For a detailed writeup see our guide
               * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
               * to implement supply mechanisms].
               *
               * The default value of {decimals} is 18. To change this, you should override
               * this function so it returns a different value.
               *
               * We have followed general OpenZeppelin Contracts guidelines: functions revert
               * instead returning `false` on failure. This behavior is nonetheless
               * conventional and does not conflict with the expectations of ERC-20
               * applications.
               */
              abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
                  mapping(address account => uint256) private _balances;
                  mapping(address account => mapping(address spender => uint256)) private _allowances;
                  uint256 private _totalSupply;
                  string private _name;
                  string private _symbol;
                  /**
                   * @dev Sets the values for {name} and {symbol}.
                   *
                   * All two of these values are immutable: they can only be set once during
                   * construction.
                   */
                  constructor(string memory name_, string memory symbol_) {
                      _name = name_;
                      _symbol = symbol_;
                  }
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() public view virtual returns (string memory) {
                      return _name;
                  }
                  /**
                   * @dev Returns the symbol of the token, usually a shorter version of the
                   * name.
                   */
                  function symbol() public view virtual returns (string memory) {
                      return _symbol;
                  }
                  /**
                   * @dev Returns the number of decimals used to get its user representation.
                   * For example, if `decimals` equals `2`, a balance of `505` tokens should
                   * be displayed to a user as `5.05` (`505 / 10 ** 2`).
                   *
                   * Tokens usually opt for a value of 18, imitating the relationship between
                   * Ether and Wei. This is the default value returned by this function, unless
                   * it's overridden.
                   *
                   * NOTE: This information is only used for _display_ purposes: it in
                   * no way affects any of the arithmetic of the contract, including
                   * {IERC20-balanceOf} and {IERC20-transfer}.
                   */
                  function decimals() public view virtual returns (uint8) {
                      return 18;
                  }
                  /**
                   * @dev See {IERC20-totalSupply}.
                   */
                  function totalSupply() public view virtual returns (uint256) {
                      return _totalSupply;
                  }
                  /**
                   * @dev See {IERC20-balanceOf}.
                   */
                  function balanceOf(address account) public view virtual returns (uint256) {
                      return _balances[account];
                  }
                  /**
                   * @dev See {IERC20-transfer}.
                   *
                   * Requirements:
                   *
                   * - `to` cannot be the zero address.
                   * - the caller must have a balance of at least `value`.
                   */
                  function transfer(address to, uint256 value) public virtual returns (bool) {
                      address owner = _msgSender();
                      _transfer(owner, to, value);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-allowance}.
                   */
                  function allowance(address owner, address spender) public view virtual returns (uint256) {
                      return _allowances[owner][spender];
                  }
                  /**
                   * @dev See {IERC20-approve}.
                   *
                   * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
                   * `transferFrom`. This is semantically equivalent to an infinite approval.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   */
                  function approve(address spender, uint256 value) public virtual returns (bool) {
                      address owner = _msgSender();
                      _approve(owner, spender, value);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-transferFrom}.
                   *
                   * Skips emitting an {Approval} event indicating an allowance update. This is not
                   * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
                   *
                   * 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:
                   *
                   * ```solidity
                   * 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
              // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
              pragma solidity ^0.8.20;
              import {IAccessControl} from "./IAccessControl.sol";
              import {Context} from "../utils/Context.sol";
              import {ERC165} from "../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 account => bool) hasRole;
                      bytes32 adminRole;
                  }
                  mapping(bytes32 role => RoleData) private _roles;
                  bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                  /**
                   * @dev Modifier that checks that an account has a specific role. Reverts
                   * with an {AccessControlUnauthorizedAccount} error including the required role.
                   */
                  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 returns (bool) {
                      return _roles[role].hasRole[account];
                  }
                  /**
                   * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
                   * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
                   */
                  function _checkRole(bytes32 role) internal view virtual {
                      _checkRole(role, _msgSender());
                  }
                  /**
                   * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
                   * is missing `role`.
                   */
                  function _checkRole(bytes32 role, address account) internal view virtual {
                      if (!hasRole(role, account)) {
                          revert AccessControlUnauthorizedAccount(account, role);
                      }
                  }
                  /**
                   * @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 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 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 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 `callerConfirmation`.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function renounceRole(bytes32 role, address callerConfirmation) public virtual {
                      if (callerConfirmation != _msgSender()) {
                          revert AccessControlBadConfirmation();
                      }
                      _revokeRole(role, callerConfirmation);
                  }
                  /**
                   * @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 Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
                   *
                   * Internal function without access restriction.
                   *
                   * May emit a {RoleGranted} event.
                   */
                  function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
                      if (!hasRole(role, account)) {
                          _roles[role].hasRole[account] = true;
                          emit RoleGranted(role, account, _msgSender());
                          return true;
                      } else {
                          return false;
                      }
                  }
                  /**
                   * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
                   *
                   * Internal function without access restriction.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
                      if (hasRole(role, account)) {
                          _roles[role].hasRole[account] = false;
                          emit RoleRevoked(role, account, _msgSender());
                          return true;
                      } else {
                          return false;
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Interface of the ERC-20 standard as defined in the ERC.
               */
              interface IERC20 {
                  /**
                   * @dev Emitted when `value` tokens are moved from one account (`from`) to
                   * another (`to`).
                   *
                   * Note that `value` may be zero.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  /**
                   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                   * a call to {approve}. `value` is the new allowance.
                   */
                  event Approval(address indexed owner, address indexed spender, uint256 value);
                  /**
                   * @dev Returns the value of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
                  /**
                   * @dev Returns the value of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
                  /**
                   * @dev Moves a `value` amount of tokens from the caller's account to `to`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address to, uint256 value) external returns (bool);
                  /**
                   * @dev Returns the remaining number of tokens that `spender` will be
                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                   * zero by default.
                   *
                   * This value changes when {approve} or {transferFrom} are called.
                   */
                  function allowance(address owner, address spender) external view returns (uint256);
                  /**
                   * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
                   * caller's tokens.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                   * that someone may use both the old and the new allowance by unfortunate
                   * transaction ordering. One possible solution to mitigate this race
                   * condition is to first reduce the spender's allowance to 0 and set the
                   * desired value afterwards:
                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address spender, uint256 value) external returns (bool);
                  /**
                   * @dev Moves a `value` amount of tokens from `from` to `to` using the
                   * allowance mechanism. `value` is then deducted from the caller's
                   * allowance.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(address from, address to, uint256 value) external returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
              pragma solidity ^0.8.20;
              import {IERC20} from "../IERC20.sol";
              /**
               * @dev Interface for the optional metadata functions from the ERC-20 standard.
               */
              interface IERC20Metadata is IERC20 {
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() external view returns (string memory);
                  /**
                   * @dev Returns the symbol of the token.
                   */
                  function symbol() external view returns (string memory);
                  /**
                   * @dev Returns the decimals places of the token.
                   */
                  function decimals() external view returns (uint8);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
                  function _contextSuffixLength() internal view virtual returns (uint256) {
                      return 0;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Standard ERC-20 Errors
               * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 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 ERC-721 Errors
               * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
               */
              interface IERC721Errors {
                  /**
                   * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-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 ERC-1155 Errors
               * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 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
              // OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev External interface of AccessControl declared to support ERC-165 detection.
               */
              interface IAccessControl {
                  /**
                   * @dev The `account` is missing a role.
                   */
                  error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
                  /**
                   * @dev The caller of a function is not the expected one.
                   *
                   * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
                   */
                  error AccessControlBadConfirmation();
                  /**
                   * @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.
                   */
                  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. This account bears the admin role (for the granted role).
                   * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
                   */
                  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 `callerConfirmation`.
                   */
                  function renounceRole(bytes32 role, address callerConfirmation) external;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)
              pragma solidity ^0.8.20;
              import {IERC165} from "./IERC165.sol";
              /**
               * @dev Implementation of the {IERC165} interface.
               *
               * Contracts that want to implement ERC-165 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);
               * }
               * ```
               */
              abstract contract ERC165 is IERC165 {
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
                      return interfaceId == type(IERC165).interfaceId;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Interface of the ERC-165 standard, as defined in the
               * https://eips.ethereum.org/EIPS/eip-165[ERC].
               *
               * 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[ERC 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);
              }
              

              File 5 of 8: OracleConfigurator
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.26;
              import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
              import {Oracle} from "./Oracle.sol";
              import "../Errors.sol";
              contract OracleConfigurator is AccessControl {
                  bytes32 public constant ORACLE_MANAGER_ROLE =
                      keccak256("ORACLE_MANAGER_ROLE");
                  mapping(address => address) public oracles;
                  event OracleUpdated(address oldOracle, address newOracle);
                  constructor() {
                      _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
                  }
                  function updateOracle(
                      address _token,
                      address _oracle
                  ) external onlyRole(ORACLE_MANAGER_ROLE) {
                      if (_token == address(0)) revert InvalidToken();
                      if (_oracle == address(0)) revert InvalidOracle();
                      emit OracleUpdated(oracles[_token], _oracle);
                      oracles[_token] = _oracle;
                  }
                  function getPrice(address _token) external view returns (uint256 price) {
                      address oracle = oracles[_token];
                      if (_token == address(0) || oracle == address(0)) revert InvalidToken();
                      price = Oracle(oracle).getPrice();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
              pragma solidity ^0.8.20;
              import {IAccessControl} from "./IAccessControl.sol";
              import {Context} from "../utils/Context.sol";
              import {ERC165} from "../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 account => bool) hasRole;
                      bytes32 adminRole;
                  }
                  mapping(bytes32 role => RoleData) private _roles;
                  bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                  /**
                   * @dev Modifier that checks that an account has a specific role. Reverts
                   * with an {AccessControlUnauthorizedAccount} error including the required role.
                   */
                  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 returns (bool) {
                      return _roles[role].hasRole[account];
                  }
                  /**
                   * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
                   * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
                   */
                  function _checkRole(bytes32 role) internal view virtual {
                      _checkRole(role, _msgSender());
                  }
                  /**
                   * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
                   * is missing `role`.
                   */
                  function _checkRole(bytes32 role, address account) internal view virtual {
                      if (!hasRole(role, account)) {
                          revert AccessControlUnauthorizedAccount(account, role);
                      }
                  }
                  /**
                   * @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 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 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 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 `callerConfirmation`.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function renounceRole(bytes32 role, address callerConfirmation) public virtual {
                      if (callerConfirmation != _msgSender()) {
                          revert AccessControlBadConfirmation();
                      }
                      _revokeRole(role, callerConfirmation);
                  }
                  /**
                   * @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 Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
                   *
                   * Internal function without access restriction.
                   *
                   * May emit a {RoleGranted} event.
                   */
                  function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
                      if (!hasRole(role, account)) {
                          _roles[role].hasRole[account] = true;
                          emit RoleGranted(role, account, _msgSender());
                          return true;
                      } else {
                          return false;
                      }
                  }
                  /**
                   * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
                   *
                   * Internal function without access restriction.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
                      if (hasRole(role, account)) {
                          _roles[role].hasRole[account] = false;
                          emit RoleRevoked(role, account, _msgSender());
                          return true;
                      } else {
                          return false;
                      }
                  }
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.26;
              abstract contract Oracle {
                  address public immutable token;
                  string public name;
                  constructor(address _token, string memory _name) {
                      token = _token;
                      name = _name;
                  }
                  function getPrice() external view virtual returns (uint256 price) {}
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.26;
              error InvalidToken();
              error InvalidAsset();
              error InsufficientBalance();
              error InvalidOracle();
              error InvalidPrice();
              error InvalidArrayLength();
              error DepositCapped();
              error DepositPaused();
              error ZeroShares();
              error NoRequestingShares();
              error NoClaimableRedeem();
              error ZeroAddress();
              error InvalidRequest();
              error InvalidRequestToken();
              error CannotRemove();
              error InvalidDecimals();
              error InvalidFeeRate();
              error NoFeeRecipient();
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev External interface of AccessControl declared to support ERC-165 detection.
               */
              interface IAccessControl {
                  /**
                   * @dev The `account` is missing a role.
                   */
                  error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
                  /**
                   * @dev The caller of a function is not the expected one.
                   *
                   * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
                   */
                  error AccessControlBadConfirmation();
                  /**
                   * @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.
                   */
                  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. This account bears the admin role (for the granted role).
                   * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
                   */
                  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 `callerConfirmation`.
                   */
                  function renounceRole(bytes32 role, address callerConfirmation) external;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
                  function _contextSuffixLength() internal view virtual returns (uint256) {
                      return 0;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)
              pragma solidity ^0.8.20;
              import {IERC165} from "./IERC165.sol";
              /**
               * @dev Implementation of the {IERC165} interface.
               *
               * Contracts that want to implement ERC-165 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);
               * }
               * ```
               */
              abstract contract ERC165 is IERC165 {
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
                      return interfaceId == type(IERC165).interfaceId;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Interface of the ERC-165 standard, as defined in the
               * https://eips.ethereum.org/EIPS/eip-165[ERC].
               *
               * 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[ERC 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);
              }
              

              File 6 of 8: WETHOracle
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.26;
              import {Oracle} from "./Oracle.sol";
              contract WETHOracle is Oracle {
                  uint256 public constant D18 = 1e18;
                  constructor(address _token, string memory _name) Oracle(_token, _name) {
                      require(_token != address(0), "Invalid Address");
                      token = _token;
                      name = _name;
                  }
                  function getPrice() external view override returns (uint256 price) {
                      price = D18;
                  }
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.26;
              abstract contract Oracle {
                  address public immutable token;
                  string public name;
                  constructor(address _token, string memory _name) {
                      token = _token;
                      name = _name;
                  }
                  function getPrice() external view virtual returns (uint256 price) {}
              }
              

              File 7 of 8: StoneOracle
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.26;
              import {Oracle} from "./Oracle.sol";
              import "../Errors.sol";
              interface IStoneVault {
                  function currentSharePrice() external returns (uint256 price);
              }
              contract StoneOracle is Oracle {
                  uint256 public constant D18 = 1e18;
                  IStoneVault public immutable stoneVault;
                  uint256[] internal prices;
                  constructor(
                      address _token,
                      string memory _name,
                      address _stoneVault
                  ) Oracle(_token, _name) {
                      require(
                          _token != address(0) && _stoneVault != address(0),
                          "ZERO ADDRESS"
                      );
                      token = _token;
                      name = _name;
                      stoneVault = IStoneVault(_stoneVault);
                      updatePrice();
                  }
                  function getPrice() external view override returns (uint256 price) {
                      price = prices[prices.length - 1];
                  }
                  function updatePrice() public {
                      uint256 price = stoneVault.currentSharePrice();
                      if (price == 0) revert InvalidPrice();
                      prices.push(price);
                  }
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.26;
              abstract contract Oracle {
                  address public immutable token;
                  string public name;
                  constructor(address _token, string memory _name) {
                      token = _token;
                      name = _name;
                  }
                  function getPrice() external view virtual returns (uint256 price) {}
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.26;
              error InvalidToken();
              error InvalidAsset();
              error InsufficientBalance();
              error InvalidOracle();
              error InvalidPrice();
              error InvalidArrayLength();
              error DepositCapped();
              error DepositPaused();
              error ZeroShares();
              error NoRequestingShares();
              error NoClaimableRedeem();
              error ZeroAddress();
              error InvalidRequest();
              error InvalidRequestToken();
              error CannotRemove();
              error InvalidDecimals();
              error InvalidFeeRate();
              error NoFeeRecipient();
              

              File 8 of 8: Stone
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.21;
              import "@layerzerolabs/solidity-examples/contracts/token/oft/extension/BasedOFT.sol";
              import {Minter} from "./Minter.sol";
              contract Stone is BasedOFT {
                  uint256 public constant DAY_INTERVAL = 24 * 60 * 60;
                  address public minter;
                  uint16 public constant PT_FEED = 1;
                  uint16 public constant PT_SET_ENABLE = 2;
                  uint16 public constant PT_SET_CAP = 3;
                  uint256 public cap;
                  bool public enable = true;
                  mapping(uint256 => uint256) public quota;
                  event FeedToChain(
                      uint16 indexed dstChainId,
                      address indexed from,
                      bytes toAddress,
                      uint price
                  );
                  event SetCapFor(uint16 indexed dstChainId, bytes toAddress, uint cap);
                  event SetEnableFor(uint16 indexed dstChainId, bytes toAddress, bool flag);
                  constructor(
                      address _minter,
                      address _layerZeroEndpoint,
                      uint256 _cap
                  ) BasedOFT("StakeStone Ether", "STONE", _layerZeroEndpoint) {
                      minter = _minter;
                      cap = _cap;
                  }
                  modifier onlyMinter() {
                      require(msg.sender == minter, "NM");
                      _;
                  }
                  function mint(address _to, uint256 _amount) external onlyMinter {
                      _mint(_to, _amount);
                  }
                  function burn(address _from, uint256 _amount) external onlyMinter {
                      _burn(_from, _amount);
                  }
                  function sendFrom(
                      address _from,
                      uint16 _dstChainId,
                      bytes calldata _toAddress,
                      uint _amount,
                      address payable _refundAddress,
                      address _zroPaymentAddress,
                      bytes calldata _adapterParams
                  ) public payable override(IOFTCore, OFTCore) {
                      require(enable, "invalid");
                      uint256 id;
                      assembly {
                          id := chainid()
                      }
                      require(id != _dstChainId, "same chain");
                      uint256 day = block.timestamp / DAY_INTERVAL;
                      require(_amount + quota[day] <= cap, "Exceed cap");
                      quota[day] = quota[day] + _amount;
                      super.sendFrom(
                          _from,
                          _dstChainId,
                          _toAddress,
                          _amount,
                          _refundAddress,
                          _zroPaymentAddress,
                          _adapterParams
                      );
                  }
                  function updatePrice(
                      uint16 _dstChainId,
                      bytes memory _toAddress
                  ) external payable returns (uint256 price) {
                      require(enable, "invalid");
                      uint256 id;
                      assembly {
                          id := chainid()
                      }
                      require(id != _dstChainId, "same chain");
                      price = tokenPrice();
                      bytes memory lzPayload = abi.encode(
                          PT_FEED,
                          _toAddress,
                          price,
                          block.timestamp
                      );
                      _lzSend(
                          _dstChainId,
                          lzPayload,
                          payable(msg.sender),
                          address(0),
                          bytes(""),
                          msg.value
                      );
                      emit FeedToChain(_dstChainId, msg.sender, _toAddress, price);
                  }
                  function setEnableFor(
                      uint16 _dstChainId,
                      bool _flag,
                      bytes memory _toAddress
                  ) external payable onlyOwner {
                      uint256 id;
                      assembly {
                          id := chainid()
                      }
                      if (_dstChainId == id) {
                          enable = _flag;
                          emit SetEnableFor(
                              _dstChainId,
                              abi.encodePacked(address(this)),
                              enable
                          );
                          return;
                      }
                      bytes memory lzPayload = abi.encode(PT_SET_ENABLE, _toAddress, _flag);
                      _lzSend(
                          _dstChainId,
                          lzPayload,
                          payable(msg.sender),
                          address(0),
                          bytes(""),
                          msg.value
                      );
                      emit SetEnableFor(_dstChainId, _toAddress, _flag);
                  }
                  function setCapFor(
                      uint16 _dstChainId,
                      uint256 _cap,
                      bytes memory _toAddress
                  ) external payable onlyOwner {
                      uint256 id;
                      assembly {
                          id := chainid()
                      }
                      if (_dstChainId == id) {
                          cap = _cap;
                          emit SetCapFor(_dstChainId, abi.encodePacked(address(this)), cap);
                          return;
                      }
                      bytes memory lzPayload = abi.encode(PT_SET_CAP, _toAddress, _cap);
                      _lzSend(
                          _dstChainId,
                          lzPayload,
                          payable(msg.sender),
                          address(0),
                          bytes(""),
                          msg.value
                      );
                      emit SetCapFor(_dstChainId, _toAddress, _cap);
                  }
                  function tokenPrice() public returns (uint256 price) {
                      price = Minter(minter).getTokenPrice();
                  }
                  function getQuota() external view returns (uint256) {
                      uint256 amount = quota[block.timestamp / DAY_INTERVAL];
                      if (cap > amount && enable) {
                          return cap - amount;
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.21;
              import {Stone} from "./Stone.sol";
              import {StoneVault} from "../StoneVault.sol";
              contract Minter {
                  // TODO: governable upgrade
                  address public stone;
                  address payable public vault;
                  modifier onlyVault() {
                      require(msg.sender == vault, "not vault");
                      _;
                  }
                  constructor(address _stone, address payable _vault) {
                      stone = _stone;
                      vault = _vault;
                  }
                  function mint(address _to, uint256 _amount) external onlyVault {
                      Stone(stone).mint(_to, _amount);
                  }
                  function burn(address _from, uint256 _amount) external onlyVault {
                      Stone(stone).burn(_from, _amount);
                  }
                  function setNewVault(address _vault) external onlyVault {
                      vault = payable(_vault);
                  }
                  function getTokenPrice() public returns (uint256 price) {
                      price = StoneVault(vault).currentSharePrice();
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.21;
              import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
              import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
              import {Strategy} from "./Strategy.sol";
              import {AssetsVault} from "../AssetsVault.sol";
              contract StrategyController {
                  using EnumerableSet for EnumerableSet.AddressSet;
                  uint256 internal constant ONE_HUNDRED_PERCENT = 1e6;
                  address public stoneVault;
                  address payable public immutable assetsVault;
                  EnumerableSet.AddressSet private strategies;
                  mapping(address => uint256) public ratios;
                  struct StrategyDiff {
                      address strategy;
                      bool isDeposit;
                      uint256 amount;
                  }
                  modifier onlyVault() {
                      require(stoneVault == msg.sender, "not vault");
                      _;
                  }
                  constructor(
                      address payable _assetsVault,
                      address[] memory _strategies,
                      uint256[] memory _ratios
                  ) {
                      require(_assetsVault != address(0), "ZERO ADDRESS");
                      uint256 length = _strategies.length;
                      for (uint256 i; i < length; i++) {
                          require(_strategies[i] != address(0), "ZERO ADDRESS");
                      }
                      stoneVault = msg.sender;
                      assetsVault = _assetsVault;
                      _initStrategies(_strategies, _ratios);
                  }
                  function onlyRebaseStrategies() external {
                      _rebase(0, 0);
                  }
                  function forceWithdraw(
                      uint256 _amount
                  ) external onlyVault returns (uint256 actualAmount) {
                      uint256 balanceBeforeRepay = address(this).balance;
                      if (balanceBeforeRepay >= _amount) {
                          _repayToVault();
                          actualAmount = balanceBeforeRepay;
                      } else {
                          actualAmount =
                              _forceWithdraw(_amount - balanceBeforeRepay) +
                              balanceBeforeRepay;
                      }
                  }
                  function setStrategies(
                      address[] memory _strategies,
                      uint256[] memory _ratios
                  ) external onlyVault {
                      _setStrategies(_strategies, _ratios);
                  }
                  function addStrategy(address _strategy) external onlyVault {
                      require(!strategies.contains(_strategy), "already exist");
                      strategies.add(_strategy);
                  }
                  function rebaseStrategies(
                      uint256 _in,
                      uint256 _out
                  ) external payable onlyVault {
                      _rebase(_in, _out);
                  }
                  function destroyStrategy(address _strategy) external onlyVault {
                      _destoryStrategy(_strategy);
                  }
                  function _rebase(uint256 _in, uint256 _out) internal {
                      require(_in == 0 || _out == 0, "only deposit or withdraw");
                      if (_in != 0) {
                          AssetsVault(assetsVault).withdraw(address(this), _in);
                      }
                      uint256 total = getAllStrategyValidValue();
                      if (total < _out) {
                          total = 0;
                      } else {
                          total = total + _in - _out;
                      }
                      uint256 length = strategies.length();
                      StrategyDiff[] memory diffs = new StrategyDiff[](length);
                      uint256 head;
                      uint256 tail = length - 1;
                      for (uint i; i < length; i++) {
                          address strategy = strategies.at(i);
                          if (ratios[strategy] == 0) {
                              _clearStrategy(strategy, true);
                              continue;
                          }
                          uint256 newPosition = (total * ratios[strategy]) /
                              ONE_HUNDRED_PERCENT;
                          uint256 position = getStrategyValidValue(strategy);
                          if (newPosition < position) {
                              diffs[head] = StrategyDiff(
                                  strategy,
                                  false,
                                  position - newPosition
                              );
                              head++;
                          } else if (newPosition > position) {
                              diffs[tail] = StrategyDiff(
                                  strategy,
                                  true,
                                  newPosition - position
                              );
                              if (tail != 0) {
                                  tail--;
                              }
                          }
                      }
                      length = diffs.length;
                      for (uint256 i; i < length; i++) {
                          StrategyDiff memory diff = diffs[i];
                          if (diff.amount == 0) {
                              continue;
                          }
                          if (diff.isDeposit) {
                              if (address(this).balance < diff.amount) {
                                  diff.amount = address(this).balance;
                              }
                              _depositToStrategy(diff.strategy, diff.amount);
                          } else {
                              _withdrawFromStrategy(diff.strategy, diff.amount);
                          }
                      }
                      _repayToVault();
                  }
                  function _repayToVault() internal {
                      if (address(this).balance != 0) {
                          TransferHelper.safeTransferETH(assetsVault, address(this).balance);
                      }
                  }
                  function _depositToStrategy(address _strategy, uint256 _amount) internal {
                      Strategy(_strategy).deposit{value: _amount}();
                  }
                  function _withdrawFromStrategy(
                      address _strategy,
                      uint256 _amount
                  ) internal {
                      Strategy(_strategy).withdraw(_amount);
                  }
                  function _forceWithdraw(
                      uint256 _amount
                  ) internal returns (uint256 actualAmount) {
                      uint256 length = strategies.length();
                      for (uint i; i < length; i++) {
                          address strategy = strategies.at(i);
                          uint256 withAmount = (_amount * ratios[strategy]) /
                              ONE_HUNDRED_PERCENT;
                          if (withAmount != 0) {
                              actualAmount =
                                  Strategy(strategy).instantWithdraw(withAmount) +
                                  actualAmount;
                          }
                      }
                      _repayToVault();
                  }
                  function getStrategyValue(
                      address _strategy
                  ) public returns (uint256 _value) {
                      return Strategy(_strategy).getAllValue();
                  }
                  function getStrategyValidValue(
                      address _strategy
                  ) public returns (uint256 _value) {
                      return Strategy(_strategy).getInvestedValue();
                  }
                  function getStrategyPendingValue(
                      address _strategy
                  ) public returns (uint256 _value) {
                      return Strategy(_strategy).getPendingValue();
                  }
                  function getAllStrategiesValue() public returns (uint256 _value) {
                      uint256 length = strategies.length();
                      for (uint i; i < length; i++) {
                          _value = _value + getStrategyValue(strategies.at(i));
                      }
                  }
                  function getAllStrategyValidValue() public returns (uint256 _value) {
                      uint256 length = strategies.length();
                      for (uint i; i < length; i++) {
                          _value = _value + getStrategyValidValue(strategies.at(i));
                      }
                  }
                  function getAllStrategyPendingValue() public returns (uint256 _value) {
                      uint256 length = strategies.length();
                      for (uint i; i < length; i++) {
                          _value = _value + getStrategyPendingValue(strategies.at(i));
                      }
                  }
                  function getStrategies()
                      public
                      view
                      returns (address[] memory addrs, uint256[] memory portions)
                  {
                      uint256 length = strategies.length();
                      addrs = new address[](length);
                      portions = new uint256[](length);
                      for (uint256 i; i < length; i++) {
                          address addr = strategies.at(i);
                          addrs[i] = addr;
                          portions[i] = ratios[addr];
                      }
                  }
                  function _initStrategies(
                      address[] memory _strategies,
                      uint256[] memory _ratios
                  ) internal {
                      require(_strategies.length == _ratios.length, "invalid length");
                      uint256 totalRatio;
                      uint256 length = _strategies.length;
                      for (uint i; i < length; i++) {
                          strategies.add(_strategies[i]);
                          ratios[_strategies[i]] = _ratios[i];
                          totalRatio = totalRatio + _ratios[i];
                      }
                      require(totalRatio <= ONE_HUNDRED_PERCENT, "exceed 100%");
                  }
                  function _setStrategies(
                      address[] memory _strategies,
                      uint256[] memory _ratios
                  ) internal {
                      uint256 length = _strategies.length;
                      require(length == _ratios.length, "invalid length");
                      uint256 oldLength = strategies.length();
                      for (uint i; i < oldLength; i++) {
                          ratios[strategies.at(i)] = 0;
                      }
                      uint256 totalRatio;
                      for (uint i; i < length; i++) {
                          require(
                              Strategy(_strategies[i]).controller() == address(this),
                              "controller mismatch"
                          );
                          strategies.add(_strategies[i]);
                          ratios[_strategies[i]] = _ratios[i];
                          totalRatio = totalRatio + _ratios[i];
                      }
                      require(totalRatio <= ONE_HUNDRED_PERCENT, "exceed 100%");
                  }
                  function clearStrategy(address _strategy) public onlyVault {
                      _clearStrategy(_strategy, false);
                  }
                  function _clearStrategy(address _strategy, bool _isRebase) internal {
                      Strategy(_strategy).clear();
                      if (!_isRebase) {
                          _repayToVault();
                      }
                  }
                  function _destoryStrategy(address _strategy) internal {
                      require(_couldDestroyStrategy(_strategy), "still active");
                      strategies.remove(_strategy);
                      _repayToVault();
                  }
                  function _couldDestroyStrategy(
                      address _strategy
                  ) internal returns (bool status) {
                      return
                          ratios[_strategy] == 0 && Strategy(_strategy).getAllValue() < 1e4;
                  }
                  function setNewVault(address _vault) external onlyVault {
                      stoneVault = _vault;
                  }
                  receive() external payable {}
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.21;
              import {StrategyController} from "../strategies/StrategyController.sol";
              abstract contract Strategy {
                  address payable public immutable controller;
                  address public governance;
                  string public name;
                  modifier onlyGovernance() {
                      require(governance == msg.sender, "not governace");
                      _;
                  }
                  event TransferGovernance(address oldOwner, address newOwner);
                  constructor(address payable _controller, string memory _name) {
                      require(_controller != address(0), "ZERO ADDRESS");
                      governance = msg.sender;
                      controller = _controller;
                      name = _name;
                  }
                  modifier onlyController() {
                      require(controller == msg.sender, "not controller");
                      _;
                  }
                  function deposit() public payable virtual onlyController {}
                  function withdraw(
                      uint256 _amount
                  ) public virtual onlyController returns (uint256 actualAmount) {}
                  function instantWithdraw(
                      uint256 _amount
                  ) public virtual onlyController returns (uint256 actualAmount) {}
                  function clear() public virtual onlyController returns (uint256 amount) {}
                  function execPendingRequest(
                      uint256 _amount
                  ) public virtual returns (uint256 amount) {}
                  function getAllValue() public virtual returns (uint256 value) {}
                  function getPendingValue() public virtual returns (uint256 value) {}
                  function getInvestedValue() public virtual returns (uint256 value) {}
                  function checkPendingStatus()
                      public
                      virtual
                      returns (uint256 pending, uint256 executable)
                  {}
                  function setGovernance(address governance_) external onlyGovernance {
                      emit TransferGovernance(governance, governance_);
                      governance = governance_;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.21;
              library VaultMath {
                  uint256 internal constant DECIMALS = 18;
                  function assetToShares(
                      uint256 _assetAmount,
                      uint256 _assetPerShare
                  ) internal pure returns (uint256) {
                      require(_assetPerShare > 1, "Vault Lib: invalid assetPerShare");
                      return (_assetAmount * (10 ** DECIMALS)) / _assetPerShare;
                  }
                  function sharesToAsset(
                      uint256 _shares,
                      uint256 _assetPerShare
                  ) internal pure returns (uint256) {
                      require(_assetPerShare > 1, "Vault Lib: invalid assetPerShare");
                      return (_shares * _assetPerShare) / (10 ** DECIMALS);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.21;
              import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
              import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
              import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
              import {Minter} from "./token/Minter.sol";
              import {Stone} from "./token/Stone.sol";
              import {AssetsVault} from "./AssetsVault.sol";
              import {StrategyController} from "./strategies/StrategyController.sol";
              import {VaultMath} from "./libraries/VaultMath.sol";
              contract StoneVault is ReentrancyGuard, Ownable {
                  uint256 internal constant MULTIPLIER = 1e18;
                  uint256 internal constant ONE_HUNDRED_PERCENT = 1e6;
                  uint256 internal constant MAXMIUM_FEE_RATE = ONE_HUNDRED_PERCENT / 100; // 1%
                  uint256 internal constant MINIMUM_REBASE_INTERVAL = 7 * 24 * 60 * 60;
                  uint256 public constant VERSION = 1;
                  uint256 public rebaseTimeInterval = 24 * 60 * 60;
                  address public immutable minter;
                  address public immutable stone;
                  address payable public immutable strategyController;
                  address payable public immutable assetsVault;
                  address public proposal;
                  address public feeRecipient;
                  uint256 public latestRoundID;
                  uint256 public withdrawableAmountInPast;
                  uint256 public withdrawingSharesInPast;
                  uint256 public withdrawingSharesInRound;
                  uint256 public withdrawFeeRate;
                  uint256 public rebaseTime;
                  mapping(uint256 => uint256) public roundPricePerShare;
                  mapping(uint256 => uint256) public settlementTime;
                  mapping(address => UserReceipt) public userReceipts;
                  struct UserReceipt {
                      uint256 withdrawRound;
                      uint256 withdrawShares;
                      uint256 withdrawableAmount;
                  }
                  event Deposit(
                      address indexed account,
                      uint256 amount,
                      uint256 mint,
                      uint256 round
                  );
                  event InitiateWithdraw(
                      address indexed account,
                      uint256 shares,
                      uint256 round
                  );
                  event CancelWithdraw(
                      address indexed account,
                      uint256 amount,
                      uint256 round
                  );
                  event Withdrawn(address indexed account, uint256 amount, uint256 round);
                  event WithdrawnFromStrategy(
                      address indexed account,
                      uint256 amount,
                      uint256 actualAmount,
                      uint256 round
                  );
                  event RollToNextRound(
                      uint256 round,
                      uint256 vaultIn,
                      uint256 vaultOut,
                      uint256 sharePrice
                  );
                  event StragetyAdded(address strategy);
                  event StragetyDestroyed(address strategy);
                  event StragetyCleared(address strategy);
                  event PortfolioConfigUpdated(address[] strategies, uint256[] ratios);
                  event FeeCharged(address indexed account, uint256 amount);
                  event SetWithdrawFeeRate(uint256 oldRate, uint256 newRate);
                  event SetFeeRecipient(address oldAddr, address newAddr);
                  event SetRebaseInterval(uint256 interval);
                  modifier onlyProposal() {
                      require(proposal == msg.sender, "not proposal");
                      _;
                  }
                  constructor(
                      address _minter,
                      address _proposal,
                      address payable _assetsVault,
                      address[] memory _strategies,
                      uint256[] memory _ratios
                  ) {
                      require(
                          _minter != address(0) &&
                              _proposal != address(0) &&
                              _assetsVault != address(0),
                          "ZERO ADDRESS"
                      );
                      uint256 length = _strategies.length;
                      for (uint256 i; i < length; i++) {
                          require(_strategies[i] != address(0), "ZERO ADDRESS");
                      }
                      minter = _minter;
                      proposal = _proposal;
                      assetsVault = _assetsVault;
                      feeRecipient = msg.sender;
                      StrategyController controller = new StrategyController(
                          _assetsVault,
                          _strategies,
                          _ratios
                      );
                      strategyController = payable(address(controller));
                      stone = Minter(_minter).stone();
                      roundPricePerShare[0] = MULTIPLIER;
                      latestRoundID = 0;
                  }
                  function deposit()
                      external
                      payable
                      nonReentrant
                      returns (uint256 mintAmount)
                  {
                      mintAmount = _depositFor(msg.value, msg.sender);
                  }
                  function depositFor(
                      address _user
                  ) external payable nonReentrant returns (uint256 mintAmount) {
                      mintAmount = _depositFor(msg.value, _user);
                  }
                  function _depositFor(
                      uint256 _amount,
                      address _user
                  ) internal returns (uint256 mintAmount) {
                      require(_amount != 0, "too small");
                      uint256 sharePrice;
                      uint256 currSharePrice = currentSharePrice();
                      if (latestRoundID == 0) {
                          sharePrice = MULTIPLIER;
                      } else {
                          uint256 latestSharePrice = roundPricePerShare[latestRoundID - 1];
                          sharePrice = latestSharePrice > currSharePrice
                              ? latestSharePrice
                              : currSharePrice;
                      }
                      mintAmount = (_amount * MULTIPLIER) / sharePrice;
                      AssetsVault(assetsVault).deposit{value: address(this).balance}();
                      Minter(minter).mint(_user, mintAmount);
                      emit Deposit(_user, _amount, mintAmount, latestRoundID);
                  }
                  function requestWithdraw(uint256 _shares) external nonReentrant {
                      require(_shares != 0, "too small");
                      require(latestRoundID != 0, "should withdraw instantly");
                      Stone stoneToken = Stone(stone);
                      Minter stoneMinter = Minter(minter);
                      require(stoneToken.balanceOf(msg.sender) >= _shares, "exceed balance");
                      TransferHelper.safeTransferFrom(
                          stone,
                          msg.sender,
                          address(this),
                          _shares
                      );
                      withdrawingSharesInRound = withdrawingSharesInRound + _shares;
                      UserReceipt storage receipt = userReceipts[msg.sender];
                      if (receipt.withdrawRound == latestRoundID) {
                          receipt.withdrawShares = receipt.withdrawShares + _shares;
                      } else if (receipt.withdrawRound == 0) {
                          receipt.withdrawShares = _shares;
                          receipt.withdrawRound = latestRoundID;
                      } else {
                          // Withdraw previous round share first
                          uint256 withdrawAmount = VaultMath.sharesToAsset(
                              receipt.withdrawShares,
                              roundPricePerShare[receipt.withdrawRound]
                          );
                          stoneMinter.burn(address(this), receipt.withdrawShares);
                          withdrawingSharesInPast =
                              withdrawingSharesInPast -
                              receipt.withdrawShares;
                          receipt.withdrawShares = _shares;
                          receipt.withdrawableAmount =
                              receipt.withdrawableAmount +
                              withdrawAmount;
                          receipt.withdrawRound = latestRoundID;
                      }
                      emit InitiateWithdraw(msg.sender, _shares, latestRoundID);
                  }
                  function cancelWithdraw(uint256 _shares) external nonReentrant {
                      require(_shares != 0, "too small");
                      UserReceipt storage receipt = userReceipts[msg.sender];
                      require(receipt.withdrawRound == latestRoundID, "no pending withdraw");
                      require(receipt.withdrawShares >= _shares, "exceed pending withdraw");
                      receipt.withdrawShares = receipt.withdrawShares - _shares;
                      TransferHelper.safeTransfer(stone, msg.sender, _shares);
                      if (receipt.withdrawShares == 0) {
                          receipt.withdrawRound = 0;
                      }
                      withdrawingSharesInRound = withdrawingSharesInRound - _shares;
                      emit CancelWithdraw(msg.sender, _shares, latestRoundID);
                  }
                  function instantWithdraw(
                      uint256 _amount,
                      uint256 _shares
                  ) external nonReentrant returns (uint256 actualWithdrawn) {
                      require(_amount != 0 || _shares != 0, "too small");
                      AssetsVault aVault = AssetsVault(assetsVault);
                      Minter stoneMinter = Minter(minter);
                      (uint256 idleAmount, ) = getVaultAvailableAmount();
                      if (_amount != 0) {
                          UserReceipt storage receipt = userReceipts[msg.sender];
                          if (
                              receipt.withdrawRound != latestRoundID &&
                              receipt.withdrawRound != 0
                          ) {
                              // Withdraw previous round share first
                              uint256 withdrawAmount = VaultMath.sharesToAsset(
                                  receipt.withdrawShares,
                                  roundPricePerShare[receipt.withdrawRound]
                              );
                              stoneMinter.burn(address(this), receipt.withdrawShares);
                              withdrawingSharesInPast =
                                  withdrawingSharesInPast -
                                  receipt.withdrawShares;
                              receipt.withdrawShares = 0;
                              receipt.withdrawableAmount =
                                  receipt.withdrawableAmount +
                                  withdrawAmount;
                              receipt.withdrawRound = 0;
                          }
                          require(
                              receipt.withdrawableAmount >= _amount,
                              "exceed withdrawable"
                          );
                          receipt.withdrawableAmount = receipt.withdrawableAmount - _amount;
                          withdrawableAmountInPast = withdrawableAmountInPast - _amount;
                          actualWithdrawn = _amount;
                          emit Withdrawn(msg.sender, _amount, latestRoundID);
                      }
                      if (_shares != 0) {
                          uint256 sharePrice;
                          if (latestRoundID == 0) {
                              sharePrice = MULTIPLIER;
                          } else {
                              uint256 currSharePrice = currentSharePrice();
                              uint256 latestSharePrice = roundPricePerShare[
                                  latestRoundID - 1
                              ];
                              sharePrice = latestSharePrice < currSharePrice
                                  ? latestSharePrice
                                  : currSharePrice;
                          }
                          uint256 ethAmount = VaultMath.sharesToAsset(_shares, sharePrice);
                          stoneMinter.burn(msg.sender, _shares);
                          if (ethAmount <= idleAmount) {
                              actualWithdrawn = actualWithdrawn + ethAmount;
                              emit Withdrawn(msg.sender, ethAmount, latestRoundID);
                          } else {
                              actualWithdrawn = actualWithdrawn + idleAmount;
                              ethAmount = ethAmount - idleAmount;
                              StrategyController controller = StrategyController(
                                  strategyController
                              );
                              uint256 actualAmount = controller.forceWithdraw(ethAmount);
                              actualWithdrawn = actualWithdrawn + actualAmount;
                              emit WithdrawnFromStrategy(
                                  msg.sender,
                                  ethAmount,
                                  actualAmount,
                                  latestRoundID
                              );
                          }
                      }
                      require(aVault.getBalance() >= actualWithdrawn, "still need wait");
                      uint256 withFee;
                      if (withdrawFeeRate != 0) {
                          withFee = (actualWithdrawn * withdrawFeeRate) / ONE_HUNDRED_PERCENT;
                          aVault.withdraw(feeRecipient, withFee);
                          emit FeeCharged(msg.sender, withFee);
                      }
                      aVault.withdraw(msg.sender, actualWithdrawn - withFee);
                  }
                  function rollToNextRound() external {
                      require(
                          block.timestamp > rebaseTime + rebaseTimeInterval,
                          "already rebased"
                      );
                      StrategyController controller = StrategyController(strategyController);
                      AssetsVault aVault = AssetsVault(assetsVault);
                      uint256 previewSharePrice = currentSharePrice();
                      uint256 vaultBalance = aVault.getBalance();
                      uint256 amountToWithdraw = VaultMath.sharesToAsset(
                          withdrawingSharesInRound,
                          previewSharePrice
                      );
                      uint256 amountVaultNeed = withdrawableAmountInPast + amountToWithdraw;
                      uint256 allPendingValue = controller.getAllStrategyPendingValue();
                      uint256 vaultIn;
                      uint256 vaultOut;
                      if (vaultBalance > amountVaultNeed) {
                          vaultIn = vaultBalance - amountVaultNeed;
                      } else if (vaultBalance + allPendingValue < amountVaultNeed) {
                          vaultOut = amountVaultNeed - vaultBalance - allPendingValue;
                      }
                      controller.rebaseStrategies(vaultIn, vaultOut);
                      uint256 newSharePrice = currentSharePrice();
                      roundPricePerShare[latestRoundID] = previewSharePrice < newSharePrice
                          ? previewSharePrice
                          : newSharePrice;
                      settlementTime[latestRoundID] = block.timestamp;
                      latestRoundID = latestRoundID + 1;
                      withdrawingSharesInPast =
                          withdrawingSharesInPast +
                          withdrawingSharesInRound;
                      withdrawableAmountInPast =
                          withdrawableAmountInPast +
                          VaultMath.sharesToAsset(withdrawingSharesInRound, newSharePrice);
                      withdrawingSharesInRound = 0;
                      rebaseTime = block.timestamp;
                      emit RollToNextRound(latestRoundID, vaultIn, vaultOut, newSharePrice);
                  }
                  function addStrategy(address _strategy) external onlyProposal {
                      StrategyController controller = StrategyController(strategyController);
                      controller.addStrategy(_strategy);
                      emit StragetyAdded(_strategy);
                  }
                  function destroyStrategy(address _strategy) external onlyOwner {
                      StrategyController controller = StrategyController(strategyController);
                      controller.destroyStrategy(_strategy);
                      emit StragetyDestroyed(_strategy);
                  }
                  function clearStrategy(address _strategy) external onlyOwner {
                      StrategyController controller = StrategyController(strategyController);
                      controller.clearStrategy(_strategy);
                      emit StragetyCleared(_strategy);
                  }
                  function updatePortfolioConfig(
                      address[] memory _strategies,
                      uint256[] memory _ratios
                  ) external onlyProposal {
                      StrategyController controller = StrategyController(strategyController);
                      controller.setStrategies(_strategies, _ratios);
                      emit PortfolioConfigUpdated(_strategies, _ratios);
                  }
                  function updateProposal(address _proposal) external onlyProposal {
                      proposal = _proposal;
                  }
                  function migrateVault(address _vault) external onlyProposal {
                      Minter(minter).setNewVault(_vault);
                      AssetsVault(assetsVault).setNewVault(_vault);
                      StrategyController(strategyController).setNewVault(_vault);
                  }
                  function currentSharePrice() public returns (uint256 price) {
                      Stone stoneToken = Stone(stone);
                      uint256 totalStone = stoneToken.totalSupply();
                      if (
                          latestRoundID == 0 ||
                          totalStone == 0 ||
                          totalStone == withdrawingSharesInPast
                      ) {
                          return MULTIPLIER;
                      }
                      uint256 etherAmount = AssetsVault(assetsVault).getBalance() +
                          StrategyController(strategyController).getAllStrategiesValue() -
                          withdrawableAmountInPast;
                      uint256 activeShare = totalStone - withdrawingSharesInPast;
                      return (etherAmount * MULTIPLIER) / activeShare;
                  }
                  function getVaultAvailableAmount()
                      public
                      returns (uint256 idleAmount, uint256 investedAmount)
                  {
                      AssetsVault vault = AssetsVault(assetsVault);
                      if (vault.getBalance() > withdrawableAmountInPast) {
                          idleAmount = vault.getBalance() - withdrawableAmountInPast;
                      }
                      investedAmount = StrategyController(strategyController)
                          .getAllStrategyValidValue();
                  }
                  function setWithdrawFeeRate(uint256 _withdrawFeeRate) external onlyOwner {
                      require(_withdrawFeeRate <= MAXMIUM_FEE_RATE, "exceed maximum");
                      emit SetWithdrawFeeRate(withdrawFeeRate, _withdrawFeeRate);
                      withdrawFeeRate = _withdrawFeeRate;
                  }
                  function setFeeRecipient(address _feeRecipient) external onlyOwner {
                      require(_feeRecipient != address(0), "zero address");
                      emit SetFeeRecipient(feeRecipient, _feeRecipient);
                      feeRecipient = _feeRecipient;
                  }
                  function setRebaseInterval(uint256 _interval) external onlyOwner {
                      require(_interval <= MINIMUM_REBASE_INTERVAL, "invalid");
                      rebaseTimeInterval = _interval;
                      emit SetRebaseInterval(rebaseTimeInterval);
                  }
                  receive() external payable {}
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.21;
              import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
              contract AssetsVault {
                  address public stoneVault;
                  address public strategyController;
                  modifier onlyPermit() {
                      require(
                          stoneVault == msg.sender || strategyController == msg.sender,
                          "not permit"
                      );
                      _;
                  }
                  constructor(address _stoneVault, address _strategyController) {
                      require(
                          _stoneVault != address(0) && _strategyController != address(0),
                          "ZERO ADDRESS"
                      );
                      stoneVault = _stoneVault;
                      strategyController = _strategyController;
                  }
                  function deposit() external payable {
                      require(msg.value != 0, "too small");
                  }
                  function withdraw(address _to, uint256 _amount) external onlyPermit {
                      TransferHelper.safeTransferETH(_to, _amount);
                  }
                  function setNewVault(address _vault) external onlyPermit {
                      stoneVault = _vault;
                  }
                  function getBalance() external view returns (uint256 amount) {
                      amount = address(this).balance;
                  }
                  receive() external payable {}
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.6.0;
              import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
              library TransferHelper {
                  /// @notice Transfers tokens from the targeted address to the given destination
                  /// @notice Errors with 'STF' if transfer fails
                  /// @param token The contract address of the token to be transferred
                  /// @param from The originating address from which the tokens will be transferred
                  /// @param to The destination address of the transfer
                  /// @param value The amount to be transferred
                  function safeTransferFrom(
                      address token,
                      address from,
                      address to,
                      uint256 value
                  ) internal {
                      (bool success, bytes memory data) =
                          token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
                      require(success && (data.length == 0 || abi.decode(data, (bool))), 'STF');
                  }
                  /// @notice Transfers tokens from msg.sender to a recipient
                  /// @dev Errors with ST if transfer fails
                  /// @param token The contract address of the token which will be transferred
                  /// @param to The recipient of the transfer
                  /// @param value The value of the transfer
                  function safeTransfer(
                      address token,
                      address to,
                      uint256 value
                  ) internal {
                      (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
                      require(success && (data.length == 0 || abi.decode(data, (bool))), 'ST');
                  }
                  /// @notice Approves the stipulated contract to spend the given allowance in the given token
                  /// @dev Errors with 'SA' if transfer fails
                  /// @param token The contract address of the token to be approved
                  /// @param to The target of the approval
                  /// @param value The amount of the given token the target will be allowed to spend
                  function safeApprove(
                      address token,
                      address to,
                      uint256 value
                  ) internal {
                      (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
                      require(success && (data.length == 0 || abi.decode(data, (bool))), 'SA');
                  }
                  /// @notice Transfers ETH to the recipient address
                  /// @dev Fails with `STE`
                  /// @param to The destination of the transfer
                  /// @param value The value to be transferred
                  function safeTransferETH(address to, uint256 value) internal {
                      (bool success, ) = to.call{value: value}(new bytes(0));
                      require(success, 'STE');
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
              // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
              pragma solidity ^0.8.0;
              /**
               * @dev Library for managing
               * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
               * types.
               *
               * Sets have the following properties:
               *
               * - Elements are added, removed, and checked for existence in constant time
               * (O(1)).
               * - Elements are enumerated in O(n). No guarantees are made on the ordering.
               *
               * ```solidity
               * contract Example {
               *     // Add the library methods
               *     using EnumerableSet for EnumerableSet.AddressSet;
               *
               *     // Declare a set state variable
               *     EnumerableSet.AddressSet private mySet;
               * }
               * ```
               *
               * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
               * and `uint256` (`UintSet`) are supported.
               *
               * [WARNING]
               * ====
               * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
               * unusable.
               * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
               *
               * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
               * array of EnumerableSet.
               * ====
               */
              library EnumerableSet {
                  // To implement this library for multiple types with as little code
                  // repetition as possible, we write it in terms of a generic Set type with
                  // bytes32 values.
                  // The Set implementation uses private functions, and user-facing
                  // implementations (such as AddressSet) are just wrappers around the
                  // underlying Set.
                  // This means that we can only create new EnumerableSets for types that fit
                  // in bytes32.
                  struct Set {
                      // Storage of set values
                      bytes32[] _values;
                      // Position of the value in the `values` array, plus 1 because index 0
                      // means a value is not in the set.
                      mapping(bytes32 => uint256) _indexes;
                  }
                  /**
                   * @dev Add a value to a set. O(1).
                   *
                   * Returns true if the value was added to the set, that is if it was not
                   * already present.
                   */
                  function _add(Set storage set, bytes32 value) private returns (bool) {
                      if (!_contains(set, value)) {
                          set._values.push(value);
                          // The value is stored at length-1, but we add 1 to all indexes
                          // and use 0 as a sentinel value
                          set._indexes[value] = set._values.length;
                          return true;
                      } else {
                          return false;
                      }
                  }
                  /**
                   * @dev Removes a value from a set. O(1).
                   *
                   * Returns true if the value was removed from the set, that is if it was
                   * present.
                   */
                  function _remove(Set storage set, bytes32 value) private returns (bool) {
                      // We read and store the value's index to prevent multiple reads from the same storage slot
                      uint256 valueIndex = set._indexes[value];
                      if (valueIndex != 0) {
                          // Equivalent to contains(set, value)
                          // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                          // the array, and then remove the last element (sometimes called as 'swap and pop').
                          // This modifies the order of the array, as noted in {at}.
                          uint256 toDeleteIndex = valueIndex - 1;
                          uint256 lastIndex = set._values.length - 1;
                          if (lastIndex != toDeleteIndex) {
                              bytes32 lastValue = set._values[lastIndex];
                              // Move the last value to the index where the value to delete is
                              set._values[toDeleteIndex] = lastValue;
                              // Update the index for the moved value
                              set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
                          }
                          // Delete the slot where the moved value was stored
                          set._values.pop();
                          // Delete the index for the deleted slot
                          delete set._indexes[value];
                          return true;
                      } else {
                          return false;
                      }
                  }
                  /**
                   * @dev Returns true if the value is in the set. O(1).
                   */
                  function _contains(Set storage set, bytes32 value) private view returns (bool) {
                      return set._indexes[value] != 0;
                  }
                  /**
                   * @dev Returns the number of values on the set. O(1).
                   */
                  function _length(Set storage set) private view returns (uint256) {
                      return set._values.length;
                  }
                  /**
                   * @dev Returns the value stored at position `index` in the set. O(1).
                   *
                   * Note that there are no guarantees on the ordering of values inside the
                   * array, and it may change when more values are added or removed.
                   *
                   * Requirements:
                   *
                   * - `index` must be strictly less than {length}.
                   */
                  function _at(Set storage set, uint256 index) private view returns (bytes32) {
                      return set._values[index];
                  }
                  /**
                   * @dev Return the entire set in an array
                   *
                   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                   */
                  function _values(Set storage set) private view returns (bytes32[] memory) {
                      return set._values;
                  }
                  // Bytes32Set
                  struct Bytes32Set {
                      Set _inner;
                  }
                  /**
                   * @dev Add a value to a set. O(1).
                   *
                   * Returns true if the value was added to the set, that is if it was not
                   * already present.
                   */
                  function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
                      return _add(set._inner, value);
                  }
                  /**
                   * @dev Removes a value from a set. O(1).
                   *
                   * Returns true if the value was removed from the set, that is if it was
                   * present.
                   */
                  function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
                      return _remove(set._inner, value);
                  }
                  /**
                   * @dev Returns true if the value is in the set. O(1).
                   */
                  function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
                      return _contains(set._inner, value);
                  }
                  /**
                   * @dev Returns the number of values in the set. O(1).
                   */
                  function length(Bytes32Set storage set) internal view returns (uint256) {
                      return _length(set._inner);
                  }
                  /**
                   * @dev Returns the value stored at position `index` in the set. O(1).
                   *
                   * Note that there are no guarantees on the ordering of values inside the
                   * array, and it may change when more values are added or removed.
                   *
                   * Requirements:
                   *
                   * - `index` must be strictly less than {length}.
                   */
                  function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
                      return _at(set._inner, index);
                  }
                  /**
                   * @dev Return the entire set in an array
                   *
                   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                   */
                  function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
                      bytes32[] memory store = _values(set._inner);
                      bytes32[] memory result;
                      /// @solidity memory-safe-assembly
                      assembly {
                          result := store
                      }
                      return result;
                  }
                  // AddressSet
                  struct AddressSet {
                      Set _inner;
                  }
                  /**
                   * @dev Add a value to a set. O(1).
                   *
                   * Returns true if the value was added to the set, that is if it was not
                   * already present.
                   */
                  function add(AddressSet storage set, address value) internal returns (bool) {
                      return _add(set._inner, bytes32(uint256(uint160(value))));
                  }
                  /**
                   * @dev Removes a value from a set. O(1).
                   *
                   * Returns true if the value was removed from the set, that is if it was
                   * present.
                   */
                  function remove(AddressSet storage set, address value) internal returns (bool) {
                      return _remove(set._inner, bytes32(uint256(uint160(value))));
                  }
                  /**
                   * @dev Returns true if the value is in the set. O(1).
                   */
                  function contains(AddressSet storage set, address value) internal view returns (bool) {
                      return _contains(set._inner, bytes32(uint256(uint160(value))));
                  }
                  /**
                   * @dev Returns the number of values in the set. O(1).
                   */
                  function length(AddressSet storage set) internal view returns (uint256) {
                      return _length(set._inner);
                  }
                  /**
                   * @dev Returns the value stored at position `index` in the set. O(1).
                   *
                   * Note that there are no guarantees on the ordering of values inside the
                   * array, and it may change when more values are added or removed.
                   *
                   * Requirements:
                   *
                   * - `index` must be strictly less than {length}.
                   */
                  function at(AddressSet storage set, uint256 index) internal view returns (address) {
                      return address(uint160(uint256(_at(set._inner, index))));
                  }
                  /**
                   * @dev Return the entire set in an array
                   *
                   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                   */
                  function values(AddressSet storage set) internal view returns (address[] memory) {
                      bytes32[] memory store = _values(set._inner);
                      address[] memory result;
                      /// @solidity memory-safe-assembly
                      assembly {
                          result := store
                      }
                      return result;
                  }
                  // UintSet
                  struct UintSet {
                      Set _inner;
                  }
                  /**
                   * @dev Add a value to a set. O(1).
                   *
                   * Returns true if the value was added to the set, that is if it was not
                   * already present.
                   */
                  function add(UintSet storage set, uint256 value) internal returns (bool) {
                      return _add(set._inner, bytes32(value));
                  }
                  /**
                   * @dev Removes a value from a set. O(1).
                   *
                   * Returns true if the value was removed from the set, that is if it was
                   * present.
                   */
                  function remove(UintSet storage set, uint256 value) internal returns (bool) {
                      return _remove(set._inner, bytes32(value));
                  }
                  /**
                   * @dev Returns true if the value is in the set. O(1).
                   */
                  function contains(UintSet storage set, uint256 value) internal view returns (bool) {
                      return _contains(set._inner, bytes32(value));
                  }
                  /**
                   * @dev Returns the number of values in the set. O(1).
                   */
                  function length(UintSet storage set) internal view returns (uint256) {
                      return _length(set._inner);
                  }
                  /**
                   * @dev Returns the value stored at position `index` in the set. O(1).
                   *
                   * Note that there are no guarantees on the ordering of values inside the
                   * array, and it may change when more values are added or removed.
                   *
                   * Requirements:
                   *
                   * - `index` must be strictly less than {length}.
                   */
                  function at(UintSet storage set, uint256 index) internal view returns (uint256) {
                      return uint256(_at(set._inner, index));
                  }
                  /**
                   * @dev Return the entire set in an array
                   *
                   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                   */
                  function values(UintSet storage set) internal view returns (uint256[] memory) {
                      bytes32[] memory store = _values(set._inner);
                      uint256[] memory result;
                      /// @solidity memory-safe-assembly
                      assembly {
                          result := store
                      }
                      return result;
                  }
              }
              // 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 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/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;
                  }
              }
              // 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.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.0) (token/ERC20/ERC20.sol)
              pragma solidity ^0.8.0;
              import "./IERC20.sol";
              import "./extensions/IERC20Metadata.sol";
              import "../../utils/Context.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}.
               * For a generic mechanism see {ERC20PresetMinterPauser}.
               *
               * 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.
               *
               * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
               * functions have been added to mitigate the well-known issues around setting
               * allowances. See {IERC20-approve}.
               */
              contract ERC20 is Context, IERC20, IERC20Metadata {
                  mapping(address => uint256) private _balances;
                  mapping(address => mapping(address => uint256)) private _allowances;
                  uint256 private _totalSupply;
                  string private _name;
                  string private _symbol;
                  /**
                   * @dev Sets the values for {name} and {symbol}.
                   *
                   * All two of these values are immutable: they can only be set once during
                   * construction.
                   */
                  constructor(string memory name_, string memory symbol_) {
                      _name = name_;
                      _symbol = symbol_;
                  }
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() public view virtual override returns (string memory) {
                      return _name;
                  }
                  /**
                   * @dev Returns the symbol of the token, usually a shorter version of the
                   * name.
                   */
                  function symbol() public view virtual override 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 override returns (uint8) {
                      return 18;
                  }
                  /**
                   * @dev See {IERC20-totalSupply}.
                   */
                  function totalSupply() public view virtual override returns (uint256) {
                      return _totalSupply;
                  }
                  /**
                   * @dev See {IERC20-balanceOf}.
                   */
                  function balanceOf(address account) public view virtual override 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 `amount`.
                   */
                  function transfer(address to, uint256 amount) public virtual override returns (bool) {
                      address owner = _msgSender();
                      _transfer(owner, to, amount);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-allowance}.
                   */
                  function allowance(address owner, address spender) public view virtual override returns (uint256) {
                      return _allowances[owner][spender];
                  }
                  /**
                   * @dev See {IERC20-approve}.
                   *
                   * NOTE: If `amount` 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 amount) public virtual override returns (bool) {
                      address owner = _msgSender();
                      _approve(owner, spender, amount);
                      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 `amount`.
                   * - the caller must have allowance for ``from``'s tokens of at least
                   * `amount`.
                   */
                  function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
                      address spender = _msgSender();
                      _spendAllowance(from, spender, amount);
                      _transfer(from, to, amount);
                      return true;
                  }
                  /**
                   * @dev Atomically increases the allowance granted to `spender` by the caller.
                   *
                   * This is an alternative to {approve} that can be used as a mitigation for
                   * problems described in {IERC20-approve}.
                   *
                   * Emits an {Approval} event indicating the updated allowance.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   */
                  function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                      address owner = _msgSender();
                      _approve(owner, spender, allowance(owner, spender) + addedValue);
                      return true;
                  }
                  /**
                   * @dev Atomically decreases the allowance granted to `spender` by the caller.
                   *
                   * This is an alternative to {approve} that can be used as a mitigation for
                   * problems described in {IERC20-approve}.
                   *
                   * Emits an {Approval} event indicating the updated allowance.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   * - `spender` must have allowance for the caller of at least
                   * `subtractedValue`.
                   */
                  function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                      address owner = _msgSender();
                      uint256 currentAllowance = allowance(owner, spender);
                      require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
                      unchecked {
                          _approve(owner, spender, currentAllowance - subtractedValue);
                      }
                      return true;
                  }
                  /**
                   * @dev Moves `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.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `from` must have a balance of at least `amount`.
                   */
                  function _transfer(address from, address to, uint256 amount) internal virtual {
                      require(from != address(0), "ERC20: transfer from the zero address");
                      require(to != address(0), "ERC20: transfer to the zero address");
                      _beforeTokenTransfer(from, to, amount);
                      uint256 fromBalance = _balances[from];
                      require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
                      unchecked {
                          _balances[from] = fromBalance - amount;
                          // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
                          // decrementing then incrementing.
                          _balances[to] += amount;
                      }
                      emit Transfer(from, to, amount);
                      _afterTokenTransfer(from, to, amount);
                  }
                  /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                   * the total supply.
                   *
                   * Emits a {Transfer} event with `from` set to the zero address.
                   *
                   * Requirements:
                   *
                   * - `account` cannot be the zero address.
                   */
                  function _mint(address account, uint256 amount) internal virtual {
                      require(account != address(0), "ERC20: mint to the zero address");
                      _beforeTokenTransfer(address(0), account, amount);
                      _totalSupply += amount;
                      unchecked {
                          // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
                          _balances[account] += amount;
                      }
                      emit Transfer(address(0), account, amount);
                      _afterTokenTransfer(address(0), account, amount);
                  }
                  /**
                   * @dev Destroys `amount` tokens from `account`, reducing the
                   * total supply.
                   *
                   * Emits a {Transfer} event with `to` set to the zero address.
                   *
                   * Requirements:
                   *
                   * - `account` cannot be the zero address.
                   * - `account` must have at least `amount` tokens.
                   */
                  function _burn(address account, uint256 amount) internal virtual {
                      require(account != address(0), "ERC20: burn from the zero address");
                      _beforeTokenTransfer(account, address(0), amount);
                      uint256 accountBalance = _balances[account];
                      require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
                      unchecked {
                          _balances[account] = accountBalance - amount;
                          // Overflow not possible: amount <= accountBalance <= totalSupply.
                          _totalSupply -= amount;
                      }
                      emit Transfer(account, address(0), amount);
                      _afterTokenTransfer(account, address(0), amount);
                  }
                  /**
                   * @dev Sets `amount` 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.
                   */
                  function _approve(address owner, address spender, uint256 amount) internal virtual {
                      require(owner != address(0), "ERC20: approve from the zero address");
                      require(spender != address(0), "ERC20: approve to the zero address");
                      _allowances[owner][spender] = amount;
                      emit Approval(owner, spender, amount);
                  }
                  /**
                   * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
                   *
                   * Does not update the allowance amount in case of infinite allowance.
                   * Revert if not enough allowance is available.
                   *
                   * Might emit an {Approval} event.
                   */
                  function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
                      uint256 currentAllowance = allowance(owner, spender);
                      if (currentAllowance != type(uint256).max) {
                          require(currentAllowance >= amount, "ERC20: insufficient allowance");
                          unchecked {
                              _approve(owner, spender, currentAllowance - amount);
                          }
                      }
                  }
                  /**
                   * @dev Hook that is called before any transfer of tokens. This includes
                   * minting and burning.
                   *
                   * Calling conditions:
                   *
                   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                   * will be transferred to `to`.
                   * - when `from` is zero, `amount` tokens will be minted for `to`.
                   * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
                   * - `from` and `to` are never both zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
                  /**
                   * @dev Hook that is called after any transfer of tokens. This includes
                   * minting and burning.
                   *
                   * Calling conditions:
                   *
                   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                   * has been transferred to `to`.
                   * - when `from` is zero, `amount` tokens have been minted for `to`.
                   * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
                   * - `from` and `to` are never both zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Contract module that helps prevent reentrant calls to a function.
               *
               * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
               * available, which can be applied to functions to make sure there are no nested
               * (reentrant) calls to them.
               *
               * Note that because there is a single `nonReentrant` guard, functions marked as
               * `nonReentrant` may not call one another. This can be worked around by making
               * those functions `private`, and then adding `external` `nonReentrant` entry
               * points to them.
               *
               * TIP: If you would like to learn more about reentrancy and alternative ways
               * to protect against it, check out our blog post
               * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
               */
              abstract contract ReentrancyGuard {
                  // Booleans are more expensive than uint256 or any type that takes up a full
                  // word because each write operation emits an extra SLOAD to first read the
                  // slot's contents, replace the bits taken up by the boolean, and then write
                  // back. This is the compiler's defense against contract upgrades and
                  // pointer aliasing, and it cannot be disabled.
                  // The values being non-zero value makes deployment a bit more expensive,
                  // but in exchange the refund on every call to nonReentrant will be lower in
                  // amount. Since refunds are capped to a percentage of the total
                  // transaction's gas, it is best to keep them low in cases like this one, to
                  // increase the likelihood of the full refund coming into effect.
                  uint256 private constant _NOT_ENTERED = 1;
                  uint256 private constant _ENTERED = 2;
                  uint256 private _status;
                  constructor() {
                      _status = _NOT_ENTERED;
                  }
                  /**
                   * @dev Prevents a contract from calling itself, directly or indirectly.
                   * Calling a `nonReentrant` function from another `nonReentrant`
                   * function is not supported. It is possible to prevent this from happening
                   * by making the `nonReentrant` function external, and making it call a
                   * `private` function that does the actual work.
                   */
                  modifier nonReentrant() {
                      _nonReentrantBefore();
                      _;
                      _nonReentrantAfter();
                  }
                  function _nonReentrantBefore() private {
                      // On the first call to nonReentrant, _status will be _NOT_ENTERED
                      require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
                      // Any calls to nonReentrant after this point will fail
                      _status = _ENTERED;
                  }
                  function _nonReentrantAfter() private {
                      // By storing the original value once again, a refund is triggered (see
                      // https://eips.ethereum.org/EIPS/eip-2200)
                      _status = _NOT_ENTERED;
                  }
                  /**
                   * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
                   * `nonReentrant` function in the call stack.
                   */
                  function _reentrancyGuardEntered() internal view returns (bool) {
                      return _status == _ENTERED;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
              pragma solidity ^0.8.0;
              import "../utils/Context.sol";
              /**
               * @dev Contract module which provides a basic access control mechanism, where
               * there is an account (an owner) that can be granted exclusive access to
               * specific functions.
               *
               * By default, the owner account will be the one that deploys the contract. This
               * can later be changed with {transferOwnership}.
               *
               * This module is used through inheritance. It will make available the modifier
               * `onlyOwner`, which can be applied to your functions to restrict their use to
               * the owner.
               */
              abstract contract Ownable is Context {
                  address private _owner;
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  /**
                   * @dev Initializes the contract setting the deployer as the initial owner.
                   */
                  constructor() {
                      _transferOwnership(_msgSender());
                  }
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      _checkOwner();
                      _;
                  }
                  /**
                   * @dev Returns the address of the current owner.
                   */
                  function owner() public view virtual returns (address) {
                      return _owner;
                  }
                  /**
                   * @dev Throws if the sender is not the owner.
                   */
                  function _checkOwner() internal view virtual {
                      require(owner() == _msgSender(), "Ownable: caller is not the owner");
                  }
                  /**
                   * @dev Leaves the contract without owner. It will not be possible to call
                   * `onlyOwner` functions. Can only be called by the current owner.
                   *
                   * NOTE: Renouncing ownership will leave the contract without an owner,
                   * thereby disabling any functionality that is only available to the owner.
                   */
                  function renounceOwnership() public virtual onlyOwner {
                      _transferOwnership(address(0));
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Can only be called by the current owner.
                   */
                  function transferOwnership(address newOwner) public virtual onlyOwner {
                      require(newOwner != address(0), "Ownable: new owner is the zero address");
                      _transferOwnership(newOwner);
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Internal function without access restriction.
                   */
                  function _transferOwnership(address newOwner) internal virtual {
                      address oldOwner = _owner;
                      _owner = newOwner;
                      emit OwnershipTransferred(oldOwner, newOwner);
                  }
              }
              // SPDX-License-Identifier: MIT OR Apache-2.0
              pragma solidity >=0.7.6;
              library ExcessivelySafeCall {
                  uint256 constant LOW_28_MASK =
                  0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
                  /// @notice Use when you _really_ really _really_ don't trust the called
                  /// contract. This prevents the called contract from causing reversion of
                  /// the caller in as many ways as we can.
                  /// @dev The main difference between this and a solidity low-level call is
                  /// that we limit the number of bytes that the callee can cause to be
                  /// copied to caller memory. This prevents stupid things like malicious
                  /// contracts returning 10,000,000 bytes causing a local OOG when copying
                  /// to memory.
                  /// @param _target The address to call
                  /// @param _gas The amount of gas to forward to the remote contract
                  /// @param _maxCopy The maximum number of bytes of returndata to copy
                  /// to memory.
                  /// @param _calldata The data to send to the remote contract
                  /// @return success and returndata, as `.call()`. Returndata is capped to
                  /// `_maxCopy` bytes.
                  function excessivelySafeCall(
                      address _target,
                      uint256 _gas,
                      uint16 _maxCopy,
                      bytes memory _calldata
                  ) internal returns (bool, bytes memory) {
                      // set up for assembly call
                      uint256 _toCopy;
                      bool _success;
                      bytes memory _returnData = new bytes(_maxCopy);
                      // dispatch message to recipient
                      // by assembly calling "handle" function
                      // we call via assembly to avoid memcopying a very large returndata
                      // returned by a malicious contract
                      assembly {
                          _success := call(
                          _gas, // gas
                          _target, // recipient
                          0, // ether value
                          add(_calldata, 0x20), // inloc
                          mload(_calldata), // inlen
                          0, // outloc
                          0 // outlen
                          )
                      // limit our copy to 256 bytes
                          _toCopy := returndatasize()
                          if gt(_toCopy, _maxCopy) {
                              _toCopy := _maxCopy
                          }
                      // Store the length of the copied bytes
                          mstore(_returnData, _toCopy)
                      // copy the bytes from returndata[0:_toCopy]
                          returndatacopy(add(_returnData, 0x20), 0, _toCopy)
                      }
                      return (_success, _returnData);
                  }
                  /// @notice Use when you _really_ really _really_ don't trust the called
                  /// contract. This prevents the called contract from causing reversion of
                  /// the caller in as many ways as we can.
                  /// @dev The main difference between this and a solidity low-level call is
                  /// that we limit the number of bytes that the callee can cause to be
                  /// copied to caller memory. This prevents stupid things like malicious
                  /// contracts returning 10,000,000 bytes causing a local OOG when copying
                  /// to memory.
                  /// @param _target The address to call
                  /// @param _gas The amount of gas to forward to the remote contract
                  /// @param _maxCopy The maximum number of bytes of returndata to copy
                  /// to memory.
                  /// @param _calldata The data to send to the remote contract
                  /// @return success and returndata, as `.call()`. Returndata is capped to
                  /// `_maxCopy` bytes.
                  function excessivelySafeStaticCall(
                      address _target,
                      uint256 _gas,
                      uint16 _maxCopy,
                      bytes memory _calldata
                  ) internal view returns (bool, bytes memory) {
                      // set up for assembly call
                      uint256 _toCopy;
                      bool _success;
                      bytes memory _returnData = new bytes(_maxCopy);
                      // dispatch message to recipient
                      // by assembly calling "handle" function
                      // we call via assembly to avoid memcopying a very large returndata
                      // returned by a malicious contract
                      assembly {
                          _success := staticcall(
                          _gas, // gas
                          _target, // recipient
                          add(_calldata, 0x20), // inloc
                          mload(_calldata), // inlen
                          0, // outloc
                          0 // outlen
                          )
                      // limit our copy to 256 bytes
                          _toCopy := returndatasize()
                          if gt(_toCopy, _maxCopy) {
                              _toCopy := _maxCopy
                          }
                      // Store the length of the copied bytes
                          mstore(_returnData, _toCopy)
                      // copy the bytes from returndata[0:_toCopy]
                          returndatacopy(add(_returnData, 0x20), 0, _toCopy)
                      }
                      return (_success, _returnData);
                  }
                  /**
                   * @notice Swaps function selectors in encoded contract calls
                   * @dev Allows reuse of encoded calldata for functions with identical
                   * argument types but different names. It simply swaps out the first 4 bytes
                   * for the new selector. This function modifies memory in place, and should
                   * only be used with caution.
                   * @param _newSelector The new 4-byte selector
                   * @param _buf The encoded contract args
                   */
                  function swapSelector(bytes4 _newSelector, bytes memory _buf)
                  internal
                  pure
                  {
                      require(_buf.length >= 4);
                      uint256 _mask = LOW_28_MASK;
                      assembly {
                      // load the first word of
                          let _word := mload(add(_buf, 0x20))
                      // mask out the top 4 bytes
                      // /x
                          _word := and(_word, _mask)
                          _word := or(_newSelector, _word)
                          mstore(add(_buf, 0x20), _word)
                      }
                  }
              }
              // SPDX-License-Identifier: Unlicense
              /*
               * @title Solidity Bytes Arrays Utils
               * @author Gonçalo Sá <[email protected]>
               *
               * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
               *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
               */
              pragma solidity >=0.8.0 <0.9.0;
              library BytesLib {
                  function concat(
                      bytes memory _preBytes,
                      bytes memory _postBytes
                  )
                  internal
                  pure
                  returns (bytes memory)
                  {
                      bytes memory tempBytes;
                      assembly {
                      // Get a location of some free memory and store it in tempBytes as
                      // Solidity does for memory variables.
                          tempBytes := mload(0x40)
                      // Store the length of the first bytes array at the beginning of
                      // the memory for tempBytes.
                          let length := mload(_preBytes)
                          mstore(tempBytes, length)
                      // Maintain a memory counter for the current write location in the
                      // temp bytes array by adding the 32 bytes for the array length to
                      // the starting location.
                          let mc := add(tempBytes, 0x20)
                      // Stop copying when the memory counter reaches the length of the
                      // first bytes array.
                          let end := add(mc, length)
                          for {
                          // Initialize a copy counter to the start of the _preBytes data,
                          // 32 bytes into its memory.
                              let cc := add(_preBytes, 0x20)
                          } lt(mc, end) {
                          // Increase both counters by 32 bytes each iteration.
                              mc := add(mc, 0x20)
                              cc := add(cc, 0x20)
                          } {
                          // Write the _preBytes data into the tempBytes memory 32 bytes
                          // at a time.
                              mstore(mc, mload(cc))
                          }
                      // Add the length of _postBytes to the current length of tempBytes
                      // and store it as the new length in the first 32 bytes of the
                      // tempBytes memory.
                          length := mload(_postBytes)
                          mstore(tempBytes, add(length, mload(tempBytes)))
                      // Move the memory counter back from a multiple of 0x20 to the
                      // actual end of the _preBytes data.
                          mc := end
                      // Stop copying when the memory counter reaches the new combined
                      // length of the arrays.
                          end := add(mc, length)
                          for {
                              let cc := add(_postBytes, 0x20)
                          } lt(mc, end) {
                              mc := add(mc, 0x20)
                              cc := add(cc, 0x20)
                          } {
                              mstore(mc, mload(cc))
                          }
                      // Update the free-memory pointer by padding our last write location
                      // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
                      // next 32 byte block, then round down to the nearest multiple of
                      // 32. If the sum of the length of the two arrays is zero then add
                      // one before rounding down to leave a blank 32 bytes (the length block with 0).
                          mstore(0x40, and(
                          add(add(end, iszero(add(length, mload(_preBytes)))), 31),
                          not(31) // Round down to the nearest 32 bytes.
                          ))
                      }
                      return tempBytes;
                  }
                  function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
                      assembly {
                      // Read the first 32 bytes of _preBytes storage, which is the length
                      // of the array. (We don't need to use the offset into the slot
                      // because arrays use the entire slot.)
                          let fslot := sload(_preBytes.slot)
                      // Arrays of 31 bytes or less have an even value in their slot,
                      // while longer arrays have an odd value. The actual length is
                      // the slot divided by two for odd values, and the lowest order
                      // byte divided by two for even values.
                      // If the slot is even, bitwise and the slot with 255 and divide by
                      // two to get the length. If the slot is odd, bitwise and the slot
                      // with -1 and divide by two.
                          let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
                          let mlength := mload(_postBytes)
                          let newlength := add(slength, mlength)
                      // slength can contain both the length and contents of the array
                      // if length < 32 bytes so let's prepare for that
                      // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                          switch add(lt(slength, 32), lt(newlength, 32))
                          case 2 {
                          // Since the new array still fits in the slot, we just need to
                          // update the contents of the slot.
                          // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                              sstore(
                              _preBytes.slot,
                              // all the modifications to the slot are inside this
                              // next block
                              add(
                              // we can just add to the slot contents because the
                              // bytes we want to change are the LSBs
                              fslot,
                              add(
                              mul(
                              div(
                              // load the bytes from memory
                              mload(add(_postBytes, 0x20)),
                              // zero all bytes to the right
                              exp(0x100, sub(32, mlength))
                              ),
                              // and now shift left the number of bytes to
                              // leave space for the length in the slot
                              exp(0x100, sub(32, newlength))
                              ),
                              // increase length by the double of the memory
                              // bytes length
                              mul(mlength, 2)
                              )
                              )
                              )
                          }
                          case 1 {
                          // The stored value fits in the slot, but the combined value
                          // will exceed it.
                          // get the keccak hash to get the contents of the array
                              mstore(0x0, _preBytes.slot)
                              let sc := add(keccak256(0x0, 0x20), div(slength, 32))
                          // save new length
                              sstore(_preBytes.slot, add(mul(newlength, 2), 1))
                          // The contents of the _postBytes array start 32 bytes into
                          // the structure. Our first read should obtain the `submod`
                          // bytes that can fit into the unused space in the last word
                          // of the stored array. To get this, we read 32 bytes starting
                          // from `submod`, so the data we read overlaps with the array
                          // contents by `submod` bytes. Masking the lowest-order
                          // `submod` bytes allows us to add that value directly to the
                          // stored value.
                              let submod := sub(32, slength)
                              let mc := add(_postBytes, submod)
                              let end := add(_postBytes, mlength)
                              let mask := sub(exp(0x100, submod), 1)
                              sstore(
                              sc,
                              add(
                              and(
                              fslot,
                              0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
                              ),
                              and(mload(mc), mask)
                              )
                              )
                              for {
                                  mc := add(mc, 0x20)
                                  sc := add(sc, 1)
                              } lt(mc, end) {
                                  sc := add(sc, 1)
                                  mc := add(mc, 0x20)
                              } {
                                  sstore(sc, mload(mc))
                              }
                              mask := exp(0x100, sub(mc, end))
                              sstore(sc, mul(div(mload(mc), mask), mask))
                          }
                          default {
                          // get the keccak hash to get the contents of the array
                              mstore(0x0, _preBytes.slot)
                          // Start copying to the last used word of the stored array.
                              let sc := add(keccak256(0x0, 0x20), div(slength, 32))
                          // save new length
                              sstore(_preBytes.slot, add(mul(newlength, 2), 1))
                          // Copy over the first `submod` bytes of the new data as in
                          // case 1 above.
                              let slengthmod := mod(slength, 32)
                              let mlengthmod := mod(mlength, 32)
                              let submod := sub(32, slengthmod)
                              let mc := add(_postBytes, submod)
                              let end := add(_postBytes, mlength)
                              let mask := sub(exp(0x100, submod), 1)
                              sstore(sc, add(sload(sc), and(mload(mc), mask)))
                              for {
                                  sc := add(sc, 1)
                                  mc := add(mc, 0x20)
                              } lt(mc, end) {
                                  sc := add(sc, 1)
                                  mc := add(mc, 0x20)
                              } {
                                  sstore(sc, mload(mc))
                              }
                              mask := exp(0x100, sub(mc, end))
                              sstore(sc, mul(div(mload(mc), mask), mask))
                          }
                      }
                  }
                  function slice(
                      bytes memory _bytes,
                      uint256 _start,
                      uint256 _length
                  )
                  internal
                  pure
                  returns (bytes memory)
                  {
                      require(_length + 31 >= _length, "slice_overflow");
                      require(_bytes.length >= _start + _length, "slice_outOfBounds");
                      bytes memory tempBytes;
                      assembly {
                          switch iszero(_length)
                          case 0 {
                          // Get a location of some free memory and store it in tempBytes as
                          // Solidity does for memory variables.
                              tempBytes := mload(0x40)
                          // The first word of the slice result is potentially a partial
                          // word read from the original array. To read it, we calculate
                          // the length of that partial word and start copying that many
                          // bytes into the array. The first word we copy will start with
                          // data we don't care about, but the last `lengthmod` bytes will
                          // land at the beginning of the contents of the new array. When
                          // we're done copying, we overwrite the full first word with
                          // the actual length of the slice.
                              let lengthmod := and(_length, 31)
                          // The multiplication in the next line is necessary
                          // because when slicing multiples of 32 bytes (lengthmod == 0)
                          // the following copy loop was copying the origin's length
                          // and then ending prematurely not copying everything it should.
                              let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                              let end := add(mc, _length)
                              for {
                              // The multiplication in the next line has the same exact purpose
                              // as the one above.
                                  let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                              } lt(mc, end) {
                                  mc := add(mc, 0x20)
                                  cc := add(cc, 0x20)
                              } {
                                  mstore(mc, mload(cc))
                              }
                              mstore(tempBytes, _length)
                          //update free-memory pointer
                          //allocating the array padded to 32 bytes like the compiler does now
                              mstore(0x40, and(add(mc, 31), not(31)))
                          }
                          //if we want a zero-length slice let's just return a zero-length array
                          default {
                              tempBytes := mload(0x40)
                          //zero out the 32 bytes slice we are about to return
                          //we need to do it because Solidity does not garbage collect
                              mstore(tempBytes, 0)
                              mstore(0x40, add(tempBytes, 0x20))
                          }
                      }
                      return tempBytes;
                  }
                  function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
                      require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
                      address tempAddress;
                      assembly {
                          tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
                      }
                      return tempAddress;
                  }
                  function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
                      require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
                      uint8 tempUint;
                      assembly {
                          tempUint := mload(add(add(_bytes, 0x1), _start))
                      }
                      return tempUint;
                  }
                  function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
                      require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
                      uint16 tempUint;
                      assembly {
                          tempUint := mload(add(add(_bytes, 0x2), _start))
                      }
                      return tempUint;
                  }
                  function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
                      require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
                      uint32 tempUint;
                      assembly {
                          tempUint := mload(add(add(_bytes, 0x4), _start))
                      }
                      return tempUint;
                  }
                  function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
                      require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
                      uint64 tempUint;
                      assembly {
                          tempUint := mload(add(add(_bytes, 0x8), _start))
                      }
                      return tempUint;
                  }
                  function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
                      require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
                      uint96 tempUint;
                      assembly {
                          tempUint := mload(add(add(_bytes, 0xc), _start))
                      }
                      return tempUint;
                  }
                  function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
                      require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
                      uint128 tempUint;
                      assembly {
                          tempUint := mload(add(add(_bytes, 0x10), _start))
                      }
                      return tempUint;
                  }
                  function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
                      require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
                      uint256 tempUint;
                      assembly {
                          tempUint := mload(add(add(_bytes, 0x20), _start))
                      }
                      return tempUint;
                  }
                  function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
                      require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
                      bytes32 tempBytes32;
                      assembly {
                          tempBytes32 := mload(add(add(_bytes, 0x20), _start))
                      }
                      return tempBytes32;
                  }
                  function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
                      bool success = true;
                      assembly {
                          let length := mload(_preBytes)
                      // if lengths don't match the arrays are not equal
                          switch eq(length, mload(_postBytes))
                          case 1 {
                          // cb is a circuit breaker in the for loop since there's
                          //  no said feature for inline assembly loops
                          // cb = 1 - don't breaker
                          // cb = 0 - break
                              let cb := 1
                              let mc := add(_preBytes, 0x20)
                              let end := add(mc, length)
                              for {
                                  let cc := add(_postBytes, 0x20)
                              // the next line is the loop condition:
                              // while(uint256(mc < end) + cb == 2)
                              } eq(add(lt(mc, end), cb), 2) {
                                  mc := add(mc, 0x20)
                                  cc := add(cc, 0x20)
                              } {
                              // if any of these checks fails then arrays are not equal
                                  if iszero(eq(mload(mc), mload(cc))) {
                                  // unsuccess:
                                      success := 0
                                      cb := 0
                                  }
                              }
                          }
                          default {
                          // unsuccess:
                              success := 0
                          }
                      }
                      return success;
                  }
                  function equalStorage(
                      bytes storage _preBytes,
                      bytes memory _postBytes
                  )
                  internal
                  view
                  returns (bool)
                  {
                      bool success = true;
                      assembly {
                      // we know _preBytes_offset is 0
                          let fslot := sload(_preBytes.slot)
                      // Decode the length of the stored array like in concatStorage().
                          let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
                          let mlength := mload(_postBytes)
                      // if lengths don't match the arrays are not equal
                          switch eq(slength, mlength)
                          case 1 {
                          // slength can contain both the length and contents of the array
                          // if length < 32 bytes so let's prepare for that
                          // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                              if iszero(iszero(slength)) {
                                  switch lt(slength, 32)
                                  case 1 {
                                  // blank the last byte which is the length
                                      fslot := mul(div(fslot, 0x100), 0x100)
                                      if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                                      // unsuccess:
                                          success := 0
                                      }
                                  }
                                  default {
                                  // cb is a circuit breaker in the for loop since there's
                                  //  no said feature for inline assembly loops
                                  // cb = 1 - don't breaker
                                  // cb = 0 - break
                                      let cb := 1
                                  // get the keccak hash to get the contents of the array
                                      mstore(0x0, _preBytes.slot)
                                      let sc := keccak256(0x0, 0x20)
                                      let mc := add(_postBytes, 0x20)
                                      let end := add(mc, mlength)
                                  // the next line is the loop condition:
                                  // while(uint256(mc < end) + cb == 2)
                                      for {} eq(add(lt(mc, end), cb), 2) {
                                          sc := add(sc, 1)
                                          mc := add(mc, 0x20)
                                      } {
                                          if iszero(eq(sload(sc), mload(mc))) {
                                          // unsuccess:
                                              success := 0
                                              cb := 0
                                          }
                                      }
                                  }
                              }
                          }
                          default {
                          // unsuccess:
                              success := 0
                          }
                      }
                      return success;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "../OFT.sol";
              contract BasedOFT is OFT {
                  constructor(string memory _name, string memory _symbol, address _lzEndpoint) OFT(_name, _symbol, _lzEndpoint) {}
                  function circulatingSupply() public view virtual override returns (uint) {
                      unchecked {
                          return totalSupply() - balanceOf(address(this));
                      }
                  }
                  function _debitFrom(address _from, uint16, bytes memory, uint _amount) internal virtual override returns(uint) {
                      address spender = _msgSender();
                      if (_from != spender) _spendAllowance(_from, spender, _amount);
                      _transfer(_from, address(this), _amount);
                      return _amount;
                  }
                  function _creditTo(uint16, address _toAddress, uint _amount) internal virtual override returns(uint) {
                      _transfer(address(this), _toAddress, _amount);
                      return _amount;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "../../lzApp/NonblockingLzApp.sol";
              import "./IOFTCore.sol";
              import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
              abstract contract OFTCore is NonblockingLzApp, ERC165, IOFTCore {
                  using BytesLib for bytes;
                  uint public constant NO_EXTRA_GAS = 0;
                  // packet type
                  uint16 public constant PT_SEND = 0;
                  bool public useCustomAdapterParams;
                  constructor(address _lzEndpoint) NonblockingLzApp(_lzEndpoint) {}
                  function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
                      return interfaceId == type(IOFTCore).interfaceId || super.supportsInterface(interfaceId);
                  }
                  function estimateSendFee(uint16 _dstChainId, bytes calldata _toAddress, uint _amount, bool _useZro, bytes calldata _adapterParams) public view virtual override returns (uint nativeFee, uint zroFee) {
                      // mock the payload for sendFrom()
                      bytes memory payload = abi.encode(PT_SEND, _toAddress, _amount);
                      return lzEndpoint.estimateFees(_dstChainId, address(this), payload, _useZro, _adapterParams);
                  }
                  function sendFrom(address _from, uint16 _dstChainId, bytes calldata _toAddress, uint _amount, address payable _refundAddress, address _zroPaymentAddress, bytes calldata _adapterParams) public payable virtual override {
                      _send(_from, _dstChainId, _toAddress, _amount, _refundAddress, _zroPaymentAddress, _adapterParams);
                  }
                  function setUseCustomAdapterParams(bool _useCustomAdapterParams) public virtual onlyOwner {
                      useCustomAdapterParams = _useCustomAdapterParams;
                      emit SetUseCustomAdapterParams(_useCustomAdapterParams);
                  }
                  function _nonblockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal virtual override {
                      uint16 packetType;
                      assembly {
                          packetType := mload(add(_payload, 32))
                      }
                      if (packetType == PT_SEND) {
                          _sendAck(_srcChainId, _srcAddress, _nonce, _payload);
                      } else {
                          revert("OFTCore: unknown packet type");
                      }
                  }
                  function _send(address _from, uint16 _dstChainId, bytes memory _toAddress, uint _amount, address payable _refundAddress, address _zroPaymentAddress, bytes memory _adapterParams) internal virtual {
                      _checkAdapterParams(_dstChainId, PT_SEND, _adapterParams, NO_EXTRA_GAS);
                      uint amount = _debitFrom(_from, _dstChainId, _toAddress, _amount);
                      bytes memory lzPayload = abi.encode(PT_SEND, _toAddress, amount);
                      _lzSend(_dstChainId, lzPayload, _refundAddress, _zroPaymentAddress, _adapterParams, msg.value);
                      emit SendToChain(_dstChainId, _from, _toAddress, amount);
                  }
                  function _sendAck(uint16 _srcChainId, bytes memory, uint64, bytes memory _payload) internal virtual {
                      (, bytes memory toAddressBytes, uint amount) = abi.decode(_payload, (uint16, bytes, uint));
                      address to = toAddressBytes.toAddress(0);
                      amount = _creditTo(_srcChainId, to, amount);
                      emit ReceiveFromChain(_srcChainId, to, amount);
                  }
                  function _checkAdapterParams(uint16 _dstChainId, uint16 _pkType, bytes memory _adapterParams, uint _extraGas) internal virtual {
                      if (useCustomAdapterParams) {
                          _checkGasLimit(_dstChainId, _pkType, _adapterParams, _extraGas);
                      } else {
                          require(_adapterParams.length == 0, "OFTCore: _adapterParams must be empty.");
                      }
                  }
                  function _debitFrom(address _from, uint16 _dstChainId, bytes memory _toAddress, uint _amount) internal virtual returns(uint);
                  function _creditTo(uint16 _srcChainId, address _toAddress, uint _amount) internal virtual returns(uint);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
              import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
              import "./IOFT.sol";
              import "./OFTCore.sol";
              // override decimal() function is needed
              contract OFT is OFTCore, ERC20, IOFT {
                  constructor(string memory _name, string memory _symbol, address _lzEndpoint) ERC20(_name, _symbol) OFTCore(_lzEndpoint) {}
                  function supportsInterface(bytes4 interfaceId) public view virtual override(OFTCore, IERC165) returns (bool) {
                      return interfaceId == type(IOFT).interfaceId || interfaceId == type(IERC20).interfaceId || super.supportsInterface(interfaceId);
                  }
                  function token() public view virtual override returns (address) {
                      return address(this);
                  }
                  function circulatingSupply() public view virtual override returns (uint) {
                      return totalSupply();
                  }
                  function _debitFrom(address _from, uint16, bytes memory, uint _amount) internal virtual override returns(uint) {
                      address spender = _msgSender();
                      if (_from != spender) _spendAllowance(_from, spender, _amount);
                      _burn(_from, _amount);
                      return _amount;
                  }
                  function _creditTo(uint16, address _toAddress, uint _amount) internal virtual override returns(uint) {
                      _mint(_toAddress, _amount);
                      return _amount;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.5.0;
              import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
              /**
               * @dev Interface of the IOFT core standard
               */
              interface IOFTCore is IERC165 {
                  /**
                   * @dev estimate send token `_tokenId` to (`_dstChainId`, `_toAddress`)
                   * _dstChainId - L0 defined chain id to send tokens too
                   * _toAddress - dynamic bytes array which contains the address to whom you are sending tokens to on the dstChain
                   * _amount - amount of the tokens to transfer
                   * _useZro - indicates to use zro to pay L0 fees
                   * _adapterParam - flexible bytes array to indicate messaging adapter services in L0
                   */
                  function estimateSendFee(uint16 _dstChainId, bytes calldata _toAddress, uint _amount, bool _useZro, bytes calldata _adapterParams) external view returns (uint nativeFee, uint zroFee);
                  /**
                   * @dev send `_amount` amount of token to (`_dstChainId`, `_toAddress`) from `_from`
                   * `_from` the owner of token
                   * `_dstChainId` the destination chain identifier
                   * `_toAddress` can be any size depending on the `dstChainId`.
                   * `_amount` the quantity of tokens in wei
                   * `_refundAddress` the address LayerZero refunds if too much message fee is sent
                   * `_zroPaymentAddress` set to address(0x0) if not paying in ZRO (LayerZero Token)
                   * `_adapterParams` is a flexible bytes array to indicate messaging adapter services
                   */
                  function sendFrom(address _from, uint16 _dstChainId, bytes calldata _toAddress, uint _amount, address payable _refundAddress, address _zroPaymentAddress, bytes calldata _adapterParams) external payable;
                  /**
                   * @dev returns the circulating amount of tokens on current chain
                   */
                  function circulatingSupply() external view returns (uint);
                  /**
                   * @dev returns the address of the ERC20 token
                   */
                  function token() external view returns (address);
                  /**
                   * @dev Emitted when `_amount` tokens are moved from the `_sender` to (`_dstChainId`, `_toAddress`)
                   * `_nonce` is the outbound nonce
                   */
                  event SendToChain(uint16 indexed _dstChainId, address indexed _from, bytes _toAddress, uint _amount);
                  /**
                   * @dev Emitted when `_amount` tokens are received from `_srcChainId` into the `_toAddress` on the local chain.
                   * `_nonce` is the inbound nonce.
                   */
                  event ReceiveFromChain(uint16 indexed _srcChainId, address indexed _to, uint _amount);
                  event SetUseCustomAdapterParams(bool _useCustomAdapterParams);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.5.0;
              import "./IOFTCore.sol";
              import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              /**
               * @dev Interface of the OFT standard
               */
              interface IOFT is IOFTCore, IERC20 {
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "./LzApp.sol";
              import "../util/ExcessivelySafeCall.sol";
              /*
               * the default LayerZero messaging behaviour is blocking, i.e. any failed message will block the channel
               * this abstract class try-catch all fail messages and store locally for future retry. hence, non-blocking
               * NOTE: if the srcAddress is not configured properly, it will still block the message pathway from (srcChainId, srcAddress)
               */
              abstract contract NonblockingLzApp is LzApp {
                  using ExcessivelySafeCall for address;
                  constructor(address _endpoint) LzApp(_endpoint) {}
                  mapping(uint16 => mapping(bytes => mapping(uint64 => bytes32))) public failedMessages;
                  event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, bytes _reason);
                  event RetryMessageSuccess(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes32 _payloadHash);
                  // overriding the virtual function in LzReceiver
                  function _blockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal virtual override {
                      (bool success, bytes memory reason) = address(this).excessivelySafeCall(gasleft(), 150, abi.encodeWithSelector(this.nonblockingLzReceive.selector, _srcChainId, _srcAddress, _nonce, _payload));
                      // try-catch all errors/exceptions
                      if (!success) {
                          _storeFailedMessage(_srcChainId, _srcAddress, _nonce, _payload, reason);
                      }
                  }
                  function _storeFailedMessage(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload, bytes memory _reason) internal virtual {
                      failedMessages[_srcChainId][_srcAddress][_nonce] = keccak256(_payload);
                      emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, _reason);
                  }
                  function nonblockingLzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) public virtual {
                      // only internal transaction
                      require(_msgSender() == address(this), "NonblockingLzApp: caller must be LzApp");
                      _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload);
                  }
                  //@notice override this function
                  function _nonblockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal virtual;
                  function retryMessage(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) public payable virtual {
                      // assert there is message to retry
                      bytes32 payloadHash = failedMessages[_srcChainId][_srcAddress][_nonce];
                      require(payloadHash != bytes32(0), "NonblockingLzApp: no stored message");
                      require(keccak256(_payload) == payloadHash, "NonblockingLzApp: invalid payload");
                      // clear the stored message
                      failedMessages[_srcChainId][_srcAddress][_nonce] = bytes32(0);
                      // execute the message. revert if it fails again
                      _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload);
                      emit RetryMessageSuccess(_srcChainId, _srcAddress, _nonce, payloadHash);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/access/Ownable.sol";
              import "../interfaces/ILayerZeroReceiver.sol";
              import "../interfaces/ILayerZeroUserApplicationConfig.sol";
              import "../interfaces/ILayerZeroEndpoint.sol";
              import "../util/BytesLib.sol";
              /*
               * a generic LzReceiver implementation
               */
              abstract contract LzApp is Ownable, ILayerZeroReceiver, ILayerZeroUserApplicationConfig {
                  using BytesLib for bytes;
                  // ua can not send payload larger than this by default, but it can be changed by the ua owner
                  uint constant public DEFAULT_PAYLOAD_SIZE_LIMIT = 10000;
                  ILayerZeroEndpoint public immutable lzEndpoint;
                  mapping(uint16 => bytes) public trustedRemoteLookup;
                  mapping(uint16 => mapping(uint16 => uint)) public minDstGasLookup;
                  mapping(uint16 => uint) public payloadSizeLimitLookup;
                  address public precrime;
                  event SetPrecrime(address precrime);
                  event SetTrustedRemote(uint16 _remoteChainId, bytes _path);
                  event SetTrustedRemoteAddress(uint16 _remoteChainId, bytes _remoteAddress);
                  event SetMinDstGas(uint16 _dstChainId, uint16 _type, uint _minDstGas);
                  constructor(address _endpoint) {
                      lzEndpoint = ILayerZeroEndpoint(_endpoint);
                  }
                  function lzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) public virtual override {
                      // lzReceive must be called by the endpoint for security
                      require(_msgSender() == address(lzEndpoint), "LzApp: invalid endpoint caller");
                      bytes memory trustedRemote = trustedRemoteLookup[_srcChainId];
                      // if will still block the message pathway from (srcChainId, srcAddress). should not receive message from untrusted remote.
                      require(_srcAddress.length == trustedRemote.length && trustedRemote.length > 0 && keccak256(_srcAddress) == keccak256(trustedRemote), "LzApp: invalid source sending contract");
                      _blockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload);
                  }
                  // abstract function - the default behaviour of LayerZero is blocking. See: NonblockingLzApp if you dont need to enforce ordered messaging
                  function _blockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal virtual;
                  function _lzSend(uint16 _dstChainId, bytes memory _payload, address payable _refundAddress, address _zroPaymentAddress, bytes memory _adapterParams, uint _nativeFee) internal virtual {
                      bytes memory trustedRemote = trustedRemoteLookup[_dstChainId];
                      require(trustedRemote.length != 0, "LzApp: destination chain is not a trusted source");
                      _checkPayloadSize(_dstChainId, _payload.length);
                      lzEndpoint.send{value: _nativeFee}(_dstChainId, trustedRemote, _payload, _refundAddress, _zroPaymentAddress, _adapterParams);
                  }
                  function _checkGasLimit(uint16 _dstChainId, uint16 _type, bytes memory _adapterParams, uint _extraGas) internal view virtual {
                      uint providedGasLimit = _getGasLimit(_adapterParams);
                      uint minGasLimit = minDstGasLookup[_dstChainId][_type] + _extraGas;
                      require(minGasLimit > 0, "LzApp: minGasLimit not set");
                      require(providedGasLimit >= minGasLimit, "LzApp: gas limit is too low");
                  }
                  function _getGasLimit(bytes memory _adapterParams) internal pure virtual returns (uint gasLimit) {
                      require(_adapterParams.length >= 34, "LzApp: invalid adapterParams");
                      assembly {
                          gasLimit := mload(add(_adapterParams, 34))
                      }
                  }
                  function _checkPayloadSize(uint16 _dstChainId, uint _payloadSize) internal view virtual {
                      uint payloadSizeLimit = payloadSizeLimitLookup[_dstChainId];
                      if (payloadSizeLimit == 0) { // use default if not set
                          payloadSizeLimit = DEFAULT_PAYLOAD_SIZE_LIMIT;
                      }
                      require(_payloadSize <= payloadSizeLimit, "LzApp: payload size is too large");
                  }
                  //---------------------------UserApplication config----------------------------------------
                  function getConfig(uint16 _version, uint16 _chainId, address, uint _configType) external view returns (bytes memory) {
                      return lzEndpoint.getConfig(_version, _chainId, address(this), _configType);
                  }
                  // generic config for LayerZero user Application
                  function setConfig(uint16 _version, uint16 _chainId, uint _configType, bytes calldata _config) external override onlyOwner {
                      lzEndpoint.setConfig(_version, _chainId, _configType, _config);
                  }
                  function setSendVersion(uint16 _version) external override onlyOwner {
                      lzEndpoint.setSendVersion(_version);
                  }
                  function setReceiveVersion(uint16 _version) external override onlyOwner {
                      lzEndpoint.setReceiveVersion(_version);
                  }
                  function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external override onlyOwner {
                      lzEndpoint.forceResumeReceive(_srcChainId, _srcAddress);
                  }
                  // _path = abi.encodePacked(remoteAddress, localAddress)
                  // this function set the trusted path for the cross-chain communication
                  function setTrustedRemote(uint16 _remoteChainId, bytes calldata _path) external onlyOwner {
                      trustedRemoteLookup[_remoteChainId] = _path;
                      emit SetTrustedRemote(_remoteChainId, _path);
                  }
                  function setTrustedRemoteAddress(uint16 _remoteChainId, bytes calldata _remoteAddress) external onlyOwner {
                      trustedRemoteLookup[_remoteChainId] = abi.encodePacked(_remoteAddress, address(this));
                      emit SetTrustedRemoteAddress(_remoteChainId, _remoteAddress);
                  }
                  function getTrustedRemoteAddress(uint16 _remoteChainId) external view returns (bytes memory) {
                      bytes memory path = trustedRemoteLookup[_remoteChainId];
                      require(path.length != 0, "LzApp: no trusted path record");
                      return path.slice(0, path.length - 20); // the last 20 bytes should be address(this)
                  }
                  function setPrecrime(address _precrime) external onlyOwner {
                      precrime = _precrime;
                      emit SetPrecrime(_precrime);
                  }
                  function setMinDstGas(uint16 _dstChainId, uint16 _packetType, uint _minGas) external onlyOwner {
                      require(_minGas > 0, "LzApp: invalid minGas");
                      minDstGasLookup[_dstChainId][_packetType] = _minGas;
                      emit SetMinDstGas(_dstChainId, _packetType, _minGas);
                  }
                  // if the size is 0, it means default size limit
                  function setPayloadSizeLimit(uint16 _dstChainId, uint _size) external onlyOwner {
                      payloadSizeLimitLookup[_dstChainId] = _size;
                  }
                  //--------------------------- VIEW FUNCTION ----------------------------------------
                  function isTrustedRemote(uint16 _srcChainId, bytes calldata _srcAddress) external view returns (bool) {
                      bytes memory trustedSource = trustedRemoteLookup[_srcChainId];
                      return keccak256(trustedSource) == keccak256(_srcAddress);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.5.0;
              interface ILayerZeroUserApplicationConfig {
                  // @notice set the configuration of the LayerZero messaging library of the specified version
                  // @param _version - messaging library version
                  // @param _chainId - the chainId for the pending config change
                  // @param _configType - type of configuration. every messaging library has its own convention.
                  // @param _config - configuration in the bytes. can encode arbitrary content.
                  function setConfig(uint16 _version, uint16 _chainId, uint _configType, bytes calldata _config) external;
                  // @notice set the send() LayerZero messaging library version to _version
                  // @param _version - new messaging library version
                  function setSendVersion(uint16 _version) external;
                  // @notice set the lzReceive() LayerZero messaging library version to _version
                  // @param _version - new messaging library version
                  function setReceiveVersion(uint16 _version) external;
                  // @notice Only when the UA needs to resume the message flow in blocking mode and clear the stored payload
                  // @param _srcChainId - the chainId of the source chain
                  // @param _srcAddress - the contract address of the source contract at the source chain
                  function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.5.0;
              interface ILayerZeroReceiver {
                  // @notice LayerZero endpoint will invoke this function to deliver the message on the destination
                  // @param _srcChainId - the source endpoint identifier
                  // @param _srcAddress - the source sending contract address from the source chain
                  // @param _nonce - the ordered message nonce
                  // @param _payload - the signed payload is the UA bytes has encoded to be sent
                  function lzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.5.0;
              import "./ILayerZeroUserApplicationConfig.sol";
              interface ILayerZeroEndpoint is ILayerZeroUserApplicationConfig {
                  // @notice send a LayerZero message to the specified address at a LayerZero endpoint.
                  // @param _dstChainId - the destination chain identifier
                  // @param _destination - the address on destination chain (in bytes). address length/format may vary by chains
                  // @param _payload - a custom bytes payload to send to the destination contract
                  // @param _refundAddress - if the source transaction is cheaper than the amount of value passed, refund the additional amount to this address
                  // @param _zroPaymentAddress - the address of the ZRO token holder who would pay for the transaction
                  // @param _adapterParams - parameters for custom functionality. e.g. receive airdropped native gas from the relayer on destination
                  function send(uint16 _dstChainId, bytes calldata _destination, bytes calldata _payload, address payable _refundAddress, address _zroPaymentAddress, bytes calldata _adapterParams) external payable;
                  // @notice used by the messaging library to publish verified payload
                  // @param _srcChainId - the source chain identifier
                  // @param _srcAddress - the source contract (as bytes) at the source chain
                  // @param _dstAddress - the address on destination chain
                  // @param _nonce - the unbound message ordering nonce
                  // @param _gasLimit - the gas limit for external contract execution
                  // @param _payload - verified payload to send to the destination contract
                  function receivePayload(uint16 _srcChainId, bytes calldata _srcAddress, address _dstAddress, uint64 _nonce, uint _gasLimit, bytes calldata _payload) external;
                  // @notice get the inboundNonce of a lzApp from a source chain which could be EVM or non-EVM chain
                  // @param _srcChainId - the source chain identifier
                  // @param _srcAddress - the source chain contract address
                  function getInboundNonce(uint16 _srcChainId, bytes calldata _srcAddress) external view returns (uint64);
                  // @notice get the outboundNonce from this source chain which, consequently, is always an EVM
                  // @param _srcAddress - the source chain contract address
                  function getOutboundNonce(uint16 _dstChainId, address _srcAddress) external view returns (uint64);
                  // @notice gets a quote in source native gas, for the amount that send() requires to pay for message delivery
                  // @param _dstChainId - the destination chain identifier
                  // @param _userApplication - the user app address on this EVM chain
                  // @param _payload - the custom message to send over LayerZero
                  // @param _payInZRO - if false, user app pays the protocol fee in native token
                  // @param _adapterParam - parameters for the adapter service, e.g. send some dust native token to dstChain
                  function estimateFees(uint16 _dstChainId, address _userApplication, bytes calldata _payload, bool _payInZRO, bytes calldata _adapterParam) external view returns (uint nativeFee, uint zroFee);
                  // @notice get this Endpoint's immutable source identifier
                  function getChainId() external view returns (uint16);
                  // @notice the interface to retry failed message on this Endpoint destination
                  // @param _srcChainId - the source chain identifier
                  // @param _srcAddress - the source chain contract address
                  // @param _payload - the payload to be retried
                  function retryPayload(uint16 _srcChainId, bytes calldata _srcAddress, bytes calldata _payload) external;
                  // @notice query if any STORED payload (message blocking) at the endpoint.
                  // @param _srcChainId - the source chain identifier
                  // @param _srcAddress - the source chain contract address
                  function hasStoredPayload(uint16 _srcChainId, bytes calldata _srcAddress) external view returns (bool);
                  // @notice query if the _libraryAddress is valid for sending msgs.
                  // @param _userApplication - the user app address on this EVM chain
                  function getSendLibraryAddress(address _userApplication) external view returns (address);
                  // @notice query if the _libraryAddress is valid for receiving msgs.
                  // @param _userApplication - the user app address on this EVM chain
                  function getReceiveLibraryAddress(address _userApplication) external view returns (address);
                  // @notice query if the non-reentrancy guard for send() is on
                  // @return true if the guard is on. false otherwise
                  function isSendingPayload() external view returns (bool);
                  // @notice query if the non-reentrancy guard for receive() is on
                  // @return true if the guard is on. false otherwise
                  function isReceivingPayload() external view returns (bool);
                  // @notice get the configuration of the LayerZero messaging library of the specified version
                  // @param _version - messaging library version
                  // @param _chainId - the chainId for the pending config change
                  // @param _userApplication - the contract address of the user application
                  // @param _configType - type of configuration. every messaging library has its own convention.
                  function getConfig(uint16 _version, uint16 _chainId, address _userApplication, uint _configType) external view returns (bytes memory);
                  // @notice get the send() LayerZero messaging library version
                  // @param _userApplication - the contract address of the user application
                  function getSendVersion(address _userApplication) external view returns (uint16);
                  // @notice get the lzReceive() LayerZero messaging library version
                  // @param _userApplication - the contract address of the user application
                  function getReceiveVersion(address _userApplication) external view returns (uint16);
              }