ETH Price: $2,240.96 (-2.05%)
Gas: 0.58 Gwei

Transaction Decoder

Block:
20818317 at Sep-24-2024 05:19:47 AM +UTC
Transaction Fee:
0.00211777120874485 ETH $4.75
Gas Used:
154,775 Gas / 13.682902334 Gwei

Emitted Events:

277 WBTC.Transfer( from=[Sender] 0x3fdca2af2eaf7c744008faa018074120af1e3dc8, to=BoringVault, value=5997389 )
278 BoringVault.Transfer( from=0x0000000000000000000000000000000000000000, to=[Sender] 0x3fdca2af2eaf7c744008faa018074120af1e3dc8, amount=5997389 )
279 BoringVault.Enter( from=[Sender] 0x3fdca2af2eaf7c744008faa018074120af1e3dc8, asset=WBTC, amount=5997389, to=[Sender] 0x3fdca2af2eaf7c744008faa018074120af1e3dc8, shares=5997389 )
280 TellerWithMultiAssetSupport.Deposit( nonce=2981, receiver=[Sender] 0x3fdca2af2eaf7c744008faa018074120af1e3dc8, depositAsset=WBTC, depositAmount=5997389, shareAmount=5997389, depositTimestamp=1727155187, shareLockPeriodAtTimeOfDeposit=0 )

Account State Difference:

  Address   Before After State Difference Code
0x2260FAC5...93bc2C599
0x3fdCA2AF...0af1e3DC8
0.007064977112296561 Eth
Nonce: 30
0.004947205903551711 Eth
Nonce: 31
0.00211777120874485
(Titan Builder)
6.924791977595514347 Eth6.925078107915445347 Eth0.000286130319931
0x657e8C86...EB088C642
0xe19a43B1...38B3242D1

Execution Trace

TellerWithMultiAssetSupport.deposit( depositAsset=0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599, depositAmount=5997389, minimumMint=0 ) => ( shares=5997389 )
  • RolesAuthority.canCall( user=0x3fdCA2AF2eaF7C744008FaA018074120af1e3DC8, target=0xe19a43B1b8af6CeE71749Af2332627338B3242D1, functionSig=System.Byte[] ) => ( True )
  • AccountantWithRateProviders.getRateInQuoteSafe( quote=0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599 ) => ( rateInQuote=100000000 )
  • BoringVault.enter( from=0x3fdCA2AF2eaF7C744008FaA018074120af1e3DC8, asset=0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599, assetAmount=5997389, to=0x3fdCA2AF2eaF7C744008FaA018074120af1e3DC8, shareAmount=5997389 )
    • RolesAuthority.canCall( user=0xe19a43B1b8af6CeE71749Af2332627338B3242D1, target=0x657e8C867D8B37dCC18fA4Caead9C45EB088C642, functionSig=System.Byte[] ) => ( True )
    • WBTC.transferFrom( _from=0x3fdCA2AF2eaF7C744008FaA018074120af1e3DC8, _to=0x657e8C867D8B37dCC18fA4Caead9C45EB088C642, _value=5997389 ) => ( True )
      File 1 of 5: TellerWithMultiAssetSupport
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.21;
      import {ERC20} from "@solmate/tokens/ERC20.sol";
      import {WETH} from "@solmate/tokens/WETH.sol";
      import {BoringVault} from "src/base/BoringVault.sol";
      import {AccountantWithRateProviders} from "src/base/Roles/AccountantWithRateProviders.sol";
      import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
      import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
      import {BeforeTransferHook} from "src/interfaces/BeforeTransferHook.sol";
      import {Auth, Authority} from "@solmate/auth/Auth.sol";
      import {ReentrancyGuard} from "@solmate/utils/ReentrancyGuard.sol";
      import {IPausable} from "src/interfaces/IPausable.sol";
      contract TellerWithMultiAssetSupport is Auth, BeforeTransferHook, ReentrancyGuard, IPausable {
          using FixedPointMathLib for uint256;
          using SafeTransferLib for ERC20;
          using SafeTransferLib for WETH;
          // ========================================= CONSTANTS =========================================
          /**
           * @notice Native address used to tell the contract to handle native asset deposits.
           */
          address internal constant NATIVE = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
          /**
           * @notice The maximum possible share lock period.
           */
          uint256 internal constant MAX_SHARE_LOCK_PERIOD = 3 days;
          // ========================================= STATE =========================================
          /**
           * @notice Mapping ERC20s to an isSupported bool.
           */
          mapping(ERC20 => bool) public isSupported;
          /**
           * @notice The deposit nonce used to map to a deposit hash.
           */
          uint96 public depositNonce = 1;
          /**
           * @notice After deposits, shares are locked to the msg.sender's address
           *         for `shareLockPeriod`.
           * @dev During this time all trasnfers from msg.sender will revert, and
           *      deposits are refundable.
           */
          uint64 public shareLockPeriod;
          /**
           * @notice Used to pause calls to `deposit` and `depositWithPermit`.
           */
          bool public isPaused;
          /**
           * @dev Maps deposit nonce to keccak256(address receiver, address depositAsset, uint256 depositAmount, uint256 shareAmount, uint256 timestamp, uint256 shareLockPeriod).
           */
          mapping(uint256 => bytes32) public publicDepositHistory;
          /**
           * @notice Maps user address to the time their shares will be unlocked.
           */
          mapping(address => uint256) public shareUnlockTime;
          /**
           * @notice Mapping `from` address to a bool to deny them from transferring shares.
           */
          mapping(address => bool) public fromDenyList;
          /**
           * @notice Mapping `to` address to a bool to deny them from receiving shares.
           */
          mapping(address => bool) public toDenyList;
          /**
           * @notice Mapping `opeartor` address to a bool to deny them from calling `transfer` or `transferFrom`.
           */
          mapping(address => bool) public operatorDenyList;
          //============================== ERRORS ===============================
          error TellerWithMultiAssetSupport__ShareLockPeriodTooLong();
          error TellerWithMultiAssetSupport__SharesAreLocked();
          error TellerWithMultiAssetSupport__SharesAreUnLocked();
          error TellerWithMultiAssetSupport__BadDepositHash();
          error TellerWithMultiAssetSupport__AssetNotSupported();
          error TellerWithMultiAssetSupport__ZeroAssets();
          error TellerWithMultiAssetSupport__MinimumMintNotMet();
          error TellerWithMultiAssetSupport__MinimumAssetsNotMet();
          error TellerWithMultiAssetSupport__PermitFailedAndAllowanceTooLow();
          error TellerWithMultiAssetSupport__ZeroShares();
          error TellerWithMultiAssetSupport__DualDeposit();
          error TellerWithMultiAssetSupport__Paused();
          error TellerWithMultiAssetSupport__TransferDenied(address from, address to, address operator);
          //============================== EVENTS ===============================
          event Paused();
          event Unpaused();
          event AssetAdded(address indexed asset);
          event AssetRemoved(address indexed asset);
          event Deposit(
              uint256 indexed nonce,
              address indexed receiver,
              address indexed depositAsset,
              uint256 depositAmount,
              uint256 shareAmount,
              uint256 depositTimestamp,
              uint256 shareLockPeriodAtTimeOfDeposit
          );
          event BulkDeposit(address indexed asset, uint256 depositAmount);
          event BulkWithdraw(address indexed asset, uint256 shareAmount);
          event DepositRefunded(uint256 indexed nonce, bytes32 depositHash, address indexed user);
          event DenyFrom(address indexed user);
          event DenyTo(address indexed user);
          event DenyOperator(address indexed user);
          event AllowFrom(address indexed user);
          event AllowTo(address indexed user);
          event AllowOperator(address indexed user);
          //============================== IMMUTABLES ===============================
          /**
           * @notice The BoringVault this contract is working with.
           */
          BoringVault public immutable vault;
          /**
           * @notice The AccountantWithRateProviders this contract is working with.
           */
          AccountantWithRateProviders public immutable accountant;
          /**
           * @notice One share of the BoringVault.
           */
          uint256 internal immutable ONE_SHARE;
          /**
           * @notice The native wrapper contract.
           */
          WETH public immutable nativeWrapper;
          constructor(address _owner, address _vault, address _accountant, address _weth)
              Auth(_owner, Authority(address(0)))
          {
              vault = BoringVault(payable(_vault));
              ONE_SHARE = 10 ** vault.decimals();
              accountant = AccountantWithRateProviders(_accountant);
              nativeWrapper = WETH(payable(_weth));
          }
          // ========================================= ADMIN FUNCTIONS =========================================
          /**
           * @notice Pause this contract, which prevents future calls to `deposit` and `depositWithPermit`.
           * @dev Callable by MULTISIG_ROLE.
           */
          function pause() external requiresAuth {
              isPaused = true;
              emit Paused();
          }
          /**
           * @notice Unpause this contract, which allows future calls to `deposit` and `depositWithPermit`.
           * @dev Callable by MULTISIG_ROLE.
           */
          function unpause() external requiresAuth {
              isPaused = false;
              emit Unpaused();
          }
          /**
           * @notice Adds this asset as a deposit asset.
           * @dev The accountant must also support pricing this asset, else the `deposit` call will revert.
           * @dev Callable by OWNER_ROLE.
           */
          function addAsset(ERC20 asset) external requiresAuth {
              isSupported[asset] = true;
              emit AssetAdded(address(asset));
          }
          /**
           * @notice Removes this asset as a deposit asset.
           * @dev Callable by OWNER_ROLE.
           */
          function removeAsset(ERC20 asset) external requiresAuth {
              isSupported[asset] = false;
              emit AssetRemoved(address(asset));
          }
          /**
           * @notice Sets the share lock period.
           * @dev This not only locks shares to the user address, but also serves as the pending deposit period, where deposits can be reverted.
           * @dev If a new shorter share lock period is set, users with pending share locks could make a new deposit to receive 1 wei shares,
           *      and have their shares unlock sooner than their original deposit allows. This state would allow for the user deposit to be refunded,
           *      but only if they have not transferred their shares out of there wallet. This is an accepted limitation, and should be known when decreasing
           *      the share lock period.
           * @dev Callable by OWNER_ROLE.
           */
          function setShareLockPeriod(uint64 _shareLockPeriod) external requiresAuth {
              if (_shareLockPeriod > MAX_SHARE_LOCK_PERIOD) revert TellerWithMultiAssetSupport__ShareLockPeriodTooLong();
              shareLockPeriod = _shareLockPeriod;
          }
          /**
           * @notice Deny a user from transferring or receiving shares.
           * @dev Callable by OWNER_ROLE, and DENIER_ROLE.
           */
          function denyAll(address user) external requiresAuth {
              fromDenyList[user] = true;
              toDenyList[user] = true;
              operatorDenyList[user] = true;
              emit DenyFrom(user);
              emit DenyTo(user);
              emit DenyOperator(user);
          }
          /**
           * @notice Allow a user to transfer or receive shares.
           * @dev Callable by OWNER_ROLE, and DENIER_ROLE.
           */
          function allowAll(address user) external requiresAuth {
              fromDenyList[user] = false;
              toDenyList[user] = false;
              operatorDenyList[user] = false;
              emit AllowFrom(user);
              emit AllowTo(user);
              emit AllowOperator(user);
          }
          /**
           * @notice Deny a user from transferring shares.
           * @dev Callable by OWNER_ROLE, and DENIER_ROLE.
           */
          function denyFrom(address user) external requiresAuth {
              fromDenyList[user] = true;
              emit DenyFrom(user);
          }
          /**
           * @notice Allow a user to transfer shares.
           * @dev Callable by OWNER_ROLE, and DENIER_ROLE.
           */
          function allowFrom(address user) external requiresAuth {
              fromDenyList[user] = false;
              emit AllowFrom(user);
          }
          /**
           * @notice Deny a user from receiving shares.
           * @dev Callable by OWNER_ROLE, and DENIER_ROLE.
           */
          function denyTo(address user) external requiresAuth {
              toDenyList[user] = true;
              emit DenyTo(user);
          }
          /**
           * @notice Allow a user to receive shares.
           * @dev Callable by OWNER_ROLE, and DENIER_ROLE.
           */
          function allowTo(address user) external requiresAuth {
              toDenyList[user] = false;
              emit AllowTo(user);
          }
          /**
           * @notice Deny an operator from transferring shares.
           * @dev Callable by OWNER_ROLE, and DENIER_ROLE.
           */
          function denyOperator(address user) external requiresAuth {
              operatorDenyList[user] = true;
              emit DenyOperator(user);
          }
          /**
           * @notice Allow an operator to transfer shares.
           * @dev Callable by OWNER_ROLE, and DENIER_ROLE.
           */
          function allowOperator(address user) external requiresAuth {
              operatorDenyList[user] = false;
              emit AllowOperator(user);
          }
          // ========================================= BeforeTransferHook FUNCTIONS =========================================
          /**
           * @notice Implement beforeTransfer hook to check if shares are locked, or if `from`, `to`, or `operator` are on the deny list.
           */
          function beforeTransfer(address from, address to, address operator) public view virtual {
              if (fromDenyList[from] || toDenyList[to] || operatorDenyList[operator]) {
                  revert TellerWithMultiAssetSupport__TransferDenied(from, to, operator);
              }
              if (shareUnlockTime[from] >= block.timestamp) revert TellerWithMultiAssetSupport__SharesAreLocked();
          }
          // ========================================= REVERT DEPOSIT FUNCTIONS =========================================
          /**
           * @notice Allows DEPOSIT_REFUNDER_ROLE to revert a pending deposit.
           * @dev Once a deposit share lock period has passed, it can no longer be reverted.
           * @dev It is possible the admin does not setup the BoringVault to call the transfer hook,
           *      but this contract can still be saving share lock state. In the event this happens
           *      deposits are still refundable if the user has not transferred their shares.
           *      But there is no guarantee that the user has not transferred their shares.
           * @dev Callable by STRATEGIST_MULTISIG_ROLE.
           */
          function refundDeposit(
              uint256 nonce,
              address receiver,
              address depositAsset,
              uint256 depositAmount,
              uint256 shareAmount,
              uint256 depositTimestamp,
              uint256 shareLockUpPeriodAtTimeOfDeposit
          ) external requiresAuth {
              if ((block.timestamp - depositTimestamp) > shareLockUpPeriodAtTimeOfDeposit) {
                  // Shares are already unlocked, so we can not revert deposit.
                  revert TellerWithMultiAssetSupport__SharesAreUnLocked();
              }
              bytes32 depositHash = keccak256(
                  abi.encode(
                      receiver, depositAsset, depositAmount, shareAmount, depositTimestamp, shareLockUpPeriodAtTimeOfDeposit
                  )
              );
              if (publicDepositHistory[nonce] != depositHash) revert TellerWithMultiAssetSupport__BadDepositHash();
              // Delete hash to prevent refund gas.
              delete publicDepositHistory[nonce];
              // If deposit used native asset, send user back wrapped native asset.
              depositAsset = depositAsset == NATIVE ? address(nativeWrapper) : depositAsset;
              // Burn shares and refund assets to receiver.
              vault.exit(receiver, ERC20(depositAsset), depositAmount, receiver, shareAmount);
              emit DepositRefunded(nonce, depositHash, receiver);
          }
          // ========================================= USER FUNCTIONS =========================================
          /**
           * @notice Allows users to deposit into the BoringVault, if this contract is not paused.
           * @dev Publicly callable.
           */
          function deposit(ERC20 depositAsset, uint256 depositAmount, uint256 minimumMint)
              public
              payable
              requiresAuth
              nonReentrant
              returns (uint256 shares)
          {
              if (isPaused) revert TellerWithMultiAssetSupport__Paused();
              if (!isSupported[depositAsset]) revert TellerWithMultiAssetSupport__AssetNotSupported();
              if (address(depositAsset) == NATIVE) {
                  if (msg.value == 0) revert TellerWithMultiAssetSupport__ZeroAssets();
                  nativeWrapper.deposit{value: msg.value}();
                  depositAmount = msg.value;
                  shares = depositAmount.mulDivDown(ONE_SHARE, accountant.getRateInQuoteSafe(nativeWrapper));
                  if (shares < minimumMint) revert TellerWithMultiAssetSupport__MinimumMintNotMet();
                  // `from` is address(this) since user already sent value.
                  nativeWrapper.safeApprove(address(vault), depositAmount);
                  vault.enter(address(this), nativeWrapper, depositAmount, msg.sender, shares);
              } else {
                  if (msg.value > 0) revert TellerWithMultiAssetSupport__DualDeposit();
                  shares = _erc20Deposit(depositAsset, depositAmount, minimumMint, msg.sender);
              }
              _afterPublicDeposit(msg.sender, depositAsset, depositAmount, shares, shareLockPeriod);
          }
          /**
           * @notice Allows users to deposit into BoringVault using permit.
           * @dev Publicly callable.
           */
          function depositWithPermit(
              ERC20 depositAsset,
              uint256 depositAmount,
              uint256 minimumMint,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) public requiresAuth nonReentrant returns (uint256 shares) {
              if (isPaused) revert TellerWithMultiAssetSupport__Paused();
              if (!isSupported[depositAsset]) revert TellerWithMultiAssetSupport__AssetNotSupported();
              try depositAsset.permit(msg.sender, address(vault), depositAmount, deadline, v, r, s) {}
              catch {
                  if (depositAsset.allowance(msg.sender, address(vault)) < depositAmount) {
                      revert TellerWithMultiAssetSupport__PermitFailedAndAllowanceTooLow();
                  }
              }
              shares = _erc20Deposit(depositAsset, depositAmount, minimumMint, msg.sender);
              _afterPublicDeposit(msg.sender, depositAsset, depositAmount, shares, shareLockPeriod);
          }
          /**
           * @notice Allows on ramp role to deposit into this contract.
           * @dev Does NOT support native deposits.
           * @dev Callable by SOLVER_ROLE.
           */
          function bulkDeposit(ERC20 depositAsset, uint256 depositAmount, uint256 minimumMint, address to)
              external
              requiresAuth
              nonReentrant
              returns (uint256 shares)
          {
              if (isPaused) revert TellerWithMultiAssetSupport__Paused();
              if (!isSupported[depositAsset]) revert TellerWithMultiAssetSupport__AssetNotSupported();
              shares = _erc20Deposit(depositAsset, depositAmount, minimumMint, to);
              emit BulkDeposit(address(depositAsset), depositAmount);
          }
          /**
           * @notice Allows off ramp role to withdraw from this contract.
           * @dev Callable by SOLVER_ROLE.
           */
          function bulkWithdraw(ERC20 withdrawAsset, uint256 shareAmount, uint256 minimumAssets, address to)
              external
              requiresAuth
              returns (uint256 assetsOut)
          {
              if (isPaused) revert TellerWithMultiAssetSupport__Paused();
              if (!isSupported[withdrawAsset]) revert TellerWithMultiAssetSupport__AssetNotSupported();
              if (shareAmount == 0) revert TellerWithMultiAssetSupport__ZeroShares();
              assetsOut = shareAmount.mulDivDown(accountant.getRateInQuoteSafe(withdrawAsset), ONE_SHARE);
              if (assetsOut < minimumAssets) revert TellerWithMultiAssetSupport__MinimumAssetsNotMet();
              vault.exit(to, withdrawAsset, assetsOut, msg.sender, shareAmount);
              emit BulkWithdraw(address(withdrawAsset), shareAmount);
          }
          // ========================================= INTERNAL HELPER FUNCTIONS =========================================
          /**
           * @notice Implements a common ERC20 deposit into BoringVault.
           */
          function _erc20Deposit(ERC20 depositAsset, uint256 depositAmount, uint256 minimumMint, address to)
              internal
              returns (uint256 shares)
          {
              if (depositAmount == 0) revert TellerWithMultiAssetSupport__ZeroAssets();
              shares = depositAmount.mulDivDown(ONE_SHARE, accountant.getRateInQuoteSafe(depositAsset));
              if (shares < minimumMint) revert TellerWithMultiAssetSupport__MinimumMintNotMet();
              vault.enter(msg.sender, depositAsset, depositAmount, to, shares);
          }
          /**
           * @notice Handle share lock logic, and event.
           */
          function _afterPublicDeposit(
              address user,
              ERC20 depositAsset,
              uint256 depositAmount,
              uint256 shares,
              uint256 currentShareLockPeriod
          ) internal {
              shareUnlockTime[user] = block.timestamp + currentShareLockPeriod;
              uint256 nonce = depositNonce;
              publicDepositHistory[nonce] =
                  keccak256(abi.encode(user, depositAsset, depositAmount, shares, block.timestamp, currentShareLockPeriod));
              depositNonce++;
              emit Deposit(nonce, user, address(depositAsset), depositAmount, shares, block.timestamp, currentShareLockPeriod);
          }
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity >=0.8.0;
      /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
      /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
      /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
      abstract contract ERC20 {
          /*//////////////////////////////////////////////////////////////
                                       EVENTS
          //////////////////////////////////////////////////////////////*/
          event Transfer(address indexed from, address indexed to, uint256 amount);
          event Approval(address indexed owner, address indexed spender, uint256 amount);
          /*//////////////////////////////////////////////////////////////
                                  METADATA STORAGE
          //////////////////////////////////////////////////////////////*/
          string public name;
          string public symbol;
          uint8 public immutable decimals;
          /*//////////////////////////////////////////////////////////////
                                    ERC20 STORAGE
          //////////////////////////////////////////////////////////////*/
          uint256 public totalSupply;
          mapping(address => uint256) public balanceOf;
          mapping(address => mapping(address => uint256)) public allowance;
          /*//////////////////////////////////////////////////////////////
                                  EIP-2612 STORAGE
          //////////////////////////////////////////////////////////////*/
          uint256 internal immutable INITIAL_CHAIN_ID;
          bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
          mapping(address => uint256) public nonces;
          /*//////////////////////////////////////////////////////////////
                                     CONSTRUCTOR
          //////////////////////////////////////////////////////////////*/
          constructor(
              string memory _name,
              string memory _symbol,
              uint8 _decimals
          ) {
              name = _name;
              symbol = _symbol;
              decimals = _decimals;
              INITIAL_CHAIN_ID = block.chainid;
              INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
          }
          /*//////////////////////////////////////////////////////////////
                                     ERC20 LOGIC
          //////////////////////////////////////////////////////////////*/
          function approve(address spender, uint256 amount) public virtual returns (bool) {
              allowance[msg.sender][spender] = amount;
              emit Approval(msg.sender, spender, amount);
              return true;
          }
          function transfer(address to, uint256 amount) public virtual returns (bool) {
              balanceOf[msg.sender] -= amount;
              // Cannot overflow because the sum of all user
              // balances can't exceed the max uint256 value.
              unchecked {
                  balanceOf[to] += amount;
              }
              emit Transfer(msg.sender, to, amount);
              return true;
          }
          function transferFrom(
              address from,
              address to,
              uint256 amount
          ) public virtual returns (bool) {
              uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
              if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
              balanceOf[from] -= amount;
              // Cannot overflow because the sum of all user
              // balances can't exceed the max uint256 value.
              unchecked {
                  balanceOf[to] += amount;
              }
              emit Transfer(from, to, amount);
              return true;
          }
          /*//////////////////////////////////////////////////////////////
                                   EIP-2612 LOGIC
          //////////////////////////////////////////////////////////////*/
          function permit(
              address owner,
              address spender,
              uint256 value,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) public virtual {
              require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
              // Unchecked because the only math done is incrementing
              // the owner's nonce which cannot realistically overflow.
              unchecked {
                  address recoveredAddress = ecrecover(
                      keccak256(
                          abi.encodePacked(
                              "\\x19\\x01",
                              DOMAIN_SEPARATOR(),
                              keccak256(
                                  abi.encode(
                                      keccak256(
                                          "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                      ),
                                      owner,
                                      spender,
                                      value,
                                      nonces[owner]++,
                                      deadline
                                  )
                              )
                          )
                      ),
                      v,
                      r,
                      s
                  );
                  require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
                  allowance[recoveredAddress][spender] = value;
              }
              emit Approval(owner, spender, value);
          }
          function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
              return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
          }
          function computeDomainSeparator() internal view virtual returns (bytes32) {
              return
                  keccak256(
                      abi.encode(
                          keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                          keccak256(bytes(name)),
                          keccak256("1"),
                          block.chainid,
                          address(this)
                      )
                  );
          }
          /*//////////////////////////////////////////////////////////////
                              INTERNAL MINT/BURN LOGIC
          //////////////////////////////////////////////////////////////*/
          function _mint(address to, uint256 amount) internal virtual {
              totalSupply += amount;
              // Cannot overflow because the sum of all user
              // balances can't exceed the max uint256 value.
              unchecked {
                  balanceOf[to] += amount;
              }
              emit Transfer(address(0), to, amount);
          }
          function _burn(address from, uint256 amount) internal virtual {
              balanceOf[from] -= amount;
              // Cannot underflow because a user's balance
              // will never be larger than the total supply.
              unchecked {
                  totalSupply -= amount;
              }
              emit Transfer(from, address(0), amount);
          }
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity >=0.8.0;
      import {ERC20} from "./ERC20.sol";
      import {SafeTransferLib} from "../utils/SafeTransferLib.sol";
      /// @notice Minimalist and modern Wrapped Ether implementation.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/WETH.sol)
      /// @author Inspired by WETH9 (https://github.com/dapphub/ds-weth/blob/master/src/weth9.sol)
      contract WETH is ERC20("Wrapped Ether", "WETH", 18) {
          using SafeTransferLib for address;
          event Deposit(address indexed from, uint256 amount);
          event Withdrawal(address indexed to, uint256 amount);
          function deposit() public payable virtual {
              _mint(msg.sender, msg.value);
              emit Deposit(msg.sender, msg.value);
          }
          function withdraw(uint256 amount) public virtual {
              _burn(msg.sender, amount);
              emit Withdrawal(msg.sender, amount);
              msg.sender.safeTransferETH(amount);
          }
          receive() external payable virtual {
              deposit();
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.21;
      import {Address} from "@openzeppelin/contracts/utils/Address.sol";
      import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
      import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
      import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
      import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
      import {ERC20} from "@solmate/tokens/ERC20.sol";
      import {BeforeTransferHook} from "src/interfaces/BeforeTransferHook.sol";
      import {Auth, Authority} from "@solmate/auth/Auth.sol";
      contract BoringVault is ERC20, Auth, ERC721Holder, ERC1155Holder {
          using Address for address;
          using SafeTransferLib for ERC20;
          using FixedPointMathLib for uint256;
          // ========================================= STATE =========================================
          /**
           * @notice Contract responsbile for implementing `beforeTransfer`.
           */
          BeforeTransferHook public hook;
          //============================== EVENTS ===============================
          event Enter(address indexed from, address indexed asset, uint256 amount, address indexed to, uint256 shares);
          event Exit(address indexed to, address indexed asset, uint256 amount, address indexed from, uint256 shares);
          //============================== CONSTRUCTOR ===============================
          constructor(address _owner, string memory _name, string memory _symbol, uint8 _decimals)
              ERC20(_name, _symbol, _decimals)
              Auth(_owner, Authority(address(0)))
          {}
          //============================== MANAGE ===============================
          /**
           * @notice Allows manager to make an arbitrary function call from this contract.
           * @dev Callable by MANAGER_ROLE.
           */
          function manage(address target, bytes calldata data, uint256 value)
              external
              requiresAuth
              returns (bytes memory result)
          {
              result = target.functionCallWithValue(data, value);
          }
          /**
           * @notice Allows manager to make arbitrary function calls from this contract.
           * @dev Callable by MANAGER_ROLE.
           */
          function manage(address[] calldata targets, bytes[] calldata data, uint256[] calldata values)
              external
              requiresAuth
              returns (bytes[] memory results)
          {
              uint256 targetsLength = targets.length;
              results = new bytes[](targetsLength);
              for (uint256 i; i < targetsLength; ++i) {
                  results[i] = targets[i].functionCallWithValue(data[i], values[i]);
              }
          }
          //============================== ENTER ===============================
          /**
           * @notice Allows minter to mint shares, in exchange for assets.
           * @dev If assetAmount is zero, no assets are transferred in.
           * @dev Callable by MINTER_ROLE.
           */
          function enter(address from, ERC20 asset, uint256 assetAmount, address to, uint256 shareAmount)
              external
              requiresAuth
          {
              // Transfer assets in
              if (assetAmount > 0) asset.safeTransferFrom(from, address(this), assetAmount);
              // Mint shares.
              _mint(to, shareAmount);
              emit Enter(from, address(asset), assetAmount, to, shareAmount);
          }
          //============================== EXIT ===============================
          /**
           * @notice Allows burner to burn shares, in exchange for assets.
           * @dev If assetAmount is zero, no assets are transferred out.
           * @dev Callable by BURNER_ROLE.
           */
          function exit(address to, ERC20 asset, uint256 assetAmount, address from, uint256 shareAmount)
              external
              requiresAuth
          {
              // Burn shares.
              _burn(from, shareAmount);
              // Transfer assets out.
              if (assetAmount > 0) asset.safeTransfer(to, assetAmount);
              emit Exit(to, address(asset), assetAmount, from, shareAmount);
          }
          //============================== BEFORE TRANSFER HOOK ===============================
          /**
           * @notice Sets the share locker.
           * @notice If set to zero address, the share locker logic is disabled.
           * @dev Callable by OWNER_ROLE.
           */
          function setBeforeTransferHook(address _hook) external requiresAuth {
              hook = BeforeTransferHook(_hook);
          }
          /**
           * @notice Call `beforeTransferHook` passing in `from` `to`, and `msg.sender`.
           */
          function _callBeforeTransfer(address from, address to) internal view {
              if (address(hook) != address(0)) hook.beforeTransfer(from, to, msg.sender);
          }
          function transfer(address to, uint256 amount) public override returns (bool) {
              _callBeforeTransfer(msg.sender, to);
              return super.transfer(to, amount);
          }
          function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
              _callBeforeTransfer(from, to);
              return super.transferFrom(from, to, amount);
          }
          //============================== RECEIVE ===============================
          receive() external payable {}
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.21;
      import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
      import {IRateProvider} from "src/interfaces/IRateProvider.sol";
      import {ERC20} from "@solmate/tokens/ERC20.sol";
      import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
      import {BoringVault} from "src/base/BoringVault.sol";
      import {Auth, Authority} from "@solmate/auth/Auth.sol";
      import {IPausable} from "src/interfaces/IPausable.sol";
      contract AccountantWithRateProviders is Auth, IRateProvider, IPausable {
          using FixedPointMathLib for uint256;
          using SafeTransferLib for ERC20;
          // ========================================= STRUCTS =========================================
          /**
           * @param payoutAddress the address `claimFees` sends fees to
           * @param highwaterMark the highest value of the BoringVault's share price
           * @param feesOwedInBase total pending fees owed in terms of base
           * @param totalSharesLastUpdate total amount of shares the last exchange rate update
           * @param exchangeRate the current exchange rate in terms of base
           * @param allowedExchangeRateChangeUpper the max allowed change to exchange rate from an update
           * @param allowedExchangeRateChangeLower the min allowed change to exchange rate from an update
           * @param lastUpdateTimestamp the block timestamp of the last exchange rate update
           * @param isPaused whether or not this contract is paused
           * @param minimumUpdateDelayInSeconds the minimum amount of time that must pass between
           *        exchange rate updates, such that the update won't trigger the contract to be paused
           * @param managementFee the management fee
           * @param performanceFee the performance fee
           */
          struct AccountantState {
              address payoutAddress;
              uint96 highwaterMark;
              uint128 feesOwedInBase;
              uint128 totalSharesLastUpdate;
              uint96 exchangeRate;
              uint16 allowedExchangeRateChangeUpper;
              uint16 allowedExchangeRateChangeLower;
              uint64 lastUpdateTimestamp;
              bool isPaused;
              uint24 minimumUpdateDelayInSeconds;
              uint16 managementFee;
              uint16 performanceFee;
          }
          /**
           * @param isPeggedToBase whether or not the asset is 1:1 with the base asset
           * @param rateProvider the rate provider for this asset if `isPeggedToBase` is false
           */
          struct RateProviderData {
              bool isPeggedToBase;
              IRateProvider rateProvider;
          }
          // ========================================= STATE =========================================
          /**
           * @notice Store the accountant state in 3 packed slots.
           */
          AccountantState public accountantState;
          /**
           * @notice Maps ERC20s to their RateProviderData.
           */
          mapping(ERC20 => RateProviderData) public rateProviderData;
          //============================== ERRORS ===============================
          error AccountantWithRateProviders__UpperBoundTooSmall();
          error AccountantWithRateProviders__LowerBoundTooLarge();
          error AccountantWithRateProviders__ManagementFeeTooLarge();
          error AccountantWithRateProviders__PerformanceFeeTooLarge();
          error AccountantWithRateProviders__Paused();
          error AccountantWithRateProviders__ZeroFeesOwed();
          error AccountantWithRateProviders__OnlyCallableByBoringVault();
          error AccountantWithRateProviders__UpdateDelayTooLarge();
          error AccountantWithRateProviders__ExchangeRateAboveHighwaterMark();
          //============================== EVENTS ===============================
          event Paused();
          event Unpaused();
          event DelayInSecondsUpdated(uint24 oldDelay, uint24 newDelay);
          event UpperBoundUpdated(uint16 oldBound, uint16 newBound);
          event LowerBoundUpdated(uint16 oldBound, uint16 newBound);
          event ManagementFeeUpdated(uint16 oldFee, uint16 newFee);
          event PerformanceFeeUpdated(uint16 oldFee, uint16 newFee);
          event PayoutAddressUpdated(address oldPayout, address newPayout);
          event RateProviderUpdated(address asset, bool isPegged, address rateProvider);
          event ExchangeRateUpdated(uint96 oldRate, uint96 newRate, uint64 currentTime);
          event FeesClaimed(address indexed feeAsset, uint256 amount);
          event HighwaterMarkReset();
          //============================== IMMUTABLES ===============================
          /**
           * @notice The base asset rates are provided in.
           */
          ERC20 public immutable base;
          /**
           * @notice The decimals rates are provided in.
           */
          uint8 public immutable decimals;
          /**
           * @notice The BoringVault this accountant is working with.
           *         Used to determine share supply for fee calculation.
           */
          BoringVault public immutable vault;
          /**
           * @notice One share of the BoringVault.
           */
          uint256 internal immutable ONE_SHARE;
          constructor(
              address _owner,
              address _vault,
              address payoutAddress,
              uint96 startingExchangeRate,
              address _base,
              uint16 allowedExchangeRateChangeUpper,
              uint16 allowedExchangeRateChangeLower,
              uint24 minimumUpdateDelayInSeconds,
              uint16 managementFee,
              uint16 performanceFee
          ) Auth(_owner, Authority(address(0))) {
              base = ERC20(_base);
              decimals = ERC20(_base).decimals();
              vault = BoringVault(payable(_vault));
              ONE_SHARE = 10 ** vault.decimals();
              accountantState = AccountantState({
                  payoutAddress: payoutAddress,
                  highwaterMark: startingExchangeRate,
                  feesOwedInBase: 0,
                  totalSharesLastUpdate: uint128(vault.totalSupply()),
                  exchangeRate: startingExchangeRate,
                  allowedExchangeRateChangeUpper: allowedExchangeRateChangeUpper,
                  allowedExchangeRateChangeLower: allowedExchangeRateChangeLower,
                  lastUpdateTimestamp: uint64(block.timestamp),
                  isPaused: false,
                  minimumUpdateDelayInSeconds: minimumUpdateDelayInSeconds,
                  managementFee: managementFee,
                  performanceFee: performanceFee
              });
          }
          // ========================================= ADMIN FUNCTIONS =========================================
          /**
           * @notice Pause this contract, which prevents future calls to `updateExchangeRate`, and any safe rate
           *         calls will revert.
           * @dev Callable by MULTISIG_ROLE.
           */
          function pause() external requiresAuth {
              accountantState.isPaused = true;
              emit Paused();
          }
          /**
           * @notice Unpause this contract, which allows future calls to `updateExchangeRate`, and any safe rate
           *         calls will stop reverting.
           * @dev Callable by MULTISIG_ROLE.
           */
          function unpause() external requiresAuth {
              accountantState.isPaused = false;
              emit Unpaused();
          }
          /**
           * @notice Update the minimum time delay between `updateExchangeRate` calls.
           * @dev There are no input requirements, as it is possible the admin would want
           *      the exchange rate updated as frequently as needed.
           * @dev Callable by OWNER_ROLE.
           */
          function updateDelay(uint24 minimumUpdateDelayInSeconds) external requiresAuth {
              if (minimumUpdateDelayInSeconds > 14 days) revert AccountantWithRateProviders__UpdateDelayTooLarge();
              uint24 oldDelay = accountantState.minimumUpdateDelayInSeconds;
              accountantState.minimumUpdateDelayInSeconds = minimumUpdateDelayInSeconds;
              emit DelayInSecondsUpdated(oldDelay, minimumUpdateDelayInSeconds);
          }
          /**
           * @notice Update the allowed upper bound change of exchange rate between `updateExchangeRateCalls`.
           * @dev Callable by OWNER_ROLE.
           */
          function updateUpper(uint16 allowedExchangeRateChangeUpper) external requiresAuth {
              if (allowedExchangeRateChangeUpper < 1e4) revert AccountantWithRateProviders__UpperBoundTooSmall();
              uint16 oldBound = accountantState.allowedExchangeRateChangeUpper;
              accountantState.allowedExchangeRateChangeUpper = allowedExchangeRateChangeUpper;
              emit UpperBoundUpdated(oldBound, allowedExchangeRateChangeUpper);
          }
          /**
           * @notice Update the allowed lower bound change of exchange rate between `updateExchangeRateCalls`.
           * @dev Callable by OWNER_ROLE.
           */
          function updateLower(uint16 allowedExchangeRateChangeLower) external requiresAuth {
              if (allowedExchangeRateChangeLower > 1e4) revert AccountantWithRateProviders__LowerBoundTooLarge();
              uint16 oldBound = accountantState.allowedExchangeRateChangeLower;
              accountantState.allowedExchangeRateChangeLower = allowedExchangeRateChangeLower;
              emit LowerBoundUpdated(oldBound, allowedExchangeRateChangeLower);
          }
          /**
           * @notice Update the management fee to a new value.
           * @dev Callable by OWNER_ROLE.
           */
          function updateManagementFee(uint16 managementFee) external requiresAuth {
              if (managementFee > 0.2e4) revert AccountantWithRateProviders__ManagementFeeTooLarge();
              uint16 oldFee = accountantState.managementFee;
              accountantState.managementFee = managementFee;
              emit ManagementFeeUpdated(oldFee, managementFee);
          }
          /**
           * @notice Update the performance fee to a new value.
           * @dev Callable by OWNER_ROLE.
           */
          function updatePerformanceFee(uint16 performanceFee) external requiresAuth {
              if (performanceFee > 0.5e4) revert AccountantWithRateProviders__PerformanceFeeTooLarge();
              uint16 oldFee = accountantState.performanceFee;
              accountantState.performanceFee = performanceFee;
              emit PerformanceFeeUpdated(oldFee, performanceFee);
          }
          /**
           * @notice Update the payout address fees are sent to.
           * @dev Callable by OWNER_ROLE.
           */
          function updatePayoutAddress(address payoutAddress) external requiresAuth {
              address oldPayout = accountantState.payoutAddress;
              accountantState.payoutAddress = payoutAddress;
              emit PayoutAddressUpdated(oldPayout, payoutAddress);
          }
          /**
           * @notice Update the rate provider data for a specific `asset`.
           * @dev Rate providers must return rates in terms of `base` or
           * an asset pegged to base and they must use the same decimals
           * as `asset`.
           * @dev Callable by OWNER_ROLE.
           */
          function setRateProviderData(ERC20 asset, bool isPeggedToBase, address rateProvider) external requiresAuth {
              rateProviderData[asset] =
                  RateProviderData({isPeggedToBase: isPeggedToBase, rateProvider: IRateProvider(rateProvider)});
              emit RateProviderUpdated(address(asset), isPeggedToBase, rateProvider);
          }
          /**
           * @notice Reset the highwater mark to the current exchange rate.
           * @dev Callable by OWNER_ROLE.
           */
          function resetHighwaterMark() external requiresAuth {
              AccountantState storage state = accountantState;
              if (state.exchangeRate > state.highwaterMark) {
                  revert AccountantWithRateProviders__ExchangeRateAboveHighwaterMark();
              }
              uint64 currentTime = uint64(block.timestamp);
              uint256 currentTotalShares = vault.totalSupply();
              _calculateFeesOwed(state, state.exchangeRate, state.exchangeRate, currentTotalShares, currentTime);
              state.totalSharesLastUpdate = uint128(currentTotalShares);
              state.highwaterMark = accountantState.exchangeRate;
              state.lastUpdateTimestamp = currentTime;
              emit HighwaterMarkReset();
          }
          // ========================================= UPDATE EXCHANGE RATE/FEES FUNCTIONS =========================================
          /**
           * @notice Updates this contract exchangeRate.
           * @dev If new exchange rate is outside of accepted bounds, or if not enough time has passed, this
           *      will pause the contract, and this function will NOT calculate fees owed.
           * @dev Callable by UPDATE_EXCHANGE_RATE_ROLE.
           */
          function updateExchangeRate(uint96 newExchangeRate) external requiresAuth {
              AccountantState storage state = accountantState;
              if (state.isPaused) revert AccountantWithRateProviders__Paused();
              uint64 currentTime = uint64(block.timestamp);
              uint256 currentExchangeRate = state.exchangeRate;
              uint256 currentTotalShares = vault.totalSupply();
              if (
                  currentTime < state.lastUpdateTimestamp + state.minimumUpdateDelayInSeconds
                      || newExchangeRate > currentExchangeRate.mulDivDown(state.allowedExchangeRateChangeUpper, 1e4)
                      || newExchangeRate < currentExchangeRate.mulDivDown(state.allowedExchangeRateChangeLower, 1e4)
              ) {
                  // Instead of reverting, pause the contract. This way the exchange rate updater is able to update the exchange rate
                  // to a better value, and pause it.
                  state.isPaused = true;
              } else {
                  _calculateFeesOwed(state, newExchangeRate, currentExchangeRate, currentTotalShares, currentTime);
              }
              state.exchangeRate = newExchangeRate;
              state.totalSharesLastUpdate = uint128(currentTotalShares);
              state.lastUpdateTimestamp = currentTime;
              emit ExchangeRateUpdated(uint96(currentExchangeRate), newExchangeRate, currentTime);
          }
          /**
           * @notice Claim pending fees.
           * @dev This function must be called by the BoringVault.
           * @dev This function will lose precision if the exchange rate
           *      decimals is greater than the feeAsset's decimals.
           */
          function claimFees(ERC20 feeAsset) external {
              if (msg.sender != address(vault)) revert AccountantWithRateProviders__OnlyCallableByBoringVault();
              AccountantState storage state = accountantState;
              if (state.isPaused) revert AccountantWithRateProviders__Paused();
              if (state.feesOwedInBase == 0) revert AccountantWithRateProviders__ZeroFeesOwed();
              // Determine amount of fees owed in feeAsset.
              uint256 feesOwedInFeeAsset;
              RateProviderData memory data = rateProviderData[feeAsset];
              if (address(feeAsset) == address(base)) {
                  feesOwedInFeeAsset = state.feesOwedInBase;
              } else {
                  uint8 feeAssetDecimals = ERC20(feeAsset).decimals();
                  uint256 feesOwedInBaseUsingFeeAssetDecimals =
                      changeDecimals(state.feesOwedInBase, decimals, feeAssetDecimals);
                  if (data.isPeggedToBase) {
                      feesOwedInFeeAsset = feesOwedInBaseUsingFeeAssetDecimals;
                  } else {
                      uint256 rate = data.rateProvider.getRate();
                      feesOwedInFeeAsset = feesOwedInBaseUsingFeeAssetDecimals.mulDivDown(10 ** feeAssetDecimals, rate);
                  }
              }
              // Zero out fees owed.
              state.feesOwedInBase = 0;
              // Transfer fee asset to payout address.
              feeAsset.safeTransferFrom(msg.sender, state.payoutAddress, feesOwedInFeeAsset);
              emit FeesClaimed(address(feeAsset), feesOwedInFeeAsset);
          }
          // ========================================= RATE FUNCTIONS =========================================
          /**
           * @notice Get this BoringVault's current rate in the base.
           */
          function getRate() public view returns (uint256 rate) {
              rate = accountantState.exchangeRate;
          }
          /**
           * @notice Get this BoringVault's current rate in the base.
           * @dev Revert if paused.
           */
          function getRateSafe() external view returns (uint256 rate) {
              if (accountantState.isPaused) revert AccountantWithRateProviders__Paused();
              rate = getRate();
          }
          /**
           * @notice Get this BoringVault's current rate in the provided quote.
           * @dev `quote` must have its RateProviderData set, else this will revert.
           * @dev This function will lose precision if the exchange rate
           *      decimals is greater than the quote's decimals.
           */
          function getRateInQuote(ERC20 quote) public view returns (uint256 rateInQuote) {
              if (address(quote) == address(base)) {
                  rateInQuote = accountantState.exchangeRate;
              } else {
                  RateProviderData memory data = rateProviderData[quote];
                  uint8 quoteDecimals = ERC20(quote).decimals();
                  uint256 exchangeRateInQuoteDecimals = changeDecimals(accountantState.exchangeRate, decimals, quoteDecimals);
                  if (data.isPeggedToBase) {
                      rateInQuote = exchangeRateInQuoteDecimals;
                  } else {
                      uint256 quoteRate = data.rateProvider.getRate();
                      uint256 oneQuote = 10 ** quoteDecimals;
                      rateInQuote = oneQuote.mulDivDown(exchangeRateInQuoteDecimals, quoteRate);
                  }
              }
          }
          /**
           * @notice Get this BoringVault's current rate in the provided quote.
           * @dev `quote` must have its RateProviderData set, else this will revert.
           * @dev Revert if paused.
           */
          function getRateInQuoteSafe(ERC20 quote) external view returns (uint256 rateInQuote) {
              if (accountantState.isPaused) revert AccountantWithRateProviders__Paused();
              rateInQuote = getRateInQuote(quote);
          }
          // ========================================= INTERNAL HELPER FUNCTIONS =========================================
          /**
           * @notice Used to change the decimals of precision used for an amount.
           */
          function changeDecimals(uint256 amount, uint8 fromDecimals, uint8 toDecimals) internal pure returns (uint256) {
              if (fromDecimals == toDecimals) {
                  return amount;
              } else if (fromDecimals < toDecimals) {
                  return amount * 10 ** (toDecimals - fromDecimals);
              } else {
                  return amount / 10 ** (fromDecimals - toDecimals);
              }
          }
          /**
           * @notice Calculate fees owed in base.
           * @dev This function will update the highwater mark if the new exchange rate is higher.
           */
          function _calculateFeesOwed(
              AccountantState storage state,
              uint96 newExchangeRate,
              uint256 currentExchangeRate,
              uint256 currentTotalShares,
              uint64 currentTime
          ) internal {
              // Only update fees if we are not paused.
              // Update fee accounting.
              uint256 shareSupplyToUse = currentTotalShares;
              // Use the minimum between current total supply and total supply for last update.
              if (state.totalSharesLastUpdate < shareSupplyToUse) {
                  shareSupplyToUse = state.totalSharesLastUpdate;
              }
              // Determine management fees owned.
              uint256 timeDelta = currentTime - state.lastUpdateTimestamp;
              uint256 minimumAssets = newExchangeRate > currentExchangeRate
                  ? shareSupplyToUse.mulDivDown(currentExchangeRate, ONE_SHARE)
                  : shareSupplyToUse.mulDivDown(newExchangeRate, ONE_SHARE);
              uint256 managementFeesAnnual = minimumAssets.mulDivDown(state.managementFee, 1e4);
              uint256 newFeesOwedInBase = managementFeesAnnual.mulDivDown(timeDelta, 365 days);
              // Account for performance fees.
              if (newExchangeRate > state.highwaterMark) {
                  if (state.performanceFee > 0) {
                      uint256 changeInExchangeRate = newExchangeRate - state.highwaterMark;
                      uint256 yieldEarned = changeInExchangeRate.mulDivDown(shareSupplyToUse, ONE_SHARE);
                      uint256 performanceFeesOwedInBase = yieldEarned.mulDivDown(state.performanceFee, 1e4);
                      newFeesOwedInBase += performanceFeesOwedInBase;
                  }
                  // Always update the highwater mark if the new exchange rate is higher.
                  // This way if we are not iniitiall taking performance fees, we can start taking them
                  // without back charging them on past performance.
                  state.highwaterMark = newExchangeRate;
              }
              state.feesOwedInBase += uint128(newFeesOwedInBase);
          }
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity >=0.8.0;
      /// @notice Arithmetic library with operations for fixed-point numbers.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
      /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
      library FixedPointMathLib {
          /*//////////////////////////////////////////////////////////////
                          SIMPLIFIED FIXED POINT OPERATIONS
          //////////////////////////////////////////////////////////////*/
          uint256 internal constant MAX_UINT256 = 2**256 - 1;
          uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
          function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
              return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
          }
          function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
              return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
          }
          function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
              return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
          }
          function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
              return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
          }
          /*//////////////////////////////////////////////////////////////
                          LOW LEVEL FIXED POINT OPERATIONS
          //////////////////////////////////////////////////////////////*/
          function mulDivDown(
              uint256 x,
              uint256 y,
              uint256 denominator
          ) internal pure returns (uint256 z) {
              /// @solidity memory-safe-assembly
              assembly {
                  // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
                  if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                      revert(0, 0)
                  }
                  // Divide x * y by the denominator.
                  z := div(mul(x, y), denominator)
              }
          }
          function mulDivUp(
              uint256 x,
              uint256 y,
              uint256 denominator
          ) internal pure returns (uint256 z) {
              /// @solidity memory-safe-assembly
              assembly {
                  // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
                  if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                      revert(0, 0)
                  }
                  // If x * y modulo the denominator is strictly greater than 0,
                  // 1 is added to round up the division of x * y by the denominator.
                  z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
              }
          }
          function rpow(
              uint256 x,
              uint256 n,
              uint256 scalar
          ) internal pure returns (uint256 z) {
              /// @solidity memory-safe-assembly
              assembly {
                  switch x
                  case 0 {
                      switch n
                      case 0 {
                          // 0 ** 0 = 1
                          z := scalar
                      }
                      default {
                          // 0 ** n = 0
                          z := 0
                      }
                  }
                  default {
                      switch mod(n, 2)
                      case 0 {
                          // If n is even, store scalar in z for now.
                          z := scalar
                      }
                      default {
                          // If n is odd, store x in z for now.
                          z := x
                      }
                      // Shifting right by 1 is like dividing by 2.
                      let half := shr(1, scalar)
                      for {
                          // Shift n right by 1 before looping to halve it.
                          n := shr(1, n)
                      } n {
                          // Shift n right by 1 each iteration to halve it.
                          n := shr(1, n)
                      } {
                          // Revert immediately if x ** 2 would overflow.
                          // Equivalent to iszero(eq(div(xx, x), x)) here.
                          if shr(128, x) {
                              revert(0, 0)
                          }
                          // Store x squared.
                          let xx := mul(x, x)
                          // Round to the nearest number.
                          let xxRound := add(xx, half)
                          // Revert if xx + half overflowed.
                          if lt(xxRound, xx) {
                              revert(0, 0)
                          }
                          // Set x to scaled xxRound.
                          x := div(xxRound, scalar)
                          // If n is even:
                          if mod(n, 2) {
                              // Compute z * x.
                              let zx := mul(z, x)
                              // If z * x overflowed:
                              if iszero(eq(div(zx, x), z)) {
                                  // Revert if x is non-zero.
                                  if iszero(iszero(x)) {
                                      revert(0, 0)
                                  }
                              }
                              // Round to the nearest number.
                              let zxRound := add(zx, half)
                              // Revert if zx + half overflowed.
                              if lt(zxRound, zx) {
                                  revert(0, 0)
                              }
                              // Return properly scaled zxRound.
                              z := div(zxRound, scalar)
                          }
                      }
                  }
              }
          }
          /*//////////////////////////////////////////////////////////////
                              GENERAL NUMBER UTILITIES
          //////////////////////////////////////////////////////////////*/
          function sqrt(uint256 x) internal pure returns (uint256 z) {
              /// @solidity memory-safe-assembly
              assembly {
                  let y := x // We start y at x, which will help us make our initial estimate.
                  z := 181 // The "correct" value is 1, but this saves a multiplication later.
                  // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
                  // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
                  // We check y >= 2^(k + 8) but shift right by k bits
                  // each branch to ensure that if x >= 256, then y >= 256.
                  if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                      y := shr(128, y)
                      z := shl(64, z)
                  }
                  if iszero(lt(y, 0x1000000000000000000)) {
                      y := shr(64, y)
                      z := shl(32, z)
                  }
                  if iszero(lt(y, 0x10000000000)) {
                      y := shr(32, y)
                      z := shl(16, z)
                  }
                  if iszero(lt(y, 0x1000000)) {
                      y := shr(16, y)
                      z := shl(8, z)
                  }
                  // Goal was to get z*z*y within a small factor of x. More iterations could
                  // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
                  // We ensured y >= 256 so that the relative difference between y and y+1 is small.
                  // That's not possible if x < 256 but we can just verify those cases exhaustively.
                  // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
                  // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
                  // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
                  // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
                  // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
                  // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
                  // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
                  // There is no overflow risk here since y < 2^136 after the first branch above.
                  z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
                  // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
                  z := shr(1, add(z, div(x, z)))
                  z := shr(1, add(z, div(x, z)))
                  z := shr(1, add(z, div(x, z)))
                  z := shr(1, add(z, div(x, z)))
                  z := shr(1, add(z, div(x, z)))
                  z := shr(1, add(z, div(x, z)))
                  z := shr(1, add(z, div(x, z)))
                  // If x+1 is a perfect square, the Babylonian method cycles between
                  // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
                  // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
                  // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
                  // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
                  z := sub(z, lt(div(x, z), z))
              }
          }
          function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
              /// @solidity memory-safe-assembly
              assembly {
                  // Mod x by y. Note this will return
                  // 0 instead of reverting if y is zero.
                  z := mod(x, y)
              }
          }
          function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
              /// @solidity memory-safe-assembly
              assembly {
                  // Divide x by y. Note this will return
                  // 0 instead of reverting if y is zero.
                  r := div(x, y)
              }
          }
          function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
              /// @solidity memory-safe-assembly
              assembly {
                  // Add 1 to x * y if x % y > 0. Note this will
                  // return 0 instead of reverting if y is zero.
                  z := add(gt(mod(x, y), 0), div(x, y))
              }
          }
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity >=0.8.0;
      import {ERC20} from "../tokens/ERC20.sol";
      /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
      /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
      /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
      library SafeTransferLib {
          /*//////////////////////////////////////////////////////////////
                                   ETH OPERATIONS
          //////////////////////////////////////////////////////////////*/
          function safeTransferETH(address to, uint256 amount) internal {
              bool success;
              /// @solidity memory-safe-assembly
              assembly {
                  // Transfer the ETH and store if it succeeded or not.
                  success := call(gas(), to, amount, 0, 0, 0, 0)
              }
              require(success, "ETH_TRANSFER_FAILED");
          }
          /*//////////////////////////////////////////////////////////////
                                  ERC20 OPERATIONS
          //////////////////////////////////////////////////////////////*/
          function safeTransferFrom(
              ERC20 token,
              address from,
              address to,
              uint256 amount
          ) internal {
              bool success;
              /// @solidity memory-safe-assembly
              assembly {
                  // Get a pointer to some free memory.
                  let freeMemoryPointer := mload(0x40)
                  // Write the abi-encoded calldata into memory, beginning with the function selector.
                  mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
                  mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
                  mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                  mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                  success := and(
                      // Set success to whether the call reverted, if not we check it either
                      // returned exactly 1 (can't just be non-zero data), or had no return data.
                      or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                      // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                      // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                      // Counterintuitively, this call must be positioned second to the or() call in the
                      // surrounding and() call or else returndatasize() will be zero during the computation.
                      call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
                  )
              }
              require(success, "TRANSFER_FROM_FAILED");
          }
          function safeTransfer(
              ERC20 token,
              address to,
              uint256 amount
          ) internal {
              bool success;
              /// @solidity memory-safe-assembly
              assembly {
                  // Get a pointer to some free memory.
                  let freeMemoryPointer := mload(0x40)
                  // Write the abi-encoded calldata into memory, beginning with the function selector.
                  mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
                  mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                  mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                  success := and(
                      // Set success to whether the call reverted, if not we check it either
                      // returned exactly 1 (can't just be non-zero data), or had no return data.
                      or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                      // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                      // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                      // Counterintuitively, this call must be positioned second to the or() call in the
                      // surrounding and() call or else returndatasize() will be zero during the computation.
                      call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                  )
              }
              require(success, "TRANSFER_FAILED");
          }
          function safeApprove(
              ERC20 token,
              address to,
              uint256 amount
          ) internal {
              bool success;
              /// @solidity memory-safe-assembly
              assembly {
                  // Get a pointer to some free memory.
                  let freeMemoryPointer := mload(0x40)
                  // Write the abi-encoded calldata into memory, beginning with the function selector.
                  mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
                  mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                  mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                  success := and(
                      // Set success to whether the call reverted, if not we check it either
                      // returned exactly 1 (can't just be non-zero data), or had no return data.
                      or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                      // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                      // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                      // Counterintuitively, this call must be positioned second to the or() call in the
                      // surrounding and() call or else returndatasize() will be zero during the computation.
                      call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                  )
              }
              require(success, "APPROVE_FAILED");
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.21;
      interface BeforeTransferHook {
          function beforeTransfer(address from, address to, address operator) external view;
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity >=0.8.0;
      /// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
      /// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
      abstract contract Auth {
          event OwnershipTransferred(address indexed user, address indexed newOwner);
          event AuthorityUpdated(address indexed user, Authority indexed newAuthority);
          address public owner;
          Authority public authority;
          constructor(address _owner, Authority _authority) {
              owner = _owner;
              authority = _authority;
              emit OwnershipTransferred(msg.sender, _owner);
              emit AuthorityUpdated(msg.sender, _authority);
          }
          modifier requiresAuth() virtual {
              require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");
              _;
          }
          function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
              Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.
              // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
              // aware that this makes protected functions uncallable even to the owner if the authority is out of order.
              return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
          }
          function setAuthority(Authority newAuthority) public virtual {
              // We check if the caller is the owner first because we want to ensure they can
              // always swap out the authority even if it's reverting or using up a lot of gas.
              require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));
              authority = newAuthority;
              emit AuthorityUpdated(msg.sender, newAuthority);
          }
          function transferOwnership(address newOwner) public virtual requiresAuth {
              owner = newOwner;
              emit OwnershipTransferred(msg.sender, newOwner);
          }
      }
      /// @notice A generic interface for a contract which provides authorization data to an Auth instance.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
      /// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
      interface Authority {
          function canCall(
              address user,
              address target,
              bytes4 functionSig
          ) external view returns (bool);
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity >=0.8.0;
      /// @notice Gas optimized reentrancy protection for smart contracts.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
      /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
      abstract contract ReentrancyGuard {
          uint256 private locked = 1;
          modifier nonReentrant() virtual {
              require(locked == 1, "REENTRANCY");
              locked = 2;
              _;
              locked = 1;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.21;
      interface IPausable {
          function pause() external;
          function unpause() external;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
      pragma solidity ^0.8.20;
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev The ETH balance of the account is not enough to perform the operation.
           */
          error AddressInsufficientBalance(address account);
          /**
           * @dev There's no code at `target` (it is not a contract).
           */
          error AddressEmptyCode(address target);
          /**
           * @dev A call to an address target failed. The target may have reverted.
           */
          error FailedInnerCall();
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              if (address(this).balance < amount) {
                  revert AddressInsufficientBalance(address(this));
              }
              (bool success, ) = recipient.call{value: amount}("");
              if (!success) {
                  revert FailedInnerCall();
              }
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain `call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason or custom error, it is bubbled
           * up by this function (like regular Solidity function calls). However, if
           * the call reverted with no returned reason, this function reverts with a
           * {FailedInnerCall} error.
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              if (address(this).balance < value) {
                  revert AddressInsufficientBalance(address(this));
              }
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResultFromTarget(target, success, returndata);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResultFromTarget(target, success, returndata);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return verifyCallResultFromTarget(target, success, returndata);
          }
          /**
           * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
           * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
           * unsuccessful call.
           */
          function verifyCallResultFromTarget(
              address target,
              bool success,
              bytes memory returndata
          ) internal view returns (bytes memory) {
              if (!success) {
                  _revert(returndata);
              } else {
                  // only check if target is a contract if the call was successful and the return data is empty
                  // otherwise we already know that it was a contract
                  if (returndata.length == 0 && target.code.length == 0) {
                      revert AddressEmptyCode(target);
                  }
                  return returndata;
              }
          }
          /**
           * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
           * revert reason or with a default {FailedInnerCall} error.
           */
          function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
              if (!success) {
                  _revert(returndata);
              } else {
                  return returndata;
              }
          }
          /**
           * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
           */
          function _revert(bytes memory returndata) private pure {
              // Look for revert reason and bubble it up if present
              if (returndata.length > 0) {
                  // The easiest way to bubble the revert reason is using memory via assembly
                  /// @solidity memory-safe-assembly
                  assembly {
                      let returndata_size := mload(returndata)
                      revert(add(32, returndata), returndata_size)
                  }
              } else {
                  revert FailedInnerCall();
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/utils/ERC721Holder.sol)
      pragma solidity ^0.8.20;
      import {IERC721Receiver} from "../IERC721Receiver.sol";
      /**
       * @dev Implementation of the {IERC721Receiver} interface.
       *
       * Accepts all token transfers.
       * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or
       * {IERC721-setApprovalForAll}.
       */
      abstract contract ERC721Holder is IERC721Receiver {
          /**
           * @dev See {IERC721Receiver-onERC721Received}.
           *
           * Always returns `IERC721Receiver.onERC721Received.selector`.
           */
          function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
              return this.onERC721Received.selector;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/utils/ERC1155Holder.sol)
      pragma solidity ^0.8.20;
      import {IERC165, ERC165} from "../../../utils/introspection/ERC165.sol";
      import {IERC1155Receiver} from "../IERC1155Receiver.sol";
      /**
       * @dev Simple implementation of `IERC1155Receiver` that will allow a contract to hold ERC1155 tokens.
       *
       * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be
       * stuck.
       */
      abstract contract ERC1155Holder is ERC165, IERC1155Receiver {
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
              return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
          }
          function onERC1155Received(
              address,
              address,
              uint256,
              uint256,
              bytes memory
          ) public virtual override returns (bytes4) {
              return this.onERC1155Received.selector;
          }
          function onERC1155BatchReceived(
              address,
              address,
              uint256[] memory,
              uint256[] memory,
              bytes memory
          ) public virtual override returns (bytes4) {
              return this.onERC1155BatchReceived.selector;
          }
      }
      // SPDX-License-Identifier: GPL-3.0-or-later
      // 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.8.0;
      interface IRateProvider {
          function getRate() external view returns (uint256);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)
      pragma solidity ^0.8.20;
      /**
       * @title ERC721 token receiver interface
       * @dev Interface for any contract that wants to support safeTransfers
       * from ERC721 asset contracts.
       */
      interface IERC721Receiver {
          /**
           * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
           * by `operator` from `from`, this function is called.
           *
           * It must return its Solidity selector to confirm the token transfer.
           * If any other value is returned or the interface is not implemented by the recipient, the transfer will be
           * reverted.
           *
           * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
           */
          function onERC721Received(
              address operator,
              address from,
              uint256 tokenId,
              bytes calldata data
          ) external returns (bytes4);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.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 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);
       * }
       * ```
       */
      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.0.0) (token/ERC1155/IERC1155Receiver.sol)
      pragma solidity ^0.8.20;
      import {IERC165} from "../../utils/introspection/IERC165.sol";
      /**
       * @dev Interface that must be implemented by smart contracts in order to receive
       * ERC-1155 token transfers.
       */
      interface IERC1155Receiver is IERC165 {
          /**
           * @dev Handles the receipt of a single ERC1155 token type. This function is
           * called at the end of a `safeTransferFrom` after the balance has been updated.
           *
           * NOTE: To accept the transfer, this must return
           * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
           * (i.e. 0xf23a6e61, or its own function selector).
           *
           * @param operator The address which initiated the transfer (i.e. msg.sender)
           * @param from The address which previously owned the token
           * @param id The ID of the token being transferred
           * @param value The amount of tokens being transferred
           * @param data Additional data with no specified format
           * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
           */
          function onERC1155Received(
              address operator,
              address from,
              uint256 id,
              uint256 value,
              bytes calldata data
          ) external returns (bytes4);
          /**
           * @dev Handles the receipt of a multiple ERC1155 token types. This function
           * is called at the end of a `safeBatchTransferFrom` after the balances have
           * been updated.
           *
           * NOTE: To accept the transfer(s), this must return
           * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
           * (i.e. 0xbc197c81, or its own function selector).
           *
           * @param operator The address which initiated the batch transfer (i.e. msg.sender)
           * @param from The address which previously owned the token
           * @param ids An array containing ids of each token being transferred (order and length must match values array)
           * @param values An array containing amounts of each token being transferred (order and length must match ids array)
           * @param data Additional data with no specified format
           * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
           */
          function onERC1155BatchReceived(
              address operator,
              address from,
              uint256[] calldata ids,
              uint256[] calldata values,
              bytes calldata data
          ) external returns (bytes4);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
      pragma solidity ^0.8.20;
      /**
       * @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);
      }
      

      File 2 of 5: BoringVault
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.21;
      import {Address} from "@openzeppelin/contracts/utils/Address.sol";
      import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
      import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
      import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
      import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
      import {ERC20} from "@solmate/tokens/ERC20.sol";
      import {BeforeTransferHook} from "src/interfaces/BeforeTransferHook.sol";
      import {Auth, Authority} from "@solmate/auth/Auth.sol";
      contract BoringVault is ERC20, Auth, ERC721Holder, ERC1155Holder {
          using Address for address;
          using SafeTransferLib for ERC20;
          using FixedPointMathLib for uint256;
          // ========================================= STATE =========================================
          /**
           * @notice Contract responsbile for implementing `beforeTransfer`.
           */
          BeforeTransferHook public hook;
          //============================== EVENTS ===============================
          event Enter(address indexed from, address indexed asset, uint256 amount, address indexed to, uint256 shares);
          event Exit(address indexed to, address indexed asset, uint256 amount, address indexed from, uint256 shares);
          //============================== CONSTRUCTOR ===============================
          constructor(address _owner, string memory _name, string memory _symbol, uint8 _decimals)
              ERC20(_name, _symbol, _decimals)
              Auth(_owner, Authority(address(0)))
          {}
          //============================== MANAGE ===============================
          /**
           * @notice Allows manager to make an arbitrary function call from this contract.
           * @dev Callable by MANAGER_ROLE.
           */
          function manage(address target, bytes calldata data, uint256 value)
              external
              requiresAuth
              returns (bytes memory result)
          {
              result = target.functionCallWithValue(data, value);
          }
          /**
           * @notice Allows manager to make arbitrary function calls from this contract.
           * @dev Callable by MANAGER_ROLE.
           */
          function manage(address[] calldata targets, bytes[] calldata data, uint256[] calldata values)
              external
              requiresAuth
              returns (bytes[] memory results)
          {
              uint256 targetsLength = targets.length;
              results = new bytes[](targetsLength);
              for (uint256 i; i < targetsLength; ++i) {
                  results[i] = targets[i].functionCallWithValue(data[i], values[i]);
              }
          }
          //============================== ENTER ===============================
          /**
           * @notice Allows minter to mint shares, in exchange for assets.
           * @dev If assetAmount is zero, no assets are transferred in.
           * @dev Callable by MINTER_ROLE.
           */
          function enter(address from, ERC20 asset, uint256 assetAmount, address to, uint256 shareAmount)
              external
              requiresAuth
          {
              // Transfer assets in
              if (assetAmount > 0) asset.safeTransferFrom(from, address(this), assetAmount);
              // Mint shares.
              _mint(to, shareAmount);
              emit Enter(from, address(asset), assetAmount, to, shareAmount);
          }
          //============================== EXIT ===============================
          /**
           * @notice Allows burner to burn shares, in exchange for assets.
           * @dev If assetAmount is zero, no assets are transferred out.
           * @dev Callable by BURNER_ROLE.
           */
          function exit(address to, ERC20 asset, uint256 assetAmount, address from, uint256 shareAmount)
              external
              requiresAuth
          {
              // Burn shares.
              _burn(from, shareAmount);
              // Transfer assets out.
              if (assetAmount > 0) asset.safeTransfer(to, assetAmount);
              emit Exit(to, address(asset), assetAmount, from, shareAmount);
          }
          //============================== BEFORE TRANSFER HOOK ===============================
          /**
           * @notice Sets the share locker.
           * @notice If set to zero address, the share locker logic is disabled.
           * @dev Callable by OWNER_ROLE.
           */
          function setBeforeTransferHook(address _hook) external requiresAuth {
              hook = BeforeTransferHook(_hook);
          }
          /**
           * @notice Call `beforeTransferHook` passing in `from` `to`, and `msg.sender`.
           */
          function _callBeforeTransfer(address from, address to) internal view {
              if (address(hook) != address(0)) hook.beforeTransfer(from, to, msg.sender);
          }
          function transfer(address to, uint256 amount) public override returns (bool) {
              _callBeforeTransfer(msg.sender, to);
              return super.transfer(to, amount);
          }
          function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
              _callBeforeTransfer(from, to);
              return super.transferFrom(from, to, amount);
          }
          //============================== RECEIVE ===============================
          receive() external payable {}
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
      pragma solidity ^0.8.20;
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev The ETH balance of the account is not enough to perform the operation.
           */
          error AddressInsufficientBalance(address account);
          /**
           * @dev There's no code at `target` (it is not a contract).
           */
          error AddressEmptyCode(address target);
          /**
           * @dev A call to an address target failed. The target may have reverted.
           */
          error FailedInnerCall();
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              if (address(this).balance < amount) {
                  revert AddressInsufficientBalance(address(this));
              }
              (bool success, ) = recipient.call{value: amount}("");
              if (!success) {
                  revert FailedInnerCall();
              }
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain `call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason or custom error, it is bubbled
           * up by this function (like regular Solidity function calls). However, if
           * the call reverted with no returned reason, this function reverts with a
           * {FailedInnerCall} error.
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              if (address(this).balance < value) {
                  revert AddressInsufficientBalance(address(this));
              }
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResultFromTarget(target, success, returndata);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResultFromTarget(target, success, returndata);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return verifyCallResultFromTarget(target, success, returndata);
          }
          /**
           * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
           * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
           * unsuccessful call.
           */
          function verifyCallResultFromTarget(
              address target,
              bool success,
              bytes memory returndata
          ) internal view returns (bytes memory) {
              if (!success) {
                  _revert(returndata);
              } else {
                  // only check if target is a contract if the call was successful and the return data is empty
                  // otherwise we already know that it was a contract
                  if (returndata.length == 0 && target.code.length == 0) {
                      revert AddressEmptyCode(target);
                  }
                  return returndata;
              }
          }
          /**
           * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
           * revert reason or with a default {FailedInnerCall} error.
           */
          function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
              if (!success) {
                  _revert(returndata);
              } else {
                  return returndata;
              }
          }
          /**
           * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
           */
          function _revert(bytes memory returndata) private pure {
              // Look for revert reason and bubble it up if present
              if (returndata.length > 0) {
                  // The easiest way to bubble the revert reason is using memory via assembly
                  /// @solidity memory-safe-assembly
                  assembly {
                      let returndata_size := mload(returndata)
                      revert(add(32, returndata), returndata_size)
                  }
              } else {
                  revert FailedInnerCall();
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/utils/ERC721Holder.sol)
      pragma solidity ^0.8.20;
      import {IERC721Receiver} from "../IERC721Receiver.sol";
      /**
       * @dev Implementation of the {IERC721Receiver} interface.
       *
       * Accepts all token transfers.
       * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or
       * {IERC721-setApprovalForAll}.
       */
      abstract contract ERC721Holder is IERC721Receiver {
          /**
           * @dev See {IERC721Receiver-onERC721Received}.
           *
           * Always returns `IERC721Receiver.onERC721Received.selector`.
           */
          function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
              return this.onERC721Received.selector;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/utils/ERC1155Holder.sol)
      pragma solidity ^0.8.20;
      import {IERC165, ERC165} from "../../../utils/introspection/ERC165.sol";
      import {IERC1155Receiver} from "../IERC1155Receiver.sol";
      /**
       * @dev Simple implementation of `IERC1155Receiver` that will allow a contract to hold ERC1155 tokens.
       *
       * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be
       * stuck.
       */
      abstract contract ERC1155Holder is ERC165, IERC1155Receiver {
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
              return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
          }
          function onERC1155Received(
              address,
              address,
              uint256,
              uint256,
              bytes memory
          ) public virtual override returns (bytes4) {
              return this.onERC1155Received.selector;
          }
          function onERC1155BatchReceived(
              address,
              address,
              uint256[] memory,
              uint256[] memory,
              bytes memory
          ) public virtual override returns (bytes4) {
              return this.onERC1155BatchReceived.selector;
          }
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity >=0.8.0;
      /// @notice Arithmetic library with operations for fixed-point numbers.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
      /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
      library FixedPointMathLib {
          /*//////////////////////////////////////////////////////////////
                          SIMPLIFIED FIXED POINT OPERATIONS
          //////////////////////////////////////////////////////////////*/
          uint256 internal constant MAX_UINT256 = 2**256 - 1;
          uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
          function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
              return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
          }
          function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
              return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
          }
          function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
              return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
          }
          function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
              return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
          }
          /*//////////////////////////////////////////////////////////////
                          LOW LEVEL FIXED POINT OPERATIONS
          //////////////////////////////////////////////////////////////*/
          function mulDivDown(
              uint256 x,
              uint256 y,
              uint256 denominator
          ) internal pure returns (uint256 z) {
              /// @solidity memory-safe-assembly
              assembly {
                  // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
                  if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                      revert(0, 0)
                  }
                  // Divide x * y by the denominator.
                  z := div(mul(x, y), denominator)
              }
          }
          function mulDivUp(
              uint256 x,
              uint256 y,
              uint256 denominator
          ) internal pure returns (uint256 z) {
              /// @solidity memory-safe-assembly
              assembly {
                  // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
                  if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                      revert(0, 0)
                  }
                  // If x * y modulo the denominator is strictly greater than 0,
                  // 1 is added to round up the division of x * y by the denominator.
                  z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
              }
          }
          function rpow(
              uint256 x,
              uint256 n,
              uint256 scalar
          ) internal pure returns (uint256 z) {
              /// @solidity memory-safe-assembly
              assembly {
                  switch x
                  case 0 {
                      switch n
                      case 0 {
                          // 0 ** 0 = 1
                          z := scalar
                      }
                      default {
                          // 0 ** n = 0
                          z := 0
                      }
                  }
                  default {
                      switch mod(n, 2)
                      case 0 {
                          // If n is even, store scalar in z for now.
                          z := scalar
                      }
                      default {
                          // If n is odd, store x in z for now.
                          z := x
                      }
                      // Shifting right by 1 is like dividing by 2.
                      let half := shr(1, scalar)
                      for {
                          // Shift n right by 1 before looping to halve it.
                          n := shr(1, n)
                      } n {
                          // Shift n right by 1 each iteration to halve it.
                          n := shr(1, n)
                      } {
                          // Revert immediately if x ** 2 would overflow.
                          // Equivalent to iszero(eq(div(xx, x), x)) here.
                          if shr(128, x) {
                              revert(0, 0)
                          }
                          // Store x squared.
                          let xx := mul(x, x)
                          // Round to the nearest number.
                          let xxRound := add(xx, half)
                          // Revert if xx + half overflowed.
                          if lt(xxRound, xx) {
                              revert(0, 0)
                          }
                          // Set x to scaled xxRound.
                          x := div(xxRound, scalar)
                          // If n is even:
                          if mod(n, 2) {
                              // Compute z * x.
                              let zx := mul(z, x)
                              // If z * x overflowed:
                              if iszero(eq(div(zx, x), z)) {
                                  // Revert if x is non-zero.
                                  if iszero(iszero(x)) {
                                      revert(0, 0)
                                  }
                              }
                              // Round to the nearest number.
                              let zxRound := add(zx, half)
                              // Revert if zx + half overflowed.
                              if lt(zxRound, zx) {
                                  revert(0, 0)
                              }
                              // Return properly scaled zxRound.
                              z := div(zxRound, scalar)
                          }
                      }
                  }
              }
          }
          /*//////////////////////////////////////////////////////////////
                              GENERAL NUMBER UTILITIES
          //////////////////////////////////////////////////////////////*/
          function sqrt(uint256 x) internal pure returns (uint256 z) {
              /// @solidity memory-safe-assembly
              assembly {
                  let y := x // We start y at x, which will help us make our initial estimate.
                  z := 181 // The "correct" value is 1, but this saves a multiplication later.
                  // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
                  // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
                  // We check y >= 2^(k + 8) but shift right by k bits
                  // each branch to ensure that if x >= 256, then y >= 256.
                  if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                      y := shr(128, y)
                      z := shl(64, z)
                  }
                  if iszero(lt(y, 0x1000000000000000000)) {
                      y := shr(64, y)
                      z := shl(32, z)
                  }
                  if iszero(lt(y, 0x10000000000)) {
                      y := shr(32, y)
                      z := shl(16, z)
                  }
                  if iszero(lt(y, 0x1000000)) {
                      y := shr(16, y)
                      z := shl(8, z)
                  }
                  // Goal was to get z*z*y within a small factor of x. More iterations could
                  // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
                  // We ensured y >= 256 so that the relative difference between y and y+1 is small.
                  // That's not possible if x < 256 but we can just verify those cases exhaustively.
                  // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
                  // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
                  // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
                  // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
                  // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
                  // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
                  // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
                  // There is no overflow risk here since y < 2^136 after the first branch above.
                  z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
                  // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
                  z := shr(1, add(z, div(x, z)))
                  z := shr(1, add(z, div(x, z)))
                  z := shr(1, add(z, div(x, z)))
                  z := shr(1, add(z, div(x, z)))
                  z := shr(1, add(z, div(x, z)))
                  z := shr(1, add(z, div(x, z)))
                  z := shr(1, add(z, div(x, z)))
                  // If x+1 is a perfect square, the Babylonian method cycles between
                  // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
                  // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
                  // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
                  // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
                  z := sub(z, lt(div(x, z), z))
              }
          }
          function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
              /// @solidity memory-safe-assembly
              assembly {
                  // Mod x by y. Note this will return
                  // 0 instead of reverting if y is zero.
                  z := mod(x, y)
              }
          }
          function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
              /// @solidity memory-safe-assembly
              assembly {
                  // Divide x by y. Note this will return
                  // 0 instead of reverting if y is zero.
                  r := div(x, y)
              }
          }
          function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
              /// @solidity memory-safe-assembly
              assembly {
                  // Add 1 to x * y if x % y > 0. Note this will
                  // return 0 instead of reverting if y is zero.
                  z := add(gt(mod(x, y), 0), div(x, y))
              }
          }
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity >=0.8.0;
      import {ERC20} from "../tokens/ERC20.sol";
      /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
      /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
      /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
      library SafeTransferLib {
          /*//////////////////////////////////////////////////////////////
                                   ETH OPERATIONS
          //////////////////////////////////////////////////////////////*/
          function safeTransferETH(address to, uint256 amount) internal {
              bool success;
              /// @solidity memory-safe-assembly
              assembly {
                  // Transfer the ETH and store if it succeeded or not.
                  success := call(gas(), to, amount, 0, 0, 0, 0)
              }
              require(success, "ETH_TRANSFER_FAILED");
          }
          /*//////////////////////////////////////////////////////////////
                                  ERC20 OPERATIONS
          //////////////////////////////////////////////////////////////*/
          function safeTransferFrom(
              ERC20 token,
              address from,
              address to,
              uint256 amount
          ) internal {
              bool success;
              /// @solidity memory-safe-assembly
              assembly {
                  // Get a pointer to some free memory.
                  let freeMemoryPointer := mload(0x40)
                  // Write the abi-encoded calldata into memory, beginning with the function selector.
                  mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
                  mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
                  mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                  mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                  success := and(
                      // Set success to whether the call reverted, if not we check it either
                      // returned exactly 1 (can't just be non-zero data), or had no return data.
                      or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                      // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                      // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                      // Counterintuitively, this call must be positioned second to the or() call in the
                      // surrounding and() call or else returndatasize() will be zero during the computation.
                      call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
                  )
              }
              require(success, "TRANSFER_FROM_FAILED");
          }
          function safeTransfer(
              ERC20 token,
              address to,
              uint256 amount
          ) internal {
              bool success;
              /// @solidity memory-safe-assembly
              assembly {
                  // Get a pointer to some free memory.
                  let freeMemoryPointer := mload(0x40)
                  // Write the abi-encoded calldata into memory, beginning with the function selector.
                  mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
                  mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                  mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                  success := and(
                      // Set success to whether the call reverted, if not we check it either
                      // returned exactly 1 (can't just be non-zero data), or had no return data.
                      or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                      // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                      // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                      // Counterintuitively, this call must be positioned second to the or() call in the
                      // surrounding and() call or else returndatasize() will be zero during the computation.
                      call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                  )
              }
              require(success, "TRANSFER_FAILED");
          }
          function safeApprove(
              ERC20 token,
              address to,
              uint256 amount
          ) internal {
              bool success;
              /// @solidity memory-safe-assembly
              assembly {
                  // Get a pointer to some free memory.
                  let freeMemoryPointer := mload(0x40)
                  // Write the abi-encoded calldata into memory, beginning with the function selector.
                  mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
                  mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                  mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                  success := and(
                      // Set success to whether the call reverted, if not we check it either
                      // returned exactly 1 (can't just be non-zero data), or had no return data.
                      or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                      // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                      // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                      // Counterintuitively, this call must be positioned second to the or() call in the
                      // surrounding and() call or else returndatasize() will be zero during the computation.
                      call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                  )
              }
              require(success, "APPROVE_FAILED");
          }
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity >=0.8.0;
      /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
      /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
      /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
      abstract contract ERC20 {
          /*//////////////////////////////////////////////////////////////
                                       EVENTS
          //////////////////////////////////////////////////////////////*/
          event Transfer(address indexed from, address indexed to, uint256 amount);
          event Approval(address indexed owner, address indexed spender, uint256 amount);
          /*//////////////////////////////////////////////////////////////
                                  METADATA STORAGE
          //////////////////////////////////////////////////////////////*/
          string public name;
          string public symbol;
          uint8 public immutable decimals;
          /*//////////////////////////////////////////////////////////////
                                    ERC20 STORAGE
          //////////////////////////////////////////////////////////////*/
          uint256 public totalSupply;
          mapping(address => uint256) public balanceOf;
          mapping(address => mapping(address => uint256)) public allowance;
          /*//////////////////////////////////////////////////////////////
                                  EIP-2612 STORAGE
          //////////////////////////////////////////////////////////////*/
          uint256 internal immutable INITIAL_CHAIN_ID;
          bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
          mapping(address => uint256) public nonces;
          /*//////////////////////////////////////////////////////////////
                                     CONSTRUCTOR
          //////////////////////////////////////////////////////////////*/
          constructor(
              string memory _name,
              string memory _symbol,
              uint8 _decimals
          ) {
              name = _name;
              symbol = _symbol;
              decimals = _decimals;
              INITIAL_CHAIN_ID = block.chainid;
              INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
          }
          /*//////////////////////////////////////////////////////////////
                                     ERC20 LOGIC
          //////////////////////////////////////////////////////////////*/
          function approve(address spender, uint256 amount) public virtual returns (bool) {
              allowance[msg.sender][spender] = amount;
              emit Approval(msg.sender, spender, amount);
              return true;
          }
          function transfer(address to, uint256 amount) public virtual returns (bool) {
              balanceOf[msg.sender] -= amount;
              // Cannot overflow because the sum of all user
              // balances can't exceed the max uint256 value.
              unchecked {
                  balanceOf[to] += amount;
              }
              emit Transfer(msg.sender, to, amount);
              return true;
          }
          function transferFrom(
              address from,
              address to,
              uint256 amount
          ) public virtual returns (bool) {
              uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
              if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
              balanceOf[from] -= amount;
              // Cannot overflow because the sum of all user
              // balances can't exceed the max uint256 value.
              unchecked {
                  balanceOf[to] += amount;
              }
              emit Transfer(from, to, amount);
              return true;
          }
          /*//////////////////////////////////////////////////////////////
                                   EIP-2612 LOGIC
          //////////////////////////////////////////////////////////////*/
          function permit(
              address owner,
              address spender,
              uint256 value,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) public virtual {
              require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
              // Unchecked because the only math done is incrementing
              // the owner's nonce which cannot realistically overflow.
              unchecked {
                  address recoveredAddress = ecrecover(
                      keccak256(
                          abi.encodePacked(
                              "\\x19\\x01",
                              DOMAIN_SEPARATOR(),
                              keccak256(
                                  abi.encode(
                                      keccak256(
                                          "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                      ),
                                      owner,
                                      spender,
                                      value,
                                      nonces[owner]++,
                                      deadline
                                  )
                              )
                          )
                      ),
                      v,
                      r,
                      s
                  );
                  require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
                  allowance[recoveredAddress][spender] = value;
              }
              emit Approval(owner, spender, value);
          }
          function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
              return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
          }
          function computeDomainSeparator() internal view virtual returns (bytes32) {
              return
                  keccak256(
                      abi.encode(
                          keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                          keccak256(bytes(name)),
                          keccak256("1"),
                          block.chainid,
                          address(this)
                      )
                  );
          }
          /*//////////////////////////////////////////////////////////////
                              INTERNAL MINT/BURN LOGIC
          //////////////////////////////////////////////////////////////*/
          function _mint(address to, uint256 amount) internal virtual {
              totalSupply += amount;
              // Cannot overflow because the sum of all user
              // balances can't exceed the max uint256 value.
              unchecked {
                  balanceOf[to] += amount;
              }
              emit Transfer(address(0), to, amount);
          }
          function _burn(address from, uint256 amount) internal virtual {
              balanceOf[from] -= amount;
              // Cannot underflow because a user's balance
              // will never be larger than the total supply.
              unchecked {
                  totalSupply -= amount;
              }
              emit Transfer(from, address(0), amount);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.21;
      interface BeforeTransferHook {
          function beforeTransfer(address from, address to, address operator) external view;
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity >=0.8.0;
      /// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
      /// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
      abstract contract Auth {
          event OwnershipTransferred(address indexed user, address indexed newOwner);
          event AuthorityUpdated(address indexed user, Authority indexed newAuthority);
          address public owner;
          Authority public authority;
          constructor(address _owner, Authority _authority) {
              owner = _owner;
              authority = _authority;
              emit OwnershipTransferred(msg.sender, _owner);
              emit AuthorityUpdated(msg.sender, _authority);
          }
          modifier requiresAuth() virtual {
              require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");
              _;
          }
          function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
              Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.
              // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
              // aware that this makes protected functions uncallable even to the owner if the authority is out of order.
              return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
          }
          function setAuthority(Authority newAuthority) public virtual {
              // We check if the caller is the owner first because we want to ensure they can
              // always swap out the authority even if it's reverting or using up a lot of gas.
              require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));
              authority = newAuthority;
              emit AuthorityUpdated(msg.sender, newAuthority);
          }
          function transferOwnership(address newOwner) public virtual requiresAuth {
              owner = newOwner;
              emit OwnershipTransferred(msg.sender, newOwner);
          }
      }
      /// @notice A generic interface for a contract which provides authorization data to an Auth instance.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
      /// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
      interface Authority {
          function canCall(
              address user,
              address target,
              bytes4 functionSig
          ) external view returns (bool);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)
      pragma solidity ^0.8.20;
      /**
       * @title ERC721 token receiver interface
       * @dev Interface for any contract that wants to support safeTransfers
       * from ERC721 asset contracts.
       */
      interface IERC721Receiver {
          /**
           * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
           * by `operator` from `from`, this function is called.
           *
           * It must return its Solidity selector to confirm the token transfer.
           * If any other value is returned or the interface is not implemented by the recipient, the transfer will be
           * reverted.
           *
           * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
           */
          function onERC721Received(
              address operator,
              address from,
              uint256 tokenId,
              bytes calldata data
          ) external returns (bytes4);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.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 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);
       * }
       * ```
       */
      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.0.0) (token/ERC1155/IERC1155Receiver.sol)
      pragma solidity ^0.8.20;
      import {IERC165} from "../../utils/introspection/IERC165.sol";
      /**
       * @dev Interface that must be implemented by smart contracts in order to receive
       * ERC-1155 token transfers.
       */
      interface IERC1155Receiver is IERC165 {
          /**
           * @dev Handles the receipt of a single ERC1155 token type. This function is
           * called at the end of a `safeTransferFrom` after the balance has been updated.
           *
           * NOTE: To accept the transfer, this must return
           * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
           * (i.e. 0xf23a6e61, or its own function selector).
           *
           * @param operator The address which initiated the transfer (i.e. msg.sender)
           * @param from The address which previously owned the token
           * @param id The ID of the token being transferred
           * @param value The amount of tokens being transferred
           * @param data Additional data with no specified format
           * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
           */
          function onERC1155Received(
              address operator,
              address from,
              uint256 id,
              uint256 value,
              bytes calldata data
          ) external returns (bytes4);
          /**
           * @dev Handles the receipt of a multiple ERC1155 token types. This function
           * is called at the end of a `safeBatchTransferFrom` after the balances have
           * been updated.
           *
           * NOTE: To accept the transfer(s), this must return
           * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
           * (i.e. 0xbc197c81, or its own function selector).
           *
           * @param operator The address which initiated the batch transfer (i.e. msg.sender)
           * @param from The address which previously owned the token
           * @param ids An array containing ids of each token being transferred (order and length must match values array)
           * @param values An array containing amounts of each token being transferred (order and length must match ids array)
           * @param data Additional data with no specified format
           * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
           */
          function onERC1155BatchReceived(
              address operator,
              address from,
              uint256[] calldata ids,
              uint256[] calldata values,
              bytes calldata data
          ) external returns (bytes4);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
      pragma solidity ^0.8.20;
      /**
       * @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);
      }
      

      File 3 of 5: WBTC
      pragma solidity 0.4.24;
      
      // File: openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol
      
      /**
       * @title ERC20Basic
       * @dev Simpler version of ERC20 interface
       * See https://github.com/ethereum/EIPs/issues/179
       */
      contract ERC20Basic {
        function totalSupply() public view returns (uint256);
        function balanceOf(address _who) public view returns (uint256);
        function transfer(address _to, uint256 _value) public returns (bool);
        event Transfer(address indexed from, address indexed to, uint256 value);
      }
      
      // File: openzeppelin-solidity/contracts/math/SafeMath.sol
      
      /**
       * @title SafeMath
       * @dev Math operations with safety checks that throw on error
       */
      library SafeMath {
      
        /**
        * @dev Multiplies two numbers, throws on overflow.
        */
        function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
          // Gas optimization: this is cheaper than asserting 'a' not being zero, but the
          // benefit is lost if 'b' is also tested.
          // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
          if (_a == 0) {
            return 0;
          }
      
          c = _a * _b;
          assert(c / _a == _b);
          return c;
        }
      
        /**
        * @dev Integer division of two numbers, truncating the quotient.
        */
        function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
          // assert(_b > 0); // Solidity automatically throws when dividing by 0
          // uint256 c = _a / _b;
          // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
          return _a / _b;
        }
      
        /**
        * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
        */
        function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
          assert(_b <= _a);
          return _a - _b;
        }
      
        /**
        * @dev Adds two numbers, throws on overflow.
        */
        function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
          c = _a + _b;
          assert(c >= _a);
          return c;
        }
      }
      
      // File: openzeppelin-solidity/contracts/token/ERC20/BasicToken.sol
      
      /**
       * @title Basic token
       * @dev Basic version of StandardToken, with no allowances.
       */
      contract BasicToken is ERC20Basic {
        using SafeMath for uint256;
      
        mapping(address => uint256) internal balances;
      
        uint256 internal totalSupply_;
      
        /**
        * @dev Total number of tokens in existence
        */
        function totalSupply() public view returns (uint256) {
          return totalSupply_;
        }
      
        /**
        * @dev Transfer token for a specified address
        * @param _to The address to transfer to.
        * @param _value The amount to be transferred.
        */
        function transfer(address _to, uint256 _value) public returns (bool) {
          require(_value <= balances[msg.sender]);
          require(_to != address(0));
      
          balances[msg.sender] = balances[msg.sender].sub(_value);
          balances[_to] = balances[_to].add(_value);
          emit Transfer(msg.sender, _to, _value);
          return true;
        }
      
        /**
        * @dev Gets the balance of the specified address.
        * @param _owner The address to query the the balance of.
        * @return An uint256 representing the amount owned by the passed address.
        */
        function balanceOf(address _owner) public view returns (uint256) {
          return balances[_owner];
        }
      
      }
      
      // File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
      
      /**
       * @title ERC20 interface
       * @dev see https://github.com/ethereum/EIPs/issues/20
       */
      contract ERC20 is ERC20Basic {
        function allowance(address _owner, address _spender)
          public view returns (uint256);
      
        function transferFrom(address _from, address _to, uint256 _value)
          public returns (bool);
      
        function approve(address _spender, uint256 _value) public returns (bool);
        event Approval(
          address indexed owner,
          address indexed spender,
          uint256 value
        );
      }
      
      // File: openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol
      
      /**
       * @title Standard ERC20 token
       *
       * @dev Implementation of the basic standard token.
       * https://github.com/ethereum/EIPs/issues/20
       * Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
       */
      contract StandardToken is ERC20, BasicToken {
      
        mapping (address => mapping (address => uint256)) internal allowed;
      
      
        /**
         * @dev Transfer tokens from one address to another
         * @param _from address The address which you want to send tokens from
         * @param _to address The address which you want to transfer to
         * @param _value uint256 the amount of tokens to be transferred
         */
        function transferFrom(
          address _from,
          address _to,
          uint256 _value
        )
          public
          returns (bool)
        {
          require(_value <= balances[_from]);
          require(_value <= allowed[_from][msg.sender]);
          require(_to != address(0));
      
          balances[_from] = balances[_from].sub(_value);
          balances[_to] = balances[_to].add(_value);
          allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
          emit Transfer(_from, _to, _value);
          return true;
        }
      
        /**
         * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
         * 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
         * @param _spender The address which will spend the funds.
         * @param _value The amount of tokens to be spent.
         */
        function approve(address _spender, uint256 _value) public returns (bool) {
          allowed[msg.sender][_spender] = _value;
          emit Approval(msg.sender, _spender, _value);
          return true;
        }
      
        /**
         * @dev Function to check the amount of tokens that an owner allowed to a spender.
         * @param _owner address The address which owns the funds.
         * @param _spender address The address which will spend the funds.
         * @return A uint256 specifying the amount of tokens still available for the spender.
         */
        function allowance(
          address _owner,
          address _spender
         )
          public
          view
          returns (uint256)
        {
          return allowed[_owner][_spender];
        }
      
        /**
         * @dev Increase the amount of tokens that an owner allowed to a spender.
         * approve should be called when allowed[_spender] == 0. To increment
         * allowed value is better to use this function to avoid 2 calls (and wait until
         * the first transaction is mined)
         * From MonolithDAO Token.sol
         * @param _spender The address which will spend the funds.
         * @param _addedValue The amount of tokens to increase the allowance by.
         */
        function increaseApproval(
          address _spender,
          uint256 _addedValue
        )
          public
          returns (bool)
        {
          allowed[msg.sender][_spender] = (
            allowed[msg.sender][_spender].add(_addedValue));
          emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
          return true;
        }
      
        /**
         * @dev Decrease the amount of tokens that an owner allowed to a spender.
         * approve should be called when allowed[_spender] == 0. To decrement
         * allowed value is better to use this function to avoid 2 calls (and wait until
         * the first transaction is mined)
         * From MonolithDAO Token.sol
         * @param _spender The address which will spend the funds.
         * @param _subtractedValue The amount of tokens to decrease the allowance by.
         */
        function decreaseApproval(
          address _spender,
          uint256 _subtractedValue
        )
          public
          returns (bool)
        {
          uint256 oldValue = allowed[msg.sender][_spender];
          if (_subtractedValue >= oldValue) {
            allowed[msg.sender][_spender] = 0;
          } else {
            allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
          }
          emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
          return true;
        }
      
      }
      
      // File: openzeppelin-solidity/contracts/token/ERC20/DetailedERC20.sol
      
      /**
       * @title DetailedERC20 token
       * @dev The decimals are only for visualization purposes.
       * All the operations are done using the smallest and indivisible token unit,
       * just as on Ethereum all the operations are done in wei.
       */
      contract DetailedERC20 is ERC20 {
        string public name;
        string public symbol;
        uint8 public decimals;
      
        constructor(string _name, string _symbol, uint8 _decimals) public {
          name = _name;
          symbol = _symbol;
          decimals = _decimals;
        }
      }
      
      // File: openzeppelin-solidity/contracts/ownership/Ownable.sol
      
      /**
       * @title Ownable
       * @dev The Ownable contract has an owner address, and provides basic authorization control
       * functions, this simplifies the implementation of "user permissions".
       */
      contract Ownable {
        address public owner;
      
      
        event OwnershipRenounced(address indexed previousOwner);
        event OwnershipTransferred(
          address indexed previousOwner,
          address indexed newOwner
        );
      
      
        /**
         * @dev The Ownable constructor sets the original `owner` of the contract to the sender
         * account.
         */
        constructor() public {
          owner = msg.sender;
        }
      
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
          require(msg.sender == owner);
          _;
        }
      
        /**
         * @dev Allows the current owner to relinquish control of the contract.
         * @notice Renouncing to ownership will leave the contract without an owner.
         * It will not be possible to call the functions with the `onlyOwner`
         * modifier anymore.
         */
        function renounceOwnership() public onlyOwner {
          emit OwnershipRenounced(owner);
          owner = address(0);
        }
      
        /**
         * @dev Allows the current owner to transfer control of the contract to a newOwner.
         * @param _newOwner The address to transfer ownership to.
         */
        function transferOwnership(address _newOwner) public onlyOwner {
          _transferOwnership(_newOwner);
        }
      
        /**
         * @dev Transfers control of the contract to a newOwner.
         * @param _newOwner The address to transfer ownership to.
         */
        function _transferOwnership(address _newOwner) internal {
          require(_newOwner != address(0));
          emit OwnershipTransferred(owner, _newOwner);
          owner = _newOwner;
        }
      }
      
      // File: openzeppelin-solidity/contracts/token/ERC20/MintableToken.sol
      
      /**
       * @title Mintable token
       * @dev Simple ERC20 Token example, with mintable token creation
       * Based on code by TokenMarketNet: https://github.com/TokenMarketNet/ico/blob/master/contracts/MintableToken.sol
       */
      contract MintableToken is StandardToken, Ownable {
        event Mint(address indexed to, uint256 amount);
        event MintFinished();
      
        bool public mintingFinished = false;
      
      
        modifier canMint() {
          require(!mintingFinished);
          _;
        }
      
        modifier hasMintPermission() {
          require(msg.sender == owner);
          _;
        }
      
        /**
         * @dev Function to mint tokens
         * @param _to The address that will receive the minted tokens.
         * @param _amount The amount of tokens to mint.
         * @return A boolean that indicates if the operation was successful.
         */
        function mint(
          address _to,
          uint256 _amount
        )
          public
          hasMintPermission
          canMint
          returns (bool)
        {
          totalSupply_ = totalSupply_.add(_amount);
          balances[_to] = balances[_to].add(_amount);
          emit Mint(_to, _amount);
          emit Transfer(address(0), _to, _amount);
          return true;
        }
      
        /**
         * @dev Function to stop minting new tokens.
         * @return True if the operation was successful.
         */
        function finishMinting() public onlyOwner canMint returns (bool) {
          mintingFinished = true;
          emit MintFinished();
          return true;
        }
      }
      
      // File: openzeppelin-solidity/contracts/token/ERC20/BurnableToken.sol
      
      /**
       * @title Burnable Token
       * @dev Token that can be irreversibly burned (destroyed).
       */
      contract BurnableToken is BasicToken {
      
        event Burn(address indexed burner, uint256 value);
      
        /**
         * @dev Burns a specific amount of tokens.
         * @param _value The amount of token to be burned.
         */
        function burn(uint256 _value) public {
          _burn(msg.sender, _value);
        }
      
        function _burn(address _who, uint256 _value) internal {
          require(_value <= balances[_who]);
          // no need to require value <= totalSupply, since that would imply the
          // sender's balance is greater than the totalSupply, which *should* be an assertion failure
      
          balances[_who] = balances[_who].sub(_value);
          totalSupply_ = totalSupply_.sub(_value);
          emit Burn(_who, _value);
          emit Transfer(_who, address(0), _value);
        }
      }
      
      // File: openzeppelin-solidity/contracts/lifecycle/Pausable.sol
      
      /**
       * @title Pausable
       * @dev Base contract which allows children to implement an emergency stop mechanism.
       */
      contract Pausable is Ownable {
        event Pause();
        event Unpause();
      
        bool public paused = false;
      
      
        /**
         * @dev Modifier to make a function callable only when the contract is not paused.
         */
        modifier whenNotPaused() {
          require(!paused);
          _;
        }
      
        /**
         * @dev Modifier to make a function callable only when the contract is paused.
         */
        modifier whenPaused() {
          require(paused);
          _;
        }
      
        /**
         * @dev called by the owner to pause, triggers stopped state
         */
        function pause() public onlyOwner whenNotPaused {
          paused = true;
          emit Pause();
        }
      
        /**
         * @dev called by the owner to unpause, returns to normal state
         */
        function unpause() public onlyOwner whenPaused {
          paused = false;
          emit Unpause();
        }
      }
      
      // File: openzeppelin-solidity/contracts/token/ERC20/PausableToken.sol
      
      /**
       * @title Pausable token
       * @dev StandardToken modified with pausable transfers.
       **/
      contract PausableToken is StandardToken, Pausable {
      
        function transfer(
          address _to,
          uint256 _value
        )
          public
          whenNotPaused
          returns (bool)
        {
          return super.transfer(_to, _value);
        }
      
        function transferFrom(
          address _from,
          address _to,
          uint256 _value
        )
          public
          whenNotPaused
          returns (bool)
        {
          return super.transferFrom(_from, _to, _value);
        }
      
        function approve(
          address _spender,
          uint256 _value
        )
          public
          whenNotPaused
          returns (bool)
        {
          return super.approve(_spender, _value);
        }
      
        function increaseApproval(
          address _spender,
          uint _addedValue
        )
          public
          whenNotPaused
          returns (bool success)
        {
          return super.increaseApproval(_spender, _addedValue);
        }
      
        function decreaseApproval(
          address _spender,
          uint _subtractedValue
        )
          public
          whenNotPaused
          returns (bool success)
        {
          return super.decreaseApproval(_spender, _subtractedValue);
        }
      }
      
      // File: openzeppelin-solidity/contracts/ownership/Claimable.sol
      
      /**
       * @title Claimable
       * @dev Extension for the Ownable contract, where the ownership needs to be claimed.
       * This allows the new owner to accept the transfer.
       */
      contract Claimable is Ownable {
        address public pendingOwner;
      
        /**
         * @dev Modifier throws if called by any account other than the pendingOwner.
         */
        modifier onlyPendingOwner() {
          require(msg.sender == pendingOwner);
          _;
        }
      
        /**
         * @dev Allows the current owner to set the pendingOwner address.
         * @param newOwner The address to transfer ownership to.
         */
        function transferOwnership(address newOwner) public onlyOwner {
          pendingOwner = newOwner;
        }
      
        /**
         * @dev Allows the pendingOwner address to finalize the transfer.
         */
        function claimOwnership() public onlyPendingOwner {
          emit OwnershipTransferred(owner, pendingOwner);
          owner = pendingOwner;
          pendingOwner = address(0);
        }
      }
      
      // File: openzeppelin-solidity/contracts/token/ERC20/SafeERC20.sol
      
      /**
       * @title SafeERC20
       * @dev Wrappers around ERC20 operations that throw on failure.
       * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
       * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
       */
      library SafeERC20 {
        function safeTransfer(
          ERC20Basic _token,
          address _to,
          uint256 _value
        )
          internal
        {
          require(_token.transfer(_to, _value));
        }
      
        function safeTransferFrom(
          ERC20 _token,
          address _from,
          address _to,
          uint256 _value
        )
          internal
        {
          require(_token.transferFrom(_from, _to, _value));
        }
      
        function safeApprove(
          ERC20 _token,
          address _spender,
          uint256 _value
        )
          internal
        {
          require(_token.approve(_spender, _value));
        }
      }
      
      // File: openzeppelin-solidity/contracts/ownership/CanReclaimToken.sol
      
      /**
       * @title Contracts that should be able to recover tokens
       * @author SylTi
       * @dev This allow a contract to recover any ERC20 token received in a contract by transferring the balance to the contract owner.
       * This will prevent any accidental loss of tokens.
       */
      contract CanReclaimToken is Ownable {
        using SafeERC20 for ERC20Basic;
      
        /**
         * @dev Reclaim all ERC20Basic compatible tokens
         * @param _token ERC20Basic The address of the token contract
         */
        function reclaimToken(ERC20Basic _token) external onlyOwner {
          uint256 balance = _token.balanceOf(this);
          _token.safeTransfer(owner, balance);
        }
      
      }
      
      // File: contracts/utils/OwnableContract.sol
      
      // empty block is used as this contract just inherits others.
      contract OwnableContract is CanReclaimToken, Claimable { } /* solhint-disable-line no-empty-blocks */
      
      // File: contracts/token/WBTC.sol
      
      contract WBTC is StandardToken, DetailedERC20("Wrapped BTC", "WBTC", 8),
          MintableToken, BurnableToken, PausableToken, OwnableContract {
      
          function burn(uint value) public onlyOwner {
              super.burn(value);
          }
      
          function finishMinting() public onlyOwner returns (bool) {
              return false;
          }
      
          function renounceOwnership() public onlyOwner {
              revert("renouncing ownership is blocked");
          }
      }

      File 4 of 5: RolesAuthority
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity >=0.8.0;
      import {Auth, Authority} from "../Auth.sol";
      /// @notice Role based Authority that supports up to 256 roles.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/authorities/RolesAuthority.sol)
      /// @author Modified from Dappsys (https://github.com/dapphub/ds-roles/blob/master/src/roles.sol)
      contract RolesAuthority is Auth, Authority {
          /*//////////////////////////////////////////////////////////////
                                       EVENTS
          //////////////////////////////////////////////////////////////*/
          event UserRoleUpdated(address indexed user, uint8 indexed role, bool enabled);
          event PublicCapabilityUpdated(address indexed target, bytes4 indexed functionSig, bool enabled);
          event RoleCapabilityUpdated(uint8 indexed role, address indexed target, bytes4 indexed functionSig, bool enabled);
          /*//////////////////////////////////////////////////////////////
                                     CONSTRUCTOR
          //////////////////////////////////////////////////////////////*/
          constructor(address _owner, Authority _authority) Auth(_owner, _authority) {}
          /*//////////////////////////////////////////////////////////////
                                  ROLE/USER STORAGE
          //////////////////////////////////////////////////////////////*/
          mapping(address => bytes32) public getUserRoles;
          mapping(address => mapping(bytes4 => bool)) public isCapabilityPublic;
          mapping(address => mapping(bytes4 => bytes32)) public getRolesWithCapability;
          function doesUserHaveRole(address user, uint8 role) public view virtual returns (bool) {
              return (uint256(getUserRoles[user]) >> role) & 1 != 0;
          }
          function doesRoleHaveCapability(
              uint8 role,
              address target,
              bytes4 functionSig
          ) public view virtual returns (bool) {
              return (uint256(getRolesWithCapability[target][functionSig]) >> role) & 1 != 0;
          }
          /*//////////////////////////////////////////////////////////////
                                 AUTHORIZATION LOGIC
          //////////////////////////////////////////////////////////////*/
          function canCall(
              address user,
              address target,
              bytes4 functionSig
          ) public view virtual override returns (bool) {
              return
                  isCapabilityPublic[target][functionSig] ||
                  bytes32(0) != getUserRoles[user] & getRolesWithCapability[target][functionSig];
          }
          /*//////////////////////////////////////////////////////////////
                         ROLE CAPABILITY CONFIGURATION LOGIC
          //////////////////////////////////////////////////////////////*/
          function setPublicCapability(
              address target,
              bytes4 functionSig,
              bool enabled
          ) public virtual requiresAuth {
              isCapabilityPublic[target][functionSig] = enabled;
              emit PublicCapabilityUpdated(target, functionSig, enabled);
          }
          function setRoleCapability(
              uint8 role,
              address target,
              bytes4 functionSig,
              bool enabled
          ) public virtual requiresAuth {
              if (enabled) {
                  getRolesWithCapability[target][functionSig] |= bytes32(1 << role);
              } else {
                  getRolesWithCapability[target][functionSig] &= ~bytes32(1 << role);
              }
              emit RoleCapabilityUpdated(role, target, functionSig, enabled);
          }
          /*//////////////////////////////////////////////////////////////
                             USER ROLE ASSIGNMENT LOGIC
          //////////////////////////////////////////////////////////////*/
          function setUserRole(
              address user,
              uint8 role,
              bool enabled
          ) public virtual requiresAuth {
              if (enabled) {
                  getUserRoles[user] |= bytes32(1 << role);
              } else {
                  getUserRoles[user] &= ~bytes32(1 << role);
              }
              emit UserRoleUpdated(user, role, enabled);
          }
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity >=0.8.0;
      /// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
      /// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
      abstract contract Auth {
          event OwnershipTransferred(address indexed user, address indexed newOwner);
          event AuthorityUpdated(address indexed user, Authority indexed newAuthority);
          address public owner;
          Authority public authority;
          constructor(address _owner, Authority _authority) {
              owner = _owner;
              authority = _authority;
              emit OwnershipTransferred(msg.sender, _owner);
              emit AuthorityUpdated(msg.sender, _authority);
          }
          modifier requiresAuth() virtual {
              require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");
              _;
          }
          function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
              Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.
              // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
              // aware that this makes protected functions uncallable even to the owner if the authority is out of order.
              return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
          }
          function setAuthority(Authority newAuthority) public virtual {
              // We check if the caller is the owner first because we want to ensure they can
              // always swap out the authority even if it's reverting or using up a lot of gas.
              require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));
              authority = newAuthority;
              emit AuthorityUpdated(msg.sender, newAuthority);
          }
          function transferOwnership(address newOwner) public virtual requiresAuth {
              owner = newOwner;
              emit OwnershipTransferred(msg.sender, newOwner);
          }
      }
      /// @notice A generic interface for a contract which provides authorization data to an Auth instance.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
      /// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
      interface Authority {
          function canCall(
              address user,
              address target,
              bytes4 functionSig
          ) external view returns (bool);
      }
      

      File 5 of 5: AccountantWithRateProviders
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.21;
      import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
      import {IRateProvider} from "src/interfaces/IRateProvider.sol";
      import {ERC20} from "@solmate/tokens/ERC20.sol";
      import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
      import {BoringVault} from "src/base/BoringVault.sol";
      import {Auth, Authority} from "@solmate/auth/Auth.sol";
      import {IPausable} from "src/interfaces/IPausable.sol";
      contract AccountantWithRateProviders is Auth, IRateProvider, IPausable {
          using FixedPointMathLib for uint256;
          using SafeTransferLib for ERC20;
          // ========================================= STRUCTS =========================================
          /**
           * @param payoutAddress the address `claimFees` sends fees to
           * @param highwaterMark the highest value of the BoringVault's share price
           * @param feesOwedInBase total pending fees owed in terms of base
           * @param totalSharesLastUpdate total amount of shares the last exchange rate update
           * @param exchangeRate the current exchange rate in terms of base
           * @param allowedExchangeRateChangeUpper the max allowed change to exchange rate from an update
           * @param allowedExchangeRateChangeLower the min allowed change to exchange rate from an update
           * @param lastUpdateTimestamp the block timestamp of the last exchange rate update
           * @param isPaused whether or not this contract is paused
           * @param minimumUpdateDelayInSeconds the minimum amount of time that must pass between
           *        exchange rate updates, such that the update won't trigger the contract to be paused
           * @param managementFee the management fee
           * @param performanceFee the performance fee
           */
          struct AccountantState {
              address payoutAddress;
              uint96 highwaterMark;
              uint128 feesOwedInBase;
              uint128 totalSharesLastUpdate;
              uint96 exchangeRate;
              uint16 allowedExchangeRateChangeUpper;
              uint16 allowedExchangeRateChangeLower;
              uint64 lastUpdateTimestamp;
              bool isPaused;
              uint24 minimumUpdateDelayInSeconds;
              uint16 managementFee;
              uint16 performanceFee;
          }
          /**
           * @param isPeggedToBase whether or not the asset is 1:1 with the base asset
           * @param rateProvider the rate provider for this asset if `isPeggedToBase` is false
           */
          struct RateProviderData {
              bool isPeggedToBase;
              IRateProvider rateProvider;
          }
          // ========================================= STATE =========================================
          /**
           * @notice Store the accountant state in 3 packed slots.
           */
          AccountantState public accountantState;
          /**
           * @notice Maps ERC20s to their RateProviderData.
           */
          mapping(ERC20 => RateProviderData) public rateProviderData;
          //============================== ERRORS ===============================
          error AccountantWithRateProviders__UpperBoundTooSmall();
          error AccountantWithRateProviders__LowerBoundTooLarge();
          error AccountantWithRateProviders__ManagementFeeTooLarge();
          error AccountantWithRateProviders__PerformanceFeeTooLarge();
          error AccountantWithRateProviders__Paused();
          error AccountantWithRateProviders__ZeroFeesOwed();
          error AccountantWithRateProviders__OnlyCallableByBoringVault();
          error AccountantWithRateProviders__UpdateDelayTooLarge();
          error AccountantWithRateProviders__ExchangeRateAboveHighwaterMark();
          //============================== EVENTS ===============================
          event Paused();
          event Unpaused();
          event DelayInSecondsUpdated(uint24 oldDelay, uint24 newDelay);
          event UpperBoundUpdated(uint16 oldBound, uint16 newBound);
          event LowerBoundUpdated(uint16 oldBound, uint16 newBound);
          event ManagementFeeUpdated(uint16 oldFee, uint16 newFee);
          event PerformanceFeeUpdated(uint16 oldFee, uint16 newFee);
          event PayoutAddressUpdated(address oldPayout, address newPayout);
          event RateProviderUpdated(address asset, bool isPegged, address rateProvider);
          event ExchangeRateUpdated(uint96 oldRate, uint96 newRate, uint64 currentTime);
          event FeesClaimed(address indexed feeAsset, uint256 amount);
          event HighwaterMarkReset();
          //============================== IMMUTABLES ===============================
          /**
           * @notice The base asset rates are provided in.
           */
          ERC20 public immutable base;
          /**
           * @notice The decimals rates are provided in.
           */
          uint8 public immutable decimals;
          /**
           * @notice The BoringVault this accountant is working with.
           *         Used to determine share supply for fee calculation.
           */
          BoringVault public immutable vault;
          /**
           * @notice One share of the BoringVault.
           */
          uint256 internal immutable ONE_SHARE;
          constructor(
              address _owner,
              address _vault,
              address payoutAddress,
              uint96 startingExchangeRate,
              address _base,
              uint16 allowedExchangeRateChangeUpper,
              uint16 allowedExchangeRateChangeLower,
              uint24 minimumUpdateDelayInSeconds,
              uint16 managementFee,
              uint16 performanceFee
          ) Auth(_owner, Authority(address(0))) {
              base = ERC20(_base);
              decimals = ERC20(_base).decimals();
              vault = BoringVault(payable(_vault));
              ONE_SHARE = 10 ** vault.decimals();
              accountantState = AccountantState({
                  payoutAddress: payoutAddress,
                  highwaterMark: startingExchangeRate,
                  feesOwedInBase: 0,
                  totalSharesLastUpdate: uint128(vault.totalSupply()),
                  exchangeRate: startingExchangeRate,
                  allowedExchangeRateChangeUpper: allowedExchangeRateChangeUpper,
                  allowedExchangeRateChangeLower: allowedExchangeRateChangeLower,
                  lastUpdateTimestamp: uint64(block.timestamp),
                  isPaused: false,
                  minimumUpdateDelayInSeconds: minimumUpdateDelayInSeconds,
                  managementFee: managementFee,
                  performanceFee: performanceFee
              });
          }
          // ========================================= ADMIN FUNCTIONS =========================================
          /**
           * @notice Pause this contract, which prevents future calls to `updateExchangeRate`, and any safe rate
           *         calls will revert.
           * @dev Callable by MULTISIG_ROLE.
           */
          function pause() external requiresAuth {
              accountantState.isPaused = true;
              emit Paused();
          }
          /**
           * @notice Unpause this contract, which allows future calls to `updateExchangeRate`, and any safe rate
           *         calls will stop reverting.
           * @dev Callable by MULTISIG_ROLE.
           */
          function unpause() external requiresAuth {
              accountantState.isPaused = false;
              emit Unpaused();
          }
          /**
           * @notice Update the minimum time delay between `updateExchangeRate` calls.
           * @dev There are no input requirements, as it is possible the admin would want
           *      the exchange rate updated as frequently as needed.
           * @dev Callable by OWNER_ROLE.
           */
          function updateDelay(uint24 minimumUpdateDelayInSeconds) external requiresAuth {
              if (minimumUpdateDelayInSeconds > 14 days) revert AccountantWithRateProviders__UpdateDelayTooLarge();
              uint24 oldDelay = accountantState.minimumUpdateDelayInSeconds;
              accountantState.minimumUpdateDelayInSeconds = minimumUpdateDelayInSeconds;
              emit DelayInSecondsUpdated(oldDelay, minimumUpdateDelayInSeconds);
          }
          /**
           * @notice Update the allowed upper bound change of exchange rate between `updateExchangeRateCalls`.
           * @dev Callable by OWNER_ROLE.
           */
          function updateUpper(uint16 allowedExchangeRateChangeUpper) external requiresAuth {
              if (allowedExchangeRateChangeUpper < 1e4) revert AccountantWithRateProviders__UpperBoundTooSmall();
              uint16 oldBound = accountantState.allowedExchangeRateChangeUpper;
              accountantState.allowedExchangeRateChangeUpper = allowedExchangeRateChangeUpper;
              emit UpperBoundUpdated(oldBound, allowedExchangeRateChangeUpper);
          }
          /**
           * @notice Update the allowed lower bound change of exchange rate between `updateExchangeRateCalls`.
           * @dev Callable by OWNER_ROLE.
           */
          function updateLower(uint16 allowedExchangeRateChangeLower) external requiresAuth {
              if (allowedExchangeRateChangeLower > 1e4) revert AccountantWithRateProviders__LowerBoundTooLarge();
              uint16 oldBound = accountantState.allowedExchangeRateChangeLower;
              accountantState.allowedExchangeRateChangeLower = allowedExchangeRateChangeLower;
              emit LowerBoundUpdated(oldBound, allowedExchangeRateChangeLower);
          }
          /**
           * @notice Update the management fee to a new value.
           * @dev Callable by OWNER_ROLE.
           */
          function updateManagementFee(uint16 managementFee) external requiresAuth {
              if (managementFee > 0.2e4) revert AccountantWithRateProviders__ManagementFeeTooLarge();
              uint16 oldFee = accountantState.managementFee;
              accountantState.managementFee = managementFee;
              emit ManagementFeeUpdated(oldFee, managementFee);
          }
          /**
           * @notice Update the performance fee to a new value.
           * @dev Callable by OWNER_ROLE.
           */
          function updatePerformanceFee(uint16 performanceFee) external requiresAuth {
              if (performanceFee > 0.5e4) revert AccountantWithRateProviders__PerformanceFeeTooLarge();
              uint16 oldFee = accountantState.performanceFee;
              accountantState.performanceFee = performanceFee;
              emit PerformanceFeeUpdated(oldFee, performanceFee);
          }
          /**
           * @notice Update the payout address fees are sent to.
           * @dev Callable by OWNER_ROLE.
           */
          function updatePayoutAddress(address payoutAddress) external requiresAuth {
              address oldPayout = accountantState.payoutAddress;
              accountantState.payoutAddress = payoutAddress;
              emit PayoutAddressUpdated(oldPayout, payoutAddress);
          }
          /**
           * @notice Update the rate provider data for a specific `asset`.
           * @dev Rate providers must return rates in terms of `base` or
           * an asset pegged to base and they must use the same decimals
           * as `asset`.
           * @dev Callable by OWNER_ROLE.
           */
          function setRateProviderData(ERC20 asset, bool isPeggedToBase, address rateProvider) external requiresAuth {
              rateProviderData[asset] =
                  RateProviderData({isPeggedToBase: isPeggedToBase, rateProvider: IRateProvider(rateProvider)});
              emit RateProviderUpdated(address(asset), isPeggedToBase, rateProvider);
          }
          /**
           * @notice Reset the highwater mark to the current exchange rate.
           * @dev Callable by OWNER_ROLE.
           */
          function resetHighwaterMark() external requiresAuth {
              AccountantState storage state = accountantState;
              if (state.exchangeRate > state.highwaterMark) {
                  revert AccountantWithRateProviders__ExchangeRateAboveHighwaterMark();
              }
              uint64 currentTime = uint64(block.timestamp);
              uint256 currentTotalShares = vault.totalSupply();
              _calculateFeesOwed(state, state.exchangeRate, state.exchangeRate, currentTotalShares, currentTime);
              state.totalSharesLastUpdate = uint128(currentTotalShares);
              state.highwaterMark = accountantState.exchangeRate;
              state.lastUpdateTimestamp = currentTime;
              emit HighwaterMarkReset();
          }
          // ========================================= UPDATE EXCHANGE RATE/FEES FUNCTIONS =========================================
          /**
           * @notice Updates this contract exchangeRate.
           * @dev If new exchange rate is outside of accepted bounds, or if not enough time has passed, this
           *      will pause the contract, and this function will NOT calculate fees owed.
           * @dev Callable by UPDATE_EXCHANGE_RATE_ROLE.
           */
          function updateExchangeRate(uint96 newExchangeRate) external requiresAuth {
              AccountantState storage state = accountantState;
              if (state.isPaused) revert AccountantWithRateProviders__Paused();
              uint64 currentTime = uint64(block.timestamp);
              uint256 currentExchangeRate = state.exchangeRate;
              uint256 currentTotalShares = vault.totalSupply();
              if (
                  currentTime < state.lastUpdateTimestamp + state.minimumUpdateDelayInSeconds
                      || newExchangeRate > currentExchangeRate.mulDivDown(state.allowedExchangeRateChangeUpper, 1e4)
                      || newExchangeRate < currentExchangeRate.mulDivDown(state.allowedExchangeRateChangeLower, 1e4)
              ) {
                  // Instead of reverting, pause the contract. This way the exchange rate updater is able to update the exchange rate
                  // to a better value, and pause it.
                  state.isPaused = true;
              } else {
                  _calculateFeesOwed(state, newExchangeRate, currentExchangeRate, currentTotalShares, currentTime);
              }
              state.exchangeRate = newExchangeRate;
              state.totalSharesLastUpdate = uint128(currentTotalShares);
              state.lastUpdateTimestamp = currentTime;
              emit ExchangeRateUpdated(uint96(currentExchangeRate), newExchangeRate, currentTime);
          }
          /**
           * @notice Claim pending fees.
           * @dev This function must be called by the BoringVault.
           * @dev This function will lose precision if the exchange rate
           *      decimals is greater than the feeAsset's decimals.
           */
          function claimFees(ERC20 feeAsset) external {
              if (msg.sender != address(vault)) revert AccountantWithRateProviders__OnlyCallableByBoringVault();
              AccountantState storage state = accountantState;
              if (state.isPaused) revert AccountantWithRateProviders__Paused();
              if (state.feesOwedInBase == 0) revert AccountantWithRateProviders__ZeroFeesOwed();
              // Determine amount of fees owed in feeAsset.
              uint256 feesOwedInFeeAsset;
              RateProviderData memory data = rateProviderData[feeAsset];
              if (address(feeAsset) == address(base)) {
                  feesOwedInFeeAsset = state.feesOwedInBase;
              } else {
                  uint8 feeAssetDecimals = ERC20(feeAsset).decimals();
                  uint256 feesOwedInBaseUsingFeeAssetDecimals =
                      changeDecimals(state.feesOwedInBase, decimals, feeAssetDecimals);
                  if (data.isPeggedToBase) {
                      feesOwedInFeeAsset = feesOwedInBaseUsingFeeAssetDecimals;
                  } else {
                      uint256 rate = data.rateProvider.getRate();
                      feesOwedInFeeAsset = feesOwedInBaseUsingFeeAssetDecimals.mulDivDown(10 ** feeAssetDecimals, rate);
                  }
              }
              // Zero out fees owed.
              state.feesOwedInBase = 0;
              // Transfer fee asset to payout address.
              feeAsset.safeTransferFrom(msg.sender, state.payoutAddress, feesOwedInFeeAsset);
              emit FeesClaimed(address(feeAsset), feesOwedInFeeAsset);
          }
          // ========================================= RATE FUNCTIONS =========================================
          /**
           * @notice Get this BoringVault's current rate in the base.
           */
          function getRate() public view returns (uint256 rate) {
              rate = accountantState.exchangeRate;
          }
          /**
           * @notice Get this BoringVault's current rate in the base.
           * @dev Revert if paused.
           */
          function getRateSafe() external view returns (uint256 rate) {
              if (accountantState.isPaused) revert AccountantWithRateProviders__Paused();
              rate = getRate();
          }
          /**
           * @notice Get this BoringVault's current rate in the provided quote.
           * @dev `quote` must have its RateProviderData set, else this will revert.
           * @dev This function will lose precision if the exchange rate
           *      decimals is greater than the quote's decimals.
           */
          function getRateInQuote(ERC20 quote) public view returns (uint256 rateInQuote) {
              if (address(quote) == address(base)) {
                  rateInQuote = accountantState.exchangeRate;
              } else {
                  RateProviderData memory data = rateProviderData[quote];
                  uint8 quoteDecimals = ERC20(quote).decimals();
                  uint256 exchangeRateInQuoteDecimals = changeDecimals(accountantState.exchangeRate, decimals, quoteDecimals);
                  if (data.isPeggedToBase) {
                      rateInQuote = exchangeRateInQuoteDecimals;
                  } else {
                      uint256 quoteRate = data.rateProvider.getRate();
                      uint256 oneQuote = 10 ** quoteDecimals;
                      rateInQuote = oneQuote.mulDivDown(exchangeRateInQuoteDecimals, quoteRate);
                  }
              }
          }
          /**
           * @notice Get this BoringVault's current rate in the provided quote.
           * @dev `quote` must have its RateProviderData set, else this will revert.
           * @dev Revert if paused.
           */
          function getRateInQuoteSafe(ERC20 quote) external view returns (uint256 rateInQuote) {
              if (accountantState.isPaused) revert AccountantWithRateProviders__Paused();
              rateInQuote = getRateInQuote(quote);
          }
          // ========================================= INTERNAL HELPER FUNCTIONS =========================================
          /**
           * @notice Used to change the decimals of precision used for an amount.
           */
          function changeDecimals(uint256 amount, uint8 fromDecimals, uint8 toDecimals) internal pure returns (uint256) {
              if (fromDecimals == toDecimals) {
                  return amount;
              } else if (fromDecimals < toDecimals) {
                  return amount * 10 ** (toDecimals - fromDecimals);
              } else {
                  return amount / 10 ** (fromDecimals - toDecimals);
              }
          }
          /**
           * @notice Calculate fees owed in base.
           * @dev This function will update the highwater mark if the new exchange rate is higher.
           */
          function _calculateFeesOwed(
              AccountantState storage state,
              uint96 newExchangeRate,
              uint256 currentExchangeRate,
              uint256 currentTotalShares,
              uint64 currentTime
          ) internal {
              // Only update fees if we are not paused.
              // Update fee accounting.
              uint256 shareSupplyToUse = currentTotalShares;
              // Use the minimum between current total supply and total supply for last update.
              if (state.totalSharesLastUpdate < shareSupplyToUse) {
                  shareSupplyToUse = state.totalSharesLastUpdate;
              }
              // Determine management fees owned.
              uint256 timeDelta = currentTime - state.lastUpdateTimestamp;
              uint256 minimumAssets = newExchangeRate > currentExchangeRate
                  ? shareSupplyToUse.mulDivDown(currentExchangeRate, ONE_SHARE)
                  : shareSupplyToUse.mulDivDown(newExchangeRate, ONE_SHARE);
              uint256 managementFeesAnnual = minimumAssets.mulDivDown(state.managementFee, 1e4);
              uint256 newFeesOwedInBase = managementFeesAnnual.mulDivDown(timeDelta, 365 days);
              // Account for performance fees.
              if (newExchangeRate > state.highwaterMark) {
                  if (state.performanceFee > 0) {
                      uint256 changeInExchangeRate = newExchangeRate - state.highwaterMark;
                      uint256 yieldEarned = changeInExchangeRate.mulDivDown(shareSupplyToUse, ONE_SHARE);
                      uint256 performanceFeesOwedInBase = yieldEarned.mulDivDown(state.performanceFee, 1e4);
                      newFeesOwedInBase += performanceFeesOwedInBase;
                  }
                  // Always update the highwater mark if the new exchange rate is higher.
                  // This way if we are not iniitiall taking performance fees, we can start taking them
                  // without back charging them on past performance.
                  state.highwaterMark = newExchangeRate;
              }
              state.feesOwedInBase += uint128(newFeesOwedInBase);
          }
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity >=0.8.0;
      /// @notice Arithmetic library with operations for fixed-point numbers.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
      /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
      library FixedPointMathLib {
          /*//////////////////////////////////////////////////////////////
                          SIMPLIFIED FIXED POINT OPERATIONS
          //////////////////////////////////////////////////////////////*/
          uint256 internal constant MAX_UINT256 = 2**256 - 1;
          uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
          function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
              return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
          }
          function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
              return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
          }
          function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
              return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
          }
          function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
              return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
          }
          /*//////////////////////////////////////////////////////////////
                          LOW LEVEL FIXED POINT OPERATIONS
          //////////////////////////////////////////////////////////////*/
          function mulDivDown(
              uint256 x,
              uint256 y,
              uint256 denominator
          ) internal pure returns (uint256 z) {
              /// @solidity memory-safe-assembly
              assembly {
                  // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
                  if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                      revert(0, 0)
                  }
                  // Divide x * y by the denominator.
                  z := div(mul(x, y), denominator)
              }
          }
          function mulDivUp(
              uint256 x,
              uint256 y,
              uint256 denominator
          ) internal pure returns (uint256 z) {
              /// @solidity memory-safe-assembly
              assembly {
                  // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
                  if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                      revert(0, 0)
                  }
                  // If x * y modulo the denominator is strictly greater than 0,
                  // 1 is added to round up the division of x * y by the denominator.
                  z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
              }
          }
          function rpow(
              uint256 x,
              uint256 n,
              uint256 scalar
          ) internal pure returns (uint256 z) {
              /// @solidity memory-safe-assembly
              assembly {
                  switch x
                  case 0 {
                      switch n
                      case 0 {
                          // 0 ** 0 = 1
                          z := scalar
                      }
                      default {
                          // 0 ** n = 0
                          z := 0
                      }
                  }
                  default {
                      switch mod(n, 2)
                      case 0 {
                          // If n is even, store scalar in z for now.
                          z := scalar
                      }
                      default {
                          // If n is odd, store x in z for now.
                          z := x
                      }
                      // Shifting right by 1 is like dividing by 2.
                      let half := shr(1, scalar)
                      for {
                          // Shift n right by 1 before looping to halve it.
                          n := shr(1, n)
                      } n {
                          // Shift n right by 1 each iteration to halve it.
                          n := shr(1, n)
                      } {
                          // Revert immediately if x ** 2 would overflow.
                          // Equivalent to iszero(eq(div(xx, x), x)) here.
                          if shr(128, x) {
                              revert(0, 0)
                          }
                          // Store x squared.
                          let xx := mul(x, x)
                          // Round to the nearest number.
                          let xxRound := add(xx, half)
                          // Revert if xx + half overflowed.
                          if lt(xxRound, xx) {
                              revert(0, 0)
                          }
                          // Set x to scaled xxRound.
                          x := div(xxRound, scalar)
                          // If n is even:
                          if mod(n, 2) {
                              // Compute z * x.
                              let zx := mul(z, x)
                              // If z * x overflowed:
                              if iszero(eq(div(zx, x), z)) {
                                  // Revert if x is non-zero.
                                  if iszero(iszero(x)) {
                                      revert(0, 0)
                                  }
                              }
                              // Round to the nearest number.
                              let zxRound := add(zx, half)
                              // Revert if zx + half overflowed.
                              if lt(zxRound, zx) {
                                  revert(0, 0)
                              }
                              // Return properly scaled zxRound.
                              z := div(zxRound, scalar)
                          }
                      }
                  }
              }
          }
          /*//////////////////////////////////////////////////////////////
                              GENERAL NUMBER UTILITIES
          //////////////////////////////////////////////////////////////*/
          function sqrt(uint256 x) internal pure returns (uint256 z) {
              /// @solidity memory-safe-assembly
              assembly {
                  let y := x // We start y at x, which will help us make our initial estimate.
                  z := 181 // The "correct" value is 1, but this saves a multiplication later.
                  // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
                  // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
                  // We check y >= 2^(k + 8) but shift right by k bits
                  // each branch to ensure that if x >= 256, then y >= 256.
                  if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                      y := shr(128, y)
                      z := shl(64, z)
                  }
                  if iszero(lt(y, 0x1000000000000000000)) {
                      y := shr(64, y)
                      z := shl(32, z)
                  }
                  if iszero(lt(y, 0x10000000000)) {
                      y := shr(32, y)
                      z := shl(16, z)
                  }
                  if iszero(lt(y, 0x1000000)) {
                      y := shr(16, y)
                      z := shl(8, z)
                  }
                  // Goal was to get z*z*y within a small factor of x. More iterations could
                  // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
                  // We ensured y >= 256 so that the relative difference between y and y+1 is small.
                  // That's not possible if x < 256 but we can just verify those cases exhaustively.
                  // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
                  // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
                  // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
                  // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
                  // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
                  // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
                  // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
                  // There is no overflow risk here since y < 2^136 after the first branch above.
                  z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
                  // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
                  z := shr(1, add(z, div(x, z)))
                  z := shr(1, add(z, div(x, z)))
                  z := shr(1, add(z, div(x, z)))
                  z := shr(1, add(z, div(x, z)))
                  z := shr(1, add(z, div(x, z)))
                  z := shr(1, add(z, div(x, z)))
                  z := shr(1, add(z, div(x, z)))
                  // If x+1 is a perfect square, the Babylonian method cycles between
                  // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
                  // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
                  // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
                  // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
                  z := sub(z, lt(div(x, z), z))
              }
          }
          function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
              /// @solidity memory-safe-assembly
              assembly {
                  // Mod x by y. Note this will return
                  // 0 instead of reverting if y is zero.
                  z := mod(x, y)
              }
          }
          function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
              /// @solidity memory-safe-assembly
              assembly {
                  // Divide x by y. Note this will return
                  // 0 instead of reverting if y is zero.
                  r := div(x, y)
              }
          }
          function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
              /// @solidity memory-safe-assembly
              assembly {
                  // Add 1 to x * y if x % y > 0. Note this will
                  // return 0 instead of reverting if y is zero.
                  z := add(gt(mod(x, y), 0), div(x, y))
              }
          }
      }
      // SPDX-License-Identifier: GPL-3.0-or-later
      // 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.8.0;
      interface IRateProvider {
          function getRate() external view returns (uint256);
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity >=0.8.0;
      /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
      /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
      /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
      abstract contract ERC20 {
          /*//////////////////////////////////////////////////////////////
                                       EVENTS
          //////////////////////////////////////////////////////////////*/
          event Transfer(address indexed from, address indexed to, uint256 amount);
          event Approval(address indexed owner, address indexed spender, uint256 amount);
          /*//////////////////////////////////////////////////////////////
                                  METADATA STORAGE
          //////////////////////////////////////////////////////////////*/
          string public name;
          string public symbol;
          uint8 public immutable decimals;
          /*//////////////////////////////////////////////////////////////
                                    ERC20 STORAGE
          //////////////////////////////////////////////////////////////*/
          uint256 public totalSupply;
          mapping(address => uint256) public balanceOf;
          mapping(address => mapping(address => uint256)) public allowance;
          /*//////////////////////////////////////////////////////////////
                                  EIP-2612 STORAGE
          //////////////////////////////////////////////////////////////*/
          uint256 internal immutable INITIAL_CHAIN_ID;
          bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
          mapping(address => uint256) public nonces;
          /*//////////////////////////////////////////////////////////////
                                     CONSTRUCTOR
          //////////////////////////////////////////////////////////////*/
          constructor(
              string memory _name,
              string memory _symbol,
              uint8 _decimals
          ) {
              name = _name;
              symbol = _symbol;
              decimals = _decimals;
              INITIAL_CHAIN_ID = block.chainid;
              INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
          }
          /*//////////////////////////////////////////////////////////////
                                     ERC20 LOGIC
          //////////////////////////////////////////////////////////////*/
          function approve(address spender, uint256 amount) public virtual returns (bool) {
              allowance[msg.sender][spender] = amount;
              emit Approval(msg.sender, spender, amount);
              return true;
          }
          function transfer(address to, uint256 amount) public virtual returns (bool) {
              balanceOf[msg.sender] -= amount;
              // Cannot overflow because the sum of all user
              // balances can't exceed the max uint256 value.
              unchecked {
                  balanceOf[to] += amount;
              }
              emit Transfer(msg.sender, to, amount);
              return true;
          }
          function transferFrom(
              address from,
              address to,
              uint256 amount
          ) public virtual returns (bool) {
              uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
              if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
              balanceOf[from] -= amount;
              // Cannot overflow because the sum of all user
              // balances can't exceed the max uint256 value.
              unchecked {
                  balanceOf[to] += amount;
              }
              emit Transfer(from, to, amount);
              return true;
          }
          /*//////////////////////////////////////////////////////////////
                                   EIP-2612 LOGIC
          //////////////////////////////////////////////////////////////*/
          function permit(
              address owner,
              address spender,
              uint256 value,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) public virtual {
              require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
              // Unchecked because the only math done is incrementing
              // the owner's nonce which cannot realistically overflow.
              unchecked {
                  address recoveredAddress = ecrecover(
                      keccak256(
                          abi.encodePacked(
                              "\\x19\\x01",
                              DOMAIN_SEPARATOR(),
                              keccak256(
                                  abi.encode(
                                      keccak256(
                                          "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                      ),
                                      owner,
                                      spender,
                                      value,
                                      nonces[owner]++,
                                      deadline
                                  )
                              )
                          )
                      ),
                      v,
                      r,
                      s
                  );
                  require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
                  allowance[recoveredAddress][spender] = value;
              }
              emit Approval(owner, spender, value);
          }
          function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
              return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
          }
          function computeDomainSeparator() internal view virtual returns (bytes32) {
              return
                  keccak256(
                      abi.encode(
                          keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                          keccak256(bytes(name)),
                          keccak256("1"),
                          block.chainid,
                          address(this)
                      )
                  );
          }
          /*//////////////////////////////////////////////////////////////
                              INTERNAL MINT/BURN LOGIC
          //////////////////////////////////////////////////////////////*/
          function _mint(address to, uint256 amount) internal virtual {
              totalSupply += amount;
              // Cannot overflow because the sum of all user
              // balances can't exceed the max uint256 value.
              unchecked {
                  balanceOf[to] += amount;
              }
              emit Transfer(address(0), to, amount);
          }
          function _burn(address from, uint256 amount) internal virtual {
              balanceOf[from] -= amount;
              // Cannot underflow because a user's balance
              // will never be larger than the total supply.
              unchecked {
                  totalSupply -= amount;
              }
              emit Transfer(from, address(0), amount);
          }
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity >=0.8.0;
      import {ERC20} from "../tokens/ERC20.sol";
      /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
      /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
      /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
      library SafeTransferLib {
          /*//////////////////////////////////////////////////////////////
                                   ETH OPERATIONS
          //////////////////////////////////////////////////////////////*/
          function safeTransferETH(address to, uint256 amount) internal {
              bool success;
              /// @solidity memory-safe-assembly
              assembly {
                  // Transfer the ETH and store if it succeeded or not.
                  success := call(gas(), to, amount, 0, 0, 0, 0)
              }
              require(success, "ETH_TRANSFER_FAILED");
          }
          /*//////////////////////////////////////////////////////////////
                                  ERC20 OPERATIONS
          //////////////////////////////////////////////////////////////*/
          function safeTransferFrom(
              ERC20 token,
              address from,
              address to,
              uint256 amount
          ) internal {
              bool success;
              /// @solidity memory-safe-assembly
              assembly {
                  // Get a pointer to some free memory.
                  let freeMemoryPointer := mload(0x40)
                  // Write the abi-encoded calldata into memory, beginning with the function selector.
                  mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
                  mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
                  mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                  mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                  success := and(
                      // Set success to whether the call reverted, if not we check it either
                      // returned exactly 1 (can't just be non-zero data), or had no return data.
                      or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                      // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                      // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                      // Counterintuitively, this call must be positioned second to the or() call in the
                      // surrounding and() call or else returndatasize() will be zero during the computation.
                      call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
                  )
              }
              require(success, "TRANSFER_FROM_FAILED");
          }
          function safeTransfer(
              ERC20 token,
              address to,
              uint256 amount
          ) internal {
              bool success;
              /// @solidity memory-safe-assembly
              assembly {
                  // Get a pointer to some free memory.
                  let freeMemoryPointer := mload(0x40)
                  // Write the abi-encoded calldata into memory, beginning with the function selector.
                  mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
                  mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                  mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                  success := and(
                      // Set success to whether the call reverted, if not we check it either
                      // returned exactly 1 (can't just be non-zero data), or had no return data.
                      or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                      // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                      // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                      // Counterintuitively, this call must be positioned second to the or() call in the
                      // surrounding and() call or else returndatasize() will be zero during the computation.
                      call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                  )
              }
              require(success, "TRANSFER_FAILED");
          }
          function safeApprove(
              ERC20 token,
              address to,
              uint256 amount
          ) internal {
              bool success;
              /// @solidity memory-safe-assembly
              assembly {
                  // Get a pointer to some free memory.
                  let freeMemoryPointer := mload(0x40)
                  // Write the abi-encoded calldata into memory, beginning with the function selector.
                  mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
                  mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
                  mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
                  success := and(
                      // Set success to whether the call reverted, if not we check it either
                      // returned exactly 1 (can't just be non-zero data), or had no return data.
                      or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                      // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                      // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                      // Counterintuitively, this call must be positioned second to the or() call in the
                      // surrounding and() call or else returndatasize() will be zero during the computation.
                      call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                  )
              }
              require(success, "APPROVE_FAILED");
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.21;
      import {Address} from "@openzeppelin/contracts/utils/Address.sol";
      import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
      import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
      import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
      import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
      import {ERC20} from "@solmate/tokens/ERC20.sol";
      import {BeforeTransferHook} from "src/interfaces/BeforeTransferHook.sol";
      import {Auth, Authority} from "@solmate/auth/Auth.sol";
      contract BoringVault is ERC20, Auth, ERC721Holder, ERC1155Holder {
          using Address for address;
          using SafeTransferLib for ERC20;
          using FixedPointMathLib for uint256;
          // ========================================= STATE =========================================
          /**
           * @notice Contract responsbile for implementing `beforeTransfer`.
           */
          BeforeTransferHook public hook;
          //============================== EVENTS ===============================
          event Enter(address indexed from, address indexed asset, uint256 amount, address indexed to, uint256 shares);
          event Exit(address indexed to, address indexed asset, uint256 amount, address indexed from, uint256 shares);
          //============================== CONSTRUCTOR ===============================
          constructor(address _owner, string memory _name, string memory _symbol, uint8 _decimals)
              ERC20(_name, _symbol, _decimals)
              Auth(_owner, Authority(address(0)))
          {}
          //============================== MANAGE ===============================
          /**
           * @notice Allows manager to make an arbitrary function call from this contract.
           * @dev Callable by MANAGER_ROLE.
           */
          function manage(address target, bytes calldata data, uint256 value)
              external
              requiresAuth
              returns (bytes memory result)
          {
              result = target.functionCallWithValue(data, value);
          }
          /**
           * @notice Allows manager to make arbitrary function calls from this contract.
           * @dev Callable by MANAGER_ROLE.
           */
          function manage(address[] calldata targets, bytes[] calldata data, uint256[] calldata values)
              external
              requiresAuth
              returns (bytes[] memory results)
          {
              uint256 targetsLength = targets.length;
              results = new bytes[](targetsLength);
              for (uint256 i; i < targetsLength; ++i) {
                  results[i] = targets[i].functionCallWithValue(data[i], values[i]);
              }
          }
          //============================== ENTER ===============================
          /**
           * @notice Allows minter to mint shares, in exchange for assets.
           * @dev If assetAmount is zero, no assets are transferred in.
           * @dev Callable by MINTER_ROLE.
           */
          function enter(address from, ERC20 asset, uint256 assetAmount, address to, uint256 shareAmount)
              external
              requiresAuth
          {
              // Transfer assets in
              if (assetAmount > 0) asset.safeTransferFrom(from, address(this), assetAmount);
              // Mint shares.
              _mint(to, shareAmount);
              emit Enter(from, address(asset), assetAmount, to, shareAmount);
          }
          //============================== EXIT ===============================
          /**
           * @notice Allows burner to burn shares, in exchange for assets.
           * @dev If assetAmount is zero, no assets are transferred out.
           * @dev Callable by BURNER_ROLE.
           */
          function exit(address to, ERC20 asset, uint256 assetAmount, address from, uint256 shareAmount)
              external
              requiresAuth
          {
              // Burn shares.
              _burn(from, shareAmount);
              // Transfer assets out.
              if (assetAmount > 0) asset.safeTransfer(to, assetAmount);
              emit Exit(to, address(asset), assetAmount, from, shareAmount);
          }
          //============================== BEFORE TRANSFER HOOK ===============================
          /**
           * @notice Sets the share locker.
           * @notice If set to zero address, the share locker logic is disabled.
           * @dev Callable by OWNER_ROLE.
           */
          function setBeforeTransferHook(address _hook) external requiresAuth {
              hook = BeforeTransferHook(_hook);
          }
          /**
           * @notice Call `beforeTransferHook` passing in `from` `to`, and `msg.sender`.
           */
          function _callBeforeTransfer(address from, address to) internal view {
              if (address(hook) != address(0)) hook.beforeTransfer(from, to, msg.sender);
          }
          function transfer(address to, uint256 amount) public override returns (bool) {
              _callBeforeTransfer(msg.sender, to);
              return super.transfer(to, amount);
          }
          function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
              _callBeforeTransfer(from, to);
              return super.transferFrom(from, to, amount);
          }
          //============================== RECEIVE ===============================
          receive() external payable {}
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity >=0.8.0;
      /// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
      /// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
      abstract contract Auth {
          event OwnershipTransferred(address indexed user, address indexed newOwner);
          event AuthorityUpdated(address indexed user, Authority indexed newAuthority);
          address public owner;
          Authority public authority;
          constructor(address _owner, Authority _authority) {
              owner = _owner;
              authority = _authority;
              emit OwnershipTransferred(msg.sender, _owner);
              emit AuthorityUpdated(msg.sender, _authority);
          }
          modifier requiresAuth() virtual {
              require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");
              _;
          }
          function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
              Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.
              // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
              // aware that this makes protected functions uncallable even to the owner if the authority is out of order.
              return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
          }
          function setAuthority(Authority newAuthority) public virtual {
              // We check if the caller is the owner first because we want to ensure they can
              // always swap out the authority even if it's reverting or using up a lot of gas.
              require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));
              authority = newAuthority;
              emit AuthorityUpdated(msg.sender, newAuthority);
          }
          function transferOwnership(address newOwner) public virtual requiresAuth {
              owner = newOwner;
              emit OwnershipTransferred(msg.sender, newOwner);
          }
      }
      /// @notice A generic interface for a contract which provides authorization data to an Auth instance.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
      /// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
      interface Authority {
          function canCall(
              address user,
              address target,
              bytes4 functionSig
          ) external view returns (bool);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.21;
      interface IPausable {
          function pause() external;
          function unpause() external;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
      pragma solidity ^0.8.20;
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev The ETH balance of the account is not enough to perform the operation.
           */
          error AddressInsufficientBalance(address account);
          /**
           * @dev There's no code at `target` (it is not a contract).
           */
          error AddressEmptyCode(address target);
          /**
           * @dev A call to an address target failed. The target may have reverted.
           */
          error FailedInnerCall();
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              if (address(this).balance < amount) {
                  revert AddressInsufficientBalance(address(this));
              }
              (bool success, ) = recipient.call{value: amount}("");
              if (!success) {
                  revert FailedInnerCall();
              }
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain `call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason or custom error, it is bubbled
           * up by this function (like regular Solidity function calls). However, if
           * the call reverted with no returned reason, this function reverts with a
           * {FailedInnerCall} error.
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              if (address(this).balance < value) {
                  revert AddressInsufficientBalance(address(this));
              }
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResultFromTarget(target, success, returndata);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResultFromTarget(target, success, returndata);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return verifyCallResultFromTarget(target, success, returndata);
          }
          /**
           * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
           * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
           * unsuccessful call.
           */
          function verifyCallResultFromTarget(
              address target,
              bool success,
              bytes memory returndata
          ) internal view returns (bytes memory) {
              if (!success) {
                  _revert(returndata);
              } else {
                  // only check if target is a contract if the call was successful and the return data is empty
                  // otherwise we already know that it was a contract
                  if (returndata.length == 0 && target.code.length == 0) {
                      revert AddressEmptyCode(target);
                  }
                  return returndata;
              }
          }
          /**
           * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
           * revert reason or with a default {FailedInnerCall} error.
           */
          function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
              if (!success) {
                  _revert(returndata);
              } else {
                  return returndata;
              }
          }
          /**
           * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
           */
          function _revert(bytes memory returndata) private pure {
              // Look for revert reason and bubble it up if present
              if (returndata.length > 0) {
                  // The easiest way to bubble the revert reason is using memory via assembly
                  /// @solidity memory-safe-assembly
                  assembly {
                      let returndata_size := mload(returndata)
                      revert(add(32, returndata), returndata_size)
                  }
              } else {
                  revert FailedInnerCall();
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/utils/ERC721Holder.sol)
      pragma solidity ^0.8.20;
      import {IERC721Receiver} from "../IERC721Receiver.sol";
      /**
       * @dev Implementation of the {IERC721Receiver} interface.
       *
       * Accepts all token transfers.
       * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or
       * {IERC721-setApprovalForAll}.
       */
      abstract contract ERC721Holder is IERC721Receiver {
          /**
           * @dev See {IERC721Receiver-onERC721Received}.
           *
           * Always returns `IERC721Receiver.onERC721Received.selector`.
           */
          function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
              return this.onERC721Received.selector;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/utils/ERC1155Holder.sol)
      pragma solidity ^0.8.20;
      import {IERC165, ERC165} from "../../../utils/introspection/ERC165.sol";
      import {IERC1155Receiver} from "../IERC1155Receiver.sol";
      /**
       * @dev Simple implementation of `IERC1155Receiver` that will allow a contract to hold ERC1155 tokens.
       *
       * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be
       * stuck.
       */
      abstract contract ERC1155Holder is ERC165, IERC1155Receiver {
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
              return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
          }
          function onERC1155Received(
              address,
              address,
              uint256,
              uint256,
              bytes memory
          ) public virtual override returns (bytes4) {
              return this.onERC1155Received.selector;
          }
          function onERC1155BatchReceived(
              address,
              address,
              uint256[] memory,
              uint256[] memory,
              bytes memory
          ) public virtual override returns (bytes4) {
              return this.onERC1155BatchReceived.selector;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.21;
      interface BeforeTransferHook {
          function beforeTransfer(address from, address to, address operator) external view;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)
      pragma solidity ^0.8.20;
      /**
       * @title ERC721 token receiver interface
       * @dev Interface for any contract that wants to support safeTransfers
       * from ERC721 asset contracts.
       */
      interface IERC721Receiver {
          /**
           * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
           * by `operator` from `from`, this function is called.
           *
           * It must return its Solidity selector to confirm the token transfer.
           * If any other value is returned or the interface is not implemented by the recipient, the transfer will be
           * reverted.
           *
           * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
           */
          function onERC721Received(
              address operator,
              address from,
              uint256 tokenId,
              bytes calldata data
          ) external returns (bytes4);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.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 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);
       * }
       * ```
       */
      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.0.0) (token/ERC1155/IERC1155Receiver.sol)
      pragma solidity ^0.8.20;
      import {IERC165} from "../../utils/introspection/IERC165.sol";
      /**
       * @dev Interface that must be implemented by smart contracts in order to receive
       * ERC-1155 token transfers.
       */
      interface IERC1155Receiver is IERC165 {
          /**
           * @dev Handles the receipt of a single ERC1155 token type. This function is
           * called at the end of a `safeTransferFrom` after the balance has been updated.
           *
           * NOTE: To accept the transfer, this must return
           * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
           * (i.e. 0xf23a6e61, or its own function selector).
           *
           * @param operator The address which initiated the transfer (i.e. msg.sender)
           * @param from The address which previously owned the token
           * @param id The ID of the token being transferred
           * @param value The amount of tokens being transferred
           * @param data Additional data with no specified format
           * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
           */
          function onERC1155Received(
              address operator,
              address from,
              uint256 id,
              uint256 value,
              bytes calldata data
          ) external returns (bytes4);
          /**
           * @dev Handles the receipt of a multiple ERC1155 token types. This function
           * is called at the end of a `safeBatchTransferFrom` after the balances have
           * been updated.
           *
           * NOTE: To accept the transfer(s), this must return
           * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
           * (i.e. 0xbc197c81, or its own function selector).
           *
           * @param operator The address which initiated the batch transfer (i.e. msg.sender)
           * @param from The address which previously owned the token
           * @param ids An array containing ids of each token being transferred (order and length must match values array)
           * @param values An array containing amounts of each token being transferred (order and length must match ids array)
           * @param data Additional data with no specified format
           * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
           */
          function onERC1155BatchReceived(
              address operator,
              address from,
              uint256[] calldata ids,
              uint256[] calldata values,
              bytes calldata data
          ) external returns (bytes4);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
      pragma solidity ^0.8.20;
      /**
       * @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);
      }