ETH Price: $3,405.50 (+2.77%)

Contract

0x50eBdf73A0dF61B782CeA489e8102b3Bfde0BDA6
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Execute Withdraw...214667862024-12-23 17:50:5915 hrs ago1734976259IN
0x50eBdf73...Bfde0BDA6
0 ETH0.0021548514.64561894
Execute Withdraw...214650642024-12-23 12:03:4720 hrs ago1734955427IN
0x50eBdf73...Bfde0BDA6
0 ETH0.001129377.8
Queue Withdrawal214647742024-12-23 11:05:3521 hrs ago1734951935IN
0x50eBdf73...Bfde0BDA6
0 ETH0.001346627.19695847
Queue Withdrawal214643962024-12-23 9:49:2323 hrs ago1734947363IN
0x50eBdf73...Bfde0BDA6
0 ETH0.0021795311.4
Execute Withdraw...214449282024-12-20 16:29:233 days ago1734712163IN
0x50eBdf73...Bfde0BDA6
0 ETH0.0037438528.79158581
Queue Withdrawal214364752024-12-19 12:09:594 days ago1734610199IN
0x50eBdf73...Bfde0BDA6
0 ETH0.0023273112.17373343
Execute Withdraw...214296572024-12-18 13:16:355 days ago1734527795IN
0x50eBdf73...Bfde0BDA6
0 ETH0.0017630614.91004329
Execute Withdraw...214204242024-12-17 6:20:357 days ago1734416435IN
0x50eBdf73...Bfde0BDA6
0 ETH0.001584649.98647178
Queue Withdrawal214189342024-12-17 1:21:357 days ago1734398495IN
0x50eBdf73...Bfde0BDA6
0 ETH0.001729559.93499293
Queue Withdrawal213790182024-12-11 11:38:5912 days ago1733917139IN
0x50eBdf73...Bfde0BDA6
0 ETH0.0017333814.19389374
Queue Withdrawal213789572024-12-11 11:26:2312 days ago1733916383IN
0x50eBdf73...Bfde0BDA6
0 ETH0.0020659511.86816139
Deposit211920372024-11-15 8:55:2339 days ago1731660923IN
0x50eBdf73...Bfde0BDA6
0 ETH0.0021691317.6249248
Deposit211915382024-11-15 7:15:3539 days ago1731654935IN
0x50eBdf73...Bfde0BDA6
0 ETH0.0020061216.30039917
Deposit211915332024-11-15 7:14:3539 days ago1731654875IN
0x50eBdf73...Bfde0BDA6
0 ETH0.0020125415.74166763
Deposit211903412024-11-15 3:14:3539 days ago1731640475IN
0x50eBdf73...Bfde0BDA6
0 ETH0.0016975313.79306111
Deposit211903312024-11-15 3:12:3539 days ago1731640355IN
0x50eBdf73...Bfde0BDA6
0 ETH0.002053515.09551492
Queue Withdrawal211703542024-11-12 8:17:3542 days ago1731399455IN
0x50eBdf73...Bfde0BDA6
0 ETH0.0045991424.05725549
Deposit211639872024-11-11 10:56:4742 days ago1731322607IN
0x50eBdf73...Bfde0BDA6
0 ETH0.0019968216.2248697
Deposit211636762024-11-11 9:54:2342 days ago1731318863IN
0x50eBdf73...Bfde0BDA6
0 ETH0.0020699316.81889934
Deposit211636672024-11-11 9:52:3542 days ago1731318755IN
0x50eBdf73...Bfde0BDA6
0 ETH0.0020759916.87143705
Deposit211498172024-11-09 11:31:4744 days ago1731151907IN
0x50eBdf73...Bfde0BDA6
0 ETH0.001159399.42136538
Deposit211351482024-11-07 10:22:1146 days ago1730974931IN
0x50eBdf73...Bfde0BDA6
0 ETH0.0018907215.98623573
Deposit211351332024-11-07 10:19:1146 days ago1730974751IN
0x50eBdf73...Bfde0BDA6
0 ETH0.0020779416.88563371
Deposit211132142024-11-04 8:52:4750 days ago1730710367IN
0x50eBdf73...Bfde0BDA6
0 ETH0.000694325.64158656
Deposit211131982024-11-04 8:49:3550 days ago1730710175IN
0x50eBdf73...Bfde0BDA6
0 ETH0.000647295.0629779
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block
From
To
188156052023-12-18 21:39:47371 days ago1702935587  Contract Creation0 ETH
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x5850Afc8...9395323a3
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
WildcatMarket

Compiler Version
v0.8.22+commit.4fc1097e

Optimization Enabled:
Yes with 200 runs

Other Settings:
shanghai EvmVersion
File 1 of 32 : WildcatMarket.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

import './WildcatMarketBase.sol';
import './WildcatMarketConfig.sol';
import './WildcatMarketToken.sol';
import './WildcatMarketWithdrawals.sol';
import '../WildcatSanctionsSentinel.sol';

contract WildcatMarket is
  WildcatMarketBase,
  WildcatMarketConfig,
  WildcatMarketToken,
  WildcatMarketWithdrawals
{
  using MathUtils for uint256;
  using SafeCastLib for uint256;
  using SafeTransferLib for address;

  /**
   * @dev Apply pending interest, delinquency fees and protocol fees
   *      to the state and process the pending withdrawal batch if
   *      one exists and has expired, then update the market's
   *      delinquency status.
   */
  function updateState() external nonReentrant sphereXGuardExternal {
    MarketState memory state = _getUpdatedState();
    _writeState(state);
  }

  /**
   * @dev Deposit up to `amount` underlying assets and mint market tokens
   *      for `msg.sender`.
   *
   *      The actual deposit amount is limited by the market's maximum deposit
   *      amount, which is the configured `maxTotalSupply` minus the current
   *      total supply.
   *
   *      Reverts if the market is closed or if the scaled token amount
   *      that would be minted for the deposit is zero.
   */
  function _depositUpTo(
    uint256 amount
  ) internal virtual nonReentrant returns (uint256 /* actualAmount */) {
    // Get current state
    MarketState memory state = _getUpdatedState();

    if (IWildcatSanctionsSentinel(sentinel).isSanctioned(borrower, msg.sender)) {
      _blockAccount(state, msg.sender);
      _writeState(state);
    } else {
      if (state.isClosed) {
        revert_DepositToClosedMarket();
      }

      // Reduce amount if it would exceed totalSupply
      amount = MathUtils.min(amount, state.maximumDeposit());

      // Scale the mint amount
      uint104 scaledAmount = state.scaleAmount(amount).toUint104();
      if (scaledAmount == 0) revert_NullMintAmount();

      // Transfer deposit from caller
      asset.safeTransferFrom(msg.sender, address(this), amount);

      // Cache account data and revert if not authorized to deposit.
      Account memory account = _castReturnAccount(_getAccountWithRole)(
        msg.sender,
        AuthRole.DepositAndWithdraw
      );
      account.scaledBalance += scaledAmount;
      _accounts[msg.sender] = account;

      emit_Transfer(address(0), msg.sender, amount);
      emit_Deposit(msg.sender, amount, scaledAmount);

      // Increase supply
      state.scaledTotalSupply += scaledAmount;

      // Update stored state
      _writeState(state);

      return amount;
    }
  }

  /**
   * @dev Deposit up to `amount` underlying assets and mint market tokens
   *      for `msg.sender`.
   *
   *      The actual deposit amount is limited by the market's maximum deposit
   *      amount, which is the configured `maxTotalSupply` minus the current
   *      total supply.
   *
   *      Reverts if the market is closed or if the scaled token amount
   *      that would be minted for the deposit is zero.
   */
  function depositUpTo(
    uint256 amount
  ) external virtual sphereXGuardExternal returns (uint256 /* actualAmount */) {
    return _depositUpTo(amount);
  }

  /**
   * @dev Deposit exactly `amount` underlying assets and mint market tokens
   *      for `msg.sender`.
   *
   *     Reverts if the deposit amount would cause the market to exceed the
   *     configured `maxTotalSupply`.
   */
  function deposit(uint256 amount) external virtual sphereXGuardExternal {
    uint256 actualAmount = _depositUpTo(amount);
    if (amount != actualAmount) {
      revert_MaxSupplyExceeded();
    }
  }

  /**
   * @dev Withdraw available protocol fees to the fee recipient.
   */
  function collectFees() external nonReentrant sphereXGuardExternal {
    MarketState memory state = _getUpdatedState();
    if (state.accruedProtocolFees == 0) {
      revert_NullFeeAmount();
    }
    uint128 withdrawableFees = state.withdrawableProtocolFees(totalAssets());
    if (withdrawableFees == 0) {
      revert_InsufficientReservesForFeeWithdrawal();
    }
    state.accruedProtocolFees -= withdrawableFees;
    asset.safeTransfer(feeRecipient, withdrawableFees);
    _writeState(state);
    emit_FeesCollected(withdrawableFees);
  }

  /**
   * @dev Withdraw funds from the market to the borrower.
   *
   *      Can only withdraw up to the assets that are not required
   *      to meet the borrower's collateral obligations.
   *
   *      Reverts if the market is closed.
   */
  function borrow(uint256 amount) external onlyBorrower nonReentrant sphereXGuardExternal {
    if (IWildcatSanctionsSentinel(sentinel).isFlaggedByChainalysis(borrower)) {
      revert_BorrowWhileSanctioned();
    }

    MarketState memory state = _getUpdatedState();
    if (state.isClosed) {
      revert_BorrowFromClosedMarket();
    }
    uint256 borrowable = state.borrowableAssets(totalAssets());
    if (amount > borrowable) {
      revert_BorrowAmountTooHigh();
    }
    asset.safeTransfer(msg.sender, amount);
    _writeState(state);
    emit_Borrow(amount);
  }

  function _repay(MarketState memory state, uint256 amount) internal {
    if (amount == 0) {
      revert_NullRepayAmount();
    }
    if (state.isClosed) {
      revert_RepayToClosedMarket();
    }
    asset.safeTransferFrom(msg.sender, address(this), amount);
    emit_DebtRepaid(msg.sender, amount);
  }

  function repayOutstandingDebt() external nonReentrant sphereXGuardExternal {
    MarketState memory state = _getUpdatedState();
    uint256 outstandingDebt = state.totalDebts().satSub(totalAssets());
    _repay(state, outstandingDebt);
    _writeState(state);
  }

  function repayDelinquentDebt() external nonReentrant sphereXGuardExternal {
    MarketState memory state = _getUpdatedState();
    uint256 delinquentDebt = state.liquidityRequired().satSub(totalAssets());
    _repay(state, delinquentDebt);
    _writeState(state);
  }

  /**
   * @dev Transfers funds from the caller to the market.
   *
   *      Any payments made through this function are considered
   *      repayments from the borrower. Do *not* use this function
   *      if you are a lender or an unrelated third party.
   *
   *      Reverts if the market is closed or `amount` is 0.
   */
  function repay(uint256 amount) external nonReentrant sphereXGuardExternal {
    if (amount == 0) revert_NullRepayAmount();
    asset.safeTransferFrom(msg.sender, address(this), amount);
    emit_DebtRepaid(msg.sender, amount);

    MarketState memory state = _getUpdatedState();
    if (state.isClosed) {
      revert_RepayToClosedMarket();
    }
    _writeState(state);
  }

  /**
   * @dev Sets the market APR to 0% and marks market as closed.
   *
   *      Can not be called if there are any unpaid withdrawal batches.
   *
   *      Transfers remaining debts from borrower if market is not fully
   *      collateralized; otherwise, transfers any assets in excess of
   *      debts to the borrower.
   */
  function closeMarket() external onlyController nonReentrant sphereXGuardExternal {
    if (_withdrawalData.unpaidBatches.length() > 0) {
      revert_CloseMarketWithUnpaidWithdrawals();
    }

    MarketState memory state = _getUpdatedState();

    state.annualInterestBips = 0;
    state.isClosed = true;
    state.reserveRatioBips = 10000;
    // Ensures that delinquency fee doesn't increase scale factor further
    // as doing so would mean last lender in market couldn't fully redeem
    state.timeDelinquent = 0;

    uint256 currentlyHeld = totalAssets();
    uint256 totalDebts = state.totalDebts();
    if (currentlyHeld < totalDebts) {
      // Transfer remaining debts from borrower
      asset.safeTransferFrom(borrower, address(this), totalDebts - currentlyHeld);
    } else if (currentlyHeld > totalDebts) {
      // Transfer excess assets to borrower
      asset.safeTransfer(borrower, currentlyHeld - totalDebts);
    }
    _writeState(state);
    emit_MarketClosed(block.timestamp);
  }
}

File 2 of 32 : WildcatMarketBase.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

import '../ReentrancyGuard.sol';
import '../spherex/SphereXProtectedRegisteredBase.sol';
import '../interfaces/IMarketEventsAndErrors.sol';
import '../interfaces/IERC20Metadata.sol';
import '../interfaces/IWildcatMarketController.sol';
import '../interfaces/IWildcatSanctionsSentinel.sol';
import '../libraries/FeeMath.sol';
import '../libraries/MarketErrors.sol';
import '../libraries/MarketEvents.sol';
import '../libraries/Withdrawal.sol';

contract WildcatMarketBase is
  SphereXProtectedRegisteredBase,
  ReentrancyGuard,
  IMarketEventsAndErrors
{
  using SafeCastLib for uint256;
  using MathUtils for uint256;

  // ==================================================================== //
  //                       Market Config (immutable)                       //
  // ==================================================================== //

  string public constant version = '1.0';

  /// @dev Account with blacklist control, used for blocking sanctioned addresses.
  address public immutable sentinel;

  /// @dev Account with authority to borrow assets from the market.
  address public immutable borrower;

  /// @dev Account that receives protocol fees.
  address public immutable feeRecipient;

  /// @dev Protocol fee added to interest paid by borrower.
  uint256 public immutable protocolFeeBips;

  /// @dev Penalty fee added to interest earned by lenders, does not affect protocol fee.
  uint256 public immutable delinquencyFeeBips;

  /// @dev Time after which delinquency incurs penalty fee.
  uint256 public immutable delinquencyGracePeriod;

  /// @dev Address of the Market Controller.
  address public immutable controller;

  /// @dev Address of the underlying asset.
  address public immutable asset;

  /// @dev Time before withdrawal batches are processed.
  uint256 public immutable withdrawalBatchDuration;

  /// @dev Token decimals (same as underlying asset).
  uint8 public immutable decimals;

  /// @dev Token name (prefixed name of underlying asset).
  string public name;

  /// @dev Token symbol (prefixed symbol of underlying asset).
  string public symbol;

  /// @dev Returns immutable arch-controller address.
  function archController() external view returns (address) {
    return _archController;
  }

  // ===================================================================== //
  //                             Market State                               //
  // ===================================================================== //

  MarketState internal _state;

  mapping(address => Account) internal _accounts;

  WithdrawalData internal _withdrawalData;

  // ===================================================================== //
  //                             Constructor                               //
  // ===================================================================== //

  constructor() {
    MarketParameters memory parameters = IWildcatMarketController(msg.sender).getMarketParameters();

    // Set asset metadata
    asset = parameters.asset;
    name = parameters.name;
    symbol = parameters.symbol;
    decimals = IERC20Metadata(parameters.asset).decimals();

    _state = MarketState({
      isClosed: false,
      maxTotalSupply: parameters.maxTotalSupply,
      accruedProtocolFees: 0,
      normalizedUnclaimedWithdrawals: 0,
      scaledTotalSupply: 0,
      scaledPendingWithdrawals: 0,
      pendingWithdrawalExpiry: 0,
      isDelinquent: false,
      timeDelinquent: 0,
      annualInterestBips: parameters.annualInterestBips,
      reserveRatioBips: parameters.reserveRatioBips,
      scaleFactor: uint112(RAY),
      lastInterestAccruedTimestamp: uint32(block.timestamp)
    });

    sentinel = parameters.sentinel;
    borrower = parameters.borrower;
    controller = parameters.controller;
    feeRecipient = parameters.feeRecipient;
    protocolFeeBips = parameters.protocolFeeBips;
    delinquencyFeeBips = parameters.delinquencyFeeBips;
    delinquencyGracePeriod = parameters.delinquencyGracePeriod;
    withdrawalBatchDuration = parameters.withdrawalBatchDuration;
    _archController = parameters.archController;
    __SphereXProtectedRegisteredBase_init(parameters.sphereXEngine);
  }

  // ===================================================================== //
  //                              Modifiers                                //
  // ===================================================================== //

  modifier onlyBorrower() {
    if (msg.sender != borrower) revert_NotApprovedBorrower();
    _;
  }

  modifier onlyController() {
    if (msg.sender != controller) revert_NotController();
    _;
  }

  // ===================================================================== //
  //                       Internal State Getters                          //
  // ===================================================================== //

  /**
   * @dev Retrieve an account from storage.
   *
   *      Reverts if account is blocked.
   */
  function _getAccount(address accountAddress) internal view returns (Account memory account) {
    account = _accounts[accountAddress];
    if (account.approval == AuthRole.Blocked) {
      revert_AccountBlocked();
    }
  }

  /**
   * @dev Block an account and transfer its balance of market tokens
   *      to an escrow contract.
   *
   *      If the account is already blocked, this function does nothing.
   */
  function _blockAccount(MarketState memory state, address accountAddress) internal {
    Account memory account = _accounts[accountAddress];
    if (account.approval != AuthRole.Blocked) {
      uint104 scaledBalance = account.scaledBalance;
      account.approval = AuthRole.Blocked;
      emit_AuthorizationStatusUpdated(accountAddress, AuthRole.Blocked);

      if (scaledBalance > 0) {
        account.scaledBalance = 0;
        address escrow = IWildcatSanctionsSentinel(sentinel).createEscrow(
          borrower,
          accountAddress,
          address(this)
        );
        emit_Transfer(accountAddress, escrow, state.normalizeAmount(scaledBalance));
        _accounts[escrow].scaledBalance += scaledBalance;
        emit_SanctionedAccountAssetsSentToEscrow(
          accountAddress,
          escrow,
          state.normalizeAmount(scaledBalance)
        );
      }
      _accounts[accountAddress] = account;
    }
  }

  /**
   * @dev Retrieve an account from storage and assert that it has at
   *      least the required role.
   *
   *      If the account's role is not set, queries the controller to
   *      determine if it is an approved lender; if it is, its role
   *      is initialized to DepositAndWithdraw.
   *
   *      Return parameter is declared as a pointer rather than `Account`
   *      to avoid unnecessary zeroing and allocation of memory.
   */
  function _getAccountWithRole(
    address accountAddress,
    AuthRole requiredRole
  ) internal returns (uint256 accountPointer) {
    Account memory account = _getAccount(accountAddress);
    // If account role is null, see if it is authorized on controller.
    if (account.approval == AuthRole.Null) {
      if (IWildcatMarketController(controller).isAuthorizedLender(accountAddress)) {
        account.approval = AuthRole.DepositAndWithdraw;
        emit_AuthorizationStatusUpdated(accountAddress, AuthRole.DepositAndWithdraw);
      }
    }
    // If account role is insufficient, revert.
    if (uint256(account.approval) < uint256(requiredRole)) {
      revert_NotApprovedLender();
    }
    assembly {
      accountPointer := account
    }
  }

  /**
   * @dev Function type cast to avoid duplicate declaration of Account return parameter.
   *
   *      With `viaIR` enabled, calling this function is a noop.
   */
  function _castReturnAccount(
    function(address, AuthRole) internal returns (uint256) fnIn
  ) internal pure returns (function(address, AuthRole) internal returns (Account memory) fnOut) {
    assembly {
      fnOut := fnIn
    }
  }

  // ===================================================================== //
  //                       External State Getters                          //
  // ===================================================================== //

  /**
   * @dev Returns the amount of underlying assets the borrower is obligated
   *      to maintain in the market to avoid delinquency.
   */
  function coverageLiquidity() external view nonReentrantView returns (uint256) {
    return _castReturnMarketState(_calculateCurrentStatePointers)().liquidityRequired();
  }

  /**
   * @dev Returns the scale factor (in ray) used to convert scaled balances
   *      to normalized balances.
   */
  function scaleFactor() external view nonReentrantView returns (uint256) {
    return _castReturnMarketState(_calculateCurrentStatePointers)().scaleFactor;
  }

  /**
   * @dev Total balance in underlying asset.
   */
  function totalAssets() public view returns (uint256 _totalAssets) {
    address assetAddress = asset;
    assembly {
      // Write selector for `balanceOf(address)` to the end of the first word
      // of scratch space, then write `address(this)` to the second word.
      mstore(0, 0x70a08231)
      mstore(0x20, address())
      // Call `asset.balanceOf(address(this))`, writing up to 32 bytes of returndata
      // to scratch space, overwriting calldata.
      // Reverts if the call fails or does not return exactly 32 bytes.
      if iszero(
        and(eq(returndatasize(), 0x20), staticcall(gas(), assetAddress, 0x1c, 0x24, 0, 0x20))
      ) {
        // Revert with error message from the call.
        returndatacopy(0, 0, returndatasize())
        revert(0, returndatasize())
      }
      // Read the return value from scratch space
      _totalAssets := mload(0)
    }
  }

  /**
   * @dev Returns the amount of underlying assets the borrower is allowed
   *      to borrow.
   *
   *      This is the balance of underlying assets minus:
   *      - pending (unpaid) withdrawals
   *      - paid withdrawals
   *      - reserve ratio times the portion of the supply not pending withdrawal
   *      - protocol fees
   */
  function borrowableAssets() external view nonReentrantView returns (uint256) {
    return _castReturnMarketState(_calculateCurrentStatePointers)().borrowableAssets(totalAssets());
  }

  /**
   * @dev Returns the amount of protocol fees (in underlying asset amount)
   *      that have accrued and are pending withdrawal.
   */
  function accruedProtocolFees() external view nonReentrantView returns (uint256) {
    return _castReturnMarketState(_calculateCurrentStatePointers)().accruedProtocolFees;
  }

  function totalDebts() external view nonReentrantView returns (uint256) {
    return _castReturnMarketState(_calculateCurrentStatePointers)().totalDebts();
  }

  function outstandingDebt() external view nonReentrantView returns (uint256) {
    return
      _castReturnMarketState(_calculateCurrentStatePointers)().totalDebts().satSub(totalAssets());
  }

  function delinquentDebt() external view nonReentrantView returns (uint256) {
    return
      _castReturnMarketState(_calculateCurrentStatePointers)().liquidityRequired().satSub(
        totalAssets()
      );
  }

  /**
   * @dev Returns the state of the market as of the last update.
   */
  function previousState() external view returns (MarketState memory) {
    return _state;
  }

  /**
   * @dev Return the state the market would have at the current block after applying
   *      interest and fees accrued since the last update and processing the pending
   *      withdrawal batch if it is expired.
   */
  function currentState() public view nonReentrantView returns (MarketState memory state) {
    state = _castReturnMarketState(_calculateCurrentStatePointers)();
  }

  /**
   * @dev Call `_calculateCurrentState()` and return only the `state` parameter.
   *
   *      Casting the function type prevents a duplicate declaration of the MarketState
   *      return parameter, which would cause unnecessary zeroing and allocation of memory.
   *      With `viaIR` enabled, the cast is a noop.
   */
  function _calculateCurrentStatePointers() internal view returns (uint256 state) {
    (state, , ) = _castReturnPointers(_calculateCurrentState)();
  }

  /**
   * @dev Function type cast to avoid duplicate declaration of MarketState return parameter.
   *
   *      With `viaIR` enabled, calling this function is a noop.
   */
  function _castReturnMarketState(
    function() internal view returns (uint256) fnIn
  ) internal pure returns (function() internal view returns (MarketState memory) fnOut) {
    assembly {
      fnOut := fnIn
    }
  }

  /**
   * @dev Function type cast to avoid duplicate declaration of MarketState and WithdrawalBatch
   *      return parameters.
   *
   *      With `viaIR` enabled, calling this function is a noop.
   */
  function _castReturnPointers(
    function() internal view returns (MarketState memory, uint32, WithdrawalBatch memory) fnIn
  ) internal pure returns (function() internal view returns (uint256, uint32, uint256) fnOut) {
    assembly {
      fnOut := fnIn
    }
  }

  /**
   * @dev Returns the scaled total supply the vaut would have at the current block
   *      after applying interest and fees accrued since the last update and burning
   *      market tokens for the pending withdrawal batch if it is expired.
   */
  function scaledTotalSupply() external view nonReentrantView returns (uint256) {
    return _castReturnMarketState(_calculateCurrentStatePointers)().scaledTotalSupply;
  }

  /**
   * @dev Returns the scaled balance of `account`
   */
  function scaledBalanceOf(address account) external view nonReentrantView returns (uint256) {
    return _accounts[account].scaledBalance;
  }

  /**
   * @dev Returns current role of `account`.
   */
  function getAccountRole(address account) external view nonReentrantView returns (AuthRole) {
    return _accounts[account].approval;
  }

  /**
   * @dev Returns the amount of protocol fees that are currently
   *      withdrawable by the fee recipient.
   */
  function withdrawableProtocolFees() external view returns (uint128) {
    return
      _castReturnMarketState(_calculateCurrentStatePointers)().withdrawableProtocolFees(
        totalAssets()
      );
  }

  // /*//////////////////////////////////////////////////////////////
  //                     Internal State Handlers
  // //////////////////////////////////////////////////////////////*/

  /**
   * @dev Returns cached MarketState after accruing interest and delinquency / protocol fees
   *      and processing expired withdrawal batch, if any.
   *
   *      Used by functions that make additional changes to `state`.
   *
   *      NOTE: Returned `state` does not match `_state` if interest is accrued
   *            Calling function must update `_state` or revert.
   *
   * @return state Market state after interest is accrued.
   */
  function _getUpdatedState() internal returns (MarketState memory state) {
    state = _state;
    // Handle expired withdrawal batch
    if (state.hasPendingExpiredBatch()) {
      uint256 expiry = state.pendingWithdrawalExpiry;
      // Only accrue interest if time has passed since last update.
      // This will only be false if withdrawalBatchDuration is 0.
      uint32 lastInterestAccruedTimestamp = state.lastInterestAccruedTimestamp;
      if (expiry != lastInterestAccruedTimestamp) {
        (uint256 baseInterestRay, uint256 delinquencyFeeRay, uint256 protocolFee) = state
          .updateScaleFactorAndFees(
            protocolFeeBips,
            delinquencyFeeBips,
            delinquencyGracePeriod,
            expiry
          );
        emit_InterestAndFeesAccrued(
          lastInterestAccruedTimestamp,
          expiry,
          state.scaleFactor,
          baseInterestRay,
          delinquencyFeeRay,
          protocolFee
        );
      }
      _processExpiredWithdrawalBatch(state);
    }
    uint32 lastInterestAccruedTimestamp = state.lastInterestAccruedTimestamp;
    // Apply interest and fees accrued since last update (expiry or previous tx)
    if (block.timestamp != lastInterestAccruedTimestamp) {
      (uint256 baseInterestRay, uint256 delinquencyFeeRay, uint256 protocolFee) = state
        .updateScaleFactorAndFees(
          protocolFeeBips,
          delinquencyFeeBips,
          delinquencyGracePeriod,
          block.timestamp
        );
      emit_InterestAndFeesAccrued(
        lastInterestAccruedTimestamp,
        block.timestamp,
        state.scaleFactor,
        baseInterestRay,
        delinquencyFeeRay,
        protocolFee
      );
    }

    // If there is a pending withdrawal batch which is not fully paid off, set aside
    // up to the available liquidity for that batch.
    if (state.pendingWithdrawalExpiry != 0) {
      uint32 expiry = state.pendingWithdrawalExpiry;
      WithdrawalBatch memory batch = _withdrawalData.batches[expiry];
      if (batch.scaledAmountBurned < batch.scaledTotalAmount) {
        // Burn as much of the withdrawal batch as possible with available liquidity.
        uint256 availableLiquidity = batch.availableLiquidityForPendingBatch(state, totalAssets());
        if (availableLiquidity > 0) {
          _applyWithdrawalBatchPayment(batch, state, expiry, availableLiquidity);
          _withdrawalData.batches[expiry] = batch;
        }
      }
    }
  }

  /**
   * @dev Calculate the current state, applying fees and interest accrued since
   *      the last state update as well as the effects of withdrawal batch expiry
   *      on the market state.
   *      Identical to _getUpdatedState() except it does not modify storage or
   *      or emit events.
   *      Returns expired batch data, if any, so queries against batches have
   *      access to the most recent data.
   */
  function _calculateCurrentState()
    internal
    view
    returns (
      MarketState memory state,
      uint32 pendingBatchExpiry,
      WithdrawalBatch memory pendingBatch
    )
  {
    state = _state;
    // Handle expired withdrawal batch
    if (state.hasPendingExpiredBatch()) {
      pendingBatchExpiry = state.pendingWithdrawalExpiry;
      // Only accrue interest if time has passed since last update.
      // This will only be false if withdrawalBatchDuration is 0.
      if (pendingBatchExpiry != state.lastInterestAccruedTimestamp) {
        state.updateScaleFactorAndFees(
          protocolFeeBips,
          delinquencyFeeBips,
          delinquencyGracePeriod,
          pendingBatchExpiry
        );
      }

      pendingBatch = _withdrawalData.batches[pendingBatchExpiry];
      uint256 availableLiquidity = pendingBatch.availableLiquidityForPendingBatch(
        state,
        totalAssets()
      );
      if (availableLiquidity > 0) {
        _applyWithdrawalBatchPaymentView(pendingBatch, state, availableLiquidity);
      }
      state.pendingWithdrawalExpiry = 0;
    }

    if (state.lastInterestAccruedTimestamp != block.timestamp) {
      state.updateScaleFactorAndFees(
        protocolFeeBips,
        delinquencyFeeBips,
        delinquencyGracePeriod,
        block.timestamp
      );
    }

    // If there is a pending withdrawal batch which is not fully paid off, set aside
    // up to the available liquidity for that batch.
    if (state.pendingWithdrawalExpiry != 0) {
      pendingBatchExpiry = state.pendingWithdrawalExpiry;
      pendingBatch = _withdrawalData.batches[pendingBatchExpiry];
      if (pendingBatch.scaledAmountBurned < pendingBatch.scaledTotalAmount) {
        // Burn as much of the withdrawal batch as possible with available liquidity.
        uint256 availableLiquidity = pendingBatch.availableLiquidityForPendingBatch(
          state,
          totalAssets()
        );
        if (availableLiquidity > 0) {
          _applyWithdrawalBatchPaymentView(pendingBatch, state, availableLiquidity);
        }
      }
    }
  }

  /**
   * @dev Writes the cached MarketState to storage and emits an event.
   *      Used at the end of all functions which modify `state`.
   */
  function _writeState(MarketState memory state) internal {
    bool isDelinquent = state.liquidityRequired() > totalAssets();
    state.isDelinquent = isDelinquent;
    _state = state;
    emit_StateUpdated(state.scaleFactor, isDelinquent);
  }

  /**
   * @dev Handles an expired withdrawal batch:
   *      - Retrieves the amount of underlying assets that can be used to pay for the batch.
   *      - If the amount is sufficient to pay the full amount owed to the batch, the batch
   *        is closed and the total withdrawal amount is reserved.
   *      - If the amount is insufficient to pay the full amount owed to the batch, the batch
   *        is recorded as an unpaid batch and the available assets are reserved.
   *      - The assets reserved for the batch are scaled by the current scale factor and that
   *        amount of scaled tokens is burned, ensuring borrowers do not continue paying interest
   *        on withdrawn assets.
   */
  function _processExpiredWithdrawalBatch(MarketState memory state) internal {
    uint32 expiry = state.pendingWithdrawalExpiry;
    WithdrawalBatch memory batch = _withdrawalData.batches[expiry];

    if (batch.scaledAmountBurned < batch.scaledTotalAmount) {
      // Burn as much of the withdrawal batch as possible with available liquidity.
      uint256 availableLiquidity = batch.availableLiquidityForPendingBatch(state, totalAssets());
      if (availableLiquidity > 0) {
        _applyWithdrawalBatchPayment(batch, state, expiry, availableLiquidity);
      }
    }

    emit_WithdrawalBatchExpired(
      expiry,
      batch.scaledTotalAmount,
      batch.scaledAmountBurned,
      batch.normalizedAmountPaid
    );

    if (batch.scaledAmountBurned < batch.scaledTotalAmount) {
      _withdrawalData.unpaidBatches.push(expiry);
    } else {
      emit_WithdrawalBatchClosed(expiry);
    }

    state.pendingWithdrawalExpiry = 0;

    _withdrawalData.batches[expiry] = batch;
  }

  /**
   * @dev Process withdrawal payment, burning market tokens and reserving
   *      underlying assets so they are only available for withdrawals.
   */
  function _applyWithdrawalBatchPayment(
    WithdrawalBatch memory batch,
    MarketState memory state,
    uint32 expiry,
    uint256 availableLiquidity
  ) internal returns (uint104 scaledAmountBurned, uint128 normalizedAmountPaid) {
    uint104 scaledAmountOwed = batch.scaledTotalAmount - batch.scaledAmountBurned;
    // Do nothing if batch is already paid
    if (scaledAmountOwed == 0) {
      return (0, 0);
    }

    uint256 scaledAvailableLiquidity = state.scaleAmount(availableLiquidity);
    scaledAmountBurned = MathUtils.min(scaledAvailableLiquidity, scaledAmountOwed).toUint104();
    normalizedAmountPaid = state.normalizeAmount(scaledAmountBurned).toUint128();

    batch.scaledAmountBurned += scaledAmountBurned;
    batch.normalizedAmountPaid += normalizedAmountPaid;
    state.scaledPendingWithdrawals -= scaledAmountBurned;

    // Update normalizedUnclaimedWithdrawals so the tokens are only accessible for withdrawals.
    state.normalizedUnclaimedWithdrawals += normalizedAmountPaid;

    // Burn market tokens to stop interest accrual upon withdrawal payment.
    state.scaledTotalSupply -= scaledAmountBurned;

    // Emit transfer for external trackers to indicate burn.
    emit_Transfer(address(this), address(0), normalizedAmountPaid);
    emit_WithdrawalBatchPayment(expiry, scaledAmountBurned, normalizedAmountPaid);
  }

  function _applyWithdrawalBatchPaymentView(
    WithdrawalBatch memory batch,
    MarketState memory state,
    uint256 availableLiquidity
  ) internal pure {
    uint104 scaledAmountOwed = batch.scaledTotalAmount - batch.scaledAmountBurned;
    // Do nothing if batch is already paid
    if (scaledAmountOwed == 0) {
      return;
    }
    uint256 scaledAvailableLiquidity = state.scaleAmount(availableLiquidity);
    uint104 scaledAmountBurned = MathUtils
      .min(scaledAvailableLiquidity, scaledAmountOwed)
      .toUint104();
    uint128 normalizedAmountPaid = state.normalizeAmount(scaledAmountBurned).toUint128();

    batch.scaledAmountBurned += scaledAmountBurned;
    batch.normalizedAmountPaid += normalizedAmountPaid;
    state.scaledPendingWithdrawals -= scaledAmountBurned;

    // Update normalizedUnclaimedWithdrawals so the tokens are only accessible for withdrawals.
    state.normalizedUnclaimedWithdrawals += normalizedAmountPaid;

    // Burn market tokens to stop interest accrual upon withdrawal payment.
    state.scaledTotalSupply -= scaledAmountBurned;
  }
}

File 3 of 32 : WildcatMarketConfig.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

import './WildcatMarketBase.sol';
import '../interfaces/IWildcatSanctionsSentinel.sol';
import '../libraries/FeeMath.sol';
import '../libraries/SafeCastLib.sol';

contract WildcatMarketConfig is WildcatMarketBase {
  using SafeCastLib for uint256;

  // ===================================================================== //
  //                      External Config Getters                          //
  // ===================================================================== //

  /**
   * @dev Returns whether or not a market has been closed.
   */
  function isClosed() external view returns (bool) {
    MarketState memory state = currentState();
    return state.isClosed;
  }

  /**
   * @dev Returns the maximum amount of underlying asset that can
   *      currently be deposited to the market.
   */
  function maximumDeposit() external view returns (uint256) {
    MarketState memory state = currentState();
    return state.maximumDeposit();
  }

  /**
   * @dev Returns the maximum supply the market can reach via
   *      deposits (does not apply to interest accrual).
   */
  function maxTotalSupply() external view returns (uint256) {
    return _state.maxTotalSupply;
  }

  /**
   * @dev Returns the annual interest rate earned by lenders
   *      in bips.
   */
  function annualInterestBips() external view returns (uint256) {
    return _state.annualInterestBips;
  }

  function reserveRatioBips() external view returns (uint256) {
    return _state.reserveRatioBips;
  }

  // ========================================================================== //
  //                                  Sanctions                                 //
  // ========================================================================== //

  /// @dev Block a sanctioned account from interacting with the market
  ///      and transfer its balance to an escrow contract.
  // ******************************************************************
  //          *  |\**/|  *          *                                *
  //          *  \ == /  *          *                                *
  //          *   | b|   *          *                                *
  //          *   | y|   *          *                                *
  //          *   \ e/   *          *                                *
  //          *    \/    *          *                                *
  //          *          *          *                                *
  //          *          *          *                                *
  //          *          *  |\**/|  *                                *
  //          *          *  \ == /  *         _.-^^---....,,--       *
  //          *          *   | b|   *    _--                  --_    *
  //          *          *   | y|   *   <                        >)  *
  //          *          *   \ e/   *   |         O-FAC!          |  *
  //          *          *    \/    *    \._                   _./   *
  //          *          *          *       ```--. . , ; .--'''      *
  //          *          *          *   💸        | |   |            *
  //          *          *          *          .-=||  | |=-.    💸   *
  //  💰🤑💰 *   😅    *    😐    *    💸    `-=#$%&%$#=-'         *
  //   \|/    *   /|\    *   /|\    *  🌪         | ;  :|    🌪       *
  //   /\     * 💰/\ 💰 * 💰/\ 💰 *    _____.,-#%&$@%#&#~,._____    *
  // ******************************************************************
  function nukeFromOrbit(address accountAddress) external nonReentrant sphereXGuardExternal {
    if (!IWildcatSanctionsSentinel(sentinel).isSanctioned(borrower, accountAddress)) {
      revert_BadLaunchCode();
    }
    MarketState memory state = _getUpdatedState();
    _blockAccount(state, accountAddress);
    _writeState(state);
  }

  /**
   * @dev Unblock an account that was previously sanctioned and blocked
   *      and has since been removed from the sanctions list or had
   *      their sanctioned status overridden by the borrower.
   */
  function stunningReversal(address accountAddress) external nonReentrant sphereXGuardExternal {
    if (IWildcatSanctionsSentinel(sentinel).isSanctioned(borrower, accountAddress)) {
      revert_NotReversedOrStunning();
    }

    Account memory account = _accounts[accountAddress];
    if (account.approval != AuthRole.Blocked) {
      revert_AccountNotBlocked();
    }

    account.approval = AuthRole.WithdrawOnly;
    emit_AuthorizationStatusUpdated(accountAddress, account.approval);

    _accounts[accountAddress] = account;
  }

  // ========================================================================== //
  //                           External Config Setters                          //
  // ========================================================================== //

  /**
   * @dev Updates multiple accounts' authorization statuses based on whether the controller
   *      has them marked as approved. Requires that the lender *had* full access (i.e.
   *      they were previously authorized) before dropping them down to WithdrawOnly,
   *      else arbitrary accounts could grant themselves Withdraw.
   */
  function updateAccountAuthorizations(
    address[] memory accounts,
    bool authorize
  ) external onlyController nonReentrant sphereXGuardExternal {
    MarketState memory state = _getUpdatedState();
    for (uint256 i = 0; i < accounts.length; i++) {
      Account memory account = _getAccount(accounts[i]);
      if (authorize) {
        account.approval = AuthRole.DepositAndWithdraw;
      } else if (account.approval == AuthRole.DepositAndWithdraw) {
        account.approval = AuthRole.WithdrawOnly;
      }
      _accounts[accounts[i]] = account;
      emit_AuthorizationStatusUpdated(accounts[i], account.approval);
    }
    _writeState(state);
  }

  /**
   * @dev Sets the maximum total supply - this only limits deposits and
   *      does not affect interest accrual.
   */
  function setMaxTotalSupply(
    uint256 _maxTotalSupply
  ) external onlyController nonReentrant sphereXGuardExternal {
    MarketState memory state = _getUpdatedState();

    state.maxTotalSupply = _maxTotalSupply.toUint128();
    _writeState(state);
    emit_MaxTotalSupplyUpdated(_maxTotalSupply);
  }

  /**
   * @dev Sets the annual interest rate earned by lenders in bips.
   */
  function setAnnualInterestBips(
    uint16 _annualInterestBips
  ) public onlyController nonReentrant sphereXGuardExternal {
    MarketState memory state = _getUpdatedState();

    state.annualInterestBips = _annualInterestBips;
    _writeState(state);
    emit_AnnualInterestBipsUpdated(_annualInterestBips);
  }

  /**
   * @dev Adjust the market's reserve ratio.
   *
   *      If the new ratio is lower than the old ratio,
   *      asserts that the market is not currently delinquent.
   *
   *      If the new ratio is higher than the old ratio,
   *      asserts that the market will not become delinquent
   *      because of the change.
   */
  function setReserveRatioBips(
    uint16 _reserveRatioBips
  ) public onlyController nonReentrant sphereXGuardExternal {
    MarketState memory state = _getUpdatedState();

    uint256 initialReserveRatioBips = state.reserveRatioBips;

    if (_reserveRatioBips < initialReserveRatioBips) {
      if (state.liquidityRequired() > totalAssets()) {
        revert_InsufficientReservesForOldLiquidityRatio();
      }
    }
    state.reserveRatioBips = _reserveRatioBips;
    if (_reserveRatioBips > initialReserveRatioBips) {
      if (state.liquidityRequired() > totalAssets()) {
        revert_InsufficientReservesForNewLiquidityRatio();
      }
    }
    _writeState(state);
    emit_ReserveRatioBipsUpdated(_reserveRatioBips);
  }
}

File 4 of 32 : WildcatMarketToken.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

import './WildcatMarketBase.sol';

contract WildcatMarketToken is WildcatMarketBase {
  using SafeCastLib for uint256;

  // ========================================================================== //
  //                                ERC20 Queries                               //
  // ========================================================================== //

  mapping(address => mapping(address => uint256)) public allowance;

  /// @notice Returns the normalized balance of `account` with interest.
  function balanceOf(address account) public view virtual nonReentrantView returns (uint256) {
    return
      _castReturnMarketState(_calculateCurrentStatePointers)().normalizeAmount(
        _accounts[account].scaledBalance
      );
  }

  /// @notice Returns the normalized total supply with interest.
  function totalSupply() external view virtual nonReentrantView returns (uint256) {
    return _castReturnMarketState(_calculateCurrentStatePointers)().totalSupply();
  }

  // ========================================================================== //
  //                                ERC20 Actions                               //
  // ========================================================================== //

  function approve(
    address spender,
    uint256 amount
  ) external virtual nonReentrant sphereXGuardExternal returns (bool) {
    _approve(msg.sender, spender, amount);
    return true;
  }

  function transfer(
    address to,
    uint256 amount
  ) external virtual nonReentrant sphereXGuardExternal returns (bool) {
    _transfer(msg.sender, to, amount);
    return true;
  }

  function transferFrom(
    address from,
    address to,
    uint256 amount
  ) external virtual nonReentrant sphereXGuardExternal returns (bool) {
    uint256 allowed = allowance[from][msg.sender];

    // Saves gas for unlimited approvals.
    if (allowed != type(uint256).max) {
      uint256 newAllowance = allowed - amount;
      _approve(from, msg.sender, newAllowance);
    }

    _transfer(from, to, amount);

    return true;
  }

  function _approve(address approver, address spender, uint256 amount) internal virtual {
    allowance[approver][spender] = amount;
    emit_Approval(approver, spender, amount);
  }

  function _transfer(address from, address to, uint256 amount) internal virtual {
    MarketState memory state = _getUpdatedState();
    uint104 scaledAmount = state.scaleAmount(amount).toUint104();

    if (scaledAmount == 0) {
      revert_NullTransferAmount();
    }

    Account memory fromAccount = _getAccount(from);
    fromAccount.scaledBalance -= scaledAmount;
    _accounts[from] = fromAccount;

    Account memory toAccount = _getAccount(to);
    toAccount.scaledBalance += scaledAmount;
    _accounts[to] = toAccount;

    _writeState(state);
    emit_Transfer(from, to, amount);
  }
}

File 5 of 32 : WildcatMarketWithdrawals.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

import './WildcatMarketBase.sol';
import 'solady/utils/SafeTransferLib.sol';
import '../libraries/BoolUtils.sol';

contract WildcatMarketWithdrawals is WildcatMarketBase {
  using SafeTransferLib for address;
  using MathUtils for uint256;
  using SafeCastLib for uint256;
  using BoolUtils for bool;

  // ========================================================================== //
  //                             Withdrawal Queries                             //
  // ========================================================================== //

  /**
   * @dev Returns the expiry timestamp of every unpaid withdrawal batch.
   */
  function getUnpaidBatchExpiries() external view nonReentrantView returns (uint32[] memory) {
    return _withdrawalData.unpaidBatches.values();
  }

  function getWithdrawalBatch(
    uint32 expiry
  ) external view nonReentrantView returns (WithdrawalBatch memory batch) {
    (, uint32 pendingBatchExpiry, WithdrawalBatch memory pendingBatch) = _calculateCurrentState();
    if ((expiry == pendingBatchExpiry).and(expiry > 0)) {
      return pendingBatch;
    }

    WithdrawalBatch storage _batch = _withdrawalData.batches[expiry];
    batch.scaledTotalAmount = _batch.scaledTotalAmount;
    batch.scaledAmountBurned = _batch.scaledAmountBurned;
    batch.normalizedAmountPaid = _batch.normalizedAmountPaid;
  }

  function getAccountWithdrawalStatus(
    address accountAddress,
    uint32 expiry
  ) external view nonReentrantView returns (AccountWithdrawalStatus memory status) {
    AccountWithdrawalStatus storage _status = _withdrawalData.accountStatuses[expiry][
      accountAddress
    ];
    status.scaledAmount = _status.scaledAmount;
    status.normalizedAmountWithdrawn = _status.normalizedAmountWithdrawn;
  }

  function getAvailableWithdrawalAmount(
    address accountAddress,
    uint32 expiry
  ) external view nonReentrantView returns (uint256) {
    if (expiry >= block.timestamp) {
      revert_WithdrawalBatchNotExpired();
    }
    (, uint32 pendingBatchExpiry, WithdrawalBatch memory pendingBatch) = _calculateCurrentState();
    WithdrawalBatch memory batch;
    if (expiry == pendingBatchExpiry) {
      batch = pendingBatch;
    } else {
      batch = _withdrawalData.batches[expiry];
    }
    AccountWithdrawalStatus memory status = _withdrawalData.accountStatuses[expiry][accountAddress];
    // Rounding errors will lead to some dust accumulating in the batch, but the cost of
    // executing a withdrawal will be lower for users.
    uint256 previousTotalWithdrawn = status.normalizedAmountWithdrawn;
    uint256 newTotalWithdrawn = uint256(batch.normalizedAmountPaid).mulDiv(
      status.scaledAmount,
      batch.scaledTotalAmount
    );
    return newTotalWithdrawn - previousTotalWithdrawn;
  }

  // ========================================================================== //
  //                             Withdrawal Actions                             //
  // ========================================================================== //

  /**
   * @dev Create a withdrawal request for a lender.
   */
  function queueWithdrawal(uint256 amount) external nonReentrant sphereXGuardExternal {
    MarketState memory state = _getUpdatedState();

    uint104 scaledAmount = state.scaleAmount(amount).toUint104();
    if (scaledAmount == 0) {
      revert_NullBurnAmount();
    }

    // Cache account data and revert_if not authorized to withdraw.
    Account memory account = _castReturnAccount(_getAccountWithRole)(
      msg.sender,
      AuthRole.WithdrawOnly
    );

    // Reduce caller's balance and emit transfer event.
    account.scaledBalance -= scaledAmount;
    _accounts[msg.sender] = account;
    emit_Transfer(msg.sender, address(this), amount);

    // Cache batch expiry on the stack for gas savings.
    uint32 expiry = state.pendingWithdrawalExpiry;

    // If there is no pending withdrawal batch, create a new one.
    if (state.pendingWithdrawalExpiry == 0) {
      expiry = uint32(block.timestamp + withdrawalBatchDuration);
      emit_WithdrawalBatchCreated(expiry);
      state.pendingWithdrawalExpiry = expiry;
    }

    WithdrawalBatch memory batch = _withdrawalData.batches[expiry];

    // Add scaled withdrawal amount to account withdrawal status, withdrawal batch and market state.
    _withdrawalData.accountStatuses[expiry][msg.sender].scaledAmount += scaledAmount;
    batch.scaledTotalAmount += scaledAmount;
    state.scaledPendingWithdrawals += scaledAmount;

    emit_WithdrawalQueued(expiry, msg.sender, scaledAmount, amount);

    // Burn as much of the withdrawal batch as possible with available liquidity.
    uint256 availableLiquidity = batch.availableLiquidityForPendingBatch(state, totalAssets());
    if (availableLiquidity > 0) {
      _applyWithdrawalBatchPayment(batch, state, expiry, availableLiquidity);
    }

    // Update stored batch data
    _withdrawalData.batches[expiry] = batch;

    // Update stored state
    _writeState(state);
  }

  /**
   * @dev Execute a pending withdrawal request for a batch that has expired.
   *
   *      Withdraws the proportional amount of the paid batch owed to
   *      `accountAddress` which has not already been withdrawn.
   *
   *      If `accountAddress` is sanctioned, transfers the owed amount to
   *      an escrow contract specific to the account and blocks the account.
   *
   *      Reverts if:
   *      - `expiry >= block.timestamp`
   *      -  `expiry` does not correspond to an existing withdrawal batch
   *      - `accountAddress` has already withdrawn the full amount owed
   */
  function executeWithdrawal(
    address accountAddress,
    uint32 expiry
  ) public nonReentrant sphereXGuardExternal returns (uint256) {
    MarketState memory state = _getUpdatedState();
    uint256 normalizedAmountWithdrawn = _executeWithdrawal(state, accountAddress, expiry);
    // Update stored state
    _writeState(state);
    return normalizedAmountWithdrawn;
  }

  function executeWithdrawals(
    address[] calldata accountAddresses,
    uint32[] calldata expiries
  ) external nonReentrant sphereXGuardExternal returns (uint256[] memory amounts) {
    if (accountAddresses.length != expiries.length) {
      revert_InvalidArrayLength();
    }
    amounts = new uint256[](accountAddresses.length);

    MarketState memory state = _getUpdatedState();

    for (uint256 i = 0; i < accountAddresses.length; i++) {
      amounts[i] = _executeWithdrawal(state, accountAddresses[i], expiries[i]);
    }
    // Update stored state
    _writeState(state);
    return amounts;
  }

  function _executeWithdrawal(
    MarketState memory state,
    address accountAddress,
    uint32 expiry
  ) internal returns (uint256 normalizedAmountWithdrawn) {
    if (expiry >= block.timestamp) {
      revert_WithdrawalBatchNotExpired();
    }

    WithdrawalBatch memory batch = _withdrawalData.batches[expiry];
    AccountWithdrawalStatus storage status = _withdrawalData.accountStatuses[expiry][
      accountAddress
    ];

    uint128 newTotalWithdrawn = uint128(
      MathUtils.mulDiv(batch.normalizedAmountPaid, status.scaledAmount, batch.scaledTotalAmount)
    );

    uint128 normalizedAmountWithdrawn = newTotalWithdrawn - status.normalizedAmountWithdrawn;

    if (normalizedAmountWithdrawn == 0) {
      revert_NullWithdrawalAmount();
    }

    status.normalizedAmountWithdrawn = newTotalWithdrawn;
    state.normalizedUnclaimedWithdrawals -= normalizedAmountWithdrawn;

    if (IWildcatSanctionsSentinel(sentinel).isSanctioned(borrower, accountAddress)) {
      _blockAccount(state, accountAddress);
      address escrow = IWildcatSanctionsSentinel(sentinel).createEscrow(
        borrower,
        accountAddress,
        address(asset)
      );
      asset.safeTransfer(escrow, normalizedAmountWithdrawn);
      emit_SanctionedAccountWithdrawalSentToEscrow(
        accountAddress,
        escrow,
        expiry,
        normalizedAmountWithdrawn
      );
    } else {
      asset.safeTransfer(accountAddress, normalizedAmountWithdrawn);
    }

    emit_WithdrawalExecuted(expiry, accountAddress, normalizedAmountWithdrawn);

    return normalizedAmountWithdrawn;
  }

  function repayAndProcessUnpaidWithdrawalBatches(
    uint256 repayAmount,
    uint256 maxBatches
  ) public nonReentrant sphereXGuardExternal {
    if (repayAmount > 0) {
      asset.safeTransferFrom(msg.sender, address(this), repayAmount);
      emit_DebtRepaid(msg.sender, repayAmount);
    }

    MarketState memory state = _getUpdatedState();
    if (state.isClosed) {
      revert_RepayToClosedMarket();
    }

    // Calculate assets available to process the first batch - will be updated after each batch
    uint256 availableLiquidity = totalAssets() -
      (state.normalizedUnclaimedWithdrawals + state.accruedProtocolFees);

    // Get the maximum number of batches to process
    uint256 numBatches = MathUtils.min(maxBatches, _withdrawalData.unpaidBatches.length());

    uint256 i;
    // Process up to `maxBatches` unpaid batches while there is available liquidity
    while (i++ < numBatches && availableLiquidity > 0) {
      // Process the next unpaid batch using available liquidity
      uint256 normalizedAmountPaid = _processUnpaidWithdrawalBatch(state, availableLiquidity);
      // Reduce liquidity available to next batch
      availableLiquidity = availableLiquidity.satSub(normalizedAmountPaid);
    }
    _writeState(state);
  }

  function _processUnpaidWithdrawalBatch(
    MarketState memory state,
    uint256 availableLiquidity
  ) internal returns (uint256 normalizedAmountPaid) {
    // Get the next unpaid batch timestamp from storage (reverts if none)
    uint32 expiry = _withdrawalData.unpaidBatches.first();

    // Cache batch data in memory
    WithdrawalBatch memory batch = _withdrawalData.batches[expiry];

    // Pay up to the available liquidity to the batch
    (, normalizedAmountPaid) = _applyWithdrawalBatchPayment(
      batch,
      state,
      expiry,
      availableLiquidity
    );

    // Update stored batch
    _withdrawalData.batches[expiry] = batch;

    // Remove batch from unpaid set if fully paid
    if (batch.scaledTotalAmount == batch.scaledAmountBurned) {
      _withdrawalData.unpaidBatches.shift();
      emit_WithdrawalBatchClosed(expiry);
    }
  }
}

File 6 of 32 : WildcatSanctionsSentinel.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

import { IChainalysisSanctionsList } from './interfaces/IChainalysisSanctionsList.sol';
import { IWildcatSanctionsSentinel } from './interfaces/IWildcatSanctionsSentinel.sol';
import { WildcatSanctionsEscrow } from './WildcatSanctionsEscrow.sol';

contract WildcatSanctionsSentinel is IWildcatSanctionsSentinel {
  // ========================================================================== //
  //                                  Constants                                 //
  // ========================================================================== //

  bytes32 public constant override WildcatSanctionsEscrowInitcodeHash =
    keccak256(type(WildcatSanctionsEscrow).creationCode);

  address public immutable override chainalysisSanctionsList;

  address public immutable override archController;

  // ========================================================================== //
  //                                   Storage                                  //
  // ========================================================================== //

  TmpEscrowParams public override tmpEscrowParams;

  mapping(address borrower => mapping(address account => bool sanctionOverride))
    public
    override sanctionOverrides;

  // ========================================================================== //
  //                                 Constructor                                //
  // ========================================================================== //

  constructor(address _archController, address _chainalysisSanctionsList) {
    archController = _archController;
    chainalysisSanctionsList = _chainalysisSanctionsList;
    _resetTmpEscrowParams();
  }

  // ========================================================================== //
  //                              Internal Helpers                              //
  // ========================================================================== //

  function _resetTmpEscrowParams() internal {
    tmpEscrowParams = TmpEscrowParams(address(1), address(1), address(1));
  }

  /**
   * @dev Derive create2 salt for an escrow given the borrower, account and asset.
   *      name prefix and symbol prefix.
   */
  function _deriveSalt(
    address borrower,
    address account,
    address asset
  ) internal pure returns (bytes32 salt) {
    assembly {
      // Cache free memory pointer
      let freeMemoryPointer := mload(0x40)
      // `keccak256(abi.encode(borrower, account, asset))`
      mstore(0x00, borrower)
      mstore(0x20, account)
      mstore(0x40, asset)
      salt := keccak256(0, 0x60)
      // Restore free memory pointer
      mstore(0x40, freeMemoryPointer)
    }
  }

  // ========================================================================== //
  //                              Sanction Queries                              //
  // ========================================================================== //

  /**
   * @dev Returns boolean indicating whether `account` is sanctioned on Chainalysis.
   */
  function isFlaggedByChainalysis(address account) public view override returns (bool) {
    return IChainalysisSanctionsList(chainalysisSanctionsList).isSanctioned(account);
  }

  /**
   * @dev Returns boolean indicating whether `account` is sanctioned on Chainalysis
   *      and that status has not been overridden by `borrower`.
   */
  function isSanctioned(address borrower, address account) public view override returns (bool) {
    return !sanctionOverrides[borrower][account] && isFlaggedByChainalysis(account);
  }

  // ========================================================================== //
  //                             Sanction Overrides                             //
  // ========================================================================== //

  /**
   * @dev Overrides the sanction status of `account` for `borrower`.
   */
  function overrideSanction(address account) public override {
    sanctionOverrides[msg.sender][account] = true;
    emit SanctionOverride(msg.sender, account);
  }

  /**
   * @dev Removes the sanction override of `account` for `borrower`.
   */
  function removeSanctionOverride(address account) public override {
    sanctionOverrides[msg.sender][account] = false;
    emit SanctionOverrideRemoved(msg.sender, account);
  }

  // ========================================================================== //
  //                              Escrow Deployment                             //
  // ========================================================================== //

  /**
   * @dev Creates a new WildcatSanctionsEscrow contract for `borrower`,
   *      `account`, and `asset` or returns the existing escrow contract
   *      if one already exists.
   *
   *      The escrow contract is added to the set of sanction override
   *      addresses for `borrower` so that it can not be blocked.
   */
  function createEscrow(
    address borrower,
    address account,
    address asset
  ) public override returns (address escrowContract) {
    escrowContract = getEscrowAddress(borrower, account, asset);

    // Skip creation if the address code size is non-zero
    if (escrowContract.code.length != 0) return escrowContract;

    tmpEscrowParams = TmpEscrowParams(borrower, account, asset);

    new WildcatSanctionsEscrow{ salt: _deriveSalt(borrower, account, asset) }();

    emit NewSanctionsEscrow(borrower, account, asset);

    sanctionOverrides[borrower][escrowContract] = true;

    emit SanctionOverride(borrower, escrowContract);

    _resetTmpEscrowParams();
  }

  /**
   * @dev Calculate the create2 escrow address for the combination
   *      of `borrower`, `account`, and `asset`.
   */
  function getEscrowAddress(
    address borrower,
    address account,
    address asset
  ) public view override returns (address escrowAddress) {
    bytes32 salt = _deriveSalt(borrower, account, asset);
    bytes32 initCodeHash = WildcatSanctionsEscrowInitcodeHash;
    assembly {
      // Cache the free memory pointer so it can be restored at the end
      let freeMemoryPointer := mload(0x40)

      // Write 0xff + address(this) to bytes 11:32
      mstore(0x00, or(0xff0000000000000000000000000000000000000000, address()))

      // Write salt to bytes 32:64
      mstore(0x20, salt)

      // Write initcode hash to bytes 64:96
      mstore(0x40, initCodeHash)

      // Calculate create2 hash
      escrowAddress := and(keccak256(0x0b, 0x55), 0xffffffffffffffffffffffffffffffffffffffff)

      // Restore the free memory pointer
      mstore(0x40, freeMemoryPointer)
    }
  }
}

File 7 of 32 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20;

/**
 * @title ReentrancyGuard
 * @author 0age
 *         https://github.com/ProjectOpenSea/seaport/blob/main/contracts/lib/ReentrancyGuard.sol
 * Changes: add modifier, bring constants & error definition into contract
 * @notice ReentrancyGuard contains a storage variable and related functionality
 *         for protecting against reentrancy.
 */
contract ReentrancyGuard {
  /**
   * @dev Revert with an error when a caller attempts to reenter a protected
   *      function.
   */
  error NoReentrantCalls();

  // Prevent reentrant calls on protected functions.
  uint256 private _reentrancyGuard;

  uint256 private constant _NOT_ENTERED = 1;
  uint256 private constant _ENTERED = 2;

  /**
   * @dev Reentrancy guard for state-changing functions.
   *      Reverts if the reentrancy guard is currently set; otherwise, sets
   *      the reentrancy guard, executes the function body, then clears the
   *      reentrancy guard.
   */
  modifier nonReentrant() {
    _setReentrancyGuard();
    _;
    _clearReentrancyGuard();
  }

  /**
   * @dev Reentrancy guard for view functions.
   *      Reverts if the reentrancy guard is currently set.
   */
  modifier nonReentrantView() {
    _assertNonReentrant();
    _;
  }

  /**
   * @dev Initialize the reentrancy guard during deployment.
   */
  constructor() {
    // Initialize the reentrancy guard in a cleared state.
    _reentrancyGuard = _NOT_ENTERED;
  }

  /**
   * @dev Internal function to ensure that a sentinel value for the reentrancy
   *      guard is not currently set and, if not, to set a sentinel value for
   *      the reentrancy guard.
   */
  function _setReentrancyGuard() internal {
    // Ensure that the reentrancy guard is not already set.
    _assertNonReentrant();

    // Set the reentrancy guard.
    unchecked {
      _reentrancyGuard = _ENTERED;
    }
  }

  /**
   * @dev Internal function to unset the reentrancy guard sentinel value.
   */
  function _clearReentrancyGuard() internal {
    // Clear the reentrancy guard.
    _reentrancyGuard = _NOT_ENTERED;
  }

  /**
   * @dev Internal view function to ensure that a sentinel value for the
   *         reentrancy guard is not currently set.
   */
  function _assertNonReentrant() internal view {
    // Ensure that the reentrancy guard is not currently set.
    if (_reentrancyGuard != _NOT_ENTERED) {
      revert NoReentrantCalls();
    }
  }
}

File 8 of 32 : SphereXProtectedRegisteredBase.sol
// SPDX-License-Identifier: UNLICENSED
// (c) SphereX 2023 Terms&Conditions
pragma solidity ^0.8.20;

import { ISphereXEngine, ModifierLocals } from './ISphereXEngine.sol';
import './SphereXProtectedEvents.sol';
import './SphereXProtectedErrors.sol';

/**
 * @title Modified version of SphereXProtectedBase for contracts registered
 *        on Wildcat's arch controller.
 *
 * @author Modified from https://github.com/spherex-xyz/spherex-protect-contracts/blob/main/src/SphereXProtectedBase.sol
 *
 * @dev In this version, the WildcatArchController deployment is the SphereX operator.
 *      There is no admin because the arch controller address can not be modified.
 *
 *      All admin functions/events/errors have been removed to reduce contract size.
 *
 *      SphereX engine address validation is delegated to the arch controller.
 */
abstract contract SphereXProtectedRegisteredBase {
  // ========================================================================== //
  //                                  Constants                                 //
  // ========================================================================== //

  /// @dev Storage slot with the address of the SphereX engine contract.
  bytes32 private constant SPHEREX_ENGINE_STORAGE_SLOT =
    bytes32(uint256(keccak256('eip1967.spherex.spherex_engine')) - 1);

  /**
   * @dev Address of the WildcatArchController deployment.
   *      The arch controller is able to set the SphereX engine address.
   *      The inheriting contract must assign this in the constructor.
   */
  address internal immutable _archController;

  // ========================================================================== //
  //                                 Initializer                                //
  // ========================================================================== //

  /**
   * @dev Initializes the SphereXEngine and emits events for the initial
   *      engine and operator (arch controller).
   */
  function __SphereXProtectedRegisteredBase_init(address engine) internal virtual {
    emit_ChangedSpherexOperator(address(0), _archController);
    _setAddress(SPHEREX_ENGINE_STORAGE_SLOT, engine);
    emit_ChangedSpherexEngineAddress(address(0), engine);
  }

  // ========================================================================== //
  //                              Events and Errors                             //
  // ========================================================================== //

  error SphereXOperatorRequired();

  event ChangedSpherexOperator(address oldSphereXAdmin, address newSphereXAdmin);
  event ChangedSpherexEngineAddress(address oldEngineAddress, address newEngineAddress);

  // ========================================================================== //
  //                               Local Modifiers                              //
  // ========================================================================== //

  modifier spherexOnlyOperator() {
    if (msg.sender != _archController) {
      revert_SphereXOperatorRequired();
    }
    _;
  }

  modifier returnsIfNotActivatedPre(ModifierLocals memory locals) {
    locals.engine = sphereXEngine();
    if (locals.engine == address(0)) {
      return;
    }

    _;
  }

  modifier returnsIfNotActivatedPost(ModifierLocals memory locals) {
    if (locals.engine == address(0)) {
      return;
    }

    _;
  }

  // ========================================================================== //
  //                                 Management                                 //
  // ========================================================================== //

  /// @dev Returns the current operator address.
  function sphereXOperator() public view returns (address) {
    return _archController;
  }

  /// @dev Returns the current engine address.
  function sphereXEngine() public view returns (address) {
    return _getAddress(SPHEREX_ENGINE_STORAGE_SLOT);
  }

  /**
   * @dev  Change the address of the SphereX engine.
   *
   *       This is also used to enable SphereX protection, which is disabled
   *       when the engine address is 0.
   *
   * Note: The new engine is not validated as it would be in `SphereXProtectedBase`
   *       because the operator is the arch controller, which validates the engine
   *       address prior to updating it here.
   */
  function changeSphereXEngine(address newSphereXEngine) external spherexOnlyOperator {
    address oldEngine = _getAddress(SPHEREX_ENGINE_STORAGE_SLOT);
    _setAddress(SPHEREX_ENGINE_STORAGE_SLOT, newSphereXEngine);
    emit_ChangedSpherexEngineAddress(oldEngine, newSphereXEngine);
  }

  // ========================================================================== //
  //                                    Hooks                                   //
  // ========================================================================== //

  /**
   * @dev Wrapper for `_getStorageSlotsAndPreparePostCalldata` that returns
   *      a `uint256` pointer to `locals` rather than the struct itself.
   *
   *      Declaring a return parameter for a struct will always zero and
   *      allocate memory for every field in the struct. If the parameter
   *      is always reassigned, the gas and memory used on this are wasted.
   *
   *      Using a `uint256` pointer instead of a struct declaration avoids
   *      this waste while being functionally identical.
   */
  function _sphereXValidateExternalPre() internal returns (uint256 localsPointer) {
    return _castFunctionToPointerOutput(_getStorageSlotsAndPreparePostCalldata)(_getSelector());
  }

  /**
   * @dev Internal function for engine communication. We use it to reduce contract size.
   *      Should be called before the code of an external function.
   *
   *      Queries `storageSlots` from `sphereXValidatePre` on the engine and writes
   *      the result to `locals.storageSlots`, then caches the current storage values
   *      for those slots in `locals.valuesBefore`.
   *
   *      Also allocates memory for the calldata of the future call to `sphereXValidatePost`
   *      and initializes every value in the calldata except for `gas` and `valuesAfter` data.
   *
   * @param num function identifier
   */
  function _getStorageSlotsAndPreparePostCalldata(
    int256 num
  ) internal returnsIfNotActivatedPre(locals) returns (ModifierLocals memory locals) {
    assembly {
      // Read engine from `locals.engine` - this is filled by `returnsIfNotActivatedPre`
      let engineAddress := mload(add(locals, 0x60))

      // Get free memory pointer - this will be used for the calldata
      // to `sphereXValidatePre` and then reused for both `storageSlots`
      // and the future calldata to `sphereXValidatePost`
      let pointer := mload(0x40)

      // Call `sphereXValidatePre(num, msg.sender, msg.data)`
      mstore(pointer, 0x8925ca5a)
      mstore(add(pointer, 0x20), num)
      mstore(add(pointer, 0x40), caller())
      mstore(add(pointer, 0x60), 0x60)
      mstore(add(pointer, 0x80), calldatasize())
      calldatacopy(add(pointer, 0xa0), 0, calldatasize())
      let size := add(0xc4, calldatasize())

      if iszero(
        and(eq(mload(0), 0x20), call(gas(), engineAddress, 0, add(pointer, 28), size, 0, 0x40))
      ) {
        returndatacopy(0, 0, returndatasize())
        revert(0, returndatasize())
      }
      let length := mload(0x20)

      // Set up the memory after the allocation `locals` struct as:
      // [0x00:0x20]: `storageSlots.length`
      // [0x20:0x20+(length * 0x20)]: `storageSlots` data
      // [0x20+(length*0x20):]: calldata for `sphereXValidatePost`

      // The layout for the `sphereXValidatePost` calldata is:
      // [0x00:0x20]: num
      // [0x20:0x40]: gas
      // [0x40:0x60]: valuesBefore offset (0x80)
      // [0x60:0x80]: valuesAfter offset (0xa0 + (0x20 * length))
      // [0x80:0xa0]: valuesBefore length (0xa0 + (0x20 * length))
      // [0xa0:0xa0+(0x20*length)]: valuesBefore data
      // [0xa0+(0x20*length):0xc0+(0x20*length)] valuesAfter length
      // [0xc0+(0x20*length):0xc0+(0x40*length)]: valuesAfter data
      //
      // size of calldata: 0xc0 + (0x40 * length)
      //
      // size of allocation: 0xe0 + (0x60 * length)

      // Calculate size of array data (excluding length): 32 * length
      let arrayDataSize := shl(5, length)

      // Finalize memory allocation with space for `storageSlots` and
      // the calldata for `sphereXValidatePost`.
      mstore(0x40, add(pointer, add(0xe0, mul(arrayDataSize, 3))))

      // Copy `storageSlots` from returndata to the start of the allocated
      // memory buffer and write the pointer to `locals.storageSlots`
      returndatacopy(pointer, 0x20, add(arrayDataSize, 0x20))
      mstore(locals, pointer)

      // Get pointer to future calldata.
      // Add `32 + arrayDataSize` to skip the allocation for `locals.storageSlots`
      // @todo *could* put `valuesBefore` before `storageSlots` and reuse
      // the `storageSlots` buffer for `valuesAfter`
      let calldataPointer := add(pointer, add(arrayDataSize, 0x20))

      // Write `-num` to calldata
      mstore(calldataPointer, sub(0, num))

      // Write `valuesBefore` offset to calldata
      mstore(add(calldataPointer, 0x40), 0x80)

      // Write `locals.valuesBefore` pointer
      mstore(add(locals, 0x20), add(calldataPointer, 0x80))

      // Write `valuesAfter` offset to calldata
      mstore(add(calldataPointer, 0x60), add(0xa0, arrayDataSize))

      // Write `gasleft()` to `locals.gas`
      mstore(add(locals, 0x40), gas())
    }
    _readStorageTo(locals.storageSlots, locals.valuesBefore);
  }

  /**
   * @dev Wrapper for `_callSphereXValidatePost` that takes a pointer
   *      instead of a struct.
   */
  function _sphereXValidateExternalPost(uint256 locals) internal {
    _castFunctionToPointerInput(_callSphereXValidatePost)(locals);
  }

  function _callSphereXValidatePost(
    ModifierLocals memory locals
  ) internal returnsIfNotActivatedPost(locals) {
    uint256 length;
    bytes32[] memory storageSlots;
    bytes32[] memory valuesAfter;
    assembly {
      storageSlots := mload(locals)
      length := mload(storageSlots)
      valuesAfter := add(storageSlots, add(0xc0, shl(6, length)))
    }
    _readStorageTo(storageSlots, valuesAfter);
    assembly {
      let sphereXEngineAddress := mload(add(locals, 0x60))
      let arrayDataSize := shl(5, length)
      let calldataSize := add(0xc4, shl(1, arrayDataSize))

      let calldataPointer := add(storageSlots, add(arrayDataSize, 0x20))
      let gasDiff := sub(mload(add(locals, 0x40)), gas())
      mstore(add(calldataPointer, 0x20), gasDiff)
      let slotBefore := sub(calldataPointer, 32)
      let slotBeforeCache := mload(slotBefore)
      mstore(slotBefore, 0xf0bd9468)
      if iszero(call(gas(), sphereXEngineAddress, 0, add(slotBefore, 28), calldataSize, 0, 0)) {
        returndatacopy(0, 0, returndatasize())
        revert(0, returndatasize())
      }
      mstore(slotBefore, slotBeforeCache)
    }
  }

  /// @dev Returns the function selector from the current calldata.
  function _getSelector() internal pure returns (int256 selector) {
    assembly {
      selector := shr(224, calldataload(0))
    }
  }

  /// @dev Modifier to be incorporated in all external protected non-view functions
  modifier sphereXGuardExternal() {
    uint256 localsPointer = _sphereXValidateExternalPre();
    _;
    _sphereXValidateExternalPost(localsPointer);
  }

  // ========================================================================== //
  //                          Internal Storage Helpers                          //
  // ========================================================================== //

  /// @dev Stores an address in an arbitrary slot
  function _setAddress(bytes32 slot, address newAddress) internal {
    assembly {
      sstore(slot, newAddress)
    }
  }

  /// @dev Returns an address from an arbitrary slot.
  function _getAddress(bytes32 slot) internal view returns (address addr) {
    assembly {
      addr := sload(slot)
    }
  }

  /**
   * @dev Internal function that reads values from given storage slots
   *      and writes them to a particular memory location.
   *
   * @param storageSlots array of storage slots to read
   * @param values array of values to write values to
   */
  function _readStorageTo(bytes32[] memory storageSlots, bytes32[] memory values) internal view {
    assembly {
      let length := mload(storageSlots)
      let arrayDataSize := shl(5, length)
      mstore(values, length)
      let nextSlotPointer := add(storageSlots, 0x20)
      let nextElementPointer := add(values, 0x20)
      let endPointer := add(nextElementPointer, shl(5, length))
      for {

      } lt(nextElementPointer, endPointer) {

      } {
        mstore(nextElementPointer, sload(mload(nextSlotPointer)))
        nextElementPointer := add(nextElementPointer, 0x20)
        nextSlotPointer := add(nextSlotPointer, 0x20)
      }
    }
  }

  // ========================================================================== //
  //                             Function Type Casts                            //
  // ========================================================================== //

  function _castFunctionToPointerInput(
    function(ModifierLocals memory) internal fnIn
  ) internal pure returns (function(uint256) internal fnOut) {
    assembly {
      fnOut := fnIn
    }
  }

  function _castFunctionToPointerOutput(
    function(int256) internal returns (ModifierLocals memory) fnIn
  ) internal pure returns (function(int256) internal returns (uint256) fnOut) {
    assembly {
      fnOut := fnIn
    }
  }
}

File 9 of 32 : IMarketEventsAndErrors.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

import { MarketState } from '../libraries/MarketState.sol';
import { AuthRole } from './WildcatStructsAndEnums.sol';

interface IMarketEventsAndErrors {
  /// @notice Error thrown when deposit exceeds maxTotalSupply
  error MaxSupplyExceeded();

  /// @notice Error thrown when non-borrower tries accessing borrower-only actions
  error NotApprovedBorrower();

  /// @notice Error thrown when non-approved lender tries lending to the market
  error NotApprovedLender();

  /// @notice Error thrown when non-controller tries accessing controller-only actions
  error NotController();

  /// @notice Error thrown when non-sentinel tries to use nukeFromOrbit
  error BadLaunchCode();

  /// @notice Error thrown when new maxTotalSupply lower than totalSupply
  error NewMaxSupplyTooLow();

  /// @notice Error thrown when transfer target is blacklisted
  error AccountBlocked();

  error AccountNotBlocked();

  error NotReversedOrStunning();

  error BorrowAmountTooHigh();

  error InsufficientReservesForFeeWithdrawal();

  error WithdrawalBatchNotExpired();

  error NullMintAmount();

  error NullBurnAmount();

  error NullFeeAmount();

  error NullTransferAmount();

  error NullWithdrawalAmount();

  error NullRepayAmount();

  error DepositToClosedMarket();

  error RepayToClosedMarket();

  error BorrowWhileSanctioned();

  error BorrowFromClosedMarket();

  error CloseMarketWithUnpaidWithdrawals();

  /// @notice Error thrown when reserve ratio set to value
  ///         the market currently would not meet.
  error InsufficientReservesForNewLiquidityRatio();

  error InsufficientReservesForOldLiquidityRatio();

  error InvalidArrayLength();

  event Transfer(address indexed from, address indexed to, uint256 value);

  event Approval(address indexed owner, address indexed spender, uint256 value);

  event MaxTotalSupplyUpdated(uint256 assets);

  event AnnualInterestBipsUpdated(uint256 annualInterestBipsUpdated);

  event ReserveRatioBipsUpdated(uint256 reserveRatioBipsUpdated);

  event SanctionedAccountAssetsSentToEscrow(
    address indexed account,
    address escrow,
    uint256 amount
  );

  event Deposit(address indexed account, uint256 assetAmount, uint256 scaledAmount);

  event Borrow(uint256 assetAmount);

  event DebtRepaid(address indexed from, uint256 assetAmount);

  event MarketClosed(uint256 timestamp);

  event FeesCollected(uint256 assets);

  event StateUpdated(uint256 scaleFactor, bool isDelinquent);

  event InterestAndFeesAccrued(
    uint256 fromTimestamp,
    uint256 toTimestamp,
    uint256 scaleFactor,
    uint256 baseInterestRay,
    uint256 delinquencyFeeRay,
    uint256 protocolFees
  );

  event AuthorizationStatusUpdated(address indexed account, AuthRole role);

  // =====================================================================//
  //                          Withdrawl Events                            //
  // =====================================================================//

  event WithdrawalBatchExpired(
    uint256 indexed expiry,
    uint256 scaledTotalAmount,
    uint256 scaledAmountBurned,
    uint256 normalizedAmountPaid
  );

  /**
   * @dev Emitted when a new withdrawal batch is created.
   */
  event WithdrawalBatchCreated(uint256 indexed expiry);

  /**
   * @dev Emitted when a withdrawal batch is paid off.
   */
  event WithdrawalBatchClosed(uint256 indexed expiry);

  event WithdrawalBatchPayment(
    uint256 indexed expiry,
    uint256 scaledAmountBurned,
    uint256 normalizedAmountPaid
  );

  event WithdrawalQueued(
    uint256 indexed expiry,
    address indexed account,
    uint256 scaledAmount,
    uint256 normalizedAmount
  );

  event WithdrawalExecuted(
    uint256 indexed expiry,
    address indexed account,
    uint256 normalizedAmount
  );

  event SanctionedAccountWithdrawalSentToEscrow(
    address indexed account,
    address escrow,
    uint32 expiry,
    uint256 amount
  );
}

File 10 of 32 : IERC20Metadata.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

import './IERC20.sol';

interface IERC20Metadata is IERC20 {
  function name() external view returns (string memory);

  function symbol() external view returns (string memory);

  function decimals() external view returns (uint8);
}

File 11 of 32 : IWildcatMarketController.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

import './WildcatStructsAndEnums.sol';
import './IWildcatMarketControllerEventsAndErrors.sol';

interface IWildcatMarketController is IWildcatMarketControllerEventsAndErrors {
  // Returns immutable arch-controller
  function archController() external view returns (address);

  // Returns immutable controller factory
  function controllerFactory() external view returns (address);

  // Returns immutable borrower address
  function borrower() external view returns (address);

  // Returns immutable sentinel address
  function sentinel() external view returns (address);

  function marketInitCodeStorage() external view returns (address);

  function marketInitCodeHash() external view returns (uint256);

  /**
   * @dev Returns immutable protocol fee configuration for new markets.
   *      Queried from the controller factory.
   *
   * @return feeRecipient         feeRecipient to use in new markets
   * @return originationFeeAsset  Asset used to pay fees for new market
   *                              deployments
   * @return originationFeeAmount Amount of originationFeeAsset paid
   *                              for new market deployments
   * @return protocolFeeBips      protocolFeeBips to use in new markets
   */
  function getProtocolFeeConfiguration()
    external
    view
    returns (
      address feeRecipient,
      address originationFeeAsset,
      uint80 originationFeeAmount,
      uint16 protocolFeeBips
    );

  /**
   * @dev Returns immutable constraints on market parameters that
   *      the controller will enforce.
   */
  function getParameterConstraints()
    external
    view
    returns (MarketParameterConstraints memory constraints);

  /* -------------------------------------------------------------------------- */
  /*                               Lender Registry                              */
  /* -------------------------------------------------------------------------- */

  function getAuthorizedLenders() external view returns (address[] memory);

  function getAuthorizedLenders(
    uint256 start,
    uint256 end
  ) external view returns (address[] memory);

  function getAuthorizedLendersCount() external view returns (uint256);

  function isAuthorizedLender(address lender) external view returns (bool);

  /**
   * @dev Grant authorization for a set of lenders.
   *
   *      Note: Only updates the internal set of approved lenders.
   *      Must call `updateLenderAuthorization` to apply changes
   *      to existing market accounts
   */
  function authorizeLenders(address[] memory lenders) external;

  /**
   * @dev Revoke authorization for a set of lenders.
   *
   *      Note: Only updates the internal set of approved lenders.
   *      Must call `updateLenderAuthorization` to apply changes
   *      to existing market accounts
   */
  function deauthorizeLenders(address[] memory lenders) external;

  /**
   * @dev Update lender authorization for a set of markets to the current
   *      status.
   */
  function updateLenderAuthorization(address lender, address[] memory markets) external;

  function authorizeLendersAndUpdateMarkets(
    address[] memory markets,
    address[] memory lenders
  ) external;

  function deauthorizeLendersAndUpdateMarkets(
    address[] memory markets,
    address[] memory lenders
  ) external;

  /* -------------------------------------------------------------------------- */
  /*                               Market Registry                              */
  /* -------------------------------------------------------------------------- */

  function isControlledMarket(address market) external view returns (bool);

  function getControlledMarkets() external view returns (address[] memory);

  function getControlledMarkets(
    uint256 start,
    uint256 end
  ) external view returns (address[] memory arr);

  function getControlledMarketsCount() external view returns (uint256);

  function computeMarketAddress(
    address asset,
    string memory namePrefix,
    string memory symbolPrefix
  ) external view returns (address);

  /* -------------------------------------------------------------------------- */
  /*                               Market Controls                              */
  /* -------------------------------------------------------------------------- */

  /**
   * @dev Close a market, setting interest rate to zero and returning all
   * outstanding debt.
   */
  function closeMarket(address market) external;

  /**
   * @dev Sets the maximum total supply (capacity) of a market - this only limits
   *      deposits and does not affect interest accrual.
   */
  function setMaxTotalSupply(address market, uint256 maxTotalSupply) external;

  /**
   * @dev Modify the interest rate for a market.
   * If the new interest rate is lower than the current interest rate,
   * the reserve ratio is set to 90% for the next two weeks.
   */
  function setAnnualInterestBips(address market, uint16 annualInterestBips) external;

  /**
   * @dev Reset the reserve ratio to the value it had prior to
   *      a call to `setAnnualInterestBips`.
   */
  function resetReserveRatio(address market) external;

  function temporaryExcessReserveRatio(
    address
  )
    external
    view
    returns (uint16 originalAnnualInterestBips, uint16 originalReserveRatioBips, uint32 expiry);

  /**
   * @dev Deploys a new instance of the market through the market factory
   *      and registers it with the arch-controller.
   *
   *      If `msg.sender` is not `borrower` or `controllerFactory`,
   *      reverts with `CallerNotBorrowerOrControllerFactory`.
   *
   *	    If `msg.sender == borrower && !archController.isRegisteredBorrower(msg.sender)`,
   *		  reverts with `NotRegisteredBorrower`.
   *
   *      If called by `controllerFactory`, skips borrower check.
   *
   *      If `originationFeeAmount` returned by controller factory is not zero,
   *      transfers `originationFeeAmount` of `originationFeeAsset` from
   *      `msg.sender` to `feeRecipient`.
   */
  function deployMarket(
    address asset,
    string memory namePrefix,
    string memory symbolPrefix,
    uint128 maxTotalSupply,
    uint16 annualInterestBips,
    uint16 delinquencyFeeBips,
    uint32 withdrawalBatchDuration,
    uint16 reserveRatioBips,
    uint32 delinquencyGracePeriod
  ) external returns (address);

  function getMarketParameters() external view returns (MarketParameters memory);
}

File 12 of 32 : IWildcatSanctionsSentinel.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

interface IWildcatSanctionsSentinel {
  event NewSanctionsEscrow(
    address indexed borrower,
    address indexed account,
    address indexed asset
  );

  event SanctionOverride(address indexed borrower, address indexed account);

  event SanctionOverrideRemoved(address indexed borrower, address indexed account);

  struct TmpEscrowParams {
    address borrower;
    address account;
    address asset;
  }

  function WildcatSanctionsEscrowInitcodeHash() external pure returns (bytes32);

  // Returns immutable sanctions list contract
  function chainalysisSanctionsList() external view returns (address);

  // Returns immutable arch-controller
  function archController() external view returns (address);

  // Returns temporary escrow params
  function tmpEscrowParams()
    external
    view
    returns (address borrower, address account, address asset);

  // Returns result of `chainalysisSanctionsList().isSanctioned(account)`
  function isFlaggedByChainalysis(address account) external view returns (bool);

  // Returns result of `chainalysisSanctionsList().isSanctioned(account)`
  // if borrower has not overridden the status of `account`
  function isSanctioned(address borrower, address account) external view returns (bool);

  // Returns boolean indicating whether `borrower` has overridden the
  // sanction status of `account`
  function sanctionOverrides(address borrower, address account) external view returns (bool);

  function overrideSanction(address account) external;

  function removeSanctionOverride(address account) external;

  // Returns create2 address of sanctions escrow contract for
  // combination of `borrower,account,asset`
  function getEscrowAddress(
    address borrower,
    address account,
    address asset
  ) external view returns (address escrowContract);

  /**
   * @dev Returns a create2 deployment of WildcatSanctionsEscrow unique to each
   *      combination of `account,borrower,asset`. If the contract is already
   *      deployed, returns the existing address.
   *
   *      Emits `NewSanctionsEscrow(borrower, account, asset)` if a new contract
   *      is deployed.
   *
   *      The sanctions escrow contract is used to hold assets until either the
   *      sanctioned status is lifted or the assets are released by the borrower.
   */
  function createEscrow(
    address borrower,
    address account,
    address asset
  ) external returns (address escrowContract);
}

File 13 of 32 : FeeMath.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20;

import './MathUtils.sol';
import './SafeCastLib.sol';
import './MarketState.sol';

using SafeCastLib for uint256;
using MathUtils for uint256;

library FeeMath {
  /**
   * @dev Function to calculate the interest accumulated using a linear interest rate formula
   *
   * @param rateBip The interest rate, in bips
   * @param timeDelta The time elapsed since the last interest accrual
   * @return result The interest rate linearly accumulated during the timeDelta, in ray
   */
  function calculateLinearInterestFromBips(
    uint256 rateBip,
    uint256 timeDelta
  ) internal pure returns (uint256 result) {
    uint256 rate = rateBip.bipToRay();
    uint256 accumulatedInterestRay = rate * timeDelta;
    unchecked {
      return accumulatedInterestRay / SECONDS_IN_365_DAYS;
    }
  }

  function calculateBaseInterest(
    MarketState memory state,
    uint256 timestamp
  ) internal pure returns (uint256 baseInterestRay) {
    baseInterestRay = MathUtils.calculateLinearInterestFromBips(
      state.annualInterestBips,
      timestamp - state.lastInterestAccruedTimestamp
    );
  }

  function applyProtocolFee(
    MarketState memory state,
    uint256 baseInterestRay,
    uint256 protocolFeeBips
  ) internal pure returns (uint256 protocolFee) {
    // Protocol fee is charged in addition to the interest paid to lenders.
    uint256 protocolFeeRay = protocolFeeBips.bipMul(baseInterestRay);
    protocolFee = uint256(state.scaledTotalSupply).rayMul(
      uint256(state.scaleFactor).rayMul(protocolFeeRay)
    );
    state.accruedProtocolFees = (state.accruedProtocolFees + protocolFee).toUint128();
  }

  function updateDelinquency(
    MarketState memory state,
    uint256 timestamp,
    uint256 delinquencyFeeBips,
    uint256 delinquencyGracePeriod
  ) internal pure returns (uint256 delinquencyFeeRay) {
    // Calculate the number of seconds the borrower spent in penalized
    // delinquency since the last update.
    uint256 timeWithPenalty = updateTimeDelinquentAndGetPenaltyTime(
      state,
      delinquencyGracePeriod,
      timestamp - state.lastInterestAccruedTimestamp
    );

    if (timeWithPenalty > 0) {
      // Calculate penalty fees on the interest accrued.
      delinquencyFeeRay = calculateLinearInterestFromBips(delinquencyFeeBips, timeWithPenalty);
    }
  }

  /**
   * @notice  Calculate the number of seconds that the market has been in
   *          penalized delinquency since the last update, and update
   *          `timeDelinquent` in state.
   *
   * @dev When `isDelinquent`, equivalent to:
   *        max(0, timeDelta - max(0, delinquencyGracePeriod - previousTimeDelinquent))
   *      When `!isDelinquent`, equivalent to:
   *        min(timeDelta, max(0, previousTimeDelinquent - delinquencyGracePeriod))
   *
   * @param state Encoded state parameters
   * @param delinquencyGracePeriod Seconds in delinquency before penalties apply
   * @param timeDelta Seconds since the last update
   * @param `timeWithPenalty` Number of seconds since the last update where
   *        the market was in delinquency outside of the grace period.
   */
  function updateTimeDelinquentAndGetPenaltyTime(
    MarketState memory state,
    uint256 delinquencyGracePeriod,
    uint256 timeDelta
  ) internal pure returns (uint256 /* timeWithPenalty */) {
    // Seconds in delinquency at last update
    uint256 previousTimeDelinquent = state.timeDelinquent;

    if (state.isDelinquent) {
      // Since the borrower is still delinquent, increase the total
      // time in delinquency by the time elapsed.
      state.timeDelinquent = (previousTimeDelinquent + timeDelta).toUint32();

      // Calculate the number of seconds the borrower had remaining
      // in the grace period.
      uint256 secondsRemainingWithoutPenalty = delinquencyGracePeriod.satSub(
        previousTimeDelinquent
      );

      // Penalties apply for the number of seconds the market spent in
      // delinquency outside of the grace period since the last update.
      return timeDelta.satSub(secondsRemainingWithoutPenalty);
    }

    // Reduce the total time in delinquency by the time elapsed, stopping
    // when it reaches zero.
    state.timeDelinquent = previousTimeDelinquent.satSub(timeDelta).toUint32();

    // Calculate the number of seconds the old timeDelinquent had remaining
    // outside the grace period, or zero if it was already in the grace period.
    uint256 secondsRemainingWithPenalty = previousTimeDelinquent.satSub(delinquencyGracePeriod);

    // Only apply penalties for the remaining time outside of the grace period.
    return MathUtils.min(secondsRemainingWithPenalty, timeDelta);
  }

  /**
   * @dev Calculates interest and delinquency/protocol fees accrued since last state update
   *      and applies it to cached state, returning the rates for base interest and delinquency
   *      fees and the normalized amount of protocol fees accrued.
   *
   *      Takes `timestamp` as input to allow separate calculation of interest
   *      before and after withdrawal batch expiry.
   *
   * @param state Market scale parameters
   * @param protocolFeeBips Protocol fee rate (in bips)
   * @param delinquencyFeeBips Delinquency fee rate (in bips)
   * @param delinquencyGracePeriod Grace period (in seconds) before delinquency fees apply
   * @param timestamp Time to calculate interest and fees accrued until
   * @return baseInterestRay Interest accrued to lenders (ray)
   * @return delinquencyFeeRay Penalty fee incurred by borrower for delinquency (ray).
   * @return protocolFee Protocol fee charged on interest (normalized token amount).
   */
  function updateScaleFactorAndFees(
    MarketState memory state,
    uint256 protocolFeeBips,
    uint256 delinquencyFeeBips,
    uint256 delinquencyGracePeriod,
    uint256 timestamp
  )
    internal
    pure
    returns (uint256 baseInterestRay, uint256 delinquencyFeeRay, uint256 protocolFee)
  {
    baseInterestRay = state.calculateBaseInterest(timestamp);

    if (protocolFeeBips > 0) {
      protocolFee = state.applyProtocolFee(baseInterestRay, protocolFeeBips);
    }

    if (delinquencyFeeBips > 0) {
      delinquencyFeeRay = state.updateDelinquency(
        timestamp,
        delinquencyFeeBips,
        delinquencyGracePeriod
      );
    }

    // Calculate new scaleFactor
    uint256 prevScaleFactor = state.scaleFactor;
    uint256 scaleFactorDelta = prevScaleFactor.rayMul(baseInterestRay + delinquencyFeeRay);

    state.scaleFactor = (prevScaleFactor + scaleFactorDelta).toUint112();
    state.lastInterestAccruedTimestamp = uint32(timestamp);
  }
}

File 14 of 32 : MarketErrors.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

/// @dev Equivalent to `revert MaxSupplyExceeded()`
function revert_MaxSupplyExceeded() pure {
  assembly {
    mstore(0, 0x8a164f63)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert NotApprovedBorrower()`
function revert_NotApprovedBorrower() pure {
  assembly {
    mstore(0, 0x02171e6a)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert NotApprovedLender()`
function revert_NotApprovedLender() pure {
  assembly {
    mstore(0, 0xe50a45ce)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert NotController()`
function revert_NotController() pure {
  assembly {
    mstore(0, 0x23019e67)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert BadLaunchCode()`
function revert_BadLaunchCode() pure {
  assembly {
    mstore(0, 0xa97ab167)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert ReserveRatioBipsTooHigh()`
function revert_ReserveRatioBipsTooHigh() pure {
  assembly {
    mstore(0, 0x8ec83073)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert InterestRateTooHigh()`
function revert_InterestRateTooHigh() pure {
  assembly {
    mstore(0, 0x40c2ffa4)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert InterestFeeTooHigh()`
function revert_InterestFeeTooHigh() pure {
  assembly {
    mstore(0, 0x8e395cd1)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert PenaltyFeeTooHigh()`
function revert_PenaltyFeeTooHigh() pure {
  assembly {
    mstore(0, 0xfdc73e4c)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert AccountBlocked()`
function revert_AccountBlocked() pure {
  assembly {
    mstore(0, 0x6bc671fd)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert AccountNotBlocked()`
function revert_AccountNotBlocked() pure {
  assembly {
    mstore(0, 0xe79042e6)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert NotReversedOrStunning()`
function revert_NotReversedOrStunning() pure {
  assembly {
    mstore(0, 0x3c57ebee)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert BorrowAmountTooHigh()`
function revert_BorrowAmountTooHigh() pure {
  assembly {
    mstore(0, 0x119fe6e3)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert FeeSetWithoutRecipient()`
function revert_FeeSetWithoutRecipient() pure {
  assembly {
    mstore(0, 0x199c082f)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert InsufficientReservesForFeeWithdrawal()`
function revert_InsufficientReservesForFeeWithdrawal() pure {
  assembly {
    mstore(0, 0xf784cfa4)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert WithdrawalBatchNotExpired()`
function revert_WithdrawalBatchNotExpired() pure {
  assembly {
    mstore(0, 0x2561b880)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert NullMintAmount()`
function revert_NullMintAmount() pure {
  assembly {
    mstore(0, 0xe4aa5055)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert NullBurnAmount()`
function revert_NullBurnAmount() pure {
  assembly {
    mstore(0, 0xd61c50f8)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert NullFeeAmount()`
function revert_NullFeeAmount() pure {
  assembly {
    mstore(0, 0x45c835cb)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert NullTransferAmount()`
function revert_NullTransferAmount() pure {
  assembly {
    mstore(0, 0xddee9b30)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert NullWithdrawalAmount()`
function revert_NullWithdrawalAmount() pure {
  assembly {
    mstore(0, 0x186334fe)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert NullRepayAmount()`
function revert_NullRepayAmount() pure {
  assembly {
    mstore(0, 0x7e082088)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert DepositToClosedMarket()`
function revert_DepositToClosedMarket() pure {
  assembly {
    mstore(0, 0x22d7c043)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert RepayToClosedMarket()`
function revert_RepayToClosedMarket() pure {
  assembly {
    mstore(0, 0x61d1bc8f)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert BorrowWhileSanctioned()`
function revert_BorrowWhileSanctioned() pure {
  assembly {
    mstore(0, 0x4a1c13a9)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert BorrowFromClosedMarket()`
function revert_BorrowFromClosedMarket() pure {
  assembly {
    mstore(0, 0xd0242b28)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert CloseMarketWithUnpaidWithdrawals()`
function revert_CloseMarketWithUnpaidWithdrawals() pure {
  assembly {
    mstore(0, 0x4d790997)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert InsufficientReservesForNewLiquidityRatio()`
function revert_InsufficientReservesForNewLiquidityRatio() pure {
  assembly {
    mstore(0, 0x253ecbb9)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert InsufficientReservesForOldLiquidityRatio()`
function revert_InsufficientReservesForOldLiquidityRatio() pure {
  assembly {
    mstore(0, 0x0a68e5bf)
    revert(0x1c, 0x04)
  }
}

/// @dev Equivalent to `revert InvalidArrayLength()`
function revert_InvalidArrayLength() pure {
  assembly {
    mstore(0, 0x9d89020a)
    revert(0x1c, 0x04)
  }
}

File 15 of 32 : MarketEvents.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import { AuthRole } from '../interfaces/WildcatStructsAndEnums.sol';

uint256 constant InterestAndFeesAccrued_abi_head_size = 0xc0;
uint256 constant InterestAndFeesAccrued_toTimestamp_offset = 0x20;
uint256 constant InterestAndFeesAccrued_scaleFactor_offset = 0x40;
uint256 constant InterestAndFeesAccrued_baseInterestRay_offset = 0x60;
uint256 constant InterestAndFeesAccrued_delinquencyFeeRay_offset = 0x80;
uint256 constant InterestAndFeesAccrued_protocolFees_offset = 0xa0;

function emit_Transfer(address from, address to, uint256 value) {
  assembly {
    mstore(0, value)
    log3(0, 0x20, 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, from, to)
  }
}

function emit_Approval(address owner, address spender, uint256 value) {
  assembly {
    mstore(0, value)
    log3(
      0,
      0x20,
      0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925,
      owner,
      spender
    )
  }
}

function emit_MaxTotalSupplyUpdated(uint256 assets) {
  assembly {
    mstore(0, assets)
    log1(0, 0x20, 0xf2672935fc79f5237559e2e2999dbe743bf65430894ac2b37666890e7c69e1af)
  }
}

function emit_AnnualInterestBipsUpdated(uint256 annualInterestBipsUpdated) {
  assembly {
    mstore(0, annualInterestBipsUpdated)
    log1(0, 0x20, 0xff7b6c8be373823323d3c5d99f5d027dd409dce5db54eae511bbdd5546b75037)
  }
}

function emit_ReserveRatioBipsUpdated(uint256 reserveRatioBipsUpdated) {
  assembly {
    mstore(0, reserveRatioBipsUpdated)
    log1(0, 0x20, 0x72877a153052500f5edbb2f9da96a0f45d671d4b4555fdf8628a709dc4eab43a)
  }
}

function emit_SanctionedAccountAssetsSentToEscrow(address account, address escrow, uint256 amount) {
  assembly {
    mstore(0, escrow)
    mstore(0x20, amount)
    log2(0, 0x40, 0x571e706c2f09ae0632313e5f3ae89fffdedfc370a2ea59a07fb0d8091147645b, account)
  }
}

function emit_Deposit(address account, uint256 assetAmount, uint256 scaledAmount) {
  assembly {
    mstore(0, assetAmount)
    mstore(0x20, scaledAmount)
    log2(0, 0x40, 0x90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15, account)
  }
}

function emit_Borrow(uint256 assetAmount) {
  assembly {
    mstore(0, assetAmount)
    log1(0, 0x20, 0xb848ae6b1253b6cb77e81464128ce8bd94d3d524fea54e801e0da869784dca33)
  }
}

function emit_DebtRepaid(address from, uint256 assetAmount) {
  assembly {
    mstore(0, assetAmount)
    log2(0, 0x20, 0xe8b606ac1e5df7657db58d297ca8f41c090fc94c5fd2d6958f043e41736e9fa6, from)
  }
}

function emit_MarketClosed(uint256 _timestamp) {
  assembly {
    mstore(0, _timestamp)
    log1(0, 0x20, 0x9dc30b8eda31a6a144e092e5de600955523a6a925cc15cc1d1b9b4872cfa6155)
  }
}

function emit_FeesCollected(uint256 assets) {
  assembly {
    mstore(0, assets)
    log1(0, 0x20, 0x860c0aa5520013080c2f65981705fcdea474d9f7c3daf954656ed5e65d692d1f)
  }
}

function emit_StateUpdated(uint256 scaleFactor, bool isDelinquent) {
  assembly {
    mstore(0, scaleFactor)
    mstore(0x20, isDelinquent)
    log1(0, 0x40, 0x9385f9ff65bcd2fb81cece54b27d4ec7376795fc4dcff686e370e347b0ed86c0)
  }
}

function emit_InterestAndFeesAccrued(
  uint256 fromTimestamp,
  uint256 toTimestamp,
  uint256 scaleFactor,
  uint256 baseInterestRay,
  uint256 delinquencyFeeRay,
  uint256 protocolFees
) {
  assembly {
    let dst := mload(0x40)
    /// Copy fromTimestamp
    mstore(dst, fromTimestamp)
    /// Copy toTimestamp
    mstore(add(dst, InterestAndFeesAccrued_toTimestamp_offset), toTimestamp)
    /// Copy scaleFactor
    mstore(add(dst, InterestAndFeesAccrued_scaleFactor_offset), scaleFactor)
    /// Copy baseInterestRay
    mstore(add(dst, InterestAndFeesAccrued_baseInterestRay_offset), baseInterestRay)
    /// Copy delinquencyFeeRay
    mstore(add(dst, InterestAndFeesAccrued_delinquencyFeeRay_offset), delinquencyFeeRay)
    /// Copy protocolFees
    mstore(add(dst, InterestAndFeesAccrued_protocolFees_offset), protocolFees)
    log1(
      dst,
      InterestAndFeesAccrued_abi_head_size,
      0x18247a393d0531b65fbd94f5e78bc5639801a4efda62ae7b43533c4442116c3a
    )
  }
}

function emit_AuthorizationStatusUpdated(address account, AuthRole role) {
  assembly {
    mstore(0, role)
    log2(0, 0x20, 0x4cdbc4f47aef831a90102e26cda881868aa5b0c95440b98fe37dbe530f34f5e4, account)
  }
}

function emit_WithdrawalBatchExpired(
  uint256 expiry,
  uint256 scaledTotalAmount,
  uint256 scaledAmountBurned,
  uint256 normalizedAmountPaid
) {
  assembly {
    let freePointer := mload(0x40)
    mstore(0, scaledTotalAmount)
    mstore(0x20, scaledAmountBurned)
    mstore(0x40, normalizedAmountPaid)
    log2(0, 0x60, 0x9262dc39b47cad3a0512e4c08dda248cb345e7163058f300bc63f56bda288b6e, expiry)
    mstore(0x40, freePointer)
  }
}

function emit_WithdrawalBatchCreated(uint256 expiry) {
  assembly {
    log2(0, 0x00, 0x5c9a946d3041134198ebefcd814de7748def6576efd3d1b48f48193e183e89ef, expiry)
  }
}

function emit_WithdrawalBatchClosed(uint256 expiry) {
  assembly {
    log2(0, 0x00, 0xcbdf25bf6e096dd9030d89bb2ba2e3e7adb82d25a233c3ca3d92e9f098b74e55, expiry)
  }
}

function emit_WithdrawalBatchPayment(
  uint256 expiry,
  uint256 scaledAmountBurned,
  uint256 normalizedAmountPaid
) {
  assembly {
    mstore(0, scaledAmountBurned)
    mstore(0x20, normalizedAmountPaid)
    log2(0, 0x40, 0x5272034725119f19d7236de4129fdb5093f0dcb80282ca5edbd587df91d2bd89, expiry)
  }
}

function emit_WithdrawalQueued(
  uint256 expiry,
  address account,
  uint256 scaledAmount,
  uint256 normalizedAmount
) {
  assembly {
    mstore(0, scaledAmount)
    mstore(0x20, normalizedAmount)
    log3(
      0,
      0x40,
      0xecc966b282a372469fa4d3e497c2ac17983c3eaed03f3f17c9acf4b15591663e,
      expiry,
      account
    )
  }
}

function emit_WithdrawalExecuted(uint256 expiry, address account, uint256 normalizedAmount) {
  assembly {
    mstore(0, normalizedAmount)
    log3(
      0,
      0x20,
      0xd6cddb3d69146e96ebc2c87b1b3dd0b20ee2d3b0eadf134e011afb434a3e56e6,
      expiry,
      account
    )
  }
}

function emit_SanctionedAccountWithdrawalSentToEscrow(
  address account,
  address escrow,
  uint32 expiry,
  uint256 amount
) {
  assembly {
    let freePointer := mload(0x40)
    mstore(0, escrow)
    mstore(0x20, expiry)
    mstore(0x40, amount)
    log2(0, 0x60, 0x0d0843a0fcb8b83f625aafb6e42f234ac48c6728b207d52d97cfa8fbd34d498f, account)
    mstore(0x40, freePointer)
  }
}

File 16 of 32 : Withdrawal.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

import './MarketState.sol';
import './FIFOQueue.sol';

using MathUtils for uint256;
using SafeCastLib for uint256;
using WithdrawalLib for WithdrawalBatch global;
using WithdrawalLib for WithdrawalData global;

/**
 * Withdrawals are grouped together in batches with a fixed expiry.
 * Until a withdrawal is paid out, the tokens are not burned from the market
 * and continue to accumulate interest.
 */
struct WithdrawalBatch {
  // Total scaled amount of tokens to be withdrawn
  uint104 scaledTotalAmount;
  // Amount of scaled tokens that have been paid by borrower
  uint104 scaledAmountBurned;
  // Amount of normalized tokens that have been paid by borrower
  uint128 normalizedAmountPaid;
}

struct AccountWithdrawalStatus {
  uint104 scaledAmount;
  uint128 normalizedAmountWithdrawn;
}

struct WithdrawalData {
  FIFOQueue unpaidBatches;
  mapping(uint32 => WithdrawalBatch) batches;
  mapping(uint256 => mapping(address => AccountWithdrawalStatus)) accountStatuses;
}

library WithdrawalLib {
  function scaledOwedAmount(WithdrawalBatch memory batch) internal pure returns (uint104) {
    return batch.scaledTotalAmount - batch.scaledAmountBurned;
  }

  /**
   * @dev Get the amount of assets which are not already reserved
   *      for prior withdrawal batches. This must only be used on
   *      the latest withdrawal batch to expire.
   */
  function availableLiquidityForPendingBatch(
    WithdrawalBatch memory batch,
    MarketState memory state,
    uint256 totalAssets
  ) internal pure returns (uint256) {
    // Subtract normalized value of pending scaled withdrawals, processed
    // withdrawals and protocol fees.
    uint256 priorScaledAmountPending = (state.scaledPendingWithdrawals - batch.scaledOwedAmount());
    uint256 unavailableAssets = state.normalizedUnclaimedWithdrawals +
      state.normalizeAmount(priorScaledAmountPending) +
      state.accruedProtocolFees;
    return totalAssets.satSub(unavailableAssets);
  }
}

File 17 of 32 : SafeCastLib.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20;

import './Errors.sol';

library SafeCastLib {
  function _assertNonOverflow(bool didNotOverflow) private pure {
    assembly {
      if iszero(didNotOverflow) {
        mstore(0, Panic_ErrorSelector)
        mstore(Panic_ErrorCodePointer, Panic_Arithmetic)
        revert(Error_SelectorPointer, Panic_ErrorLength)
      }
    }
  }

  function toUint8(uint256 x) internal pure returns (uint8 y) {
    _assertNonOverflow(x == (y = uint8(x)));
  }

  function toUint16(uint256 x) internal pure returns (uint16 y) {
    _assertNonOverflow(x == (y = uint16(x)));
  }

  function toUint24(uint256 x) internal pure returns (uint24 y) {
    _assertNonOverflow(x == (y = uint24(x)));
  }

  function toUint32(uint256 x) internal pure returns (uint32 y) {
    _assertNonOverflow(x == (y = uint32(x)));
  }

  function toUint40(uint256 x) internal pure returns (uint40 y) {
    _assertNonOverflow(x == (y = uint40(x)));
  }

  function toUint48(uint256 x) internal pure returns (uint48 y) {
    _assertNonOverflow(x == (y = uint48(x)));
  }

  function toUint56(uint256 x) internal pure returns (uint56 y) {
    _assertNonOverflow(x == (y = uint56(x)));
  }

  function toUint64(uint256 x) internal pure returns (uint64 y) {
    _assertNonOverflow(x == (y = uint64(x)));
  }

  function toUint72(uint256 x) internal pure returns (uint72 y) {
    _assertNonOverflow(x == (y = uint72(x)));
  }

  function toUint80(uint256 x) internal pure returns (uint80 y) {
    _assertNonOverflow(x == (y = uint80(x)));
  }

  function toUint88(uint256 x) internal pure returns (uint88 y) {
    _assertNonOverflow(x == (y = uint88(x)));
  }

  function toUint96(uint256 x) internal pure returns (uint96 y) {
    _assertNonOverflow(x == (y = uint96(x)));
  }

  function toUint104(uint256 x) internal pure returns (uint104 y) {
    _assertNonOverflow(x == (y = uint104(x)));
  }

  function toUint112(uint256 x) internal pure returns (uint112 y) {
    _assertNonOverflow(x == (y = uint112(x)));
  }

  function toUint120(uint256 x) internal pure returns (uint120 y) {
    _assertNonOverflow(x == (y = uint120(x)));
  }

  function toUint128(uint256 x) internal pure returns (uint128 y) {
    _assertNonOverflow(x == (y = uint128(x)));
  }

  function toUint136(uint256 x) internal pure returns (uint136 y) {
    _assertNonOverflow(x == (y = uint136(x)));
  }

  function toUint144(uint256 x) internal pure returns (uint144 y) {
    _assertNonOverflow(x == (y = uint144(x)));
  }

  function toUint152(uint256 x) internal pure returns (uint152 y) {
    _assertNonOverflow(x == (y = uint152(x)));
  }

  function toUint160(uint256 x) internal pure returns (uint160 y) {
    _assertNonOverflow(x == (y = uint160(x)));
  }

  function toUint168(uint256 x) internal pure returns (uint168 y) {
    _assertNonOverflow(x == (y = uint168(x)));
  }

  function toUint176(uint256 x) internal pure returns (uint176 y) {
    _assertNonOverflow(x == (y = uint176(x)));
  }

  function toUint184(uint256 x) internal pure returns (uint184 y) {
    _assertNonOverflow(x == (y = uint184(x)));
  }

  function toUint192(uint256 x) internal pure returns (uint192 y) {
    _assertNonOverflow(x == (y = uint192(x)));
  }

  function toUint200(uint256 x) internal pure returns (uint200 y) {
    _assertNonOverflow(x == (y = uint200(x)));
  }

  function toUint208(uint256 x) internal pure returns (uint208 y) {
    _assertNonOverflow(x == (y = uint208(x)));
  }

  function toUint216(uint256 x) internal pure returns (uint216 y) {
    _assertNonOverflow(x == (y = uint216(x)));
  }

  function toUint224(uint256 x) internal pure returns (uint224 y) {
    _assertNonOverflow(x == (y = uint224(x)));
  }

  function toUint232(uint256 x) internal pure returns (uint232 y) {
    _assertNonOverflow(x == (y = uint232(x)));
  }

  function toUint240(uint256 x) internal pure returns (uint240 y) {
    _assertNonOverflow(x == (y = uint240(x)));
  }

  function toUint248(uint256 x) internal pure returns (uint248 y) {
    _assertNonOverflow(x == (y = uint248(x)));
  }
}

File 18 of 32 : SafeTransferLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - For ERC20s, this implementation won't check that a token has code,
///   responsibility is delegated to the caller.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

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

    /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
    uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ETH OPERATIONS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
    //
    // The regular variants:
    // - Forwards all remaining gas to the target.
    // - Reverts if the target reverts.
    // - Reverts if the current contract has insufficient balance.
    //
    // The force variants:
    // - Forwards with an optional gas stipend
    //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
    // - If the target reverts, or if the gas stipend is exhausted,
    //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
    //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
    // - Reverts if the current contract has insufficient balance.
    //
    // The try variants:
    // - Forwards with a mandatory gas stipend.
    // - Instead of reverting, returns whether the transfer succeeded.

    /// @dev Sends `amount` (in wei) ETH to `to`.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`.
    function safeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer all the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // forgefmt: disable-next-item
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function trySafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC20 OPERATIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for
    /// the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
            amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, to) // Store the `to` argument.
            amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x34, 0) // Store 0 for the `amount`.
                mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                pop(call(gas(), token, 0, 0x10, 0x44, 0x00, 0x00)) // Reset the approval.
                mstore(0x34, amount) // Store back the original `amount`.
                // Retry the approval, reverting upon failure.
                if iszero(
                    and(
                        or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                        call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    )
                ) {
                    mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            amount :=
                mul(
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }
}

File 19 of 32 : BoolUtils.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

library BoolUtils {
  function and(bool a, bool b) internal pure returns (bool c) {
    assembly {
      c := and(a, b)
    }
  }

  function or(bool a, bool b) internal pure returns (bool c) {
    assembly {
      c := or(a, b)
    }
  }

  function xor(bool a, bool b) internal pure returns (bool c) {
    assembly {
      c := xor(a, b)
    }
  }
}

File 20 of 32 : IChainalysisSanctionsList.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

interface IChainalysisSanctionsList {
  function isSanctioned(address addr) external view returns (bool);
}

File 21 of 32 : WildcatSanctionsEscrow.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

import './interfaces/IERC20.sol';
import './interfaces/IWildcatSanctionsEscrow.sol';
import './interfaces/IWildcatSanctionsSentinel.sol';
import 'solady/utils/SafeTransferLib.sol';

contract WildcatSanctionsEscrow is IWildcatSanctionsEscrow {
  using SafeTransferLib for address;

  address public immutable override sentinel;
  address public immutable override borrower;
  address public immutable override account;
  address internal immutable asset;

  constructor() {
    sentinel = msg.sender;
    (borrower, account, asset) = IWildcatSanctionsSentinel(sentinel).tmpEscrowParams();
  }

  function balance() public view override returns (uint256) {
    return IERC20(asset).balanceOf(address(this));
  }

  function canReleaseEscrow() public view override returns (bool) {
    return !IWildcatSanctionsSentinel(sentinel).isSanctioned(borrower, account);
  }

  function escrowedAsset() public view override returns (address, uint256) {
    return (asset, balance());
  }

  function releaseEscrow() public override {
    if (!canReleaseEscrow()) revert CanNotReleaseEscrow();

    uint256 amount = balance();
    address _account = account;
    address _asset = asset;

    asset.safeTransfer(_account, amount);

    emit EscrowReleased(_account, _asset, amount);
  }
}

File 22 of 32 : ISphereXEngine.sol
// SPDX-License-Identifier: UNLICENSED
// (c) SphereX 2023 Terms&Conditions
pragma solidity ^0.8.20;

/// @dev this struct is used to reduce the stack usage of the modifiers.
struct ModifierLocals {
  bytes32[] storageSlots;
  bytes32[] valuesBefore;
  uint256 gas;
  address engine;
}

/// @title Interface for SphereXEngine - definitions of core functionality
/// @author SphereX Technologies ltd
/// @notice This interface is imported by SphereXProtected, so that SphereXProtected can call functions from SphereXEngine
/// @dev Full docs of these functions can be found in SphereXEngine
interface ISphereXEngine {
  function sphereXValidatePre(
    int256 num,
    address sender,
    bytes calldata data
  ) external returns (bytes32[] memory);

  function sphereXValidatePost(
    int256 num,
    uint256 gas,
    bytes32[] calldata valuesBefore,
    bytes32[] calldata valuesAfter
  ) external;

  function sphereXValidateInternalPre(int256 num) external returns (bytes32[] memory);

  function sphereXValidateInternalPost(
    int256 num,
    uint256 gas,
    bytes32[] calldata valuesBefore,
    bytes32[] calldata valuesAfter
  ) external;

  function addAllowedSenderOnChain(address sender) external;

  /// This function is taken as is from OZ IERC165, we don't inherit from OZ
  /// to avoid collisions with the customer OZ version.
  /// @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 23 of 32 : SphereXProtectedEvents.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

function emit_ChangedSpherexOperator(address oldSphereXAdmin, address newSphereXAdmin) {
  assembly {
    mstore(0, oldSphereXAdmin)
    mstore(0x20, newSphereXAdmin)
    log1(0, 0x40, 0x2ac55ae7ba47db34b5334622acafeb34a65daf143b47019273185d64c73a35a5)
  }
}

function emit_ChangedSpherexEngineAddress(address oldEngineAddress, address newEngineAddress) {
  assembly {
    mstore(0, oldEngineAddress)
    mstore(0x20, newEngineAddress)
    log1(0, 0x40, 0xf33499cccaa0611882086224cc48cd82ef54b66a4d2edf4ed67108dd516896d5)
  }
}

function emit_SpherexAdminTransferStarted(address currentAdmin, address pendingAdmin) {
  assembly {
    mstore(0, currentAdmin)
    mstore(0x20, pendingAdmin)
    log1(0, 0x40, 0x5778f1547abbbb86090a43c32aec38334b31df4beeb6f8f3fa063f593b53a526)
  }
}

function emit_SpherexAdminTransferCompleted(address oldAdmin, address newAdmin) {
  assembly {
    mstore(0, oldAdmin)
    mstore(0x20, newAdmin)
    log1(0, 0x40, 0x67ebaebcd2ca5a91a404e898110f221747e8d15567f2388a34794aab151cf3e6)
  }
}

function emit_NewAllowedSenderOnchain(address sender) {
  assembly {
    mstore(0, sender)
    log1(0, 0x20, 0x6de0a1fd3a59e5479e6480ba65ef28d4f3ab8143c2c631bbfd9969ab39074797)
  }
}

File 24 of 32 : SphereXProtectedErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

function revert_SphereXOperatorRequired() pure {
  assembly {
    mstore(0, 0x4ee0b8f8)
    revert(0x1c, 0x04)
  }
}

function revert_SphereXAdminRequired() pure {
  assembly {
    mstore(0, 0x6222a550)
    revert(0x1c, 0x04)
  }
}

function revert_SphereXOperatorOrAdminRequired() pure {
  assembly {
    mstore(0, 0xb2dbeb59)
    revert(0x1c, 0x04)
  }
}

function revert_SphereXNotPendingAdmin() pure {
  assembly {
    mstore(0, 0x4d28a58e)
    revert(0x1c, 0x04)
  }
}

function revert_SphereXNotEngine() pure {
  assembly {
    mstore(0, 0x7dcb7ada)
    revert(0x1c, 0x04)
  }
}

File 25 of 32 : MarketState.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

import { AuthRole } from '../interfaces/WildcatStructsAndEnums.sol';
import './MathUtils.sol';
import './SafeCastLib.sol';
import './FeeMath.sol';

using MarketStateLib for MarketState global;
using MarketStateLib for Account global;
using FeeMath for MarketState global;

struct MarketState {
  bool isClosed;
  uint128 maxTotalSupply;
  uint128 accruedProtocolFees;
  // Underlying assets reserved for withdrawals which have been paid
  // by the borrower but not yet executed.
  uint128 normalizedUnclaimedWithdrawals;
  // Scaled token supply (divided by scaleFactor)
  uint104 scaledTotalSupply;
  // Scaled token amount in withdrawal batches that have not been
  // paid by borrower yet.
  uint104 scaledPendingWithdrawals;
  uint32 pendingWithdrawalExpiry;
  // Whether market is currently delinquent (liquidity under requirement)
  bool isDelinquent;
  // Seconds borrower has been delinquent
  uint32 timeDelinquent;
  // Annual interest rate accrued to lenders, in basis points
  uint16 annualInterestBips;
  // Percentage of outstanding balance that must be held in liquid reserves
  uint16 reserveRatioBips;
  // Ratio between internal balances and underlying token amounts
  uint112 scaleFactor;
  uint32 lastInterestAccruedTimestamp;
}

struct Account {
  AuthRole approval;
  uint104 scaledBalance;
}

library MarketStateLib {
  using MathUtils for uint256;
  using SafeCastLib for uint256;

  /**
   * @dev Returns the normalized total supply of the market.
   */
  function totalSupply(MarketState memory state) internal pure returns (uint256) {
    return state.normalizeAmount(state.scaledTotalSupply);
  }

  /**
   * @dev Returns the maximum amount of tokens that can be deposited without
   *      reaching the maximum total supply.
   */
  function maximumDeposit(MarketState memory state) internal pure returns (uint256) {
    return uint256(state.maxTotalSupply).satSub(state.totalSupply());
  }

  /**
   * @dev Normalize an amount of scaled tokens using the current scale factor.
   */
  function normalizeAmount(
    MarketState memory state,
    uint256 amount
  ) internal pure returns (uint256) {
    return amount.rayMul(state.scaleFactor);
  }

  /**
   * @dev Scale an amount of normalized tokens using the current scale factor.
   */
  function scaleAmount(MarketState memory state, uint256 amount) internal pure returns (uint256) {
    return amount.rayDiv(state.scaleFactor);
  }

  /**
   * @dev Collateralization requirement is:
   *      - 100% of all pending (unpaid) withdrawals
   *      - 100% of all unclaimed (paid) withdrawals
   *      - reserve ratio times the outstanding debt (supply - pending withdrawals)
   *      - accrued protocol fees
   */
  function liquidityRequired(
    MarketState memory state
  ) internal pure returns (uint256 _liquidityRequired) {
    uint256 scaledWithdrawals = state.scaledPendingWithdrawals;
    uint256 scaledRequiredReserves = (state.scaledTotalSupply - scaledWithdrawals).bipMul(
      state.reserveRatioBips
    ) + scaledWithdrawals;
    return
      state.normalizeAmount(scaledRequiredReserves) +
      state.accruedProtocolFees +
      state.normalizedUnclaimedWithdrawals;
  }

  /**
   * @dev Returns the amount of underlying assets that can be withdrawn
   *      for protocol fees. The only debts with higher priority are
   *      processed withdrawals that have not been executed.
   */
  function withdrawableProtocolFees(
    MarketState memory state,
    uint256 totalAssets
  ) internal pure returns (uint128) {
    uint256 totalAvailableAssets = totalAssets - state.normalizedUnclaimedWithdrawals;
    return uint128(MathUtils.min(totalAvailableAssets, state.accruedProtocolFees));
  }

  /**
   * @dev Returns the amount of underlying assets that can be borrowed.
   *
   *      The borrower must maintain sufficient assets in the market to
   *      cover 100% of pending withdrawals, 100% of previously processed
   *      withdrawals (before they are executed), and the reserve ratio
   *      times the outstanding debt (deposits not pending withdrawal).
   *
   *      Any underlying assets in the market above this amount can be borrowed.
   */
  function borrowableAssets(
    MarketState memory state,
    uint256 totalAssets
  ) internal pure returns (uint256) {
    return totalAssets.satSub(state.liquidityRequired());
  }

  function hasPendingExpiredBatch(MarketState memory state) internal view returns (bool result) {
    uint256 expiry = state.pendingWithdrawalExpiry;
    assembly {
      // Equivalent to expiry > 0 && expiry < block.timestamp
      result := and(gt(expiry, 0), gt(timestamp(), expiry))
    }
  }

  function totalDebts(MarketState memory state) internal pure returns (uint256) {
    return
      state.normalizeAmount(state.scaledTotalSupply) +
      state.normalizedUnclaimedWithdrawals +
      state.accruedProtocolFees;
  }
}

File 26 of 32 : WildcatStructsAndEnums.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

import { MarketState } from '../libraries/MarketState.sol';

enum AuthRole {
  Null,
  Blocked,
  WithdrawOnly,
  DepositAndWithdraw
}

struct MarketParameters {
  address asset;
  string name;
  string symbol;
  address borrower;
  address controller;
  address feeRecipient;
  address sentinel;
  uint128 maxTotalSupply;
  uint16 protocolFeeBips;
  uint16 annualInterestBips;
  uint16 delinquencyFeeBips;
  uint32 withdrawalBatchDuration;
  uint16 reserveRatioBips;
  uint32 delinquencyGracePeriod;
  address archController;
  address sphereXEngine;
}

struct MarketControllerParameters {
  address archController;
  address borrower;
  address sentinel;
  address marketInitCodeStorage;
  uint256 marketInitCodeHash;
  uint32 minimumDelinquencyGracePeriod;
  uint32 maximumDelinquencyGracePeriod;
  uint16 minimumReserveRatioBips;
  uint16 maximumReserveRatioBips;
  uint16 minimumDelinquencyFeeBips;
  uint16 maximumDelinquencyFeeBips;
  uint32 minimumWithdrawalBatchDuration;
  uint32 maximumWithdrawalBatchDuration;
  uint16 minimumAnnualInterestBips;
  uint16 maximumAnnualInterestBips;
  address sphereXEngine;
}

struct ProtocolFeeConfiguration {
  address feeRecipient;
  address originationFeeAsset;
  uint80 originationFeeAmount;
  uint16 protocolFeeBips;
}

struct MarketParameterConstraints {
  uint32 minimumDelinquencyGracePeriod;
  uint32 maximumDelinquencyGracePeriod;
  uint16 minimumReserveRatioBips;
  uint16 maximumReserveRatioBips;
  uint16 minimumDelinquencyFeeBips;
  uint16 maximumDelinquencyFeeBips;
  uint32 minimumWithdrawalBatchDuration;
  uint32 maximumWithdrawalBatchDuration;
  uint16 minimumAnnualInterestBips;
  uint16 maximumAnnualInterestBips;
}

File 27 of 32 : IERC20.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

interface IERC20 {
  event Transfer(address indexed from, address indexed to, uint256 value);
  event Approval(address indexed owner, address indexed spender, uint256 value);

  function totalSupply() external view returns (uint256);

  function balanceOf(address account) external view returns (uint256);

  function transfer(address recipient, uint256 amount) external returns (bool);

  function allowance(address owner, address spender) external view returns (uint256);

  function approve(address spender, uint256 amount) external returns (bool);

  function increaseAllowance(address spender, uint256 addedValue) external returns (bool);

  function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);

  function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
}

File 28 of 32 : IWildcatMarketControllerEventsAndErrors.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

interface IWildcatMarketControllerEventsAndErrors {
  // ========================================================================== //
  //                                   Errors                                   //
  // ========================================================================== //

  error DelinquencyGracePeriodOutOfBounds();
  error ReserveRatioBipsOutOfBounds();
  error DelinquencyFeeBipsOutOfBounds();
  error WithdrawalBatchDurationOutOfBounds();
  error AnnualInterestBipsOutOfBounds();

  // Error thrown when a borrower-only method is called by another account.
  error CallerNotBorrower();

  // Error thrown when `deployMarket` called by an account other than `borrower` or
  // `controllerFactory`.
  error CallerNotBorrowerOrControllerFactory();

  // Error thrown when `deployMarket` is called for an underlying asset which has
  // been blacklisted by the arch-controller owner.
  error UnderlyingNotPermitted();

  // Error thrown if borrower calls `deployMarket` and is no longer
  // registered with the arch-controller.
  error NotRegisteredBorrower();

  error EmptyString();

  error NotControlledMarket();

  error MarketAlreadyDeployed();

  error ExcessReserveRatioStillActive();

  error CapacityChangeOnClosedMarket();

  error AprChangeOnClosedMarket();

  error AprChangeNotPending();

  error MarketAlreadyClosed();

  error UnknownNameQueryError();

  error UnknownSymbolQueryError();

  // ========================================================================== //
  //                                   Events                                   //
  // ========================================================================== //

  event LenderAuthorized(address);

  event LenderDeauthorized(address);

  event MarketDeployed(
    address indexed market,
    string name,
    string symbol,
    address asset,
    uint256 maxTotalSupply,
    uint256 annualInterestBips,
    uint256 delinquencyFeeBips,
    uint256 withdrawalBatchDuration,
    uint256 reserveRatioBips,
    uint256 delinquencyGracePeriod
  );

  event TemporaryExcessReserveRatioActivated(
    address indexed market,
    uint256 originalReserveRatioBips,
    uint256 temporaryReserveRatioBips,
    uint256 temporaryReserveRatioExpiry
  );

  event TemporaryExcessReserveRatioUpdated(
    address indexed market,
    uint256 originalReserveRatioBips,
    uint256 temporaryReserveRatioBips,
    uint256 temporaryReserveRatioExpiry
  );

  event TemporaryExcessReserveRatioCanceled(address indexed market);

  event TemporaryExcessReserveRatioExpired(address indexed market);
}

File 29 of 32 : MathUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20;

import './Errors.sol';

uint256 constant BIP = 1e4;
uint256 constant HALF_BIP = 0.5e4;

uint256 constant RAY = 1e27;
uint256 constant HALF_RAY = 0.5e27;

uint256 constant BIP_RAY_RATIO = 1e23;

uint256 constant SECONDS_IN_365_DAYS = 365 days;

library MathUtils {
  /// @dev The multiply-divide operation failed, either due to a
  /// multiplication overflow, or a division by a zero.
  error MulDivFailed();

  using MathUtils for uint256;

  /**
   * @dev Function to calculate the interest accumulated using a linear interest rate formula
   *
   * @param rateBip The interest rate, in bips
   * @param timeDelta The time elapsed since the last interest accrual
   * @return result The interest rate linearly accumulated during the timeDelta, in ray
   */
  function calculateLinearInterestFromBips(
    uint256 rateBip,
    uint256 timeDelta
  ) internal pure returns (uint256 result) {
    uint256 rate = rateBip.bipToRay();
    uint256 accumulatedInterestRay = rate * timeDelta;
    unchecked {
      return accumulatedInterestRay / SECONDS_IN_365_DAYS;
    }
  }

  /**
   * @dev Return the smaller of `a` and `b`
   */
  function min(uint256 a, uint256 b) internal pure returns (uint256 c) {
    c = ternary(a < b, a, b);
  }

  /**
   * @dev Return the larger of `a` and `b`.
   */
  function max(uint256 a, uint256 b) internal pure returns (uint256 c) {
    c = ternary(a < b, b, a);
  }

  /**
   * @dev Saturation subtraction. Subtract `b` from `a` and return the result
   *      if it is positive or zero if it underflows.
   */
  function satSub(uint256 a, uint256 b) internal pure returns (uint256 c) {
    assembly {
      // (a > b) * (a - b)
      // If a-b underflows, the product will be zero
      c := mul(gt(a, b), sub(a, b))
    }
  }

  /**
   * @dev Return `valueIfTrue` if `condition` is true and `valueIfFalse` if it is false.
   *      Equivalent to `condition ? valueIfTrue : valueIfFalse`
   */
  function ternary(
    bool condition,
    uint256 valueIfTrue,
    uint256 valueIfFalse
  ) internal pure returns (uint256 c) {
    assembly {
      c := add(valueIfFalse, mul(condition, sub(valueIfTrue, valueIfFalse)))
    }
  }

  /**
   * @dev Multiplies two bip, rounding half up to the nearest bip
   *      see https://twitter.com/transmissions11/status/1451131036377571328
   */
  function bipMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
    assembly {
      // equivalent to `require(b == 0 || a <= (type(uint256).max - HALF_BIP) / b)`
      if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_BIP), b))))) {
        // Store the Panic error signature.
        mstore(0, Panic_ErrorSelector)
        // Store the arithmetic (0x11) panic code.
        mstore(Panic_ErrorCodePointer, Panic_Arithmetic)
        // revert(abi.encodeWithSignature("Panic(uint256)", 0x11))
        revert(Error_SelectorPointer, Panic_ErrorLength)
      }

      c := div(add(mul(a, b), HALF_BIP), BIP)
    }
  }

  /**
   * @dev Divides two bip, rounding half up to the nearest bip
   *      see https://twitter.com/transmissions11/status/1451131036377571328
   */
  function bipDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
    assembly {
      // equivalent to `require(b != 0 && a <= (type(uint256).max - b/2) / BIP)`
      if or(iszero(b), gt(a, div(sub(not(0), div(b, 2)), BIP))) {
        mstore(0, Panic_ErrorSelector)
        mstore(Panic_ErrorCodePointer, Panic_Arithmetic)
        revert(Error_SelectorPointer, Panic_ErrorLength)
      }

      c := div(add(mul(a, BIP), div(b, 2)), b)
    }
  }

  /**
   * @dev Converts bip up to ray
   */
  function bipToRay(uint256 a) internal pure returns (uint256 b) {
    // to avoid overflow, b/BIP_RAY_RATIO == a
    assembly {
      b := mul(a, BIP_RAY_RATIO)
      // equivalent to `require((b = a * BIP_RAY_RATIO) / BIP_RAY_RATIO == a )
      if iszero(eq(div(b, BIP_RAY_RATIO), a)) {
        mstore(0, Panic_ErrorSelector)
        mstore(Panic_ErrorCodePointer, Panic_Arithmetic)
        revert(Error_SelectorPointer, Panic_ErrorLength)
      }
    }
  }

  /**
   * @dev Multiplies two ray, rounding half up to the nearest ray
   *      see https://twitter.com/transmissions11/status/1451131036377571328
   */
  function rayMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
    assembly {
      // equivalent to `require(b == 0 || a <= (type(uint256).max - HALF_RAY) / b)`
      if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_RAY), b))))) {
        mstore(0, Panic_ErrorSelector)
        mstore(Panic_ErrorCodePointer, Panic_Arithmetic)
        revert(Error_SelectorPointer, Panic_ErrorLength)
      }

      c := div(add(mul(a, b), HALF_RAY), RAY)
    }
  }

  /**
   * @dev Divide two ray, rounding half up to the nearest ray
   *      see https://twitter.com/transmissions11/status/1451131036377571328
   */
  function rayDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
    assembly {
      // equivalent to `require(b != 0 && a <= (type(uint256).max - halfB) / RAY)`
      if or(iszero(b), gt(a, div(sub(not(0), div(b, 2)), RAY))) {
        mstore(0, Panic_ErrorSelector)
        mstore(Panic_ErrorCodePointer, Panic_Arithmetic)
        revert(Error_SelectorPointer, Panic_ErrorLength)
      }

      c := div(add(mul(a, RAY), div(b, 2)), b)
    }
  }

  /**
   * @dev Returns `floor(x * y / d)`.
   *      Reverts if `x * y` overflows, or `d` is zero.
   * @custom:author solady/src/utils/FixedPointMathLib.sol
   */
  function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
    assembly {
      // Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y))
      if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) {
        // Store the function selector of `MulDivFailed()`.
        mstore(0x00, 0xad251c27)
        // Revert with (offset, size).
        revert(0x1c, 0x04)
      }
      z := div(mul(x, y), d)
    }
  }

  /**
   * @dev Returns `ceil(x * y / d)`.
   *      Reverts if `x * y` overflows, or `d` is zero.
   * @custom:author solady/src/utils/FixedPointMathLib.sol
   */
  function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
    assembly {
      // Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y))
      if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) {
        // Store the function selector of `MulDivFailed()`.
        mstore(0x00, 0xad251c27)
        // Revert with (offset, size).
        revert(0x1c, 0x04)
      }
      z := add(iszero(iszero(mod(mul(x, y), d))), div(mul(x, y), d))
    }
  }
}

File 30 of 32 : FIFOQueue.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

struct FIFOQueue {
  uint128 startIndex;
  uint128 nextIndex;
  mapping(uint256 => uint32) data;
}

// @todo - make array tightly packed for gas efficiency with multiple reads/writes
//         also make a memory version of the array with (nextIndex, startIndex, storageSlot)
//         so that multiple storage reads aren't required for tx's using multiple functions

using FIFOQueueLib for FIFOQueue global;

library FIFOQueueLib {
  error FIFOQueueOutOfBounds();

  function empty(FIFOQueue storage arr) internal view returns (bool) {
    return arr.nextIndex == arr.startIndex;
  }

  function first(FIFOQueue storage arr) internal view returns (uint32) {
    if (arr.startIndex == arr.nextIndex) {
      revert FIFOQueueOutOfBounds();
    }
    return arr.data[arr.startIndex];
  }

  function at(FIFOQueue storage arr, uint256 index) internal view returns (uint32) {
    index += arr.startIndex;
    if (index >= arr.nextIndex) {
      revert FIFOQueueOutOfBounds();
    }
    return arr.data[index];
  }

  function length(FIFOQueue storage arr) internal view returns (uint128) {
    return arr.nextIndex - arr.startIndex;
  }

  function values(FIFOQueue storage arr) internal view returns (uint32[] memory _values) {
    uint256 startIndex = arr.startIndex;
    uint256 nextIndex = arr.nextIndex;
    uint256 len = nextIndex - startIndex;
    _values = new uint32[](len);

    for (uint256 i = 0; i < len; i++) {
      _values[i] = arr.data[startIndex + i];
    }

    return _values;
  }

  function push(FIFOQueue storage arr, uint32 value) internal {
    uint128 nextIndex = arr.nextIndex;
    arr.data[nextIndex] = value;
    arr.nextIndex = nextIndex + 1;
  }

  function shift(FIFOQueue storage arr) internal {
    uint128 startIndex = arr.startIndex;
    if (startIndex == arr.nextIndex) {
      revert FIFOQueueOutOfBounds();
    }
    delete arr.data[startIndex];
    arr.startIndex = startIndex + 1;
  }

  function shiftN(FIFOQueue storage arr, uint128 n) internal {
    uint128 startIndex = arr.startIndex;
    if (startIndex + n > arr.nextIndex) {
      revert FIFOQueueOutOfBounds();
    }
    for (uint256 i = 0; i < n; i++) {
      delete arr.data[startIndex + i];
    }
    arr.startIndex = startIndex + n;
  }
}

File 31 of 32 : Errors.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

uint256 constant Panic_CompilerPanic = 0x00;
uint256 constant Panic_AssertFalse = 0x01;
uint256 constant Panic_Arithmetic = 0x11;
uint256 constant Panic_DivideByZero = 0x12;
uint256 constant Panic_InvalidEnumValue = 0x21;
uint256 constant Panic_InvalidStorageByteArray = 0x22;
uint256 constant Panic_EmptyArrayPop = 0x31;
uint256 constant Panic_ArrayOutOfBounds = 0x32;
uint256 constant Panic_MemoryTooLarge = 0x41;
uint256 constant Panic_UninitializedFunctionPointer = 0x51;

uint256 constant Panic_ErrorSelector = 0x4e487b71;
uint256 constant Panic_ErrorCodePointer = 0x20;
uint256 constant Panic_ErrorLength = 0x24;
uint256 constant Error_SelectorPointer = 0x1c;

/**
 * @dev Reverts with the given error selector.
 * @param errorSelector The left-aligned error selector.
 */
function revertWithSelector(bytes4 errorSelector) pure {
  assembly {
    mstore(0, errorSelector)
    revert(0, 4)
  }
}

/**
 * @dev Reverts with the given error selector.
 * @param errorSelector The left-padded error selector.
 */
function revertWithSelector(uint256 errorSelector) pure {
  assembly {
    mstore(0, errorSelector)
    revert(Error_SelectorPointer, 4)
  }
}

/**
 * @dev Reverts with the given error selector and argument.
 * @param errorSelector The left-aligned error selector.
 * @param argument The argument to the error.
 */
function revertWithSelectorAndArgument(bytes4 errorSelector, uint256 argument) pure {
  assembly {
    mstore(0, errorSelector)
    mstore(4, argument)
    revert(0, 0x24)
  }
}

/**
 * @dev Reverts with the given error selector and argument.
 * @param errorSelector The left-padded error selector.
 * @param argument The argument to the error.
 */
function revertWithSelectorAndArgument(uint256 errorSelector, uint256 argument) pure {
  assembly {
    mstore(0, errorSelector)
    mstore(0x20, argument)
    revert(Error_SelectorPointer, 0x24)
  }
}

File 32 of 32 : IWildcatSanctionsEscrow.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.20;

interface IWildcatSanctionsEscrow {
  event EscrowReleased(address indexed account, address indexed asset, uint256 amount);

  error CanNotReleaseEscrow();

  function sentinel() external view returns (address);

  function borrower() external view returns (address);

  function account() external view returns (address);

  function balance() external view returns (uint256);

  function canReleaseEscrow() external view returns (bool);

  function escrowedAsset() external view returns (address token, uint256 amount);

  function releaseEscrow() external;
}

Settings
{
  "remappings": [
    "src/=src/",
    "forge-std/=lib/forge-std/src/",
    "ds-test/=lib/ds-test/src/",
    "solmate/=lib/solmate/src/",
    "solady/=lib/solady/src/",
    "openzeppelin/=lib/openzeppelin-contracts/",
    "sol-utils/=lib/sol-utils/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "vulcan/=lib/vulcan/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[],"name":"AccountBlocked","type":"error"},{"inputs":[],"name":"AccountNotBlocked","type":"error"},{"inputs":[],"name":"BadLaunchCode","type":"error"},{"inputs":[],"name":"BorrowAmountTooHigh","type":"error"},{"inputs":[],"name":"BorrowFromClosedMarket","type":"error"},{"inputs":[],"name":"BorrowWhileSanctioned","type":"error"},{"inputs":[],"name":"CloseMarketWithUnpaidWithdrawals","type":"error"},{"inputs":[],"name":"DepositToClosedMarket","type":"error"},{"inputs":[],"name":"FIFOQueueOutOfBounds","type":"error"},{"inputs":[],"name":"InsufficientReservesForFeeWithdrawal","type":"error"},{"inputs":[],"name":"InsufficientReservesForNewLiquidityRatio","type":"error"},{"inputs":[],"name":"InsufficientReservesForOldLiquidityRatio","type":"error"},{"inputs":[],"name":"InvalidArrayLength","type":"error"},{"inputs":[],"name":"MaxSupplyExceeded","type":"error"},{"inputs":[],"name":"NewMaxSupplyTooLow","type":"error"},{"inputs":[],"name":"NoReentrantCalls","type":"error"},{"inputs":[],"name":"NotApprovedBorrower","type":"error"},{"inputs":[],"name":"NotApprovedLender","type":"error"},{"inputs":[],"name":"NotController","type":"error"},{"inputs":[],"name":"NotReversedOrStunning","type":"error"},{"inputs":[],"name":"NullBurnAmount","type":"error"},{"inputs":[],"name":"NullFeeAmount","type":"error"},{"inputs":[],"name":"NullMintAmount","type":"error"},{"inputs":[],"name":"NullRepayAmount","type":"error"},{"inputs":[],"name":"NullTransferAmount","type":"error"},{"inputs":[],"name":"NullWithdrawalAmount","type":"error"},{"inputs":[],"name":"RepayToClosedMarket","type":"error"},{"inputs":[],"name":"SphereXOperatorRequired","type":"error"},{"inputs":[],"name":"WithdrawalBatchNotExpired","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"annualInterestBipsUpdated","type":"uint256"}],"name":"AnnualInterestBipsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"enum AuthRole","name":"role","type":"uint8"}],"name":"AuthorizationStatusUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assetAmount","type":"uint256"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldEngineAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newEngineAddress","type":"address"}],"name":"ChangedSpherexEngineAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldSphereXAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newSphereXAdmin","type":"address"}],"name":"ChangedSpherexOperator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"assetAmount","type":"uint256"}],"name":"DebtRepaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"assetAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"scaledAmount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"FeesCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fromTimestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toTimestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"scaleFactor","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseInterestRay","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delinquencyFeeRay","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFees","type":"uint256"}],"name":"InterestAndFeesAccrued","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"MarketClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"MaxTotalSupplyUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"reserveRatioBipsUpdated","type":"uint256"}],"name":"ReserveRatioBipsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"escrow","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SanctionedAccountAssetsSentToEscrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"escrow","type":"address"},{"indexed":false,"internalType":"uint32","name":"expiry","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SanctionedAccountWithdrawalSentToEscrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"scaleFactor","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isDelinquent","type":"bool"}],"name":"StateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"WithdrawalBatchClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"WithdrawalBatchCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"expiry","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"scaledTotalAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"scaledAmountBurned","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"normalizedAmountPaid","type":"uint256"}],"name":"WithdrawalBatchExpired","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"expiry","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"scaledAmountBurned","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"normalizedAmountPaid","type":"uint256"}],"name":"WithdrawalBatchPayment","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"expiry","type":"uint256"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"normalizedAmount","type":"uint256"}],"name":"WithdrawalExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"expiry","type":"uint256"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"scaledAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"normalizedAmount","type":"uint256"}],"name":"WithdrawalQueued","type":"event"},{"inputs":[],"name":"accruedProtocolFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"annualInterestBips","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"archController","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowableAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrower","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newSphereXEngine","type":"address"}],"name":"changeSphereXEngine","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"closeMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collectFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"controller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"coverageLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentState","outputs":[{"components":[{"internalType":"bool","name":"isClosed","type":"bool"},{"internalType":"uint128","name":"maxTotalSupply","type":"uint128"},{"internalType":"uint128","name":"accruedProtocolFees","type":"uint128"},{"internalType":"uint128","name":"normalizedUnclaimedWithdrawals","type":"uint128"},{"internalType":"uint104","name":"scaledTotalSupply","type":"uint104"},{"internalType":"uint104","name":"scaledPendingWithdrawals","type":"uint104"},{"internalType":"uint32","name":"pendingWithdrawalExpiry","type":"uint32"},{"internalType":"bool","name":"isDelinquent","type":"bool"},{"internalType":"uint32","name":"timeDelinquent","type":"uint32"},{"internalType":"uint16","name":"annualInterestBips","type":"uint16"},{"internalType":"uint16","name":"reserveRatioBips","type":"uint16"},{"internalType":"uint112","name":"scaleFactor","type":"uint112"},{"internalType":"uint32","name":"lastInterestAccruedTimestamp","type":"uint32"}],"internalType":"struct MarketState","name":"state","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"delinquencyFeeBips","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"delinquencyGracePeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"delinquentDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositUpTo","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"accountAddress","type":"address"},{"internalType":"uint32","name":"expiry","type":"uint32"}],"name":"executeWithdrawal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"accountAddresses","type":"address[]"},{"internalType":"uint32[]","name":"expiries","type":"uint32[]"}],"name":"executeWithdrawals","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAccountRole","outputs":[{"internalType":"enum AuthRole","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"accountAddress","type":"address"},{"internalType":"uint32","name":"expiry","type":"uint32"}],"name":"getAccountWithdrawalStatus","outputs":[{"components":[{"internalType":"uint104","name":"scaledAmount","type":"uint104"},{"internalType":"uint128","name":"normalizedAmountWithdrawn","type":"uint128"}],"internalType":"struct AccountWithdrawalStatus","name":"status","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"accountAddress","type":"address"},{"internalType":"uint32","name":"expiry","type":"uint32"}],"name":"getAvailableWithdrawalAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUnpaidBatchExpiries","outputs":[{"internalType":"uint32[]","name":"","type":"uint32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"expiry","type":"uint32"}],"name":"getWithdrawalBatch","outputs":[{"components":[{"internalType":"uint104","name":"scaledTotalAmount","type":"uint104"},{"internalType":"uint104","name":"scaledAmountBurned","type":"uint104"},{"internalType":"uint128","name":"normalizedAmountPaid","type":"uint128"}],"internalType":"struct WithdrawalBatch","name":"batch","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isClosed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maximumDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"accountAddress","type":"address"}],"name":"nukeFromOrbit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"outstandingDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"previousState","outputs":[{"components":[{"internalType":"bool","name":"isClosed","type":"bool"},{"internalType":"uint128","name":"maxTotalSupply","type":"uint128"},{"internalType":"uint128","name":"accruedProtocolFees","type":"uint128"},{"internalType":"uint128","name":"normalizedUnclaimedWithdrawals","type":"uint128"},{"internalType":"uint104","name":"scaledTotalSupply","type":"uint104"},{"internalType":"uint104","name":"scaledPendingWithdrawals","type":"uint104"},{"internalType":"uint32","name":"pendingWithdrawalExpiry","type":"uint32"},{"internalType":"bool","name":"isDelinquent","type":"bool"},{"internalType":"uint32","name":"timeDelinquent","type":"uint32"},{"internalType":"uint16","name":"annualInterestBips","type":"uint16"},{"internalType":"uint16","name":"reserveRatioBips","type":"uint16"},{"internalType":"uint112","name":"scaleFactor","type":"uint112"},{"internalType":"uint32","name":"lastInterestAccruedTimestamp","type":"uint32"}],"internalType":"struct MarketState","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFeeBips","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"queueWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"repay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"uint256","name":"maxBatches","type":"uint256"}],"name":"repayAndProcessUnpaidWithdrawalBatches","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"repayDelinquentDebt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"repayOutstandingDebt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reserveRatioBips","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"scaleFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"scaledBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"scaledTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sentinel","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"_annualInterestBips","type":"uint16"}],"name":"setAnnualInterestBips","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxTotalSupply","type":"uint256"}],"name":"setMaxTotalSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_reserveRatioBips","type":"uint16"}],"name":"setReserveRatioBips","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sphereXEngine","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sphereXOperator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"accountAddress","type":"address"}],"name":"stunningReversal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"_totalAssets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDebts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"bool","name":"authorize","type":"bool"}],"name":"updateAccountAuthorizations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawableProtocolFees","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawalBatchDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

Deployed Bytecode

0x60806040526004361015610011575f80fd5b5f3560e01c806301e1d1141461041f57806302372c4f1461041a57806306f94a0d1461041557806306fdde0314610410578063088fee5e1461040b578063095ea7b3146104065780630c1e3fea146104015780630c3f6acf146103fc5780630cd1a5b6146103f757806311057cd0146103f257806314a1c32d146103ed57806318160ddd146103e857806319521311146103e35780631d8557d7146103de5780631da24f3e146103d95780631e3cef53146103d457806323b872dd146103cf5780632ab4d052146103ca578063313ce567146103c557806334bca29c146103c0578063371fd8e6146103bb57806338d52e0f146103b65780633c231166146103b15780633c5f015b146103ac5780633f3e4c11146103a757806346904840146103a25780634be687c61461039d5780634c6c848f14610398578063514a4cd61461039357806354635570146102ee57806354b302c51461038e57806354fd4d50146103895780635c559e14146103845780636731ba6d1461037f578063683dd1911461037a5780636b174f35146103755780636dd4f5211461037057806370a082311461036b578063739ccdd3146103665780637405885914610361578063766360171461035c5780637df1f1b91461035757806381d66b2314610352578063878eb9211461034d578063922c01041461034857806395d89b41146103435780639e6f98021461033e578063a9059cbb14610339578063abadbf1614610334578063b1bf962d1461032f578063b67afb331461032a578063b68ce7a214610325578063b6b55f2514610320578063c2b6b58c1461031b578063c511ed5e14610316578063c5ebeaec14610311578063c87965721461030c578063d3d356fb14610307578063d98e0fe814610302578063dbcd50b4146102fd578063dcd549d4146102f8578063dd62ed3e146102f3578063e7e5db4f146102ee578063f58c251c146102e95763f77c4791146102e4575f80fd5b6125c7565b612583565b611236565b612524565b612420565b6123b9565b612395565b612370565b61228c565b61212a565b611fb0565b611f78565b611f37565b611f08565b611eca565b611e9e565b611e64565b611e22565b611de8565b611d27565b611c8a565b611ba6565b611b16565b611ab4565b611a7f565b61195c565b6118c4565b611866565b611786565b611525565b6114f8565b6113c8565b611328565b6112d0565b61127a565b6111fc565b611150565b611116565b6110d2565b611012565b610e88565b610e41565b610dfd565b610d45565b610cf3565b610cb6565b610c8d565b610bcc565b610b8e565b610b3f565b610b0d565b610ab0565b610a75565b610a50565b610a2d565b6109f4565b6109ba565b61098a565b610850565b61070a565b610605565b610508565b61048c565b34610441575f36600319011261044157602061043961260b565b604051908152f35b5f80fd5b6001600160a01b0381160361044157565b63ffffffff81160361044157565b60409060031901126104415760043561047c81610445565b9060243561048981610456565b90565b346104415760406104d860206104a136610464565b929063ffffffff6104b0612657565b946104b9612bd1565b165f52600b8352845f209060018060a01b03165f5260205260405f2090565b546001600160681b038116928381526001600160801b03928391019160681c168152835192835251166020820152f35b34610441575f36600319011261044157610520612bd1565b602061053461052d612c30565b5050612bee565b61053c61260b565b90604051918082039111028152f35b634e487b7160e01b5f52604160045260245ffd5b6040810190811067ffffffffffffffff82111761057b57604052565b61054b565b6060810190811067ffffffffffffffff82111761057b57604052565b90601f8019910116810190811067ffffffffffffffff82111761057b57604052565b602080825282518183018190529093925f5b8281106105f157505060409293505f838284010152601f8019910116010190565b8181018601518482016040015285016105d0565b34610441575f366003190112610441576040515f60018054908160011c9160018116918215610700575b6020916020851084146106ec5784875260208701939081156106cd5750600114610674575b610670866106648188038261059c565b604051918291826105be565b0390f35b60015f90815294509192917fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8386106106bc5750505091019050610664826106705f610654565b8054858701529482019481016106a1565b60ff1916845250505090151560051b019050610664826106705f610654565b634e487b7160e01b5f52602260045260245ffd5b92607f169261062f565b346104415761071836610464565b90610721612bd1565b63ffffffff9182811691428310156108435761079261078d6107f592610670966107fa9661074d612c30565b93915061075861266f565b50160361080a57610778909563ffffffff165f52600b60205260405f2090565b9060018060a01b03165f5260205260405f2090565b6126c9565b916107b66107aa60208501516001600160801b031690565b6001600160801b031690565b926107e26107d46107d46107aa60408601516001600160801b031690565b92516001600160681b031690565b6001600160681b03809116921690612e8f565b61270b565b6040519081529081906020820190565b5061077861082e6108298763ffffffff165f52600a60205260405f2090565b61268d565b9563ffffffff165f52600b60205260405f2090565b632561b8805f526004601cfd5b346104415760403660031901126104415761089760043561087081610445565b610878612bd1565b60025f55610892610887612eb1565b916024359033612ef5565b6147b0565b60015f55602060405160018152f35b8151151581526101a08101929161098891906020818101516001600160801b0316908301526040818101516001600160801b0316908301526060818101516001600160801b0316908301526080818101516001600160681b03169083015260a0818101516001600160681b03169083015260c08181015163ffffffff169083015260e0818101511515908301526101008181015163ffffffff16908301526101208181015161ffff16908301526101408181015161ffff1690830152610160818101516001600160701b0316908301526101809081015163ffffffff16910152565b565b34610441575f366003190112610441576109a2612718565b506106706109ae612789565b604051918291826108a6565b34610441575f366003190112610441576109d2612718565b506109db612bd1565b6106706109e6612c30565b5050604051918291826108a6565b34610441575f36600319011261044157610a0c612bd1565b60206001600160801b036040610a20612c30565b5050015116604051908152f35b34610441575f36600319011261044157602060065461ffff60405191831c168152f35b34610441575f36600319011261044157610a68612bd1565b602061043961052d612c30565b34610441575f36600319011261044157610a8d612bd1565b6020610439610a9a612c30565b50506001600160681b0360808201511690613ed0565b34610441575f36600319011261044157610ac8612bd1565b60025f55610b07610ad7612eb1565b610892610ae2612f49565b610b02610aee82612bee565b610af661260b565b808203911102826131bd565b61321e565b60015f55005b34610441575f36600319011261044157610b25612bd1565b60025f55610b07610b34612eb1565b610892610b02612f49565b3461044157602036600319011261044157600435610b5c81610445565b610b64612bd1565b60018060a01b03165f52600760205260206001600160681b0360405f205460081c16604051908152f35b34610441575f366003190112610441576020610bbb610bab612c30565b5050610bb561260b565b906134fa565b6001600160801b0360405191168152f35b3461044157606036600319011261044157600435610be981610445565b602435610bf581610445565b604435610c00612bd1565b60025f55610c0c612eb1565b6001600160a01b0384165f908152600c6020908152604080832033845290915290209092905460018101610c61575b509061089291610c4a94613528565b60015f556040518061067081906001602083019252565b9190818303928311610c8857610c4a94610c7f610892943383612ef5565b94509091610c3b565b6126f7565b34610441575f3660031901126104415760206001600160801b0360035460081c16604051908152f35b34610441575f36600319011261044157602060405160ff7f0000000000000000000000000000000000000000000000000000000000000006168152f35b34610441576020610d39610d0636610464565b610d11929192612bd1565b60025f55610892610d33610d23612eb1565b92610d2c612f49565b958661361f565b9361321e565b60015f55604051908152f35b3461044157602036600319011261044157600435610d61612bd1565b60025f55610d6d612eb1565b908015610df057610da08130337f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486138fe565b5f52337fe8b606ac1e5df7657db58d297ca8f41c090fc94c5fd2d6958f043e41736e9fa660205fa2610dd0612f49565b908151610de357610892610b079261321e565b6361d1bc8f5f526004601cfd5b637e0820885f526004601cfd5b34610441575f366003190112610441576040517f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b03168152602090f35b34610441575f366003190112610441577f1777adabd324f814e2b0e28f6edf876dce01d7d66358c9acfe87b1b5f38338d5546040516001600160a01b039091168152602090f35b3461044157602036600319011261044157600435610ea581610445565b610ead612bd1565b60025f55610eb9612eb1565b6040516301b9d11160e21b81526001600160a01b037f000000000000000000000000b00f9d7c25a044117d29f4b34521528076916a178116600483015283166024820152602081806044810103817f000000000000000000000000437e0551892c2c9b06d3ffd248fe60572e08cd1a6001600160a01b03165afa90811561100d575f91610fde575b50610fd9576001600160a01b0382165f908152600760205260409020610f6690612898565b9160018351610f7481611af8565b610f7d81611af8565b03610fd45782610fc461089292610f97610fc99660029052565b610fab8351610fa581611af8565b826139e4565b6001600160a01b03165f90815260076020526040902090565b6128c8565b610fd260015f55565b005b613981565b613974565b611000915060203d602011611006575b610ff8818361059c565b81019061286c565b5f610f41565b503d610fee565b612881565b34610441576020366003190112610441576004357f000000000000000000000000d22cc5d80529401cd3eedea4a6e8958c6da49cb86001600160a01b031633036110c557610b0790611062612bd1565b60025f5561106e612eb1565b9061109961107a612f49565b6001600160801b03831661108f818514614a44565b602082015261321e565b5f527ff2672935fc79f5237559e2e2999dbe743bf65430894ac2b37666890e7c69e1af60205fa16147b0565b6323019e675f526004601cfd5b34610441575f366003190112610441576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b34610441575f3660031901126104415760206040517f00000000000000000000000000000000000000000000000000000000000003e88152f35b346104415760203660031901126104415760043561116d81610445565b7f000000000000000000000000feb516d9d946dd487a9346f6fee11f40c6945ee46001600160a01b031633036111ef577f1777adabd324f814e2b0e28f6edf876dce01d7d66358c9acfe87b1b5f38338d581815491555f526020527ff33499cccaa0611882086224cc48cd82ef54b66a4d2edf4ed67108dd516896d560405fa1005b634ee0b8f85f526004601cfd5b34610441575f3660031901126104415760206040517f00000000000000000000000000000000000000000000000000000000000151808152f35b34610441575f366003190112610441576040517f000000000000000000000000feb516d9d946dd487a9346f6fee11f40c6945ee46001600160a01b03168152602090f35b34610441575f36600319011261044157611292612718565b5061129b612bd1565b60206104396112a8612c30565b5050613a24565b604051906101a0820182811067ffffffffffffffff82111761057b57604052565b34610441575f366003190112610441576106706040516112ef8161055f565b60038152620312e360ec1b6020820152604051918291826105be565b60209060031901126104415760043561ffff811681036104415790565b34610441576113363661130b565b7f000000000000000000000000d22cc5d80529401cd3eedea4a6e8958c6da49cb86001600160a01b031633036110c557610b0790611372612bd1565b60025f5561137e612eb1565b9061139c61ffff61138d612f49565b9216918261012082015261321e565b5f527fff7b6c8be373823323d3c5d99f5d027dd409dce5db54eae511bbdd5546b7503760205fa16147b0565b34610441576040366003190112610441576004356113e4612bd1565b60025f556113f0612eb1565b90806114bc575b50611400612f49565b9061140b8251151590565b6114b75761144e61141a61260b565b6114486107aa61143460608701516001600160801b031690565b60408701516001600160801b03169061292b565b9061270b565b9061146b61145d6107aa613a4a565b602435818082039110020190565b5f925b8161147885612946565b9410806114ae575b156114a1578061149361149c9287613a63565b80820391110290565b61146e565b610fc9836108928761321e565b50801515611480565b610de3565b806114ec6114f29230337f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486138fe565b3361394a565b5f6113f7565b34610441575f36600319011261044157611510612bd1565b60206001600160701b03610160610a20612c30565b3461044157602036600319011261044157600435611541612bd1565b60025f5561154d612eb1565b611555612f49565b9161158561158061157a6001600160701b03610160870151166001600160701b031690565b83614a83565b613b51565b926001600160681b03841691821561178157610fc99461170c610b0292610892956115f36115b233614d5c565b6115e0602082016115d3886115ce83516001600160681b031690565b612954565b6001600160681b03169052565b335f9081526007602052604090206128c8565b6115fe823033613ba4565b60c0860191611611835163ffffffff1690565b9163ffffffff80841615611723575b8394506116d66116446108296116df9663ffffffff165f52600a60205260405f2090565b97611698611664336107788a63ffffffff165f52600b60205260405f2090565b61167e8361167983546001600160681b031690565b61297a565b6001600160681b03166001600160681b0319825416179055565b6116bd6116b0826116798c516001600160681b031690565b6001600160681b03168a52565b6115d360a08c019161167983516001600160681b031690565b33908516613be2565b6116f16116ea61260b565b8685613c12565b80611711575b5063ffffffff165f52600a60205260405f2090565b612995565b61171d90828786613c8b565b506116f7565b6116df93506117796117616117587f0000000000000000000000000000000000000000000000000000000000000e104261296d565b63ffffffff1690565b809661176e848316613bbc565b9063ffffffff169052565b849350611620565b613b67565b34610441576117943661130b565b7f000000000000000000000000d22cc5d80529401cd3eedea4a6e8958c6da49cb86001600160a01b031633036110c5576117cc612bd1565b60025f556117d8612eb1565b6117e0612f49565b61014081019261ffff808551169116809482821061183a575b528311611816575b610fc9926118116108929261321e565b613ea7565b61181f81613e19565b61182761260b565b10156118015763253ecbb95f526004601cfd5b905061184583613e19565b61184d61260b565b106118595784906117f9565b630a68e5bf5f526004601cfd5b3461044157602036600319011261044157602061043960043561188881610445565b611890612bd1565b611898612c30565b50506001600160a01b039091165f90815260078452604090205460081c6001600160681b031690613ed0565b34610441575f366003190112610441576118dc612bd1565b60206104396118e9612c30565b5050613e19565b9181601f840112156104415782359167ffffffffffffffff8311610441576020808501948460051b01011161044157565b60209060206040818301928281528551809452019301915f5b828110611948575050505090565b83518552938101939281019260010161193a565b346104415760403660031901126104415767ffffffffffffffff6004358181116104415761198e9036906004016118f0565b9091602435908111610441576119a89036906004016118f0565b9190926119b3612bd1565b60025f556119bf612eb1565b93838303611a72576119d083611c5b565b936119de604051958661059c565b838552601f196119ed85611c5b565b013660208701376119fc612f49565b935f5b818110611a2c5761067087611a178a6108928a61321e565b611a2060015f55565b60405191829182611921565b80611a61611a3d6001938589612a1f565b35611a4781610445565b611a5a611a5584888a612a1f565b612a34565b908961361f565b611a6b828a612a3e565b52016119ff565b639d89020a5f526004601cfd5b34610441575f36600319011261044157611a97612bd1565b6020611aa1612c30565b505061053c611aae61260b565b91613e19565b34610441575f366003190112610441576040517f000000000000000000000000b00f9d7c25a044117d29f4b34521528076916a176001600160a01b03168152602090f35b60041115611b0257565b634e487b7160e01b5f52602160045260245ffd5b3461044157602036600319011261044157600435611b3381610445565b611b3b612bd1565b60018060a01b03165f52600760205260ff60405f2054166040516004821015611b02576020918152f35b60209060206040818301928281528551809452019301915f5b828110611b8c575050505090565b835163ffffffff1685529381019392810192600101611b7e565b34610441575f36600319011261044157611bbe612bd1565b6008546001600160801b0381169060801c818103908111610c8857611be281611c5b565b91611bf0604051938461059c565b818352601f19611bff83611c5b565b013660208501375f5b828110611c1d57604051806106708682611b65565b80611c55611c4b611c41611c336001958761296d565b5f52600960205260405f2090565b5463ffffffff1690565b61176e8388612a3e565b01611c08565b67ffffffffffffffff811161057b5760051b60200190565b8015150361044157565b6024359061098882611c73565b346104415760403660031901126104415760043567ffffffffffffffff8111610441573660238201121561044157806004013590611cc782611c5b565b90611cd5604051928361059c565b8282526020926024602084019160051b8301019136831161044157602401905b828210611d0e57610fd284611d08611c7d565b90612a52565b8480918335611d1c81610445565b815201910190611cf5565b34610441575f366003190112610441576040515f60025460018160011c9160018116918215611dde575b6020916020851084146106ec5784875260208701939081156106cd5750600114611d8557610670866106648188038261059c565b60025f90815294509192917f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace5b838610611dcd5750505091019050610664826106705f610654565b805485870152948201948101611db2565b92607f1692611d51565b34610441575f3660031901126104415760206040517f0000000000000000000000000000000000000000000000000000000000000e108152f35b3461044157604036600319011261044157610897600435611e4281610445565b611e4a612bd1565b60025f55610892611e59612eb1565b916024359033613528565b34610441575f3660031901126104415760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b34610441575f36600319011261044157611eb6612bd1565b60206001600160681b036080610a20612c30565b34610441575f36600319011261044157611ee2612bd1565b60025f55610b07611ef1612eb1565b610892611efc612f49565b610b02610aee82613e19565b34610441576020366003190112610441576020611f23612eb1565b610439611f31600435613f66565b916147b0565b3461044157602036600319011261044157600435611f53612eb1565b90611f5d81613f66565b03611f6b57610fd2906147b0565b638a164f635f526004601cfd5b34610441575f36600319011261044157611f90612718565b50611f99612bd1565b6020611fa3612c30565b5050511515604051908152f35b34610441575f366003190112610441577f000000000000000000000000d22cc5d80529401cd3eedea4a6e8958c6da49cb86001600160a01b031633036110c557611ff8612bd1565b60025f55612004612eb1565b6001600160801b03612014613a4a565b1661212557610fc9906120b2612028612f49565b5f610120820152600181526127106101408201525f61010082015261204b61260b565b61205482612bee565b90818110156120bb5761206a90610b029261270b565b307f000000000000000000000000b00f9d7c25a044117d29f4b34521528076916a177f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486138fe565b61089242614189565b8181116120ca575b505061321e565b61211e916120d79161270b565b7f000000000000000000000000b00f9d7c25a044117d29f4b34521528076916a177f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4861414b565b5f806120c3565b61413e565b34610441576020366003190112610441576004356001600160a01b037f000000000000000000000000b00f9d7c25a044117d29f4b34521528076916a17818116330361227f5760206121b89161217e612bd1565b60025f5561218a612eb1565b6040516395c0983960e01b81526001600160a01b039092166004830152939092839190829081906024820190565b03917f000000000000000000000000437e0551892c2c9b06d3ffd248fe60572e08cd1a165afa90811561100d575f91612260575b5061225b576121f9612f49565b80516122565761221061220a61260b565b82613ee9565b8311612251578261224c61089292610b02610fc996337f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4861414b565b6141d9565b6141cc565b6141bf565b6141b2565b612279915060203d60201161100657610ff8818361059c565b5f6121ec565b6302171e6a5f526004601cfd5b34610441575f366003190112610441576122a4612bd1565b60025f556122b0612eb1565b6122b8612f49565b9060408201916001600160801b03908184511615612363576122e16122db61260b565b826134fa565b8281169182156123565785846123036123519461089297610fc99a5116612b59565b169052610b02837f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4861414b565b614202565b63f784cfa45f526004601cfd5b6345c835cb5f526004601cfd5b34610441575f36600319011261044157612388612bd1565b60206105346118e9612c30565b34610441575f36600319011261044157602061ffff60065460301c16604051908152f35b346104415760203660031901126104415760606123f06004356123db81610456565b6123e361266f565b6123eb612bd1565b612b72565b6001600160801b0360408051926001600160681b0380825116855260208201511660208501520151166040820152f35b346104415760203660031901126104415760043561243d81610445565b612445612bd1565b60025f55612451612eb1565b6040516301b9d11160e21b81526001600160a01b037f000000000000000000000000b00f9d7c25a044117d29f4b34521528076916a178116600483015283166024820152602081806044810103817f000000000000000000000000437e0551892c2c9b06d3ffd248fe60572e08cd1a6001600160a01b03165afa90811561100d575f91612505575b50156124f857610892610fc992610b026124f1612f49565b918261422b565b63a97ab1675f526004601cfd5b61251e915060203d60201161100657610ff8818361059c565b5f6124d9565b3461044157604036600319011261044157602061257a60043561254681610445565b6024359061255382610445565b60018060a01b03165f52600c835260405f209060018060a01b03165f5260205260405f2090565b54604051908152f35b34610441575f366003190112610441576040517f000000000000000000000000437e0551892c2c9b06d3ffd248fe60572e08cd1a6001600160a01b03168152602090f35b34610441575f366003190112610441576040517f000000000000000000000000d22cc5d80529401cd3eedea4a6e8958c6da49cb86001600160a01b03168152602090f35b6370a082315f523060205260205f6024601c7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485afa60203d14161561264f575f5190565b3d5f803e3d5ffd5b604051906126648261055f565b5f6020838281520152565b6040519061267c82610580565b5f6040838281528260208201520152565b9060405161269a81610580565b60406001600160801b036001839580546001600160681b0390818116875260681c166020860152015416910152565b906040516126d68161055f565b91546001600160681b038116835260681c6001600160801b03166020830152565b634e487b7160e01b5f52601160045260245ffd5b91908203918211610c8857565b604051906101a0820182811067ffffffffffffffff82111761057b576040525f610180838281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e0820152826101008201528261012082015282610140820152826101608201520152565b6127916112af565b906127cf6003546127a760ff8216859015159052565b60081c6001600160801b039081166020850152600454908116604085015260801c6060840152565b6005546001600160681b038082166080850152606882901c1660a084015263ffffffff60d082901c811660c0850152610988916128169060ff9060f01c16151560e0860152565b60065463ffffffff8282161661010086015261ffff602082901c8116610120870152603082901c16610140860152604081901c6001600160701b031661016086015260b01c1661018084019063ffffffff169052565b90816020910312610441575161048981611c73565b6040513d5f823e3d90fd5b6004821015611b025752565b906040516128a58161055f565b60206001600160681b038294546128bf60ff82168561288c565b60081c16910152565b8151916004831015611b025760206001600160681b03916109889460ff80198654169116178455015116610100600160701b0382549160081b1690610100600160701b031916179055565b9060016001600160801b0380931601918211610c8857565b9190916001600160801b0380809416911601918211610c8857565b5f198114610c885760010190565b6001600160681b039182169082160391908211610c8857565b91908201809211610c8857565b9190916001600160681b0380809416911601918211610c8857565b604060016001600160801b03926129cc6001600160681b0386511682906001600160681b03166001600160681b0319825416179055565b60208501518154906001600160681b0360681b9060681b16906001600160681b0360681b191617815501920151166001600160801b0319825416179055565b634e487b7160e01b5f52603260045260245ffd5b9190811015612a2f5760051b0190565b612a0b565b3561048981610456565b8051821015612a2f5760209160051b010190565b9091906001600160a01b037f000000000000000000000000d22cc5d80529401cd3eedea4a6e8958c6da49cb8811633036110c557612a8e612bd1565b60025f55612a9a612eb1565b92612aa3612f49565b935f5b8451811015612b3e5780612b14612aca86612ac36001958a612a3e565b5116613ef2565b8915612b1a57600381525b612af681610fc4610fab612ae9878d612a3e565b516001600160a01b031690565b612b03612ae9848a612a3e565b905190612b0f82611af8565b6139e4565b01612aa6565b60038151612b2781611af8565b612b3081611af8565b03612ad55760028152612ad5565b509250935050610892612b509261321e565b61098860015f55565b6001600160801b039182169082160391908211610c8857565b9190612b7c612c30565b915063ffffffff809316928315159116831416612bcc57505f52600a6020526001600160801b03600160405f2080546001600160681b0390818116875260681c1660208601520154166040830152565b925050565b60015f5403612bdc57565b604051637fa8a98760e01b8152600490fd5b612c056001600160681b0360808301511682613ed0565b906001600160801b0390816060820151168301809311610c885760400151168101809111610c885790565b612c38612718565b505f612c4261266f565b612c4a612789565b92612c648460c063ffffffff910151168042119015151690565b612d93575b61018084015163ffffffff904290821603612d1e575b60c085015163ffffffff16908116612c945750565b92509050612cb36108298363ffffffff165f52600a60205260405f2090565b90612cc860208301516001600160681b031690565b6001600160681b03612cf0612ce485516001600160681b031690565b6001600160681b031690565b911610612cf957565b612d0b612d0461260b565b8584613c12565b80612d135750565b610988908584614578565b612d8b427f00000000000000000000000000000000000000000000000000000000000151807f00000000000000000000000000000000000000000000000000000000000003e87f00000000000000000000000000000000000000000000000000000000000000008961444c565b505050612c7f565b91505060c08201612da8815163ffffffff1690565b91612dbe61175861018086015163ffffffff1690565b63ffffffff8416908103612e1b575b505f612dea6108298563ffffffff165f52600a60205260405f2090565b92612dfd612df661260b565b8786613c12565b80612e0a575b5052612c69565b612e15908786614578565b5f612e03565b612e88907f00000000000000000000000000000000000000000000000000000000000151807f00000000000000000000000000000000000000000000000000000000000003e87f00000000000000000000000000000000000000000000000000000000000000008861444c565b5050612dcd565b815f19048111820215830215612ea457020490565b63ad251c275f526004601cfd5b604051608081019080821067ffffffffffffffff83111761057b576104899160405260608152606060208201525f60408201525f60608201525f3560e01c906146d9565b6001600160a01b038082165f908152600c60209081526040808320938616835292905220919290918190555f527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560205fa3565b612f51612718565b50612f5a612789565b90612f748260c063ffffffff910151168042119015151690565b6130e3575b61018082015163ffffffff9081164281900361303c575b5060c083015163ffffffff16908116612fa65750565b612fc16108298263ffffffff165f52600a60205260405f2090565b60208101516001600160681b03166001600160681b03612feb612ce484516001600160681b031690565b911610612ff7575b5050565b61300961300261260b565b8583613c12565b8061301357505050565b8261302661170c92610988958886613c8b565b505063ffffffff165f52600a60205260405f2090565b6130dd906130ad427f00000000000000000000000000000000000000000000000000000000000151807f00000000000000000000000000000000000000000000000000000000000003e87f00000000000000000000000000000000000000000000000000000000000000008961444c565b926130d56130c96101608a95949501516001600160701b031690565b6001600160701b031690565b904290614813565b5f612f90565b6130f761175860c084015163ffffffff1690565b61310c61175861018085015163ffffffff1690565b808203613124575b505061311f8261485d565b612f79565b816131956131b6937f00000000000000000000000000000000000000000000000000000000000151807f00000000000000000000000000000000000000000000000000000000000003e87f00000000000000000000000000000000000000000000000000000000000000008961444c565b9390926131b06130c96101608a01516001600160701b031690565b91614813565b5f80613114565b8115610df05751610de3576131f48130337f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486138fe565b5f52337fe8b606ac1e5df7657db58d297ca8f41c090fc94c5fd2d6958f043e41736e9fa660205fa2565b610988906001600160701b0361323382613e19565b61323b61260b565b10918260e08201526132626132508251151590565b60ff8019600354169115151617600355565b61329c61327960208301516001600160801b031690565b610100600160881b036003549160081b1690610100600160881b03191617600355565b6132cf6132b360408301516001600160801b031690565b6001600160801b03166001600160801b03196004541617600455565b6133016132e660608301516001600160801b031690565b6001600160801b036004549181199060801b16911617600455565b61333461331860808301516001600160681b031690565b6001600160681b03166001600160681b03196005541617600555565b61338261334b60a08301516001600160681b031690565b600580546cffffffffffffffffffffffffff60681b191660689290921b6cffffffffffffffffffffffffff60681b16919091179055565b6133bb61339660c083015163ffffffff1690565b6005805463ffffffff60d01b191660d09290921b63ffffffff60d01b16919091179055565b6005805460ff60f01b191684151560f01b60ff60f01b161790556134006133ea61010083015163ffffffff1690565b63ffffffff1663ffffffff196006541617600655565b61343261341361012083015161ffff1690565b65ffff000000006006549160201b169065ffff00000000191617600655565b61346861344561014083015161ffff1690565b67ffff0000000000006006549160301b169067ffff000000000000191617600655565b6134f46134cf6101806134866101608501516001600160701b031690565b6006805475ffffffffffffffffffffffffffff00000000000000001916604083901b75ffffffffffffffffffffffffffff00000000000000001617905593015163ffffffff1690565b6006805463ffffffff60b01b191660b09290921b63ffffffff60b01b16919091179055565b166149a6565b906001600160801b0391826060820151168203918211610c8857604083910151169081808203911002011690565b9091613532612f49565b61354a6001600160701b036101608301511683614a83565b906001600160681b0391613562838216809214614a44565b80156135fd576135e792610b02916135ad61357c88613ef2565b602081018461358e8582845116612954565b1690526001600160a01b0389165f9081526007602052604090206128c8565b6135b688613ef2565b916135c860208401928284511661297a565b1690526001600160a01b0387165f9081526007602052604090206128c8565b5f525f80516020614f0783398151915260205fa3565b63ddee9b305f526004601cfd5b90816020910312610441575161048981610445565b909163ffffffff811692428410156138f95761364c6108298363ffffffff165f52600a60205260405f2090565b93613669826107788563ffffffff165f52600b60205260405f2090565b946136a56136c861368460408401516001600160801b031690565b6136bd6107aa8a54956001600160681b03958691516001600160681b031690565b936001600160801b0380978196169289169116612e8f565b9360681c1683612b59565b9081169687156138f45780546fffffffffffffffffffffffffffffffff60681b191660689390931b6fffffffffffffffffffffffffffffffff60681b169290921790915561373b9061372e606087019161372983516001600160801b031690565b612b59565b6001600160801b03169052565b6040516301b9d11160e21b81526001600160a01b037f000000000000000000000000b00f9d7c25a044117d29f4b34521528076916a1781811660048401528482166024840152956020927f000000000000000000000000437e0551892c2c9b06d3ffd248fe60572e08cd1a909216918381604481865afa801561100d5789915f916138d7575b50156138a257505f96613838926137d987869461422b565b60405163a1054f6b60e01b81526001600160a01b03928316600482015282881660248201527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb489283166044820152919891938492839182906064820190565b03925af194851561100d5761048996889687809461386a965f9361386f575b505081613864929361414b565b856149df565b614a19565b6138649350908161389492903d1061389b575b61388c818361059c565b81019061360a565b915f613857565b503d613882565b9550505050610489935061386a83837f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4861414b565b6138ee9150853d871161100657610ff8818361059c565b5f6137c1565b6149d2565b610843565b601c5f60649281946020966040519860605260405260601b602c526323b872dd60601b600c525af13d1560015f511417161561393d575f606052604052565b637939f4245f526004601cfd5b905f527fe8b606ac1e5df7657db58d297ca8f41c090fc94c5fd2d6958f043e41736e9fa660205fa2565b633c57ebee5f526004601cfd5b63e79042e65f526004601cfd5b60015f527f4cdbc4f47aef831a90102e26cda881868aa5b0c95440b98fe37dbe530f34f5e460205fa2565b60035f527f4cdbc4f47aef831a90102e26cda881868aa5b0c95440b98fe37dbe530f34f5e460205fa2565b905f527f4cdbc4f47aef831a90102e26cda881868aa5b0c95440b98fe37dbe530f34f5e460205fa2565b906109886001600160801b038316809314614a44565b6114936001600160801b03602083015116916001600160681b0360808201511690613ed0565b6104896008546001600160801b0381169060801c612b59565b9190600854906001600160801b03918281169060801c8114613b3f575f52600960205281613ab963ffffffff60405f2054169283613ab26108298263ffffffff165f52600a60205260405f2090565b9788613c8b565b90501693613ad98161170c8463ffffffff165f52600a60205260405f2090565b6001600160681b036020818351169201511614613af4575050565b600854918083169260801c8314613b3f57613b2883610988945f52600960205260405f2063ffffffff198154169055612913565b166001600160801b03196008541617600855614a5d565b604051638727480160e01b8152600490fd5b906109886001600160681b038316809314614a44565b63d61c50f85f526004601cfd5b5f9182525f80516020614f07833981519152602083a3565b905f525f5f80516020614f07833981519152602082a3565b90915f525f80516020614f0783398151915260205fa3565b7f5c9a946d3041134198ebefcd814de7748def6576efd3d1b48f48193e183e89ef5f80a2565b919290925f526020527fecc966b282a372469fa4d3e497c2ac17983c3eaed03f3f17c9acf4b15591663e60405fa3565b613c406001600160681b0391613c3a8360a08601511691846020818351169201511690612954565b90612954565b16906001600160801b0390613c6b82606083015116936001600160701b036101608401511690614b02565b8301809311610c885760400151168101809111610c885780820391110290565b929091613cc69492613ca485516001600160681b031690565b9460208101956001600160681b03978891613c3a89516001600160681b031690565b168015613e0c576101608301948551613ce5906001600160701b031690565b6001600160701b0316613cf791614a83565b818110908290030201613d0990613b51565b935193968716936001600160701b0316613d239085614b02565b613d2c90613a0e565b95878151613d40906001600160681b031690565b90613d4a9161297a565b6001600160681b031690526040018051613d6e9087906001600160801b031661292b565b6001600160801b0316905260a081018051613d939088906001600160681b0316612954565b6001600160681b03169052606081018051613db89087906001600160801b031661292b565b6001600160801b031690526080018051613ddc9087906001600160681b0316612954565b6001600160681b031690526001600160801b03841690613dfc8230613b74565b63ffffffff169161098892614ab6565b505f965086955050505050565b6001600160681b038060a08301511690608083015116818103908111610c8857613e4d9061ffff6101408501511690614ae4565b908101809111610c8857613e70906001600160701b036101608401511690614b02565b6001600160801b036040830151168101809111610c8857613ea16107aa60606104899401516001600160801b031690565b9061296d565b5f527f72877a153052500f5edbb2f9da96a0f45d671d4b4555fdf8628a709dc4eab43a60205fa1565b906001600160701b036101606104899301511690614b02565b61149390613e19565b613efa612657565b5060018060a01b03165f52600760205260405f20906001600160681b0360405192613f248461055f565b54613f3260ff82168561288c565b60081c16602083015281516004811015611b025780613f52600192611af8565b14613f5957565b636bc671fd5f526004601cfd5b613f7b90613f72612bd1565b60025f55613f82565b9060015f55565b905f91613f8d612f49565b6040516301b9d11160e21b81526001600160a01b037f000000000000000000000000b00f9d7c25a044117d29f4b34521528076916a17166004820152336024820152909190602081806044810103817f000000000000000000000000437e0551892c2c9b06d3ffd248fe60572e08cd1a6001600160a01b03165afa90811561100d575f9161411f575b501561402b575061098890610b02338261422b565b909192506140398251151590565b61411a576140569061404a83613a24565b90818082039110020190565b9061407b6115806140756130c96101608501516001600160701b031690565b84614a83565b906001600160681b038216801561411557610489926140fc610b02926140c38730337f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486138fe565b6140eb6140cf33614e3c565b6115e0602082016115d38761167983516001600160681b031690565b6140f58733613b8c565b8633614b58565b6115d3608084019161167983516001600160681b031690565b614b4b565b614b3e565b614138915060203d60201161100657610ff8818361059c565b5f614016565b634d7909975f526004601cfd5b60105f604492602095829560145260345263a9059cbb60601b82525af13d1560015f511417161561417c575f603452565b6390b8ec185f526004601cfd5b5f527f9dc30b8eda31a6a144e092e5de600955523a6a925cc15cc1d1b9b4872cfa615560205fa1565b634a1c13a95f526004601cfd5b63d0242b285f526004601cfd5b63119fe6e35f526004601cfd5b5f527fb848ae6b1253b6cb77e81464128ce8bd94d3d524fea54e801e0da869784dca3360205fa1565b5f527f860c0aa5520013080c2f65981705fcdea474d9f7c3daf954656ed5e65d692d1f60205fa1565b6001600160a01b0382165f90815260076020526040902061424b90612898565b906001825161425981611af8565b61426281611af8565b0361426c57505050565b602082019061428282516001600160681b031690565b60018452906142908561398e565b6001600160681b03821692836142c6575b5050506001600160a01b039092165f90815260076020526040902061098892506128c8565b5f905260405163a1054f6b60e01b81526001600160a01b037f000000000000000000000000b00f9d7c25a044117d29f4b34521528076916a1781166004830152861660248201523060448201529294919290602082806064810103815f7f000000000000000000000000437e0551892c2c9b06d3ffd248fe60572e08cd1a6001600160a01b03165af1801561100d5761098896610fc49561440f945f93614417575b506130c9614402916143f5610160614408969701916143a461439d6143976130c986516001600160701b031690565b88614b02565b898c613ba4565b6001600160a01b0388165f908152600760205260409020805490916143d49160081c6001600160681b031661297a565b610100600160701b0382549160081b1690610100600160701b031916179055565b516001600160701b031690565b90614b02565b9083614b86565b925f806142a1565b6144089350614402916143f56101606144416130c99460203d60201161389b5761388c818361059c565b965050509150614368565b93919490945f935f9361ffff610120880151169161018088019363ffffffff93848651168403848111610c885761448291614cbf565b8a819b61450f575b5050806144ed575b50506144e261016061098895969798016144d56144d08b613ea16144ca8d6144c46130c988516001600160701b031690565b9361296d565b82614b02565b614bed565b6001600160701b03169052565b1663ffffffff169052565b610988959697506145066144e29261016092858c614bb4565b97969550614492565b6145459298509061451f91614ae4565b6144026001600160681b0360808c015116916001600160701b036101608d015116614b02565b604089016001600160801b03908181511691838301809311610c885761456f908316809314614a44565b5295895f61448a565b80516001600160681b0316906145a860208201926001600160681b03918291613c3a86516001600160681b031690565b1680156146d15761016085019586516145c7906001600160701b031690565b6001600160701b03166145d991614a83565b8181109082900302016145eb90613b51565b94516001600160701b03166001600160701b031661460a918616614b02565b61461390613a0e565b91848151614627906001600160681b031690565b906146319161297a565b6001600160681b0316905260400180516146559083906001600160801b031661292b565b6001600160801b0316905260a08201805161467a9085906001600160681b0316612954565b6001600160681b031690526060820190815161469c906001600160801b031690565b906146a69161292b565b6001600160801b031690526080019081516146c7906001600160681b031690565b906115d391612954565b505050505050565b7f1777adabd324f814e2b0e28f6edf876dce01d7d66358c9acfe87b1b5f38338d5546001600160a01b03166060820181905290918282156147a9575060405190638925ca5a825260405f60209483868601523383860152606080860152366080860152368260a08701373660c4019082601c8701915af1835f5114161561264f57608083610489945160051b809460e06003830282010160405282820183823e80885201925f038184015281606084015285019260a08301845260a0019101525a60408401528251905190614c03565b9392505050565b6060810180519091906001600160a01b031615612ff3575f8082518051908160061b956147e260c08884010183614c03565b519160051b019360405a91015103604085015283519463f0bd9468855260c4019082601c8601915af11561264f5752565b927f18247a393d0531b65fbd94f5e78bc5639801a4efda62ae7b43533c4442116c3a959260c09592604051958652602086015260408501526060840152608083015260a0820152a1565b6109889061170c60c082015f614877825163ffffffff1690565b916148936108298463ffffffff165f52600a60205260405f2090565b9460208601906148aa82516001600160681b031690565b906148bf612ce489516001600160681b031690565b6001600160681b0380931610614976575b5061492b6148e588516001600160681b031690565b9261491e6148fa82516001600160681b031690565b60408b015163ffffffff8a16966001600160801b0390911691861690861687614c3b565b516001600160681b031690565b90614940612ce489516001600160681b031690565b9116108314614968575061495383614c75565b5263ffffffff165f52600a60205260405f2090565b61497190614a5d565b614953565b61498861498161260b565b828a613c12565b8681614996575b50506148d0565b6149a0928a613c8b565b8661498f565b5f526020527f9385f9ff65bcd2fb81cece54b27d4ec7376795fc4dcff686e370e347b0ed86c060405fa1565b63186334fe5f526004601cfd5b91929092604051935f526020526040527f0d0843a0fcb8b83f625aafb6e42f234ac48c6728b207d52d97cfa8fbd34d498f60605fa2604052565b90915f527fd6cddb3d69146e96ebc2c87b1b3dd0b20ee2d3b0eadf134e011afb434a3e56e660205fa3565b15614a4b57565b634e487b715f5260116020526024601cfd5b7fcbdf25bf6e096dd9030d89bb2ba2e3e7adb82d25a233c3ca3d92e9f098b74e555f80a2565b8160011c906b033b2e3c9fd0803ce800000090818319048111841517614a4b5702010490565b63e50a45ce5f526004601cfd5b91905f526020527f5272034725119f19d7236de4129fdb5093f0dcb80282ca5edbd587df91d2bd8960405fa2565b81611388190481111582151715614a4b576127109102611388010490565b816b019d971e4fe8401e74000000190481111582151715614a4b576b033b2e3c9fd0803ce800000091026b019d971e4fe8401e74000000010490565b6322d7c0435f526004601cfd5b63e4aa50555f526004601cfd5b91905f526020527f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a1560405fa2565b91905f526020527f571e706c2f09ae0632313e5f3ae89fffdedfc370a2ea59a07fb0d8091147645b60405fa2565b909392935f9463ffffffff610180840151168203918211610c8857614bd892614cf1565b9081614be2575050565b610489929350614cbf565b906109886001600160701b038316809314614a44565b9190825190818152602080809501918181019360051b0101915b828110614c2b575050509050565b8151548152908401908401614c1d565b91929092604051935f526020526040527f9262dc39b47cad3a0512e4c08dda248cb345e7163058f300bc63f56bda288b6e60605fa2604052565b614ca49060085460801c90815f52600960205263ffffffff60405f20911663ffffffff19825416179055612913565b6001600160801b036008549181199060801b16911617600855565b69152d02c7e14af6800000808202918082840403614a4b57830202918183041490151715610c88576301e13380900490565b90610100820163ffffffff9060e082825116940151614d3157614d1f85850386861102928316809314614a44565b52808203911102818082039110020190565b848493940191828411610c8857614d4c908316809314614a44565b5280820391110280820391110290565b614d6581613ef2565b908151614d7181611af8565b614d7a81611af8565b15614da4575b5060028151614d8e81611af8565b614d9781611af8565b10614d9f5790565b614aa9565b60405163366ae42d60e21b81526001600160a01b0382166004820152602081806024810103817f000000000000000000000000d22cc5d80529401cd3eedea4a6e8958c6da49cb86001600160a01b03165afa90811561100d575f91614e1d575b5015614d805760038252614e17906139b9565b5f614d80565b614e36915060203d60201161100657610ff8818361059c565b5f614e04565b614e4581613ef2565b908151614e5181611af8565b614e5a81611af8565b15614e6e575b5060038151614d8e81611af8565b60405163366ae42d60e21b81526001600160a01b0382166004820152602081806024810103817f000000000000000000000000d22cc5d80529401cd3eedea4a6e8958c6da49cb86001600160a01b03165afa90811561100d575f91614ee7575b5015614e605760038252614ee1906139b9565b5f614e60565b614f00915060203d60201161100657610ff8818361059c565b5f614ece56feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa164736f6c6343000816000a

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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