ETH Price: $2,513.83 (+0.49%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Deploy Market An...226389732025-06-05 14:21:592 days ago1749133319IN
0xdd7dd3b5...6386207BE
0 ETH0.053733245.94746295
Deploy Market An...225860732025-05-29 4:37:599 days ago1748493479IN
0xdd7dd3b5...6386207BE
0 ETH0.041754864.62175892
Deploy Market225322882025-05-21 15:54:1117 days ago1747842851IN
0xdd7dd3b5...6386207BE
0 ETH0.026827385.6114954
Deploy Market An...224306222025-05-07 8:31:4731 days ago1746606707IN
0xdd7dd3b5...6386207BE
0 ETH0.004014130.44432851
Deploy Market An...223832082025-04-30 16:59:5938 days ago1746032399IN
0xdd7dd3b5...6386207BE
0 ETH0.012106921.4
Deploy Market223326302025-04-23 15:34:3545 days ago1745422475IN
0xdd7dd3b5...6386207BE
0 ETH0.023135694.83097755
Deploy Market An...221812882025-04-02 12:40:1166 days ago1743597611IN
0xdd7dd3b5...6386207BE
0 ETH0.01032171.14357785
Deploy Market221523282025-03-29 11:41:3570 days ago1743248495IN
0xdd7dd3b5...6386207BE
0 ETH0.00500151.04696792
Deploy Market An...221523032025-03-29 11:36:3570 days ago1743248195IN
0xdd7dd3b5...6386207BE
0 ETH0.00911871.05432635
Deploy Market An...220899542025-03-20 18:45:2378 days ago1742496323IN
0xdd7dd3b5...6386207BE
0 ETH0.004130590.47757351
Deploy Market An...220893052025-03-20 16:34:5979 days ago1742488499IN
0xdd7dd3b5...6386207BE
0 ETH0.01766551.9554382
Deploy Market220163972025-03-10 12:12:5989 days ago1741608779IN
0xdd7dd3b5...6386207BE
0 ETH0.004513980.94323024
Deploy Market219897002025-03-06 18:41:1192 days ago1741286471IN
0xdd7dd3b5...6386207BE
0 ETH0.004454830.93255455
Deploy Market219895472025-03-06 18:10:2392 days ago1741284623IN
0xdd7dd3b5...6386207BE
0 ETH0.006974441.45991595
Deploy Market An...219894382025-03-06 17:47:5992 days ago1741283279IN
0xdd7dd3b5...6386207BE
0 ETH0.024403852.81594116
Add Hooks Templa...217882042025-02-06 14:57:11121 days ago1738853831IN
0xdd7dd3b5...6386207BE
0 ETH0.000387533
Add Hooks Templa...217882032025-02-06 14:56:59121 days ago1738853819IN
0xdd7dd3b5...6386207BE
0 ETH0.00043883
Register With Ar...217882022025-02-06 14:56:47121 days ago1738853807IN
0xdd7dd3b5...6386207BE
0 ETH0.000271773

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x61026060226389732025-06-05 14:21:592 days ago1749133319
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x60e06040226389732025-06-05 14:21:592 days ago1749133319
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x61026060225860732025-05-29 4:37:599 days ago1748493479
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x60e06040225860732025-05-29 4:37:599 days ago1748493479
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x61026060225788882025-05-28 4:28:2310 days ago1748406503
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x61026060225365672025-05-22 6:17:1116 days ago1747894631
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x61026060225364942025-05-22 6:02:3516 days ago1747893755
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x61026060225364552025-05-22 5:54:4716 days ago1747893287
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x61026060225364282025-05-22 5:48:5916 days ago1747892939
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x61026060225362502025-05-22 5:12:4716 days ago1747890767
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x60e06040225362502025-05-22 5:12:4716 days ago1747890767
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x61026060225322882025-05-21 15:54:1117 days ago1747842851
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x61026060224306222025-05-07 8:31:4731 days ago1746606707
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x60e06040224306222025-05-07 8:31:4731 days ago1746606707
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x61026060223832082025-04-30 16:59:5938 days ago1746032399
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x60e06040223832082025-04-30 16:59:5938 days ago1746032399
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x61026060223326302025-04-23 15:34:3545 days ago1745422475
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x61026060222251072025-04-08 15:33:3560 days ago1744126415
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x60e06040222251072025-04-08 15:33:3560 days ago1744126415
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x61026060221812882025-04-02 12:40:1166 days ago1743597611
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x60e06040221812882025-04-02 12:40:1166 days ago1743597611
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x61026060221523282025-03-29 11:41:3570 days ago1743248495
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x61026060221523032025-03-29 11:36:3570 days ago1743248195
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x60e06040221523032025-03-29 11:36:3570 days ago1743248195
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
0x61026060220899542025-03-20 18:45:2378 days ago1742496323
0xdd7dd3b5...6386207BE
 Contract Creation0 ETH
View All Internal Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
HooksFactory

Compiler Version
v0.8.25+commit.b61c2a91

Optimization Enabled:
Yes with 50000 runs

Other Settings:
default evmVersion, Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0 WITH LicenseRef-Commons-Clause-1.0
pragma solidity >=0.8.20;

import './libraries/LibERC20.sol';
import './interfaces/IWildcatArchController.sol';
import './libraries/LibStoredInitCode.sol';
import './libraries/MathUtils.sol';
import './ReentrancyGuard.sol';
import './interfaces/WildcatStructsAndEnums.sol';
import './access/IHooks.sol';
import './IHooksFactory.sol';
import './types/TransientBytesArray.sol';
import './spherex/SphereXProtectedRegisteredBase.sol';

struct TmpMarketParameterStorage {
  address borrower;
  address asset;
  address feeRecipient;
  uint16 protocolFeeBips;
  uint128 maxTotalSupply;
  uint16 annualInterestBips;
  uint16 delinquencyFeeBips;
  uint32 withdrawalBatchDuration;
  uint16 reserveRatioBips;
  uint32 delinquencyGracePeriod;
  bytes32 packedNameWord0;
  bytes32 packedNameWord1;
  bytes32 packedSymbolWord0;
  bytes32 packedSymbolWord1;
  uint8 decimals;
  HooksConfig hooks;
}

contract HooksFactory is SphereXProtectedRegisteredBase, ReentrancyGuard, IHooksFactory {
  using LibERC20 for address;

  TransientBytesArray internal constant _tmpMarketParameters =
    TransientBytesArray.wrap(uint256(keccak256('Transient:TmpMarketParametersStorage')) - 1);

  uint256 internal immutable ownCreate2Prefix = LibStoredInitCode.getCreate2Prefix(address(this));

  address public immutable override marketInitCodeStorage;

  uint256 public immutable override marketInitCodeHash;

  address public immutable override sanctionsSentinel;

  /**
   * @dev Return the contract name "WildcatHooksFactory"
   */
  function name() external pure override returns (string memory) {
    // Use yul to avoid duplicate memory allocation and reduce code size
    // Uses words at 0x20, 0x40, 0x60
    // 0x20 is overwritten with the ABI offset (32)
    // 0x40 contains the free pointer which will be 1 byte when this function executes.
    // The length of the string (19) is written to the last byte of the free pointer word.
    // 0x60 is the zero slot, so it will not have any dirty bits when this function executes.
    // It is overwritten with the name bytes in the same operation as the length.
    assembly {
      mstore(0x53, 0x1357696c64636174486f6f6b73466163746f7279)
      mstore(0x20, 0x20)
      return(0x20, 0x60)
    }
  }

  address[] internal _hooksTemplates;

  /// @dev Mapping from borrower to their deployed hooks instances
  mapping(address borrower => address[] hooksInstances) internal _hooksInstancesByBorrower;

  /**
   * @dev Mapping from hooks template to markets created with it.
   *      Used for pushing protocol fee changes to affected markets.
   */
  mapping(address hooksTemplate => address[] markets) internal _marketsByHooksTemplate;

  /**
   * @dev Mapping from hooks instance to markets deployed using it.
   *      Intended primarily for off-chain queries.
   */
  mapping(address hooksInstance => address[] markets) internal _marketsByHooksInstance;

  /**
   * @dev Mapping from hooks template to its fee configuration and name
   */
  mapping(address hooksTemplate => HooksTemplate details) internal _templateDetails;

  mapping(address hooksInstance => address hooksTemplate)
    public
    override getHooksTemplateForInstance;

  constructor(
    address archController_,
    address _sanctionsSentinel,
    address _marketInitCodeStorage,
    uint256 _marketInitCodeHash
  ) {
    marketInitCodeStorage = _marketInitCodeStorage;
    marketInitCodeHash = _marketInitCodeHash;
    _archController = archController_;
    sanctionsSentinel = _sanctionsSentinel;
    __SphereXProtectedRegisteredBase_init(IWildcatArchController(archController_).sphereXEngine());
  }

  /**
   * @dev Registers the factory as a controller with the arch-controller, allowing
   *      it to register new markets.
   *      Needs to be executed once at deployment.
   *      Does not need checks for whether it has already been registered as the
   *      arch-controller will revert if it is already registered.
   */
  function registerWithArchController() external override {
    IWildcatArchController(_archController).registerController(address(this));
  }

  function archController() external view override returns (address) {
    return _archController;
  }

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

  /**
   * @dev Get the temporary market parameters from transient storage.
   */
  function _getTmpMarketParameters()
    internal
    view
    returns (TmpMarketParameterStorage memory parameters)
  {
    return abi.decode(_tmpMarketParameters.read(), (TmpMarketParameterStorage));
  }

  /**
   * @dev Set the temporary market parameters in transient storage.
   */
  function _setTmpMarketParameters(TmpMarketParameterStorage memory parameters) internal {
    _tmpMarketParameters.write(abi.encode(parameters));
  }

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

  modifier onlyArchControllerOwner() {
    if (msg.sender != IWildcatArchController(_archController).owner()) {
      revert CallerNotArchControllerOwner();
    }
    _;
  }

  // ========================================================================== //
  //                               Hooks Templates                              //
  // ========================================================================== //

  function addHooksTemplate(
    address hooksTemplate,
    string calldata name,
    address feeRecipient,
    address originationFeeAsset,
    uint80 originationFeeAmount,
    uint16 protocolFeeBips
  ) external override onlyArchControllerOwner {
    if (_templateDetails[hooksTemplate].exists) {
      revert HooksTemplateAlreadyExists();
    }
    _validateFees(feeRecipient, originationFeeAsset, originationFeeAmount, protocolFeeBips);
    _templateDetails[hooksTemplate] = HooksTemplate({
      exists: true,
      name: name,
      feeRecipient: feeRecipient,
      originationFeeAsset: originationFeeAsset,
      originationFeeAmount: originationFeeAmount,
      protocolFeeBips: protocolFeeBips,
      enabled: true,
      index: uint24(_hooksTemplates.length)
    });
    _hooksTemplates.push(hooksTemplate);
    emit HooksTemplateAdded(
      hooksTemplate,
      name,
      feeRecipient,
      originationFeeAsset,
      originationFeeAmount,
      protocolFeeBips
    );
  }

  function _validateFees(
    address feeRecipient,
    address originationFeeAsset,
    uint80 originationFeeAmount,
    uint16 protocolFeeBips
  ) internal pure {
    bool hasOriginationFee = originationFeeAmount > 0;
    bool nullFeeRecipient = feeRecipient == address(0);
    bool nullOriginationFeeAsset = originationFeeAsset == address(0);
    if (
      (protocolFeeBips > 0 && nullFeeRecipient) ||
      (hasOriginationFee && nullFeeRecipient) ||
      (hasOriginationFee && nullOriginationFeeAsset) ||
      protocolFeeBips > 1_000
    ) {
      revert InvalidFeeConfiguration();
    }
  }

  /// @dev Update the fees for a hooks template
  /// Note: The new fee structure will apply to all NEW markets created with existing
  ///       or future instances of the hooks template, and the protocol fee can be pushed
  ///       to existing markets using `pushProtocolFeeBipsUpdates`.
  function updateHooksTemplateFees(
    address hooksTemplate,
    address feeRecipient,
    address originationFeeAsset,
    uint80 originationFeeAmount,
    uint16 protocolFeeBips
  ) external override onlyArchControllerOwner {
    if (!_templateDetails[hooksTemplate].exists) {
      revert HooksTemplateNotFound();
    }
    _validateFees(feeRecipient, originationFeeAsset, originationFeeAmount, protocolFeeBips);
    HooksTemplate storage template = _templateDetails[hooksTemplate];
    template.feeRecipient = feeRecipient;
    template.originationFeeAsset = originationFeeAsset;
    template.originationFeeAmount = originationFeeAmount;
    template.protocolFeeBips = protocolFeeBips;
    emit HooksTemplateFeesUpdated(
      hooksTemplate,
      feeRecipient,
      originationFeeAsset,
      originationFeeAmount,
      protocolFeeBips
    );
  }

  function disableHooksTemplate(address hooksTemplate) external override onlyArchControllerOwner {
    if (!_templateDetails[hooksTemplate].exists) {
      revert HooksTemplateNotFound();
    }
    _templateDetails[hooksTemplate].enabled = false;
    // Emit an event to indicate that the template has been removed
    emit HooksTemplateDisabled(hooksTemplate);
  }

  function getHooksTemplateDetails(
    address hooksTemplate
  ) external view override returns (HooksTemplate memory) {
    return _templateDetails[hooksTemplate];
  }

  function isHooksTemplate(address hooksTemplate) external view override returns (bool) {
    return _templateDetails[hooksTemplate].exists;
  }

  function getHooksTemplates() external view override returns (address[] memory) {
    return _hooksTemplates;
  }

  function getHooksTemplates(
    uint256 start,
    uint256 end
  ) external view override returns (address[] memory arr) {
    uint256 len = _hooksTemplates.length;
    end = MathUtils.min(end, len);
    uint256 count = end - start;
    arr = new address[](count);
    for (uint256 i = 0; i < count; i++) {
      arr[i] = _hooksTemplates[start + i];
    }
  }

  function getHooksTemplatesCount() external view override returns (uint256) {
    return _hooksTemplates.length;
  }

  function getMarketsForHooksTemplate(
    address hooksTemplate
  ) external view override returns (address[] memory) {
    return _marketsByHooksTemplate[hooksTemplate];
  }

  function getMarketsForHooksTemplate(
    address hooksTemplate,
    uint256 start,
    uint256 end
  ) external view override returns (address[] memory arr) {
    address[] storage markets = _marketsByHooksTemplate[hooksTemplate];
    uint256 len = markets.length;
    end = MathUtils.min(end, len);
    uint256 count = end - start;
    arr = new address[](count);
    for (uint256 i = 0; i < count; i++) {
      arr[i] = markets[start + i];
    }
  }

  function getMarketsForHooksTemplateCount(
    address hooksTemplate
  ) external view override returns (uint256) {
    return _marketsByHooksTemplate[hooksTemplate].length;
  }

  // ========================================================================== //
  //                               Hooks Instances                              //
  // ========================================================================== //

  /// @dev Deploy a hooks instance for an approved template with constructor args.
  ///      Callable by approved borrowers on the arch-controller.
  ///      May require payment of origination fees.
  function deployHooksInstance(
    address hooksTemplate,
    bytes calldata constructorArgs
  ) external override nonReentrant returns (address hooksInstance) {
    if (!IWildcatArchController(_archController).isRegisteredBorrower(msg.sender)) {
      revert NotApprovedBorrower();
    }
    hooksInstance = _deployHooksInstance(hooksTemplate, constructorArgs);
  }

  function getHooksInstancesForBorrower(
    address borrower
  ) external view override returns (address[] memory) {
    return _hooksInstancesByBorrower[borrower];
  }

  function getHooksInstancesCountForBorrower(
    address borrower
  ) external view override returns (uint256) {
    return _hooksInstancesByBorrower[borrower].length;
  }

  function isHooksInstance(address hooksInstance) external view override returns (bool) {
    return getHooksTemplateForInstance[hooksInstance] != address(0);
  }

  function _deployHooksInstance(
    address hooksTemplate,
    bytes calldata constructorArgs
  ) internal returns (address hooksInstance) {
    HooksTemplate storage template = _templateDetails[hooksTemplate];
    if (!template.exists) {
      revert HooksTemplateNotFound();
    }
    if (!template.enabled) {
      revert HooksTemplateNotAvailable();
    }

    uint256 numHooksForBorrower = _hooksInstancesByBorrower[msg.sender].length;
    bytes32 salt;
    assembly {
      salt := or(shl(96, caller()), numHooksForBorrower)
      let initCodePointer := mload(0x40)
      let initCodeSize := sub(extcodesize(hooksTemplate), 1)
      // Copy code from target address to memory starting at byte 1
      extcodecopy(hooksTemplate, initCodePointer, 1, initCodeSize)
      let endInitCodePointer := add(initCodePointer, initCodeSize)
      // Write the address of the caller as the first parameter
      mstore(endInitCodePointer, caller())
      // Write the offset to the encoded constructor args
      mstore(add(endInitCodePointer, 0x20), 0x40)
      // Write the length of the encoded constructor args
      let constructorArgsSize := constructorArgs.length
      mstore(add(endInitCodePointer, 0x40), constructorArgsSize)
      // Copy constructor args to initcode after the bytes length
      calldatacopy(add(endInitCodePointer, 0x60), constructorArgs.offset, constructorArgsSize)
      // Get the full size of the initcode with the constructor args
      let initCodeSizeWithArgs := add(add(initCodeSize, 0x60), constructorArgsSize)
      // Deploy the contract with the initcode
      hooksInstance := create2(0, initCodePointer, initCodeSizeWithArgs, salt)
      if iszero(hooksInstance) {
        mstore(0x00, 0x30116425) // DeploymentFailed()
        revert(0x1c, 0x04)
      }
    }
    _hooksInstancesByBorrower[msg.sender].push(hooksInstance);

    emit HooksInstanceDeployed(hooksInstance, hooksTemplate);
    getHooksTemplateForInstance[hooksInstance] = hooksTemplate;
  }

  // ========================================================================== //
  //                                   Markets                                  //
  // ========================================================================== //

  function getMarketsForHooksInstance(
    address hooksInstance
  ) external view override returns (address[] memory) {
    return _marketsByHooksInstance[hooksInstance];
  }

  function getMarketsForHooksInstance(
    address hooksInstance,
    uint256 start,
    uint256 end
  ) external view override returns (address[] memory arr) {
    address[] storage markets = _marketsByHooksInstance[hooksInstance];
    end = MathUtils.min(end, markets.length);
    uint256 count = end - start;
    arr = new address[](count);
    for (uint256 i = 0; i < count; i++) {
      arr[i] = markets[start + i];
    }
  }

  function getMarketsForHooksInstanceCount(
    address hooksInstance
  ) external view override returns (uint256) {
    return _marketsByHooksInstance[hooksInstance].length;
  }

  /**
   * @dev Get the temporarily stored market parameters for a market that is
   *      currently being deployed.
   */
  function getMarketParameters()
    external
    view
    override
    returns (MarketParameters memory parameters)
  {
    TmpMarketParameterStorage memory tmp = _getTmpMarketParameters();

    parameters.asset = tmp.asset;
    parameters.packedNameWord0 = tmp.packedNameWord0;
    parameters.packedNameWord1 = tmp.packedNameWord1;
    parameters.packedSymbolWord0 = tmp.packedSymbolWord0;
    parameters.packedSymbolWord1 = tmp.packedSymbolWord1;
    parameters.decimals = tmp.decimals;
    parameters.borrower = tmp.borrower;
    parameters.feeRecipient = tmp.feeRecipient;
    parameters.sentinel = sanctionsSentinel;
    parameters.maxTotalSupply = tmp.maxTotalSupply;
    parameters.protocolFeeBips = tmp.protocolFeeBips;
    parameters.annualInterestBips = tmp.annualInterestBips;
    parameters.delinquencyFeeBips = tmp.delinquencyFeeBips;
    parameters.withdrawalBatchDuration = tmp.withdrawalBatchDuration;
    parameters.reserveRatioBips = tmp.reserveRatioBips;
    parameters.delinquencyGracePeriod = tmp.delinquencyGracePeriod;
    parameters.archController = _archController;
    parameters.sphereXEngine = sphereXEngine();
    parameters.hooks = tmp.hooks;
  }

  function computeMarketAddress(bytes32 salt) external view override returns (address) {
    return LibStoredInitCode.calculateCreate2Address(ownCreate2Prefix, salt, marketInitCodeHash);
  }

  /**
   * @dev Given a string of at most 63 bytes, produces a packed version with two words,
   *      where the first word contains the length byte and the first 31 bytes of the string,
   *      and the second word contains the second 32 bytes of the string.
   */
  function _packString(string memory str) internal pure returns (bytes32 word0, bytes32 word1) {
    assembly {
      let length := mload(str)
      // Equivalent to:
      // if (str.length > 63) revert NameOrSymbolTooLong();
      if gt(length, 0x3f) {
        mstore(0, 0x19a65cb6)
        revert(0x1c, 0x04)
      }
      // Load the length and first 31 bytes of the string into the first word
      // by reading from 31 bytes after the length pointer.
      word0 := mload(add(str, 0x1f))
      // If the string is less than 32 bytes, the second word will be zeroed out.
      word1 := mul(mload(add(str, 0x3f)), gt(mload(str), 0x1f))
    }
  }

  function _deployMarket(
    DeployMarketInputs memory parameters,
    bytes memory hooksData,
    address hooksTemplate,
    HooksTemplate memory templateDetails,
    bytes32 salt,
    address originationFeeAsset,
    uint256 originationFeeAmount
  ) internal returns (address market) {
    if (IWildcatArchController(_archController).isBlacklistedAsset(parameters.asset)) {
      revert AssetBlacklisted();
    }
    address hooksInstance = parameters.hooks.hooksAddress();

    if (!(address(bytes20(salt)) == msg.sender || bytes20(salt) == bytes20(0))) {
      revert SaltDoesNotContainSender();
    }

    if (
      originationFeeAsset != templateDetails.originationFeeAsset ||
      originationFeeAmount != templateDetails.originationFeeAmount
    ) {
      revert FeeMismatch();
    }

    if (originationFeeAsset != address(0)) {
      originationFeeAsset.safeTransferFrom(
        msg.sender,
        templateDetails.feeRecipient,
        originationFeeAmount
      );
    }

    market = LibStoredInitCode.calculateCreate2Address(ownCreate2Prefix, salt, marketInitCodeHash);

    parameters.hooks = IHooks(hooksInstance).onCreateMarket(
      msg.sender,
      market,
      parameters,
      hooksData
    );
    uint8 decimals = parameters.asset.decimals();

    string memory name = string.concat(parameters.namePrefix, parameters.asset.name());
    string memory symbol = string.concat(parameters.symbolPrefix, parameters.asset.symbol());

    TmpMarketParameterStorage memory tmp = TmpMarketParameterStorage({
      borrower: msg.sender,
      asset: parameters.asset,
      packedNameWord0: bytes32(0),
      packedNameWord1: bytes32(0),
      packedSymbolWord0: bytes32(0),
      packedSymbolWord1: bytes32(0),
      decimals: decimals,
      feeRecipient: templateDetails.feeRecipient,
      protocolFeeBips: templateDetails.protocolFeeBips,
      maxTotalSupply: parameters.maxTotalSupply,
      annualInterestBips: parameters.annualInterestBips,
      delinquencyFeeBips: parameters.delinquencyFeeBips,
      withdrawalBatchDuration: parameters.withdrawalBatchDuration,
      reserveRatioBips: parameters.reserveRatioBips,
      delinquencyGracePeriod: parameters.delinquencyGracePeriod,
      hooks: parameters.hooks
    });
    {
      (tmp.packedNameWord0, tmp.packedNameWord1) = _packString(name);
      (tmp.packedSymbolWord0, tmp.packedSymbolWord1) = _packString(symbol);
    }

    _setTmpMarketParameters(tmp);

    if (market.code.length != 0) {
      revert MarketAlreadyExists();
    }
    LibStoredInitCode.create2WithStoredInitCode(marketInitCodeStorage, salt);

    IWildcatArchController(_archController).registerMarket(market);

    _tmpMarketParameters.setEmpty();

    _marketsByHooksTemplate[hooksTemplate].push(market);
    _marketsByHooksInstance[hooksInstance].push(market);

    emit MarketDeployed(
      hooksTemplate,
      market,
      name,
      symbol,
      tmp.asset,
      tmp.maxTotalSupply,
      tmp.annualInterestBips,
      tmp.delinquencyFeeBips,
      tmp.withdrawalBatchDuration,
      tmp.reserveRatioBips,
      tmp.delinquencyGracePeriod,
      tmp.hooks
    );
  }

  function deployMarket(
    DeployMarketInputs calldata parameters,
    bytes calldata hooksData,
    bytes32 salt,
    address originationFeeAsset,
    uint256 originationFeeAmount
  ) external override nonReentrant returns (address market) {
    if (!IWildcatArchController(_archController).isRegisteredBorrower(msg.sender)) {
      revert NotApprovedBorrower();
    }
    address hooksInstance = parameters.hooks.hooksAddress();
    address hooksTemplate = getHooksTemplateForInstance[hooksInstance];
    if (hooksTemplate == address(0)) {
      revert HooksInstanceNotFound();
    }
    HooksTemplate memory templateDetails = _templateDetails[hooksTemplate];
    market = _deployMarket(
      parameters,
      hooksData,
      hooksTemplate,
      templateDetails,
      salt,
      originationFeeAsset,
      originationFeeAmount
    );
  }

  function deployMarketAndHooks(
    address hooksTemplate,
    bytes calldata hooksTemplateArgs,
    DeployMarketInputs memory parameters,
    bytes calldata hooksData,
    bytes32 salt,
    address originationFeeAsset,
    uint256 originationFeeAmount
  ) external override nonReentrant returns (address market, address hooksInstance) {
    if (!IWildcatArchController(_archController).isRegisteredBorrower(msg.sender)) {
      revert NotApprovedBorrower();
    }
    HooksTemplate memory templateDetails = _templateDetails[hooksTemplate];
    if (!templateDetails.exists) {
      revert HooksTemplateNotFound();
    }
    hooksInstance = _deployHooksInstance(hooksTemplate, hooksTemplateArgs);
    parameters.hooks = parameters.hooks.setHooksAddress(hooksInstance);
    market = _deployMarket(
      parameters,
      hooksData,
      hooksTemplate,
      templateDetails,
      salt,
      originationFeeAsset,
      originationFeeAmount
    );
  }

  /**
   * @dev Push any changes to the fee configuration of `hooksTemplate` to markets
   *      using any instances of that template at `_marketsByHooksTemplate[hooksTemplate]`.
   *      Starts at `marketStartIndex` and ends one before `marketEndIndex`  or markets.length,
   *      whichever is lowest.
   */
  function pushProtocolFeeBipsUpdates(
    address hooksTemplate,
    uint marketStartIndex,
    uint marketEndIndex
  ) public override nonReentrant {
    HooksTemplate memory details = _templateDetails[hooksTemplate];
    if (!details.exists) revert HooksTemplateNotFound();

    address[] storage markets = _marketsByHooksTemplate[hooksTemplate];
    marketEndIndex = MathUtils.min(marketEndIndex, markets.length);
    uint256 count = marketEndIndex - marketStartIndex;
    uint256 setProtocolFeeBipsCalldataPointer;
    uint16 protocolFeeBips = details.protocolFeeBips;
    assembly {
      // Write the calldata for `market.setProtocolFeeBips(protocolFeeBips)`
      // this will be reused for every market
      setProtocolFeeBipsCalldataPointer := mload(0x40)
      mstore(0x40, add(setProtocolFeeBipsCalldataPointer, 0x40))
      // Write selector for `setProtocolFeeBips(uint16)`
      mstore(setProtocolFeeBipsCalldataPointer, 0xae6ea191)
      mstore(add(setProtocolFeeBipsCalldataPointer, 0x20), protocolFeeBips)
      // Add 28 bytes to get the exact pointer to the first byte of the selector
      setProtocolFeeBipsCalldataPointer := add(setProtocolFeeBipsCalldataPointer, 0x1c)
    }
    for (uint256 i = 0; i < count; i++) {
      address market = markets[marketStartIndex + i];
      assembly {
        if iszero(call(gas(), market, 0, setProtocolFeeBipsCalldataPointer, 0x24, 0, 0)) {
          // Equivalent to `revert SetProtocolFeeBipsFailed()`
          mstore(0, 0x4484a4a9)
          revert(0x1c, 0x04)
        }
      }
    }
  }

  /**
   * @dev Push any changes to the fee configuration of `hooksTemplate` to all markets
   *      using any instances of that template at `_marketsByHooksTemplate[hooksTemplate]`.
   */
  function pushProtocolFeeBipsUpdates(address hooksTemplate) external override {
    pushProtocolFeeBipsUpdates(hooksTemplate, 0, type(uint256).max);
  }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import './StringQuery.sol';

/// @notice Safe ERC20 library
/// @author d1ll0n
/// @notice Changes from solady:
///   - Removed Permit2 and ETH functions
///   - `balanceOf(address)` reverts if the call fails or does not return >=32 bytes
///   - Added queries for `name`, `symbol`, `decimals`
///   - Set name to LibERC20 as it has queries unrelated to transfers and ETH functions were removed
/// @author Modified from Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibERC20.sol)
/// @author Previously modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibERC20.sol)
///
/// @dev Note:
/// - For ERC20s, this implementation won't check that a token has code,
///   responsibility is delegated to the caller.
library LibERC20 {
  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
  /*                       CUSTOM ERRORS                        */
  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

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

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

  /// @dev The ERC20 `balanceOf` call has failed.
  error BalanceOfFailed();

  /// @dev The ERC20 `name` call has failed.
  error NameFailed();

  /// @dev The ERC20 `symbol` call has failed.
  error SymbolFailed();

  /// @dev The ERC20 `decimals` call has failed.
  error DecimalsFailed();

  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
  /*                      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 `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 Returns the amount of ERC20 `token` owned by `account`.
  /// Reverts if the call to `balanceOf` reverts or returns less than 32 bytes.
  function balanceOf(address token, address account) internal view returns (uint256 amount) {
    /// @solidity memory-safe-assembly
    assembly {
      mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
      mstore(0x20, account) // Store the `account` argument.
      // 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, 0x00, 0x20)
        )
      ) {
        mstore(0x00, 0x4963f6d5) // `BalanceOfFailed()`.
        revert(0x1c, 0x04)
      }
      amount := mload(0x00)
    }
  }

  /// @dev Returns the `decimals` of ERC20 `token`.
  /// Reverts if the call to `decimals` reverts or returns less than 32 bytes.
  function decimals(address token) internal view returns (uint8 _decimals) {
    assembly {
      // Write selector for `decimals()` to the end of the first word
      // of scratch space.
      mstore(0, 0x313ce567)
      // Call `asset.decimals()`, writing up to 32 bytes of returndata
      // to scratch space, overwriting the calldata used for the call.
      // Reverts if the call fails, does not return exactly 32 bytes, or the returndata
      // exceeds 8 bits.
      if iszero(
        and(
          and(eq(returndatasize(), 0x20), lt(mload(0), 0x100)),
          staticcall(gas(), token, 0x1c, 0x04, 0, 0x20)
        )
      ) {
        mstore(0x00, 0x3394d170) // `DecimalsFailed()`.
        revert(0x1c, 0x04)
      }
      // Read the return value from scratch space
      _decimals := mload(0)
    }
  }

  /// @dev Returns the `name` of ERC20 `token`.
  /// Reverts if the call to `name` reverts or returns a value which is neither
  /// a bytes32 string nor a valid ABI-encoded string.
  function name(address token) internal view returns (string memory) {
    // The `name` function selector is 0x06fdde03.
    // The `NameFailed` error selector is 0x2ed09f54.
    return queryStringOrBytes32AsString(token, 0x06fdde03, 0x2ed09f54);
  }

  /// @dev Returns the `symbol` of ERC20 `token`.
  /// Reverts if the call to `symbol` reverts or returns a value which is neither
  /// a bytes32 string nor a valid ABI-encoded string.
  function symbol(address token) internal view returns (string memory) {
    // The `symbol` function selector is 0x95d89b41.
    // The `SymbolFailed` error selector is 0x3ddcc60a.
    return queryStringOrBytes32AsString(token, 0x95d89b41, 0x3ddcc60a);
  }
}

File 3 of 21 : IWildcatArchController.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IWildcatArchController {
  error NotMarketFactory();

  error NotControllerFactory();

  function owner() external view returns (address);

  // ========================================================================== //
  //                               SphereX Config                               //
  // ========================================================================== //

  event ChangedSpherexOperator(address oldSphereXAdmin, address newSphereXAdmin);

  event ChangedSpherexEngineAddress(address oldEngineAddress, address newEngineAddress);

  event SpherexAdminTransferStarted(address currentAdmin, address pendingAdmin);

  event SpherexAdminTransferCompleted(address oldAdmin, address newAdmin);

  event NewAllowedSenderOnchain(address sender);

  error SphereXOperatorRequired();

  error SphereXAdminRequired();

  error SphereXOperatorOrAdminRequired();

  error SphereXNotPendingAdmin();

  error SphereXNotEngine();

  function pendingSphereXAdmin() external view returns (address);

  function sphereXAdmin() external view returns (address);

  function sphereXOperator() external view returns (address);

  function sphereXEngine() external view returns (address);

  function transferSphereXAdminRole(address newAdmin) external virtual;

  function acceptSphereXAdminRole() external virtual;

  function changeSphereXOperator(address newSphereXOperator) external;

  function changeSphereXEngine(address newSphereXEngine) external;

  // ========================================================================== //
  //                         Controller Factory Registry                        //
  // ========================================================================== //

  event ControllerFactoryAdded(address);

  event ControllerFactoryRemoved(address);

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

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

  function getRegisteredControllerFactoriesCount() external view returns (uint256);

  function isRegisteredControllerFactory(address factory) external view returns (bool);

  function registerControllerFactory(address factory) external;

  function removeControllerFactory(address factory) external;

  // ========================================================================== //
  //                             Controller Registry                            //
  // ========================================================================== //

  event ControllerAdded(address, address);

  event ControllerRemoved(address);

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

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

  function getRegisteredControllersCount() external view returns (uint256);

  function isRegisteredController(address controller) external view returns (bool);

  function registerController(address controller) external;

  function removeController(address controller) external;

  // ========================================================================== //
  //                             Borrowers Registry                             //
  // ========================================================================== //

  event BorrowerAdded(address);

  event BorrowerRemoved(address);

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

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

  function getRegisteredBorrowersCount() external view returns (uint256);

  function isRegisteredBorrower(address borrower) external view returns (bool);

  function registerBorrower(address borrower) external;

  function removeBorrower(address borrower) external;

  // ========================================================================== //
  //                          Asset Blacklist Registry                          //
  // ========================================================================== //

  event AssetPermitted();

  event AssetBlacklisted();

  function addBlacklist(address asset) external;

  function removeBlacklist(address asset) external;

  function isBlacklistedAsset(address asset) external view returns (bool);

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

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

  function getBlacklistedAssetsCount() external view returns (uint256);

  // ========================================================================== //
  //                               Markets Registry                             //
  // ========================================================================== //

  event MarketAdded(address, address);

  event MarketRemoved(address);

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

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

  function getRegisteredMarketsCount() external view returns (uint256);

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

  function registerMarket(address market) external;

  function removeMarket(address market) external;
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.24;

library LibStoredInitCode {
  error InitCodeDeploymentFailed();
  error DeploymentFailed();

  function deployInitCode(bytes memory data) internal returns (address initCodeStorage) {
    assembly {
      let size := mload(data)
      let createSize := add(size, 0x0b)
      // Prefix Code
      //
      // Has trailing STOP instruction so the deployed data
      // can not be executed as a smart contract.
      //
      // Instruction                | Stack
      // ----------------------------------------------------
      // PUSH2 size                 | size                  |
      // PUSH0                      | 0, size               |
      // DUP2                       | size, 0, size         |
      // PUSH1 10 (offset to STOP)  | 10, size, 0, size     |
      // PUSH0                      | 0, 10, size, 0, size  |
      // CODECOPY                   | 0, size               |
      // RETURN                     |                       |
      // STOP                       |                       |
      // ----------------------------------------------------

      // Shift (size + 1) to position it in front of the PUSH2 instruction.
      // Reuse `data.length` memory for the create prefix to avoid
      // unnecessary memory allocation.
      mstore(data, or(shl(64, add(size, 1)), 0x6100005f81600a5f39f300))
      // Deploy the code storage
      initCodeStorage := create(0, add(data, 21), createSize)
      // if (initCodeStorage == address(0)) revert InitCodeDeploymentFailed();
      if iszero(initCodeStorage) {
        mstore(0, 0x11c8c3c0)
        revert(0x1c, 0x04)
      }
      // Restore `data.length`
      mstore(data, size)
    }
  }

  /**
   * @dev Returns the create2 prefix for a given deployer address.
   *      Equivalent to `uint256(uint160(deployer)) | (0xff << 160)`
   */
  function getCreate2Prefix(address deployer) internal pure returns (uint256 create2Prefix) {
    assembly {
      create2Prefix := or(deployer, 0xff0000000000000000000000000000000000000000)
    }
  }

  function calculateCreate2Address(
    uint256 create2Prefix,
    bytes32 salt,
    uint256 initCodeHash
  ) internal pure returns (address create2Address) {
    assembly {
      // Cache the free memory pointer so it can be restored at the end
      let freeMemoryPointer := mload(0x40)

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

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

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

      // Calculate create2 address
      create2Address := and(keccak256(0x0b, 0x55), 0xffffffffffffffffffffffffffffffffffffffff)

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

  function createWithStoredInitCode(address initCodeStorage) internal returns (address deployment) {
    deployment = createWithStoredInitCode(initCodeStorage, 0);
  }

  function createWithStoredInitCode(
    address initCodeStorage,
    uint256 value
  ) internal returns (address deployment) {
    assembly {
      let initCodePointer := mload(0x40)
      let initCodeSize := sub(extcodesize(initCodeStorage), 1)
      extcodecopy(initCodeStorage, initCodePointer, 1, initCodeSize)
      deployment := create(value, initCodePointer, initCodeSize)
      if iszero(deployment) {
        mstore(0x00, 0x30116425) // DeploymentFailed()
        revert(0x1c, 0x04)
      }
    }
  }

  function create2WithStoredInitCode(
    address initCodeStorage,
    bytes32 salt
  ) internal returns (address deployment) {
    deployment = create2WithStoredInitCode(initCodeStorage, salt, 0);
  }

  function create2WithStoredInitCode(
    address initCodeStorage,
    bytes32 salt,
    uint256 value
  ) internal returns (address deployment) {
    assembly {
      let initCodePointer := mload(0x40)
      let initCodeSize := sub(extcodesize(initCodeStorage), 1)
      extcodecopy(initCodeStorage, initCodePointer, 1, initCodeSize)
      deployment := create2(value, initCodePointer, initCodeSize, salt)
      if iszero(deployment) {
        mstore(0x00, 0x30116425) // DeploymentFailed()
        revert(0x1c, 0x04)
      }
    }
  }

  function create2WithStoredInitCode(
    address initCodeStorage,
    bytes32 salt,
    uint256 value,
    bytes memory constructorArgs
  ) internal returns (address deployment) {
    assembly {
      let initCodePointer := mload(0x40)
      let initCodeSize := sub(extcodesize(initCodeStorage), 1)
      // Copy code from target address to memory starting at byte 1
      extcodecopy(initCodeStorage, initCodePointer, 1, initCodeSize)
      // Copy constructor args from memory to initcode
      let constructorArgsSize := mload(constructorArgs)
      mcopy(add(initCodePointer, initCodeSize), add(constructorArgs, 0x20), constructorArgsSize)
      let initCodeSizeWithArgs := add(initCodeSize, constructorArgsSize)
      deployment := create2(value, initCodePointer, initCodeSizeWithArgs, salt)
      if iszero(deployment) {
        mstore(0x00, 0x30116425) // DeploymentFailed()
        revert(0x1c, 0x04)
      }
    }
  }

  function create2WithStoredInitCode(
    address initCodeStorage,
    bytes32 salt,
    bytes memory constructorArgs
  ) internal returns (address deployment) {
    return create2WithStoredInitCode(initCodeStorage, salt, 0, constructorArgs);
  }

  function create2WithStoredInitCodeCD(
    address initCodeStorage,
    bytes32 salt,
    uint256 value,
    bytes calldata constructorArgs
  ) internal returns (address deployment) {
    assembly {
      let initCodePointer := mload(0x40)
      let initCodeSize := sub(extcodesize(initCodeStorage), 1)
      // Copy code from target address to memory starting at byte 1
      extcodecopy(initCodeStorage, initCodePointer, 1, initCodeSize)
      // Copy constructor args from calldata to end of initcode
      let constructorArgsSize := constructorArgs.length
      calldatacopy(add(initCodePointer, initCodeSize), constructorArgs.offset, constructorArgsSize)
      let initCodeSizeWithArgs := add(initCodeSize, constructorArgsSize)
      deployment := create2(value, initCodePointer, initCodeSizeWithArgs, salt)
      if iszero(deployment) {
        mstore(0x00, 0x30116425) // DeploymentFailed()
        revert(0x1c, 0x04)
      }
    }
  }

  function create2WithStoredInitCodeCD(
    address initCodeStorage,
    bytes32 salt,
    bytes calldata constructorArgs
  ) internal returns (address deployment) {
    return create2WithStoredInitCodeCD(initCodeStorage, salt, 0, constructorArgs);
  }
}

// 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 Saturation addition. Add `a` to `b` and return the result
   *      if it is less than `maxValue` or `maxValue` if it overflows.
   */
  function satAdd(uint256 a, uint256 b, uint256 maxValue) internal pure returns (uint256 c) {
    unchecked {
      c = a + b;
      return ternary(c < maxValue, c, maxValue);
    }
  }

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

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20;

/// @dev Selector for `error NoReentrantCalls()`
uint256 constant NoReentrantCalls_ErrorSelector = 0x7fa8a987;

uint256 constant _REENTRANCY_GUARD_SLOT = 0x929eee14;

/**
 * @title ReentrancyGuard
 * @author d1ll0n
 * @notice Changes from original:
 *   - Removed the checks for whether tstore is supported.
 * @author Modified from Seaport contract by 0age (https://github.com/ProjectOpenSea/seaport-1.6)
 *
 * @notice ReentrancyGuard contains a transient 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.
   *
   *      Note: Only defined for the sake of the interface and readability - the
   *      definition is not directly referenced in the contract code.
   */
  error NoReentrantCalls();

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

  /**
   * @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 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 {
    assembly {
      // Retrieve the current value of the reentrancy guard slot.
      let _reentrancyGuard := tload(_REENTRANCY_GUARD_SLOT)

      // Ensure that the reentrancy guard is not already set.
      // Equivalent to `if (_reentrancyGuard != _NOT_ENTERED) revert NoReentrantCalls();`
      if _reentrancyGuard {
        mstore(0, NoReentrantCalls_ErrorSelector)
        revert(0x1c, 0x04)
      }

      // Set the reentrancy guard.
      // Equivalent to `_reentrancyGuard = _ENTERED;`
      tstore(_REENTRANCY_GUARD_SLOT, _ENTERED)
    }
  }

  /**
   * @dev Internal function to unset the reentrancy guard sentinel value.
   */
  function _clearReentrancyGuard() internal {
    assembly {
      // Equivalent to `_reentrancyGuard = _NOT_ENTERED;`
      tstore(_REENTRANCY_GUARD_SLOT, _NOT_ENTERED)
    }
  }

  /**
   * @dev Internal view function to ensure that a sentinel value for the
   *         reentrancy guard is not currently set.
   */
  function _assertNonReentrant() internal view {
    assembly {
      // Ensure that the reentrancy guard is not currently set.
      // Equivalent to `if (_reentrancyGuard != _NOT_ENTERED) revert NoReentrantCalls();`
      if tload(_REENTRANCY_GUARD_SLOT) {
        mstore(0, NoReentrantCalls_ErrorSelector)
        revert(0x1c, 0x04)
      }
    }
  }
}

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

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

import { HooksConfig } from '../types/HooksConfig.sol';

struct MarketParameters {
  address asset;
  uint8 decimals;
  bytes32 packedNameWord0;
  bytes32 packedNameWord1;
  bytes32 packedSymbolWord0;
  bytes32 packedSymbolWord1;
  address borrower;
  address feeRecipient;
  address sentinel;
  uint128 maxTotalSupply;
  uint16 protocolFeeBips;
  uint16 annualInterestBips;
  uint16 delinquencyFeeBips;
  uint32 withdrawalBatchDuration;
  uint16 reserveRatioBips;
  uint32 delinquencyGracePeriod;
  address archController;
  address sphereXEngine;
  HooksConfig hooks;
}

struct DeployMarketInputs {
  address asset;
  string namePrefix;
  string symbolPrefix;
  uint128 maxTotalSupply;
  uint16 annualInterestBips;
  uint16 delinquencyFeeBips;
  uint32 withdrawalBatchDuration;
  uint16 reserveRatioBips;
  uint32 delinquencyGracePeriod;
  HooksConfig hooks;
}

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;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import '../types/HooksConfig.sol';
import '../libraries/MarketState.sol';
import '../interfaces/WildcatStructsAndEnums.sol';

abstract contract IHooks {
  error CallerNotFactory();

  address public immutable factory;

  constructor() {
    factory = msg.sender;
  }

  /// @dev Returns the version string of the hooks contract.
  ///      Used to determine what the contract does and how `extraData` is interpreted.
  function version() external view virtual returns (string memory);

  /// @dev Returns the HooksDeploymentConfig type which contains the sets
  ///      of optional and required hooks that this contract implements.
  function config() external view virtual returns (HooksDeploymentConfig);

  function onCreateMarket(
    address deployer,
    address marketAddress,
    DeployMarketInputs calldata parameters,
    bytes calldata extraData
  ) external returns (HooksConfig) {
    if (msg.sender != factory) revert CallerNotFactory();
    return _onCreateMarket(deployer, marketAddress, parameters, extraData);
  }

  function _onCreateMarket(
    address deployer,
    address marketAddress,
    DeployMarketInputs calldata parameters,
    bytes calldata extraData
  ) internal virtual returns (HooksConfig);

  function onDeposit(
    address lender,
    uint256 scaledAmount,
    MarketState calldata intermediateState,
    bytes calldata extraData
  ) external virtual;

  function onQueueWithdrawal(
    address lender,
    uint32 expiry,
    uint scaledAmount,
    MarketState calldata intermediateState,
    bytes calldata extraData
  ) external virtual;

  function onExecuteWithdrawal(
    address lender,
    uint128 normalizedAmountWithdrawn,
    MarketState calldata intermediateState,
    bytes calldata extraData
  ) external virtual;

  function onTransfer(
    address caller,
    address from,
    address to,
    uint scaledAmount,
    MarketState calldata intermediateState,
    bytes calldata extraData
  ) external virtual;

  function onBorrow(
    uint normalizedAmount,
    MarketState calldata intermediateState,
    bytes calldata extraData
  ) external virtual;

  function onRepay(
    uint normalizedAmount,
    MarketState calldata intermediateState,
    bytes calldata extraData
  ) external virtual;

  function onCloseMarket(
    MarketState calldata intermediateState,
    bytes calldata extraData
  ) external virtual;

  function onNukeFromOrbit(
    address lender,
    MarketState calldata intermediateState,
    bytes calldata extraData
  ) external virtual;

  function onSetMaxTotalSupply(
    uint256 maxTotalSupply,
    MarketState calldata intermediateState,
    bytes calldata extraData
  ) external virtual;

  function onSetAnnualInterestAndReserveRatioBips(
    uint16 annualInterestBips,
    uint16 reserveRatioBips,
    MarketState calldata intermediateState,
    bytes calldata extraData
  ) external virtual returns (uint16 updatedAnnualInterestBips, uint16 updatedReserveRatioBips);

  function onSetProtocolFeeBips(
    uint16 protocolFeeBips,
    MarketState memory intermediateState,
    bytes calldata extraData
  ) external virtual;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import './access/IHooks.sol';
import './interfaces/WildcatStructsAndEnums.sol';

struct HooksTemplate {
  /// @dev Asset used to pay origination fee
  address originationFeeAsset;
  /// @dev Amount of `originationFeeAsset` paid to deploy a new market using
  ///      an instance of this template.
  uint80 originationFeeAmount;
  /// @dev Basis points paid on interest for markets deployed using hooks
  ///      based on this template
  uint16 protocolFeeBips;
  /// @dev Whether the template exists
  bool exists;
  /// @dev Whether the template is enabled
  bool enabled;
  /// @dev Index of the template address in the array of hooks templates
  uint24 index;
  /// @dev Address to pay origination and interest fees
  address feeRecipient;
  /// @dev Name of the template
  string name;
}

interface IHooksFactoryEventsAndErrors {
  error FeeMismatch();
  error NotApprovedBorrower();
  error HooksTemplateNotFound();
  error HooksTemplateNotAvailable();
  error HooksTemplateAlreadyExists();
  error DeploymentFailed();
  error HooksInstanceNotFound();
  error CallerNotArchControllerOwner();
  error InvalidFeeConfiguration();
  error SaltDoesNotContainSender();
  error MarketAlreadyExists();
  error HooksInstanceAlreadyExists();
  error NameOrSymbolTooLong();
  error AssetBlacklisted();
  error SetProtocolFeeBipsFailed();

  event HooksInstanceDeployed(address hooksInstance, address hooksTemplate);
  event HooksTemplateAdded(
    address hooksTemplate,
    string name,
    address feeRecipient,
    address originationFeeAsset,
    uint80 originationFeeAmount,
    uint16 protocolFeeBips
  );
  event HooksTemplateDisabled(address hooksTemplate);
  event HooksTemplateFeesUpdated(
    address hooksTemplate,
    address feeRecipient,
    address originationFeeAsset,
    uint80 originationFeeAmount,
    uint16 protocolFeeBips
  );

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

interface IHooksFactory is IHooksFactoryEventsAndErrors {
  function archController() external view returns (address);

  function sanctionsSentinel() external view returns (address);

  function marketInitCodeStorage() external view returns (address);

  function marketInitCodeHash() external view returns (uint256);

  /// @dev Set-up function to register the factory as a controller with the arch-controller.
  ///      This enables the factory to register new markets.
  function registerWithArchController() external;

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

  // ========================================================================== //
  //                               Hooks Templates                              //
  // ========================================================================== //

  /// @dev Add a hooks template that stores the initcode for the template.
  ///
  ///      On success:
  ///      - Emits `HooksTemplateAdded` on success.
  ///      - Adds the template to the list of templates.
  ///      - Creates `HooksTemplate` struct with the given parameters mapped to the template address.
  ///
  ///      Reverts if:
  ///      - The caller is not the owner of the arch-controller.
  ///      - The template already exists.
  ///      - The fee settings are invalid.
  function addHooksTemplate(
    address hooksTemplate,
    string calldata name,
    address feeRecipient,
    address originationFeeAsset,
    uint80 originationFeeAmount,
    uint16 protocolFeeBips
  ) external;

  /// @dev Update the fees for a hooks template.
  ///
  ///      On success:
  ///      - Emits `HooksTemplateFeesUpdated` on success.
  ///      - Updates the fees for the `HooksTemplate` struct mapped to the template address.
  ///
  ///      Reverts if:
  ///      - The caller is not the owner of the arch-controller.
  ///      - The template does not exist.
  ///      - The fee settings are invalid.
  function updateHooksTemplateFees(
    address hooksTemplate,
    address feeRecipient,
    address originationFeeAsset,
    uint80 originationFeeAmount,
    uint16 protocolFeeBips
  ) external;

  /// @dev Disable a hooks template.
  ///
  ///      On success:
  ///      - Emits `HooksTemplateDisabled` on success.
  ///      - Disables the `HooksTemplate` struct mapped to the template address.
  ///
  ///      Reverts if:
  ///      - The caller is not the owner of the arch-controller.
  ///      - The template does not exist.
  function disableHooksTemplate(address hooksTemplate) external;

  /// @dev Get the name and fee configuration for an approved hooks template.
  function getHooksTemplateDetails(
    address hooksTemplate
  ) external view returns (HooksTemplate memory);

  /// @dev Check if a hooks template is approved.
  function isHooksTemplate(address hooksTemplate) external view returns (bool);

  /// @dev Get the list of approved hooks templates.
  function getHooksTemplates() external view returns (address[] memory);

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

  function getHooksTemplatesCount() external view returns (uint256);

  function getMarketsForHooksTemplate(
    address hooksTemplate
  ) external view returns (address[] memory);

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

  function getMarketsForHooksTemplateCount(address hooksTemplate) external view returns (uint256);

  // ========================================================================== //
  //                               Hooks Instances                              //
  // ========================================================================== //

  /// @dev Deploy a hooks instance for an approved template with constructor args.
  ///
  ///      On success:
  ///      - Emits `HooksInstanceDeployed`.
  ///      - Deploys a new hooks instance with the given templates and constructor args.
  ///      - Maps the hooks instance to the template address.
  ///
  ///      Reverts if:
  ///      - The caller is not an approved borrower.
  ///      - The template does not exist.
  ///      - The template is not enabled.
  ///      - The deployment fails.
  function deployHooksInstance(
    address hooksTemplate,
    bytes calldata constructorArgs
  ) external returns (address hooksDeployment);

  function getHooksInstancesForBorrower(address borrower) external view returns (address[] memory);

  function getHooksInstancesCountForBorrower(address borrower) external view returns (uint256);

  /// @dev Check if a hooks instance was deployed by the factory.
  function isHooksInstance(address hooks) external view returns (bool);

  /// @dev Get the template that was used to deploy a hooks instance.
  function getHooksTemplateForInstance(address hooks) external view returns (address);

  // ========================================================================== //
  //                                   Markets                                  //
  // ========================================================================== //
  function getMarketsForHooksInstance(
    address hooksInstance
  ) external view returns (address[] memory);

  function getMarketsForHooksInstance(
    address hooksInstance,
    uint256 start,
    uint256 len
  ) external view returns (address[] memory arr);

  function getMarketsForHooksInstanceCount(address hooksInstance) external view returns (uint256);

  /// @dev Get the temporarily stored market parameters for a market that is
  ///      currently being deployed.
  function getMarketParameters() external view returns (MarketParameters memory parameters);

  /// @dev Deploy a market with an existing hooks deployment (in `parameters.hooks`)
  ///
  ///      On success:
  ///      - Pays the origination fee (if applicable).
  ///      - Calls `onDeployMarket` on the hooks contract.
  ///      - Deploys a new market with the given parameters.
  ///      - Emits `MarketDeployed`.
  ///
  ///      Reverts if:
  ///      - The caller is not an approved borrower.
  ///      - The hooks instance does not exist.
  ///      - Payment of origination fee fails.
  ///      - The deployment fails.
  ///      - The call to `onDeployMarket` fails.
  ///      - `originationFeeAsset` does not match the hook template's
  ///      - `originationFeeAmount` does not match the hook template's
  function deployMarket(
    DeployMarketInputs calldata parameters,
    bytes calldata hooksData,
    bytes32 salt,
    address originationFeeAsset,
    uint256 originationFeeAmount
  ) external returns (address market);

  /// @dev Deploy a hooks instance for an approved template,then deploy a new market with that
  ///      instance as its hooks contract.
  ///      Will call `onCreateMarket` on `parameters.hooks`.
  function deployMarketAndHooks(
    address hooksTemplate,
    bytes calldata hooksConstructorArgs,
    DeployMarketInputs calldata parameters,
    bytes calldata hooksData,
    bytes32 salt,
    address originationFeeAsset,
    uint256 originationFeeAmount
  ) external returns (address market, address hooks);

  function computeMarketAddress(bytes32 salt) external view returns (address);

  function pushProtocolFeeBipsUpdates(
    address hooksTemplate,
    uint marketStartIndex,
    uint marketEndIndex
  ) external;

  function pushProtocolFeeBipsUpdates(address hooksTemplate) external;
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.25;
import { Panic_ErrorSelector, Panic_ErrorCodePointer, Panic_InvalidStorageByteArray, Error_SelectorPointer, Panic_ErrorLength } from '../libraries/Errors.sol';

type TransientBytesArray is uint256;

using LibTransientBytesArray for TransientBytesArray global;

library LibTransientBytesArray {
  /**
   * @dev Decode a dynamic bytes array from transient storage.
   * @param transientSlot Slot for the dynamic bytes array in transient storage
   * @param memoryPointer Pointer to the memory location to write the decoded array to
   * @return endPointer Pointer to the end of the decoded array
   */
  function readToPointer(
    TransientBytesArray transientSlot,
    uint256 memoryPointer
  ) internal view returns (uint256 endPointer) {
    assembly {
      function extractByteArrayLength(data) -> length {
        length := div(data, 2)
        let outOfPlaceEncoding := and(data, 1)
        if iszero(outOfPlaceEncoding) {
          length := and(length, 0x7f)
        }

        if eq(outOfPlaceEncoding, lt(length, 32)) {
          // Store the Panic error signature.
          mstore(0, Panic_ErrorSelector)
          // Store the arithmetic (0x11) panic code.
          mstore(Panic_ErrorCodePointer, Panic_InvalidStorageByteArray)
          // revert(abi.encodeWithSignature("Panic(uint256)", 0x22))
          revert(Error_SelectorPointer, Panic_ErrorLength)
        }
      }
      let slotValue := tload(transientSlot)
      let length := extractByteArrayLength(slotValue)
      mstore(memoryPointer, length)
      memoryPointer := add(memoryPointer, 0x20)
      switch and(slotValue, 1)
      case 0 {
        // short byte array
        let value := and(slotValue, not(0xff))
        mstore(memoryPointer, value)
        endPointer := add(memoryPointer, 0x20)
      }
      case 1 {
        // long byte array
        mstore(0, transientSlot)
        // Calculate the slot of the data portion of the array
        let dataTSlot := keccak256(0, 0x20)
        let i := 0
        for {

        } lt(i, length) {
          i := add(i, 0x20)
        } {
          mstore(add(memoryPointer, i), tload(dataTSlot))
          dataTSlot := add(dataTSlot, 1)
        }
        endPointer := add(memoryPointer, i)
      }
    }
  }

  function read(TransientBytesArray transientSlot) internal view returns (bytes memory data) {
    uint256 dataPointer;
    assembly {
      dataPointer := mload(0x40)
      data := dataPointer
      mstore(data, 0)
    }
    uint256 endPointer = readToPointer(transientSlot, dataPointer);
    assembly {
      mstore(0x40, endPointer)
    }
  }

  /**
   * @dev Write a dynamic bytes array to transient storage.
   * @param transientSlot Slot for the dynamic bytes array in transient storage
   * @param memoryPointer Pointer to the memory location of the array to write
   */
  function write(TransientBytesArray transientSlot, bytes memory memoryPointer) internal {
    assembly {
      let length := mload(memoryPointer)
      memoryPointer := add(memoryPointer, 0x20)
      switch lt(length, 32)
      case 0 {
        // For long byte arrays, the length slot holds (length * 2 + 1)
        tstore(transientSlot, add(1, mul(2, length)))
        // Calculate the slot of the data portion of the array
        mstore(0, transientSlot)
        let dataTSlot := keccak256(0, 0x20)
        let i := 0
        for {

        } lt(i, length) {
          i := add(i, 0x20)
        } {
          tstore(dataTSlot, mload(add(memoryPointer, i)))
          dataTSlot := add(dataTSlot, 1)
        }
      }
      case 1 {
        // For short byte arrays, the first 31 bytes are the data and the last byte is (length * 2).
        let lengthByte := mul(2, length)
        let data := mload(memoryPointer)
        tstore(transientSlot, or(data, lengthByte))
      }
    }
  }

  function setEmpty(TransientBytesArray transientSlot) internal {
    assembly {
      tstore(transientSlot, 0)
    }
  }
}

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

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20;

import { LibBit } from 'solady/utils/LibBit.sol';

using LibBit for uint256;

uint256 constant OnlyFullWordMask = 0xffffffe0;

function bytes32ToString(bytes32 value) pure returns (string memory str) {
  uint256 size;
  unchecked {
    uint256 sizeInBits = 255 - uint256(value).ffs();
    size = (sizeInBits + 7) / 8;
  }
  assembly {
    str := mload(0x40)
    mstore(0x40, add(str, 0x40))
    mstore(str, size)
    mstore(add(str, 0x20), value)
  }
}

function queryStringOrBytes32AsString(
  address target,
  uint256 leftPaddedFunctionSelector,
  uint256 leftPaddedGenericErrorSelector
) view returns (string memory str) {
  bool isBytes32;
  assembly {
    mstore(0, leftPaddedFunctionSelector)
    let status := staticcall(gas(), target, 0x1c, 0x04, 0, 0)
    isBytes32 := eq(returndatasize(), 0x20)
    // If call fails or function returns invalid data, revert.
    // Strings are always right padded to full words - if the returndata
    // is not 32 bytes (string encoded as bytes32) or >95 bytes (minimum abi
    // encoded string) it is an invalid string.
    if or(iszero(status), iszero(or(isBytes32, gt(returndatasize(), 0x5f)))) {
      // Check if call failed
      if iszero(status) {
        // Check if any revert data was given
        if returndatasize() {
          returndatacopy(0, 0, returndatasize())
          revert(0, returndatasize())
        }
        // If not, throw a generic error
        mstore(0, leftPaddedGenericErrorSelector)
        revert(0x1c, 0x04)
      }
      // If the returndata is the wrong size, `revert InvalidReturnDataString()`
      mstore(0, 0x4cb9c000)
      revert(0x1c, 0x04)
    }
  }
  if (isBytes32) {
    bytes32 value;
    assembly {
      returndatacopy(0x00, 0x00, 0x20)
      value := mload(0)
    }
    str = bytes32ToString(value);
  } else {
    // If returndata is a string, copy the length and value
    assembly {
      str := mload(0x40)
      // Get allocation size for the string including the length and data.
      // Rounding down returndatasize to nearest word because the returndata
      // has an extra offset word.
      let allocSize := and(sub(returndatasize(), 1), OnlyFullWordMask)
      mstore(0x40, add(str, allocSize))
      // Copy returndata after the offset
      returndatacopy(str, 0x20, sub(returndatasize(), 0x20))
      let length := mload(str)
      // Check if the length matches the returndatasize.
      // The encoded string should have the string length rounded up to the nearest word
      // as well as two words for length and offset.
      let expectedReturndataSize := add(allocSize, 0x20)
      if xor(returndatasize(), expectedReturndataSize) {
        mstore(0, 0x4cb9c000)
        revert(0x1c, 0x04)
      }
    }
  }
}

File 13 of 21 : Errors.sol
// SPDX-License-Identifier: MIT
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)
  }
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20;

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;
  // Fee charged to borrowers as a fraction of the annual interest rate
  uint16 protocolFeeBips;
  // 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 {
  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 15 of 21 : HooksConfig.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20;

import '../access/IHooks.sol';
import '../libraries/MarketState.sol';

type HooksConfig is uint256;

HooksConfig constant EmptyHooksConfig = HooksConfig.wrap(0);

using LibHooksConfig for HooksConfig global;
using LibHooksConfig for HooksDeploymentConfig global;

// Type that contains only the flags for a specific hooks contract, with one
// set of flags for optional hooks and one set of flags for required hooks.
type HooksDeploymentConfig is uint256;

function encodeHooksDeploymentConfig(
  HooksConfig optionalFlags,
  HooksConfig requiredFlags
) pure returns (HooksDeploymentConfig flags) {
  assembly {
    let cleanedOptionalFlags := and(0xffff, shr(0x50, optionalFlags))
    let cleanedRequiredFlags := and(0xffff0000, shr(0x40, requiredFlags))
    flags := or(cleanedOptionalFlags, cleanedRequiredFlags)
  }
}

// --------------------- Bits after hook activation flag -------------------- //

// Offsets are from the right

uint256 constant Bit_Enabled_Deposit = 95;
uint256 constant Bit_Enabled_QueueWithdrawal = 94;
uint256 constant Bit_Enabled_ExecuteWithdrawal = 93;
uint256 constant Bit_Enabled_Transfer = 92;
uint256 constant Bit_Enabled_Borrow = 91;
uint256 constant Bit_Enabled_Repay = 90;
uint256 constant Bit_Enabled_CloseMarket = 89;
uint256 constant Bit_Enabled_NukeFromOrbit = 88;
uint256 constant Bit_Enabled_SetMaxTotalSupply = 87;
uint256 constant Bit_Enabled_SetAnnualInterestAndReserveRatioBips = 86;
uint256 constant Bit_Enabled_SetProtocolFeeBips = 85;

uint256 constant MarketStateSize = 0x01c0;

function encodeHooksConfig(
  address hooksAddress,
  bool useOnDeposit,
  bool useOnQueueWithdrawal,
  bool useOnExecuteWithdrawal,
  bool useOnTransfer,
  bool useOnBorrow,
  bool useOnRepay,
  bool useOnCloseMarket,
  bool useOnNukeFromOrbit,
  bool useOnSetMaxTotalSupply,
  bool useOnSetAnnualInterestAndReserveRatioBips,
  bool useOnSetProtocolFeeBips
) pure returns (HooksConfig hooks) {
  assembly {
    hooks := shl(96, hooksAddress)
    hooks := or(hooks, shl(Bit_Enabled_Deposit, useOnDeposit))
    hooks := or(hooks, shl(Bit_Enabled_QueueWithdrawal, useOnQueueWithdrawal))
    hooks := or(hooks, shl(Bit_Enabled_ExecuteWithdrawal, useOnExecuteWithdrawal))
    hooks := or(hooks, shl(Bit_Enabled_Transfer, useOnTransfer))
    hooks := or(hooks, shl(Bit_Enabled_Borrow, useOnBorrow))
    hooks := or(hooks, shl(Bit_Enabled_Repay, useOnRepay))
    hooks := or(hooks, shl(Bit_Enabled_CloseMarket, useOnCloseMarket))
    hooks := or(hooks, shl(Bit_Enabled_NukeFromOrbit, useOnNukeFromOrbit))
    hooks := or(hooks, shl(Bit_Enabled_SetMaxTotalSupply, useOnSetMaxTotalSupply))
    hooks := or(
      hooks,
      shl(
        Bit_Enabled_SetAnnualInterestAndReserveRatioBips,
        useOnSetAnnualInterestAndReserveRatioBips
      )
    )
    hooks := or(hooks, shl(Bit_Enabled_SetProtocolFeeBips, useOnSetProtocolFeeBips))
  }
}

library LibHooksConfig {
  function setHooksAddress(
    HooksConfig hooks,
    address _hooksAddress
  ) internal pure returns (HooksConfig updatedHooks) {
    assembly {
      // Shift twice to clear the address
      updatedHooks := shr(160, shl(160, hooks))
      // Set the new address
      updatedHooks := or(updatedHooks, shl(96, _hooksAddress))
    }
  }

  /**
   * @dev Create a merged HooksConfig with the shared flags of `a` and `b`
   *      and the address of `a`.
   */
  function mergeSharedFlags(
    HooksConfig a,
    HooksConfig b
  ) internal pure returns (HooksConfig merged) {
    assembly {
      let addressA := shl(0x60, shr(0x60, a))
      let flagsA := shl(0xa0, a)
      let flagsB := shl(0xa0, b)
      let mergedFlags := shr(0xa0, and(flagsA, flagsB))
      merged := or(addressA, mergedFlags)
    }
  }

  /**
   * @dev Create a merged HooksConfig with the shared flags of `a` and `b`
   *      and the address of `a`.
   */
  function mergeAllFlags(HooksConfig a, HooksConfig b) internal pure returns (HooksConfig merged) {
    assembly {
      let addressA := shl(0x60, shr(0x60, a))
      let flagsA := shl(0xa0, a)
      let flagsB := shl(0xa0, b)
      let mergedFlags := shr(0xa0, or(flagsA, flagsB))
      merged := or(addressA, mergedFlags)
    }
  }

  function mergeFlags(
    HooksConfig config,
    HooksDeploymentConfig flags
  ) internal pure returns (HooksConfig merged) {
    assembly {
      let _hooksAddress := shl(96, shr(96, config))
      // Position flags at the end of the word
      let configFlags := shr(0x50, config)
      // Optional flags are already in the right position, required flags must be
      // shifted to align with the other flags. The leading and trailing bits for all 3
      // words will be masked out at the end
      let _optionalFlags := flags
      let _requiredFlags := shr(0x10, flags)
      let mergedFlags := and(0xffff, or(and(configFlags, _optionalFlags), _requiredFlags))

      merged := or(_hooksAddress, shl(0x50, mergedFlags))
    }
  }

  function optionalFlags(HooksDeploymentConfig flags) internal pure returns (HooksConfig config) {
    assembly {
      config := shl(0x50, and(flags, 0xffff))
    }
  }

  function requiredFlags(HooksDeploymentConfig flags) internal pure returns (HooksConfig config) {
    assembly {
      config := shl(0x40, and(flags, 0xffff0000))
    }
  }

  // ========================================================================== //
  //                              Parameter Readers                             //
  // ========================================================================== //

  function readFlag(HooksConfig hooks, uint256 bitsAfter) internal pure returns (bool flagged) {
    assembly {
      flagged := and(shr(bitsAfter, hooks), 1)
    }
  }

  function setFlag(
    HooksConfig hooks,
    uint256 bitsAfter
  ) internal pure returns (HooksConfig updatedHooks) {
    assembly {
      updatedHooks := or(hooks, shl(bitsAfter, 1))
    }
  }

  function clearFlag(
    HooksConfig hooks,
    uint256 bitsAfter
  ) internal pure returns (HooksConfig updatedHooks) {
    assembly {
      updatedHooks := and(hooks, not(shl(bitsAfter, 1)))
    }
  }

  /// @dev Address of the hooks contract
  function hooksAddress(HooksConfig hooks) internal pure returns (address _hooks) {
    assembly {
      _hooks := shr(96, hooks)
    }
  }

  /// @dev Whether to call hook contract for deposit
  function useOnDeposit(HooksConfig hooks) internal pure returns (bool) {
    return hooks.readFlag(Bit_Enabled_Deposit);
  }

  /// @dev Whether to call hook contract for queueWithdrawal
  function useOnQueueWithdrawal(HooksConfig hooks) internal pure returns (bool) {
    return hooks.readFlag(Bit_Enabled_QueueWithdrawal);
  }

  /// @dev Whether to call hook contract for executeWithdrawal
  function useOnExecuteWithdrawal(HooksConfig hooks) internal pure returns (bool) {
    return hooks.readFlag(Bit_Enabled_ExecuteWithdrawal);
  }

  /// @dev Whether to call hook contract for transfer
  function useOnTransfer(HooksConfig hooks) internal pure returns (bool) {
    return hooks.readFlag(Bit_Enabled_Transfer);
  }

  /// @dev Whether to call hook contract for borrow
  function useOnBorrow(HooksConfig hooks) internal pure returns (bool) {
    return hooks.readFlag(Bit_Enabled_Borrow);
  }

  /// @dev Whether to call hook contract for repay
  function useOnRepay(HooksConfig hooks) internal pure returns (bool) {
    return hooks.readFlag(Bit_Enabled_Repay);
  }

  /// @dev Whether to call hook contract for closeMarket
  function useOnCloseMarket(HooksConfig hooks) internal pure returns (bool) {
    return hooks.readFlag(Bit_Enabled_CloseMarket);
  }

  /// @dev Whether to call hook contract when account sanctioned
  function useOnNukeFromOrbit(HooksConfig hooks) internal pure returns (bool) {
    return hooks.readFlag(Bit_Enabled_NukeFromOrbit);
  }

  /// @dev Whether to call hook contract for setMaxTotalSupply
  function useOnSetMaxTotalSupply(HooksConfig hooks) internal pure returns (bool) {
    return hooks.readFlag(Bit_Enabled_SetMaxTotalSupply);
  }

  /// @dev Whether to call hook contract for setAnnualInterestAndReserveRatioBips
  function useOnSetAnnualInterestAndReserveRatioBips(
    HooksConfig hooks
  ) internal pure returns (bool) {
    return hooks.readFlag(Bit_Enabled_SetAnnualInterestAndReserveRatioBips);
  }

  /// @dev Whether to call hook contract for setProtocolFeeBips
  function useOnSetProtocolFeeBips(HooksConfig hooks) internal pure returns (bool) {
    return hooks.readFlag(Bit_Enabled_SetProtocolFeeBips);
  }

  // ========================================================================== //
  //                              Hook for deposit                              //
  // ========================================================================== //

  uint256 internal constant DepositCalldataSize = 0x24;
  // Size of lender + scaledAmount + state + extraData.offset + extraData.length
  uint256 internal constant DepositHook_Base_Size = 0x0244;
  uint256 internal constant DepositHook_ScaledAmount_Offset = 0x20;
  uint256 internal constant DepositHook_State_Offset = 0x40;
  uint256 internal constant DepositHook_ExtraData_Head_Offset = 0x200;
  uint256 internal constant DepositHook_ExtraData_Length_Offset = 0x0220;
  uint256 internal constant DepositHook_ExtraData_TailOffset = 0x0240;

  function onDeposit(
    HooksConfig self,
    address lender,
    uint256 scaledAmount,
    MarketState memory state
  ) internal {
    address target = self.hooksAddress();
    uint32 onDepositSelector = uint32(IHooks.onDeposit.selector);
    if (self.useOnDeposit()) {
      assembly {
        let extraCalldataBytes := sub(calldatasize(), DepositCalldataSize)
        let cdPointer := mload(0x40)
        let headPointer := add(cdPointer, 0x20)
        // Write selector for `onDeposit`
        mstore(cdPointer, onDepositSelector)
        // Write `lender` to hook calldata
        mstore(headPointer, lender)
        // Write `scaledAmount` to hook calldata
        mstore(add(headPointer, DepositHook_ScaledAmount_Offset), scaledAmount)
        // Copy market state to hook calldata
        mcopy(add(headPointer, DepositHook_State_Offset), state, MarketStateSize)
        // Write bytes offset for `extraData`
        mstore(
          add(headPointer, DepositHook_ExtraData_Head_Offset),
          DepositHook_ExtraData_Length_Offset
        )
        // Write length for `extraData`
        mstore(add(headPointer, DepositHook_ExtraData_Length_Offset), extraCalldataBytes)
        // Copy `extraData` from end of calldata to hook calldata
        calldatacopy(
          add(headPointer, DepositHook_ExtraData_TailOffset),
          DepositCalldataSize,
          extraCalldataBytes
        )

        let size := add(DepositHook_Base_Size, extraCalldataBytes)

        if iszero(call(gas(), target, 0, add(cdPointer, 0x1c), size, 0, 0)) {
          returndatacopy(0, 0, returndatasize())
          revert(0, returndatasize())
        }
      }
    }
  }

  // ========================================================================== //
  //                          Hook for queueWithdrawal                          //
  // ========================================================================== //

  // Size of lender + scaledAmount + state + extraData.offset + extraData.length
  uint256 internal constant QueueWithdrawalHook_Base_Size = 0x0264;
  uint256 internal constant QueueWithdrawalHook_Expiry_Offset = 0x20;
  uint256 internal constant QueueWithdrawalHook_ScaledAmount_Offset = 0x40;
  uint256 internal constant QueueWithdrawalHook_State_Offset = 0x60;
  uint256 internal constant QueueWithdrawalHook_ExtraData_Head_Offset = 0x220;
  uint256 internal constant QueueWithdrawalHook_ExtraData_Length_Offset = 0x0240;
  uint256 internal constant QueueWithdrawalHook_ExtraData_TailOffset = 0x0260;

  function onQueueWithdrawal(
    HooksConfig self,
    address lender,
    uint32 expiry,
    uint256 scaledAmount,
    MarketState memory state,
    uint256 baseCalldataSize
  ) internal {
    address target = self.hooksAddress();
    uint32 onQueueWithdrawalSelector = uint32(IHooks.onQueueWithdrawal.selector);
    if (self.useOnQueueWithdrawal()) {
      assembly {
        let extraCalldataBytes := sub(calldatasize(), baseCalldataSize)
        let cdPointer := mload(0x40)
        let headPointer := add(cdPointer, 0x20)
        // Write selector for `onQueueWithdrawal`
        mstore(cdPointer, onQueueWithdrawalSelector)
        // Write `lender` to hook calldata
        mstore(headPointer, lender)
        // Write `expiry` to hook calldata
        mstore(add(headPointer, QueueWithdrawalHook_Expiry_Offset), expiry)
        // Write `scaledAmount` to hook calldata
        mstore(add(headPointer, QueueWithdrawalHook_ScaledAmount_Offset), scaledAmount)
        // Copy market state to hook calldata
        mcopy(add(headPointer, QueueWithdrawalHook_State_Offset), state, MarketStateSize)
        // Write bytes offset for `extraData`
        mstore(
          add(headPointer, QueueWithdrawalHook_ExtraData_Head_Offset),
          QueueWithdrawalHook_ExtraData_Length_Offset
        )
        // Write length for `extraData`
        mstore(add(headPointer, QueueWithdrawalHook_ExtraData_Length_Offset), extraCalldataBytes)
        // Copy `extraData` from end of calldata to hook calldata
        calldatacopy(
          add(headPointer, QueueWithdrawalHook_ExtraData_TailOffset),
          baseCalldataSize,
          extraCalldataBytes
        )

        let size := add(QueueWithdrawalHook_Base_Size, extraCalldataBytes)

        if iszero(call(gas(), target, 0, add(cdPointer, 0x1c), size, 0, 0)) {
          returndatacopy(0, 0, returndatasize())
          revert(0, returndatasize())
        }
      }
    }
  }

  // ========================================================================== //
  //                         Hook for executeWithdrawal                         //
  // ========================================================================== //

  // Size of lender + scaledAmount + state + extraData.offset + extraData.length
  uint256 internal constant ExecuteWithdrawalHook_Base_Size = 0x0244;
  uint256 internal constant ExecuteWithdrawalHook_ScaledAmount_Offset = 0x20;
  uint256 internal constant ExecuteWithdrawalHook_State_Offset = 0x40;
  uint256 internal constant ExecuteWithdrawalHook_ExtraData_Head_Offset = 0x0200;
  uint256 internal constant ExecuteWithdrawalHook_ExtraData_Length_Offset = 0x0220;
  uint256 internal constant ExecuteWithdrawalHook_ExtraData_TailOffset = 0x0240;

  function onExecuteWithdrawal(
    HooksConfig self,
    address lender,
    uint256 scaledAmount,
    MarketState memory state,
    uint256 baseCalldataSize
  ) internal {
    address target = self.hooksAddress();
    uint32 onExecuteWithdrawalSelector = uint32(IHooks.onExecuteWithdrawal.selector);
    if (self.useOnExecuteWithdrawal()) {
      assembly {
        let extraCalldataBytes := sub(calldatasize(), baseCalldataSize)
        let cdPointer := mload(0x40)
        let headPointer := add(cdPointer, 0x20)
        // Write selector for `onExecuteWithdrawal`
        mstore(cdPointer, onExecuteWithdrawalSelector)
        // Write `lender` to hook calldata
        mstore(headPointer, lender)
        // Write `scaledAmount` to hook calldata
        mstore(add(headPointer, ExecuteWithdrawalHook_ScaledAmount_Offset), scaledAmount)
        // Copy market state to hook calldata
        mcopy(add(headPointer, ExecuteWithdrawalHook_State_Offset), state, MarketStateSize)
        // Write bytes offset for `extraData`
        mstore(
          add(headPointer, ExecuteWithdrawalHook_ExtraData_Head_Offset),
          ExecuteWithdrawalHook_ExtraData_Length_Offset
        )
        // Write length for `extraData`
        mstore(add(headPointer, ExecuteWithdrawalHook_ExtraData_Length_Offset), extraCalldataBytes)
        // Copy `extraData` from end of calldata to hook calldata
        calldatacopy(
          add(headPointer, ExecuteWithdrawalHook_ExtraData_TailOffset),
          baseCalldataSize,
          extraCalldataBytes
        )

        let size := add(ExecuteWithdrawalHook_Base_Size, extraCalldataBytes)

        if iszero(call(gas(), target, 0, add(cdPointer, 0x1c), size, 0, 0)) {
          returndatacopy(0, 0, returndatasize())
          revert(0, returndatasize())
        }
      }
    }
  }

  // ========================================================================== //
  //                              Hook for transfer                             //
  // ========================================================================== //

  // Size of caller + from + to + scaledAmount + state + extraData.offset + extraData.length
  uint256 internal constant TransferHook_Base_Size = 0x0284;
  uint256 internal constant TransferHook_From_Offset = 0x20;
  uint256 internal constant TransferHook_To_Offset = 0x40;
  uint256 internal constant TransferHook_ScaledAmount_Offset = 0x60;
  uint256 internal constant TransferHook_State_Offset = 0x80;
  uint256 internal constant TransferHook_ExtraData_Head_Offset = 0x240;
  uint256 internal constant TransferHook_ExtraData_Length_Offset = 0x0260;
  uint256 internal constant TransferHook_ExtraData_TailOffset = 0x0280;

  function onTransfer(
    HooksConfig self,
    address from,
    address to,
    uint256 scaledAmount,
    MarketState memory state,
    uint256 baseCalldataSize
  ) internal {
    address target = self.hooksAddress();
    uint32 onTransferSelector = uint32(IHooks.onTransfer.selector);
    if (self.useOnTransfer()) {
      assembly {
        let extraCalldataBytes := sub(calldatasize(), baseCalldataSize)
        let cdPointer := mload(0x40)
        let headPointer := add(cdPointer, 0x20)
        // Write selector for `onTransfer`
        mstore(cdPointer, onTransferSelector)
        // Write `caller` to hook calldata
        mstore(headPointer, caller())
        // Write `from` to hook calldata
        mstore(add(headPointer, TransferHook_From_Offset), from)
        // Write `to` to hook calldata
        mstore(add(headPointer, TransferHook_To_Offset), to)
        // Write `scaledAmount` to hook calldata
        mstore(add(headPointer, TransferHook_ScaledAmount_Offset), scaledAmount)
        // Copy market state to hook calldata
        mcopy(add(headPointer, TransferHook_State_Offset), state, MarketStateSize)
        // Write bytes offset for `extraData`
        mstore(
          add(headPointer, TransferHook_ExtraData_Head_Offset),
          TransferHook_ExtraData_Length_Offset
        )
        // Write length for `extraData`
        mstore(add(headPointer, TransferHook_ExtraData_Length_Offset), extraCalldataBytes)
        // Copy `extraData` from end of calldata to hook calldata
        calldatacopy(
          add(headPointer, TransferHook_ExtraData_TailOffset),
          baseCalldataSize,
          extraCalldataBytes
        )

        let size := add(TransferHook_Base_Size, extraCalldataBytes)

        if iszero(call(gas(), target, 0, add(cdPointer, 0x1c), size, 0, 0)) {
          returndatacopy(0, 0, returndatasize())
          revert(0, returndatasize())
        }
      }
    }
  }

  // ========================================================================== //
  //                               Hook for borrow                              //
  // ========================================================================== //

  uint256 internal constant BorrowCalldataSize = 0x24;
  // Size of normalizedAmount + state + extraData.offset + extraData.length
  uint256 internal constant BorrowHook_Base_Size = 0x0224;
  uint256 internal constant BorrowHook_State_Offset = 0x20;
  uint256 internal constant BorrowHook_ExtraData_Head_Offset = 0x01e0;
  uint256 internal constant BorrowHook_ExtraData_Length_Offset = 0x0200;
  uint256 internal constant BorrowHook_ExtraData_TailOffset = 0x0220;

  function onBorrow(HooksConfig self, uint256 normalizedAmount, MarketState memory state) internal {
    address target = self.hooksAddress();
    uint32 onBorrowSelector = uint32(IHooks.onBorrow.selector);
    if (self.useOnBorrow()) {
      assembly {
        let extraCalldataBytes := sub(calldatasize(), BorrowCalldataSize)
        let ptr := mload(0x40)
        let headPointer := add(ptr, 0x20)

        mstore(ptr, onBorrowSelector)
        // Copy `normalizedAmount` to hook calldata
        mstore(headPointer, normalizedAmount)
        // Copy market state to hook calldata
        mcopy(add(headPointer, BorrowHook_State_Offset), state, MarketStateSize)
        // Write bytes offset for `extraData`
        mstore(
          add(headPointer, BorrowHook_ExtraData_Head_Offset),
          BorrowHook_ExtraData_Length_Offset
        )
        // Write length for `extraData`
        mstore(add(headPointer, BorrowHook_ExtraData_Length_Offset), extraCalldataBytes)
        // Copy `extraData` from end of calldata to hook calldata
        calldatacopy(
          add(headPointer, BorrowHook_ExtraData_TailOffset),
          BorrowCalldataSize,
          extraCalldataBytes
        )

        let size := add(RepayHook_Base_Size, extraCalldataBytes)
        if iszero(call(gas(), target, 0, add(ptr, 0x1c), size, 0, 0)) {
          returndatacopy(0, 0, returndatasize())
          revert(0, returndatasize())
        }
      }
    }
  }

  // ========================================================================== //
  //                               Hook for repay                               //
  // ========================================================================== //

  // Size of normalizedAmount + state + extraData.offset + extraData.length
  uint256 internal constant RepayHook_Base_Size = 0x0224;
  uint256 internal constant RepayHook_State_Offset = 0x20;
  uint256 internal constant RepayHook_ExtraData_Head_Offset = 0x01e0;
  uint256 internal constant RepayHook_ExtraData_Length_Offset = 0x0200;
  uint256 internal constant RepayHook_ExtraData_TailOffset = 0x0220;

  function onRepay(
    HooksConfig self,
    uint256 normalizedAmount,
    MarketState memory state,
    uint256 baseCalldataSize
  ) internal {
    address target = self.hooksAddress();
    uint32 onRepaySelector = uint32(IHooks.onRepay.selector);
    if (self.useOnRepay()) {
      assembly {
        let extraCalldataBytes := sub(calldatasize(), baseCalldataSize)
        let ptr := mload(0x40)
        let headPointer := add(ptr, 0x20)

        mstore(ptr, onRepaySelector)
        // Copy `normalizedAmount` to hook calldata
        mstore(headPointer, normalizedAmount)
        // Copy market state to hook calldata
        mcopy(add(headPointer, RepayHook_State_Offset), state, MarketStateSize)
        // Write bytes offset for `extraData`
        mstore(add(headPointer, RepayHook_ExtraData_Head_Offset), RepayHook_ExtraData_Length_Offset)
        // Write length for `extraData`
        mstore(add(headPointer, RepayHook_ExtraData_Length_Offset), extraCalldataBytes)
        // Copy `extraData` from end of calldata to hook calldata
        calldatacopy(
          add(headPointer, RepayHook_ExtraData_TailOffset),
          baseCalldataSize,
          extraCalldataBytes
        )

        let size := add(RepayHook_Base_Size, extraCalldataBytes)
        if iszero(call(gas(), target, 0, add(ptr, 0x1c), size, 0, 0)) {
          returndatacopy(0, 0, returndatasize())
          revert(0, returndatasize())
        }
      }
    }
  }

  // ========================================================================== //
  //                            Hook for closeMarket                            //
  // ========================================================================== //

  // Size of calldata to `market.closeMarket`
  uint256 internal constant CloseMarketCalldataSize = 0x04;

  // Base size of calldata for `hooks.onCloseMarket()`
  uint256 internal constant CloseMarketHook_Base_Size = 0x0204;
  uint256 internal constant CloseMarketHook_ExtraData_Head_Offset = MarketStateSize;
  uint256 internal constant CloseMarketHook_ExtraData_Length_Offset = 0x01e0;
  uint256 internal constant CloseMarketHook_ExtraData_TailOffset = 0x0200;

  function onCloseMarket(HooksConfig self, MarketState memory state) internal {
    address target = self.hooksAddress();
    uint32 onCloseMarketSelector = uint32(IHooks.onCloseMarket.selector);
    if (self.useOnCloseMarket()) {
      assembly {
        let extraCalldataBytes := sub(calldatasize(), CloseMarketCalldataSize)
        let cdPointer := mload(0x40)
        let headPointer := add(cdPointer, 0x20)
        // Write selector for `onCloseMarket`
        mstore(cdPointer, onCloseMarketSelector)
        // Copy market state to hook calldata
        mcopy(headPointer, state, MarketStateSize)
        // Write bytes offset for `extraData`
        mstore(
          add(headPointer, CloseMarketHook_ExtraData_Head_Offset),
          CloseMarketHook_ExtraData_Length_Offset
        )
        // Write length for `extraData`
        mstore(add(headPointer, CloseMarketHook_ExtraData_Length_Offset), extraCalldataBytes)
        // Copy `extraData` from end of calldata to hook calldata
        calldatacopy(
          add(headPointer, CloseMarketHook_ExtraData_TailOffset),
          CloseMarketCalldataSize,
          extraCalldataBytes
        )

        let size := add(CloseMarketHook_Base_Size, extraCalldataBytes)

        if iszero(call(gas(), target, 0, add(cdPointer, 0x1c), size, 0, 0)) {
          returndatacopy(0, 0, returndatasize())
          revert(0, returndatasize())
        }
      }
    }
  }

  // ========================================================================== //
  //                         Hook for setMaxTotalSupply                         //
  // ========================================================================== //

  uint256 internal constant SetMaxTotalSupplyCalldataSize = 0x24;
  // Size of maxTotalSupply + state + extraData.offset + extraData.length
  uint256 internal constant SetMaxTotalSupplyHook_Base_Size = 0x0224;
  uint256 internal constant SetMaxTotalSupplyHook_State_Offset = 0x20;
  uint256 internal constant SetMaxTotalSupplyHook_ExtraData_Head_Offset = 0x01e0;
  uint256 internal constant SetMaxTotalSupplyHook_ExtraData_Length_Offset = 0x0200;
  uint256 internal constant SetMaxTotalSupplyHook_ExtraData_TailOffset = 0x0220;

  function onSetMaxTotalSupply(
    HooksConfig self,
    uint256 maxTotalSupply,
    MarketState memory state
  ) internal {
    address target = self.hooksAddress();
    uint32 onSetMaxTotalSupplySelector = uint32(IHooks.onSetMaxTotalSupply.selector);
    if (self.useOnSetMaxTotalSupply()) {
      assembly {
        let extraCalldataBytes := sub(calldatasize(), SetMaxTotalSupplyCalldataSize)
        let cdPointer := mload(0x40)
        let headPointer := add(cdPointer, 0x20)
        // Write selector for `onSetMaxTotalSupply`
        mstore(cdPointer, onSetMaxTotalSupplySelector)
        // Write `maxTotalSupply` to hook calldata
        mstore(headPointer, maxTotalSupply)
        // Copy market state to hook calldata
        mcopy(add(headPointer, SetMaxTotalSupplyHook_State_Offset), state, MarketStateSize)
        // Write bytes offset for `extraData`
        mstore(
          add(headPointer, SetMaxTotalSupplyHook_ExtraData_Head_Offset),
          SetMaxTotalSupplyHook_ExtraData_Length_Offset
        )
        // Write length for `extraData`
        mstore(add(headPointer, SetMaxTotalSupplyHook_ExtraData_Length_Offset), extraCalldataBytes)
        // Copy `extraData` from end of calldata to hook calldata
        calldatacopy(
          add(headPointer, SetMaxTotalSupplyHook_ExtraData_TailOffset),
          SetMaxTotalSupplyCalldataSize,
          extraCalldataBytes
        )

        let size := add(SetMaxTotalSupplyHook_Base_Size, extraCalldataBytes)

        if iszero(call(gas(), target, 0, add(cdPointer, 0x1c), size, 0, 0)) {
          returndatacopy(0, 0, returndatasize())
          revert(0, returndatasize())
        }
      }
    }
  }

  // ========================================================================== //
  //                       Hook for setAnnualInterestBips                       //
  // ========================================================================== //

  uint256 internal constant SetAnnualInterestAndReserveRatioBipsCalldataSize = 0x44;
  // Size of annualInterestBips + state + extraData.offset + extraData.length
  uint256 internal constant SetAnnualInterestAndReserveRatioBipsHook_Base_Size = 0x0244;
  uint256 internal constant SetAnnualInterestAndReserveRatioBipsHook_ReserveRatioBits_Offset = 0x20;
  uint256 internal constant SetAnnualInterestAndReserveRatioBipsHook_State_Offset = 0x40;
  uint256 internal constant SetAnnualInterestAndReserveRatioBipsHook_ExtraData_Head_Offset = 0x0200;
  uint256 internal constant SetAnnualInterestAndReserveRatioBipsHook_ExtraData_Length_Offset =
    0x0220;
  uint256 internal constant SetAnnualInterestAndReserveRatioBipsHook_ExtraData_TailOffset = 0x0240;

  function onSetAnnualInterestAndReserveRatioBips(
    HooksConfig self,
    uint16 annualInterestBips,
    uint16 reserveRatioBips,
    MarketState memory state
  ) internal returns (uint16 newAnnualInterestBips, uint16 newReserveRatioBips) {
    address target = self.hooksAddress();
    uint32 onSetAnnualInterestBipsSelector = uint32(
      IHooks.onSetAnnualInterestAndReserveRatioBips.selector
    );
    if (self.useOnSetAnnualInterestAndReserveRatioBips()) {
      assembly {
        let extraCalldataBytes := sub(
          calldatasize(),
          SetAnnualInterestAndReserveRatioBipsCalldataSize
        )
        let cdPointer := mload(0x40)
        let headPointer := add(cdPointer, 0x20)
        // Write selector for `onSetAnnualInterestBips`
        mstore(cdPointer, onSetAnnualInterestBipsSelector)
        // Write `annualInterestBips` to hook calldata
        mstore(headPointer, annualInterestBips)
        // Write `reserveRatioBips` to hook calldata
        mstore(
          add(headPointer, SetAnnualInterestAndReserveRatioBipsHook_ReserveRatioBits_Offset),
          reserveRatioBips
        )
        // Copy market state to hook calldata
        mcopy(
          add(headPointer, SetAnnualInterestAndReserveRatioBipsHook_State_Offset),
          state,
          MarketStateSize
        )
        // Write bytes offset for `extraData`
        mstore(
          add(headPointer, SetAnnualInterestAndReserveRatioBipsHook_ExtraData_Head_Offset),
          SetAnnualInterestAndReserveRatioBipsHook_ExtraData_Length_Offset
        )
        // Write length for `extraData`
        mstore(
          add(headPointer, SetAnnualInterestAndReserveRatioBipsHook_ExtraData_Length_Offset),
          extraCalldataBytes
        )
        // Copy `extraData` from end of calldata to hook calldata
        calldatacopy(
          add(headPointer, SetAnnualInterestAndReserveRatioBipsHook_ExtraData_TailOffset),
          SetAnnualInterestAndReserveRatioBipsCalldataSize,
          extraCalldataBytes
        )

        let size := add(SetAnnualInterestAndReserveRatioBipsHook_Base_Size, extraCalldataBytes)

        // Returndata is expected to have the new values for `annualInterestBips` and `reserveRatioBips`
        if or(
          lt(returndatasize(), 0x40),
          iszero(call(gas(), target, 0, add(cdPointer, 0x1c), size, 0, 0x40))
        ) {
          returndatacopy(0, 0, returndatasize())
          revert(0, returndatasize())
        }

        newAnnualInterestBips := and(mload(0), 0xffff)
        newReserveRatioBips := and(mload(0x20), 0xffff)
      }
    } else {
      (newAnnualInterestBips, newReserveRatioBips) = (annualInterestBips, reserveRatioBips);
    }
  }

  // ========================================================================== //
  //                     Hook for protocol fee bips updated                     //
  // ========================================================================== //

  uint256 internal constant SetProtocolFeeBipsCalldataSize = 0x24;
  // Size of protocolFeeBips + state + extraData.offset + extraData.length
  uint256 internal constant SetProtocolFeeBips_Base_Size = 0x0224;
  uint256 internal constant SetProtocolFeeBips_State_Offset = 0x20;
  uint256 internal constant SetProtocolFeeBips_ExtraData_Head_Offset = 0x01e0;
  uint256 internal constant SetProtocolFeeBips_ExtraData_Length_Offset = 0x0200;
  uint256 internal constant SetProtocolFeeBips_ExtraData_TailOffset = 0x0220;

  function onSetProtocolFeeBips(
    HooksConfig self,
    uint protocolFeeBips,
    MarketState memory state
  ) internal {
    address target = self.hooksAddress();
    uint32 onSetProtocolFeeBipsSelector = uint32(IHooks.onSetProtocolFeeBips.selector);
    if (self.useOnSetProtocolFeeBips()) {
      assembly {
        let extraCalldataBytes := sub(calldatasize(), SetProtocolFeeBipsCalldataSize)
        let cdPointer := mload(0x40)
        let headPointer := add(cdPointer, 0x20)
        // Write selector for `onSetProtocolFeeBips`
        mstore(cdPointer, onSetProtocolFeeBipsSelector)
        // Write `protocolFeeBips` to hook calldata
        mstore(headPointer, protocolFeeBips)
        // Copy market state to hook calldata
        mcopy(add(headPointer, SetProtocolFeeBips_State_Offset), state, MarketStateSize)
        // Write bytes offset for `extraData`
        mstore(
          add(headPointer, SetProtocolFeeBips_ExtraData_Head_Offset),
          SetProtocolFeeBips_ExtraData_Length_Offset
        )
        // Write length for `extraData`
        mstore(add(headPointer, SetProtocolFeeBips_ExtraData_Length_Offset), extraCalldataBytes)
        // Copy `extraData` from end of calldata to hook calldata
        calldatacopy(
          add(headPointer, SetProtocolFeeBips_ExtraData_TailOffset),
          SetProtocolFeeBipsCalldataSize,
          extraCalldataBytes
        )

        let size := add(SetProtocolFeeBips_Base_Size, extraCalldataBytes)

        if iszero(call(gas(), target, 0, add(cdPointer, 0x1c), size, 0, 0)) {
          returndatacopy(0, 0, returndatasize())
          revert(0, returndatasize())
        }
      }
    }
  }

  // ========================================================================== //
  //                       Hook for assets sent to escrow                       //
  // ========================================================================== //

  uint256 internal constant NukeFromOrbitCalldataSize = 0x24;
  // Size of lender + state + extraData.offset + extraData.length
  uint256 internal constant NukeFromOrbit_Base_Size = 0x0224;
  uint256 internal constant NukeFromOrbit_State_Offset = 0x20;
  uint256 internal constant NukeFromOrbit_ExtraData_Head_Offset = 0x01e0;
  uint256 internal constant NukeFromOrbit_ExtraData_Length_Offset = 0x0200;
  uint256 internal constant NukeFromOrbit_ExtraData_TailOffset = 0x0220;

  function onNukeFromOrbit(HooksConfig self, address lender, MarketState memory state) internal {
    address target = self.hooksAddress();
    uint32 onNukeFromOrbitSelector = uint32(IHooks.onNukeFromOrbit.selector);
    if (self.useOnNukeFromOrbit()) {
      assembly {
        let extraCalldataBytes := sub(calldatasize(), NukeFromOrbitCalldataSize)
        let cdPointer := mload(0x40)
        let headPointer := add(cdPointer, 0x20)
        // Write selector for `onNukeFromOrbit`
        mstore(cdPointer, onNukeFromOrbitSelector)
        // Write `lender` to hook calldata
        mstore(headPointer, lender)
        // Copy market state to hook calldata
        mcopy(add(headPointer, NukeFromOrbit_State_Offset), state, MarketStateSize)
        // Write bytes offset for `extraData`
        mstore(
          add(headPointer, NukeFromOrbit_ExtraData_Head_Offset),
          NukeFromOrbit_ExtraData_Length_Offset
        )
        // Write length for `extraData`
        mstore(add(headPointer, NukeFromOrbit_ExtraData_Length_Offset), extraCalldataBytes)
        // Copy `extraData` from end of calldata to hook calldata
        calldatacopy(
          add(headPointer, NukeFromOrbit_ExtraData_TailOffset),
          NukeFromOrbitCalldataSize,
          extraCalldataBytes
        )

        let size := add(NukeFromOrbit_Base_Size, extraCalldataBytes)

        if iszero(call(gas(), target, 0, add(cdPointer, 0x1c), size, 0, 0)) {
          returndatacopy(0, 0, returndatasize())
          revert(0, returndatasize())
        }
      }
    }
  }
}

// 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);
}

// 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)
  }
}

// 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)
  }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for bit twiddling and boolean operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBit.sol)
/// @author Inspired by (https://graphics.stanford.edu/~seander/bithacks.html)
library LibBit {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  BIT TWIDDLING OPERATIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Find last set.
    /// Returns the index of the most significant bit of `x`,
    /// counting from the least significant bit position.
    /// If `x` is zero, returns 256.
    function fls(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, x)))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            r := or(r, shl(2, lt(0xf, shr(r, x))))
            r := or(r, byte(shr(r, x), hex"00000101020202020303030303030303"))
        }
    }

    /// @dev Count leading zeros.
    /// Returns the number of zeros preceding the most significant one bit.
    /// If `x` is zero, returns 256.
    function clz(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            r := or(r, shl(2, lt(0xf, shr(r, x))))
            // forgefmt: disable-next-item
            r := add(iszero(x), xor(255,
                or(r, byte(shr(r, x), hex"00000101020202020303030303030303"))))
        }
    }

    /// @dev Find first set.
    /// Returns the index of the least significant bit of `x`,
    /// counting from the least significant bit position.
    /// If `x` is zero, returns 256.
    /// Equivalent to `ctz` (count trailing zeros), which gives
    /// the number of zeros following the least significant one bit.
    function ffs(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Isolate the least significant bit.
            let b := and(x, add(not(x), 1))

            r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, b)))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, b))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, b))))

            // For the remaining 32 bits, use a De Bruijn lookup.
            // forgefmt: disable-next-item
            r := or(r, byte(and(div(0xd76453e0, shr(r, b)), 0x1f),
                0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
        }
    }

    /// @dev Returns the number of set bits in `x`.
    function popCount(uint256 x) internal pure returns (uint256 c) {
        /// @solidity memory-safe-assembly
        assembly {
            let max := not(0)
            let isMax := eq(x, max)
            x := sub(x, and(shr(1, x), div(max, 3)))
            x := add(and(x, div(max, 5)), and(shr(2, x), div(max, 5)))
            x := and(add(x, shr(4, x)), div(max, 17))
            c := or(shl(8, isMax), shr(248, mul(x, div(max, 255))))
        }
    }

    /// @dev Returns whether `x` is a power of 2.
    function isPo2(uint256 x) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `x && !(x & (x - 1))`.
            result := iszero(add(and(x, sub(x, 1)), iszero(x)))
        }
    }

    /// @dev Returns `x` reversed at the bit level.
    function reverseBits(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Computing masks on-the-fly reduces bytecode size by about 500 bytes.
            let m := not(0)
            r := x
            for { let s := 128 } 1 {} {
                m := xor(m, shl(s, m))
                r := or(and(shr(s, r), m), and(shl(s, r), not(m)))
                s := shr(1, s)
                if iszero(s) { break }
            }
        }
    }

    /// @dev Returns `x` reversed at the byte level.
    function reverseBytes(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Computing masks on-the-fly reduces bytecode size by about 200 bytes.
            let m := not(0)
            r := x
            for { let s := 128 } 1 {} {
                m := xor(m, shl(s, m))
                r := or(and(shr(s, r), m), and(shl(s, r), not(m)))
                s := shr(1, s)
                if eq(s, 4) { break }
            }
        }
    }

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

    // A Solidity bool on the stack or memory is represented as a 256-bit word.
    // Non-zero values are true, zero is false.
    // A clean bool is either 0 (false) or 1 (true) under the hood.
    // Usually, if not always, the bool result of a regular Solidity expression,
    // or the argument of a public/external function will be a clean bool.
    // You can usually use the raw variants for more performance.
    // If uncertain, test (best with exact compiler settings).
    // Or use the non-raw variants (compiler can sometimes optimize out the double `iszero`s).

    /// @dev Returns `x & y`. Inputs must be clean.
    function rawAnd(bool x, bool y) internal pure returns (bool z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := and(x, y)
        }
    }

    /// @dev Returns `x & y`.
    function and(bool x, bool y) internal pure returns (bool z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := and(iszero(iszero(x)), iszero(iszero(y)))
        }
    }

    /// @dev Returns `x | y`. Inputs must be clean.
    function rawOr(bool x, bool y) internal pure returns (bool z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(x, y)
        }
    }

    /// @dev Returns `x | y`.
    function or(bool x, bool y) internal pure returns (bool z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(iszero(iszero(x)), iszero(iszero(y)))
        }
    }

    /// @dev Returns 1 if `b` is true, else 0. Input must be clean.
    function rawToUint(bool b) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := b
        }
    }

    /// @dev Returns 1 if `b` is true, else 0.
    function toUint(bool b) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := iszero(iszero(b))
        }
    }
}

// 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)));
  }
}

// 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
  ) internal pure returns (uint256 protocolFee) {
    // Protocol fee is charged in addition to the interest paid to lenders.
    uint256 protocolFeeRay = uint(state.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 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 delinquencyFeeBips,
    uint256 delinquencyGracePeriod,
    uint256 timestamp
  )
    internal
    pure
    returns (uint256 baseInterestRay, uint256 delinquencyFeeRay, uint256 protocolFee)
  {
    baseInterestRay = state.calculateBaseInterest(timestamp);

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

    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);
  }
}

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/",
    "ethereum-access-token/=lib/ethereum-access-token/contracts/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "vulcan/=lib/vulcan/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 50000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"archController_","type":"address"},{"internalType":"address","name":"_sanctionsSentinel","type":"address"},{"internalType":"address","name":"_marketInitCodeStorage","type":"address"},{"internalType":"uint256","name":"_marketInitCodeHash","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AssetBlacklisted","type":"error"},{"inputs":[],"name":"CallerNotArchControllerOwner","type":"error"},{"inputs":[],"name":"DeploymentFailed","type":"error"},{"inputs":[],"name":"FeeMismatch","type":"error"},{"inputs":[],"name":"HooksInstanceAlreadyExists","type":"error"},{"inputs":[],"name":"HooksInstanceNotFound","type":"error"},{"inputs":[],"name":"HooksTemplateAlreadyExists","type":"error"},{"inputs":[],"name":"HooksTemplateNotAvailable","type":"error"},{"inputs":[],"name":"HooksTemplateNotFound","type":"error"},{"inputs":[],"name":"InvalidFeeConfiguration","type":"error"},{"inputs":[],"name":"MarketAlreadyExists","type":"error"},{"inputs":[],"name":"NameOrSymbolTooLong","type":"error"},{"inputs":[],"name":"NoReentrantCalls","type":"error"},{"inputs":[],"name":"NotApprovedBorrower","type":"error"},{"inputs":[],"name":"SaltDoesNotContainSender","type":"error"},{"inputs":[],"name":"SetProtocolFeeBipsFailed","type":"error"},{"inputs":[],"name":"SphereXOperatorRequired","type":"error"},{"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":false,"internalType":"address","name":"hooksInstance","type":"address"},{"indexed":false,"internalType":"address","name":"hooksTemplate","type":"address"}],"name":"HooksInstanceDeployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"hooksTemplate","type":"address"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"address","name":"feeRecipient","type":"address"},{"indexed":false,"internalType":"address","name":"originationFeeAsset","type":"address"},{"indexed":false,"internalType":"uint80","name":"originationFeeAmount","type":"uint80"},{"indexed":false,"internalType":"uint16","name":"protocolFeeBips","type":"uint16"}],"name":"HooksTemplateAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"hooksTemplate","type":"address"}],"name":"HooksTemplateDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"hooksTemplate","type":"address"},{"indexed":false,"internalType":"address","name":"feeRecipient","type":"address"},{"indexed":false,"internalType":"address","name":"originationFeeAsset","type":"address"},{"indexed":false,"internalType":"uint80","name":"originationFeeAmount","type":"uint80"},{"indexed":false,"internalType":"uint16","name":"protocolFeeBips","type":"uint16"}],"name":"HooksTemplateFeesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"hooksTemplate","type":"address"},{"indexed":true,"internalType":"address","name":"market","type":"address"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"string","name":"symbol","type":"string"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"maxTotalSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"annualInterestBips","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delinquencyFeeBips","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"withdrawalBatchDuration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reserveRatioBips","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delinquencyGracePeriod","type":"uint256"},{"indexed":false,"internalType":"HooksConfig","name":"hooks","type":"uint256"}],"name":"MarketDeployed","type":"event"},{"inputs":[{"internalType":"address","name":"hooksTemplate","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"feeRecipient","type":"address"},{"internalType":"address","name":"originationFeeAsset","type":"address"},{"internalType":"uint80","name":"originationFeeAmount","type":"uint80"},{"internalType":"uint16","name":"protocolFeeBips","type":"uint16"}],"name":"addHooksTemplate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"archController","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":[{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"computeMarketAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"hooksTemplate","type":"address"},{"internalType":"bytes","name":"constructorArgs","type":"bytes"}],"name":"deployHooksInstance","outputs":[{"internalType":"address","name":"hooksInstance","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"string","name":"namePrefix","type":"string"},{"internalType":"string","name":"symbolPrefix","type":"string"},{"internalType":"uint128","name":"maxTotalSupply","type":"uint128"},{"internalType":"uint16","name":"annualInterestBips","type":"uint16"},{"internalType":"uint16","name":"delinquencyFeeBips","type":"uint16"},{"internalType":"uint32","name":"withdrawalBatchDuration","type":"uint32"},{"internalType":"uint16","name":"reserveRatioBips","type":"uint16"},{"internalType":"uint32","name":"delinquencyGracePeriod","type":"uint32"},{"internalType":"HooksConfig","name":"hooks","type":"uint256"}],"internalType":"struct DeployMarketInputs","name":"parameters","type":"tuple"},{"internalType":"bytes","name":"hooksData","type":"bytes"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"address","name":"originationFeeAsset","type":"address"},{"internalType":"uint256","name":"originationFeeAmount","type":"uint256"}],"name":"deployMarket","outputs":[{"internalType":"address","name":"market","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"hooksTemplate","type":"address"},{"internalType":"bytes","name":"hooksTemplateArgs","type":"bytes"},{"components":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"string","name":"namePrefix","type":"string"},{"internalType":"string","name":"symbolPrefix","type":"string"},{"internalType":"uint128","name":"maxTotalSupply","type":"uint128"},{"internalType":"uint16","name":"annualInterestBips","type":"uint16"},{"internalType":"uint16","name":"delinquencyFeeBips","type":"uint16"},{"internalType":"uint32","name":"withdrawalBatchDuration","type":"uint32"},{"internalType":"uint16","name":"reserveRatioBips","type":"uint16"},{"internalType":"uint32","name":"delinquencyGracePeriod","type":"uint32"},{"internalType":"HooksConfig","name":"hooks","type":"uint256"}],"internalType":"struct DeployMarketInputs","name":"parameters","type":"tuple"},{"internalType":"bytes","name":"hooksData","type":"bytes"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"address","name":"originationFeeAsset","type":"address"},{"internalType":"uint256","name":"originationFeeAmount","type":"uint256"}],"name":"deployMarketAndHooks","outputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"address","name":"hooksInstance","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"hooksTemplate","type":"address"}],"name":"disableHooksTemplate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"}],"name":"getHooksInstancesCountForBorrower","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"}],"name":"getHooksInstancesForBorrower","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"hooksTemplate","type":"address"}],"name":"getHooksTemplateDetails","outputs":[{"components":[{"internalType":"address","name":"originationFeeAsset","type":"address"},{"internalType":"uint80","name":"originationFeeAmount","type":"uint80"},{"internalType":"uint16","name":"protocolFeeBips","type":"uint16"},{"internalType":"bool","name":"exists","type":"bool"},{"internalType":"bool","name":"enabled","type":"bool"},{"internalType":"uint24","name":"index","type":"uint24"},{"internalType":"address","name":"feeRecipient","type":"address"},{"internalType":"string","name":"name","type":"string"}],"internalType":"struct HooksTemplate","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"hooksInstance","type":"address"}],"name":"getHooksTemplateForInstance","outputs":[{"internalType":"address","name":"hooksTemplate","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getHooksTemplates","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"}],"name":"getHooksTemplates","outputs":[{"internalType":"address[]","name":"arr","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getHooksTemplatesCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMarketParameters","outputs":[{"components":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"bytes32","name":"packedNameWord0","type":"bytes32"},{"internalType":"bytes32","name":"packedNameWord1","type":"bytes32"},{"internalType":"bytes32","name":"packedSymbolWord0","type":"bytes32"},{"internalType":"bytes32","name":"packedSymbolWord1","type":"bytes32"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"address","name":"feeRecipient","type":"address"},{"internalType":"address","name":"sentinel","type":"address"},{"internalType":"uint128","name":"maxTotalSupply","type":"uint128"},{"internalType":"uint16","name":"protocolFeeBips","type":"uint16"},{"internalType":"uint16","name":"annualInterestBips","type":"uint16"},{"internalType":"uint16","name":"delinquencyFeeBips","type":"uint16"},{"internalType":"uint32","name":"withdrawalBatchDuration","type":"uint32"},{"internalType":"uint16","name":"reserveRatioBips","type":"uint16"},{"internalType":"uint32","name":"delinquencyGracePeriod","type":"uint32"},{"internalType":"address","name":"archController","type":"address"},{"internalType":"address","name":"sphereXEngine","type":"address"},{"internalType":"HooksConfig","name":"hooks","type":"uint256"}],"internalType":"struct MarketParameters","name":"parameters","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"hooksInstance","type":"address"}],"name":"getMarketsForHooksInstance","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"hooksInstance","type":"address"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"}],"name":"getMarketsForHooksInstance","outputs":[{"internalType":"address[]","name":"arr","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"hooksInstance","type":"address"}],"name":"getMarketsForHooksInstanceCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"hooksTemplate","type":"address"}],"name":"getMarketsForHooksTemplate","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"hooksTemplate","type":"address"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"}],"name":"getMarketsForHooksTemplate","outputs":[{"internalType":"address[]","name":"arr","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"hooksTemplate","type":"address"}],"name":"getMarketsForHooksTemplateCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"hooksInstance","type":"address"}],"name":"isHooksInstance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"hooksTemplate","type":"address"}],"name":"isHooksTemplate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"marketInitCodeHash","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"marketInitCodeStorage","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"hooksTemplate","type":"address"}],"name":"pushProtocolFeeBipsUpdates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"hooksTemplate","type":"address"},{"internalType":"uint256","name":"marketStartIndex","type":"uint256"},{"internalType":"uint256","name":"marketEndIndex","type":"uint256"}],"name":"pushProtocolFeeBipsUpdates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"registerWithArchController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sanctionsSentinel","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":"hooksTemplate","type":"address"},{"internalType":"address","name":"feeRecipient","type":"address"},{"internalType":"address","name":"originationFeeAsset","type":"address"},{"internalType":"uint80","name":"originationFeeAmount","type":"uint80"},{"internalType":"uint16","name":"protocolFeeBips","type":"uint16"}],"name":"updateHooksTemplateFees","outputs":[],"stateMutability":"nonpayable","type":"function"}]

61012080604052346101015760809061446c9081380391829161002183610105565b39126101015761004d61003261013e565b61003a610155565b61004261016c565b9061018051926101a2565b60405161419e90816102ce82396080518181816105b301528181610d8001528181610f3a015281816110bf01528181611170015281816113f1015281816114b2015281816119cb0152818161299f01528181612b0101528181613396015281816137a0015261382a015260a0518181816118de015261347a015260c051818181610e4b0152613764015260e0518181816118a40152818161190901526134580152610100518181816104cc0152610fdc0152f35b5f80fd5b601f01601f1916610120908101906001600160401b0382119082101761012a57604052565b634e487b7160e01b5f52604160045260245ffd5b61012051906001600160a01b038216820361010157565b61014051906001600160a01b038216820361010157565b61016051906001600160a01b038216820361010157565b9081602091031261010157516001600160a01b03811681036101015790565b9060049360209360ff60a01b301760a05260c05260e052816080526101005260405192838092631e1188b360e11b825260018060a01b03165afa801561023f575f906101f5575b6101f3915061024a565b565b506020903d602011610237575b601f8201601f19168101906001600160401b0382118183101761012a576101f39261023292604052810190610183565b6101e9565b3d9150610202565b6040513d5f823e3d90fd5b60018060a01b03608051165f80526020527f2ac55ae7ba47db34b5334622acafeb34a65daf143b47019273185d64c73a35a560405fa1807f1777adabd324f814e2b0e28f6edf876dce01d7d66358c9acfe87b1b5f38338d5556020527ff33499cccaa0611882086224cc48cd82ef54b66a4d2edf4ed67108dd516896d560405fa156fe60806040526004361015610011575f80fd5b5f3560e01c806304032dbb1461022f57806306fdde031461022a578063155733ef146102255780631caab59d146102205780632812d2de1461021b5780632b07f2801461021657806333ee81f2146102115780633476b76d1461020c578063356dd352146102075780633c231166146102025780633fd83e1e146101fd57806341f259b8146101f85780634272711d146101f35780634bd1acf3146101ee5780634c6c848f146101e9578063546355701461019957806362ef8119146101e45780636e50f960146101df5780637a749060146101da5780639c5cd31a146101d55780639f2f94b3146101d0578063ad78e45a146101cb578063aeefe08c146101c6578063c29a7677146101c1578063cd01e52a146101bc578063cdbb8374146101b7578063d14a5402146101b2578063d19bd571146101ad578063d1a0745c146101a8578063d9d58025146101a3578063e7ca86e31461019e578063e7e5db4f14610199578063eae6ff66146101945763fe604ec71461018f575f80fd5b611d64565b611cd7565b611144565b611c65565b611c49565b611c00565b611bb3565b611b6a565b611aab565b61194f565b6118c7565b61188d565b6117d9565b611451565b611362565b6112ab565b611220565b611194565b61108b565b611000565b610fb0565b610f12565b610ec1565b610e6f565b610e1f565b610cd0565b610b4b565b610add565b6109c4565b61087e565b6106de565b610674565b6103c9565b5f91031261023e57565b5f80fd5b815173ffffffffffffffffffffffffffffffffffffffff1681526102608101929160208181015160ff169083015260408101516040830152606081015160608301526080810151608083015260a081015160a08301526102bf60c082015160c084019073ffffffffffffffffffffffffffffffffffffffff169052565b60e08181015173ffffffffffffffffffffffffffffffffffffffff16908301526101008181015173ffffffffffffffffffffffffffffffffffffffff1690830152610120818101516fffffffffffffffffffffffffffffffff16908301526101408181015161ffff16908301526101608181015161ffff16908301526101808181015161ffff16908301526101a08181015163ffffffff16908301526101c08181015161ffff16908301526101e08181015163ffffffff16908301526102008181015173ffffffffffffffffffffffffffffffffffffffff16908301526102208181015173ffffffffffffffffffffffffffffffffffffffff169083015261024080910151910152565b3461023e575f60031936011261023e576103e1611dad565b6103e9612d5a565b602081015173ffffffffffffffffffffffffffffffffffffffff1682526101408082015160408401526101608083015160608501526101808084015160808601526101a08085015160a08701526101c0808601519093929060ff1660ff166020880152855173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660c0880152604086015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660e088015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016610100888101919091529260808701516fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff166101208981019190915295606088015161ffff1661ffff169089015260a087015161ffff1661ffff169088015260c086015161ffff1661ffff169087015260e085015163ffffffff1663ffffffff169086015283015161ffff1661ffff169084015281015163ffffffff1663ffffffff166101e0838101919091529073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166102008401527f1777adabd324f814e2b0e28f6edf876dce01d7d66358c9acfe87b1b5f38338d55473ffffffffffffffffffffffffffffffffffffffff16610220840152015161024082015260405161062d819282610242565b0390f35b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b3461023e575f60031936011261023e57731357696c64636174486f6f6b73466163746f72796053526020805260606020f35b73ffffffffffffffffffffffffffffffffffffffff81160361023e57565b60a435906106d1826106a6565b565b35906106d1826106a6565b3461023e57602060031936011261023e57600480356106fc816106a6565b610704612de0565b61073461072f8273ffffffffffffffffffffffffffffffffffffffff165f52600460205260405f2090565b611f42565b906107496107456060840151151590565b1590565b610803576107759073ffffffffffffffffffffffffffffffffffffffff165f52600260205260405f2090565b6107866040825493015161ffff1690565b90604051916040830160405263ae6ea19183526020830152601c8092015f5b8481106107b6576107b4612dfe565b005b5f80602484826107e96107c9878a612026565b905473ffffffffffffffffffffffffffffffffffffffff9160031b1c1690565b5af1156107f8576001016107a5565b8584634484a4a95f52fd5b60046040517f8cb2227e000000000000000000000000000000000000000000000000000000008152fd5b60209060206040818301928281528551809452019301915f5b828110610854575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101610846565b3461023e5760208060031936011261023e5760043561089c816106a6565b73ffffffffffffffffffffffffffffffffffffffff8091165f52600260205260405f20916040518092602085549283815201945f5260205f20925f915b8383106108fc5761062d866108f0818a0382611592565b6040519182918261082d565b84548116875295810195600194850194909201916108d9565b61012060e06109c1936020845273ffffffffffffffffffffffffffffffffffffffff815116602085015269ffffffffffffffffffff602082015116604085015261096a6040820151606086019061ffff169052565b6060810151151560808501526080810151151560a085015260a081015162ffffff1660c085015260c081015173ffffffffffffffffffffffffffffffffffffffff1684830152015191610100808201520190610631565b90565b3461023e57602060031936011261023e5761062d6004356109e4816106a6565b6040606060e082516109f58161153f565b5f81525f60208201525f848201525f838201525f60808201525f60a08201525f60c0820152015273ffffffffffffffffffffffffffffffffffffffff8092165f526004602052610ace6002825f20610ac8845195610a528761153f565b8254818116885269ffffffffffffffffffff60a082901c16602089015260f01c868801525b600183015460ff811615156060890152600881901c60ff1615156080890152601081901c62ffffff1660a089015260281c1660c087019073ffffffffffffffffffffffffffffffffffffffff169052565b01611e83565b60e08301525191829182610915565b3461023e57602060031936011261023e576020600435610afc816106a6565b73ffffffffffffffffffffffffffffffffffffffff8091165f526005825260405f205416604051908152f35b600319606091011261023e57600435610b40816106a6565b906024359060443590565b3461023e57610b5936610b28565b610b61612de0565b610b8c61072f8473ffffffffffffffffffffffffffffffffffffffff165f52600460205260405f2090565b92610b9d6107456060860151151590565b610803576040610be984610be4610bd5610bf39573ffffffffffffffffffffffffffffffffffffffff165f52600260205260405f2090565b95865490818082039110020190565b611fdf565b94015161ffff1690565b91604051926040840160405263ae6ea19184526020840152601c809301915f5b858110610c22576107b4612dfe565b5f8060248682610c3e6107c9610c38888b611fec565b89612026565b5af115610c4d57600101610c13565b600485634484a4a95f52fd5b9181601f8401121561023e5782359167ffffffffffffffff831161023e576020838186019501011161023e57565b6084359069ffffffffffffffffffff8216820361023e57565b6064359069ffffffffffffffffffff8216820361023e57565b61ffff81160361023e57565b35906106d182610cb9565b3461023e5760c060031936011261023e57600435610ced816106a6565b60243567ffffffffffffffff811161023e57610d0d903690600401610c59565b604435610d19816106a6565b60643590610d26826106a6565b610d2e610c87565b9260a43594610d3c86610cb9565b73ffffffffffffffffffffffffffffffffffffffff6040517f8da5cb5b000000000000000000000000000000000000000000000000000000008152602081600481857f0000000000000000000000000000000000000000000000000000000000000000165afa908115610e1a575f91610deb575b50163303610dc1576107b49661256c565b60046040517f44d783c6000000000000000000000000000000000000000000000000000000008152fd5b610e0d915060203d602011610e13575b610e058183611592565b810190612046565b5f610db0565b503d610dfb565b61205b565b3461023e575f60031936011261023e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461023e575f60031936011261023e5760207f1777adabd324f814e2b0e28f6edf876dce01d7d66358c9acfe87b1b5f38338d55473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b3461023e57602060031936011261023e5773ffffffffffffffffffffffffffffffffffffffff600435610ef3816106a6565b165f526004602052602060ff600160405f200154166040519015158152f35b3461023e575f8060031936011261023e5773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016803b1561023e575f80916024604051809481937fd91ae8c20000000000000000000000000000000000000000000000000000000083523060048401525af18015610e1a57610fa6575080f35b6107b49150611561565b3461023e575f60031936011261023e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461023e5760208060031936011261023e5760043561101e816106a6565b73ffffffffffffffffffffffffffffffffffffffff8091165f52600360205260405f20916040518092602085549283815201945f5260205f20925f915b8383106110725761062d866108f0818a0382611592565b845481168752958101956001948501949092019161105b565b3461023e57602060031936011261023e576004356110a8816106a6565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163303611137577f1777adabd324f814e2b0e28f6edf876dce01d7d66358c9acfe87b1b5f38338d581815491555f526020527ff33499cccaa0611882086224cc48cd82ef54b66a4d2edf4ed67108dd516896d560405fa1005b634ee0b8f85f526004601cfd5b3461023e575f60031936011261023e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461023e5760208060031936011261023e576004356111b2816106a6565b73ffffffffffffffffffffffffffffffffffffffff8091165f52600191600160205260405f20926040518093602086549283815201955f5260205f20935f915b8383106112095761062d876108f0818b0382611592565b8554811688529681019694840194918401916111f2565b3461023e575f60031936011261023e576040515f8054808352818052602080840193927f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56392915b82821061127e5761062d856108f081890382611592565b835473ffffffffffffffffffffffffffffffffffffffff1686529485019460019384019390910190611267565b3461023e5773ffffffffffffffffffffffffffffffffffffffff6112ce36610b28565b91929092165f52600360205260405f2090815490818082039110020182810390811161135d576112fd816126ff565b915f5b828110611315576040518061062d868261082d565b8085019081861161135d576113576113326107c960019486612026565b61133c838861274e565b9073ffffffffffffffffffffffffffffffffffffffff169052565b01611300565b611fb2565b3461023e5760a060031936011261023e5760043561137f816106a6565b60243561138b816106a6565b604435611397816106a6565b61139f610ca0565b90608435926113ad84610cb9565b73ffffffffffffffffffffffffffffffffffffffff6040517f8da5cb5b000000000000000000000000000000000000000000000000000000008152602081600481857f0000000000000000000000000000000000000000000000000000000000000000165afa908115610e1a575f91611432575b50163303610dc1576107b494612762565b61144b915060203d602011610e1357610e058183611592565b5f611421565b3461023e57602060031936011261023e5760043561146e816106a6565b73ffffffffffffffffffffffffffffffffffffffff6040517f8da5cb5b000000000000000000000000000000000000000000000000000000008152602081600481857f0000000000000000000000000000000000000000000000000000000000000000165afa908115610e1a575f916114f3575b50163303610dc1576107b4906128ca565b61150c915060203d602011610e1357610e058183611592565b5f6114e2565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610100810190811067ffffffffffffffff82111761155c57604052565b611512565b67ffffffffffffffff811161155c57604052565b610220810190811067ffffffffffffffff82111761155c57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761155c57604052565b60405190610140820182811067ffffffffffffffff82111761155c57604052565b60405190610260820182811067ffffffffffffffff82111761155c57604052565b604051906106d18261153f565b60405190610200820182811067ffffffffffffffff82111761155c57604052565b92919267ffffffffffffffff821161155c576040519161168b60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184611592565b82948184528183011161023e578281602093845f960137010152565b9080601f8301121561023e578160206109c193359101611643565b6fffffffffffffffffffffffffffffffff81160361023e57565b35906106d1826116c2565b63ffffffff81160361023e57565b35906106d1826116e7565b9190916101408184031261023e576117166115d3565b92611720826106d3565b845267ffffffffffffffff90602083013582811161023e57816117449185016116a7565b6020860152604083013591821161023e576117609183016116a7565b6040840152611771606082016116dc565b606084015261178260808201610cc5565b608084015261179360a08201610cc5565b60a08401526117a460c082016116f5565b60c08401526117b560e08201610cc5565b60e08401526101006117c88183016116f5565b908401526101208091013590830152565b3461023e5760e060031936011261023e576004356117f6816106a6565b67ffffffffffffffff9060243582811161023e57611818903690600401610c59565b909160443584811161023e57611832903690600401611700565b60643594851161023e5761184d611864953690600401610c59565b916118566106c4565b9460c4359660843595612959565b6040805173ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152f35b3461023e575f60031936011261023e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461023e57602060031936011261023e57604080517f00000000000000000000000000000000000000000000000000000000000000005f5260043560209081527f000000000000000000000000000000000000000000000000000000000000000083526055600b209282905273ffffffffffffffffffffffffffffffffffffffff9092168152f35b3461023e57604060031936011261023e5760043561196c816106a6565b60243567ffffffffffffffff811161023e5761198c903690600401610c59565b611997929192612de0565b6040517f0787c1fe0000000000000000000000000000000000000000000000000000000081523360048201526020816024817f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165afa908115610e1a575f91611a7c575b5015611a525761062d92611a2392612edb565b611a2b612dfe565b60405173ffffffffffffffffffffffffffffffffffffffff90911681529081906020820190565b60046040517f02171e6a000000000000000000000000000000000000000000000000000000008152fd5b611a9e915060203d602011611aa4575b611a968183611592565b810190612a91565b5f611a10565b503d611a8c565b3461023e57604060031936011261023e575f54600435602435828110908390030282018181039190821161135d57611ae2826126ff565b925f5b838110611afa576040518061062d878261082d565b8083019081841161135d5782821015611b6557611b5f611b556001935f80527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563015473ffffffffffffffffffffffffffffffffffffffff1690565b61133c838961274e565b01611ae5565b611ff9565b3461023e57602060031936011261023e5773ffffffffffffffffffffffffffffffffffffffff600435611b9c816106a6565b165f526001602052602060405f2054604051908152f35b3461023e57602060031936011261023e576020600435611bd2816106a6565b73ffffffffffffffffffffffffffffffffffffffff8091165f526005825260405f2054161515604051908152f35b3461023e57602060031936011261023e5773ffffffffffffffffffffffffffffffffffffffff600435611c32816106a6565b165f526002602052602060405f2054604051908152f35b3461023e575f60031936011261023e5760205f54604051908152f35b3461023e5760031960a08136011261023e576004359067ffffffffffffffff9081831161023e5761014090833603011261023e5760243590811161023e5761062d91611cb8611a2b923690600401610c59565b909160643591611cc7836106a6565b6084359360443592600401612aa9565b3461023e5773ffffffffffffffffffffffffffffffffffffffff611cfa36610b28565b91929092165f52600260205260405f2090815490818082039110020182810390811161135d57611d29816126ff565b915f5b828110611d41576040518061062d868261082d565b8085019081861161135d57611d5e6113326107c960019486612026565b01611d2c565b3461023e57602060031936011261023e5773ffffffffffffffffffffffffffffffffffffffff600435611d96816106a6565b165f526003602052602060405f2054604051908152f35b611db56115f4565b905f82525f60208301525f60408301525f60608301525f60808301525f60a08301525f60c08301525f60e08301525f6101008301525f6101208301525f6101408301525f6101608301525f6101808301525f6101a08301525f6101c08301525f6101e08301525f6102008301525f6102208301525f610240830152565b90600182811c92168015611e79575b6020831014611e4c57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691611e41565b9060405191825f8254611e9581611e32565b908184526020946001916001811690815f14611f015750600114611ec3575b5050506106d192500383611592565b5f90815285812095935091905b818310611ee95750506106d193508201015f8080611eb4565b85548884018501529485019487945091830191611ed0565b9150506106d19593507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091501682840152151560051b8201015f8080611eb4565b90604051611f4f8161153f565b60e0611fad60028395610ac88154610a7773ffffffffffffffffffffffffffffffffffffffff918281168952611fa369ffffffffffffffffffff8260a01c1660208b019069ffffffffffffffffffff169052565b60f01c6040890152565b910152565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b9190820391821161135d57565b9190820180921161135d57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b8054821015611b65575f5260205f2001905f90565b51906106d1826106a6565b9081602091031261023e57516109c1816106a6565b6040513d5f823e3d90fd5b601f821161207357505050565b5f5260205f20906020601f840160051c830193106120ab575b601f0160051c01905b8181106120a0575050565b5f8155600101612095565b909150819061208c565b919091825167ffffffffffffffff811161155c576120dd816120d78454611e32565b84612066565b602080601f831160011461213c5750819061212d9394955f92612131575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b9055565b015190505f806120fb565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe083169561216e855f5260205f2090565b925f905b8882106121c857505083600195969710612191575b505050811b019055565b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c191690555f8080612187565b80600185968294968601518155019501930190612172565b600260e06106d19361224473ffffffffffffffffffffffffffffffffffffffff825116859073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b602081015184547fffff00000000000000000000ffffffffffffffffffffffffffffffffffffffff1660a09190911b7dffffffffffffffffffff000000000000000000000000000000000000000016178455604081015184547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660f09190911b7fffff000000000000000000000000000000000000000000000000000000000000161784556123fc600185016060830151151560ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691161781556123646123316080850151151590565b82547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1690151560081b61ff0016178255565b6123ad61237760a085015162ffffff1690565b82547fffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffff1660109190911b64ffffff000016178255565b60c083015181547fffffffffffffff0000000000000000000000000000000000000000ffffffffff1660289190911b78ffffffffffffffffffffffffffffffffffffffff000000000016179055565b015191016120b5565b5f546801000000000000000081101561155c5760018101805f55811015611b655773ffffffffffffffffffffffffffffffffffffffff905f80527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5630191167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b80546801000000000000000081101561155c576124ae91600182018155612026565b73ffffffffffffffffffffffffffffffffffffffff9291928084549260031b9316831b921b1916179055565b95909160a09569ffffffffffffffffffff9360e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f885f61ffff9b9f9e9a85908f8173ffffffffffffffffffffffffffffffffffffffff9b8c809b16835260c060208401528160c08401528483013701015201168a01019a16604089015216606087015216608085015216910152565b93959192959490946125a960016125a18773ffffffffffffffffffffffffffffffffffffffff165f52600460205260405f2090565b015460ff1690565b6126bd57866126b8946125df8585857f7735fb05b8e8ddc6039fa70a50d11434f6ba73219f3cc820e8100b10dcaf89ed9c612e07565b6126a36125ef5f5462ffffff1690565b61264a6125fa611615565b73ffffffffffffffffffffffffffffffffffffffff871681529169ffffffffffffffffffff8816602084015261ffff89166040840152600160608401526001608084015262ffffff1660a0830152565b73ffffffffffffffffffffffffffffffffffffffff841660c082015261267136848c611643565b60e082015261269e8973ffffffffffffffffffffffffffffffffffffffff165f52600460205260405f2090565b6121e0565b6126ac87612405565b604051978897886124da565b0390a1565b60046040517f3704fb41000000000000000000000000000000000000000000000000000000008152fd5b67ffffffffffffffff811161155c5760051b60200190565b90612709826126e7565b6127166040519182611592565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061274482946126e7565b0190602036910137565b8051821015611b655760209160051b010190565b929391909173ffffffffffffffffffffffffffffffffffffffff80941692835f52600460205260ff600160405f20015416156108035785858460a097846127da877fba32e46aec30b4be89696b127e3fc9923645feaf4184109ba3627ac28e4de3b59c61ffff9a69ffffffffffffffffffff99612e07565b885f5260046020526128a98760405f2061283c8460018301907fffffffffffffff0000000000000000000000000000000000000000ffffffffff78ffffffffffffffffffffffffffffffffffffffff000000000083549260281b169116179055565b7dffffffffffffffffffff000000000000000000000000000000000000000060a089901b1673ffffffffffffffffffffffffffffffffffffffff87161760f09290921b7fffff00000000000000000000000000000000000000000000000000000000000016919091179055565b604051988952166020880152166040860152166060840152166080820152a1565b73ffffffffffffffffffffffffffffffffffffffff16805f52600460205260ff600160405f2001541615610803576020817ff53ee7e14e8211031e94d3dbef2a1d525daa5842da39bffef5aa54c2cb76214a925f5260048252600160405f20017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff8154169055604051908152a1565b9198979593929096949861296b612de0565b6040517f0787c1fe0000000000000000000000000000000000000000000000000000000081523360048201526020816024817f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165afa908115610e1a575f91612a72575b5015611a5257612a1561072f8473ffffffffffffffffffffffffffffffffffffffff165f52600460205260405f2090565b93612a266107456060870151151590565b61080357612a3b612a6292612a689a86612edb565b61012084018051919c916bffffffffffffffffffffffff1660608e901b1790523691611643565b90613304565b91906106d1612dfe565b612a8b915060203d602011611aa457611a968183611592565b5f6129e4565b9081602091031261023e5751801515810361023e5790565b9094939291612ab6612de0565b6040517f0787c1fe00000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff91906020816024817f000000000000000000000000000000000000000000000000000000000000000087165afa908115610e1a575f91612c05575b5015611a5257612b85612b6b61012085013560601c73ffffffffffffffffffffffffffffffffffffffff165f52600560205260405f2090565b5473ffffffffffffffffffffffffffffffffffffffff1690565b91821615612bdb57612a62612bd297612bca612bc261072f8673ffffffffffffffffffffffffffffffffffffffff165f52600460205260405f2090565b953690611700565b923691611643565b906106d1612dfe565b60046040517f816fefa2000000000000000000000000000000000000000000000000000000008152fd5b612c1e915060203d602011611aa457611a968183611592565b5f612b32565b51906106d182610cb9565b51906106d1826116c2565b51906106d1826116e7565b519060ff8216820361023e57565b908161020091031261023e57612c67611622565b90612c718161203b565b8252612c7f6020820161203b565b6020830152612c906040820161203b565b6040830152612ca160608201612c24565b6060830152612cb260808201612c2f565b6080830152612cc360a08201612c24565b60a0830152612cd460c08201612c24565b60c0830152612ce560e08201612c3a565b60e0830152610100612cf8818301612c24565b90830152610120612d0a818301612c3a565b908301526101408082015190830152610160808201519083015261018080820151908301526101a080820151908301526101c0612d48818301612c45565b908301526101e0809101519082015290565b5f6101e0612d66611622565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e08201528261010082015282610120820152826101408201528261016082015282610180820152826101a0820152826101c082015201526109c1612dd1613b47565b60208082518301019101612c53565b63929eee14805c612df1576001905d565b637fa8a9875f526004601cfd5b5f63929eee145d565b909169ffffffffffffffffffff1615159061ffff73ffffffffffffffffffffffffffffffffffffffff809216159416928315159485612ed3575b8515612eb8575b508415612e97575b5050508115612e8b575b50612e6157565b60046040517f6d24edae000000000000000000000000000000000000000000000000000000008152fd5b6103e89150115f612e5a565b929350909182612ead575b5050905f8080612e50565b161590505f80612ea2565b838092965091612ecb575b50935f612e48565b90505f612ec3565b945084612e41565b92916001612f078573ffffffffffffffffffffffffffffffffffffffff165f52600460205260405f2090565b015460ff8116156108035760081c60ff161561309857605f612f473373ffffffffffffffffffffffffffffffffffffffff165f52600160205260405f2090565b54916040519281873b95847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8089016001898d3c8888019033908201526040601f82015283603f82015201373360601b17930101905ff591821561308b576106d190612fda84612fd53373ffffffffffffffffffffffffffffffffffffffff165f52600160205260405f2090565b61248c565b6040805173ffffffffffffffffffffffffffffffffffffffff8681168252831660208201527fa1d2f67e073e80e6ec12790c669d64ea9a5dbe85ed539a27549f7e447c480dfe9190a161304b8473ffffffffffffffffffffffffffffffffffffffff165f52600560205260405f2090565b9073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b63301164255f526004601cfd5b60046040517f0e8dc677000000000000000000000000000000000000000000000000000000008152fd5b9081602091031261023e575190565b906109c1949273ffffffffffffffffffffffffffffffffffffffff80921683521660208201526080604082015261312160808201845173ffffffffffffffffffffffffffffffffffffffff169052565b6020830151613175613141610140928360a08601526101c0850190610631565b60408601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808583030160c0860152610631565b60608501516fffffffffffffffffffffffffffffffff1660e0840152936131fa60808201516131ad610100918287019061ffff169052565b6131da60a0840151946131c9610120968789019061ffff169052565b60c085015163ffffffff1690870152565b60e083015161ffff1661016086015282015163ffffffff16610180850152565b01516101a08201526060818403910152610631565b805191908290602001825e015f815290565b6106d19061324361323d9493604051958693602085019061320f565b9061320f565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101845283611592565b96959373ffffffffffffffffffffffffffffffffffffffff6132c66101209a959d9c9b97946132b86fffffffffffffffffffffffffffffffff958c6101408091528d0190610631565b908b820360208d0152610631565b9c16604089015216606087015261ffff928380921660808801521660a086015263ffffffff80941660c08601521660e0840152166101008201520152565b9293909695919461337d602061332e865173ffffffffffffffffffffffffffffffffffffffff1690565b6040517fb0018c6000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909116600482015291829081906024820190565b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa908115610e1a575f91613b28575b50613afe5761012084015160601c92338360601c148015613ad4575b15613aaa57613421613408875173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff8316908114801590613a78575b613a4e57613a1a575b505061350060206134c97f0000000000000000000000000000000000000000000000000000000000000000847f0000000000000000000000000000000000000000000000000000000000000000929190604051935f5260205260405273ffffffffffffffffffffffffffffffffffffffff6055600b201691604052565b98604051809381927f28fd0307000000000000000000000000000000000000000000000000000000008352888d33600486016130d1565b03815f73ffffffffffffffffffffffffffffffffffffffff88165af1908115610e1a575f916139eb575b50610120840152825173ffffffffffffffffffffffffffffffffffffffff1661355290613cae565b90602084015184516135779073ffffffffffffffffffffffffffffffffffffffff1690565b61358090613e5b565b61358991613221565b94604085015185516135ae9073ffffffffffffffffffffffffffffffffffffffff1690565b6135b790613fb6565b6135c091613221565b9285516135e09073ffffffffffffffffffffffffffffffffffffffff1690565b9560c08301516136039073ffffffffffffffffffffffffffffffffffffffff1690565b60409093015161ffff1660608201516fffffffffffffffffffffffffffffffff16608083015161ffff1660a084015161ffff169060c08501516136499063ffffffff1690565b9260e086015161365a9061ffff1690565b9461010087015161366e9063ffffffff1690565b9661012001519861367d611622565b33815273ffffffffffffffffffffffffffffffffffffffff9e8f1660208201529d1660408e015261ffff1660608d01526fffffffffffffffffffffffffffffffff1660808c015261ffff1660a08b015261ffff1660c08a015263ffffffff1660e089015261ffff1661010088015263ffffffff166101208701525f6101408701819052610160870181905261018087018190526101a087015260ff166101c08601526101e085015261372e85613ce2565b61016086015261014085015261374382613ce2565b6101a086015261018085015261375884613d0d565b873b6139c157613788907f0000000000000000000000000000000000000000000000000000000000000000614157565b5073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163b1561023e576040517fa79b9ec900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff88166004820152905f82806024810103818373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1908115610e1a577f6f8c7c94fc16393d1ebec38de9899ba8c6bd860a025aa60063b7cf4c40a16c09946139118a612fd573ffffffffffffffffffffffffffffffffffffffff976138ed83612fd58e829b6139a39b6139a8575b505f7f78cf023adbf9fec5dbd8e980a6a3526d1d11c66c894ab342ade9ace71f5619045d73ffffffffffffffffffffffffffffffffffffffff165f52600260205260405f2090565b73ffffffffffffffffffffffffffffffffffffffff165f52600360205260405f2090565b602081015173ffffffffffffffffffffffffffffffffffffffff1660808201516fffffffffffffffffffffffffffffffff1660a083015161ffff1660c084015161ffff169061396760e086015163ffffffff1690565b9261397861010087015161ffff1690565b946101e061398e61012089015163ffffffff1690565b970151978b6040519c8d9c169f169d8b61326f565b0390a3565b806139b56139bb92611561565b80610234565b5f6138a5565b60046040517f0313b285000000000000000000000000000000000000000000000000000000008152fd5b613a0d915060203d602011613a13575b613a058183611592565b8101906130c2565b5f61352a565b503d6139fb565b613a4791613a3f60c088015173ffffffffffffffffffffffffffffffffffffffff1690565b903390613c59565b5f8061344c565b60046040517f36a9d011000000000000000000000000000000000000000000000000000000008152fd5b50613aa2613a93602089015169ffffffffffffffffffff1690565b69ffffffffffffffffffff1690565b821415613443565b60046040517f861f5c0b000000000000000000000000000000000000000000000000000000008152fd5b507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008316156133e2565b60046040517f47426645000000000000000000000000000000000000000000000000000000008152fd5b613b41915060203d602011611aa457611a968183611592565b5f6133c6565b604051905f82525f7f78cf023adbf9fec5dbd8e980a6a3526d1d11c66c894ab342ade9ace71f561904805c9060018260011c9160018416938415613c4f575b6020948585108114613c3e57848952908115613c035750600114613bae575b50505050604052565b5f908152929350837f810543fe2cb308dcc7090dc0718da3cf7b1ea9e6ee6878e7d574d84a057628975b838510613bef57505050508301015f808080613ba5565b805c88860183015293019284908201613bd8565b93505050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009192501690830152604082015f808080613ba5565b602286634e487b715f52526024601cfd5b92607f1692613b86565b601c5f60649281946020966040519860605260405260601b602c526f23b872dd000000000000000000000000600c525af13d1560015f5114171615613ca1575f606052604052565b637939f4245f526004601cfd5b5f6004601c60209363313ce56784525afa6101005f511060203d14161615613cd5575f5190565b633394d1705f526004601cfd5b90603f825111613d0057601f82015191603f601f8251119101510290565b6319a65cb65f526004601cfd5b604051815173ffffffffffffffffffffffffffffffffffffffff1660208201526106d191602081015173ffffffffffffffffffffffffffffffffffffffff166040830152604081015173ffffffffffffffffffffffffffffffffffffffff166060830152606081015161ffff16608083015260808101516fffffffffffffffffffffffffffffffff1660a083015260a081015161ffff1660c083015260c081015161ffff1660e083015260e0810151613dd1610100918285019063ffffffff169052565b810151613de7610120918285019061ffff169052565b810151613dff610140918285019063ffffffff169052565b810151610160908184015281015161018090818401528101516101a090818401528101516101c0908184015281015190613e416101e0928385019060ff169052565b015161020090818301528152613e5681611575565b614098565b5f6004601c82936306fdde0384525afa3d6020149015605f3d118217158117613f8b575015613f1a5760205f803e6109c15f519060018219018216916007836fffffffffffffffffffffffffffffffff10811b821560081b1784811c67ffffffffffffffff1060061b1784811c63ffffffff1060051b177e1f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405601f6040519660408801604052831c63d76453e004161a1760ff030160031c83526020830152565b60405190602063ffffffe07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3d01168084016040527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03d0182853e013d18613f7e57565b634cb9c0005f526004601cfd5b613f9c57634cb9c0005f526004601cfd5b3d613fae57632ed09f545f526004601cfd5b3d5f803e3d5ffd5b5f6004601c82936395d89b4184525afa3d6020149015605f3d118217158117614075575015613f1a5760205f803e6109c15f519060018219018216916007836fffffffffffffffffffffffffffffffff10811b821560081b1784811c67ffffffffffffffff1060061b1784811c63ffffffff1060051b177e1f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405601f6040519660408801604052831c63d76453e004161a1760ff030160031c83526020830152565b61408657634cb9c0005f526004601cfd5b3d613fae57633ddcc60a5f526004601cfd5b805160208082109081156140e057506001146140b2575050565b60209060011b910151177f78cf023adbf9fec5dbd8e980a6a3526d1d11c66c894ab342ade9ace71f5619045d565b92600191507f78cf023adbf9fec5dbd8e980a6a3526d1d11c66c894ab342ade9ace71f56190483831b8301815d5f527f810543fe2cb308dcc7090dc0718da3cf7b1ea9e6ee6878e7d574d84a05762897905f915b84831061414357505050505050565b838691828585010151815d01920191614134565b6040518160017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83943b019384923c5ff590811561308b5756fea164736f6c6343000819000a000000000000000000000000feb516d9d946dd487a9346f6fee11f40c6945ee4000000000000000000000000437e0551892c2c9b06d3ffd248fe60572e08cd1a000000000000000000000000ac3216fa28f81b8fae150fb5626ca79c7a570dafc152ead6073d54f964e3c2fd317ec6c774e67465cf3e6fb9551badb88a09e43f

Deployed Bytecode

0x60806040526004361015610011575f80fd5b5f3560e01c806304032dbb1461022f57806306fdde031461022a578063155733ef146102255780631caab59d146102205780632812d2de1461021b5780632b07f2801461021657806333ee81f2146102115780633476b76d1461020c578063356dd352146102075780633c231166146102025780633fd83e1e146101fd57806341f259b8146101f85780634272711d146101f35780634bd1acf3146101ee5780634c6c848f146101e9578063546355701461019957806362ef8119146101e45780636e50f960146101df5780637a749060146101da5780639c5cd31a146101d55780639f2f94b3146101d0578063ad78e45a146101cb578063aeefe08c146101c6578063c29a7677146101c1578063cd01e52a146101bc578063cdbb8374146101b7578063d14a5402146101b2578063d19bd571146101ad578063d1a0745c146101a8578063d9d58025146101a3578063e7ca86e31461019e578063e7e5db4f14610199578063eae6ff66146101945763fe604ec71461018f575f80fd5b611d64565b611cd7565b611144565b611c65565b611c49565b611c00565b611bb3565b611b6a565b611aab565b61194f565b6118c7565b61188d565b6117d9565b611451565b611362565b6112ab565b611220565b611194565b61108b565b611000565b610fb0565b610f12565b610ec1565b610e6f565b610e1f565b610cd0565b610b4b565b610add565b6109c4565b61087e565b6106de565b610674565b6103c9565b5f91031261023e57565b5f80fd5b815173ffffffffffffffffffffffffffffffffffffffff1681526102608101929160208181015160ff169083015260408101516040830152606081015160608301526080810151608083015260a081015160a08301526102bf60c082015160c084019073ffffffffffffffffffffffffffffffffffffffff169052565b60e08181015173ffffffffffffffffffffffffffffffffffffffff16908301526101008181015173ffffffffffffffffffffffffffffffffffffffff1690830152610120818101516fffffffffffffffffffffffffffffffff16908301526101408181015161ffff16908301526101608181015161ffff16908301526101808181015161ffff16908301526101a08181015163ffffffff16908301526101c08181015161ffff16908301526101e08181015163ffffffff16908301526102008181015173ffffffffffffffffffffffffffffffffffffffff16908301526102208181015173ffffffffffffffffffffffffffffffffffffffff169083015261024080910151910152565b3461023e575f60031936011261023e576103e1611dad565b6103e9612d5a565b602081015173ffffffffffffffffffffffffffffffffffffffff1682526101408082015160408401526101608083015160608501526101808084015160808601526101a08085015160a08701526101c0808601519093929060ff1660ff166020880152855173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660c0880152604086015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660e088015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000437e0551892c2c9b06d3ffd248fe60572e08cd1a16610100888101919091529260808701516fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff166101208981019190915295606088015161ffff1661ffff169089015260a087015161ffff1661ffff169088015260c086015161ffff1661ffff169087015260e085015163ffffffff1663ffffffff169086015283015161ffff1661ffff169084015281015163ffffffff1663ffffffff166101e0838101919091529073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000feb516d9d946dd487a9346f6fee11f40c6945ee4166102008401527f1777adabd324f814e2b0e28f6edf876dce01d7d66358c9acfe87b1b5f38338d55473ffffffffffffffffffffffffffffffffffffffff16610220840152015161024082015260405161062d819282610242565b0390f35b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b3461023e575f60031936011261023e57731357696c64636174486f6f6b73466163746f72796053526020805260606020f35b73ffffffffffffffffffffffffffffffffffffffff81160361023e57565b60a435906106d1826106a6565b565b35906106d1826106a6565b3461023e57602060031936011261023e57600480356106fc816106a6565b610704612de0565b61073461072f8273ffffffffffffffffffffffffffffffffffffffff165f52600460205260405f2090565b611f42565b906107496107456060840151151590565b1590565b610803576107759073ffffffffffffffffffffffffffffffffffffffff165f52600260205260405f2090565b6107866040825493015161ffff1690565b90604051916040830160405263ae6ea19183526020830152601c8092015f5b8481106107b6576107b4612dfe565b005b5f80602484826107e96107c9878a612026565b905473ffffffffffffffffffffffffffffffffffffffff9160031b1c1690565b5af1156107f8576001016107a5565b8584634484a4a95f52fd5b60046040517f8cb2227e000000000000000000000000000000000000000000000000000000008152fd5b60209060206040818301928281528551809452019301915f5b828110610854575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101610846565b3461023e5760208060031936011261023e5760043561089c816106a6565b73ffffffffffffffffffffffffffffffffffffffff8091165f52600260205260405f20916040518092602085549283815201945f5260205f20925f915b8383106108fc5761062d866108f0818a0382611592565b6040519182918261082d565b84548116875295810195600194850194909201916108d9565b61012060e06109c1936020845273ffffffffffffffffffffffffffffffffffffffff815116602085015269ffffffffffffffffffff602082015116604085015261096a6040820151606086019061ffff169052565b6060810151151560808501526080810151151560a085015260a081015162ffffff1660c085015260c081015173ffffffffffffffffffffffffffffffffffffffff1684830152015191610100808201520190610631565b90565b3461023e57602060031936011261023e5761062d6004356109e4816106a6565b6040606060e082516109f58161153f565b5f81525f60208201525f848201525f838201525f60808201525f60a08201525f60c0820152015273ffffffffffffffffffffffffffffffffffffffff8092165f526004602052610ace6002825f20610ac8845195610a528761153f565b8254818116885269ffffffffffffffffffff60a082901c16602089015260f01c868801525b600183015460ff811615156060890152600881901c60ff1615156080890152601081901c62ffffff1660a089015260281c1660c087019073ffffffffffffffffffffffffffffffffffffffff169052565b01611e83565b60e08301525191829182610915565b3461023e57602060031936011261023e576020600435610afc816106a6565b73ffffffffffffffffffffffffffffffffffffffff8091165f526005825260405f205416604051908152f35b600319606091011261023e57600435610b40816106a6565b906024359060443590565b3461023e57610b5936610b28565b610b61612de0565b610b8c61072f8473ffffffffffffffffffffffffffffffffffffffff165f52600460205260405f2090565b92610b9d6107456060860151151590565b610803576040610be984610be4610bd5610bf39573ffffffffffffffffffffffffffffffffffffffff165f52600260205260405f2090565b95865490818082039110020190565b611fdf565b94015161ffff1690565b91604051926040840160405263ae6ea19184526020840152601c809301915f5b858110610c22576107b4612dfe565b5f8060248682610c3e6107c9610c38888b611fec565b89612026565b5af115610c4d57600101610c13565b600485634484a4a95f52fd5b9181601f8401121561023e5782359167ffffffffffffffff831161023e576020838186019501011161023e57565b6084359069ffffffffffffffffffff8216820361023e57565b6064359069ffffffffffffffffffff8216820361023e57565b61ffff81160361023e57565b35906106d182610cb9565b3461023e5760c060031936011261023e57600435610ced816106a6565b60243567ffffffffffffffff811161023e57610d0d903690600401610c59565b604435610d19816106a6565b60643590610d26826106a6565b610d2e610c87565b9260a43594610d3c86610cb9565b73ffffffffffffffffffffffffffffffffffffffff6040517f8da5cb5b000000000000000000000000000000000000000000000000000000008152602081600481857f000000000000000000000000feb516d9d946dd487a9346f6fee11f40c6945ee4165afa908115610e1a575f91610deb575b50163303610dc1576107b49661256c565b60046040517f44d783c6000000000000000000000000000000000000000000000000000000008152fd5b610e0d915060203d602011610e13575b610e058183611592565b810190612046565b5f610db0565b503d610dfb565b61205b565b3461023e575f60031936011261023e57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ac3216fa28f81b8fae150fb5626ca79c7a570daf168152f35b3461023e575f60031936011261023e5760207f1777adabd324f814e2b0e28f6edf876dce01d7d66358c9acfe87b1b5f38338d55473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b3461023e57602060031936011261023e5773ffffffffffffffffffffffffffffffffffffffff600435610ef3816106a6565b165f526004602052602060ff600160405f200154166040519015158152f35b3461023e575f8060031936011261023e5773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000feb516d9d946dd487a9346f6fee11f40c6945ee416803b1561023e575f80916024604051809481937fd91ae8c20000000000000000000000000000000000000000000000000000000083523060048401525af18015610e1a57610fa6575080f35b6107b49150611561565b3461023e575f60031936011261023e57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000437e0551892c2c9b06d3ffd248fe60572e08cd1a168152f35b3461023e5760208060031936011261023e5760043561101e816106a6565b73ffffffffffffffffffffffffffffffffffffffff8091165f52600360205260405f20916040518092602085549283815201945f5260205f20925f915b8383106110725761062d866108f0818a0382611592565b845481168752958101956001948501949092019161105b565b3461023e57602060031936011261023e576004356110a8816106a6565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000feb516d9d946dd487a9346f6fee11f40c6945ee4163303611137577f1777adabd324f814e2b0e28f6edf876dce01d7d66358c9acfe87b1b5f38338d581815491555f526020527ff33499cccaa0611882086224cc48cd82ef54b66a4d2edf4ed67108dd516896d560405fa1005b634ee0b8f85f526004601cfd5b3461023e575f60031936011261023e57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000feb516d9d946dd487a9346f6fee11f40c6945ee4168152f35b3461023e5760208060031936011261023e576004356111b2816106a6565b73ffffffffffffffffffffffffffffffffffffffff8091165f52600191600160205260405f20926040518093602086549283815201955f5260205f20935f915b8383106112095761062d876108f0818b0382611592565b8554811688529681019694840194918401916111f2565b3461023e575f60031936011261023e576040515f8054808352818052602080840193927f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56392915b82821061127e5761062d856108f081890382611592565b835473ffffffffffffffffffffffffffffffffffffffff1686529485019460019384019390910190611267565b3461023e5773ffffffffffffffffffffffffffffffffffffffff6112ce36610b28565b91929092165f52600360205260405f2090815490818082039110020182810390811161135d576112fd816126ff565b915f5b828110611315576040518061062d868261082d565b8085019081861161135d576113576113326107c960019486612026565b61133c838861274e565b9073ffffffffffffffffffffffffffffffffffffffff169052565b01611300565b611fb2565b3461023e5760a060031936011261023e5760043561137f816106a6565b60243561138b816106a6565b604435611397816106a6565b61139f610ca0565b90608435926113ad84610cb9565b73ffffffffffffffffffffffffffffffffffffffff6040517f8da5cb5b000000000000000000000000000000000000000000000000000000008152602081600481857f000000000000000000000000feb516d9d946dd487a9346f6fee11f40c6945ee4165afa908115610e1a575f91611432575b50163303610dc1576107b494612762565b61144b915060203d602011610e1357610e058183611592565b5f611421565b3461023e57602060031936011261023e5760043561146e816106a6565b73ffffffffffffffffffffffffffffffffffffffff6040517f8da5cb5b000000000000000000000000000000000000000000000000000000008152602081600481857f000000000000000000000000feb516d9d946dd487a9346f6fee11f40c6945ee4165afa908115610e1a575f916114f3575b50163303610dc1576107b4906128ca565b61150c915060203d602011610e1357610e058183611592565b5f6114e2565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610100810190811067ffffffffffffffff82111761155c57604052565b611512565b67ffffffffffffffff811161155c57604052565b610220810190811067ffffffffffffffff82111761155c57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761155c57604052565b60405190610140820182811067ffffffffffffffff82111761155c57604052565b60405190610260820182811067ffffffffffffffff82111761155c57604052565b604051906106d18261153f565b60405190610200820182811067ffffffffffffffff82111761155c57604052565b92919267ffffffffffffffff821161155c576040519161168b60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184611592565b82948184528183011161023e578281602093845f960137010152565b9080601f8301121561023e578160206109c193359101611643565b6fffffffffffffffffffffffffffffffff81160361023e57565b35906106d1826116c2565b63ffffffff81160361023e57565b35906106d1826116e7565b9190916101408184031261023e576117166115d3565b92611720826106d3565b845267ffffffffffffffff90602083013582811161023e57816117449185016116a7565b6020860152604083013591821161023e576117609183016116a7565b6040840152611771606082016116dc565b606084015261178260808201610cc5565b608084015261179360a08201610cc5565b60a08401526117a460c082016116f5565b60c08401526117b560e08201610cc5565b60e08401526101006117c88183016116f5565b908401526101208091013590830152565b3461023e5760e060031936011261023e576004356117f6816106a6565b67ffffffffffffffff9060243582811161023e57611818903690600401610c59565b909160443584811161023e57611832903690600401611700565b60643594851161023e5761184d611864953690600401610c59565b916118566106c4565b9460c4359660843595612959565b6040805173ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152f35b3461023e575f60031936011261023e5760206040517fc152ead6073d54f964e3c2fd317ec6c774e67465cf3e6fb9551badb88a09e43f8152f35b3461023e57602060031936011261023e57604080517f0000000000000000000000ffdd7dd3b5076cf89440d05585ff56d246386207be5f5260043560209081527fc152ead6073d54f964e3c2fd317ec6c774e67465cf3e6fb9551badb88a09e43f83526055600b209282905273ffffffffffffffffffffffffffffffffffffffff9092168152f35b3461023e57604060031936011261023e5760043561196c816106a6565b60243567ffffffffffffffff811161023e5761198c903690600401610c59565b611997929192612de0565b6040517f0787c1fe0000000000000000000000000000000000000000000000000000000081523360048201526020816024817f000000000000000000000000feb516d9d946dd487a9346f6fee11f40c6945ee473ffffffffffffffffffffffffffffffffffffffff165afa908115610e1a575f91611a7c575b5015611a525761062d92611a2392612edb565b611a2b612dfe565b60405173ffffffffffffffffffffffffffffffffffffffff90911681529081906020820190565b60046040517f02171e6a000000000000000000000000000000000000000000000000000000008152fd5b611a9e915060203d602011611aa4575b611a968183611592565b810190612a91565b5f611a10565b503d611a8c565b3461023e57604060031936011261023e575f54600435602435828110908390030282018181039190821161135d57611ae2826126ff565b925f5b838110611afa576040518061062d878261082d565b8083019081841161135d5782821015611b6557611b5f611b556001935f80527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563015473ffffffffffffffffffffffffffffffffffffffff1690565b61133c838961274e565b01611ae5565b611ff9565b3461023e57602060031936011261023e5773ffffffffffffffffffffffffffffffffffffffff600435611b9c816106a6565b165f526001602052602060405f2054604051908152f35b3461023e57602060031936011261023e576020600435611bd2816106a6565b73ffffffffffffffffffffffffffffffffffffffff8091165f526005825260405f2054161515604051908152f35b3461023e57602060031936011261023e5773ffffffffffffffffffffffffffffffffffffffff600435611c32816106a6565b165f526002602052602060405f2054604051908152f35b3461023e575f60031936011261023e5760205f54604051908152f35b3461023e5760031960a08136011261023e576004359067ffffffffffffffff9081831161023e5761014090833603011261023e5760243590811161023e5761062d91611cb8611a2b923690600401610c59565b909160643591611cc7836106a6565b6084359360443592600401612aa9565b3461023e5773ffffffffffffffffffffffffffffffffffffffff611cfa36610b28565b91929092165f52600260205260405f2090815490818082039110020182810390811161135d57611d29816126ff565b915f5b828110611d41576040518061062d868261082d565b8085019081861161135d57611d5e6113326107c960019486612026565b01611d2c565b3461023e57602060031936011261023e5773ffffffffffffffffffffffffffffffffffffffff600435611d96816106a6565b165f526003602052602060405f2054604051908152f35b611db56115f4565b905f82525f60208301525f60408301525f60608301525f60808301525f60a08301525f60c08301525f60e08301525f6101008301525f6101208301525f6101408301525f6101608301525f6101808301525f6101a08301525f6101c08301525f6101e08301525f6102008301525f6102208301525f610240830152565b90600182811c92168015611e79575b6020831014611e4c57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691611e41565b9060405191825f8254611e9581611e32565b908184526020946001916001811690815f14611f015750600114611ec3575b5050506106d192500383611592565b5f90815285812095935091905b818310611ee95750506106d193508201015f8080611eb4565b85548884018501529485019487945091830191611ed0565b9150506106d19593507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091501682840152151560051b8201015f8080611eb4565b90604051611f4f8161153f565b60e0611fad60028395610ac88154610a7773ffffffffffffffffffffffffffffffffffffffff918281168952611fa369ffffffffffffffffffff8260a01c1660208b019069ffffffffffffffffffff169052565b60f01c6040890152565b910152565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b9190820391821161135d57565b9190820180921161135d57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b8054821015611b65575f5260205f2001905f90565b51906106d1826106a6565b9081602091031261023e57516109c1816106a6565b6040513d5f823e3d90fd5b601f821161207357505050565b5f5260205f20906020601f840160051c830193106120ab575b601f0160051c01905b8181106120a0575050565b5f8155600101612095565b909150819061208c565b919091825167ffffffffffffffff811161155c576120dd816120d78454611e32565b84612066565b602080601f831160011461213c5750819061212d9394955f92612131575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790565b9055565b015190505f806120fb565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe083169561216e855f5260205f2090565b925f905b8882106121c857505083600195969710612191575b505050811b019055565b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c191690555f8080612187565b80600185968294968601518155019501930190612172565b600260e06106d19361224473ffffffffffffffffffffffffffffffffffffffff825116859073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b602081015184547fffff00000000000000000000ffffffffffffffffffffffffffffffffffffffff1660a09190911b7dffffffffffffffffffff000000000000000000000000000000000000000016178455604081015184547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660f09190911b7fffff000000000000000000000000000000000000000000000000000000000000161784556123fc600185016060830151151560ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691161781556123646123316080850151151590565b82547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1690151560081b61ff0016178255565b6123ad61237760a085015162ffffff1690565b82547fffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffff1660109190911b64ffffff000016178255565b60c083015181547fffffffffffffff0000000000000000000000000000000000000000ffffffffff1660289190911b78ffffffffffffffffffffffffffffffffffffffff000000000016179055565b015191016120b5565b5f546801000000000000000081101561155c5760018101805f55811015611b655773ffffffffffffffffffffffffffffffffffffffff905f80527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5630191167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b80546801000000000000000081101561155c576124ae91600182018155612026565b73ffffffffffffffffffffffffffffffffffffffff9291928084549260031b9316831b921b1916179055565b95909160a09569ffffffffffffffffffff9360e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f885f61ffff9b9f9e9a85908f8173ffffffffffffffffffffffffffffffffffffffff9b8c809b16835260c060208401528160c08401528483013701015201168a01019a16604089015216606087015216608085015216910152565b93959192959490946125a960016125a18773ffffffffffffffffffffffffffffffffffffffff165f52600460205260405f2090565b015460ff1690565b6126bd57866126b8946125df8585857f7735fb05b8e8ddc6039fa70a50d11434f6ba73219f3cc820e8100b10dcaf89ed9c612e07565b6126a36125ef5f5462ffffff1690565b61264a6125fa611615565b73ffffffffffffffffffffffffffffffffffffffff871681529169ffffffffffffffffffff8816602084015261ffff89166040840152600160608401526001608084015262ffffff1660a0830152565b73ffffffffffffffffffffffffffffffffffffffff841660c082015261267136848c611643565b60e082015261269e8973ffffffffffffffffffffffffffffffffffffffff165f52600460205260405f2090565b6121e0565b6126ac87612405565b604051978897886124da565b0390a1565b60046040517f3704fb41000000000000000000000000000000000000000000000000000000008152fd5b67ffffffffffffffff811161155c5760051b60200190565b90612709826126e7565b6127166040519182611592565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061274482946126e7565b0190602036910137565b8051821015611b655760209160051b010190565b929391909173ffffffffffffffffffffffffffffffffffffffff80941692835f52600460205260ff600160405f20015416156108035785858460a097846127da877fba32e46aec30b4be89696b127e3fc9923645feaf4184109ba3627ac28e4de3b59c61ffff9a69ffffffffffffffffffff99612e07565b885f5260046020526128a98760405f2061283c8460018301907fffffffffffffff0000000000000000000000000000000000000000ffffffffff78ffffffffffffffffffffffffffffffffffffffff000000000083549260281b169116179055565b7dffffffffffffffffffff000000000000000000000000000000000000000060a089901b1673ffffffffffffffffffffffffffffffffffffffff87161760f09290921b7fffff00000000000000000000000000000000000000000000000000000000000016919091179055565b604051988952166020880152166040860152166060840152166080820152a1565b73ffffffffffffffffffffffffffffffffffffffff16805f52600460205260ff600160405f2001541615610803576020817ff53ee7e14e8211031e94d3dbef2a1d525daa5842da39bffef5aa54c2cb76214a925f5260048252600160405f20017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff8154169055604051908152a1565b9198979593929096949861296b612de0565b6040517f0787c1fe0000000000000000000000000000000000000000000000000000000081523360048201526020816024817f000000000000000000000000feb516d9d946dd487a9346f6fee11f40c6945ee473ffffffffffffffffffffffffffffffffffffffff165afa908115610e1a575f91612a72575b5015611a5257612a1561072f8473ffffffffffffffffffffffffffffffffffffffff165f52600460205260405f2090565b93612a266107456060870151151590565b61080357612a3b612a6292612a689a86612edb565b61012084018051919c916bffffffffffffffffffffffff1660608e901b1790523691611643565b90613304565b91906106d1612dfe565b612a8b915060203d602011611aa457611a968183611592565b5f6129e4565b9081602091031261023e5751801515810361023e5790565b9094939291612ab6612de0565b6040517f0787c1fe00000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff91906020816024817f000000000000000000000000feb516d9d946dd487a9346f6fee11f40c6945ee487165afa908115610e1a575f91612c05575b5015611a5257612b85612b6b61012085013560601c73ffffffffffffffffffffffffffffffffffffffff165f52600560205260405f2090565b5473ffffffffffffffffffffffffffffffffffffffff1690565b91821615612bdb57612a62612bd297612bca612bc261072f8673ffffffffffffffffffffffffffffffffffffffff165f52600460205260405f2090565b953690611700565b923691611643565b906106d1612dfe565b60046040517f816fefa2000000000000000000000000000000000000000000000000000000008152fd5b612c1e915060203d602011611aa457611a968183611592565b5f612b32565b51906106d182610cb9565b51906106d1826116c2565b51906106d1826116e7565b519060ff8216820361023e57565b908161020091031261023e57612c67611622565b90612c718161203b565b8252612c7f6020820161203b565b6020830152612c906040820161203b565b6040830152612ca160608201612c24565b6060830152612cb260808201612c2f565b6080830152612cc360a08201612c24565b60a0830152612cd460c08201612c24565b60c0830152612ce560e08201612c3a565b60e0830152610100612cf8818301612c24565b90830152610120612d0a818301612c3a565b908301526101408082015190830152610160808201519083015261018080820151908301526101a080820151908301526101c0612d48818301612c45565b908301526101e0809101519082015290565b5f6101e0612d66611622565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e08201528261010082015282610120820152826101408201528261016082015282610180820152826101a0820152826101c082015201526109c1612dd1613b47565b60208082518301019101612c53565b63929eee14805c612df1576001905d565b637fa8a9875f526004601cfd5b5f63929eee145d565b909169ffffffffffffffffffff1615159061ffff73ffffffffffffffffffffffffffffffffffffffff809216159416928315159485612ed3575b8515612eb8575b508415612e97575b5050508115612e8b575b50612e6157565b60046040517f6d24edae000000000000000000000000000000000000000000000000000000008152fd5b6103e89150115f612e5a565b929350909182612ead575b5050905f8080612e50565b161590505f80612ea2565b838092965091612ecb575b50935f612e48565b90505f612ec3565b945084612e41565b92916001612f078573ffffffffffffffffffffffffffffffffffffffff165f52600460205260405f2090565b015460ff8116156108035760081c60ff161561309857605f612f473373ffffffffffffffffffffffffffffffffffffffff165f52600160205260405f2090565b54916040519281873b95847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8089016001898d3c8888019033908201526040601f82015283603f82015201373360601b17930101905ff591821561308b576106d190612fda84612fd53373ffffffffffffffffffffffffffffffffffffffff165f52600160205260405f2090565b61248c565b6040805173ffffffffffffffffffffffffffffffffffffffff8681168252831660208201527fa1d2f67e073e80e6ec12790c669d64ea9a5dbe85ed539a27549f7e447c480dfe9190a161304b8473ffffffffffffffffffffffffffffffffffffffff165f52600560205260405f2090565b9073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b63301164255f526004601cfd5b60046040517f0e8dc677000000000000000000000000000000000000000000000000000000008152fd5b9081602091031261023e575190565b906109c1949273ffffffffffffffffffffffffffffffffffffffff80921683521660208201526080604082015261312160808201845173ffffffffffffffffffffffffffffffffffffffff169052565b6020830151613175613141610140928360a08601526101c0850190610631565b60408601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808583030160c0860152610631565b60608501516fffffffffffffffffffffffffffffffff1660e0840152936131fa60808201516131ad610100918287019061ffff169052565b6131da60a0840151946131c9610120968789019061ffff169052565b60c085015163ffffffff1690870152565b60e083015161ffff1661016086015282015163ffffffff16610180850152565b01516101a08201526060818403910152610631565b805191908290602001825e015f815290565b6106d19061324361323d9493604051958693602085019061320f565b9061320f565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101845283611592565b96959373ffffffffffffffffffffffffffffffffffffffff6132c66101209a959d9c9b97946132b86fffffffffffffffffffffffffffffffff958c6101408091528d0190610631565b908b820360208d0152610631565b9c16604089015216606087015261ffff928380921660808801521660a086015263ffffffff80941660c08601521660e0840152166101008201520152565b9293909695919461337d602061332e865173ffffffffffffffffffffffffffffffffffffffff1690565b6040517fb0018c6000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909116600482015291829081906024820190565b038173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000feb516d9d946dd487a9346f6fee11f40c6945ee4165afa908115610e1a575f91613b28575b50613afe5761012084015160601c92338360601c148015613ad4575b15613aaa57613421613408875173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff8316908114801590613a78575b613a4e57613a1a575b505061350060206134c97fc152ead6073d54f964e3c2fd317ec6c774e67465cf3e6fb9551badb88a09e43f847f0000000000000000000000ffdd7dd3b5076cf89440d05585ff56d246386207be929190604051935f5260205260405273ffffffffffffffffffffffffffffffffffffffff6055600b201691604052565b98604051809381927f28fd0307000000000000000000000000000000000000000000000000000000008352888d33600486016130d1565b03815f73ffffffffffffffffffffffffffffffffffffffff88165af1908115610e1a575f916139eb575b50610120840152825173ffffffffffffffffffffffffffffffffffffffff1661355290613cae565b90602084015184516135779073ffffffffffffffffffffffffffffffffffffffff1690565b61358090613e5b565b61358991613221565b94604085015185516135ae9073ffffffffffffffffffffffffffffffffffffffff1690565b6135b790613fb6565b6135c091613221565b9285516135e09073ffffffffffffffffffffffffffffffffffffffff1690565b9560c08301516136039073ffffffffffffffffffffffffffffffffffffffff1690565b60409093015161ffff1660608201516fffffffffffffffffffffffffffffffff16608083015161ffff1660a084015161ffff169060c08501516136499063ffffffff1690565b9260e086015161365a9061ffff1690565b9461010087015161366e9063ffffffff1690565b9661012001519861367d611622565b33815273ffffffffffffffffffffffffffffffffffffffff9e8f1660208201529d1660408e015261ffff1660608d01526fffffffffffffffffffffffffffffffff1660808c015261ffff1660a08b015261ffff1660c08a015263ffffffff1660e089015261ffff1661010088015263ffffffff166101208701525f6101408701819052610160870181905261018087018190526101a087015260ff166101c08601526101e085015261372e85613ce2565b61016086015261014085015261374382613ce2565b6101a086015261018085015261375884613d0d565b873b6139c157613788907f000000000000000000000000ac3216fa28f81b8fae150fb5626ca79c7a570daf614157565b5073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000feb516d9d946dd487a9346f6fee11f40c6945ee4163b1561023e576040517fa79b9ec900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff88166004820152905f82806024810103818373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000feb516d9d946dd487a9346f6fee11f40c6945ee4165af1908115610e1a577f6f8c7c94fc16393d1ebec38de9899ba8c6bd860a025aa60063b7cf4c40a16c09946139118a612fd573ffffffffffffffffffffffffffffffffffffffff976138ed83612fd58e829b6139a39b6139a8575b505f7f78cf023adbf9fec5dbd8e980a6a3526d1d11c66c894ab342ade9ace71f5619045d73ffffffffffffffffffffffffffffffffffffffff165f52600260205260405f2090565b73ffffffffffffffffffffffffffffffffffffffff165f52600360205260405f2090565b602081015173ffffffffffffffffffffffffffffffffffffffff1660808201516fffffffffffffffffffffffffffffffff1660a083015161ffff1660c084015161ffff169061396760e086015163ffffffff1690565b9261397861010087015161ffff1690565b946101e061398e61012089015163ffffffff1690565b970151978b6040519c8d9c169f169d8b61326f565b0390a3565b806139b56139bb92611561565b80610234565b5f6138a5565b60046040517f0313b285000000000000000000000000000000000000000000000000000000008152fd5b613a0d915060203d602011613a13575b613a058183611592565b8101906130c2565b5f61352a565b503d6139fb565b613a4791613a3f60c088015173ffffffffffffffffffffffffffffffffffffffff1690565b903390613c59565b5f8061344c565b60046040517f36a9d011000000000000000000000000000000000000000000000000000000008152fd5b50613aa2613a93602089015169ffffffffffffffffffff1690565b69ffffffffffffffffffff1690565b821415613443565b60046040517f861f5c0b000000000000000000000000000000000000000000000000000000008152fd5b507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008316156133e2565b60046040517f47426645000000000000000000000000000000000000000000000000000000008152fd5b613b41915060203d602011611aa457611a968183611592565b5f6133c6565b604051905f82525f7f78cf023adbf9fec5dbd8e980a6a3526d1d11c66c894ab342ade9ace71f561904805c9060018260011c9160018416938415613c4f575b6020948585108114613c3e57848952908115613c035750600114613bae575b50505050604052565b5f908152929350837f810543fe2cb308dcc7090dc0718da3cf7b1ea9e6ee6878e7d574d84a057628975b838510613bef57505050508301015f808080613ba5565b805c88860183015293019284908201613bd8565b93505050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009192501690830152604082015f808080613ba5565b602286634e487b715f52526024601cfd5b92607f1692613b86565b601c5f60649281946020966040519860605260405260601b602c526f23b872dd000000000000000000000000600c525af13d1560015f5114171615613ca1575f606052604052565b637939f4245f526004601cfd5b5f6004601c60209363313ce56784525afa6101005f511060203d14161615613cd5575f5190565b633394d1705f526004601cfd5b90603f825111613d0057601f82015191603f601f8251119101510290565b6319a65cb65f526004601cfd5b604051815173ffffffffffffffffffffffffffffffffffffffff1660208201526106d191602081015173ffffffffffffffffffffffffffffffffffffffff166040830152604081015173ffffffffffffffffffffffffffffffffffffffff166060830152606081015161ffff16608083015260808101516fffffffffffffffffffffffffffffffff1660a083015260a081015161ffff1660c083015260c081015161ffff1660e083015260e0810151613dd1610100918285019063ffffffff169052565b810151613de7610120918285019061ffff169052565b810151613dff610140918285019063ffffffff169052565b810151610160908184015281015161018090818401528101516101a090818401528101516101c0908184015281015190613e416101e0928385019060ff169052565b015161020090818301528152613e5681611575565b614098565b5f6004601c82936306fdde0384525afa3d6020149015605f3d118217158117613f8b575015613f1a5760205f803e6109c15f519060018219018216916007836fffffffffffffffffffffffffffffffff10811b821560081b1784811c67ffffffffffffffff1060061b1784811c63ffffffff1060051b177e1f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405601f6040519660408801604052831c63d76453e004161a1760ff030160031c83526020830152565b60405190602063ffffffe07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3d01168084016040527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03d0182853e013d18613f7e57565b634cb9c0005f526004601cfd5b613f9c57634cb9c0005f526004601cfd5b3d613fae57632ed09f545f526004601cfd5b3d5f803e3d5ffd5b5f6004601c82936395d89b4184525afa3d6020149015605f3d118217158117614075575015613f1a5760205f803e6109c15f519060018219018216916007836fffffffffffffffffffffffffffffffff10811b821560081b1784811c67ffffffffffffffff1060061b1784811c63ffffffff1060051b177e1f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405601f6040519660408801604052831c63d76453e004161a1760ff030160031c83526020830152565b61408657634cb9c0005f526004601cfd5b3d613fae57633ddcc60a5f526004601cfd5b805160208082109081156140e057506001146140b2575050565b60209060011b910151177f78cf023adbf9fec5dbd8e980a6a3526d1d11c66c894ab342ade9ace71f5619045d565b92600191507f78cf023adbf9fec5dbd8e980a6a3526d1d11c66c894ab342ade9ace71f56190483831b8301815d5f527f810543fe2cb308dcc7090dc0718da3cf7b1ea9e6ee6878e7d574d84a05762897905f915b84831061414357505050505050565b838691828585010151815d01920191614134565b6040518160017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83943b019384923c5ff590811561308b5756fea164736f6c6343000819000a

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000feb516d9d946dd487a9346f6fee11f40c6945ee4000000000000000000000000437e0551892c2c9b06d3ffd248fe60572e08cd1a000000000000000000000000ac3216fa28f81b8fae150fb5626ca79c7a570dafc152ead6073d54f964e3c2fd317ec6c774e67465cf3e6fb9551badb88a09e43f

-----Decoded View---------------
Arg [0] : archController_ (address): 0xfEB516d9D946dD487A9346F6fee11f40C6945eE4
Arg [1] : _sanctionsSentinel (address): 0x437e0551892C2C9b06d3fFd248fe60572e08CD1A
Arg [2] : _marketInitCodeStorage (address): 0xAc3216FA28F81B8FaE150fB5626ca79C7a570DAf
Arg [3] : _marketInitCodeHash (uint256): 87442882014718392767687475768806239364207889234860621178338783312000729080895

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000feb516d9d946dd487a9346f6fee11f40c6945ee4
Arg [1] : 000000000000000000000000437e0551892c2c9b06d3ffd248fe60572e08cd1a
Arg [2] : 000000000000000000000000ac3216fa28f81b8fae150fb5626ca79c7a570daf
Arg [3] : c152ead6073d54f964e3c2fd317ec6c774e67465cf3e6fb9551badb88a09e43f


Deployed Bytecode Sourcemap

960:23507:1:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;-1:-1:-1;;960:23507:1;;;;;;;:::i;:::-;15188:25;;:::i;:::-;15239:9;;;960:23507;;;;;15283:19;;;;960:23507;15254:26;;;960:23507;15337:19;;;;960:23507;15308:26;;;960:23507;15393:21;;;;960:23507;15362:28;;;960:23507;15451:21;;;;960:23507;15420:28;;;960:23507;15500:12;;;;960:23507;15500:12;;15337:19;15393:21;960:23507;;;;15239:9;15478:19;;960:23507;;;;;;;15518:19;;;960:23507;15254:26;15584:16;;960:23507;;;;;15558:23;;;960:23507;;15628:17;960:23507;15606:19;;;;960:23507;;;;15606:19;15362:28;15679:18;;960:23507;;;;;15651:25;;;;960:23507;;;;15651:25;15308:26;15732:19;;960:23507;;;;;15703:26;;;960:23507;15420:28;15789:22;;960:23507;;;;;15757:29;;;960:23507;15518:19;15849:22;;960:23507;;;;;15817:29;;;960:23507;15558:23;15914:27;;960:23507;;;;;15877:34;;;960:23507;15977:20;;960:23507;;;;;15947:27;;;960:23507;16039:26;;960:23507;;;;;16003:33;;;;960:23507;;;;16003:33;960:23507;16099:15;960:23507;16071:25;;;960:23507;;;;;16120:24;;;960:23507;16187:9;960:23507;16168:16;;;960:23507;15254:26;960:23507;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;960:23507:1;;;;;;;;;;;:::o;:::-;;;;;-1:-1:-1;;960:23507:1;;;;;2170:129;;;;;;;;;960:23507;;;;;;;:::o;:::-;;;;;;;:::i;:::-;:::o;:::-;;;;;;:::i;:::-;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;;:::i;:::-;1258:92:3;;:::i;:::-;960:23507:1;22752:31;;960:23507;;;;;;;;;;;;22752:31;960:23507;:::i;:::-;22794:14;22793:15;960:23507;22794:14;;;960:23507;;;;;;22793:15;;960:23507;22793:15;22789:51;;22875:38;;960:23507;;;;22875:23;960:23507;;;;;;;22875:38;960:23507;23114:23;960:23507;;23114:23;;960:23507;;;;;;23143:621;23114:23;23143:621;;23114:23;23143:621;;23114:23;23143:621;;;;960:23507;23143:621;;;;;;;-1:-1:-1;23789:9:1;;;;;;1315:1:3;;:::i;:::-;960:23507:1;23800:3;-1:-1:-1;23830:29:1;23867:243;23830:29;;960:23507;23830:29;;;;:::i;:::-;960:23507;;;;;;;;;;;23867:243;;;;;960:23507;;23774:13;;23867:243;;;;-1:-1:-1;23867:243:1;;22789:51;960:23507;;;22817:23;;;;960:23507;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;:::i;:::-;;;;;-1:-1:-1;960:23507:1;9858:23;960:23507;;;-1:-1:-1;960:23507:1;;;;;;;;;;;;;;;-1:-1:-1;960:23507:1;;-1:-1:-1;960:23507:1;;-1:-1:-1;960:23507:1;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;:::o;:::-;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;-1:-1:-1;960:23507:1;;-1:-1:-1;960:23507:1;;;;-1:-1:-1;960:23507:1;;;;-1:-1:-1;960:23507:1;;;;-1:-1:-1;960:23507:1;;;;-1:-1:-1;960:23507:1;;;;-1:-1:-1;960:23507:1;;;;;;;;;;-1:-1:-1;960:23507:1;;;;;;;-1:-1:-1;960:23507:1;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;:::i;:::-;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;;:::i;:::-;;;;;-1:-1:-1;960:23507:1;3133:107;960:23507;;;-1:-1:-1;960:23507:1;;;;;;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;;:::i;:::-;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;1258:92:3;;:::i;:::-;960:23507:1;22752:31;;960:23507;;;;;;;;;;;;;22794:14;22793:15;960:23507;22794:14;;;960:23507;;;;;22793:15;22789:51;;23114:23;23003:33;22875:38;22936:45;22875:38;960:23507;22875:38;960:23507;;;;22875:23;960:23507;;;;;;;22875:38;960:23507;;;22936:45;2458:93:12;;;;1275:5;;2458:93;;1188:104;;22936:45:1;23003:33;:::i;:::-;23114:23;;960:23507;;;;;;23143:621;23114:23;23143:621;;23114:23;23143:621;;23114:23;23143:621;;;;;;;;;;;;23774:13;-1:-1:-1;23789:9:1;;;;;;1315:1:3;;:::i;23800:3:1:-;-1:-1:-1;23838:20:1;23867:243;23838:20;;960:23507;23830:29;23838:20;;;;:::i;:::-;23830:29;;:::i;960:23507::-;23867:243;;;;;960:23507;;23774:13;;23867:243;960:23507;23867:243;;-1:-1:-1;23867:243:1;;960:23507;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;:::o;:::-;;;;;;:::i;:::-;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;5341:47;;960:23507;5364:15;960:23507;5364:15;;;960:23507;5341:47;;;;;;;-1:-1:-1;5341:47:1;;;960:23507;;;5327:10;:61;5323:119;;5447:1;;;:::i;5323:119::-;960:23507;;;5405:30;;;;5341:47;;;;960:23507;5341:47;960:23507;5341:47;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;;:::i;960:23507::-;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;1341:55;960:23507;;;;;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;;:::i;:::-;;-1:-1:-1;960:23507:1;;;;;;9083:38;960:23507;-1:-1:-1;960:23507:1;9083:38;960:23507;;;;;;;;;;;;;;;;-1:-1:-1;;960:23507:1;;;;;;4098:15;960:23507;4075:73;;;;;960:23507;;;;;;4075:73;;;;960:23507;4075:73;;4142:4;960:23507;4075:73;;960:23507;4075:73;;;;;;;;960:23507;;;4075:73;;;;;:::i;960:23507::-;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;1458:51;960:23507;;;;;;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;:::i;:::-;;;;;-1:-1:-1;960:23507:1;14243:23;960:23507;;;-1:-1:-1;960:23507:1;;;;;;;;;;;;;;;-1:-1:-1;960:23507:1;;-1:-1:-1;960:23507:1;;-1:-1:-1;960:23507:1;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;:::i;:::-;;3020:15:18;960:23507:1;3006:10:18;:29;3002:82;;960:23507:1;;;;11890:47:18;;-1:-1:-1;416:167:17;960:23507:1;416:167:17;;;-1:-1:-1;416:167:17;960:23507:1;3002:82:18;109:63:16;-1:-1:-1;109:63:16;960:23507:1;109:63:16;;960:23507:1;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;4237:15;960:23507;;;;;;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;:::i;:::-;;;;;-1:-1:-1;960:23507:1;11487:25;960:23507;11487:25;960:23507;;;-1:-1:-1;960:23507:1;;;;;;;;;;;;;;;-1:-1:-1;960:23507:1;;-1:-1:-1;960:23507:1;;-1:-1:-1;960:23507:1;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;-1:-1:-1;960:23507:1;14481:23;960:23507;;;-1:-1:-1;960:23507:1;;;;2458:93:12;;;;;1275:5;;2458:93;;960:23507:1;;;;;;;;14610:20;;;:::i;:::-;14641:13;-1:-1:-1;14656:9:1;;;;;;960:23507;;;;;;;:::i;14667:3::-;960:23507;;;;;;;;;14680:27;960:23507;14689:18;960:23507;14689:18;;;:::i;960:23507::-;14680:27;;;;:::i;:::-;960:23507;;;;;;14680:27;960:23507;14641:13;;960:23507;;:::i;:::-;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;5341:47;;960:23507;5364:15;960:23507;5364:15;;;960:23507;5341:47;;;;;;;-1:-1:-1;5341:47:1;;;960:23507;;;5327:10;:61;5323:119;;5447:1;;;:::i;5341:47::-;;;;960:23507;5341:47;960:23507;5341:47;;;;;;;:::i;:::-;;;;960:23507;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;:::i;:::-;;;;;5341:47;;960:23507;5364:15;960:23507;5364:15;;;960:23507;5341:47;;;;;;;-1:-1:-1;5341:47:1;;;960:23507;;;5327:10;:61;5323:119;;5447:1;;;:::i;5341:47::-;;;;960:23507;5341:47;960:23507;5341:47;;;;;;;:::i;:::-;;;;960:23507;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;:::i;:::-;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;;;;1148:88;960:23507;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;-1:-1:-1;960:23507:1;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::o;:::-;;;;;;:::i;:::-;;;;;;;:::o;:::-;;;;;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::o;:::-;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;21612:21;960:23507;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;21612:21;;:::i;:::-;960:23507;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;960:23507:1;;;;;;;;1401:52;960:23507;;;;;;;;-1:-1:-1;;960:23507:1;;;;;2250:561:10;;;16345:16:1;2250:561:10;;960:23507:1;;;2250:561:10;;;16369:18:1;2250:561:10;;;;;;;;;;;;;960:23507:1;;;;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;1258:92:3;;;;;:::i;:::-;960:23507:1;;;11161:72;;11222:10;960:23507;11161:72;;960:23507;;;;;11184:15;960:23507;;11161:72;;;;;;;-1:-1:-1;11161:72:1;;;960:23507;11160:73;;11156:122;;960:23507;11299:52;;;;:::i;:::-;1315:1:3;;:::i;:::-;960:23507:1;;;;;;;;;;;;;;;;11156:122;960:23507;;;11250:21;;;;11161:72;;;;960:23507;11161:72;960:23507;11161:72;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;960:23507;;;;;-1:-1:-1;;960:23507:1;;;;;-1:-1:-1;960:23507:1;;;;;1275:5:12;;;2458:93;;;;;;;960:23507:1;;;;;;;;;9489:20;;;:::i;:::-;9520:13;-1:-1:-1;9535:9:1;;;;;;960:23507;;;;;;;:::i;9546:3::-;960:23507;;;;;;;;;;;;;;;9559:35;960:23507;;;-1:-1:-1;960:23507:1;;;;;;;;;;9559:35;;;;:::i;:::-;960:23507;9520:13;;960:23507;;:::i;:::-;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;;:::i;:::-;;-1:-1:-1;960:23507:1;11654:25;960:23507;;;;-1:-1:-1;960:23507:1;;;;;;;;;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;;:::i;:::-;;;;;-1:-1:-1;960:23507:1;11804:27;960:23507;;;-1:-1:-1;960:23507:1;;;11804:56;;960:23507;;;;;;;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;;:::i;:::-;;-1:-1:-1;960:23507:1;10486:23;960:23507;;;;-1:-1:-1;960:23507:1;;;;;;;;;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;;;;;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;20676:14;960:23507;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;;20676:14;:::i;960:23507::-;;;;;;;;:::i;:::-;;;;;;-1:-1:-1;960:23507:1;10096:23;960:23507;;;-1:-1:-1;960:23507:1;;;;2458:93:12;;;;;1275:5;;2458:93;;960:23507:1;;;;;;;;10248:20;;;:::i;:::-;10279:13;-1:-1:-1;10294:9:1;;;;;;960:23507;;;;;;;:::i;10305:3::-;960:23507;;;;;;;;;10318:27;960:23507;10327:18;960:23507;10327:18;;;:::i;10318:27::-;960:23507;10279:13;;960:23507;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;;:::i;:::-;;-1:-1:-1;960:23507:1;14848:23;960:23507;;;;-1:-1:-1;960:23507:1;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;960:23507:1;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;960:23507:1;;;;;;;;-1:-1:-1;;960:23507:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;960:23507:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;-1:-1:-1;960:23507:1;;-1:-1:-1;960:23507:1;;;-1:-1:-1;960:23507:1;:::o;:::-;;;;;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::o;:::-;-1:-1:-1;960:23507:1;;-1:-1:-1;960:23507:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;-1:-1:-1;960:23507:1;;;;;;;;;-1:-1:-1;960:23507:1;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;-1:-1:-1;960:23507:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;6451:15;960:23507;;;;;;;;;;;6451:15;960:23507;;;;;;;;6451:15;960:23507;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;5707:986::-;;;;;;;;;5962:38;;:31;;960:23507;;;;;;;;;;;;5962:31;:38;960:23507;;;;;5962:38;5958:94;;6128:15;6533:155;6128:15;;;;;6533:155;6128:15;;:::i;:::-;960:23507;6444:30;6451:15;960:23507;;;;;6444:30;6184:297;960:23507;;:::i;:::-;;;;;;6184:297;960:23507;;;6184:297;;;960:23507;;;;6184:297;;;960:23507;5962:38;6184:297;;;960:23507;5962:38;6184:297;;;960:23507;;;6184:297;;;960:23507;;6184:297;960:23507;;;6184:297;;;960:23507;;;;;;:::i;:::-;6184:297;;;960:23507;6150:31;;960:23507;;;;;;;;;;;;6150:31;960:23507;:::i;:::-;6487:35;;;:::i;:::-;6184:297;960:23507;6533:155;;;;;:::i;:::-;;;;5707:986::o;5958:94::-;5962:16;960:23507;;6017:28;;;;960:23507;;;;;;;;;;;:::o;:::-;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;7589:853::-;;;;;;960:23507;;;;;;-1:-1:-1;960:23507:1;7826:16;960:23507;;;7826:38;960:23507;-1:-1:-1;960:23507:1;7826:38;960:23507;;7825:39;7821:90;;7987:15;;;960:23507;7987:15;;;;8288:149;7987:15;960:23507;7987:15;960:23507;7987:15;;:::i;:::-;960:23507;-1:-1:-1;960:23507:1;7826:16;960:23507;;8235:42;960:23507;;-1:-1:-1;960:23507:1;8079:36;:21;7826:38;8079:21;;960:23507;;;;;;;;;;;;;;;8079:36;960:23507;;;;;;;;;;;;;;;;;;;;;;;;8235:42;960:23507;;;;;;;;;;;;;;;;;;;;;;;;;8288:149;7589:853::o;8446:363::-;960:23507;;;-1:-1:-1;960:23507:1;8552:16;960:23507;;;8552:38;960:23507;-1:-1:-1;960:23507:1;8552:38;960:23507;;8551:39;8547:90;;960:23507;;8768:36;960:23507;-1:-1:-1;960:23507:1;8552:16;960:23507;;8552:38;960:23507;-1:-1:-1;960:23507:1;8642:39;960:23507;;;;;;;;;;;8768:36;8446:363::o;1258:92:3:-;;;;;;;;;;;;;:::i;:::-;960:23507:1;;;21646:72;;21707:10;21646:72;;;960:23507;21646:72;960:23507;;;21669:15;960:23507;;21646:72;;;;;;;-1:-1:-1;21646:72:1;;;1258:92:3;21645:73:1;;21641:122;;960:23507;21807:31;;960:23507;;;;;;;;;;;;;21849:22;21848:23;960:23507;21849:22;;;960:23507;;;;;21848:23;21844:74;;21939:54;960:23507;21939:54;22080:165;21939:54;;;:::i;:::-;22018:16;;;960:23507;;22018:16;;;3087:198:19;;;;;;;960:23507:1;;;;;:::i;:::-;22080:165;;:::i;:::-;1315:1:3;;;;:::i;21646:72:1:-;;;;;;;;;;;;;;:::i;:::-;;;;960:23507;;;;;;;;;;;;;;;;;;:::o;1258:92:3:-;;;;;;;;:::i;:::-;960:23507:1;;;20703:72;;20764:10;20703:72;;;960:23507;;;;20703:72;960:23507;;;20726:15;960:23507;;20703:72;;;;;;;960:23507;20703:72;;;1258:92:3;20702:73:1;;20698:122;;20910:42;;20849:16;;;960:23507;6256:47:19;;960:23507:1;;;;20910:27;960:23507;;;;;;;20910:42;960:23507;;;;;20910:42;960:23507;;;20962:27;20958:78;;960:23507;21126:165;21080:31;960:23507;;21080:31;;960:23507;;;;;;;;;;;;;;;;;:::i;:::-;;;;;:::i;21126:165::-;1315:1:3;;;:::i;20958:78:1:-;20703:72;960:23507;;21006:23;;;;20703:72;;;;;;;;;;;;;;:::i;:::-;;;;1148:88;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;:::i;:::-;;960:23507;;;;1148:88;;;;:::o;:::-;;;;;;;;;;;:::i;:::-;960:23507;;;;:::i;:::-;1148:88;;960:23507;1148:88;;;960:23507;:::i;:::-;1148:88;;;;960:23507;1148:88;;;960:23507;:::i;:::-;1148:88;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;;:::i;:::-;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::o;4593:203::-;-1:-1:-1;960:23507:1;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4723:68;4734:27;;:::i;:::-;960:23507;;;;4723:68;;;;;;:::i;1745:595:3:-;1791:545;;;;;;;;1745:595::o;1791:545::-;;;;;;;2430:177;2478:125;;;2430:177::o;6697:596:1:-;;;960:23507;;6889:24;;960:23507;;;;;;6943:26;960:23507;;7057:19;;;;:39;;;;6697:596;7056:90;;;;6697:596;7056:146;;;;;6697:596;7056:179;;;;;;;6697:596;7045:244;;;6697:596::o;7045:244::-;7257:25;960:23507;;7257:25;;;;7056:179;7230:5;7212:23;;;7056:179;;;:146;7157:44;;-1:-1:-1;7157:44:1;;;;;7056:146;;;;;;;;;7157:44;960:23507;7006:33;;-1:-1:-1;7157:44:1;;;;7056:90;7108:37;;;;;;;;7056:90;;;;;;7108:37;;;;;;7057:39;;-1:-1:-1;7057:39:1;;;11869:1990;;;12088:15;12046:31;;960:23507;;;;;;;;;;;;12046:31;12088:15;960:23507;;;;12087:16;12083:67;;960:23507;;;;12159:17;12155:72;;12331:1334;12263:37;12289:10;960:23507;;;;12088:15;960:23507;;;;;;;12263:37;960:23507;12331:1334;;;;;;;;;;;;;12088:15;12331:1334;;;;;;12289:10;;12331:1334;;;;;;;;;;;;;;;;12289:10;12331:1334;;;;;;;-1:-1:-1;12331:1334:1;;;;;;13796:58;12289:10;13670:57;12289:10;13670:37;12289:10;960:23507;;;;12088:15;960:23507;;;;;;;13670:37;:57;:::i;:::-;12331:1334;960:23507;;;;;;;;;;;;;;13739:51;;12331:1334;13739:51;13796:42;;960:23507;;;;20910:27;960:23507;;;;;;;13796:42;960:23507;;;;;;;;;;;12331:1334;;-1:-1:-1;12331:1334:1;12046:16;12331:1334;;12155:72;12046:16;960:23507;;12193:27;;;;960:23507;;;;;;;;;1148:88;960:23507;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;17317:3130::-;;;;;;;;17612:76;;960:23507;;;;;;;;;;;17612:76;;960:23507;;;;17612:76;;;960:23507;;;;;;;;;;;17612:76;;17635:15;960:23507;17635:15;960:23507;17612:76;;;;;;;17671:16;17612:76;;;17317:3130;17608:122;;;17759:16;;;960:23507;6256:47:19;;17829:10:1;;960:23507;;;17803:36;:67;;;;17317:3130;17801:70;17797:124;;17938:58;960:23507;;;;;;;;;;;;17938:58;960:23507;;;17938:58;;;;;:128;;;17317:3130;17927:181;;18114:186;;17317:3130;18381:18;;18426:109;17612:76;18315:85;18381:18;18357:16;;2089:726:10;;;2250:561;;;;;;;;;;;;;;;;;2089:726;18315:85:1;960:23507;;;18426:109;;;;960:23507;18426:109;;17829:10;;;17612:76;18426:109;;;:::i;:::-;;960:23507;17671:16;960:23507;;;18426:109;;;;;;;17671:16;18426:109;;;17317:3130;-1:-1:-1;17759:16:1;;;960:23507;;;;;18558:27;;;:::i;:::-;18627:21;17612:76;18627:21;;;960:23507;;;;;;;;;7737:59:9;;;:::i;:::-;18613:61:1;;;:::i;:::-;18717:23;960:23507;18717:23;;;960:23507;;;;;;;;;8183:59:9;;;:::i;:::-;18703:65:1;;;:::i;:::-;960:23507;;;;;;;;;;19091:28;;;;960:23507;;;;;;;;;19144:31;;;960:23507;;;;19199:25;;960:23507;;;19252:29;;;960:23507;;;19309:29;;;960:23507;;;19371:34;19091:28;19371:34;;960:23507;;;;;;;;19431:27;960:23507;19431:27;;960:23507;;;;;;;;19490:33;;;;960:23507;;;;;;;;17759:16;;;960:23507;;;;:::i;:::-;17829:10;960:23507;;;;;;17612:76;18814:747;;960:23507;;;;18814:747;;960:23507;;;;18814:747;;960:23507;;;19252:29;18814:747;;960:23507;;;19309:29;18814:747;;960:23507;;;19091:28;18814:747;;960:23507;;;;18814:747;;960:23507;;;19490:33;18814:747;;960:23507;;;17759:16;18814:747;;960:23507;17671:16;18814:747;;;960:23507;;;18814:747;;;960:23507;;;18814:747;;;960:23507;;;18814:747;;;960:23507;;;18814:747;;;960:23507;18814:747;;;960:23507;19620:17;;;:::i;:::-;18814:747;;;960:23507;18814:747;;;960:23507;19694:19;;;:::i;:::-;18814:747;;;960:23507;18814:747;;;960:23507;19750:3;;;:::i;:::-;19765:18;;19761:72;;3643:51:10;19882:21:1;;3643:51:10;:::i;:::-;;960:23507:1;17635:15;960:23507;19917:62;;;;960:23507;;;19917:62;;960:23507;;;17612:76;19917:62;;960:23507;;17671:16;960:23507;;;;;19917:62;17635:15;;960:23507;17635:15;960:23507;19917:62;;;;;;;20144:298;19917:62;20081:51;19917:62;20081:38;960:23507;19917:62;20024:51;19917:62;20024:38;19917:62;;;20144:298;19917:62;;;17317:3130;3933:47:20;17671:16:1;960:23507;3933:47:20;960:23507:1;;;;22875:23;960:23507;;;;;;;20024:51;960:23507;;;;20081:23;960:23507;;;;;;;20081:51;17612:76;18814:747;;960:23507;;;19252:29;18814:747;;960:23507;;;19309:29;18814:747;;960:23507;;;19091:28;18814:747;;960:23507;;;18814:747;960:23507;;18814:747;;960:23507;;;;;;18814:747;960:23507;19490:33;18814:747;;960:23507;;;;;;18814:747;;960:23507;17759:16;18814:747;;960:23507;;;;;;18814:747;;960:23507;;;;;;;;;;;20144:298;;;:::i;:::-;;;;17317:3130::o;19917:62::-;;;;;;:::i;:::-;;;:::i;:::-;;;;19761:72;17612:76;960:23507;;19805:21;;;;18426:109;;;;17612:76;18426:109;17612:76;18426:109;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;18114:186;18265:20;18227:28;960:23507;18227:28;;;960:23507;;;;;;17829:10;;18265:20;;:::i;:::-;18114:186;;;;17927:181;17612:76;960:23507;;18088:13;;;;17938:128;18030:36;18006:60;960:23507;17612:76;18030:36;;960:23507;;;;;;;;;;18006:60;;;;17938:128;;17797:124;17612:76;960:23507;;17888:26;;;;17803:67;960:23507;;;;17843:27;17803:67;;17608:122;17612:76;960:23507;;17705:18;;;;17612:76;;;;;;;;;;;;;;:::i;:::-;;;;2297:343:20;2419:97;;;-1:-1:-1;2419:97:20;;-1:-1:-1;960:23507:1;802:1487:20;;;;;;;;;;;;;;;;2297:343;802:1487;;;;;;;;;;;;;;;;;2419:97;802:1487;2419:97;;;802:1487;2589:47;;;;2419:97;2589:47;2297:343::o;802:1487::-;-1:-1:-1;802:1487:20;;;;;-1:-1:-1;802:1487:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2419:97;802:1487;;;;;;;;;;;;-1:-1:-1;802:1487:20;;;;;;;;;;;;2042:1017:9;2179:876;;;2042:1017;;;2179:876;2042:1017;2179:876;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2042:1017::o;2179:876::-;;;;;;;6547:818;6626:735;;;;6547:818;6626:735;;;;;;;;;;;;;;;;;;;6547:818;:::o;6626:735::-;;;;;;;16665:648:1;;16764:545;;;;;;;;;;;;;;;;;;;;16665:648;:::o;16764:545::-;;;;;;;4880:148;960:23507;;;;;;5000:22;;;960:23507;5000:22;;;960:23507;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5000:22;;;;;:::i;:::-;;:::i;513:2279:14:-;-1:-1:-1;706:997:14;;513:2279;;7773:10:9;706:997:14;;;;;;;;;;;;;;;;;;;-1:-1:-1;1706:1084:14;;;706:997;-1:-1:-1;1746:79:14;;1836:22;-1:-1:-1;1746:79:14;186:325;2602:591:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;383:126:14;;;;;;;;2602:591:0;;;;;;;316:3:14;960:23507:1;;;;383:126:14;;;;;;186:325;1706:1084;1935:851;;;706:997;1935:851;;;;;;;;;;;;;;;;;;;;;513:2279::o;1935:851::-;;-1:-1:-1;1935:851:14;706:997;;1935:851;706:997;;;;-1:-1:-1;706:997:14;;;;;;;;7785:10:9;-1:-1:-1;706:997:14;;;;;;-1:-1:-1;706:997:14;;;-1:-1:-1;706:997:14;513:2279;-1:-1:-1;706:997:14;;513:2279;;8219:10:9;706:997:14;;;;;;;;;;;;;;;;;;;-1:-1:-1;1706:1084:14;;;706:997;-1:-1:-1;1746:79:14;;1836:22;-1:-1:-1;1746:79:14;186:325;2602:591:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;383:126:14;;;;;;;;2602:591:0;;;;;;;316:3:14;960:23507:1;;;;383:126:14;;;;;;186:325;706:997;;;;-1:-1:-1;706:997:14;;;;;;;;8231:10:9;-1:-1:-1;706:997:14;;;;2875:986:20;2968:889;;;;;;;;;;;;;;;;2875:986;;:::o;2968:889::-;;;;;;;;;960:23507:1;2968:889:20;2875:986::o;2968:889::-;;;;-1:-1:-1;960:23507:1;2968:889:20;;;;;960:23507:1;2968:889:20;;;;;;;;;;;;;;;;;2875:986;;:::o;2968:889::-;;;;;;;;;;;;;;;;;;3703:534:10;3852:381;;;;;;;;;;;;;17671:16:1;3852:381:10;;;;;;3703:534::o

Swarm Source

none

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
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.