ETH Price: $1,948.80 (-1.95%)
 

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

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain 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:
ZkLighter

Compiler Version
v0.8.25+commit.b61c2a91

Optimization Enabled:
Yes with 1000 runs

Other Settings:
cancun EvmVersion
File 1 of 22 : ZkLighter.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./interfaces/IZkLighter.sol";
import "./interfaces/IZkLighterVerifier.sol";
import "./interfaces/IDesertVerifier.sol";
import "./lib/Bytes.sol";
import "./lib/TxTypes.sol";
import "./Storage.sol";
import "./ExtendableStorage.sol";

/// @title zkLighter Contract
/// @author zkLighter Team
contract ZkLighter is IZkLighter, Storage, ReentrancyGuardUpgradeable, ExtendableStorage {
  address private immutable zklighterImplementation;

  // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
  // * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure */
  // * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. */
  // * Avoid leaving a contract uninitialized. */
  // * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation */
  // * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke */
  // * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: */
  constructor() {
    zklighterImplementation = address(this);

    _disableInitializers();
  }

  /// @notice ZkLighter contract initialization.
  /// @param initializationParameters Encoded representation of initialization parameters:
  /// @dev _governanceAddress The address of Governance contract
  /// @dev _verifierAddress The address of Verifier contract
  /// @dev _additionalZkLighter The address of Additional zkLighter contract
  /// @dev _desertVerifier The address of Desert Verifier contract
  /// @dev _genesisStateRoot Genesis blocks (first block) state tree root hash
  /// @dev _genesisValidiumRoot Genesis blocks (first block) validium tree root hash
  function initialize(bytes calldata initializationParameters) external initializer {
    if (address(this) == zklighterImplementation) {
      revert ZkLighter_CannotBeInitialisedByImpl();
    }

    __ReentrancyGuard_init();

    (
      address _governanceAddress,
      address _verifierAddress,
      address _additionalZkLighter,
      address _desertVerifier,
      bytes32 _genesisStateRoot,
      bytes32 _genesisValidiumRoot
    ) = abi.decode(initializationParameters, (address, address, address, address, bytes32, bytes32));

    if (!_hasCode(_governanceAddress) || !_hasCode(_verifierAddress) || !_hasCode(_additionalZkLighter) || !_hasCode(_desertVerifier)) {
      revert ZkLighter_InvalidInitializeParameters();
    }

    verifier = IZkLighterVerifier(_verifierAddress);
    governance = IGovernance(_governanceAddress);
    additionalZkLighter = AdditionalZkLighter(_additionalZkLighter);
    desertVerifier = IDesertVerifier(_desertVerifier);

    StoredBatchInfo memory genesisBatchInfo = StoredBatchInfo({
      batchNumber: 0,
      endBlockNumber: 0,
      batchSize: 0,
      startTimestamp: 0,
      endTimestamp: 0,
      priorityRequestCount: 0,
      prefixPriorityRequestHash: 0,
      onChainOperationsHash: 0,
      stateRoot: _genesisStateRoot,
      validiumRoot: _genesisValidiumRoot,
      commitment: bytes32(0)
    });
    stateRoot = _genesisStateRoot;
    storedBatchHashes[0] = hashStoredBatchInfo(genesisBatchInfo);

    _registerDefaultAssetConfigs();

    lastAccountIndex = 2; // First 3 accounts are reserved for system accounts
  }

  /// @notice ZkLighter contract upgrade. Can be external because Proxy contract intercepts illegal calls of this function
  /// @param upgradeParameters Encoded representation of upgrade parameters
  function upgrade(bytes calldata upgradeParameters) external nonReentrant {
    if (address(this) == zklighterImplementation) {
      revert ZkLighter_OnlyProxyCanCallUpgrade();
    }

    // Commit to the initialization parameters to ensure parameters are known at the time of upgrade initialization
    bytes32 upgradeParametersHash = keccak256(upgradeParameters);
    // Commits to 0 address for _additionalZkLighter, _desertVerifier and _stateRootUpgradeVerifier
    bytes32 initializationParametersCommitment = 0xb11bb680dab3fb9238aa513aacc13268188c937de0f36b523827f67163969174;
    if (upgradeParametersHash != initializationParametersCommitment) {
      revert ZkLighter_InvalidUpgradeParameters();
    }

    (address _additionalZkLighter, address _desertVerifier, address _stateRootUpgradeVerifier) = abi.decode(
      upgradeParameters,
      (address, address, address)
    );

    if (_additionalZkLighter != address(0)) {
      if (!_hasCode(_additionalZkLighter)) {
        revert ZkLighter_InvalidUpgradeParameters();
      }
      additionalZkLighter = AdditionalZkLighter(_additionalZkLighter);
    }

    if (_desertVerifier != address(0)) {
      if (!_hasCode(_desertVerifier)) {
        revert ZkLighter_InvalidUpgradeParameters();
      }
      desertVerifier = IDesertVerifier(_desertVerifier);
    }

    if (_stateRootUpgradeVerifier != address(0)) {
      if (!_hasCode(_stateRootUpgradeVerifier)) {
        revert ZkLighter_InvalidUpgradeParameters();
      }
      stateRootUpgradeVerifier = IZkLighterStateRootUpgradeVerifier(_stateRootUpgradeVerifier);
    }
  }

  function _registerDefaultAssetConfigs() internal {
    address ethAddress = address(0);
    uint56 ethExtensionMultiplier = 100;
    uint128 ethTickSize = 10 ** 10; // 0.00000001 ETH
    assetConfigs[NATIVE_ASSET_INDEX] = AssetConfig({
      tokenAddress: ethAddress,
      withdrawalsEnabled: 1,
      extensionMultiplier: ethExtensionMultiplier,
      tickSize: ethTickSize,
      depositCapTicks: MAX_DEPOSIT_CAP_TICKS,
      minDepositTicks: 100_000 // 0.001 ETH
    });
    tokenToAssetIndex[ethAddress] = NATIVE_ASSET_INDEX;
    emit RegisterAssetConfig(NATIVE_ASSET_INDEX, ethAddress, 1, ethExtensionMultiplier, ethTickSize, MAX_DEPOSIT_CAP_TICKS, 100_000);

    address usdcAddress = address(governance.usdc());
    uint56 usdcExtensionMultiplier = 1_000_000;
    uint128 usdcTickSize = 1;
    assetConfigs[USDC_ASSET_INDEX] = AssetConfig({
      tokenAddress: usdcAddress,
      withdrawalsEnabled: 1,
      extensionMultiplier: usdcExtensionMultiplier,
      tickSize: usdcTickSize,
      depositCapTicks: MAX_DEPOSIT_CAP_TICKS,
      minDepositTicks: 1_000_000 // 1 USDC
    });
    tokenToAssetIndex[usdcAddress] = USDC_ASSET_INDEX;
    emit RegisterAssetConfig(USDC_ASSET_INDEX, usdcAddress, 1, usdcExtensionMultiplier, usdcTickSize, MAX_DEPOSIT_CAP_TICKS, 1_000_000);
  }

  /// @inheritdoc IZkLighter
  function registerAssetConfig(
    uint16 assetIndex,
    address tokenAddress,
    uint8 withdrawalsEnabled,
    uint56 extensionMultiplier,
    uint128 tickSize,
    uint64 depositCapTicks,
    uint64 minDepositTicks
  ) external nonReentrant onlyActive {
    governance.requireGovernor(msg.sender);

    if (assetIndex == NATIVE_ASSET_INDEX || assetConfigs[assetIndex].tokenAddress != address(0)) {
      revert ZkLighter_InvalidAssetIndex();
    }
    if (assetIndex < MIN_ASSET_INDEX || assetIndex > MAX_ASSET_INDEX) {
      revert ZkLighter_InvalidAssetIndex();
    }
    if (tokenAddress == address(0) || tokenToAssetIndex[tokenAddress] != 0) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    if (withdrawalsEnabled != 0 && withdrawalsEnabled != 1) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    if (!_hasCode(tokenAddress)) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    if (tickSize == 0 || depositCapTicks == 0 || minDepositTicks == 0) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    if (tickSize > MAX_TICK_SIZE || depositCapTicks > MAX_DEPOSIT_CAP_TICKS || minDepositTicks > depositCapTicks) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    uint128 extendedDepositCapTicks = uint128(extensionMultiplier) * uint128(depositCapTicks);
    if (
      extensionMultiplier == 0 || extensionMultiplier > MAX_ASSET_EXTENSION_MULTIPLIER || extendedDepositCapTicks > MAX_EXTENDED_DEPOSIT_CAP_TICKS
    ) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    assetConfigs[assetIndex] = AssetConfig({
      tokenAddress: tokenAddress,
      withdrawalsEnabled: withdrawalsEnabled,
      extensionMultiplier: extensionMultiplier,
      tickSize: tickSize,
      depositCapTicks: depositCapTicks,
      minDepositTicks: minDepositTicks
    });
    tokenToAssetIndex[tokenAddress] = assetIndex;
    emit RegisterAssetConfig(assetIndex, tokenAddress, withdrawalsEnabled, extensionMultiplier, tickSize, depositCapTicks, minDepositTicks);
  }

  /// @inheritdoc IZkLighter
  function updateAssetConfig(
    uint16 assetIndex,
    uint8 withdrawalsEnabled,
    uint64 depositCapTicks,
    uint64 minDepositTicks
  ) external nonReentrant onlyActive {
    governance.requireGovernor(msg.sender);

    AssetConfig memory config = assetConfigs[assetIndex];
    if (assetIndex != NATIVE_ASSET_INDEX && config.tokenAddress == address(0)) {
      revert ZkLighter_InvalidAssetIndex();
    }

    if (withdrawalsEnabled != 0 && withdrawalsEnabled != 1) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    // Withdrawals can not be disabled if they are enabled already
    // Only transition allowed for this parameter is from disabled to enabled
    if (withdrawalsEnabled == 0 && config.withdrawalsEnabled == 1) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    if (depositCapTicks == 0 || minDepositTicks == 0) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    if (depositCapTicks > MAX_DEPOSIT_CAP_TICKS || minDepositTicks > depositCapTicks) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    uint128 extendedDepositCapTicks = uint128(config.extensionMultiplier) * uint128(depositCapTicks);
    if (extendedDepositCapTicks > MAX_EXTENDED_DEPOSIT_CAP_TICKS) {
      revert ZkLighter_InvalidAssetConfigParams();
    }

    assetConfigs[assetIndex] = AssetConfig({
      tokenAddress: config.tokenAddress,
      withdrawalsEnabled: withdrawalsEnabled,
      extensionMultiplier: config.extensionMultiplier,
      tickSize: config.tickSize,
      depositCapTicks: depositCapTicks,
      minDepositTicks: minDepositTicks
    });
    emit UpdateAssetConfig(assetIndex, withdrawalsEnabled, depositCapTicks, minDepositTicks);
  }

  /// @inheritdoc IZkLighter
  function setSystemConfig(TxTypes.SetSystemConfig calldata _params) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function registerAsset(uint8 _l1Decimals, uint8 _decimals, bytes32 _symbol, TxTypes.RegisterAsset calldata _params) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function updateAsset(TxTypes.UpdateAsset calldata _params) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function deposit(address _to, uint16 _assetIndex, TxTypes.RouteType _routeType, uint256 _amount) external payable {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function depositBatch(uint64[] calldata _amount, address[] calldata _to, uint48[] calldata _accountIndex) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function changePubKey(uint48 _accountIndex, uint8 _apiKeyIndex, bytes calldata _pubKey) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function createMarket(uint8 _size_decimals, uint8 _price_decimals, bytes32 _symbol, TxTypes.CreateMarket calldata _params) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function updateMarket(TxTypes.UpdateMarket calldata _params) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function cancelAllOrders(uint48 _accountIndex) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function withdraw(uint48 _accountIndex, uint16 _assetIndex, TxTypes.RouteType _routeType, uint64 _baseAmount) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function createOrder(uint48 _accountIndex, uint16 _marketIndex, uint48 _baseAmount, uint32 _price, uint8 _isAsk, uint8 _orderType) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function burnShares(uint48 _accountIndex, uint48 _publicPoolIndex, uint64 _shareAmount) external {
    delegateAdditional();
  }

  /// @inheritdoc IZkLighter
  function updateStateRoot(StoredBatchInfo calldata _lastStoredBatch, bytes32 _stateRoot, bytes32 _validiumRoot, bytes calldata proof) external {
    delegateAdditional();
  }

  function createExitCommitment(
    uint256 stateRoot,
    uint48 _accountIndex,
    uint48 _masterAccountIndex,
    uint16 _assetIndex,
    uint128 _totalBaseAmount
  ) internal pure returns (bytes32) {
    return keccak256(abi.encodePacked(stateRoot, _accountIndex, _masterAccountIndex, _assetIndex, _totalBaseAmount));
  }

  /// @notice Performs exit from zkLighter in desert mode
  function performDesert(
    uint48 _accountIndex,
    uint48 _masterAccountIndex,
    uint16 _assetIndex,
    uint128 _totalBaseAmount,
    bytes calldata proof
  ) external nonReentrant {
    // Must be in desert mode
    if (!desertMode) {
      revert ZkLighter_DesertModeInactive();
    }

    if (accountPerformedDesertForAsset[_assetIndex][_accountIndex]) {
      revert ZkLighter_AccountAlreadyPerformedDesertForAsset();
    }

    uint256[] memory inputs = new uint256[](1);
    bytes32 commitment = createExitCommitment(uint256(stateRoot), _accountIndex, _masterAccountIndex, _assetIndex, _totalBaseAmount);
    inputs[0] = uint256(commitment) % BN254_MODULUS;
    bool success = desertVerifier.Verify(proof, inputs);
    if (!success) {
      revert ZkLighter_DesertVerifyProofFailed();
    }

    increaseBalanceToWithdraw(_masterAccountIndex, _assetIndex, _totalBaseAmount);
    accountPerformedDesertForAsset[_assetIndex][_accountIndex] = true;
  }

  /// @inheritdoc IZkLighter
  function cancelOutstandingDepositsForDesertMode(uint64 _n, bytes[] memory _priorityPubData) external nonReentrant {
    // Must be in desert mode
    if (!desertMode) {
      revert ZkLighter_DesertModeInactive();
    }

    if (openPriorityRequestCount == 0 || _n == 0) {
      revert ZkLighter_NoOutstandingDepositsForCancelation();
    }

    if (_n > openPriorityRequestCount || _n != _priorityPubData.length) {
      revert ZkLighter_InvalidParamsForCancelOutstandingDeposits();
    }

    uint64 startIndex = executedPriorityRequestCount;

    bytes32 pubDataPrefixHash = bytes32(0);
    if (executedPriorityRequestCount > 0) {
      pubDataPrefixHash = priorityRequests[executedPriorityRequestCount - 1].prefixHash;
    }

    uint64 currentPubDataIdx = 0;
    for (uint64 id = startIndex; id < startIndex + _n; ++id) {
      if (_priorityPubData[currentPubDataIdx].length > MAX_PRIORITY_REQUEST_PUBDATA_SIZE || _priorityPubData[currentPubDataIdx].length == 0) {
        revert ZkLighter_InvalidParamsForCancelOutstandingDeposits();
      }

      bytes memory paddedPubData = new bytes(MAX_PRIORITY_REQUEST_PUBDATA_SIZE);
      for (uint256 i = 0; i < _priorityPubData[currentPubDataIdx].length; ++i) {
        paddedPubData[i] = _priorityPubData[currentPubDataIdx][i];
      }

      pubDataPrefixHash = keccak256(abi.encodePacked(pubDataPrefixHash, paddedPubData));
      if (pubDataPrefixHash != priorityRequests[id].prefixHash) {
        revert ZkLighter_DepositPubdataHashMismatch();
      }

      if (uint8(_priorityPubData[currentPubDataIdx][0]) == TxTypes.PriorityPubDataTypeL1Deposit) {
        if (_priorityPubData[currentPubDataIdx].length != TxTypes.DEPOSIT_PUB_DATA_SIZE) {
          revert ZkLighter_DepositPubdataHashMismatch();
        }
        bytes memory depositPubdata = _priorityPubData[currentPubDataIdx];
        (uint48 accountIndex, uint16 assetIndex, uint64 baseAmount) = TxTypes.readDepositForDesertMode(depositPubdata);
        increaseBalanceToWithdraw(accountIndex, assetIndex, baseAmount);
      }

      ++currentPubDataIdx;
    }

    openPriorityRequestCount -= _n;
    executedPriorityRequestCount += _n;
  }

  /// @inheritdoc IZkLighter
  function commitBatch(CommitBatchInfo calldata newBatchData, StoredBatchInfo calldata lastStoredBatch) external nonReentrant onlyActive {
    governance.isActiveValidator(msg.sender);

    if (newBatchData.pubdataCommitments.length == 0) {
      revert ZkLighter_InvalidBlobCommitmentParams();
    }
    uint8 pubDataMode = uint8(bytes1(newBatchData.pubdataCommitments[0]));
    if (pubDataMode != uint8(PubDataMode.Blob)) {
      revert ZkLighter_InvalidPubDataMode();
    }
    if (newBatchData.endBlockNumber <= lastStoredBatch.endBlockNumber) {
      revert ZkLighter_NonIncreasingBlockNumber();
    }
    if (newBatchData.endBlockNumber != lastStoredBatch.endBlockNumber + newBatchData.batchSize) {
      revert ZkLighter_InvalidBatchSize();
    }
    if (newBatchData.startTimestamp < lastStoredBatch.endTimestamp || newBatchData.endTimestamp < newBatchData.startTimestamp) {
      revert ZkLighter_NonIncreasingTimestamp();
    }
    if (storedBatchHashes[committedBatchesCount] != hashStoredBatchInfo(lastStoredBatch)) {
      revert ZkLighter_StoredBatchInfoMismatch();
    }
    uint64 lastPriorityRequestToCommitCount = committedPriorityRequestCount + newBatchData.priorityRequestCount;
    if (executedPriorityRequestCount + openPriorityRequestCount < lastPriorityRequestToCommitCount) {
      revert ZkLighter_CommitBatchPriorityRequestCountMismatch();
    }
    if (
      lastPriorityRequestToCommitCount >= 1 &&
      priorityRequests[lastPriorityRequestToCommitCount - 1].prefixHash != newBatchData.prefixPriorityRequestHash
    ) {
      revert ZkLighter_PriorityRequestPrefixHashMismatch();
    }

    bytes32 aggregatedBlobCommitment = _processBlobs(newBatchData.pubdataCommitments[1:]);

    bytes32 oldStateRoot = lastStoredBatch.stateRoot;
    if (stateRootUpdates[committedBatchesCount] != bytes32(0)) {
      oldStateRoot = stateRootUpdates[committedBatchesCount];
    }

    committedBatchesCount++;
    if (newBatchData.onChainOperationsHash != bytes32(0)) {
      onChainExecutionQueue[executedOnChainBatchesCount + pendingOnChainBatchesCount] = ExecutionQueueItem({
        batchNumber: committedBatchesCount,
        totalPriorityRequests: committedPriorityRequestCount + newBatchData.priorityRequestCount
      });
      pendingOnChainBatchesCount++;
    }

    bytes32 commitment = keccak256(
      abi.encodePacked(
        newBatchData.endBlockNumber,
        newBatchData.batchSize,
        newBatchData.startTimestamp,
        newBatchData.endTimestamp,
        oldStateRoot,
        newBatchData.newStateRoot,
        newBatchData.newValidiumRoot,
        newBatchData.onChainOperationsHash,
        newBatchData.priorityRequestCount,
        newBatchData.prefixPriorityRequestHash,
        aggregatedBlobCommitment
      )
    );
    storedBatchHashes[committedBatchesCount] = hashStoredBatchInfo(
      StoredBatchInfo({
        batchNumber: committedBatchesCount,
        endBlockNumber: newBatchData.endBlockNumber,
        batchSize: newBatchData.batchSize,
        startTimestamp: newBatchData.startTimestamp,
        endTimestamp: newBatchData.endTimestamp,
        priorityRequestCount: newBatchData.priorityRequestCount,
        prefixPriorityRequestHash: newBatchData.prefixPriorityRequestHash,
        onChainOperationsHash: newBatchData.onChainOperationsHash,
        stateRoot: newBatchData.newStateRoot,
        validiumRoot: newBatchData.newValidiumRoot,
        commitment: commitment
      })
    );
    committedPriorityRequestCount += newBatchData.priorityRequestCount;
    emit BatchCommit(committedBatchesCount, newBatchData.batchSize, newBatchData.endBlockNumber);
  }

  /// @inheritdoc IZkLighter
  function verifyBatch(StoredBatchInfo memory batch, bytes calldata proof) external nonReentrant onlyActive {
    governance.isActiveValidator(msg.sender);

    if (batch.batchNumber != verifiedBatchesCount + 1) {
      revert ZkLighter_VerifyBatchNotInOrder();
    }
    if (hashStoredBatchInfo(batch) != storedBatchHashes[batch.batchNumber]) {
      revert ZkLighter_CannotVerifyNonCommittedBatch();
    }

    uint256[] memory inputs = new uint256[](1);
    inputs[0] = uint256(batch.commitment) % BN254_MODULUS;
    bool success = verifier.Verify(proof, inputs);
    if (!success) {
      revert ZkLighter_VerifyBatchProofFailed();
    }

    emit BatchVerification(batch.batchNumber, batch.batchSize, batch.endBlockNumber);

    verifiedBatchesCount++;
    verifiedPriorityRequestCount += batch.priorityRequestCount;
    lastVerifiedStateRoot = batch.stateRoot;
    lastVerifiedValidiumRoot = batch.validiumRoot;
    lastVerifiedEndBlockNumber = batch.endBlockNumber;
    // Lazy update the executed batches count when:
    // 1. There are no pending items in onChainExecutionQueue and a new batch is verified
    // 2. The next batch in the onChainExecutionQueue is greater than the verifiedBatchesCount
    if (pendingOnChainBatchesCount == 0 || onChainExecutionQueue[executedOnChainBatchesCount].batchNumber > verifiedBatchesCount) {
      executedBatchesCount = verifiedBatchesCount;
      stateRoot = batch.stateRoot;
      validiumRoot = batch.validiumRoot;
      openPriorityRequestCount -= verifiedPriorityRequestCount - executedPriorityRequestCount;
      executedPriorityRequestCount = verifiedPriorityRequestCount;
      emit BatchesExecuted(executedBatchesCount, batch.endBlockNumber);
    }
  }

  function _executeOneBatch(StoredBatchInfo memory batch, bytes memory _onChainOperationsPubData) internal {
    if (storedBatchHashes[batch.batchNumber] != hashStoredBatchInfo(batch)) {
      revert ZkLighter_StoredBatchInfoMismatch();
    }
    bytes32 onChainPubDataHash = bytes32(0);
    uint256 len = _onChainOperationsPubData.length;
    for (uint256 i = 0; i < len; ) {
      uint8 logType;
      (, logType) = Bytes.readUInt8(_onChainOperationsPubData, i);
      if (logType == uint8(TxTypes.OnChainPubDataType.Withdraw)) {
        if (len - i < TxTypes.WithdrawLogSize) {
          revert ZkLighter_InvalidPubDataLength();
        }
        (TxTypes.Withdraw memory _tx, uint256 _offset) = TxTypes.readWithdrawOnChainLog(_onChainOperationsPubData, i);
        increaseBalanceToWithdraw(_tx.masterAccountIndex, _tx.assetIndex, _tx.baseAmount);
        onChainPubDataHash = keccak256(
          abi.encodePacked(onChainPubDataHash, TxTypes.OnChainPubDataType.Withdraw, _tx.masterAccountIndex, _tx.assetIndex, _tx.baseAmount)
        );
        i = _offset;
      } else if (logType == uint8(TxTypes.OnChainPubDataType.USDCWithdraw)) {
        if (len - i < TxTypes.USDCWithdrawLogSize) {
          revert ZkLighter_InvalidPubDataLength();
        }
        (TxTypes.USDCWithdraw memory _tx, uint256 _offset) = TxTypes.readUSDCWithdrawOnChainLog(_onChainOperationsPubData, i);
        increaseBalanceToWithdraw(_tx.masterAccountIndex, USDC_ASSET_INDEX, _tx.usdcAmount);
        onChainPubDataHash = keccak256(
          abi.encodePacked(onChainPubDataHash, TxTypes.OnChainPubDataType.USDCWithdraw, _tx.masterAccountIndex, _tx.usdcAmount)
        );
        i = _offset;
      } else {
        revert ZkLighter_InvalidPubDataType();
      }
    }
    if (onChainPubDataHash != batch.onChainOperationsHash) {
      revert ZkLighter_OnChainOperationsHashMismatch();
    }
  }

  function executeBatches(StoredBatchInfo[] memory batches, bytes[] memory onChainOperationsPubData) external nonReentrant onlyActive {
    if (batches.length != onChainOperationsPubData.length) {
      revert ZkLighter_ExecuteInputLengthMismatch();
    }
    if (batches.length > pendingOnChainBatchesCount) {
      revert ZkLighter_ExecuteInputLengthGreaterThanPendingCount();
    }
    for (uint256 i = 0; i < batches.length; ++i) {
      uint64 batchNumber = batches[i].batchNumber;
      if (batchNumber > verifiedBatchesCount) {
        revert ZkLighter_CannotExecuteNonVerifiedBatch();
      }
      if (batchNumber != onChainExecutionQueue[executedOnChainBatchesCount].batchNumber) {
        revert ZkLighter_BatchNotInOnChainQueue();
      }
      _executeOneBatch(batches[i], onChainOperationsPubData[i]);
      uint64 numExecutedPriorityRequests = onChainExecutionQueue[executedOnChainBatchesCount].totalPriorityRequests - executedPriorityRequestCount;
      executedPriorityRequestCount = onChainExecutionQueue[executedOnChainBatchesCount].totalPriorityRequests;
      executedBatchesCount = batchNumber;
      executedOnChainBatchesCount++;
      pendingOnChainBatchesCount--;
      openPriorityRequestCount -= numExecutedPriorityRequests;
    }
    stateRoot = batches[batches.length - 1].stateRoot;
    validiumRoot = batches[batches.length - 1].validiumRoot;
    // Lazy update the executed batches count when:
    // 1. There are no pending items in onChainExecutionQueue and a new batch is verified
    // 2. The next batch in the onChainExecutionQueue is greater than the verifiedBatchesCount
    if (pendingOnChainBatchesCount == 0 || onChainExecutionQueue[executedOnChainBatchesCount].batchNumber > verifiedBatchesCount) {
      executedBatchesCount = verifiedBatchesCount;
      stateRoot = lastVerifiedStateRoot;
      validiumRoot = lastVerifiedValidiumRoot;
      openPriorityRequestCount -= verifiedPriorityRequestCount - executedPriorityRequestCount;
      executedPriorityRequestCount = verifiedPriorityRequestCount;
      emit BatchesExecuted(executedBatchesCount, lastVerifiedEndBlockNumber);
    } else {
      emit BatchesExecuted(executedBatchesCount, batches[batches.length - 1].endBlockNumber);
    }
  }

  /// @inheritdoc IZkLighter
  function revertBatches(StoredBatchInfo[] memory _batchesToRevert, StoredBatchInfo memory _remainingBatch) external nonReentrant onlyActive {
    governance.isActiveValidator(msg.sender);

    for (uint32 i = 0; i < _batchesToRevert.length; ++i) {
      StoredBatchInfo memory storedBatchInfo = _batchesToRevert[i];
      if (storedBatchInfo.endBlockNumber == 0) {
        revert ZkLighter_CannotRevertGenesisBatch();
      }
      if (storedBatchHashes[committedBatchesCount] != hashStoredBatchInfo(storedBatchInfo)) {
        revert ZkLighter_StoredBatchInfoMismatch();
      }
      if (storedBatchInfo.batchNumber != committedBatchesCount) {
        revert ZkLighter_StoredBatchInfoMismatch();
      }
      delete storedBatchHashes[committedBatchesCount];
      if (storedBatchInfo.onChainOperationsHash != bytes32(0)) {
        if (pendingOnChainBatchesCount == 0) {
          revert ZkLighter_CannotRevertExecutedBatch();
        }
        uint64 totalOnChainBatchesCount = executedOnChainBatchesCount + pendingOnChainBatchesCount;
        if (onChainExecutionQueue[totalOnChainBatchesCount - 1].batchNumber != storedBatchInfo.batchNumber) {
          revert ZkLighter_StoredBatchInfoMismatch();
        }
        // Remove the batch from the execution queue
        delete onChainExecutionQueue[totalOnChainBatchesCount - 1];
        pendingOnChainBatchesCount--;
      }
      committedBatchesCount--;
      committedPriorityRequestCount -= storedBatchInfo.priorityRequestCount;
      if (storedBatchInfo.batchNumber <= verifiedBatchesCount) {
        verifiedBatchesCount--;
        verifiedPriorityRequestCount -= storedBatchInfo.priorityRequestCount;
      }
    }

    // Can not revert executed batch or priority requests
    if (committedBatchesCount < executedBatchesCount || committedPriorityRequestCount < executedPriorityRequestCount) {
      revert ZkLighter_CannotRevertExecutedBatch();
    }

    // Make sure the remaining batch is the last batch
    if (storedBatchHashes[committedBatchesCount] != hashStoredBatchInfo(_remainingBatch)) {
      revert ZkLighter_StoredBatchInfoMismatch();
    }
    // If we reverted verified batches, update the last verified variables for lazy update on executions
    if (_remainingBatch.batchNumber == verifiedBatchesCount) {
      lastVerifiedStateRoot = _remainingBatch.stateRoot;
      lastVerifiedValidiumRoot = _remainingBatch.validiumRoot;
      lastVerifiedEndBlockNumber = _remainingBatch.endBlockNumber;
    }
    emit BatchesRevert(committedBatchesCount);
  }

  /// @inheritdoc IZkLighter
  function transferERC20(IERC20 _token, address _to, uint256 _amount, uint256 _maxAmount) external returns (uint256) {
    // can be called only from this contract as one "external" call (to revert all this function state changes if it is needed)
    if (msg.sender != address(this)) {
      revert ZkLighter_OnlyZkLighter();
    }
    uint256 balanceBefore = _token.balanceOf(address(this));
    SafeERC20.safeTransfer(_token, _to, _amount);
    uint256 balanceAfter = _token.balanceOf(address(this));
    uint256 balanceDiff = balanceBefore - balanceAfter;
    if (balanceDiff > _maxAmount) {
      revert ZkLighter_RollUpBalanceBiggerThanMaxAmount();
    }
    return balanceDiff;
  }

  /// @inheritdoc IZkLighter
  function transferETH(address _to, uint256 _amount) external returns (uint256) {
    // can be called only from this contract as one "external" call (to revert all this function state changes if it is needed)
    if (msg.sender != address(this)) {
      revert ZkLighter_OnlyZkLighter();
    }
    (bool success, ) = _to.call{value: _amount}("");
    if (!success) {
      revert ZkLighter_ETHTransferFailed();
    }
    return _amount;
  }

  function increaseBalanceToWithdraw(uint48 _masterAccountIndex, uint16 _assetIndex, uint128 _baseAmount) internal {
    uint128 balance = pendingAssetBalances[_assetIndex][_masterAccountIndex].balanceToWithdraw;
    pendingAssetBalances[_assetIndex][_masterAccountIndex] = PendingBalance(balance + _baseAmount, FILLED_GAS_RESERVE_VALUE);
  }

  /// @inheritdoc IZkLighter
  function getPendingBalance(address _owner, uint16 _assetIndex) external view returns (uint128) {
    uint48 _masterAccountIndex = getAccountIndexFromAddress(_owner);
    return pendingAssetBalances[_assetIndex][_masterAccountIndex].balanceToWithdraw;
  }

  function getPendingBalanceLegacy(address _owner) public view returns (uint128) {
    uint48 _masterAccountIndex = getAccountIndexFromAddress(_owner);
    return DEPRECATED_pendingBalance[_masterAccountIndex].balanceToWithdraw;
  }

  /// @inheritdoc IZkLighter
  function withdrawPendingBalance(address _owner, uint16 _assetIndex, uint128 _baseAmount) external nonReentrant {
    uint48 masterAccountIndex = getAccountIndexFromAddress(_owner);
    uint128 baseBalance = pendingAssetBalances[_assetIndex][masterAccountIndex].balanceToWithdraw;
    if (_baseAmount > baseBalance || _baseAmount == 0) {
      revert ZkLighter_InvalidWithdrawAmount();
    }

    AssetConfig memory assetConfig = assetConfigs[_assetIndex];
    if (_assetIndex != NATIVE_ASSET_INDEX && assetConfig.tokenAddress == address(0)) {
      revert ZkLighter_InvalidAssetIndex();
    }
    uint256 amount = uint256(_baseAmount) * uint256(assetConfig.tickSize);
    uint256 balance = uint256(baseBalance) * uint256(assetConfig.tickSize);
    // We will allow withdrawals of `value` such that:
    // `value` <= user pending balance
    // `value` can be bigger then `_amount` requested if token stakes fee from sender in addition to `_amount` requested
    uint256 transferredAmount;
    if (_assetIndex == NATIVE_ASSET_INDEX) {
      transferredAmount = this.transferETH(_owner, amount);
    } else {
      transferredAmount = this.transferERC20(IERC20(assetConfig.tokenAddress), _owner, amount, balance);
    }
    if (transferredAmount % assetConfig.tickSize != 0) {
      revert ZkLighter_TransferredAmountNotMultipleOfTickSize();
    }

    // update balanceToWithdraw by subtracting the actual amount that was sent
    uint256 transferredBaseAmount = transferredAmount / assetConfig.tickSize;
    uint128 transferredBaseAmount128 = SafeCast.toUint128(transferredBaseAmount);
    pendingAssetBalances[_assetIndex][masterAccountIndex].balanceToWithdraw = baseBalance - transferredBaseAmount128;
    emit WithdrawPending(_owner, _assetIndex, transferredBaseAmount128);
  }

  /// @inheritdoc IZkLighter
  function withdrawPendingBalanceLegacy(address _owner, uint128 _baseAmount) external nonReentrant {
    uint48 masterAccountIndex = getAccountIndexFromAddress(_owner);
    uint128 baseBalance = DEPRECATED_pendingBalance[masterAccountIndex].balanceToWithdraw;
    if (_baseAmount > baseBalance || _baseAmount == 0) {
      revert ZkLighter_InvalidWithdrawAmount();
    }
    // We will allow withdrawals of `value` such that:
    // `value` <= user pending balance
    // `value` can be bigger then `_amount` requested if token takes fee from sender in addition to `_amount` requested
    uint256 amount = this.transferERC20(governance.usdc(), _owner, uint256(_baseAmount), uint256(baseBalance));
    uint128 transferredBaseAmount128 = SafeCast.toUint128(amount);

    // Update balanceToWithdraw by subtracting the actual amount that was sent
    DEPRECATED_pendingBalance[masterAccountIndex].balanceToWithdraw = baseBalance - transferredBaseAmount128;
    emit WithdrawPending(_owner, USDC_ASSET_INDEX, transferredBaseAmount128);
  }

  /// @inheritdoc IZkLighter
  function activateDesertMode() external nonReentrant returns (bool) {
    bool trigger = openPriorityRequestCount != 0 &&
      block.timestamp >= priorityRequests[executedPriorityRequestCount].expirationTimestamp &&
      priorityRequests[executedPriorityRequestCount].expirationTimestamp != 0;
    if (trigger) {
      if (!desertMode) {
        desertMode = true;
        emit DesertMode();
      }
      return true;
    } else {
      return false;
    }
  }

  /// @notice Change address that collects fees from listed markets
  function setTreasury(address _newTreasury) external nonReentrant {
    governance.requireGovernor(msg.sender);
    if (_newTreasury == address(0)) {
      revert ZkLighter_TreasuryCannotBeZero();
    }
    if (getAccountIndexFromAddress(_newTreasury) != NIL_ACCOUNT_INDEX) {
      revert ZkLighter_TreasuryCannotBeInUse();
    }
    treasury = _newTreasury;
    emit TreasuryUpdate(treasury);
  }

  /// @notice Change address that operates the insurance fund
  function setInsuranceFundOperator(address _newInsuranceFundOperator) external nonReentrant {
    governance.requireGovernor(msg.sender);
    if (_newInsuranceFundOperator == address(0)) {
      revert ZkLighter_InsuranceFundOperatorCannotBeZero();
    }
    if (getAccountIndexFromAddress(_newInsuranceFundOperator) != NIL_ACCOUNT_INDEX) {
      revert ZkLighter_InsuranceFundOperatorCannotBeInUse();
    }
    insuranceFundOperator = _newInsuranceFundOperator;
    emit InsuranceFundOperatorUpdate(insuranceFundOperator);
  }

  /// @notice Delegates the call to the additional part of the main contract.
  /// @notice Should be only use to delegate the external calls as it passes the calldata
  /// @notice All functions delegated to additional contract should NOT be nonReentrant
  function delegateAdditional() internal {
    if (address(this) == zklighterImplementation) {
      revert ZkLighter_ImplCantDelegateToAddl();
    }

    address _target = address(additionalZkLighter);
    assembly {
      // The pointer to the free memory slot
      let ptr := mload(0x40)
      // Copy function signature and arguments from calldata at zero position into memory at pointer position
      calldatacopy(ptr, 0x0, calldatasize())
      // Delegatecall method of the implementation contract, returns 0 on error
      let result := delegatecall(gas(), _target, ptr, calldatasize(), 0x0, 0)
      // Get the size of the last return data
      let size := returndatasize()
      // Copy the size length of bytes from return data at zero position to pointer position
      returndatacopy(ptr, 0x0, size)

      // Depending on result value
      switch result
      case 0 {
        // End execution and revert state changes
        revert(ptr, size)
      }
      default {
        // Return data with length of size at pointers position
        return(ptr, size)
      }
    }
  }

  /// @notice Calls the point evaluation precompile to verify the blob commitment
  /// @param blobVersionedHash is the versioned hash of the blob
  /// @param evaluationPointCommitmentProof is the evaluation point commitment proof
  function _pointEvaluationPrecompile(bytes32 blobVersionedHash, bytes calldata evaluationPointCommitmentProof) internal view {
    bytes memory precompileInput = abi.encodePacked(blobVersionedHash, evaluationPointCommitmentProof);

    (bool success, bytes memory data) = POINT_EVALUATION_PRECOMPILE_ADDRESS.staticcall(precompileInput);

    // We verify that the point evaluation precompile call was successful by testing the latter 32 bytes of the
    // response is equal to BLS_MODULUS as defined in https://eips.ethereum.org/EIPS/eip-4844#point-evaluation-precompile
    if (!success) {
      revert ZkLighter_InvalidPointEvaluationParams();
    }
    (, uint256 result) = abi.decode(data, (uint256, uint256));
    if (result != BLS_MODULUS) {
      revert ZkLighter_InvalidPointEvaluationParams();
    }
  }

  /// @notice Verifies if the sent blob commitment data is consistent with the blob itself
  /// @param pubDataCommitments is a list of: evaluationPointX (32 bytes) || evaluationPointY (32 bytes) || commitment (48 bytes) || proof (48 bytes)) = 160 bytes
  /// @return aggregatedBlobCommitment is the aggregated blob commitment for all blobs in the pubDataCommitments
  function _processBlobs(bytes calldata pubDataCommitments) internal view returns (bytes32 aggregatedBlobCommitment) {
    if (pubDataCommitments.length % BLOB_DATA_COMMITMENT_BYTE_SIZE != 0) {
      revert ZkLighter_InvalidBlobCommitmentParams();
    }

    uint256 blobCount = pubDataCommitments.length / BLOB_DATA_COMMITMENT_BYTE_SIZE;
    if (blobCount > MAX_BLOB_COUNT) {
      revert ZkLighter_InvalidBlobCount(blobCount);
    }

    for (uint256 i = 0; i < blobCount; i++) {
      bytes calldata _blobDataCommitment = pubDataCommitments[i * BLOB_DATA_COMMITMENT_BYTE_SIZE:(1 + i) * BLOB_DATA_COMMITMENT_BYTE_SIZE];
      bytes32 evaluationPointX = bytes32(_blobDataCommitment[0:32]);
      bytes32 evaluationPointY = bytes32(_blobDataCommitment[32:64]);
      bytes32 blobVersionedHash;
      assembly {
        blobVersionedHash := blobhash(i)
      }
      if (blobVersionedHash == bytes32(0)) {
        revert ZkLighter_InvalidBlobCommitmentParams();
      }

      _pointEvaluationPrecompile(blobVersionedHash, _blobDataCommitment);

      bytes32 currentBlobCommitment = keccak256(abi.encodePacked(evaluationPointX, evaluationPointY, blobVersionedHash));
      if (i == 0) {
        aggregatedBlobCommitment = currentBlobCommitment;
      } else {
        aggregatedBlobCommitment = keccak256(abi.encodePacked(aggregatedBlobCommitment, currentBlobCommitment));
      }
    }

    // Verify there are no extra blob hashes attached to the call. `blobhash` will return 0 bytes if no blob
    // hash is present at given index. Leaps are not allowed, so checking the first next index is sufficient.
    bytes32 emptyBlobVersionedHash;
    assembly {
      emptyBlobVersionedHash := blobhash(blobCount)
    }
    if (emptyBlobVersionedHash != bytes32(0)) {
      revert ZkLighter_InvalidBlobCommitmentParams();
    }

    return aggregatedBlobCommitment;
  }
}

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

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized != type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

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

pragma solidity ^0.8.0;
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuardUpgradeable is Initializable {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    function __ReentrancyGuard_init() internal onlyInitializing {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

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

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

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

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

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

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

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

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

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

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

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

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

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

File 9 of 22 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {
    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.2._
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v2.5._
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.2._
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v2.5._
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v2.5._
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v2.5._
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     *
     * _Available since v2.5._
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     *
     * _Available since v3.0._
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.7._
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.7._
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     *
     * _Available since v3.0._
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./interfaces/IEvents.sol";
import "./lib/TxTypes.sol";
import "./Storage.sol";
import "./ExtendableStorage.sol";

/// @title zkLighter Additional Contract
/// @notice zkLighter Contract delegates some of its functionality to this contract
/// @author zkLighter Team
contract AdditionalZkLighter is IEvents, Storage, ReentrancyGuardUpgradeable, ExtendableStorage {
  error AdditionalZkLighter_InvalidAssetIndex();
  error AdditionalZkLighter_InvalidDepositAmount();
  error AdditionalZkLighter_InvalidWithdrawAmount();
  error AdditionalZkLighter_InvalidAccountIndex();
  error AdditionalZkLighter_InvalidDepositBatchLength();
  error AdditionalZkLighter_InvalidApiKeyIndex();
  error AdditionalZkLighter_InvalidPubKey();
  error AdditionalZkLighter_RecipientAddressInvalid();
  error AdditionalZkLighter_InvalidMarketStatus();
  error AdditionalZkLighter_InvalidMarginMode();
  error AdditionalZkLighter_InvalidExtensionMultiplier();
  error AdditionalZkLighter_InvalidFeeAmount();
  error AdditionalZkLighter_InvalidMarginFraction();
  error AdditionalZkLighter_InvalidMinAmounts();
  error AdditionalZkLighter_InvalidConfigPeriod();
  error AdditionalZkLighter_InvalidMarketType();
  error AdditionalZkLighter_InvalidShareAmount();
  error AdditionalZkLighter_InvalidCreateOrderParameters();
  error AdditionalZkLighter_AccountIsNotRegistered();
  error AdditionalZkLighter_InvalidBatch();
  error AdditionalZkLighter_InvalidFundingClampsOrInterestRate();
  error AdditionalZkLighter_InvalidMarketLimits();
  error AdditionalZkLighter_StateRootUpgradeVerifierFailed();

  function updateStateRoot(
    StoredBatchInfo calldata _lastStoredBatch,
    bytes32 _stateRoot,
    bytes32 _validiumRoot,
    bytes calldata proof
  ) external nonReentrant onlyActive {
    governance.isActiveValidator(msg.sender);

    if (storedBatchHashes[committedBatchesCount] != hashStoredBatchInfo(_lastStoredBatch)) {
      revert AdditionalZkLighter_InvalidBatch();
    }

    if (executedBatchesCount != committedBatchesCount) {
      revert AdditionalZkLighter_InvalidBatch();
    }

    if (stateRootUpgradeVerifier != IZkLighterStateRootUpgradeVerifier(address(0))) {
      bytes32 hashOutput = keccak256(abi.encodePacked(stateRoot, validiumRoot, _stateRoot, _validiumRoot));

      uint256[] memory inputs = new uint256[](1);
      inputs[0] = uint256(hashOutput) % BN254_MODULUS;

      bool success = stateRootUpgradeVerifier.Verify(proof, inputs);
      if (!success) {
        revert AdditionalZkLighter_StateRootUpgradeVerifierFailed();
      }

      stateRootUpgradeVerifier = IZkLighterStateRootUpgradeVerifier(address(0));
    } else {
      revert AdditionalZkLighter_StateRootUpgradeVerifierFailed();
    }

    stateRoot = _stateRoot;
    validiumRoot = _validiumRoot;
    lastVerifiedStateRoot = _stateRoot;
    lastVerifiedValidiumRoot = _validiumRoot;
    stateRootUpdates[committedBatchesCount] = _stateRoot;

    emit StateRootUpdate(committedBatchesCount, _lastStoredBatch.stateRoot, _stateRoot);
  }

  /// @notice Deposit ETH or ERC20 asset to zkLighter
  /// @param _assetIndex Asset index
  /// @param _routeType Route type
  /// @param _amount ETH or ERC20 asset amount to deposit
  /// @param _to The receiver L1 address
  function _deposit(address[] memory _to, uint16 _assetIndex, TxTypes.RouteType _routeType, uint256[] memory _amount) internal {
    AssetConfig memory assetConfig = assetConfigs[_assetIndex];
    if (_assetIndex != NATIVE_ASSET_INDEX) {
      if (assetConfig.tokenAddress == address(0)) {
        revert AdditionalZkLighter_InvalidAssetIndex();
      }
      if (msg.value != 0) {
        revert AdditionalZkLighter_InvalidDepositAmount();
      }
    }
    uint256 minDeposit = uint256(assetConfig.minDepositTicks) * uint256(assetConfig.tickSize);
    uint256 depositCap = uint256(assetConfig.depositCapTicks) * uint256(assetConfig.tickSize);
    uint256 totalAmount = 0;
    for (uint256 i = 0; i < _amount.length; ++i) {
      totalAmount += _amount[i];
      if (_amount[i] < minDeposit || _amount[i] % assetConfig.tickSize != 0) {
        revert AdditionalZkLighter_InvalidDepositAmount();
      }
      if (_to[i] == address(0)) {
        revert AdditionalZkLighter_RecipientAddressInvalid();
      }
    }

    uint256 balanceAfter = 0;
    if (_assetIndex == NATIVE_ASSET_INDEX) {
      balanceAfter = address(this).balance;
      if (msg.value != totalAmount) {
        revert AdditionalZkLighter_InvalidDepositAmount();
      }
    } else {
      IERC20 _token = IERC20(assetConfig.tokenAddress);
      uint256 balanceBefore = _token.balanceOf(address(this));
      SafeERC20.safeTransferFrom(_token, msg.sender, address(this), totalAmount);
      balanceAfter = _token.balanceOf(address(this));
      if (balanceAfter < balanceBefore + totalAmount) {
        revert AdditionalZkLighter_InvalidDepositAmount();
      } else if (balanceAfter > balanceBefore + totalAmount) {
        uint256 excessAmount = balanceAfter - (balanceBefore + totalAmount);
        uint64 baseExcessAmount = SafeCast.toUint64(excessAmount / assetConfig.tickSize);
        increaseBalanceToWithdraw(TREASURY_ACCOUNT_INDEX, _assetIndex, baseExcessAmount);
      }
    }

    if (balanceAfter > depositCap) {
      revert AdditionalZkLighter_InvalidDepositAmount();
    }
    for (uint256 i = 0; i < _amount.length; ++i) {
      uint64 baseAmount = SafeCast.toUint64(_amount[i] / assetConfig.tickSize);
      registerDeposit(_to[i], _assetIndex, _routeType, baseAmount);
    }
  }

  /// @notice Deposit asset to Lighter
  /// @param _to The receiver L1 address
  /// @param _assetIndex Asset index
  /// @param _routeType Route type
  /// @param _amount asset amount to deposit
  function deposit(address _to, uint16 _assetIndex, TxTypes.RouteType _routeType, uint256 _amount) external payable nonReentrant onlyActive {
    uint256[] memory amount = new uint256[](1);
    amount[0] = _amount;
    address[] memory to = new address[](1);
    to[0] = _to;
    _deposit(to, _assetIndex, _routeType, amount);
  }

  /// @notice Deposit USDC to Lighter for multiple users
  /// @param _amount Array of USDC Token amounts
  /// @param _to Array of receiver L1 addresses
  /// @param _accountIndex Array of account index values, will be used in the future
  function depositBatch(uint64[] calldata _amount, address[] calldata _to, uint48[] calldata _accountIndex) external nonReentrant onlyActive {
    if (_amount.length != _to.length || _amount.length != _accountIndex.length || _amount.length == 0 || _amount.length > MAX_BATCH_DEPOSIT_LENGTH) {
      revert AdditionalZkLighter_InvalidDepositBatchLength();
    }
    uint256[] memory amount = new uint256[](_amount.length);
    for (uint256 i = 0; i < _amount.length; ++i) {
      amount[i] = uint256(_amount[i]);
    }
    _deposit(_to, USDC_ASSET_INDEX, TxTypes.RouteType.Perps, amount);
  }

  /// @notice Change Lighter public key for an account api key slot
  function changePubKey(uint48 _accountIndex, uint8 _apiKeyIndex, bytes calldata _pubKey) external nonReentrant onlyActive {
    if (_accountIndex > MAX_ACCOUNT_INDEX) {
      revert AdditionalZkLighter_InvalidAccountIndex();
    }
    if (_apiKeyIndex > MAX_API_KEY_INDEX) {
      revert AdditionalZkLighter_InvalidApiKeyIndex();
    }

    // Verify that the public key is of the correct length
    if (_pubKey.length != PUB_KEY_BYTES_SIZE) {
      revert AdditionalZkLighter_InvalidPubKey();
    }

    // Verify that the public key is not empty
    for (uint8 i = 0; i < _pubKey.length; ++i) {
      if (_pubKey[i] != 0) {
        break;
      }
      if (i == _pubKey.length - 1) {
        revert AdditionalZkLighter_InvalidPubKey();
      }
    }

    // Verify that the public key is in the field
    for (uint8 i = 0; i < 5; i++) {
      bytes memory elem = _pubKey[(8 * i):(8 * (i + 1))];
      uint64 elemValue = 0;
      for (uint8 j = 0; j < 8; j++) {
        elemValue = elemValue + (uint64(uint8(elem[j])) << (8 * j));
      }
      if (elemValue >= GOLDILOCKS_MODULUS) {
        revert AdditionalZkLighter_InvalidPubKey();
      }
    }

    uint48 _masterAccountIndex = getAccountIndexFromAddress(msg.sender);
    if (_masterAccountIndex == NIL_ACCOUNT_INDEX) {
      revert AdditionalZkLighter_AccountIsNotRegistered();
    }

    // Add priority request to the queue
    TxTypes.ChangePubKey memory _tx = TxTypes.ChangePubKey({
      accountIndex: _accountIndex,
      masterAccountIndex: _masterAccountIndex,
      apiKeyIndex: _apiKeyIndex,
      pubKey: _pubKey
    });
    bytes memory pubData = TxTypes.writeChangePubKeyPubDataForPriorityQueue(_tx);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1ChangePubKey, pubData, pubData);
  }

  /// @notice Create new asset
  /// @param _params Config parameters
  function setSystemConfig(TxTypes.SetSystemConfig calldata _params) external nonReentrant onlyActive {
    governance.requireGovernor(msg.sender);
    if (_params.liquidityPoolCooldownPeriod > MAX_CONFIG_PERIOD || _params.stakingPoolLockupPeriod > MAX_CONFIG_PERIOD) {
      revert AdditionalZkLighter_InvalidConfigPeriod();
    }
    if (_params.liquidityPoolIndex > NIL_ACCOUNT_INDEX || _params.stakingPoolIndex > NIL_ACCOUNT_INDEX) {
      revert AdditionalZkLighter_InvalidAccountIndex();
    }
    bytes memory priorityRequest = TxTypes.writeSetSystemConfigPubDataForPriorityQueue(_params);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1SetSystemConfig, priorityRequest, priorityRequest);
  }

  /// @notice Create new asset
  /// @param _l1Decimals [metadata] Number of decimals of the asset on L1
  /// @param _decimals [metadata] Number of decimals of the asset in Lighter
  /// @param _symbol [metadata] symbol of the asset, formatted as bytes32
  /// @param _params Asset parameters
  function registerAsset(
    uint8 _l1Decimals,
    uint8 _decimals,
    bytes32 _symbol,
    TxTypes.RegisterAsset calldata _params
  ) external nonReentrant onlyActive {
    governance.requireGovernor(msg.sender);

    AssetConfig memory config = assetConfigs[_params.assetIndex];
    if (_params.assetIndex != NATIVE_ASSET_INDEX && config.tokenAddress == address(0)) {
      revert AdditionalZkLighter_InvalidAssetIndex();
    }
    if (_params.extensionMultiplier != config.extensionMultiplier) {
      revert AdditionalZkLighter_InvalidExtensionMultiplier();
    }
    if (_params.minL2TransferAmount == 0 || _params.minL2TransferAmount > MAX_DEPOSIT_CAP_TICKS) {
      revert AdditionalZkLighter_InvalidMinAmounts();
    }
    if (_params.minL2WithdrawalAmount == 0 || _params.minL2WithdrawalAmount > MAX_DEPOSIT_CAP_TICKS) {
      revert AdditionalZkLighter_InvalidMinAmounts();
    }
    if (_params.marginMode > uint8(type(TxTypes.AssetMarginMode).max)) {
      revert AdditionalZkLighter_InvalidMarginMode();
    }
    if (_params.marginMode == uint8(TxTypes.AssetMarginMode.Enabled) && _params.assetIndex != USDC_ASSET_INDEX) {
      revert AdditionalZkLighter_InvalidMarginMode();
    }
    if (_params.marginMode == uint8(TxTypes.AssetMarginMode.Disabled) && _params.assetIndex == USDC_ASSET_INDEX) {
      revert AdditionalZkLighter_InvalidMarginMode();
    }
    bytes memory priorityRequest = TxTypes.writeRegisterAssetPubDataForPriorityQueue(_params);
    bytes memory metadata = TxTypes.writeRegisterAssetPubDataForPriorityQueueWithMetadata(
      priorityRequest,
      _l1Decimals,
      _decimals,
      config.tickSize,
      config.tokenAddress,
      _symbol
    );
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1RegisterAsset, priorityRequest, metadata);
    emit RegisterAsset(_params, _l1Decimals, _decimals, _symbol);
  }

  /// @notice Update asset parameters
  /// @param _params Asset update parameters
  function updateAsset(TxTypes.UpdateAsset calldata _params) external nonReentrant onlyActive {
    governance.requireGovernor(msg.sender);

    validateAssetIndex(_params.assetIndex);
    if (_params.minL2TransferAmount == 0 || _params.minL2TransferAmount > MAX_DEPOSIT_CAP_TICKS) {
      revert AdditionalZkLighter_InvalidMinAmounts();
    }
    if (_params.minL2WithdrawalAmount == 0 || _params.minL2WithdrawalAmount > MAX_DEPOSIT_CAP_TICKS) {
      revert AdditionalZkLighter_InvalidMinAmounts();
    }
    if (_params.marginMode > uint8(type(TxTypes.AssetMarginMode).max)) {
      revert AdditionalZkLighter_InvalidMarginMode();
    }
    if (_params.marginMode == uint8(TxTypes.AssetMarginMode.Enabled) && _params.assetIndex != USDC_ASSET_INDEX) {
      revert AdditionalZkLighter_InvalidMarginMode();
    }
    if (_params.marginMode == uint8(TxTypes.AssetMarginMode.Disabled) && _params.assetIndex == USDC_ASSET_INDEX) {
      revert AdditionalZkLighter_InvalidMarginMode();
    }
    bytes memory pubData = TxTypes.writeUpdateAssetPubDataForPriorityQueue(_params);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1UpdateAsset, pubData, pubData);
    emit UpdateAsset(_params);
  }

  /// @notice Create new market and an order book
  /// @param _size_decimals [metadata] Number of decimals to represent size of an order in the order book
  /// @param _price_decimals [metadata] Number of decimals to represent price of an order in the order book
  /// @param _symbol [metadata] symbol of the market, formatted as bytes32
  /// @param _params Market parameters
  function createMarket(
    uint8 _size_decimals,
    uint8 _price_decimals,
    bytes32 _symbol,
    TxTypes.CreateMarket calldata _params
  ) external nonReentrant onlyActive {
    governance.requireGovernor(msg.sender);

    validateCreateMarketParams(_params);

    // Add priority request to the queue
    bytes memory priorityRequest = TxTypes.writeCreateMarketPubDataForPriorityQueue(_params);
    bytes memory metadata = TxTypes.writeCreateMarketPubDataForPriorityQueueWithMetadata(priorityRequest, _size_decimals, _price_decimals, _symbol);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1CreateMarket, priorityRequest, metadata);
    emit CreateMarket(_params, _size_decimals, _price_decimals, _symbol);
  }

  function validateCommonPerpMarketParams(TxTypes.CommonPerpsData memory perpParams) internal pure {
    if (perpParams.makerFee > FEE_TICK || perpParams.takerFee > FEE_TICK || perpParams.liquidationFee > FEE_TICK) {
      revert AdditionalZkLighter_InvalidFeeAmount();
    }
    if (
      perpParams.closeOutMarginFraction == 0 ||
      perpParams.closeOutMarginFraction > perpParams.maintenanceMarginFraction ||
      perpParams.maintenanceMarginFraction > perpParams.minInitialMarginFraction ||
      perpParams.minInitialMarginFraction > perpParams.defaultInitialMarginFraction ||
      perpParams.defaultInitialMarginFraction > MARGIN_TICK
    ) {
      revert AdditionalZkLighter_InvalidMarginFraction();
    }
    if (perpParams.interestRate > FUNDING_RATE_TICK) {
      revert AdditionalZkLighter_InvalidFundingClampsOrInterestRate();
    }
    if (perpParams.fundingClampSmall > FUNDING_RATE_TICK || perpParams.fundingClampBig > FUNDING_RATE_TICK) {
      revert AdditionalZkLighter_InvalidFundingClampsOrInterestRate();
    }
    if (
      perpParams.minBaseAmount == 0 ||
      perpParams.minBaseAmount > MAX_ORDER_BASE_AMOUNT ||
      perpParams.minQuoteAmount == 0 ||
      perpParams.minQuoteAmount > MAX_ORDER_QUOTE_AMOUNT
    ) {
      revert AdditionalZkLighter_InvalidMinAmounts();
    }
    if (perpParams.orderQuoteLimit > MAX_ORDER_QUOTE_AMOUNT || perpParams.minQuoteAmount > perpParams.orderQuoteLimit) {
      revert AdditionalZkLighter_InvalidMarketLimits();
    }
    if (perpParams.openInterestLimit < perpParams.orderQuoteLimit || perpParams.openInterestLimit > MAX_MARKET_OPEN_INTEREST) {
      revert AdditionalZkLighter_InvalidMarketLimits();
    }
  }

  function validateCommonSpotMarketParams(TxTypes.CommonSpotData memory spotParams) internal pure {
    if (spotParams.makerFee > FEE_TICK || spotParams.takerFee > FEE_TICK) {
      revert AdditionalZkLighter_InvalidFeeAmount();
    }
    if (
      spotParams.minBaseAmount == 0 ||
      spotParams.minBaseAmount > MAX_ORDER_BASE_AMOUNT ||
      spotParams.minQuoteAmount == 0 ||
      spotParams.minQuoteAmount > MAX_ORDER_QUOTE_AMOUNT
    ) {
      revert AdditionalZkLighter_InvalidMinAmounts();
    }
    if (spotParams.orderQuoteLimit > MAX_ORDER_QUOTE_AMOUNT || spotParams.minQuoteAmount > spotParams.orderQuoteLimit) {
      revert AdditionalZkLighter_InvalidMarketLimits();
    }
  }

  function validateCreateMarketParams(TxTypes.CreateMarket calldata _params) internal view {
    if (_params.marketType == TxTypes.MarketType.Perps) {
      TxTypes.CreateMarketPerpsData memory perpParams = TxTypes.readCreateMarketPerpsData(_params.marketData);
      if (_params.marketIndex > MAX_PERPS_MARKET_INDEX) {
        revert AdditionalZkLighter_InvalidMarketType();
      }
      if (perpParams.quoteMultiplier == 0 || perpParams.quoteMultiplier > MAX_QUOTE_MULTIPLIER) {
        revert AdditionalZkLighter_InvalidExtensionMultiplier();
      }
      validateCommonPerpMarketParams(perpParams.common);
    } else if (_params.marketType == TxTypes.MarketType.Spot) {
      TxTypes.CreateMarketSpotData memory spotParams = TxTypes.readCreateMarketSpotData(_params.marketData);
      if (_params.marketIndex < MIN_SPOT_MARKET_INDEX || _params.marketIndex > MAX_SPOT_MARKET_INDEX) {
        revert AdditionalZkLighter_InvalidMarketType();
      }
      if (spotParams.baseAssetIndex == spotParams.quoteAssetIndex) {
        revert AdditionalZkLighter_InvalidAssetIndex();
      }
      validateAssetIndex(spotParams.baseAssetIndex);
      validateAssetIndex(spotParams.quoteAssetIndex);
      if (
        spotParams.sizeExtensionMultiplier == 0 ||
        spotParams.sizeExtensionMultiplier > MAX_ASSET_EXTENSION_MULTIPLIER ||
        spotParams.sizeExtensionMultiplier % Config.FEE_TICK != 0
      ) {
        revert AdditionalZkLighter_InvalidExtensionMultiplier();
      }
      if (
        spotParams.quoteExtensionMultiplier == 0 ||
        spotParams.quoteExtensionMultiplier > MAX_ASSET_EXTENSION_MULTIPLIER ||
        spotParams.quoteExtensionMultiplier % Config.FEE_TICK != 0
      ) {
        revert AdditionalZkLighter_InvalidExtensionMultiplier();
      }
      validateCommonSpotMarketParams(spotParams.common);
    } else {
      revert AdditionalZkLighter_InvalidMarketType();
    }
  }

  /// @notice Update order book status
  /// @param _params Order book update parameters
  function updateMarket(TxTypes.UpdateMarket calldata _params) external nonReentrant onlyActive {
    governance.requireGovernor(msg.sender);
    validateUpdateMarketParams(_params);
    // Add priority request to the queue
    bytes memory pubdata = TxTypes.writeUpdateMarketPubDataForPriorityQueue(_params);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1UpdateMarket, pubdata, pubdata);
    emit UpdateMarket(_params);
  }

  function validateUpdateMarketParams(TxTypes.UpdateMarket calldata _params) internal pure {
    if (_params.marketType == TxTypes.MarketType.Perps) {
      if (_params.marketIndex > MAX_PERPS_MARKET_INDEX) {
        revert AdditionalZkLighter_InvalidMarketType();
      }
      TxTypes.UpdateMarketPerps memory perpParams = TxTypes.readUpdateMarketPerpsData(_params.marketData);
      if (perpParams.status != uint8(MarketStatus.ACTIVE) && perpParams.status != uint8(MarketStatus.NONE)) {
        revert AdditionalZkLighter_InvalidMarketStatus();
      }
      validateCommonPerpMarketParams(perpParams.common);
    } else if (_params.marketType == TxTypes.MarketType.Spot) {
      if (_params.marketIndex < MIN_SPOT_MARKET_INDEX || _params.marketIndex > MAX_SPOT_MARKET_INDEX) {
        revert AdditionalZkLighter_InvalidMarketType();
      }
      TxTypes.UpdateMarketSpot memory spotParams = TxTypes.readUpdateMarketSpotData(_params.marketData);
      if (spotParams.status != uint8(MarketStatus.ACTIVE) && spotParams.status != uint8(MarketStatus.NONE)) {
        revert AdditionalZkLighter_InvalidMarketStatus();
      }
      validateCommonSpotMarketParams(spotParams.common);
    } else {
      revert AdditionalZkLighter_InvalidMarketType();
    }
  }

  /// @notice Cancels all orders
  function cancelAllOrders(uint48 _accountIndex) external nonReentrant onlyActive {
    if (_accountIndex > MAX_ACCOUNT_INDEX) {
      revert AdditionalZkLighter_InvalidAccountIndex();
    }
    uint48 _masterAccountIndex = getAccountIndexFromAddress(msg.sender);
    if (_masterAccountIndex == NIL_ACCOUNT_INDEX) {
      revert AdditionalZkLighter_AccountIsNotRegistered();
    }

    // Add priority request to the queue
    TxTypes.CancelAllOrders memory _tx = TxTypes.CancelAllOrders({accountIndex: _accountIndex, masterAccountIndex: _masterAccountIndex});
    bytes memory pubData = TxTypes.writeCancelAllOrdersPubDataForPriorityQueue(_tx);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1CancelAllOrders, pubData, pubData);
  }

  /// @notice Withdraw ETH or ERC20 from zkLighter
  /// @param _assetIndex Asset index, 0 for ETH
  /// @param _routeType Route type
  /// @param _accountIndex Account index to withdraw from
  /// @param _baseAmount Amount of base token to withdraw, in ticks
  function withdraw(uint48 _accountIndex, uint16 _assetIndex, TxTypes.RouteType _routeType, uint64 _baseAmount) external nonReentrant onlyActive {
    AssetConfig memory assetConfig = assetConfigs[_assetIndex];
    if (_assetIndex != NATIVE_ASSET_INDEX && assetConfig.tokenAddress == address(0)) {
      revert AdditionalZkLighter_InvalidAssetIndex();
    }
    if (assetConfig.withdrawalsEnabled == 0) {
      revert AdditionalZkLighter_InvalidAssetIndex();
    }
    uint256 depositCapTicks = assetConfig.depositCapTicks;
    if (_baseAmount == 0 || _baseAmount > depositCapTicks) {
      revert AdditionalZkLighter_InvalidWithdrawAmount();
    }
    if (_routeType != TxTypes.RouteType.Perps && _routeType != TxTypes.RouteType.Spot) {
      revert AdditionalZkLighter_InvalidWithdrawAmount();
    }
    uint48 _masterAccountIndex = getAccountIndexFromAddress(msg.sender);
    if (_masterAccountIndex == NIL_ACCOUNT_INDEX) {
      revert AdditionalZkLighter_AccountIsNotRegistered();
    }

    TxTypes.L1Withdraw memory _tx = TxTypes.L1Withdraw({
      accountIndex: _accountIndex,
      masterAccountIndex: _masterAccountIndex,
      assetIndex: _assetIndex,
      routeType: _routeType,
      baseAmount: _baseAmount
    });

    bytes memory pubData = TxTypes.writeWithdrawPubDataForPriorityQueue(_tx);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1Withdraw, pubData, pubData);
  }

  /// @notice Create an order for a Lighter account
  /// @param _accountIndex Account index
  /// @param _marketIndex Market index
  /// @param _baseAmount Amount of base token
  /// @param _price Price of the order
  /// @param _isAsk Flag to indicate if the order is ask or bid
  /// @param _orderType Order type
  function createOrder(
    uint48 _accountIndex,
    uint16 _marketIndex,
    uint48 _baseAmount,
    uint32 _price,
    uint8 _isAsk,
    uint8 _orderType
  ) external nonReentrant onlyActive {
    if (_accountIndex > MAX_ACCOUNT_INDEX) {
      revert AdditionalZkLighter_InvalidAccountIndex();
    }
    uint48 _masterAccountIndex = getAccountIndexFromAddress(msg.sender);
    if (_masterAccountIndex == NIL_ACCOUNT_INDEX) {
      revert AdditionalZkLighter_AccountIsNotRegistered();
    }
    if (_isAsk != 0 && _isAsk != 1) {
      revert AdditionalZkLighter_InvalidCreateOrderParameters();
    }

    if (_orderType != uint8(TxTypes.OrderType.LimitOrder) && _orderType != uint8(TxTypes.OrderType.MarketOrder)) {
      revert AdditionalZkLighter_InvalidCreateOrderParameters();
    }

    if (_baseAmount != NIL_ORDER_BASE_AMOUNT && (_baseAmount > MAX_ORDER_BASE_AMOUNT || _baseAmount < MIN_ORDER_BASE_AMOUNT)) {
      revert AdditionalZkLighter_InvalidCreateOrderParameters();
    }

    if (_price > MAX_ORDER_PRICE || _price < MIN_ORDER_PRICE) {
      revert AdditionalZkLighter_InvalidCreateOrderParameters();
    }

    if (_marketIndex > MAX_PERPS_MARKET_INDEX) {
      revert AdditionalZkLighter_InvalidMarketType();
    }

    TxTypes.CreateOrder memory _tx = TxTypes.CreateOrder({
      accountIndex: _accountIndex,
      masterAccountIndex: _masterAccountIndex,
      marketIndex: _marketIndex,
      baseAmount: _baseAmount,
      price: _price,
      isAsk: _isAsk,
      orderType: _orderType
    });

    bytes memory pubData = TxTypes.writeCreateOrderPubDataForPriorityQueue(_tx);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1CreateOrder, pubData, pubData);
  }

  /// @notice Burn shares of an account in a public pool
  /// @param _accountIndex Account index
  /// @param _publicPoolIndex Public pool index
  /// @param _shareAmount Amount of shares to burn
  function burnShares(uint48 _accountIndex, uint48 _publicPoolIndex, uint64 _shareAmount) external nonReentrant onlyActive {
    validatePoolExit(_accountIndex, _publicPoolIndex);
    uint48 _masterAccountIndex = getAccountIndexFromAddress(msg.sender);
    if (_masterAccountIndex == NIL_ACCOUNT_INDEX) {
      revert AdditionalZkLighter_AccountIsNotRegistered();
    }
    if (_shareAmount < MIN_POOL_SHARES_TO_MINT_OR_BURN || _shareAmount > MAX_POOL_SHARES_TO_MINT_OR_BURN) {
      revert AdditionalZkLighter_InvalidShareAmount();
    }

    TxTypes.BurnShares memory _tx = TxTypes.BurnShares({
      accountIndex: _accountIndex,
      masterAccountIndex: _masterAccountIndex,
      publicPoolIndex: _publicPoolIndex,
      sharesAmount: _shareAmount
    });
    bytes memory pubData = TxTypes.writeBurnSharesPubDataForPriorityQueue(_tx);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1BurnShares, pubData, pubData);
  }

  /// @notice Register deposit request - pack pubdata, add into onchainOpsCheck and emit OnchainDeposit event
  /// @param _toAddress Receiver Account's L1 address
  /// @param _assetIndex Asset index
  /// @param _routeType Route type
  /// @param _baseAmount Asset amount
  function registerDeposit(address _toAddress, uint16 _assetIndex, TxTypes.RouteType _routeType, uint64 _baseAmount) internal {
    uint48 _toAccountIndex = getAccountIndexFromAddress(_toAddress);
    // No account could be found for the address
    if (_toAccountIndex <= MAX_SYSTEM_ACCOUNT_INDEX) {
      _toAddress = address(0);
    } else if (_toAccountIndex == NIL_ACCOUNT_INDEX) {
      ++lastAccountIndex;
      _toAccountIndex = lastAccountIndex;
      if (_toAccountIndex > MAX_MASTER_ACCOUNT_INDEX) {
        revert AdditionalZkLighter_InvalidAccountIndex();
      }
      addressToAccountIndex[_toAddress] = _toAccountIndex;
    }
    TxTypes.Deposit memory _tx = TxTypes.Deposit({
      accountIndex: _toAccountIndex,
      toAddress: _toAddress,
      assetIndex: _assetIndex,
      routeType: _routeType,
      baseAmount: _baseAmount
    });
    bytes memory pubData = TxTypes.writeDepositPubDataForPriorityQueue(_tx);
    addPriorityRequest(TxTypes.PriorityPubDataTypeL1Deposit, pubData, pubData);
    emit Deposit(_toAccountIndex, _toAddress, _assetIndex, _routeType, _baseAmount);
  }

  /// @notice Saves priority request in storage
  /// @dev Calculates expiration timestamp of the request and stores the request in priorityRequests
  /// @param _pubdataType Priority request public data type
  /// @param _priorityRequest Request public data that is hashed and stored in priorityRequests
  /// @param _pubDataWithMetadata Request public data that is emitted in NewPriorityRequest event including the metadata
  function addPriorityRequest(uint8 _pubdataType, bytes memory _priorityRequest, bytes memory _pubDataWithMetadata) internal {
    // Expiration timestamp is current block number + priority expiration delta
    uint64 expirationTimestamp = SafeCast.toUint64(block.timestamp + PRIORITY_EXPIRATION);
    uint64 nextPriorityRequestId = executedPriorityRequestCount + openPriorityRequestCount;
    bytes32 pubDataPrefix = bytes32(0);
    if (nextPriorityRequestId > 0) {
      pubDataPrefix = priorityRequests[nextPriorityRequestId - 1].prefixHash;
    }
    bytes memory paddedPubData = new bytes(MAX_PRIORITY_REQUEST_PUBDATA_SIZE);
    for (uint256 i = 0; i < _priorityRequest.length; ++i) {
      paddedPubData[i] = _priorityRequest[i];
    }
    priorityRequests[nextPriorityRequestId] = PriorityRequest({
      prefixHash: keccak256(abi.encodePacked(pubDataPrefix, paddedPubData)),
      expirationTimestamp: expirationTimestamp
    });
    emit NewPriorityRequest(msg.sender, nextPriorityRequestId, _pubdataType, _pubDataWithMetadata, expirationTimestamp);
    ++openPriorityRequestCount;
  }

  function increaseBalanceToWithdraw(uint48 _masterAccountIndex, uint16 _assetIndex, uint128 _baseAmount) internal {
    uint128 baseBalance = pendingAssetBalances[_assetIndex][_masterAccountIndex].balanceToWithdraw;
    pendingAssetBalances[_assetIndex][_masterAccountIndex] = PendingBalance(baseBalance + _baseAmount, FILLED_GAS_RESERVE_VALUE);
  }

  function validateAssetIndex(uint16 _assetIndex) internal view {
    if (_assetIndex != NATIVE_ASSET_INDEX && assetConfigs[_assetIndex].tokenAddress == address(0)) {
      revert AdditionalZkLighter_InvalidAssetIndex();
    }
  }

  function validatePoolExit(uint48 _accountIndex, uint48 _poolIndex) internal pure {
    if (
      _accountIndex > MAX_ACCOUNT_INDEX || _accountIndex == _poolIndex || _poolIndex > MAX_ACCOUNT_INDEX || _poolIndex <= MAX_MASTER_ACCOUNT_INDEX
    ) {
      revert AdditionalZkLighter_InvalidAccountIndex();
    }
  }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

/// @title zkLighter Configuration Contract
/// @author zkLighter Team
contract Config {
  /// @dev Max master account id that could be registered in the
  /// network (excluding treasury, which is set as accountIndex = 0)
  /// Sub accounts and pool indexes start from 2**47 to 2**48 - 2 and are set by the sequencer
  uint48 public constant MAX_MASTER_ACCOUNT_INDEX = 2 ** 47 - 1;

  /// @dev Max account id that could be registered in the network
  uint48 public constant MAX_ACCOUNT_INDEX = 2 ** 48 - 2;

  /// @dev Nil account id, that represents an empty account
  uint48 public constant NIL_ACCOUNT_INDEX = 2 ** 48 - 1;

  /// @dev Max API key index that could be registered for an account
  uint8 public constant MAX_API_KEY_INDEX = 254; // 2 ** 8 - 2

  /// @dev Min asset index that could be registered in the exchange
  uint16 public constant MIN_ASSET_INDEX = 1;

  /// @dev Native asset index, that represents the base chain native asset (ETH)
  uint16 public constant NATIVE_ASSET_INDEX = 1;

  /// @dev USDC asset index
  uint16 public constant USDC_ASSET_INDEX = 3;

  /// @dev Max asset index that could be registered in the exchange (to be extended in the future)
  uint16 public constant MAX_ASSET_INDEX = 62; // 2 ** 6 - 2

  /// @dev Max tick size for an asset
  uint128 public constant MAX_TICK_SIZE = 2 ** 128 - 1;

  /// @dev Max deposit cap ticks for an asset
  uint64 public constant MAX_DEPOSIT_CAP_TICKS = 2 ** 60 - 1;

  /// @dev Max perps market index that could be registered to the exchange
  uint16 public constant MAX_PERPS_MARKET_INDEX = 254; // 2 ** 8 - 2

  /// @dev Min spot market index that could be registered to the exchange
  uint16 public constant MIN_SPOT_MARKET_INDEX = 2048; // 2 ** 11

  /// @dev Max spot market index that could be registered to the exchange
  uint16 public constant MAX_SPOT_MARKET_INDEX = 4094; // 2 ** 12 - 2

  /// @dev Max price an order can have
  uint32 public constant MAX_ORDER_PRICE = 2 ** 32 - 1;

  /// @dev Min price an order can have
  uint32 public constant MIN_ORDER_PRICE = 1;

  /// @dev Nil order base amount
  uint48 public constant NIL_ORDER_BASE_AMOUNT = 0;

  /// @dev Max order base amount
  uint48 public constant MAX_ORDER_BASE_AMOUNT = 2 ** 48 - 1;

  /// @dev Max order quote amount
  uint48 public constant MAX_ORDER_QUOTE_AMOUNT = 2 ** 48 - 1;

  /// @dev Min order base amount
  uint48 public constant MIN_ORDER_BASE_AMOUNT = 1;

  /// @dev Max amount of pool shares that can be minted or burned
  uint64 public constant MAX_POOL_SHARES_TO_MINT_OR_BURN = 2 ** 60 - 1;

  /// @dev Min amount of pool shares that can be minted or burned
  uint64 public constant MIN_POOL_SHARES_TO_MINT_OR_BURN = 1;

  /// @dev Max amount of staking shares that can be minted or burned
  uint64 public constant MAX_STAKING_SHARES_TO_MINT_OR_BURN = 2 ** 60 - 1;

  /// @dev Min amount of staking shares that can be minted or burned
  uint64 public constant MIN_STAKING_SHARES_TO_MINT_OR_BURN = 1;

  /// @dev Expiration timestamp delta for priority request
  /// @dev Priority expiration timestamp should be greater than the operation execution timestamp
  uint256 public constant PRIORITY_EXPIRATION = 14 days;

  /// @dev Margin tick to transform margin values in form x * 0.01%
  uint16 constant MARGIN_TICK = 10_000;

  /// @dev Funding rate tick to transform funding values in form x * 0.0001%
  uint32 constant FUNDING_RATE_TICK = 1_000_000;

  /// @dev Fee tick to transform fee values in form x * 0.0001%
  uint32 constant FEE_TICK = 1_000_000;

  /// @dev Max value for quote multiplier
  uint32 constant MAX_QUOTE_MULTIPLIER = 10_000;

  /// @dev Max value for asset extension multiplier
  uint56 constant MAX_ASSET_EXTENSION_MULTIPLIER = 2 ** 56 - 1;

  /// @dev Max value for asset extended deposit cap ticks
  uint128 constant MAX_EXTENDED_DEPOSIT_CAP_TICKS = 2 ** 80 - 1;

  /// @dev Size of the public key for a Lighter API key
  uint8 constant PUB_KEY_BYTES_SIZE = 40;

  /// @dev Address of the blob point evaluation precompile (EIP-4844)
  address constant POINT_EVALUATION_PRECOMPILE_ADDRESS = address(0x0A);

  /// @dev Max priority request pubdata size stat is written to the priority request queue
  uint256 constant MAX_PRIORITY_REQUEST_PUBDATA_SIZE = 100;

  /// @dev BLS Modulus value defined in EIP-4844,
  /// returned by the precompile if successfully evaluated
  uint256 constant BLS_MODULUS = 52435875175126190479447740508185965837690552500527637822603658699938581184513;

  /// @dev Scalar field of bn254
  uint256 constant BN254_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;

  /// @dev Evaluation point x (32 bytes) || evaluation point y (32 bytes) ||
  /// commitment (48 bytes) || proof (48 bytes)) = 160 bytes
  uint256 constant BLOB_DATA_COMMITMENT_BYTE_SIZE = 160;

  /// @dev Goldilocks prime field modulus, 2^64 - 2^32 + 1
  uint64 constant GOLDILOCKS_MODULUS = 0xffffffff00000001;

  /// @dev Max open interest per market
  uint64 constant MAX_MARKET_OPEN_INTEREST = (2 ** 56) - 1;

  /// @dev Max batch deposit length
  uint64 public constant MAX_BATCH_DEPOSIT_LENGTH = 1000;

  /// @dev Max # of blobs a batch can have
  uint256 constant MAX_BLOB_COUNT = 6;

  /// @dev Treasury account index (system account)
  uint48 constant TREASURY_ACCOUNT_INDEX = 0;

  /// @dev Insurance fund operator account index (system account)
  uint48 constant INSURANCE_FUND_OPERATOR_ACCOUNT_INDEX = 1; // Account index for the insurance fund operator account

  /// @dev Max system account index, 2 is left empty for future use
  uint48 constant MAX_SYSTEM_ACCOUNT_INDEX = 2;

  function _hasCode(address account) internal view returns (bool) {
    return account.code.length > 0;
  }

  uint48 constant MAX_CONFIG_PERIOD = 1000 * 60 * 60 * 24 * 14; // 14 days in milliseconds
}

File 12 of 22 : ExtendableStorage.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import "./interfaces/IZkLighterStateRootUpgradeVerifier.sol";

/// @title zkLighter Extendable Storage Contract
/// @author zkLighter Team
contract ExtendableStorage {
  uint256[420] private __gap;

  /// @dev Stores new state root at the batch number if state root upgrade happened
  mapping(uint64 => bytes32) public stateRootUpdates;

  /// @dev Verifier contract, used for verifying state root upgrade proofs
  IZkLighterStateRootUpgradeVerifier internal stateRootUpgradeVerifier;

  /// @dev Stores if the desert mode was performed for the account index
  /// @dev Deprecated: use accountPerformedDesertForAsset instead
  mapping(uint48 => bool) internal DEPRECATED_accountPerformedDesert;

  struct PendingBalance {
    uint128 balanceToWithdraw;
    uint8 gasReserveValue;
  }

  struct AssetConfig {
    address tokenAddress; // Base layer token address
    uint8 withdrawalsEnabled; // 0 if disabled, 1 if enabled
    uint56 extensionMultiplier; // Internal asset extension multiplier
    uint128 tickSize; // Balance change unit before applying the extension multiplier
    uint64 depositCapTicks; // Max deposit cap in ticks
    uint64 minDepositTicks; // Min deposit in ticks
  }

  mapping(uint16 => AssetConfig) public assetConfigs; // id -> config
  mapping(address => uint16) public tokenToAssetIndex; // token -> id
  mapping(uint16 => mapping(uint48 => PendingBalance)) internal pendingAssetBalances;
  mapping(uint16 => mapping(uint48 => bool)) internal accountPerformedDesertForAsset;
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

/// @title zkLighter DesertVerifier Interface
/// @author zkLighter Team
interface IDesertVerifier {
  /// @notice Verifies a SNARK proof for the desert mode
  /// @param proof The SNARK proof to verify
  /// @param public_inputs The public inputs for the proof
  /// @return success True if the proof is valid, false otherwise
  function Verify(bytes calldata proof, uint256[] calldata public_inputs) external view returns (bool success);
}

File 14 of 22 : IEvents.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import "../lib/TxTypes.sol";

/// @title zkLighter Events Interface
/// @author zkLighter Team
interface IEvents {
  /// @notice Event emitted when a batch is committed
  event BatchCommit(uint64 batchNumber, uint32 batchSize, uint64 endBlockNumber);

  /// @notice Event emitted when a batch is verified
  event BatchVerification(uint64 batchNumber, uint32 batchSize, uint64 endBlockNumber);

  /// @notice Event emitted when batches until given batch number are executed
  event BatchesExecuted(uint64 batchNumber, uint64 endBlockNumber);

  /// @notice Event emitted when batches are reverted
  event BatchesRevert(uint64 newTotalBlocksCommitted);

  /// @notice Event emitted when user funds are deposited to a zkLighter account
  event Deposit(uint48 toAccountIndex, address toAddress, uint16 assetIndex, TxTypes.RouteType routeType, uint128 baseAmount);

  /// @notice Market created event
  event CreateMarket(TxTypes.CreateMarket params, uint8 sizeDecimals, uint8 priceDecimals, bytes32 symbol);

  /// @notice Asset config registered event
  event RegisterAssetConfig(
    uint16 assetIndex,
    address tokenAddress,
    uint8 withdrawalsEnabled,
    uint56 extensionMultiplier,
    uint128 tickSize,
    uint64 depositCapTicks,
    uint64 minDepositTicks
  );

  /// @notice Asset config updated event
  event UpdateAssetConfig(uint16 assetIndex, uint8 withdrawalsEnabled, uint64 depositCapTicks, uint64 minDepositTicks);

  /// @notice Asset registered event
  event RegisterAsset(TxTypes.RegisterAsset params, uint8 l1Decimals, uint8 decimals, bytes32 symbol);

  /// @notice Asset updated event
  event UpdateAsset(TxTypes.UpdateAsset params);

  /// @notice Market updated event
  event UpdateMarket(TxTypes.UpdateMarket params);

  /// @notice Event emitted when user funds are withdrawn from contract
  event WithdrawPending(address indexed owner, uint16 assetIndex, uint128 baseAmount);

  /// @notice New priority request event. Emitted when a request is placed into mapping
  event NewPriorityRequest(address sender, uint64 serialId, uint8 pubdataType, bytes pubData, uint64 expirationTimestamp);

  /// @notice Desert mode entered event
  event DesertMode();

  /// @notice The treasury address changed
  event TreasuryUpdate(address newTreasury);

  /// @notice The insurance fund operator address changed
  event InsuranceFundOperatorUpdate(address newInsuranceFundOperator);

  /// @notice The state root upgrade event
  event StateRootUpdate(uint64 batchNumber, bytes32 oldStateRoot, bytes32 newStateRoot);
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

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

/// @title zkLighter Events Interface
/// @author zkLighter Team
interface IGovernance {
  /// @notice Governor changed
  event NewGovernor(address newGovernor);

  /// @notice Validator status changed
  event ValidatorStatusUpdate(address validatorAddress, bool isActive);

  /// @notice Thrown in constructor when USDC is not a contract or zero address
  error ZkLighter_Governance_InvalidUSDCAddress();

  /// @notice Thrown in constructor when Governor Address is zero
  error ZkLighter_Governance_GovernorCannotBeZero();

  ///@notice Thrown by requireGovernor function and when the address is not a governor
  error ZkLighter_Governance_OnlyGovernor();

  /// @notice Thrown when the validator address is zero
  error ZkLighter_Governance_ValidatorCannotBeZero();

  /// @notice Thrown when the validator address is invalid
  error ZkLighter_Governance_InvalidValidator();

  /// @notice Change current governor
  /// @param _newGovernor Address of the new governor
  function changeGovernor(address _newGovernor) external;

  /// @return The address of the USDC address
  function usdc() external view returns (IERC20);

  /// @notice Check if specified address is governor
  /// @param _address Address to check
  function requireGovernor(address _address) external view;

  /// @notice Set validator address
  /// @param _validator Address of the validator
  /// @param _active Validator status
  function setValidator(address _validator, bool _active) external;

  /// @notice Check if specified address is validator
  /// @param _address Address to check
  function isActiveValidator(address _address) external view;
}

File 16 of 22 : IZkLighter.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "../Storage.sol";
import "./IEvents.sol";
import "../lib/TxTypes.sol";

/// @title zkLighter Interface
/// @author zkLighter Team
interface IZkLighter is IEvents {
  enum PubDataMode {
    Blob,
    Calldata
  }

  struct CommitBatchInfo {
    uint64 endBlockNumber;
    uint32 batchSize;
    uint64 startTimestamp;
    uint64 endTimestamp;
    uint32 priorityRequestCount;
    bytes32 prefixPriorityRequestHash;
    bytes32 onChainOperationsHash;
    bytes32 newStateRoot;
    bytes32 newValidiumRoot;
    bytes pubdataCommitments;
  }

  /// @notice Thrown when given commit batch data is inconsistent with the last stored batch
  error ZkLighter_InvalidPubDataMode();

  /// @notice Thrown when given commit batch data is inconsistent with the last stored batch
  error ZkLighter_NonIncreasingBlockNumber();

  /// @notice Thrown when given commit batch size is wrong
  error ZkLighter_InvalidBatchSize();

  /// @notice Thrown when given commit batch data is inconsistent with the last stored batch
  error ZkLighter_NonIncreasingTimestamp();

  /// @notice Thrown when given StoredBatchInfo hash doesn't match what is stored
  error ZkLighter_StoredBatchInfoMismatch();

  /// @notice Thrown when given priority request count is inconsistent with queued priority requests
  error ZkLighter_CommitBatchPriorityRequestCountMismatch();

  /// @notice Thrown when given priority request prefix hash doesn't match
  error ZkLighter_PriorityRequestPrefixHashMismatch();

  /// @notice Thrown when execute batches is called with different lengths of data
  error ZkLighter_ExecuteInputLengthMismatch();

  /// @notice Thrown when execute batches is called with input length greater than pending count
  error ZkLighter_ExecuteInputLengthGreaterThanPendingCount();

  /// @notice Thrown when revert batches is called on an already executed batch
  error ZkLighter_CannotRevertExecutedBatch();

  /// @notice Thrown when revert batches is called on the genesis batch
  error ZkLighter_CannotRevertGenesisBatch();

  /// @notice Thrown when given withdraw pubdata for a batch has invalid length
  error ZkLighter_InvalidPubDataLength();

  /// @notice Thrown when given withdraw pubdata for a batch has invalid data type
  error ZkLighter_InvalidPubDataType();

  /// @notice Thrown when given withdraw pubdata for a batch is invalid
  error ZkLighter_OnChainOperationsHashMismatch();

  /// @notice Thrown when implementation contract calls the initialise function on self
  error ZkLighter_CannotBeInitialisedByImpl();

  /// @notice Thrown when the initialisation parameters are invalid
  error ZkLighter_InvalidInitializeParameters();

  /// @notice Thrown when the upgrade parameters are invalid
  error ZkLighter_InvalidUpgradeParameters();

  /// @notice Thrown when requested amount is greater than the pending balance
  error ZkLighter_InvalidWithdrawAmount();

  /// @notice Thrown when the transferred amount is not a multiple of the tick size
  error ZkLighter_TransferredAmountNotMultipleOfTickSize();

  /// @notice Thrown when upgrade address(this) is the implementation
  error ZkLighter_OnlyProxyCanCallUpgrade();

  /// @notice Thrown when a restricted function which can be called only from zkLighterProxy is called by other address
  error ZkLighter_OnlyZkLighter();

  /// @notice thrown when ETH transfer fails
  error ZkLighter_ETHTransferFailed();

  /// @notice Thrown when rollup balance difference (before and after transfer) is bigger than `_maxAmount`
  error ZkLighter_RollUpBalanceBiggerThanMaxAmount();

  /// @notice Thrown when verifyBatch is called on a batch which is not yet committed
  error ZkLighter_CannotVerifyNonCommittedBatch();

  /// @notice Thrown when verifyBatch is called for invalid batch
  error ZkLighter_VerifyBatchNotInOrder();

  /// @notice Thrown when verifyBatch is called with invalid proof
  error ZkLighter_VerifyBatchProofFailed();

  /// @notice Thrown when given batch is not yet verified
  error ZkLighter_CannotExecuteNonVerifiedBatch();

  /// @notice Thrown when given batch either doesn't contain on chain operations or the order is wrong
  error ZkLighter_BatchNotInOnChainQueue();

  /// @notice ZkLighterImplementation cannot delegate to AdditionalZkLigher
  error ZkLighter_ImplCantDelegateToAddl();

  /// @notice Thrown when the new treasury address is zero
  error ZkLighter_TreasuryCannotBeZero();

  /// @notice Thrown when the new treasury address is already in use
  error ZkLighter_TreasuryCannotBeInUse();

  /// @notice Thrown when the new insurance fund operator address is zero
  error ZkLighter_InsuranceFundOperatorCannotBeZero();

  /// @notice Thrown when the new insurance fund operator address is already in use
  error ZkLighter_InsuranceFundOperatorCannotBeInUse();

  /// @notice Thrown when the point evaluation parameters are invalid
  error ZkLighter_InvalidPointEvaluationParams();

  /// @notice Thrown when the blob commitment parameters are invalid
  error ZkLighter_InvalidBlobCommitmentParams();

  error ZkLighter_InvalidAssetIndex();

  error ZkLighter_InvalidAssetConfigParams();

  error ZkLighter_DesertModeInactive();

  error ZkLighter_DesertVerifyProofFailed();

  error ZkLighter_AccountAlreadyPerformedDesertForAsset();

  error ZkLighter_NoOutstandingDepositsForCancelation();

  error ZkLighter_InvalidParamsForCancelOutstandingDeposits();

  error ZkLighter_DepositPubdataHashMismatch();

  /// @notice Thrown when the number of blobs in a batch exceeds the maximum allowed
  error ZkLighter_InvalidBlobCount(uint256);

  /// @notice Checks if Desert mode must be entered. If true - enters desert mode and emits DesertMode event
  /// @dev Desert mode must be entered in case of current L1 block timestamp is higher than the oldest priority request expiration timestamp
  /// @return bool Flag that is true if the desert mode must be entered
  function activateDesertMode() external returns (bool);

  /// @notice Performs the Desert Exit, can be called only when desertMode is active
  /// @param _accountIndex Account index of the user who is performing the desert exit
  /// @param _masterAccountIndex Master account index of the user who is performing the desert exit
  /// @param _assetIndex Asset index of the asset to be exited
  /// @param _totalBaseAmount Total base balance of the user for the asset to be exited
  /// @param proof Proof for the user assets
  function performDesert(
    uint48 _accountIndex,
    uint48 _masterAccountIndex,
    uint16 _assetIndex,
    uint128 _totalBaseAmount,
    bytes calldata proof
  ) external;

  /// @notice Cancels outstanding deposits, can be called only when desertMode is active
  /// @param _n Number of outstanding priority requests to be cancelled
  /// @param _priorityPubData Array of outstanding priority requests to be cancelled
  function cancelOutstandingDepositsForDesertMode(uint64 _n, bytes[] memory _priorityPubData) external;

  /// @notice Deposit to Lighter
  /// @param _assetIndex Asset index
  /// @param _routeType Route type
  /// @param _amount Token amount
  /// @param _to The receiver L1 address
  function deposit(address _to, uint16 _assetIndex, TxTypes.RouteType _routeType, uint256 _amount) external payable;

  /// @notice Deposit USDC to Lighter for multiple users
  /// @param _amount Array of USDC Token amounts
  /// @param _to Array of receiver L1 addresses
  /// @param _accountIndex Array of account index values, will be used in the future
  function depositBatch(uint64[] calldata _amount, address[] calldata _to, uint48[] calldata _accountIndex) external;

  /// @notice Change public key of a Lighter account
  /// @param _accountIndex Account index
  /// @param _apiKeyIndex API key index
  /// @param _pubKey New public key (40 bytes)
  function changePubKey(uint48 _accountIndex, uint8 _apiKeyIndex, bytes calldata _pubKey) external;

  /// @notice Register a new asset config
  /// @param assetIndex Asset index
  /// @param tokenAddress Token address
  /// @param withdrawalsEnabled Withdrawals enabled flag
  /// @param extensionMultiplier Extension multiplier of the asset
  /// @param tickSize Tick size of the asset
  /// @param depositCapTicks Deposit cap in ticks
  /// @param minDepositTicks Minimum deposit in ticks
  /// @dev This function is only callable by the governor
  function registerAssetConfig(
    uint16 assetIndex,
    address tokenAddress,
    uint8 withdrawalsEnabled,
    uint56 extensionMultiplier,
    uint128 tickSize,
    uint64 depositCapTicks,
    uint64 minDepositTicks
  ) external;

  /// @notice Update existing asset config
  /// @param assetIndex Asset index
  /// @param withdrawalsEnabled Withdrawals enabled flag
  /// @param depositCapTicks Deposit cap in ticks
  /// @param minDepositTicks Minimum deposit in ticks
  /// @dev This function is only callable by the governor
  function updateAssetConfig(uint16 assetIndex, uint8 withdrawalsEnabled, uint64 depositCapTicks, uint64 minDepositTicks) external;

  /// @notice Set system config
  /// @param _params Config parameters
  function setSystemConfig(TxTypes.SetSystemConfig calldata _params) external;

  /// @notice Register a new asset to Lighter
  /// @param _decimals Number of decimals of the asset
  /// @param _symbol Symbol of the asset
  /// @param _params Asset parameters
  function registerAsset(uint8 _l1Decimals, uint8 _decimals, bytes32 _symbol, TxTypes.RegisterAsset calldata _params) external;

  /// @notice Update existing asset in Lighter
  /// @param _params Asset parameters to update
  function updateAsset(TxTypes.UpdateAsset calldata _params) external;

  /// @notice Create new market and an order book
  /// @param _size_decimals [metadata] Number of decimals to represent size of an order in the order book
  /// @param _price_decimals [metadata] Number of decimals to represent price of an order in the order book
  /// @param _symbol [metadata] symbol of the market
  /// @param _params Order book parameters
  function createMarket(uint8 _size_decimals, uint8 _price_decimals, bytes32 _symbol, TxTypes.CreateMarket calldata _params) external;

  /// @notice Updates the given order book, all values should be provided
  /// @param _params Order book parameters to update
  function updateMarket(TxTypes.UpdateMarket calldata _params) external;

  /// @notice Cancel all orders of a Lighter account
  /// @param _accountIndex Account index
  function cancelAllOrders(uint48 _accountIndex) external;

  /// @notice Withdraw from Lighter
  /// @param _accountIndex Account index
  /// @param _assetIndex Asset index
  /// @param _routeType Route type
  /// @param _baseAmount Amount to withdraw
  function withdraw(uint48 _accountIndex, uint16 _assetIndex, TxTypes.RouteType _routeType, uint64 _baseAmount) external;

  /// @notice Create an order for a Lighter account
  /// @param _accountIndex Account index
  /// @param _marketIndex Market index
  /// @param _baseAmount Amount of base token
  /// @param _price Price of the order
  /// @param _isAsk Flag to indicate if the order is ask or bid
  /// @param _orderType Order type
  function createOrder(uint48 _accountIndex, uint16 _marketIndex, uint48 _baseAmount, uint32 _price, uint8 _isAsk, uint8 _orderType) external;

  /// @notice Burn shares of an account in a public pool
  /// @param _accountIndex Account index
  /// @param _publicPoolIndex Public pool index
  /// @param _shareAmount Amount of shares to burn
  function burnShares(uint48 _accountIndex, uint48 _publicPoolIndex, uint64 _shareAmount) external;

  /// @notice Withdraws tokens from ZkLighter contract to the owner
  /// @param _owner Account address
  /// @param _assetIndex Asset index
  /// @param _baseAmount Base amount to withdraw
  function withdrawPendingBalance(address _owner, uint16 _assetIndex, uint128 _baseAmount) external;

  /// @notice Withdraws USDC tokens from ZkLighter contract to the owner (legacy)
  /// @param _owner Account address
  /// @param _baseAmount Base USDC amount to withdraw
  function withdrawPendingBalanceLegacy(address _owner, uint128 _baseAmount) external;

  /// @notice Sends tokens
  /// @param _token Token address
  /// @param _to Address of recipient
  /// @param _amount Amount of tokens to transfer
  /// @param _maxAmount Maximum possible amount of tokens to transfer to this account
  /// @return uint256 Amount of tokens transferred
  function transferERC20(IERC20 _token, address _to, uint256 _amount, uint256 _maxAmount) external returns (uint256);

  /// @notice Sends ETH
  /// @param _to Address of recipient
  /// @param _amount Amount of ETH to transfer
  /// @return uint256 Amount of ETH transferred
  function transferETH(address _to, uint256 _amount) external returns (uint256);

  /// @notice Reverts unverified batches
  /// @param _batchesToRevert Array of batches to be reverted
  /// @param _remainingBatch Last batch that is not reverted
  function revertBatches(Storage.StoredBatchInfo[] memory _batchesToRevert, Storage.StoredBatchInfo memory _remainingBatch) external;

  /// @notice Get pending balance that the user can withdraw
  /// @param _owner Owner account address
  /// @param _assetIndex Asset index
  /// @return uint128 Pending balance
  function getPendingBalance(address _owner, uint16 _assetIndex) external view returns (uint128);

  /// @notice Get pending balance that the user can withdraw in USDC (legacy)
  /// @param _owner Owner account address
  /// @return uint128 Pending USDC balance
  function getPendingBalanceLegacy(address _owner) external view returns (uint128);

  /// @notice Commit a new batch with at least one blob.
  /// @param newBatchData  New batch to be committed
  /// @param lastStoredBatch Last committed batch
  function commitBatch(CommitBatchInfo memory newBatchData, Storage.StoredBatchInfo memory lastStoredBatch) external;

  /// @notice Execute on chain operations in a verified batch
  /// @param batches Array of batches that contains the on chain operations to be executed
  /// @param onChainOperationsPubData Array of on chain operations that are verified and to be executed
  function executeBatches(Storage.StoredBatchInfo[] memory batches, bytes[] memory onChainOperationsPubData) external;

  /// @notice Verify a committed batch alongside its validity proof
  /// @param batch Batch to be verified
  /// @param proof Proof for the batch
  function verifyBatch(Storage.StoredBatchInfo memory batch, bytes memory proof) external;

  /// @notice Change the state root
  /// @param _lastStoredBatch Last committed batch
  /// @param _stateRoot New state root
  /// @param _validiumRoot New validium root
  /// @param proof Proof for the state root change
  function updateStateRoot(
    Storage.StoredBatchInfo calldata _lastStoredBatch,
    bytes32 _stateRoot,
    bytes32 _validiumRoot,
    bytes calldata proof
  ) external;

  /// @notice Change the treasury address
  /// @notice Can be called only by ZkLighter governor
  /// @param _newTreasury Address of the new treasury
  function setTreasury(address _newTreasury) external;

  /// @notice Change the insurance fund operator address
  /// @notice Can be called only by ZkLighter governor
  /// @param _newInsuranceFundOperator Address of the new insurance fund operator
  function setInsuranceFundOperator(address _newInsuranceFundOperator) external;
}

File 17 of 22 : IZkLighterDesertMode.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

/// @title zkLighter DesertMode Interface
/// @author zkLighter Team
interface IZkLighterDesertMode {
  /// @notice Thrown when DesertMode is active
  error ZkLighter_DesertModeActive();

  /// @return True if desert mode is active, false otherwise
  function desertMode() external view returns (bool);
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

/// @title zkLighter State Root Upgrade Verifier Interface
/// @author zkLighter Team
interface IZkLighterStateRootUpgradeVerifier {
  function Verify(bytes calldata proof, uint256[] calldata public_inputs) external view returns (bool success);
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

/// @title zkLighter Verifier Interface
/// @author zkLighter Team
interface IZkLighterVerifier {
  /// @notice Verifies a SNARK proof of a batch
  /// @param proof The SNARK proof to verify
  /// @param public_inputs The public inputs for the proof
  /// @return success True if the proof is valid, false otherwise
  function Verify(bytes calldata proof, uint256[] calldata public_inputs) external view returns (bool success);
}

// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.8.25;

/// @title zkLighter Bytes Library
/// @notice Implements helper functions to read bytes and convert them to other types
/// @author zkLighter Team
library Bytes {
  /// @dev Theoretically possible overflow of (_start + 0x8)
  function bytesToUInt64(bytes memory _bytes, uint256 _start) internal pure returns (uint64 r) {
    uint256 offset = _start + 0x8;
    require(_bytes.length >= offset, "S");
    assembly {
      r := mload(add(_bytes, offset))
    }
  }

  // NOTE: theoretically possible overflow of (_start + 0x7)
  function bytesToUInt56(bytes memory _bytes, uint256 _start) internal pure returns (uint56 r) {
    uint256 offset = _start + 0x7;
    require(_bytes.length >= offset, "S");
    assembly {
      r := mload(add(_bytes, offset))
    }
  }

  /// @dev Theoretically possible overflow of (_start + 0x6)
  function bytesToUInt48(bytes memory _bytes, uint256 _start) internal pure returns (uint48 r) {
    uint256 offset = _start + 0x6;
    require(_bytes.length >= offset, "S");
    assembly {
      r := mload(add(_bytes, offset))
    }
  }

  // NOTE: theoretically possible overflow of (_start + 0x4)
  function bytesToUInt32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 r) {
    uint256 offset = _start + 0x4;
    require(_bytes.length >= offset, "S");
    assembly {
      r := mload(add(_bytes, offset))
    }
  }

  // NOTE: theoretically possible overflow of (_start + 0x3)
  function bytesToUInt24(bytes memory _bytes, uint256 _start) internal pure returns (uint24 r) {
    uint256 offset = _start + 0x3;
    require(_bytes.length >= offset, "S");
    assembly {
      r := mload(add(_bytes, offset))
    }
  }

  // NOTE: theoretically possible overflow of (_start + 0x2)
  function bytesToUInt16(bytes memory _bytes, uint256 _start) internal pure returns (uint16 r) {
    uint256 offset = _start + 0x2;
    require(_bytes.length >= offset, "S");
    assembly {
      r := mload(add(_bytes, offset))
    }
  }

  /// @dev Theoretically possible overflow of (_offset + 0x8)
  function readUInt64(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint64 r) {
    newOffset = _offset + 8;
    r = bytesToUInt64(_data, _offset);
  }

  // NOTE: theoretically possible overflow of (_offset + 7)
  function readUInt56(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint56 r) {
    newOffset = _offset + 7;
    r = bytesToUInt56(_data, _offset);
  }

  /// @dev Theoretically possible overflow of (_offset + 0x6)
  function readUInt48(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint48 r) {
    newOffset = _offset + 6;
    r = bytesToUInt48(_data, _offset);
  }

  // NOTE: theoretically possible overflow of (_offset + 4)
  function readUInt32(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint32 r) {
    newOffset = _offset + 4;
    r = bytesToUInt32(_data, _offset);
  }

  // NOTE: theoretically possible overflow of (_offset + 3)
  function readUInt24(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint24 r) {
    newOffset = _offset + 3;
    r = bytesToUInt24(_data, _offset);
  }

  // NOTE: theoretically possible overflow of (_offset + 2)
  function readUInt16(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint16 r) {
    newOffset = _offset + 2;
    r = bytesToUInt16(_data, _offset);
  }

  /// @dev Theoretically possible overflow of (_offset + 0x1)
  function readUInt8(bytes memory _data, uint256 _offset) internal pure returns (uint256 newOffset, uint8 r) {
    newOffset = _offset + 1;
    r = uint8(_data[_offset]);
  }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.25;

import "./Bytes.sol";

/// @title zkLighter TxTypes Library
/// @notice Implements helper functions to serialize and deserialize tx types
/// @author zkLighter Team
library TxTypes {
  /// @notice Market types
  enum MarketType {
    Perps, // 0
    Spot // 1
  }

  /// @notice Asset margin modes
  enum AssetMarginMode {
    Disabled, // 0
    Enabled // 1
  }

  /// @notice Asset destination / source type
  enum RouteType {
    Perps, // 0
    Spot // 1
  }

  /// @notice zklighter priority request types
  uint8 constant PriorityPubDataTypeEmpty = 40;
  uint8 constant PriorityPubDataTypeL1Deposit = 41;
  uint8 constant PriorityPubDataTypeL1ChangePubKey = 42;
  uint8 constant PriorityPubDataTypeL1CreateMarket = 43;
  uint8 constant PriorityPubDataTypeL1UpdateMarket = 44;
  uint8 constant PriorityPubDataTypeL1CancelAllOrders = 45;
  uint8 constant PriorityPubDataTypeL1Withdraw = 46;
  uint8 constant PriorityPubDataTypeL1CreateOrder = 47;
  uint8 constant PriorityPubDataTypeL1BurnShares = 48;
  uint8 constant PriorityPubDataTypeL1RegisterAsset = 49;
  uint8 constant PriorityPubDataTypeL1UpdateAsset = 50;
  uint8 constant PriorityPubDataTypeL1UnstakeAssets = 51;
  uint8 constant PriorityPubDataTypeL1SetSystemConfig = 52;

  /// @notice zklighter onchain transaction types
  enum OnChainPubDataType {
    Empty,
    USDCWithdraw,
    Withdraw
  }

  uint32 internal constant USDCWithdrawLogSize = 15; // 1 byte for type, 6 bytes for accountIndex, 8 bytes for usdcAmount
  uint32 internal constant WithdrawLogSize = 17; // 1 byte for type, 2 bytes for assetIndex, 6 bytes for accountIndex, 8 bytes for amount

  enum OrderType {
    LimitOrder,
    MarketOrder
  }

  uint256 internal constant DEPOSIT_PUB_DATA_SIZE = 38;

  struct Deposit {
    uint48 accountIndex;
    address toAddress;
    uint16 assetIndex;
    RouteType routeType;
    uint64 baseAmount;
  }

  /// @notice Serialize deposit pubData
  function writeDepositPubDataForPriorityQueue(Deposit memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(uint8(PriorityPubDataTypeL1Deposit), _tx.accountIndex, _tx.toAddress, _tx.assetIndex, _tx.routeType, _tx.baseAmount);
  }

  /// @notice Deserialize deposit pubData
  function readDepositForDesertMode(bytes memory _data) internal pure returns (uint48 accountIndex, uint16 assetIndex, uint64 baseAmount) {
    uint256 _offset = 1; // Skipping the tx type
    (_offset, accountIndex) = Bytes.readUInt48(_data, _offset);
    _offset += 20; // Skipping the address
    (_offset, assetIndex) = Bytes.readUInt16(_data, _offset);
    _offset++; // Skipping the route type
    (_offset, baseAmount) = Bytes.readUInt64(_data, _offset);
    return (accountIndex, assetIndex, baseAmount);
  }

  struct L1Withdraw {
    uint48 accountIndex;
    uint48 masterAccountIndex;
    uint16 assetIndex;
    RouteType routeType;
    uint64 baseAmount;
  }

  /// @notice Serialize withdraw pubData
  function writeWithdrawPubDataForPriorityQueue(L1Withdraw memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(
      uint8(PriorityPubDataTypeL1Withdraw),
      _tx.accountIndex,
      _tx.masterAccountIndex,
      _tx.assetIndex,
      _tx.routeType,
      _tx.baseAmount
    );
  }

  struct Withdraw {
    uint48 masterAccountIndex;
    uint16 assetIndex;
    uint64 baseAmount;
  }

  /// @notice Deserialize withdraw pubData
  function readWithdrawOnChainLog(bytes memory _data, uint256 _offset) internal pure returns (Withdraw memory parsed, uint256 newOffset) {
    _offset++; // Skipping the type
    (_offset, parsed.masterAccountIndex) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.assetIndex) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.baseAmount) = Bytes.readUInt64(_data, _offset);
    return (parsed, _offset);
  }

  struct USDCWithdraw {
    uint48 masterAccountIndex;
    uint64 usdcAmount;
  }

  /// @notice Deserialize USDC withdraw pubData
  function readUSDCWithdrawOnChainLog(bytes memory _data, uint256 _offset) internal pure returns (USDCWithdraw memory parsed, uint256 newOffset) {
    _offset++; // Skipping the type
    (_offset, parsed.masterAccountIndex) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.usdcAmount) = Bytes.readUInt64(_data, _offset);
    return (parsed, _offset);
  }

  uint8 internal constant PACKED_COMMON_PERPS_DATA_BYTES = 55;

  struct CommonPerpsData {
    uint32 takerFee;
    uint32 makerFee;
    uint32 liquidationFee;
    uint48 minBaseAmount;
    uint48 minQuoteAmount;
    uint16 defaultInitialMarginFraction;
    uint16 minInitialMarginFraction;
    uint16 maintenanceMarginFraction;
    uint16 closeOutMarginFraction;
    uint32 interestRate;
    uint24 fundingClampSmall;
    uint24 fundingClampBig;
    uint56 openInterestLimit;
    uint48 orderQuoteLimit;
  }

  uint8 internal constant PACKED_CREATE_MARKET_PERPS_BYTES = 4 + PACKED_COMMON_PERPS_DATA_BYTES;

  struct CreateMarketPerpsData {
    uint32 quoteMultiplier;
    CommonPerpsData common;
  }

  function readCreateMarketPerpsData(bytes memory _data) internal pure returns (CreateMarketPerpsData memory parsed) {
    if (_data.length != PACKED_CREATE_MARKET_PERPS_BYTES) {
      revert("Invalid packed create market perps data length");
    }
    uint256 _offset;
    (_offset, parsed.quoteMultiplier) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.takerFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.makerFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.liquidationFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.minBaseAmount) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.common.minQuoteAmount) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.common.defaultInitialMarginFraction) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.common.minInitialMarginFraction) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.common.maintenanceMarginFraction) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.common.closeOutMarginFraction) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.common.interestRate) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.fundingClampSmall) = Bytes.readUInt24(_data, _offset);
    (_offset, parsed.common.fundingClampBig) = Bytes.readUInt24(_data, _offset);
    (_offset, parsed.common.openInterestLimit) = Bytes.readUInt56(_data, _offset);
    (_offset, parsed.common.orderQuoteLimit) = Bytes.readUInt48(_data, _offset);
    return parsed;
  }

  uint8 internal constant PACKED_COMMON_SPOT_DATA_BYTES = 26;

  struct CommonSpotData {
    uint32 takerFee;
    uint32 makerFee;
    uint48 minBaseAmount;
    uint48 minQuoteAmount;
    uint48 orderQuoteLimit;
  }

  uint8 internal constant PACKED_CREATE_MARKET_SPOT_BYTES = 18 + PACKED_COMMON_SPOT_DATA_BYTES;

  struct CreateMarketSpotData {
    uint16 baseAssetIndex;
    uint16 quoteAssetIndex;
    uint56 sizeExtensionMultiplier;
    uint56 quoteExtensionMultiplier;
    CommonSpotData common;
  }

  function readCreateMarketSpotData(bytes memory _data) internal pure returns (CreateMarketSpotData memory parsed) {
    if (_data.length != PACKED_CREATE_MARKET_SPOT_BYTES) {
      revert("Invalid packed create market spot data length");
    }
    uint256 _offset;
    (_offset, parsed.baseAssetIndex) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.quoteAssetIndex) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.sizeExtensionMultiplier) = Bytes.readUInt56(_data, _offset);
    (_offset, parsed.quoteExtensionMultiplier) = Bytes.readUInt56(_data, _offset);
    (_offset, parsed.common.takerFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.makerFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.minBaseAmount) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.common.minQuoteAmount) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.common.orderQuoteLimit) = Bytes.readUInt48(_data, _offset);
    return parsed;
  }

  struct CreateMarket {
    uint16 marketIndex;
    MarketType marketType;
    bytes marketData;
  }

  /// @notice Serialize create order book pubData, it does not include metadata
  function writeCreateMarketPubDataForPriorityQueue(CreateMarket memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(uint8(PriorityPubDataTypeL1CreateMarket), _tx.marketIndex, _tx.marketType, _tx.marketData);
  }

  /// @notice Serialize create order book pubData, includes metadata
  function writeCreateMarketPubDataForPriorityQueueWithMetadata(
    bytes memory _data,
    uint8 size_decimals,
    uint8 price_decimals,
    bytes32 symbol
  ) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(_data, size_decimals, price_decimals, symbol);
  }

  struct UpdateMarket {
    uint16 marketIndex;
    MarketType marketType;
    bytes marketData;
  }

  uint8 internal constant PACKED_UPDATE_MARKET_PERPS_BYTES = 1 + PACKED_COMMON_PERPS_DATA_BYTES;

  struct UpdateMarketPerps {
    uint8 status;
    CommonPerpsData common;
  }

  function readUpdateMarketPerpsData(bytes memory _data) internal pure returns (UpdateMarketPerps memory parsed) {
    if (_data.length != PACKED_UPDATE_MARKET_PERPS_BYTES) {
      revert("Invalid packed update market perps data length");
    }
    uint256 _offset;
    (_offset, parsed.status) = Bytes.readUInt8(_data, _offset);
    (_offset, parsed.common.takerFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.makerFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.liquidationFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.minBaseAmount) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.common.minQuoteAmount) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.common.defaultInitialMarginFraction) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.common.minInitialMarginFraction) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.common.maintenanceMarginFraction) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.common.closeOutMarginFraction) = Bytes.readUInt16(_data, _offset);
    (_offset, parsed.common.interestRate) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.fundingClampSmall) = Bytes.readUInt24(_data, _offset);
    (_offset, parsed.common.fundingClampBig) = Bytes.readUInt24(_data, _offset);
    (_offset, parsed.common.openInterestLimit) = Bytes.readUInt56(_data, _offset);
    (_offset, parsed.common.orderQuoteLimit) = Bytes.readUInt48(_data, _offset);
    return parsed;
  }

  uint8 internal constant PACKED_UPDATE_MARKET_SPOT_BYTES = 1 + PACKED_COMMON_SPOT_DATA_BYTES;

  struct UpdateMarketSpot {
    uint8 status;
    CommonSpotData common;
  }

  function readUpdateMarketSpotData(bytes memory _data) internal pure returns (UpdateMarketSpot memory parsed) {
    if (_data.length != PACKED_UPDATE_MARKET_SPOT_BYTES) {
      revert("Invalid packed update market spot data length");
    }
    uint256 _offset;
    (_offset, parsed.status) = Bytes.readUInt8(_data, _offset);
    (_offset, parsed.common.takerFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.makerFee) = Bytes.readUInt32(_data, _offset);
    (_offset, parsed.common.minBaseAmount) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.common.minQuoteAmount) = Bytes.readUInt48(_data, _offset);
    (_offset, parsed.common.orderQuoteLimit) = Bytes.readUInt48(_data, _offset);
    return parsed;
  }

  /// @notice Serialize update order book pubData
  function writeUpdateMarketPubDataForPriorityQueue(UpdateMarket memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(uint8(PriorityPubDataTypeL1UpdateMarket), _tx.marketIndex, _tx.marketType, _tx.marketData);
  }

  struct CreateOrder {
    uint48 accountIndex;
    uint48 masterAccountIndex;
    uint16 marketIndex;
    uint48 baseAmount;
    uint32 price;
    uint8 isAsk;
    uint8 orderType;
  }

  /// @notice Serialize create order pubData
  function writeCreateOrderPubDataForPriorityQueue(CreateOrder memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(
      uint8(PriorityPubDataTypeL1CreateOrder),
      _tx.accountIndex,
      _tx.masterAccountIndex,
      _tx.marketIndex,
      _tx.baseAmount,
      _tx.price,
      _tx.isAsk,
      _tx.orderType
    );
  }

  struct BurnShares {
    uint48 accountIndex;
    uint48 masterAccountIndex;
    uint48 publicPoolIndex;
    uint64 sharesAmount;
  }

  /// @notice Serialize burn shares pubData
  function writeBurnSharesPubDataForPriorityQueue(BurnShares memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(uint8(PriorityPubDataTypeL1BurnShares), _tx.accountIndex, _tx.masterAccountIndex, _tx.publicPoolIndex, _tx.sharesAmount);
  }

  struct CancelAllOrders {
    uint48 accountIndex;
    uint48 masterAccountIndex;
  }

  /// @notice Serialize cancel all orders pubData
  function writeCancelAllOrdersPubDataForPriorityQueue(CancelAllOrders memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(uint8(PriorityPubDataTypeL1CancelAllOrders), _tx.accountIndex, _tx.masterAccountIndex);
  }

  struct ChangePubKey {
    uint48 accountIndex;
    uint48 masterAccountIndex;
    uint8 apiKeyIndex;
    bytes pubKey;
  }

  /// @notice Serialize change pub key pubData
  function writeChangePubKeyPubDataForPriorityQueue(ChangePubKey memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(uint8(PriorityPubDataTypeL1ChangePubKey), _tx.accountIndex, _tx.masterAccountIndex, _tx.apiKeyIndex, _tx.pubKey);
  }

  struct SetSystemConfig {
    uint48 liquidityPoolIndex;
    uint48 stakingPoolIndex;
    uint48 liquidityPoolCooldownPeriod;
    uint48 stakingPoolLockupPeriod;
  }

  /// @notice Serialize create asset pubData
  function writeSetSystemConfigPubDataForPriorityQueue(SetSystemConfig memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(
      uint8(PriorityPubDataTypeL1SetSystemConfig),
      _tx.liquidityPoolIndex,
      _tx.stakingPoolIndex,
      _tx.liquidityPoolCooldownPeriod,
      _tx.stakingPoolLockupPeriod
    );
  }

  struct RegisterAsset {
    uint16 assetIndex; // Asset index of the token being registered
    uint56 extensionMultiplier; // Lighter internal asset extension multiplier
    uint64 minL2TransferAmount; // Minimum L2 transfer amount for the asset
    uint64 minL2WithdrawalAmount; // Minimum L2 withdrawal amount for the asset
    uint8 marginMode; // 0 if disabled, 1 if enabled
  }

  /// @notice Serialize create asset pubData
  function writeRegisterAssetPubDataForPriorityQueue(RegisterAsset memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(
      uint8(PriorityPubDataTypeL1RegisterAsset),
      _tx.assetIndex,
      _tx.extensionMultiplier,
      _tx.minL2TransferAmount,
      _tx.minL2WithdrawalAmount,
      _tx.marginMode
    );
  }

  /// @notice Serialize create order book pubData, includes metadata
  function writeRegisterAssetPubDataForPriorityQueueWithMetadata(
    bytes memory _data,
    uint8 l1Decimals,
    uint8 decimals,
    uint128 tickSize,
    address tokenAddress,
    bytes32 symbol
  ) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(_data, l1Decimals, decimals, tickSize, tokenAddress, symbol);
  }

  struct UpdateAsset {
    uint16 assetIndex; // Asset index of the token being updated
    uint64 minL2TransferAmount; // Minimum L2 transfer amount for the asset
    uint64 minL2WithdrawalAmount; // Minimum L2 withdrawal amount for the asset
    uint8 marginMode; // 0 if disabled, 1 if enabled
  }

  /// @notice Serialize update asset pubData
  function writeUpdateAssetPubDataForPriorityQueue(UpdateAsset memory _tx) internal pure returns (bytes memory buf) {
    buf = abi.encodePacked(
      uint8(PriorityPubDataTypeL1UpdateAsset),
      _tx.assetIndex,
      _tx.minL2TransferAmount,
      _tx.minL2WithdrawalAmount,
      _tx.marginMode
    );
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import "./interfaces/IZkLighterDesertMode.sol";
import "./interfaces/IZkLighterVerifier.sol";
import "./interfaces/IDesertVerifier.sol";
import "./interfaces/IGovernance.sol";
import "./AdditionalZkLighter.sol";
import "./Config.sol";

/// @title zkLighter Storage Contract
/// @author zkLighter Team
contract Storage is IZkLighterDesertMode, Config {
  // Public tree roots
  bytes32 public stateRoot;
  bytes32 public validiumRoot;

  struct PriorityRequest {
    bytes32 prefixHash;
    uint64 expirationTimestamp;
  }

  /// @dev Priority Request mapping
  /// @dev Requests are indexed by their receiving order
  mapping(uint64 => PriorityRequest) internal priorityRequests;

  /// @notice Priority operation struct
  /// @dev Contains request type and hashed pubData
  struct OnChainL2Request {
    bytes20 hashedPubData;
    uint64 priorityRequestOffset;
  }

  enum MarketStatus {
    NONE,
    ACTIVE
  }

  /// @dev Deprecated: L2 Request mapping for L2 transactions that needs to be executed in the base layer
  mapping(uint64 => OnChainL2Request) internal __DEPRECATED_onChainL2Requests;

  /// @dev Verifier contract, used for verifying batch execution proofs
  IZkLighterVerifier internal verifier;

  /// @dev Desert verifier contract, used for verifying desert mode proofs
  IDesertVerifier internal desertVerifier;

  /// @dev Governance contract, stores the governor of the network
  IGovernance internal governance;

  /// @dev Additional zkLighter implementation contract (code size limitations)
  AdditionalZkLighter internal additionalZkLighter;

  /// @dev Number of priority requests committed
  uint64 public committedPriorityRequestCount;
  /// @dev Number of priority requests committed and verified
  uint64 public verifiedPriorityRequestCount;
  /// @dev Number of priority requests committed, verified and executed
  uint64 public executedPriorityRequestCount;
  /// @dev Number of queued priority requests waiting to be executed
  uint64 public openPriorityRequestCount;

  /// @dev Number of batches committed
  uint64 public committedBatchesCount;
  /// @dev Number of batches committed and verified
  uint64 public verifiedBatchesCount;
  /// @dev Number of batches committed, verified and executed
  uint64 public executedBatchesCount;

  /// @dev Number of queued batches that have onChainOperations waiting to be executed
  uint64 public pendingOnChainBatchesCount;
  /// @dev Number of queued batches that have onChainOperations executed
  uint64 public executedOnChainBatchesCount;

  bytes32 public lastVerifiedStateRoot;
  bytes32 public lastVerifiedValidiumRoot;
  uint64 public lastVerifiedEndBlockNumber;

  struct StoredBatchInfo {
    uint64 batchNumber;
    uint64 endBlockNumber;
    uint32 batchSize;
    uint64 startTimestamp;
    uint64 endTimestamp;
    uint32 priorityRequestCount;
    bytes32 prefixPriorityRequestHash;
    bytes32 onChainOperationsHash;
    bytes32 stateRoot;
    bytes32 validiumRoot;
    bytes32 commitment;
  }

  /// @dev Stores hashed StoredBatchInfo indexed by the batchNumber
  mapping(uint64 => bytes32) public storedBatchHashes;

  struct ExecutionQueueItem {
    uint64 batchNumber;
    uint64 totalPriorityRequests;
  }

  /// @dev Stores if a batch needs to be executed, indexed by the pendingOnChainBatchesCount and
  /// @dev executedOnChainBatchesCount, value is the batchNumber
  mapping(uint64 => ExecutionQueueItem) internal onChainExecutionQueue;

  /// @dev Flag indicates that desert (exit hatch) mode is triggered
  /// @dev Once desert mode is triggered, it can not be reverted
  bool public override desertMode;

  /// @dev Deprecated: Added a new mapping(uint48 => bool) internal accountPerformedDesert;
  mapping(uint32 => bool) internal __DEPRECATED_performedDesert;

  uint8 internal constant FILLED_GAS_RESERVE_VALUE = 0xff; // Used for setting gas reserve value, so that the slot will not be emptied with 0 balance
  struct DEPRECATED_PendingBalance {
    uint128 balanceToWithdraw;
    uint8 gasReserveValue;
  }

  /// @notice Address that collects fees from listed markets
  address public treasury;
  /// @notice Address that operates the insurance fund
  address public insuranceFundOperator;
  /// @notice Index of the last registered account in the network including the system accounts
  uint48 public lastAccountIndex;
  /// @notice Account address to account id mapping, excluding the system accounts
  mapping(address => uint48) public addressToAccountIndex;

  /// @dev Base layer withdrawable USDC balances for each master account index
  /// @dev Deprecated: use pendingAssetBalances instead
  mapping(uint48 => DEPRECATED_PendingBalance) internal DEPRECATED_pendingBalance;

  /// @dev Deprecated state root updates mapping, moved to ExtendableStorage
  mapping(uint64 => bytes32) public __DEPRECATED_stateRootUpdates;

  modifier onlyActive() {
    if (desertMode) {
      // Desert mode activated
      revert ZkLighter_DesertModeActive();
    }
    _;
  }

  function hashStoredBatchInfo(StoredBatchInfo memory _batch) internal pure returns (bytes32) {
    return keccak256(abi.encode(_batch));
  }

  function getAccountIndexFromAddress(address _address) internal view returns (uint48) {
    uint48 _accountIndex = addressToAccountIndex[_address];
    if (_accountIndex == 0) {
      if (_address == treasury) {
        return TREASURY_ACCOUNT_INDEX;
      } else if (_address == insuranceFundOperator) {
        return INSURANCE_FUND_OPERATOR_ACCOUNT_INDEX;
      }
      return NIL_ACCOUNT_INDEX;
    }
    return _accountIndex;
  }
}

Settings
{
  "viaIR": true,
  "evmVersion": "cancun",
  "optimizer": {
    "enabled": true,
    "runs": 1000
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ZkLighter_AccountAlreadyPerformedDesertForAsset","type":"error"},{"inputs":[],"name":"ZkLighter_BatchNotInOnChainQueue","type":"error"},{"inputs":[],"name":"ZkLighter_CannotBeInitialisedByImpl","type":"error"},{"inputs":[],"name":"ZkLighter_CannotExecuteNonVerifiedBatch","type":"error"},{"inputs":[],"name":"ZkLighter_CannotRevertExecutedBatch","type":"error"},{"inputs":[],"name":"ZkLighter_CannotRevertGenesisBatch","type":"error"},{"inputs":[],"name":"ZkLighter_CannotVerifyNonCommittedBatch","type":"error"},{"inputs":[],"name":"ZkLighter_CommitBatchPriorityRequestCountMismatch","type":"error"},{"inputs":[],"name":"ZkLighter_DepositPubdataHashMismatch","type":"error"},{"inputs":[],"name":"ZkLighter_DesertModeActive","type":"error"},{"inputs":[],"name":"ZkLighter_DesertModeInactive","type":"error"},{"inputs":[],"name":"ZkLighter_DesertVerifyProofFailed","type":"error"},{"inputs":[],"name":"ZkLighter_ETHTransferFailed","type":"error"},{"inputs":[],"name":"ZkLighter_ExecuteInputLengthGreaterThanPendingCount","type":"error"},{"inputs":[],"name":"ZkLighter_ExecuteInputLengthMismatch","type":"error"},{"inputs":[],"name":"ZkLighter_ImplCantDelegateToAddl","type":"error"},{"inputs":[],"name":"ZkLighter_InsuranceFundOperatorCannotBeInUse","type":"error"},{"inputs":[],"name":"ZkLighter_InsuranceFundOperatorCannotBeZero","type":"error"},{"inputs":[],"name":"ZkLighter_InvalidAssetConfigParams","type":"error"},{"inputs":[],"name":"ZkLighter_InvalidAssetIndex","type":"error"},{"inputs":[],"name":"ZkLighter_InvalidBatchSize","type":"error"},{"inputs":[],"name":"ZkLighter_InvalidBlobCommitmentParams","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"ZkLighter_InvalidBlobCount","type":"error"},{"inputs":[],"name":"ZkLighter_InvalidInitializeParameters","type":"error"},{"inputs":[],"name":"ZkLighter_InvalidParamsForCancelOutstandingDeposits","type":"error"},{"inputs":[],"name":"ZkLighter_InvalidPointEvaluationParams","type":"error"},{"inputs":[],"name":"ZkLighter_InvalidPubDataLength","type":"error"},{"inputs":[],"name":"ZkLighter_InvalidPubDataMode","type":"error"},{"inputs":[],"name":"ZkLighter_InvalidPubDataType","type":"error"},{"inputs":[],"name":"ZkLighter_InvalidUpgradeParameters","type":"error"},{"inputs":[],"name":"ZkLighter_InvalidWithdrawAmount","type":"error"},{"inputs":[],"name":"ZkLighter_NoOutstandingDepositsForCancelation","type":"error"},{"inputs":[],"name":"ZkLighter_NonIncreasingBlockNumber","type":"error"},{"inputs":[],"name":"ZkLighter_NonIncreasingTimestamp","type":"error"},{"inputs":[],"name":"ZkLighter_OnChainOperationsHashMismatch","type":"error"},{"inputs":[],"name":"ZkLighter_OnlyProxyCanCallUpgrade","type":"error"},{"inputs":[],"name":"ZkLighter_OnlyZkLighter","type":"error"},{"inputs":[],"name":"ZkLighter_PriorityRequestPrefixHashMismatch","type":"error"},{"inputs":[],"name":"ZkLighter_RollUpBalanceBiggerThanMaxAmount","type":"error"},{"inputs":[],"name":"ZkLighter_StoredBatchInfoMismatch","type":"error"},{"inputs":[],"name":"ZkLighter_TransferredAmountNotMultipleOfTickSize","type":"error"},{"inputs":[],"name":"ZkLighter_TreasuryCannotBeInUse","type":"error"},{"inputs":[],"name":"ZkLighter_TreasuryCannotBeZero","type":"error"},{"inputs":[],"name":"ZkLighter_VerifyBatchNotInOrder","type":"error"},{"inputs":[],"name":"ZkLighter_VerifyBatchProofFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"batchNumber","type":"uint64"},{"indexed":false,"internalType":"uint32","name":"batchSize","type":"uint32"},{"indexed":false,"internalType":"uint64","name":"endBlockNumber","type":"uint64"}],"name":"BatchCommit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"batchNumber","type":"uint64"},{"indexed":false,"internalType":"uint32","name":"batchSize","type":"uint32"},{"indexed":false,"internalType":"uint64","name":"endBlockNumber","type":"uint64"}],"name":"BatchVerification","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"batchNumber","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"endBlockNumber","type":"uint64"}],"name":"BatchesExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"newTotalBlocksCommitted","type":"uint64"}],"name":"BatchesRevert","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint16","name":"marketIndex","type":"uint16"},{"internalType":"enum TxTypes.MarketType","name":"marketType","type":"uint8"},{"internalType":"bytes","name":"marketData","type":"bytes"}],"indexed":false,"internalType":"struct TxTypes.CreateMarket","name":"params","type":"tuple"},{"indexed":false,"internalType":"uint8","name":"sizeDecimals","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"priceDecimals","type":"uint8"},{"indexed":false,"internalType":"bytes32","name":"symbol","type":"bytes32"}],"name":"CreateMarket","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint48","name":"toAccountIndex","type":"uint48"},{"indexed":false,"internalType":"address","name":"toAddress","type":"address"},{"indexed":false,"internalType":"uint16","name":"assetIndex","type":"uint16"},{"indexed":false,"internalType":"enum TxTypes.RouteType","name":"routeType","type":"uint8"},{"indexed":false,"internalType":"uint128","name":"baseAmount","type":"uint128"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[],"name":"DesertMode","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newInsuranceFundOperator","type":"address"}],"name":"InsuranceFundOperatorUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint64","name":"serialId","type":"uint64"},{"indexed":false,"internalType":"uint8","name":"pubdataType","type":"uint8"},{"indexed":false,"internalType":"bytes","name":"pubData","type":"bytes"},{"indexed":false,"internalType":"uint64","name":"expirationTimestamp","type":"uint64"}],"name":"NewPriorityRequest","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint16","name":"assetIndex","type":"uint16"},{"internalType":"uint56","name":"extensionMultiplier","type":"uint56"},{"internalType":"uint64","name":"minL2TransferAmount","type":"uint64"},{"internalType":"uint64","name":"minL2WithdrawalAmount","type":"uint64"},{"internalType":"uint8","name":"marginMode","type":"uint8"}],"indexed":false,"internalType":"struct TxTypes.RegisterAsset","name":"params","type":"tuple"},{"indexed":false,"internalType":"uint8","name":"l1Decimals","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"decimals","type":"uint8"},{"indexed":false,"internalType":"bytes32","name":"symbol","type":"bytes32"}],"name":"RegisterAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"assetIndex","type":"uint16"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint8","name":"withdrawalsEnabled","type":"uint8"},{"indexed":false,"internalType":"uint56","name":"extensionMultiplier","type":"uint56"},{"indexed":false,"internalType":"uint128","name":"tickSize","type":"uint128"},{"indexed":false,"internalType":"uint64","name":"depositCapTicks","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"minDepositTicks","type":"uint64"}],"name":"RegisterAssetConfig","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"batchNumber","type":"uint64"},{"indexed":false,"internalType":"bytes32","name":"oldStateRoot","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"newStateRoot","type":"bytes32"}],"name":"StateRootUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newTreasury","type":"address"}],"name":"TreasuryUpdate","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint16","name":"assetIndex","type":"uint16"},{"internalType":"uint64","name":"minL2TransferAmount","type":"uint64"},{"internalType":"uint64","name":"minL2WithdrawalAmount","type":"uint64"},{"internalType":"uint8","name":"marginMode","type":"uint8"}],"indexed":false,"internalType":"struct TxTypes.UpdateAsset","name":"params","type":"tuple"}],"name":"UpdateAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"assetIndex","type":"uint16"},{"indexed":false,"internalType":"uint8","name":"withdrawalsEnabled","type":"uint8"},{"indexed":false,"internalType":"uint64","name":"depositCapTicks","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"minDepositTicks","type":"uint64"}],"name":"UpdateAssetConfig","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint16","name":"marketIndex","type":"uint16"},{"internalType":"enum TxTypes.MarketType","name":"marketType","type":"uint8"},{"internalType":"bytes","name":"marketData","type":"bytes"}],"indexed":false,"internalType":"struct TxTypes.UpdateMarket","name":"params","type":"tuple"}],"name":"UpdateMarket","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint16","name":"assetIndex","type":"uint16"},{"indexed":false,"internalType":"uint128","name":"baseAmount","type":"uint128"}],"name":"WithdrawPending","type":"event"},{"inputs":[],"name":"MAX_ACCOUNT_INDEX","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_API_KEY_INDEX","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_ASSET_INDEX","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_BATCH_DEPOSIT_LENGTH","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_DEPOSIT_CAP_TICKS","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_MASTER_ACCOUNT_INDEX","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_ORDER_BASE_AMOUNT","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_ORDER_PRICE","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_ORDER_QUOTE_AMOUNT","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PERPS_MARKET_INDEX","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_POOL_SHARES_TO_MINT_OR_BURN","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SPOT_MARKET_INDEX","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_STAKING_SHARES_TO_MINT_OR_BURN","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TICK_SIZE","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_ASSET_INDEX","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_ORDER_BASE_AMOUNT","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_ORDER_PRICE","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_POOL_SHARES_TO_MINT_OR_BURN","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_SPOT_MARKET_INDEX","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_STAKING_SHARES_TO_MINT_OR_BURN","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NATIVE_ASSET_INDEX","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NIL_ACCOUNT_INDEX","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NIL_ORDER_BASE_AMOUNT","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRIORITY_EXPIRATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDC_ASSET_INDEX","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"__DEPRECATED_stateRootUpdates","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activateDesertMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"addressToAccountIndex","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"","type":"uint16"}],"name":"assetConfigs","outputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint8","name":"withdrawalsEnabled","type":"uint8"},{"internalType":"uint56","name":"extensionMultiplier","type":"uint56"},{"internalType":"uint128","name":"tickSize","type":"uint128"},{"internalType":"uint64","name":"depositCapTicks","type":"uint64"},{"internalType":"uint64","name":"minDepositTicks","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"_accountIndex","type":"uint48"},{"internalType":"uint48","name":"_publicPoolIndex","type":"uint48"},{"internalType":"uint64","name":"_shareAmount","type":"uint64"}],"name":"burnShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"_accountIndex","type":"uint48"}],"name":"cancelAllOrders","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"_n","type":"uint64"},{"internalType":"bytes[]","name":"_priorityPubData","type":"bytes[]"}],"name":"cancelOutstandingDepositsForDesertMode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"_accountIndex","type":"uint48"},{"internalType":"uint8","name":"_apiKeyIndex","type":"uint8"},{"internalType":"bytes","name":"_pubKey","type":"bytes"}],"name":"changePubKey","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint64","name":"endBlockNumber","type":"uint64"},{"internalType":"uint32","name":"batchSize","type":"uint32"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"uint64","name":"endTimestamp","type":"uint64"},{"internalType":"uint32","name":"priorityRequestCount","type":"uint32"},{"internalType":"bytes32","name":"prefixPriorityRequestHash","type":"bytes32"},{"internalType":"bytes32","name":"onChainOperationsHash","type":"bytes32"},{"internalType":"bytes32","name":"newStateRoot","type":"bytes32"},{"internalType":"bytes32","name":"newValidiumRoot","type":"bytes32"},{"internalType":"bytes","name":"pubdataCommitments","type":"bytes"}],"internalType":"struct IZkLighter.CommitBatchInfo","name":"newBatchData","type":"tuple"},{"components":[{"internalType":"uint64","name":"batchNumber","type":"uint64"},{"internalType":"uint64","name":"endBlockNumber","type":"uint64"},{"internalType":"uint32","name":"batchSize","type":"uint32"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"uint64","name":"endTimestamp","type":"uint64"},{"internalType":"uint32","name":"priorityRequestCount","type":"uint32"},{"internalType":"bytes32","name":"prefixPriorityRequestHash","type":"bytes32"},{"internalType":"bytes32","name":"onChainOperationsHash","type":"bytes32"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"},{"internalType":"bytes32","name":"validiumRoot","type":"bytes32"},{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"internalType":"struct Storage.StoredBatchInfo","name":"lastStoredBatch","type":"tuple"}],"name":"commitBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"committedBatchesCount","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"committedPriorityRequestCount","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"_size_decimals","type":"uint8"},{"internalType":"uint8","name":"_price_decimals","type":"uint8"},{"internalType":"bytes32","name":"_symbol","type":"bytes32"},{"components":[{"internalType":"uint16","name":"marketIndex","type":"uint16"},{"internalType":"enum TxTypes.MarketType","name":"marketType","type":"uint8"},{"internalType":"bytes","name":"marketData","type":"bytes"}],"internalType":"struct TxTypes.CreateMarket","name":"_params","type":"tuple"}],"name":"createMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"_accountIndex","type":"uint48"},{"internalType":"uint16","name":"_marketIndex","type":"uint16"},{"internalType":"uint48","name":"_baseAmount","type":"uint48"},{"internalType":"uint32","name":"_price","type":"uint32"},{"internalType":"uint8","name":"_isAsk","type":"uint8"},{"internalType":"uint8","name":"_orderType","type":"uint8"}],"name":"createOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint16","name":"_assetIndex","type":"uint16"},{"internalType":"enum TxTypes.RouteType","name":"_routeType","type":"uint8"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint64[]","name":"_amount","type":"uint64[]"},{"internalType":"address[]","name":"_to","type":"address[]"},{"internalType":"uint48[]","name":"_accountIndex","type":"uint48[]"}],"name":"depositBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"desertMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint64","name":"batchNumber","type":"uint64"},{"internalType":"uint64","name":"endBlockNumber","type":"uint64"},{"internalType":"uint32","name":"batchSize","type":"uint32"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"uint64","name":"endTimestamp","type":"uint64"},{"internalType":"uint32","name":"priorityRequestCount","type":"uint32"},{"internalType":"bytes32","name":"prefixPriorityRequestHash","type":"bytes32"},{"internalType":"bytes32","name":"onChainOperationsHash","type":"bytes32"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"},{"internalType":"bytes32","name":"validiumRoot","type":"bytes32"},{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"internalType":"struct Storage.StoredBatchInfo[]","name":"batches","type":"tuple[]"},{"internalType":"bytes[]","name":"onChainOperationsPubData","type":"bytes[]"}],"name":"executeBatches","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"executedBatchesCount","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"executedOnChainBatchesCount","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"executedPriorityRequestCount","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint16","name":"_assetIndex","type":"uint16"}],"name":"getPendingBalance","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"getPendingBalanceLegacy","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"initializationParameters","type":"bytes"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"insuranceFundOperator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastAccountIndex","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastVerifiedEndBlockNumber","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastVerifiedStateRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastVerifiedValidiumRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"openPriorityRequestCount","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOnChainBatchesCount","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"_accountIndex","type":"uint48"},{"internalType":"uint48","name":"_masterAccountIndex","type":"uint48"},{"internalType":"uint16","name":"_assetIndex","type":"uint16"},{"internalType":"uint128","name":"_totalBaseAmount","type":"uint128"},{"internalType":"bytes","name":"proof","type":"bytes"}],"name":"performDesert","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"_l1Decimals","type":"uint8"},{"internalType":"uint8","name":"_decimals","type":"uint8"},{"internalType":"bytes32","name":"_symbol","type":"bytes32"},{"components":[{"internalType":"uint16","name":"assetIndex","type":"uint16"},{"internalType":"uint56","name":"extensionMultiplier","type":"uint56"},{"internalType":"uint64","name":"minL2TransferAmount","type":"uint64"},{"internalType":"uint64","name":"minL2WithdrawalAmount","type":"uint64"},{"internalType":"uint8","name":"marginMode","type":"uint8"}],"internalType":"struct TxTypes.RegisterAsset","name":"_params","type":"tuple"}],"name":"registerAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"assetIndex","type":"uint16"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint8","name":"withdrawalsEnabled","type":"uint8"},{"internalType":"uint56","name":"extensionMultiplier","type":"uint56"},{"internalType":"uint128","name":"tickSize","type":"uint128"},{"internalType":"uint64","name":"depositCapTicks","type":"uint64"},{"internalType":"uint64","name":"minDepositTicks","type":"uint64"}],"name":"registerAssetConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint64","name":"batchNumber","type":"uint64"},{"internalType":"uint64","name":"endBlockNumber","type":"uint64"},{"internalType":"uint32","name":"batchSize","type":"uint32"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"uint64","name":"endTimestamp","type":"uint64"},{"internalType":"uint32","name":"priorityRequestCount","type":"uint32"},{"internalType":"bytes32","name":"prefixPriorityRequestHash","type":"bytes32"},{"internalType":"bytes32","name":"onChainOperationsHash","type":"bytes32"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"},{"internalType":"bytes32","name":"validiumRoot","type":"bytes32"},{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"internalType":"struct Storage.StoredBatchInfo[]","name":"_batchesToRevert","type":"tuple[]"},{"components":[{"internalType":"uint64","name":"batchNumber","type":"uint64"},{"internalType":"uint64","name":"endBlockNumber","type":"uint64"},{"internalType":"uint32","name":"batchSize","type":"uint32"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"uint64","name":"endTimestamp","type":"uint64"},{"internalType":"uint32","name":"priorityRequestCount","type":"uint32"},{"internalType":"bytes32","name":"prefixPriorityRequestHash","type":"bytes32"},{"internalType":"bytes32","name":"onChainOperationsHash","type":"bytes32"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"},{"internalType":"bytes32","name":"validiumRoot","type":"bytes32"},{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"internalType":"struct Storage.StoredBatchInfo","name":"_remainingBatch","type":"tuple"}],"name":"revertBatches","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newInsuranceFundOperator","type":"address"}],"name":"setInsuranceFundOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint48","name":"liquidityPoolIndex","type":"uint48"},{"internalType":"uint48","name":"stakingPoolIndex","type":"uint48"},{"internalType":"uint48","name":"liquidityPoolCooldownPeriod","type":"uint48"},{"internalType":"uint48","name":"stakingPoolLockupPeriod","type":"uint48"}],"internalType":"struct TxTypes.SetSystemConfig","name":"_params","type":"tuple"}],"name":"setSystemConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newTreasury","type":"address"}],"name":"setTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stateRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"stateRootUpdates","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"storedBatchHashes","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenToAssetIndex","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_maxAmount","type":"uint256"}],"name":"transferERC20","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"transferETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint16","name":"assetIndex","type":"uint16"},{"internalType":"uint64","name":"minL2TransferAmount","type":"uint64"},{"internalType":"uint64","name":"minL2WithdrawalAmount","type":"uint64"},{"internalType":"uint8","name":"marginMode","type":"uint8"}],"internalType":"struct TxTypes.UpdateAsset","name":"_params","type":"tuple"}],"name":"updateAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"assetIndex","type":"uint16"},{"internalType":"uint8","name":"withdrawalsEnabled","type":"uint8"},{"internalType":"uint64","name":"depositCapTicks","type":"uint64"},{"internalType":"uint64","name":"minDepositTicks","type":"uint64"}],"name":"updateAssetConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint16","name":"marketIndex","type":"uint16"},{"internalType":"enum TxTypes.MarketType","name":"marketType","type":"uint8"},{"internalType":"bytes","name":"marketData","type":"bytes"}],"internalType":"struct TxTypes.UpdateMarket","name":"_params","type":"tuple"}],"name":"updateMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint64","name":"batchNumber","type":"uint64"},{"internalType":"uint64","name":"endBlockNumber","type":"uint64"},{"internalType":"uint32","name":"batchSize","type":"uint32"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"uint64","name":"endTimestamp","type":"uint64"},{"internalType":"uint32","name":"priorityRequestCount","type":"uint32"},{"internalType":"bytes32","name":"prefixPriorityRequestHash","type":"bytes32"},{"internalType":"bytes32","name":"onChainOperationsHash","type":"bytes32"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"},{"internalType":"bytes32","name":"validiumRoot","type":"bytes32"},{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"internalType":"struct Storage.StoredBatchInfo","name":"_lastStoredBatch","type":"tuple"},{"internalType":"bytes32","name":"_stateRoot","type":"bytes32"},{"internalType":"bytes32","name":"_validiumRoot","type":"bytes32"},{"internalType":"bytes","name":"proof","type":"bytes"}],"name":"updateStateRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"upgradeParameters","type":"bytes"}],"name":"upgrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"validiumRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"verifiedBatchesCount","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"verifiedPriorityRequestCount","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint64","name":"batchNumber","type":"uint64"},{"internalType":"uint64","name":"endBlockNumber","type":"uint64"},{"internalType":"uint32","name":"batchSize","type":"uint32"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"uint64","name":"endTimestamp","type":"uint64"},{"internalType":"uint32","name":"priorityRequestCount","type":"uint32"},{"internalType":"bytes32","name":"prefixPriorityRequestHash","type":"bytes32"},{"internalType":"bytes32","name":"onChainOperationsHash","type":"bytes32"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"},{"internalType":"bytes32","name":"validiumRoot","type":"bytes32"},{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"internalType":"struct Storage.StoredBatchInfo","name":"batch","type":"tuple"},{"internalType":"bytes","name":"proof","type":"bytes"}],"name":"verifyBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"_accountIndex","type":"uint48"},{"internalType":"uint16","name":"_assetIndex","type":"uint16"},{"internalType":"enum TxTypes.RouteType","name":"_routeType","type":"uint8"},{"internalType":"uint64","name":"_baseAmount","type":"uint64"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint16","name":"_assetIndex","type":"uint16"},{"internalType":"uint128","name":"_baseAmount","type":"uint128"}],"name":"withdrawPendingBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint128","name":"_baseAmount","type":"uint128"}],"name":"withdrawPendingBalanceLegacy","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a0806040523460d857306080526016549060ff8260081c166086575060ff80821603604c575b604051615fa090816100dd823960805181818161154a01528181614b850152614ead0152f35b60ff90811916176016557f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498602060405160ff8152a15f6026565b62461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b6064820152608490fd5b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806302cfb563146104ed5780630b4d1558146104e85780630b9a9eb0146104e357806317010c68146104de5780631b06a763146104d95780631b6592fa146104d457806322b22256146104cf57806323ff50e1146104ca57806325216fda146104c557806325394645146104c057806326012922146104bb5780632d320e28146104b65780632f25807e146104b157806330f72b0a146104ac5780633177a48e146104a757806331c5da4e146104a2578063348e8a931461039e5780633c40c6761461049d5780633eec953814610498578063437545f914610493578063439fab911461048e57806345dca05c146104895780634ae71a21146103a857806355a2ba681461048457806361d027b31461047f578063634f02621461047a57806364420e05146104755780636883bde6146104435780636997fb5e146103a8578063706d6fbe146103a857806371175ff9146104705780637271277e1461046b57806372fc4c391461046657806379c929c1146104615780637b1a49091461045c5780637de213eb146104575780638096edf2146103d05780638542fb1414610443578063899cfa29146104525780638a8570831461044d57806390fda39b1461044857806393371c1a146104435780639588eca21461043e578063975364c61461043957806399e51881146104345780639d5aeec5146103a8578063a1922dfc146103cb578063a29141961461039e578063a44a93161461042f578063a4b6f7561461042a578063aab78a8e14610425578063aabe607814610420578063abf6a0381461041b578063ae150b3914610416578063af7c026014610411578063b1e09d451461040c578063b60cbbd714610407578063ba279a4914610402578063baa08f7d146103fd578063bdf723e8146103f8578063bfda3066146103a8578063c157483d146103f3578063cd565e08146103ee578063cd626497146103e9578063d0c40555146103e4578063d1cbc64f146103df578063d20191bd146103da578063d5102eea146103d5578063d925e21f146103d0578063da16ec10146103cb578063db085664146103c6578063e415f0f4146103c1578063e64d8795146103bc578063e8b2f939146103b7578063e9560055146103b2578063ea2102e4146103ad578063f051e760146103a8578063f0f44260146103a3578063f22b50ff1461039e578063fae2e046146103995763ff6b936c14610394575f80fd5b61418e565b614166565b61202d565b614017565b6121e3565b613fb2565b613f45565b613f2b565b613f05565b613755565b61372c565b612832565b612542565b613703565b6136cc565b613662565b61362d565b6132e8565b613248565b61305d565b612b71565b612b4b565b612b2f565b612b0f565b612aee565b612aa2565b612a6f565b612a2b565b6128c2565b6128a2565b612889565b61284d565b612802565b612617565b6125fb565b6123e3565b6125d5565b6125ad565b61255d565b612527565b6124b4565b612498565b61246f565b612434565b612405565b6123bd565b612394565b61236e565b6121fe565b6121c6565b6120e5565b6120c5565b6120a8565b61204d565b612004565b611fe7565b611fab565b611bf4565b611746565b6116bc565b611528565b61147c565b610ebf565b610c71565b6108ad565b610656565b61060d565b610598565b610550565b610500565b5f9103126104fc57565b5f80fd5b346104fc575f3660031901126104fc57602060ff600f54166040519015158152f35b6004359060ff821682036104fc57565b6024359060ff821682036104fc57565b908160609103126104fc5790565b346104fc5760803660031901126104fc57610569610522565b50610572610532565b506064356001600160401b0381116104fc57610592903690600401610542565b50614ea2565b346104fc575f3660031901126104fc57602060405163ffffffff8152f35b6004359065ffffffffffff821682036104fc57565b6024359065ffffffffffff821682036104fc57565b9181601f840112156104fc578235916001600160401b0383116104fc57602083818601950101116104fc57565b346104fc5760603660031901126104fc576106266105b6565b5061062f610532565b506044356001600160401b0381116104fc5761064f9036906004016105e0565b5050614ea2565b346104fc575f3660031901126104fc57602060095460c01c604051908152f35b6001600160401b038116036104fc57565b6004359061069482610676565b565b6024359061069482610676565b6064359061069482610676565b6084359061069482610676565b6044359061069482610676565b60a4359061069482610676565b359061069482610676565b634e487b7160e01b5f52604160045260245ffd5b6001600160401b03811161070957604052565b6106e2565b604081019081106001600160401b0382111761070957604052565b61018081019081106001600160401b0382111761070957604052565b90601f801991011681019081106001600160401b0382111761070957604052565b6040519061016082018281106001600160401b0382111761070957604052565b6040519060c082018281106001600160401b0382111761070957604052565b604051906106948261070e565b6001600160401b0381116107095760051b60200190565b6001600160401b03811161070957601f01601f191660200190565b9291926107f0826107c9565b916107fe6040519384610745565b8294818452818301116104fc578281602093845f960137010152565b9080601f830112156104fc57813591602091610835846107b2565b9360406108456040519687610745565b818652848087019260051b850101938385116104fc57858101925b858410610871575050505050505090565b83356001600160401b0381116104fc57820185603f820112156104fc5787916108a2878387868096013591016107e4565b815201930192610860565b346104fc576040806003193601126104fc576004908135906108ce82610676565b6001600160401b03926024358481116104fc576108ee903690830161081a565b6108f6614f20565b610909610905600f5460ff1690565b1590565b610c6257600854858160801c1680158015610c58575b610c3057868616908111908115610c24575b50610c15578590841c165f9481610be9575b925f9491949085925b610965610959878961420c565b6001600160401b031690565b8985161015610b445788831695606461097e888861424d565b5151118015610b31575b610b2357610994614261565b975f5b6109a1898961424d565b51518110156109fd57806109ea6109c46001936109be8d8d61424d565b5161428c565b517fff000000000000000000000000000000000000000000000000000000000000001690565b5f1a6109f6828d61428c565b5301610997565b5094939197610a2c610a1e919a9294989a8a5192839160208301958661429d565b03601f198101835282610745565b51902097610a4b846001600160401b03165f52600260205260405f2090565b548903610afb5789816029610a7f610a79610a736109c4610a6d8a988e61424d565b5161423b565b60f81c90565b60ff1690565b14610aaa575b5090610959916001610a99610965956142b6565b96011695989250505094909461094c565b9150506026610ab9828861424d565b515103610afb57828a6001610a9961096595610af084610ae5610adf610959998f61424d565b51614f91565b9290919216916150c8565b955050509091610a85565b8688517f196d115f000000000000000000000000000000000000000000000000000000008152fd5b5051631cc51aa360e31b8152fd5b50610b3c878761424d565b515115610988565b610bdd610baa87610b96610b6d82610b686008546001600160401b039060801c1690565b6141db565b67ffffffffffffffff60801b1967ffffffffffffffff60801b6008549260801b16911617600855565b60085460401c6001600160401b031661420c565b6fffffffffffffffff0000000000000000196fffffffffffffffff00000000000000006008549260401b16911617600855565b610be76001601755565b005b9450610c0e610bf7826141be565b6001600160401b03165f52600260205260405f2090565b5494610943565b828451631cc51aa360e31b8152fd5b9050825114155f610931565b8385517fc756d15e000000000000000000000000000000000000000000000000000000008152fd5b508686161561091f565b50905163036e45ad60e21b8152fd5b346104fc575f3660031901126104fc57610c89614f20565b6020610c936142ce565b60016017556040519015158152f35b63ffffffff8116036104fc57565b6044359061069482610ca2565b60a4359061069482610ca2565b6064359061069482610ca2565b60c4359061069482610ca2565b359061069482610ca2565b6101609060031901126104fc57610d04610766565b90610d0d610687565b8252610d17610696565b6020830152610d24610cb0565b6040830152610d316106a3565b6060830152610d3e6106b0565b6080830152610d4b610cbd565b60a083015260c43560c083015260e43560e0830152610104356101008301526101243561012083015261014435610140830152565b6101609060231901126104fc57610d95610766565b90610d9e610696565b8252610da86106bd565b6020830152610db5610cca565b6040830152610dc26106b0565b6060830152610dcf6106ca565b6080830152610ddc610cd7565b60a083015260e43560c08301526101043560e0830152610124356101008301526101443561012083015261016435610140830152565b9190826101609103126104fc57610e27610766565b91610e31816106d7565b8352610e3f602082016106d7565b6020840152610e5060408201610ce4565b6040840152610e61606082016106d7565b6060840152610e72608082016106d7565b6080840152610e8360a08201610ce4565b60a084015260c081013560c084015260e081013560e0840152610100808201359084015261012080820135908401526101408091013590830152565b346104fc576101803660031901126104fc57610eda36610cef565b6001600160401b0390610164358281116104fc57610efd600491369083016105e0565b929091610f08614f20565b60ff600f54166113e757610f33610f276006546001600160a01b031690565b6001600160a01b031690565b93843b156104fc576040945f86518092631015428760e21b82528180610f6b338983019190916001600160a01b036020820193169052565b03915afa8015611379576113ce575b5082516001600160401b031693610f996009546001600160401b031690565b9487610fa7610959886141f4565b9116036113a657610fb78461514b565b610fe2610fcb86516001600160401b031690565b6001600160401b03165f52600d60205260405f2090565b540361137e5761106191602091610ff76143cb565b916110276101408801517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001900690565b6110308461423b565b52611045610f2787546001600160a01b031690565b91895195869485938493633f27bd4560e11b85528a8501614420565b03915afa908115611379575f9161134a575b5015611323575061112e61111261109183516001600160401b031690565b937f5c836e1ff20ea85c52b6e3d2ef0124d3304bf3b37cc8fb0e2c84ae7d44c0593e6110c38786015163ffffffff1690565b602086019661110a6110dc89516001600160401b031690565b8a516001600160401b03938416815263ffffffff909416602085015291909116604083015281906060820190565b0390a16142b6565b6001600160401b03166001600160401b03196009541617600955565b61117e61116261115161114860a085015163ffffffff1690565b63ffffffff1690565b6008546001600160401b031661420c565b6001600160401b03166001600160401b03196008541617600855565b6101206101008201916111918351600a55565b019061119d8251600b55565b6111cd6111b184516001600160401b031690565b6001600160401b03166001600160401b0319600c541617600c55565b60095494808660801c161580156112e8575b6111ed57610be76001601755565b61125b61127993610b6d93611252847f5d490d991d08230b7690c7511bb854b7b8a05fb7c87e2348e1909384cb3255119a166fffffffffffffffff0000000000000000196fffffffffffffffff00000000000000006009549260401b16911617600955565b515f5551600155565b6008549061126f8183881c168284166141db565b9160801c166141db565b61128e610baa6008546001600160401b031690565b6112da6112b86112aa6009546001600160401b039060401c1690565b92516001600160401b031690565b92516001600160401b0392831681529190921660208201529081906040820190565b0390a15f8080808080610bdd565b5061131761130a8760c01c6001600160401b03165f52600e60205260405f2090565b546001600160401b031690565b818088169116116111df565b83517fc39805c6000000000000000000000000000000000000000000000000000000008152fd5b61136c915060203d602011611372575b6113648183610745565b810190614408565b5f611073565b503d61135a565b6143c0565b8286517ffa98ffb1000000000000000000000000000000000000000000000000000000008152fd5b8286517fa3449f4c000000000000000000000000000000000000000000000000000000008152fd5b806113db6113e1926106f6565b806104f2565b5f610f7a565b60405163a74f50b360e01b8152fd5b6004359061ffff821682036104fc57565b6024359061ffff821682036104fc57565b6044359061ffff821682036104fc57565b6001600160a01b038116036104fc57565b604435906001600160801b03821682036104fc57565b602435906001600160801b03821682036104fc57565b606435906001600160801b03821682036104fc57565b346104fc5760e03660031901126104fc576114956113f6565b602435906114a282611429565b6044359160ff831683036104fc5760643566ffffffffffffff811681036104fc57608435906001600160801b03821682036104fc57610be79460a435936114e885610676565b60c435956114f587610676565b614485565b60206003198201126104fc57600435906001600160401b0382116104fc57611524916004016105e0565b9091565b346104fc57611536366114fa565b61153e614f20565b6001600160a01b0391827f0000000000000000000000000000000000000000000000000000000000000000163014611692577fb11bb680dab3fb9238aa513aacc13268188c937de0f36b523827f6716396917461159c3684846107e4565b602081519101200361160e576115b78184938493019061496c565b939194909416931691169180611665575b5080611638575b50806115df57610be76001601755565b803b1561160e57611608906001600160a01b036101ee91166001600160a01b0319825416179055565b5f610bdd565b60046040517ffc2bb126000000000000000000000000000000000000000000000000000000008152fd5b803b1561160e5761165f906001600160a01b03166001600160a01b03196005541617600555565b5f6115cf565b803b1561160e5761168c906001600160a01b03166001600160a01b03196007541617600755565b5f6115c8565b60046040517f647f3cb3000000000000000000000000000000000000000000000000000000008152fd5b346104fc575f3660031901126104fc576020604051603e8152f35b81601f820112156104fc5780359060206116f0836107b2565b936116fe6040519586610745565b83855260208501906020610160809602850101938185116104fc57602001915b84831061172e5750505050505090565b83869161173b8486610e12565b81520192019161171e565b346104fc576040806003193601126104fc576004906001600160401b0382358181116104fc5761177990369085016116d7565b6024358281116104fc57611790903690860161081a565b90611799614f20565b60ff600f5416611be4578051825103611bbb578051946009956117ca61095988546001600160401b039060801c1690565b10611b94575f5b82518110156119f2576117f56117e7828561424d565b51516001600160401b031690565b87548682169087811682116119ca5761095961130a6118289260c01c6001600160401b03165f52600e60205260405f2090565b036119a2579061199c610b6d89610b6860019561185a611848878b61424d565b51611853888d61424d565b5190615208565b61195e61197361196e61186e865460c01c90565b956119166118a0611890896001600160401b03165f52600e60205260405f2090565b5460401c6001600160401b031690565b956118e3610baa6118906118cb60089a6118c58c546001600160401b039060401c1690565b906141db565b9b6001600160401b03165f52600e60205260405f2090565b6fffffffffffffffff0000000000000000196fffffffffffffffff00000000000000006009549260401b16911617600955565b61195e61192c611927835460c01c90565b6142b6565b77ffffffffffffffffffffffffffffffffffffffffffffffff6001600160c01b03196009549260c01b16911617600955565b5460801c6001600160401b031690565b6149a2565b67ffffffffffffffff60801b1967ffffffffffffffff60801b6009549260801b16911617600955565b016117d1565b8287517f516e3eb1000000000000000000000000000000000000000000000000000000008152fd5b8489517fedb9c87f000000000000000000000000000000000000000000000000000000008152fd5b7f5d490d991d08230b7690c7511bb854b7b8a05fb7c87e2348e1909384cb32551183878988610100611a2d611a2786516149b7565b8661424d565b5101515f55611a53610120611a4b611a4587516149b7565b8761424d565b510151600155565b815493818560801c16158015611b66575b15611b095750610b6d81611aae611ad293611ae79697166fffffffffffffffff0000000000000000196fffffffffffffffff00000000000000006009549260401b16911617600955565b600a545f55611abe600b54600155565b6008549061126f8183891c168284166141db565b611890610baa6008546001600160401b031690565b611afc6112b8600c546001600160401b031690565b0390a1610be76001601755565b611b5e92506020611b2782611b21611b3694516149b7565b9061424d565b5101516001600160401b031690565b9083519485941c16839060209093929360408301946001600160401b03809216845216910152565b0390a1610bdd565b50611b8861130a8660c01c6001600160401b03165f52600e60205260405f2090565b82808716911611611a64565b84517f81b1c191000000000000000000000000000000000000000000000000000000008152fd5b505050517f40068547000000000000000000000000000000000000000000000000000000008152fd5b5050505163a74f50b360e01b8152fd5b346104fc5760603660031901126104fc5760048035611c1281611429565b611c1a611407565b91611c2361143a565b90611c2c614f20565b611c3583615529565b90611c76611c6983611c548861ffff165f526101f260205260405f2090565b9065ffffffffffff165f5260205260405f2090565b546001600160801b031690565b926001600160801b038085169116908082118015611fa3575b611f9357611cb2611cad8861ffff165f526101f060205260405f2090565b6149d2565b600161ffff89161490811580611f72575b611f62579081611d10611cfb60608b950196611cf5611ce989516001600160801b031690565b6001600160801b031690565b90614a71565b94611cf5611ce988516001600160801b031690565b9115611ece575050506020611d6891604051809381927f7b1a49090000000000000000000000000000000000000000000000000000000083528a888401602090939291936001600160a01b0360408201951681520152565b03815f305af1908115611379575f91611e9f575b50915b611d9c611d96611ce984516001600160801b031690565b846143f9565b611e77575092611e427fef80235b5f4cf1822ad6a8621af41ac64372ff672c402874f507fc63dbe5e06f9593611e1e611e06611dff611dfa611e6a97611df4611ce96001600160a01b039c516001600160801b031690565b90614a93565b615582565b8095614a9d565b91611c548a61ffff165f526101f260205260405f2090565b906001600160801b03166fffffffffffffffffffffffffffffffff19825416179055565b6040519384931695839092916001600160801b0360209161ffff604085019616845216910152565b0390a2610be76001601755565b6040517fd301c22e000000000000000000000000000000000000000000000000000000008152fd5b611ec1915060203d602011611ec7575b611eb98183610745565b810190614a84565b5f611d7c565b503d611eaf565b90611f28611ee9610f2760209594516001600160a01b031690565b916040519586948594630ab4574d60e31b86528a8601909260609295949360808301966001600160a01b03809216845216602083015260408201520152565b03815f305af1908115611379575f91611f43575b5091611d7f565b611f5c915060203d602011611ec757611eb98183610745565b5f611f3c565b8460405163344c14a160e11b8152fd5b506001600160a01b03611f8c82516001600160a01b031690565b1615611cc3565b8260405163bb8f9df560e01b8152fd5b508115611c8f565b346104fc5760203660031901126104fc576001600160401b03600435611fd081610676565b165f526015602052602060405f2054604051908152f35b346104fc575f3660031901126104fc576020600a54604051908152f35b346104fc575f3660031901126104fc5760206001600160401b0360095460801c16604051908152f35b346104fc575f3660031901126104fc57602060405165ffffffffffff8152f35b346104fc5760c03660031901126104fc576120666105b6565b5061206f611407565b5060443565ffffffffffff8116036104fc5761208c606435610ca2565b60843560ff8116036104fc5760a43560ff811614614ea2575f80fd5b346104fc575f3660031901126104fc576020600b54604051908152f35b346104fc575f3660031901126104fc57602060405165fffffffffffe8152f35b346104fc5761213a6120f6366114fa565b6016929192549261211e60ff8560081c1615809581966121b8575b8115612198575b50614ab6565b83612131600160ff196016541617601655565b61217f57614b79565b61214057005b61215061ff001960165416601655565b604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602090a1005b61219361010061ff00196016541617601655565b614b79565b303b159150816121aa575b505f612118565b6001915060ff16145f6121a3565b600160ff8216109150612111565b346104fc575f3660031901126104fc576020600154604051908152f35b346104fc575f3660031901126104fc57602060405160018152f35b346104fc5760803660031901126104fc5760043561221b81611429565b60243561222781611429565b303303612344576040517f70a08231000000000000000000000000000000000000000000000000000000008082523060048301526020936001600160a01b038116939291908583602481885afa9384156113795786935f9561231f575b50906122939160443591615907565b60405190815230600482015292839060249082905afa918215611379576122c2935f93612300575b50506149c5565b60643581116122d657604051908152602090f35b60046040517f03a77b73000000000000000000000000000000000000000000000000000000008152fd5b612317929350803d10611ec757611eb98183610745565b905f806122bb565b6122939291955061233c90853d8711611ec757611eb98183610745565b949091612284565b60046040517fd463e24c000000000000000000000000000000000000000000000000000000008152fd5b346104fc575f3660031901126104fc5760206001600160a01b0360115416604051908152f35b346104fc575f3660031901126104fc5760206008546001600160401b036040519160401c168152f35b346104fc575f3660031901126104fc5760206001600160401b0360085416604051908152f35b346104fc575f3660031901126104fc576020604051670fffffffffffffff8152f35b346104fc575f3660031901126104fc576020604051621275008152f35b6101609060231901126104fc57602490565b346104fc5736600319016101c081126104fc57610160136104fc576101a4356001600160401b0381116104fc5761064f9036906004016105e0565b346104fc575f3660031901126104fc5760206001600160401b0360085460801c16604051908152f35b346104fc575f3660031901126104fc576020604051610ffe8152f35b346104fc5760403660031901126104fc576004356124d181611429565b60243590303303612344575f80808481945af16124ec614da7565b50156124fd57602090604051908152f35b60046040517f8e94fb8e000000000000000000000000000000000000000000000000000000008152fd5b346104fc575f3660031901126104fc57602060405160038152f35b346104fc576080366003190112806104fc5715614ea2575f80fd5b346104fc5760203660031901126104fc576001600160a01b0360043561258281611429565b165f526101f1602052602061ffff60405f205416604051908152f35b6044359060028210156104fc57565b60803660031901126104fc576125c4600435611429565b6125cc611407565b5061059261259e565b346104fc575f3660031901126104fc5760206001600160401b0360095416604051908152f35b346104fc575f3660031901126104fc5760205f54604051908152f35b346104fc5760403660031901126104fc5760043561263481611429565b61263c611450565b612644614f20565b61264d82615529565b9061266b611c698365ffffffffffff165f52601460205260405f2090565b926001600160801b0380851692169180831180156127fa575b6127e95761269d610f276006546001600160a01b031690565b906040518092631f209df760e11b825281600460209586935afa801561137957839285925f926127b6575b50604051630ab4574d60e31b81526001600160a01b039283166004820152919092166024820152604481019590955260648501528380608481015b03815f305af193841561137957611e1e61275d6127567fef80235b5f4cf1822ad6a8621af41ac64372ff672c402874f507fc63dbe5e06f976001600160a01b0397612774965f92612799575b5050615582565b8098614a9d565b9165ffffffffffff165f52601460205260405f2090565b60408051600381526001600160801b0390951660208601529116929081908101611e6a565b6127af9250803d10611ec757611eb98183610745565b5f8061274f565b6127039192506127db90853d87116127e2575b6127d38183610745565b810190614dd6565b91906126c8565b503d6127c9565b600460405163bb8f9df560e01b8152fd5b508215612684565b346104fc5760203660031901126104fc576004356001600160401b0381116104fc57610592903690600401610542565b346104fc575f3660031901126104fc57602060405160fe8152f35b346104fc5760203660031901126104fc576001600160401b0360043561287281610676565b165f52600d602052602060405f2054604051908152f35b346104fc5760203660031901126104fc576105926105b6565b346104fc575f3660031901126104fc57602060085460c01c604051908152f35b346104fc5760203660031901126104fc576004356128df81611429565b6128e7614f20565b6128fc610f276006546001600160a01b031690565b803b156104fc57604051633d7e13b560e21b8152336004820152905f90829060249082905afa801561137957612a18575b506001600160a01b038116156129ee5765ffffffffffff8061294e83615529565b16036129c457612974906001600160a01b03166001600160a01b03196012541617601255565b7fbbc858f043fabd2c7f56f0751bc461f96ba7e4a8d059fa6507ac6cf51238fa0f611afc6129aa6012546001600160a01b031690565b6040516001600160a01b0390911681529081906020820190565b60046040517fd78db55d000000000000000000000000000000000000000000000000000000008152fd5b60046040517f2acea741000000000000000000000000000000000000000000000000000000008152fd5b806113db612a25926106f6565b5f61292d565b346104fc5760203660031901126104fc576001600160a01b03600435612a5081611429565b165f526013602052602065ffffffffffff60405f205416604051908152f35b346104fc5760603660031901126104fc57612a886105b6565b50612a916105cb565b50612a9d604435610676565b614ea2565b346104fc5760203660031901126104fc5765ffffffffffff612ace600435612ac981611429565b615529565b165f52601460205260206001600160801b0360405f205416604051908152f35b346104fc575f3660031901126104fc5760206040516001600160801b038152f35b346104fc575f3660031901126104fc576020604051657fffffffffff8152f35b346104fc575f3660031901126104fc5760206040516103e88152f35b346104fc575f3660031901126104fc5760206001600160a01b0360125416604051908152f35b346104fc576101803660031901126104fc5760046001600160401b0381358181116104fc57612ba390369084016116d7565b90612bad36610d80565b612bb5614f20565b60ff600f541661304d57612bd4610f276006546001600160a01b031690565b90813b156104fc576040915f83518092631015428760e21b82528180612c0c338c83019190916001600160a01b036020820193169052565b03915afa80156113795761303a575b505f5b845163ffffffff821690811015612efa57612c39908661424d565b51612c5161095960208301516001600160401b031690565b15612ed257600890612c64825460c01c90565b612c7f816001600160401b03165f52600d60205260405f2090565b54612c898361514b565b03612ec35781516001600160401b031687808316911603612ec3576001600160401b03165f908152600d602052604081205560e0810151612df5575b90612db49291612d0e612cdc61196e845460c01c90565b77ffffffffffffffffffffffffffffffffffffffffffffffff6001600160c01b03196008549260c01b16911617600855565b612d8b60a0820191612d7e612d41612d2d611148865163ffffffff1690565b60075460a01c6001600160401b03166141db565b7fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff67ffffffffffffffff60a01b6007549260a01b16911617600755565b516001600160401b031690565b91612d9e6009546001600160401b031690565b928880851691161115612db9575b505050614deb565b612c1e565b610b68612ddf61114861116294612dd5611112612ded986149a2565b5163ffffffff1690565b91546001600160401b031690565b5f8080612dac565b60098054878160801c16908115612e9b5790612e139160c01c61420c565b612e3961130a612e22836141be565b6001600160401b03165f52600e60205260405f2090565b88612e4e61095986516001600160401b031690565b911603612e8c5761197361196e612db4969594935f612e72612e22612e83966141be565b555460801c6001600160401b031690565b90919250612cc5565b898751637ddbe9e960e11b8152fd5b8a88517fff6996ed000000000000000000000000000000000000000000000000000000008152fd5b888651637ddbe9e960e11b8152fd5b8684517f41fdb37d000000000000000000000000000000000000000000000000000000008152fd5b82848887600854918260c01c600954938385871c168210908115613013575b50612feb57612f39906001600160401b03165f52600d60205260405f2090565b54612f438661514b565b03612fdd577f6d80424573caa7280d1b1d9933dd38c7532f82305e148b3f3a9df551a4c53581611afc8587868680612f8284516001600160401b031690565b9216911614612fa4575b50600854905160c09190911c81529081906020820190565b6111b1602082612fbb610100612fd7950151600a55565b612fc9610120820151600b55565b01516001600160401b031690565b83612f8c565b8351637ddbe9e960e11b8152fd5b5083517fff6996ed000000000000000000000000000000000000000000000000000000008152fd5b9050838061302d6007546001600160401b039060a01c1690565b92881c1691161087612f19565b806113db613047926106f6565b5f612c1b565b8360405163a74f50b360e01b8152fd5b346104fc5760a03660031901126104fc576130766105b6565b61307e6105cb565b613086611418565b9161308f611466565b6084356001600160401b0381116104fc576130ae9036906004016105e0565b906130b7614f20565b6130c6610905600f5460ff1690565b613237576130ef6130e885611c548961ffff165f526101f360205260405f2090565b5460ff1690565b61320d57613177916020916131026143cb565b9161313a613114878b8b8b5f54615a5d565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001900690565b6131438461423b565b52613159610f276005546001600160a01b031690565b9160405195869485938493633f27bd4560e11b855260048501614420565b03915afa908115611379575f916131ee575b50156131c457836131a4611c5492610bdd966131b7966150c8565b61ffff165f526101f360205260405f2090565b805460ff19166001179055565b60046040517faf7695bd000000000000000000000000000000000000000000000000000000008152fd5b613207915060203d602011611372576113648183610745565b5f613189565b60046040517fe0ba6d1a000000000000000000000000000000000000000000000000000000008152fd5b600460405163036e45ad60e21b8152fd5b346104fc5760203660031901126104fc5761ffff6132646113f6565b165f9081526101f06020908152604091829020805460019091015483516001600160a01b038316815260a083811c60ff169482019490945260a89290921c66ffffffffffffff16938201939093526001600160801b0383166060820152608083811c6001600160401b03169082015260c092831c9181019190915290819081010390f35b346104fc5760803660031901126104fc576133016113f6565b613309610532565b6044359161331683610676565b6064359261332384610676565b61332b614f20565b60ff600f541661361c5761334a610f276006546001600160a01b031690565b803b156104fc5760408051633d7e13b560e21b81523360048083019190915291925f90829060249082905afa801561137957613609575b5061339c611cad8561ffff165f526101f060205260405f2090565b600161ffff86161415806135e8575b6135d95760ff8616801590811590816135cd575b506135be57806135a4575b613595576001600160401b0391828516928315801561358b575b61357c5783670fffffffffffffff8111918215613570575b50506135625769ffffffffffffffffffff6001600160801b036134418685019561343c613430885166ffffffffffffff1690565b66ffffffffffffff1690565b614813565b161161356257509161352d611afc94926134f3856134e36134b060606134a16134937fff23feaffcab98dc102270f0c98539db1067368280f613f9dfd91a601c20113d9f9b516001600160a01b031690565b965166ffffffffffffff1690565b9301516001600160801b031690565b916134cb6134bc610786565b6001600160a01b039096168652565b60ff8c16602086015266ffffffffffffff1684870152565b6001600160801b03166060830152565b6001600160401b03841660808201526001600160401b03851660a08201526135288761ffff165f526101f060205260405f2090565b614831565b51948594859290949360ff60609361ffff60808701981686521660208501526001600160401b03809216604085015216910152565b835163a28d29a760e01b8152fd5b8a16119050835f6133fc565b50835163a28d29a760e01b8152fd5b50808916156133e4565b50905163a28d29a760e01b8152fd5b50600160ff6135b7602084015160ff1690565b16146133ca565b82845163a28d29a760e01b8152fd5b6001915014155f6133bf565b50905163344c14a160e11b8152fd5b506001600160a01b0361360282516001600160a01b031690565b16156133ab565b806113db613616926106f6565b5f613381565b600460405163a74f50b360e01b8152fd5b346104fc576101003660031901126104fc57613647610522565b50613650610532565b5060a036606319011215614ea2575f80fd5b346104fc5760403660031901126104fc5760206001600160801b036136c260043561368c81611429565b61ffff6136a061369a611407565b92615529565b91165f526101f2845260405f209065ffffffffffff165f5260205260405f2090565b5416604051908152f35b346104fc5760803660031901126104fc576136e56105b6565b506136ee611407565b506136f761259e565b50612a9d606435610676565b346104fc575f3660031901126104fc5760206009546001600160401b036040519160401c168152f35b346104fc575f3660031901126104fc5760206001600160401b0360075460a01c16604051908152f35b346104fc57600319610180368201126104fc576004908135916001600160401b038084116104fc5783820192610140809186360301126104fc5761379836612422565b906137a1614f20565b60ff600f541661304d576137c0610f276006546001600160a01b031690565b95863b156104fc576040965f88518092631015428760e21b825281806137f8338c83019190916001600160a01b036020820193169052565b03915afa801561137957613ef2575b506101248101936138188588614e00565b905015613ee35760ff613860610a7361383a613834898c614e00565b90614e32565b357fff000000000000000000000000000000000000000000000000000000000000001690565b16613ebb5761386e87614e3b565b956020850196826138816109598a614e3b565b91161115613e945761389b61389589614e3b565b97614e3b565b96826138ba610959602487019a6138b46111488d614e45565b9061420c565b911603613e6d5760448301946138cf86614e3b565b836138df61095960808501614e3b565b9116108015613e4b575b613e2357600854968760c01c92613911846001600160401b03165f52600d60205260405f2090565b5461392461391f3686610e12565b61514b565b03613e155760075460a01c6001600160401b031698858d8160848a019c61394a8e614e45565b63ffffffff166139599161420c565b93818160801c16921c169061396d9161420c565b8183169182911610613ded57600111159081613dd1575b50613daa5750928a96949192898989956101006139b78f9b996139aa6139b1918e614e00565b8091614e4f565b90615b26565b910135936139d7816001600160401b03165f526101ed60205260405f2090565b54613d7c575b612cdc6139e9916142b6565b60c48601359485613c8e575b506139ff8a614e3b565b90613a0984614e45565b90613a138b614e3b565b6064890193613a2185614e3b565b9260e48b01359b8b8b6101048f9e01359e8f90613a3d8c614e45565b94516001600160c01b031960c098891b8116602083019081527fffffffff0000000000000000000000000000000000000000000000000000000060e09d8e1b811660288501529a8a1b8216602c8401529a891b166034820152603c81019e909e52605c8e0152607c8d0152609c8c015290861b90931660bc8a015260a490920135908801819052838801919091529186529094613adc61010082610745565b51902098600854613aed9060c01c90565b9b613af88d9c614e3b565b94613b0290614e45565b91613b0c90614e3b565b92613b1690614e3b565b93613b2090614e45565b94613b29610766565b6001600160401b03909d168d526001600160401b031660208d015263ffffffff909116908b01526001600160401b031660608a01526001600160401b0316608089015263ffffffff1660a088015260c087015260e0860152610100850152610120840152820152613b999061514b565b90613bb5906001600160401b03165f52600d60205260405f2090565b55613bbf90614e45565b63ffffffff1660075460a01c6001600160401b031690613bde9161420c565b613c1f907fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff67ffffffffffffffff60a01b6007549260a01b16911617600755565b60085460c01c90613c2f90614e45565b91613c3990614e3b565b92516001600160401b03918216815263ffffffff92909216602083015290911660408201527f181b25ea9d4d730f30d779f3d2099c03b26b653c889d33eef253d54baaacbd0d90606090a1610be76001601755565b613d5a90613d09612e22613ca460085460c01c90565b92613cf7613ccd613cc16007546001600160401b039060a01c1690565b6138b46111488b614e45565b613ce7613cd86107a5565b6001600160401b039097168752565b6001600160401b03166020860152565b600954908160801c169060c01c61420c565b906001600160401b038151166fffffffffffffffffffffffffffffffff196fffffffffffffffff00000000000000006020855494846001600160401b03198716178755015160401b16921617179055565b613d766119736119276009546001600160401b039060801c1690565b5f6139f5565b93506139e9612cdc613da0866001600160401b03165f526101ed60205260405f2090565b54959150506139dd565b8b517f5a9f2712000000000000000000000000000000000000000000000000000000008152fd5b613ddf9150610bf7906141be565b5460a487013514155f613984565b828e517f49495a1f000000000000000000000000000000000000000000000000000000008152fd5b8b51637ddbe9e960e11b8152fd5b5088517f8c9c6aae000000000000000000000000000000000000000000000000000000008152fd5b50613e5860648501614e3b565b83613e6561095989614e3b565b9116106138e9565b88517f4107ddc8000000000000000000000000000000000000000000000000000000008152fd5b88517f02a546b3000000000000000000000000000000000000000000000000000000008152fd5b8588517f2ccba61c000000000000000000000000000000000000000000000000000000008152fd5b858851630aa47a8360e11b8152fd5b806113db613eff926106f6565b5f613807565b346104fc575f3660031901126104fc5760206001600160401b03600c5416604051908152f35b346104fc575f3660031901126104fc5760206040515f8152f35b346104fc5760203660031901126104fc576001600160401b03600435613f6a81610676565b165f526101ed602052602060405f2054604051908152f35b9181601f840112156104fc578235916001600160401b0383116104fc576020808501948460051b0101116104fc57565b346104fc5760603660031901126104fc576001600160401b036004358181116104fc57613fe3903690600401613f82565b50506024358181116104fc57613ffd903690600401613f82565b50506044359081116104fc5761064f903690600401613f82565b346104fc5760203660031901126104fc5760043561403481611429565b61403c614f20565b614051610f276006546001600160a01b031690565b803b156104fc57604051633d7e13b560e21b8152336004820152905f90829060249082905afa801561137957614153575b506001600160a01b038116156141295765ffffffffffff806140a383615529565b16036140ff576140c9906001600160a01b03166001600160a01b03196011541617601155565b7f8a3509a4057c89a5993a4a3140c2ebf7e829d325d8998eaa6c48adcff98b2cef611afc6129aa6011546001600160a01b031690565b60046040517fe20b3007000000000000000000000000000000000000000000000000000000008152fd5b60046040517f4e5dd6e7000000000000000000000000000000000000000000000000000000008152fd5b806113db614160926106f6565b5f614082565b346104fc575f3660031901126104fc57602065ffffffffffff60125460a01c16604051908152f35b346104fc575f3660031901126104fc5760206040516108008152f35b634e487b7160e01b5f52601160045260245ffd5b6001600160401b039081165f1901919082116141d657565b6141aa565b6001600160401b0391821690821603919082116141d657565b9060016001600160401b03809316019182116141d657565b9190916001600160401b03808094169116019182116141d657565b634e487b7160e01b5f52603260045260245ffd5b8051156142485760200190565b614227565b80518210156142485760209160051b010190565b6040519060a082018281106001600160401b0382111761070957604052606482526080366020840137565b908151811015614248570160200190565b602092839282528051928391018483015e01015f815290565b6001600160401b038091169081146141d65760010190565b6008546001600160401b03808260801c161515918261438d575b8261434c575b50501561434857614304610905600f5460ff1690565b61430d57600190565b61431f600160ff19600f541617600f55565b7f9f7e400a81dddbf1c18b1c37f82aa303d166295ca4b577eb2a7c23d4b704ba895f80a1600190565b5f90565b61438492506143766109599260019260401c166001600160401b03165f52600260205260405f2090565b01546001600160401b031690565b15155f806142ee565b91506143b76109596001614376848660401c166001600160401b03165f52600260205260405f2090565b421015916142e8565b6040513d5f823e3d90fd5b604051906143d88261070e565b6001825260203681840137565b634e487b7160e01b5f52601260045260245ffd5b8115614403570690565b6143e5565b908160209103126104fc575180151581036104fc5790565b9180916040845281604085015260608401375f60608284010152601f801991011681016020608060608301928294836060828403019101528551809452019301915f5b828110614471575050505090565b835185529381019392810192600101614463565b939196959694929094614496614f20565b60ff600f541661361c576144b5610f276006546001600160a01b031690565b803b156104fc5760408051633d7e13b560e21b81523360048083019190915291925f90829060249082905afa801561137957614800575b5061ffff8088166001811480156147c8575b6147b957600181109081156147ae575b506135d9576001600160a01b0389161590811561477c575b506146f15760ff83168015159081614770575b506146f157873b156146f1576001600160801b038086168015801561475f575b801561474e575b6135be5781108015614734575b801561471f575b6135955766ffffffffffffff90818616916145986001600160401b038a1684614813565b908315938415614715575b505082156146ff575b50506146f15750917ff1b24e81016b9f39e2290cf2a9303264a07534a569df7e6200a39573d7f26b0c979893916146e495936146506145e9610786565b6001600160a01b038b16815260ff8416602082015266ffffffffffffff8516818401526001600160801b03861660608201526001600160401b03871660808201526001600160401b03881660a08201526135288a61ffff165f526101f060205260405f2090565b614681886146708b6001600160a01b03165f526101f160205260405f2090565b9061ffff1661ffff19825416179055565b5197889788959366ffffffffffffff9060ff60c097939a99956001600160a01b036001600160801b039661ffff60e08d019e168c521660208b01521660408901521660608701521660808501526001600160401b0380921660a085015216910152565b0390a16106946001601755565b905163a28d29a760e01b8152fd5b69ffffffffffffffffffff925016115f806145ac565b1192505f806145a3565b506001600160401b03808816908c1611614574565b50670fffffffffffffff6001600160401b0388161161456d565b506001600160401b038c1615614560565b506001600160401b03881615614559565b6001915014155f614539565b90506147a561479d8a6001600160a01b03165f526101f160205260405f2090565b5461ffff1690565b1615155f614526565b603e9150115f61450e565b82845163344c14a160e11b8152fd5b506001600160a01b036147f86147eb8b61ffff165f526101f060205260405f2090565b546001600160a01b031690565b1615156144fe565b806113db61480d926106f6565b5f6144ec565b9190916001600160801b03808094169116029182169182036141d657565b81518154602084015160408501517fffffffff000000000000000000000000000000000000000000000000000000009092166001600160a01b03939093169290921760a092831b74ff0000000000000000000000000000000000000000161760a89190911b7bffffffffffffff0000000000000000000000000000000000000000001617825560608301516106949360019093019261493c92916148fd906001600160801b031685906001600160801b03166fffffffffffffffffffffffffffffffff19825416179055565b612fc961491460808301516001600160401b031690565b855467ffffffffffffffff60801b191660809190911b67ffffffffffffffff60801b16178555565b77ffffffffffffffffffffffffffffffffffffffffffffffff6001600160c01b031983549260c01b169116179055565b908160609103126104fc57803561498281611429565b916040602083013561499381611429565b92013561499f81611429565b90565b6001600160401b031680156141d6575f190190565b5f198101919082116141d657565b919082039182116141d657565b9060405160c08101906001600160401b039181811083821117610709576106949260a091604052614a536001849766ffffffffffffff81546001600160a01b038116885260ff81881c16602089015260a81c1660408701520154916001600160801b03831660608601528260801c1660808501906001600160401b03169052565b60c01c910152565b9060a082029180830460a014901517156141d657565b818102929181159184041417156141d657565b908160209103126104fc575190565b8115614403570490565b6001600160801b0391821690821603919082116141d657565b15614abd57565b608460405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152fd5b91908260c09103126104fc578135614b3e81611429565b916020810135614b4d81611429565b916040820135614b5c81611429565b916060810135614b6b81611429565b9160a0608083013592013590565b6001600160a01b0391827f0000000000000000000000000000000000000000000000000000000000000000163014614d7d5782614bc381938293614bbb615671565b810190614b27565b98909216979691959290941693169116803b158015614d74575b8015614d6b575b8015614d62575b614d3857614cc295614c39614c5592614c1d614c71966001600160a01b03166001600160a01b03196004541617600455565b6001600160a01b03166001600160a01b03196006541617600655565b6001600160a01b03166001600160a01b03196007541617600755565b6001600160a01b03166001600160a01b03196005541617600555565b614c79610766565b915f83525f60208401525f60408401525f60608401525f60808401525f60a08401525f60c08401525f60e0840152816101008401526101208301525f6101408301525f5561514b565b5f8052600d6020527f81955a0a11e65eac625c29e8882660bae4e165a75d72780094acae8ece9a29ee55614cf4615692565b610694740200000000000000000000000000000000000000007fffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff6012541617601255565b60046040517fc8ba615c000000000000000000000000000000000000000000000000000000008152fd5b50813b15614beb565b50853b15614be4565b50823b15614bdd565b60046040517f19849362000000000000000000000000000000000000000000000000000000008152fd5b3d15614dd1573d90614db8826107c9565b91614dc66040519384610745565b82523d5f602084013e565b606090565b908160209103126104fc575161499f81611429565b63ffffffff8091169081146141d65760010190565b903590601e19813603018212156104fc57018035906001600160401b0382116104fc576020019181360383136104fc57565b90156142485790565b3561499f81610676565b3561499f81610ca2565b90929192836001116104fc5783116104fc57600101915f190190565b906020116104fc5790602090565b906040116104fc5760200190602090565b909392938483116104fc5784116104fc578101920390565b6001600160a01b03807f0000000000000000000000000000000000000000000000000000000000000000163014614ef657600754165f8060405192368285378336915af4903d91825f833e15614ef457f35bfd5b60046040517fa4a9ab33000000000000000000000000000000000000000000000000000000008152fd5b600260175414614f31576002601755565b606460405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152fd5b60010190816001116141d657565b5f1981146141d65760010190565b90614fa0600783511015615e62565b600782015190614fb4601d84511015615e62565b614fc2601d84015193615ca4565b929050929190565b65ffffffffffff81165f9081527ff3ea710bd913b11d46a8bb045be6b74773714e429efd3633381568c675975c16602052604090206101f2926001600160801b0391829081905416911601918183116141d6576106949361505c92604051946150328661070e565b16845260ff602085015260035f5260205260405f209065ffffffffffff165f5260205260405f2090565b815181546fffffffffffffffffffffffffffffffff19166001600160801b039190911617815590602001517fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff70ff0000000000000000000000000000000083549260801b169116179055565b9061ffff1691825f526101f291826020526001600160801b039182806151018460405f209065ffffffffffff165f5260205260405f2090565b5416911601928284116141d6576106949461505c93604051956151238761070e565b16855260ff60208601525f5260205260405f209065ffffffffffff165f5260205260405f2090565b60405160208101916151668382516001600160401b03169052565b60208101516001600160401b03166040830152604081015163ffffffff16606083015260608101516001600160401b0316608083015260808101516001600160401b031660a083015260a081015163ffffffff1660c083015260c081015160e083015260e08101516101009081840152810151610120908184015281015190610140918284015201516101609081830152815261520281610729565b51902090565b61521c610fcb82516001600160401b031690565b546152268261514b565b03615518575f9180515f5b8181106152705750505060e001510361524657565b60046040517f8c59c66d000000000000000000000000000000000000000000000000000000008152fd5b60ff61527c8285615cd7565b905016600281145f146153f95750601161529682846149c5565b106153cf576152a59083615d48565b94906153c36152ba825165ffffffffffff1690565b610a1e6020918285019461532161531361530a6152d9895161ffff1690565b936152fe604096878301966152f861095989516001600160401b031690565b916150c8565b5165ffffffffffff1690565b975161ffff1690565b91516001600160401b031690565b915195869485019788927fffff000000000000000000000000000000000000000000000000000000000000907fffffffffffff00000000000000000000000000000000000000000000000000006001600160c01b0319946031979487527f0200000000000000000000000000000000000000000000000000000000000000602088015260d01b16602186015260f01b16602784015260c01b1660298201520190565b51902093905b90615231565b60046040517fa9c58615000000000000000000000000000000000000000000000000000000008152fd5b6001036154ee57600f61540c82846149c5565b106153cf5761541b9083615cf6565b94906154e4615430825165ffffffffffff1690565b91610a1e61546c61545e6020936152fe858201976154586109598a516001600160401b031690565b90614fca565b94516001600160401b031690565b6040519485938401968791927fffffffffffff0000000000000000000000000000000000000000000000000000602f946001600160c01b03199385527f0100000000000000000000000000000000000000000000000000000000000000602086015260d01b16602184015260c01b1660278201520190565b51902093906153c9565b60046040517ffdbe1dce000000000000000000000000000000000000000000000000000000008152fd5b6004604051637ddbe9e960e11b8152fd5b6001600160a01b03809116805f52601360205265ffffffffffff908160405f2054169283156155585750505090565b80919293506011541682145f14615570575050505f90565b601254161461557c5790565b50600190565b6001600160801b0390818111615596571690565b608460405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201527f32382062697473000000000000000000000000000000000000000000000000006064820152fd5b1561560757565b608460405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152fd5b61568b60ff60165460081c1661568681615600565b615600565b6001601755565b61570061569d610786565b5f815260016020820152606460408201526402540be4006060820152670fffffffffffffff6080820152620186a060a082015260015f526101f06020527f8ef17085e6277aed8f6f01c714d30dd99bcfb12fc3881d7c54391ffa2a994b0e614831565b5f80526101f160205261573b7fb90953adcbf35a931ac053a7d0fe43fdd42a03d188a6bab41f6e6ed74a0821d2805461ffff19166001179055565b6040517ff1b24e81016b9f39e2290cf2a9303264a07534a569df7e6200a39573d7f26b0c81806157a7839490620186a060c060e0840193600181525f602082015260016040820152606460608201526402540be4006080820152670fffffffffffffff60a08201520152565b0390a1600460206157c3610f276006546001600160a01b031690565b60405192838092631f209df760e11b82525afa908115611379576158e3916001600160a01b03915f916158e8575b50166158686157fe610786565b6001600160a01b038316815260016020820152620f4240604082015260016060820152670fffffffffffffff6080820152620f424060a082015260035f526101f06020527fdcc7741fcb270a804667781042ff853c8745e471ecf5da3709c83259ac436883614831565b615895615887826001600160a01b03165f526101f160205260405f2090565b805461ffff19166003179055565b604051918291829190916001600160a01b0360e0820193600383521660208201526001604082015260c0620f42409182606082015260016080820152670fffffffffffffff60a08201520152565b0390a1565b615901915060203d6020116127e2576127d38183610745565b5f6157f1565b6159c2915f806001600160a01b03604051946159778661596960209a8b8301987fa9059cbb000000000000000000000000000000000000000000000000000000008a5260248401602090939291936001600160a01b0360408201951681520152565b03601f198101885287610745565b1692604051946159868661070e565b8786527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656488870152519082855af16159bc614da7565b91615ef8565b805190828215928315615a45575b505050156159db5750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b615a559350820181019101614408565b5f82816159d0565b93906fffffffffffffffffffffffffffffffff1992937fffff0000000000000000000000000000000000000000000000000000000000009160405195602087019788527fffffffffffff0000000000000000000000000000000000000000000000000000809260d01b16604088015260d01b16604686015260f01b16604c84015260801b16604e820152603e8152606081018181106001600160401b038211176107095760405251902090565b359060208110615b18575090565b5f199060200360031b1b1690565b5f9160a08106615b535760a081049160068311615c4f57905f915b838310615b645750505049615b535790565b6004604051630aa47a8360e11b8152fd5b909193615b8c615b7386614a5b565b615b84615b7f88614f75565b614a5b565b908585614e8a565b615ba1615b9b82849594614e6b565b90615b0a565b91615baf615b9b8386614e79565b918849938415615b5357615bc9615bee9260019787615dc3565b6040948551928391602096878401948591606093918352602083015260408201520190565b0391615c02601f1993848101835282610745565b5190209289615c1a575050509050945b019190615b41565b615c3a615c46939551948592830196879091604092825260208201520190565b03908101835282610745565b51902094615c12565b6040517fd8ae856600000000000000000000000000000000000000000000000000000000815260048101849052602490fd5b6006820192918381116141d657600691615c9e8582511015615e62565b01015190565b60269190601e600891615c9e8582511015615e62565b6008820192918381116141d657600891615c9e8582511015615e62565b919060018101908181116141d657615cef919361428c565b5160f81c90565b6001600160401b039291615d3e65ffffffffffff60405193615d178561070e565b5f8552615d33615d2d60208701955f8752614f83565b82615c81565b929092168552615cba565b9490941690529190565b919060405190606082016001600160401b0394838210868311176107095765ffffffffffff916040525f845260208401905f8252615d8f615d2d60408701955f8752614f83565b93909316855260028301918284116141d65761ffff6002615d3e95615db78686511015615e62565b84010151169052615cba565b5f9192908291615df660408096838251948592602084019788528484013781018683820152036020810184520182610745565b5190600a5afa615e04614da7565b9015615e525781818051810103126104fc578101517f8c1258acd66282b7ccc627f7f65e27faac425bfd0001a40100000000ffffffff01615e425750565b600490516358c6239f60e11b8152fd5b600482516358c6239f60e11b8152fd5b15615e6957565b606460405162461bcd60e51b815260206004820152600160248201527f53000000000000000000000000000000000000000000000000000000000000006044820152fd5b15615eb457565b606460405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b91929015615f185750815115615f0c575090565b61499f903b1515615ead565b825190915015615f2b5750805190602001fd5b604460209160405192839162461bcd60e51b83528160048401528051918291826024860152018484015e5f828201840152601f01601f19168101030190fdfea26469706673582212204fc5262796ab368153795eb1d901315248b207f5b57e06a37b96ad813f57aea264736f6c63430008190033

Deployed Bytecode

0x60806040526004361015610011575f80fd5b5f3560e01c806302cfb563146104ed5780630b4d1558146104e85780630b9a9eb0146104e357806317010c68146104de5780631b06a763146104d95780631b6592fa146104d457806322b22256146104cf57806323ff50e1146104ca57806325216fda146104c557806325394645146104c057806326012922146104bb5780632d320e28146104b65780632f25807e146104b157806330f72b0a146104ac5780633177a48e146104a757806331c5da4e146104a2578063348e8a931461039e5780633c40c6761461049d5780633eec953814610498578063437545f914610493578063439fab911461048e57806345dca05c146104895780634ae71a21146103a857806355a2ba681461048457806361d027b31461047f578063634f02621461047a57806364420e05146104755780636883bde6146104435780636997fb5e146103a8578063706d6fbe146103a857806371175ff9146104705780637271277e1461046b57806372fc4c391461046657806379c929c1146104615780637b1a49091461045c5780637de213eb146104575780638096edf2146103d05780638542fb1414610443578063899cfa29146104525780638a8570831461044d57806390fda39b1461044857806393371c1a146104435780639588eca21461043e578063975364c61461043957806399e51881146104345780639d5aeec5146103a8578063a1922dfc146103cb578063a29141961461039e578063a44a93161461042f578063a4b6f7561461042a578063aab78a8e14610425578063aabe607814610420578063abf6a0381461041b578063ae150b3914610416578063af7c026014610411578063b1e09d451461040c578063b60cbbd714610407578063ba279a4914610402578063baa08f7d146103fd578063bdf723e8146103f8578063bfda3066146103a8578063c157483d146103f3578063cd565e08146103ee578063cd626497146103e9578063d0c40555146103e4578063d1cbc64f146103df578063d20191bd146103da578063d5102eea146103d5578063d925e21f146103d0578063da16ec10146103cb578063db085664146103c6578063e415f0f4146103c1578063e64d8795146103bc578063e8b2f939146103b7578063e9560055146103b2578063ea2102e4146103ad578063f051e760146103a8578063f0f44260146103a3578063f22b50ff1461039e578063fae2e046146103995763ff6b936c14610394575f80fd5b61418e565b614166565b61202d565b614017565b6121e3565b613fb2565b613f45565b613f2b565b613f05565b613755565b61372c565b612832565b612542565b613703565b6136cc565b613662565b61362d565b6132e8565b613248565b61305d565b612b71565b612b4b565b612b2f565b612b0f565b612aee565b612aa2565b612a6f565b612a2b565b6128c2565b6128a2565b612889565b61284d565b612802565b612617565b6125fb565b6123e3565b6125d5565b6125ad565b61255d565b612527565b6124b4565b612498565b61246f565b612434565b612405565b6123bd565b612394565b61236e565b6121fe565b6121c6565b6120e5565b6120c5565b6120a8565b61204d565b612004565b611fe7565b611fab565b611bf4565b611746565b6116bc565b611528565b61147c565b610ebf565b610c71565b6108ad565b610656565b61060d565b610598565b610550565b610500565b5f9103126104fc57565b5f80fd5b346104fc575f3660031901126104fc57602060ff600f54166040519015158152f35b6004359060ff821682036104fc57565b6024359060ff821682036104fc57565b908160609103126104fc5790565b346104fc5760803660031901126104fc57610569610522565b50610572610532565b506064356001600160401b0381116104fc57610592903690600401610542565b50614ea2565b346104fc575f3660031901126104fc57602060405163ffffffff8152f35b6004359065ffffffffffff821682036104fc57565b6024359065ffffffffffff821682036104fc57565b9181601f840112156104fc578235916001600160401b0383116104fc57602083818601950101116104fc57565b346104fc5760603660031901126104fc576106266105b6565b5061062f610532565b506044356001600160401b0381116104fc5761064f9036906004016105e0565b5050614ea2565b346104fc575f3660031901126104fc57602060095460c01c604051908152f35b6001600160401b038116036104fc57565b6004359061069482610676565b565b6024359061069482610676565b6064359061069482610676565b6084359061069482610676565b6044359061069482610676565b60a4359061069482610676565b359061069482610676565b634e487b7160e01b5f52604160045260245ffd5b6001600160401b03811161070957604052565b6106e2565b604081019081106001600160401b0382111761070957604052565b61018081019081106001600160401b0382111761070957604052565b90601f801991011681019081106001600160401b0382111761070957604052565b6040519061016082018281106001600160401b0382111761070957604052565b6040519060c082018281106001600160401b0382111761070957604052565b604051906106948261070e565b6001600160401b0381116107095760051b60200190565b6001600160401b03811161070957601f01601f191660200190565b9291926107f0826107c9565b916107fe6040519384610745565b8294818452818301116104fc578281602093845f960137010152565b9080601f830112156104fc57813591602091610835846107b2565b9360406108456040519687610745565b818652848087019260051b850101938385116104fc57858101925b858410610871575050505050505090565b83356001600160401b0381116104fc57820185603f820112156104fc5787916108a2878387868096013591016107e4565b815201930192610860565b346104fc576040806003193601126104fc576004908135906108ce82610676565b6001600160401b03926024358481116104fc576108ee903690830161081a565b6108f6614f20565b610909610905600f5460ff1690565b1590565b610c6257600854858160801c1680158015610c58575b610c3057868616908111908115610c24575b50610c15578590841c165f9481610be9575b925f9491949085925b610965610959878961420c565b6001600160401b031690565b8985161015610b445788831695606461097e888861424d565b5151118015610b31575b610b2357610994614261565b975f5b6109a1898961424d565b51518110156109fd57806109ea6109c46001936109be8d8d61424d565b5161428c565b517fff000000000000000000000000000000000000000000000000000000000000001690565b5f1a6109f6828d61428c565b5301610997565b5094939197610a2c610a1e919a9294989a8a5192839160208301958661429d565b03601f198101835282610745565b51902097610a4b846001600160401b03165f52600260205260405f2090565b548903610afb5789816029610a7f610a79610a736109c4610a6d8a988e61424d565b5161423b565b60f81c90565b60ff1690565b14610aaa575b5090610959916001610a99610965956142b6565b96011695989250505094909461094c565b9150506026610ab9828861424d565b515103610afb57828a6001610a9961096595610af084610ae5610adf610959998f61424d565b51614f91565b9290919216916150c8565b955050509091610a85565b8688517f196d115f000000000000000000000000000000000000000000000000000000008152fd5b5051631cc51aa360e31b8152fd5b50610b3c878761424d565b515115610988565b610bdd610baa87610b96610b6d82610b686008546001600160401b039060801c1690565b6141db565b67ffffffffffffffff60801b1967ffffffffffffffff60801b6008549260801b16911617600855565b60085460401c6001600160401b031661420c565b6fffffffffffffffff0000000000000000196fffffffffffffffff00000000000000006008549260401b16911617600855565b610be76001601755565b005b9450610c0e610bf7826141be565b6001600160401b03165f52600260205260405f2090565b5494610943565b828451631cc51aa360e31b8152fd5b9050825114155f610931565b8385517fc756d15e000000000000000000000000000000000000000000000000000000008152fd5b508686161561091f565b50905163036e45ad60e21b8152fd5b346104fc575f3660031901126104fc57610c89614f20565b6020610c936142ce565b60016017556040519015158152f35b63ffffffff8116036104fc57565b6044359061069482610ca2565b60a4359061069482610ca2565b6064359061069482610ca2565b60c4359061069482610ca2565b359061069482610ca2565b6101609060031901126104fc57610d04610766565b90610d0d610687565b8252610d17610696565b6020830152610d24610cb0565b6040830152610d316106a3565b6060830152610d3e6106b0565b6080830152610d4b610cbd565b60a083015260c43560c083015260e43560e0830152610104356101008301526101243561012083015261014435610140830152565b6101609060231901126104fc57610d95610766565b90610d9e610696565b8252610da86106bd565b6020830152610db5610cca565b6040830152610dc26106b0565b6060830152610dcf6106ca565b6080830152610ddc610cd7565b60a083015260e43560c08301526101043560e0830152610124356101008301526101443561012083015261016435610140830152565b9190826101609103126104fc57610e27610766565b91610e31816106d7565b8352610e3f602082016106d7565b6020840152610e5060408201610ce4565b6040840152610e61606082016106d7565b6060840152610e72608082016106d7565b6080840152610e8360a08201610ce4565b60a084015260c081013560c084015260e081013560e0840152610100808201359084015261012080820135908401526101408091013590830152565b346104fc576101803660031901126104fc57610eda36610cef565b6001600160401b0390610164358281116104fc57610efd600491369083016105e0565b929091610f08614f20565b60ff600f54166113e757610f33610f276006546001600160a01b031690565b6001600160a01b031690565b93843b156104fc576040945f86518092631015428760e21b82528180610f6b338983019190916001600160a01b036020820193169052565b03915afa8015611379576113ce575b5082516001600160401b031693610f996009546001600160401b031690565b9487610fa7610959886141f4565b9116036113a657610fb78461514b565b610fe2610fcb86516001600160401b031690565b6001600160401b03165f52600d60205260405f2090565b540361137e5761106191602091610ff76143cb565b916110276101408801517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001900690565b6110308461423b565b52611045610f2787546001600160a01b031690565b91895195869485938493633f27bd4560e11b85528a8501614420565b03915afa908115611379575f9161134a575b5015611323575061112e61111261109183516001600160401b031690565b937f5c836e1ff20ea85c52b6e3d2ef0124d3304bf3b37cc8fb0e2c84ae7d44c0593e6110c38786015163ffffffff1690565b602086019661110a6110dc89516001600160401b031690565b8a516001600160401b03938416815263ffffffff909416602085015291909116604083015281906060820190565b0390a16142b6565b6001600160401b03166001600160401b03196009541617600955565b61117e61116261115161114860a085015163ffffffff1690565b63ffffffff1690565b6008546001600160401b031661420c565b6001600160401b03166001600160401b03196008541617600855565b6101206101008201916111918351600a55565b019061119d8251600b55565b6111cd6111b184516001600160401b031690565b6001600160401b03166001600160401b0319600c541617600c55565b60095494808660801c161580156112e8575b6111ed57610be76001601755565b61125b61127993610b6d93611252847f5d490d991d08230b7690c7511bb854b7b8a05fb7c87e2348e1909384cb3255119a166fffffffffffffffff0000000000000000196fffffffffffffffff00000000000000006009549260401b16911617600955565b515f5551600155565b6008549061126f8183881c168284166141db565b9160801c166141db565b61128e610baa6008546001600160401b031690565b6112da6112b86112aa6009546001600160401b039060401c1690565b92516001600160401b031690565b92516001600160401b0392831681529190921660208201529081906040820190565b0390a15f8080808080610bdd565b5061131761130a8760c01c6001600160401b03165f52600e60205260405f2090565b546001600160401b031690565b818088169116116111df565b83517fc39805c6000000000000000000000000000000000000000000000000000000008152fd5b61136c915060203d602011611372575b6113648183610745565b810190614408565b5f611073565b503d61135a565b6143c0565b8286517ffa98ffb1000000000000000000000000000000000000000000000000000000008152fd5b8286517fa3449f4c000000000000000000000000000000000000000000000000000000008152fd5b806113db6113e1926106f6565b806104f2565b5f610f7a565b60405163a74f50b360e01b8152fd5b6004359061ffff821682036104fc57565b6024359061ffff821682036104fc57565b6044359061ffff821682036104fc57565b6001600160a01b038116036104fc57565b604435906001600160801b03821682036104fc57565b602435906001600160801b03821682036104fc57565b606435906001600160801b03821682036104fc57565b346104fc5760e03660031901126104fc576114956113f6565b602435906114a282611429565b6044359160ff831683036104fc5760643566ffffffffffffff811681036104fc57608435906001600160801b03821682036104fc57610be79460a435936114e885610676565b60c435956114f587610676565b614485565b60206003198201126104fc57600435906001600160401b0382116104fc57611524916004016105e0565b9091565b346104fc57611536366114fa565b61153e614f20565b6001600160a01b0391827f000000000000000000000000803bd6a0577c083c1ede82da455c8e69e697c878163014611692577fb11bb680dab3fb9238aa513aacc13268188c937de0f36b523827f6716396917461159c3684846107e4565b602081519101200361160e576115b78184938493019061496c565b939194909416931691169180611665575b5080611638575b50806115df57610be76001601755565b803b1561160e57611608906001600160a01b036101ee91166001600160a01b0319825416179055565b5f610bdd565b60046040517ffc2bb126000000000000000000000000000000000000000000000000000000008152fd5b803b1561160e5761165f906001600160a01b03166001600160a01b03196005541617600555565b5f6115cf565b803b1561160e5761168c906001600160a01b03166001600160a01b03196007541617600755565b5f6115c8565b60046040517f647f3cb3000000000000000000000000000000000000000000000000000000008152fd5b346104fc575f3660031901126104fc576020604051603e8152f35b81601f820112156104fc5780359060206116f0836107b2565b936116fe6040519586610745565b83855260208501906020610160809602850101938185116104fc57602001915b84831061172e5750505050505090565b83869161173b8486610e12565b81520192019161171e565b346104fc576040806003193601126104fc576004906001600160401b0382358181116104fc5761177990369085016116d7565b6024358281116104fc57611790903690860161081a565b90611799614f20565b60ff600f5416611be4578051825103611bbb578051946009956117ca61095988546001600160401b039060801c1690565b10611b94575f5b82518110156119f2576117f56117e7828561424d565b51516001600160401b031690565b87548682169087811682116119ca5761095961130a6118289260c01c6001600160401b03165f52600e60205260405f2090565b036119a2579061199c610b6d89610b6860019561185a611848878b61424d565b51611853888d61424d565b5190615208565b61195e61197361196e61186e865460c01c90565b956119166118a0611890896001600160401b03165f52600e60205260405f2090565b5460401c6001600160401b031690565b956118e3610baa6118906118cb60089a6118c58c546001600160401b039060401c1690565b906141db565b9b6001600160401b03165f52600e60205260405f2090565b6fffffffffffffffff0000000000000000196fffffffffffffffff00000000000000006009549260401b16911617600955565b61195e61192c611927835460c01c90565b6142b6565b77ffffffffffffffffffffffffffffffffffffffffffffffff6001600160c01b03196009549260c01b16911617600955565b5460801c6001600160401b031690565b6149a2565b67ffffffffffffffff60801b1967ffffffffffffffff60801b6009549260801b16911617600955565b016117d1565b8287517f516e3eb1000000000000000000000000000000000000000000000000000000008152fd5b8489517fedb9c87f000000000000000000000000000000000000000000000000000000008152fd5b7f5d490d991d08230b7690c7511bb854b7b8a05fb7c87e2348e1909384cb32551183878988610100611a2d611a2786516149b7565b8661424d565b5101515f55611a53610120611a4b611a4587516149b7565b8761424d565b510151600155565b815493818560801c16158015611b66575b15611b095750610b6d81611aae611ad293611ae79697166fffffffffffffffff0000000000000000196fffffffffffffffff00000000000000006009549260401b16911617600955565b600a545f55611abe600b54600155565b6008549061126f8183891c168284166141db565b611890610baa6008546001600160401b031690565b611afc6112b8600c546001600160401b031690565b0390a1610be76001601755565b611b5e92506020611b2782611b21611b3694516149b7565b9061424d565b5101516001600160401b031690565b9083519485941c16839060209093929360408301946001600160401b03809216845216910152565b0390a1610bdd565b50611b8861130a8660c01c6001600160401b03165f52600e60205260405f2090565b82808716911611611a64565b84517f81b1c191000000000000000000000000000000000000000000000000000000008152fd5b505050517f40068547000000000000000000000000000000000000000000000000000000008152fd5b5050505163a74f50b360e01b8152fd5b346104fc5760603660031901126104fc5760048035611c1281611429565b611c1a611407565b91611c2361143a565b90611c2c614f20565b611c3583615529565b90611c76611c6983611c548861ffff165f526101f260205260405f2090565b9065ffffffffffff165f5260205260405f2090565b546001600160801b031690565b926001600160801b038085169116908082118015611fa3575b611f9357611cb2611cad8861ffff165f526101f060205260405f2090565b6149d2565b600161ffff89161490811580611f72575b611f62579081611d10611cfb60608b950196611cf5611ce989516001600160801b031690565b6001600160801b031690565b90614a71565b94611cf5611ce988516001600160801b031690565b9115611ece575050506020611d6891604051809381927f7b1a49090000000000000000000000000000000000000000000000000000000083528a888401602090939291936001600160a01b0360408201951681520152565b03815f305af1908115611379575f91611e9f575b50915b611d9c611d96611ce984516001600160801b031690565b846143f9565b611e77575092611e427fef80235b5f4cf1822ad6a8621af41ac64372ff672c402874f507fc63dbe5e06f9593611e1e611e06611dff611dfa611e6a97611df4611ce96001600160a01b039c516001600160801b031690565b90614a93565b615582565b8095614a9d565b91611c548a61ffff165f526101f260205260405f2090565b906001600160801b03166fffffffffffffffffffffffffffffffff19825416179055565b6040519384931695839092916001600160801b0360209161ffff604085019616845216910152565b0390a2610be76001601755565b6040517fd301c22e000000000000000000000000000000000000000000000000000000008152fd5b611ec1915060203d602011611ec7575b611eb98183610745565b810190614a84565b5f611d7c565b503d611eaf565b90611f28611ee9610f2760209594516001600160a01b031690565b916040519586948594630ab4574d60e31b86528a8601909260609295949360808301966001600160a01b03809216845216602083015260408201520152565b03815f305af1908115611379575f91611f43575b5091611d7f565b611f5c915060203d602011611ec757611eb98183610745565b5f611f3c565b8460405163344c14a160e11b8152fd5b506001600160a01b03611f8c82516001600160a01b031690565b1615611cc3565b8260405163bb8f9df560e01b8152fd5b508115611c8f565b346104fc5760203660031901126104fc576001600160401b03600435611fd081610676565b165f526015602052602060405f2054604051908152f35b346104fc575f3660031901126104fc576020600a54604051908152f35b346104fc575f3660031901126104fc5760206001600160401b0360095460801c16604051908152f35b346104fc575f3660031901126104fc57602060405165ffffffffffff8152f35b346104fc5760c03660031901126104fc576120666105b6565b5061206f611407565b5060443565ffffffffffff8116036104fc5761208c606435610ca2565b60843560ff8116036104fc5760a43560ff811614614ea2575f80fd5b346104fc575f3660031901126104fc576020600b54604051908152f35b346104fc575f3660031901126104fc57602060405165fffffffffffe8152f35b346104fc5761213a6120f6366114fa565b6016929192549261211e60ff8560081c1615809581966121b8575b8115612198575b50614ab6565b83612131600160ff196016541617601655565b61217f57614b79565b61214057005b61215061ff001960165416601655565b604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602090a1005b61219361010061ff00196016541617601655565b614b79565b303b159150816121aa575b505f612118565b6001915060ff16145f6121a3565b600160ff8216109150612111565b346104fc575f3660031901126104fc576020600154604051908152f35b346104fc575f3660031901126104fc57602060405160018152f35b346104fc5760803660031901126104fc5760043561221b81611429565b60243561222781611429565b303303612344576040517f70a08231000000000000000000000000000000000000000000000000000000008082523060048301526020936001600160a01b038116939291908583602481885afa9384156113795786935f9561231f575b50906122939160443591615907565b60405190815230600482015292839060249082905afa918215611379576122c2935f93612300575b50506149c5565b60643581116122d657604051908152602090f35b60046040517f03a77b73000000000000000000000000000000000000000000000000000000008152fd5b612317929350803d10611ec757611eb98183610745565b905f806122bb565b6122939291955061233c90853d8711611ec757611eb98183610745565b949091612284565b60046040517fd463e24c000000000000000000000000000000000000000000000000000000008152fd5b346104fc575f3660031901126104fc5760206001600160a01b0360115416604051908152f35b346104fc575f3660031901126104fc5760206008546001600160401b036040519160401c168152f35b346104fc575f3660031901126104fc5760206001600160401b0360085416604051908152f35b346104fc575f3660031901126104fc576020604051670fffffffffffffff8152f35b346104fc575f3660031901126104fc576020604051621275008152f35b6101609060231901126104fc57602490565b346104fc5736600319016101c081126104fc57610160136104fc576101a4356001600160401b0381116104fc5761064f9036906004016105e0565b346104fc575f3660031901126104fc5760206001600160401b0360085460801c16604051908152f35b346104fc575f3660031901126104fc576020604051610ffe8152f35b346104fc5760403660031901126104fc576004356124d181611429565b60243590303303612344575f80808481945af16124ec614da7565b50156124fd57602090604051908152f35b60046040517f8e94fb8e000000000000000000000000000000000000000000000000000000008152fd5b346104fc575f3660031901126104fc57602060405160038152f35b346104fc576080366003190112806104fc5715614ea2575f80fd5b346104fc5760203660031901126104fc576001600160a01b0360043561258281611429565b165f526101f1602052602061ffff60405f205416604051908152f35b6044359060028210156104fc57565b60803660031901126104fc576125c4600435611429565b6125cc611407565b5061059261259e565b346104fc575f3660031901126104fc5760206001600160401b0360095416604051908152f35b346104fc575f3660031901126104fc5760205f54604051908152f35b346104fc5760403660031901126104fc5760043561263481611429565b61263c611450565b612644614f20565b61264d82615529565b9061266b611c698365ffffffffffff165f52601460205260405f2090565b926001600160801b0380851692169180831180156127fa575b6127e95761269d610f276006546001600160a01b031690565b906040518092631f209df760e11b825281600460209586935afa801561137957839285925f926127b6575b50604051630ab4574d60e31b81526001600160a01b039283166004820152919092166024820152604481019590955260648501528380608481015b03815f305af193841561137957611e1e61275d6127567fef80235b5f4cf1822ad6a8621af41ac64372ff672c402874f507fc63dbe5e06f976001600160a01b0397612774965f92612799575b5050615582565b8098614a9d565b9165ffffffffffff165f52601460205260405f2090565b60408051600381526001600160801b0390951660208601529116929081908101611e6a565b6127af9250803d10611ec757611eb98183610745565b5f8061274f565b6127039192506127db90853d87116127e2575b6127d38183610745565b810190614dd6565b91906126c8565b503d6127c9565b600460405163bb8f9df560e01b8152fd5b508215612684565b346104fc5760203660031901126104fc576004356001600160401b0381116104fc57610592903690600401610542565b346104fc575f3660031901126104fc57602060405160fe8152f35b346104fc5760203660031901126104fc576001600160401b0360043561287281610676565b165f52600d602052602060405f2054604051908152f35b346104fc5760203660031901126104fc576105926105b6565b346104fc575f3660031901126104fc57602060085460c01c604051908152f35b346104fc5760203660031901126104fc576004356128df81611429565b6128e7614f20565b6128fc610f276006546001600160a01b031690565b803b156104fc57604051633d7e13b560e21b8152336004820152905f90829060249082905afa801561137957612a18575b506001600160a01b038116156129ee5765ffffffffffff8061294e83615529565b16036129c457612974906001600160a01b03166001600160a01b03196012541617601255565b7fbbc858f043fabd2c7f56f0751bc461f96ba7e4a8d059fa6507ac6cf51238fa0f611afc6129aa6012546001600160a01b031690565b6040516001600160a01b0390911681529081906020820190565b60046040517fd78db55d000000000000000000000000000000000000000000000000000000008152fd5b60046040517f2acea741000000000000000000000000000000000000000000000000000000008152fd5b806113db612a25926106f6565b5f61292d565b346104fc5760203660031901126104fc576001600160a01b03600435612a5081611429565b165f526013602052602065ffffffffffff60405f205416604051908152f35b346104fc5760603660031901126104fc57612a886105b6565b50612a916105cb565b50612a9d604435610676565b614ea2565b346104fc5760203660031901126104fc5765ffffffffffff612ace600435612ac981611429565b615529565b165f52601460205260206001600160801b0360405f205416604051908152f35b346104fc575f3660031901126104fc5760206040516001600160801b038152f35b346104fc575f3660031901126104fc576020604051657fffffffffff8152f35b346104fc575f3660031901126104fc5760206040516103e88152f35b346104fc575f3660031901126104fc5760206001600160a01b0360125416604051908152f35b346104fc576101803660031901126104fc5760046001600160401b0381358181116104fc57612ba390369084016116d7565b90612bad36610d80565b612bb5614f20565b60ff600f541661304d57612bd4610f276006546001600160a01b031690565b90813b156104fc576040915f83518092631015428760e21b82528180612c0c338c83019190916001600160a01b036020820193169052565b03915afa80156113795761303a575b505f5b845163ffffffff821690811015612efa57612c39908661424d565b51612c5161095960208301516001600160401b031690565b15612ed257600890612c64825460c01c90565b612c7f816001600160401b03165f52600d60205260405f2090565b54612c898361514b565b03612ec35781516001600160401b031687808316911603612ec3576001600160401b03165f908152600d602052604081205560e0810151612df5575b90612db49291612d0e612cdc61196e845460c01c90565b77ffffffffffffffffffffffffffffffffffffffffffffffff6001600160c01b03196008549260c01b16911617600855565b612d8b60a0820191612d7e612d41612d2d611148865163ffffffff1690565b60075460a01c6001600160401b03166141db565b7fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff67ffffffffffffffff60a01b6007549260a01b16911617600755565b516001600160401b031690565b91612d9e6009546001600160401b031690565b928880851691161115612db9575b505050614deb565b612c1e565b610b68612ddf61114861116294612dd5611112612ded986149a2565b5163ffffffff1690565b91546001600160401b031690565b5f8080612dac565b60098054878160801c16908115612e9b5790612e139160c01c61420c565b612e3961130a612e22836141be565b6001600160401b03165f52600e60205260405f2090565b88612e4e61095986516001600160401b031690565b911603612e8c5761197361196e612db4969594935f612e72612e22612e83966141be565b555460801c6001600160401b031690565b90919250612cc5565b898751637ddbe9e960e11b8152fd5b8a88517fff6996ed000000000000000000000000000000000000000000000000000000008152fd5b888651637ddbe9e960e11b8152fd5b8684517f41fdb37d000000000000000000000000000000000000000000000000000000008152fd5b82848887600854918260c01c600954938385871c168210908115613013575b50612feb57612f39906001600160401b03165f52600d60205260405f2090565b54612f438661514b565b03612fdd577f6d80424573caa7280d1b1d9933dd38c7532f82305e148b3f3a9df551a4c53581611afc8587868680612f8284516001600160401b031690565b9216911614612fa4575b50600854905160c09190911c81529081906020820190565b6111b1602082612fbb610100612fd7950151600a55565b612fc9610120820151600b55565b01516001600160401b031690565b83612f8c565b8351637ddbe9e960e11b8152fd5b5083517fff6996ed000000000000000000000000000000000000000000000000000000008152fd5b9050838061302d6007546001600160401b039060a01c1690565b92881c1691161087612f19565b806113db613047926106f6565b5f612c1b565b8360405163a74f50b360e01b8152fd5b346104fc5760a03660031901126104fc576130766105b6565b61307e6105cb565b613086611418565b9161308f611466565b6084356001600160401b0381116104fc576130ae9036906004016105e0565b906130b7614f20565b6130c6610905600f5460ff1690565b613237576130ef6130e885611c548961ffff165f526101f360205260405f2090565b5460ff1690565b61320d57613177916020916131026143cb565b9161313a613114878b8b8b5f54615a5d565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001900690565b6131438461423b565b52613159610f276005546001600160a01b031690565b9160405195869485938493633f27bd4560e11b855260048501614420565b03915afa908115611379575f916131ee575b50156131c457836131a4611c5492610bdd966131b7966150c8565b61ffff165f526101f360205260405f2090565b805460ff19166001179055565b60046040517faf7695bd000000000000000000000000000000000000000000000000000000008152fd5b613207915060203d602011611372576113648183610745565b5f613189565b60046040517fe0ba6d1a000000000000000000000000000000000000000000000000000000008152fd5b600460405163036e45ad60e21b8152fd5b346104fc5760203660031901126104fc5761ffff6132646113f6565b165f9081526101f06020908152604091829020805460019091015483516001600160a01b038316815260a083811c60ff169482019490945260a89290921c66ffffffffffffff16938201939093526001600160801b0383166060820152608083811c6001600160401b03169082015260c092831c9181019190915290819081010390f35b346104fc5760803660031901126104fc576133016113f6565b613309610532565b6044359161331683610676565b6064359261332384610676565b61332b614f20565b60ff600f541661361c5761334a610f276006546001600160a01b031690565b803b156104fc5760408051633d7e13b560e21b81523360048083019190915291925f90829060249082905afa801561137957613609575b5061339c611cad8561ffff165f526101f060205260405f2090565b600161ffff86161415806135e8575b6135d95760ff8616801590811590816135cd575b506135be57806135a4575b613595576001600160401b0391828516928315801561358b575b61357c5783670fffffffffffffff8111918215613570575b50506135625769ffffffffffffffffffff6001600160801b036134418685019561343c613430885166ffffffffffffff1690565b66ffffffffffffff1690565b614813565b161161356257509161352d611afc94926134f3856134e36134b060606134a16134937fff23feaffcab98dc102270f0c98539db1067368280f613f9dfd91a601c20113d9f9b516001600160a01b031690565b965166ffffffffffffff1690565b9301516001600160801b031690565b916134cb6134bc610786565b6001600160a01b039096168652565b60ff8c16602086015266ffffffffffffff1684870152565b6001600160801b03166060830152565b6001600160401b03841660808201526001600160401b03851660a08201526135288761ffff165f526101f060205260405f2090565b614831565b51948594859290949360ff60609361ffff60808701981686521660208501526001600160401b03809216604085015216910152565b835163a28d29a760e01b8152fd5b8a16119050835f6133fc565b50835163a28d29a760e01b8152fd5b50808916156133e4565b50905163a28d29a760e01b8152fd5b50600160ff6135b7602084015160ff1690565b16146133ca565b82845163a28d29a760e01b8152fd5b6001915014155f6133bf565b50905163344c14a160e11b8152fd5b506001600160a01b0361360282516001600160a01b031690565b16156133ab565b806113db613616926106f6565b5f613381565b600460405163a74f50b360e01b8152fd5b346104fc576101003660031901126104fc57613647610522565b50613650610532565b5060a036606319011215614ea2575f80fd5b346104fc5760403660031901126104fc5760206001600160801b036136c260043561368c81611429565b61ffff6136a061369a611407565b92615529565b91165f526101f2845260405f209065ffffffffffff165f5260205260405f2090565b5416604051908152f35b346104fc5760803660031901126104fc576136e56105b6565b506136ee611407565b506136f761259e565b50612a9d606435610676565b346104fc575f3660031901126104fc5760206009546001600160401b036040519160401c168152f35b346104fc575f3660031901126104fc5760206001600160401b0360075460a01c16604051908152f35b346104fc57600319610180368201126104fc576004908135916001600160401b038084116104fc5783820192610140809186360301126104fc5761379836612422565b906137a1614f20565b60ff600f541661304d576137c0610f276006546001600160a01b031690565b95863b156104fc576040965f88518092631015428760e21b825281806137f8338c83019190916001600160a01b036020820193169052565b03915afa801561137957613ef2575b506101248101936138188588614e00565b905015613ee35760ff613860610a7361383a613834898c614e00565b90614e32565b357fff000000000000000000000000000000000000000000000000000000000000001690565b16613ebb5761386e87614e3b565b956020850196826138816109598a614e3b565b91161115613e945761389b61389589614e3b565b97614e3b565b96826138ba610959602487019a6138b46111488d614e45565b9061420c565b911603613e6d5760448301946138cf86614e3b565b836138df61095960808501614e3b565b9116108015613e4b575b613e2357600854968760c01c92613911846001600160401b03165f52600d60205260405f2090565b5461392461391f3686610e12565b61514b565b03613e155760075460a01c6001600160401b031698858d8160848a019c61394a8e614e45565b63ffffffff166139599161420c565b93818160801c16921c169061396d9161420c565b8183169182911610613ded57600111159081613dd1575b50613daa5750928a96949192898989956101006139b78f9b996139aa6139b1918e614e00565b8091614e4f565b90615b26565b910135936139d7816001600160401b03165f526101ed60205260405f2090565b54613d7c575b612cdc6139e9916142b6565b60c48601359485613c8e575b506139ff8a614e3b565b90613a0984614e45565b90613a138b614e3b565b6064890193613a2185614e3b565b9260e48b01359b8b8b6101048f9e01359e8f90613a3d8c614e45565b94516001600160c01b031960c098891b8116602083019081527fffffffff0000000000000000000000000000000000000000000000000000000060e09d8e1b811660288501529a8a1b8216602c8401529a891b166034820152603c81019e909e52605c8e0152607c8d0152609c8c015290861b90931660bc8a015260a490920135908801819052838801919091529186529094613adc61010082610745565b51902098600854613aed9060c01c90565b9b613af88d9c614e3b565b94613b0290614e45565b91613b0c90614e3b565b92613b1690614e3b565b93613b2090614e45565b94613b29610766565b6001600160401b03909d168d526001600160401b031660208d015263ffffffff909116908b01526001600160401b031660608a01526001600160401b0316608089015263ffffffff1660a088015260c087015260e0860152610100850152610120840152820152613b999061514b565b90613bb5906001600160401b03165f52600d60205260405f2090565b55613bbf90614e45565b63ffffffff1660075460a01c6001600160401b031690613bde9161420c565b613c1f907fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff67ffffffffffffffff60a01b6007549260a01b16911617600755565b60085460c01c90613c2f90614e45565b91613c3990614e3b565b92516001600160401b03918216815263ffffffff92909216602083015290911660408201527f181b25ea9d4d730f30d779f3d2099c03b26b653c889d33eef253d54baaacbd0d90606090a1610be76001601755565b613d5a90613d09612e22613ca460085460c01c90565b92613cf7613ccd613cc16007546001600160401b039060a01c1690565b6138b46111488b614e45565b613ce7613cd86107a5565b6001600160401b039097168752565b6001600160401b03166020860152565b600954908160801c169060c01c61420c565b906001600160401b038151166fffffffffffffffffffffffffffffffff196fffffffffffffffff00000000000000006020855494846001600160401b03198716178755015160401b16921617179055565b613d766119736119276009546001600160401b039060801c1690565b5f6139f5565b93506139e9612cdc613da0866001600160401b03165f526101ed60205260405f2090565b54959150506139dd565b8b517f5a9f2712000000000000000000000000000000000000000000000000000000008152fd5b613ddf9150610bf7906141be565b5460a487013514155f613984565b828e517f49495a1f000000000000000000000000000000000000000000000000000000008152fd5b8b51637ddbe9e960e11b8152fd5b5088517f8c9c6aae000000000000000000000000000000000000000000000000000000008152fd5b50613e5860648501614e3b565b83613e6561095989614e3b565b9116106138e9565b88517f4107ddc8000000000000000000000000000000000000000000000000000000008152fd5b88517f02a546b3000000000000000000000000000000000000000000000000000000008152fd5b8588517f2ccba61c000000000000000000000000000000000000000000000000000000008152fd5b858851630aa47a8360e11b8152fd5b806113db613eff926106f6565b5f613807565b346104fc575f3660031901126104fc5760206001600160401b03600c5416604051908152f35b346104fc575f3660031901126104fc5760206040515f8152f35b346104fc5760203660031901126104fc576001600160401b03600435613f6a81610676565b165f526101ed602052602060405f2054604051908152f35b9181601f840112156104fc578235916001600160401b0383116104fc576020808501948460051b0101116104fc57565b346104fc5760603660031901126104fc576001600160401b036004358181116104fc57613fe3903690600401613f82565b50506024358181116104fc57613ffd903690600401613f82565b50506044359081116104fc5761064f903690600401613f82565b346104fc5760203660031901126104fc5760043561403481611429565b61403c614f20565b614051610f276006546001600160a01b031690565b803b156104fc57604051633d7e13b560e21b8152336004820152905f90829060249082905afa801561137957614153575b506001600160a01b038116156141295765ffffffffffff806140a383615529565b16036140ff576140c9906001600160a01b03166001600160a01b03196011541617601155565b7f8a3509a4057c89a5993a4a3140c2ebf7e829d325d8998eaa6c48adcff98b2cef611afc6129aa6011546001600160a01b031690565b60046040517fe20b3007000000000000000000000000000000000000000000000000000000008152fd5b60046040517f4e5dd6e7000000000000000000000000000000000000000000000000000000008152fd5b806113db614160926106f6565b5f614082565b346104fc575f3660031901126104fc57602065ffffffffffff60125460a01c16604051908152f35b346104fc575f3660031901126104fc5760206040516108008152f35b634e487b7160e01b5f52601160045260245ffd5b6001600160401b039081165f1901919082116141d657565b6141aa565b6001600160401b0391821690821603919082116141d657565b9060016001600160401b03809316019182116141d657565b9190916001600160401b03808094169116019182116141d657565b634e487b7160e01b5f52603260045260245ffd5b8051156142485760200190565b614227565b80518210156142485760209160051b010190565b6040519060a082018281106001600160401b0382111761070957604052606482526080366020840137565b908151811015614248570160200190565b602092839282528051928391018483015e01015f815290565b6001600160401b038091169081146141d65760010190565b6008546001600160401b03808260801c161515918261438d575b8261434c575b50501561434857614304610905600f5460ff1690565b61430d57600190565b61431f600160ff19600f541617600f55565b7f9f7e400a81dddbf1c18b1c37f82aa303d166295ca4b577eb2a7c23d4b704ba895f80a1600190565b5f90565b61438492506143766109599260019260401c166001600160401b03165f52600260205260405f2090565b01546001600160401b031690565b15155f806142ee565b91506143b76109596001614376848660401c166001600160401b03165f52600260205260405f2090565b421015916142e8565b6040513d5f823e3d90fd5b604051906143d88261070e565b6001825260203681840137565b634e487b7160e01b5f52601260045260245ffd5b8115614403570690565b6143e5565b908160209103126104fc575180151581036104fc5790565b9180916040845281604085015260608401375f60608284010152601f801991011681016020608060608301928294836060828403019101528551809452019301915f5b828110614471575050505090565b835185529381019392810192600101614463565b939196959694929094614496614f20565b60ff600f541661361c576144b5610f276006546001600160a01b031690565b803b156104fc5760408051633d7e13b560e21b81523360048083019190915291925f90829060249082905afa801561137957614800575b5061ffff8088166001811480156147c8575b6147b957600181109081156147ae575b506135d9576001600160a01b0389161590811561477c575b506146f15760ff83168015159081614770575b506146f157873b156146f1576001600160801b038086168015801561475f575b801561474e575b6135be5781108015614734575b801561471f575b6135955766ffffffffffffff90818616916145986001600160401b038a1684614813565b908315938415614715575b505082156146ff575b50506146f15750917ff1b24e81016b9f39e2290cf2a9303264a07534a569df7e6200a39573d7f26b0c979893916146e495936146506145e9610786565b6001600160a01b038b16815260ff8416602082015266ffffffffffffff8516818401526001600160801b03861660608201526001600160401b03871660808201526001600160401b03881660a08201526135288a61ffff165f526101f060205260405f2090565b614681886146708b6001600160a01b03165f526101f160205260405f2090565b9061ffff1661ffff19825416179055565b5197889788959366ffffffffffffff9060ff60c097939a99956001600160a01b036001600160801b039661ffff60e08d019e168c521660208b01521660408901521660608701521660808501526001600160401b0380921660a085015216910152565b0390a16106946001601755565b905163a28d29a760e01b8152fd5b69ffffffffffffffffffff925016115f806145ac565b1192505f806145a3565b506001600160401b03808816908c1611614574565b50670fffffffffffffff6001600160401b0388161161456d565b506001600160401b038c1615614560565b506001600160401b03881615614559565b6001915014155f614539565b90506147a561479d8a6001600160a01b03165f526101f160205260405f2090565b5461ffff1690565b1615155f614526565b603e9150115f61450e565b82845163344c14a160e11b8152fd5b506001600160a01b036147f86147eb8b61ffff165f526101f060205260405f2090565b546001600160a01b031690565b1615156144fe565b806113db61480d926106f6565b5f6144ec565b9190916001600160801b03808094169116029182169182036141d657565b81518154602084015160408501517fffffffff000000000000000000000000000000000000000000000000000000009092166001600160a01b03939093169290921760a092831b74ff0000000000000000000000000000000000000000161760a89190911b7bffffffffffffff0000000000000000000000000000000000000000001617825560608301516106949360019093019261493c92916148fd906001600160801b031685906001600160801b03166fffffffffffffffffffffffffffffffff19825416179055565b612fc961491460808301516001600160401b031690565b855467ffffffffffffffff60801b191660809190911b67ffffffffffffffff60801b16178555565b77ffffffffffffffffffffffffffffffffffffffffffffffff6001600160c01b031983549260c01b169116179055565b908160609103126104fc57803561498281611429565b916040602083013561499381611429565b92013561499f81611429565b90565b6001600160401b031680156141d6575f190190565b5f198101919082116141d657565b919082039182116141d657565b9060405160c08101906001600160401b039181811083821117610709576106949260a091604052614a536001849766ffffffffffffff81546001600160a01b038116885260ff81881c16602089015260a81c1660408701520154916001600160801b03831660608601528260801c1660808501906001600160401b03169052565b60c01c910152565b9060a082029180830460a014901517156141d657565b818102929181159184041417156141d657565b908160209103126104fc575190565b8115614403570490565b6001600160801b0391821690821603919082116141d657565b15614abd57565b608460405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152fd5b91908260c09103126104fc578135614b3e81611429565b916020810135614b4d81611429565b916040820135614b5c81611429565b916060810135614b6b81611429565b9160a0608083013592013590565b6001600160a01b0391827f000000000000000000000000803bd6a0577c083c1ede82da455c8e69e697c878163014614d7d5782614bc381938293614bbb615671565b810190614b27565b98909216979691959290941693169116803b158015614d74575b8015614d6b575b8015614d62575b614d3857614cc295614c39614c5592614c1d614c71966001600160a01b03166001600160a01b03196004541617600455565b6001600160a01b03166001600160a01b03196006541617600655565b6001600160a01b03166001600160a01b03196007541617600755565b6001600160a01b03166001600160a01b03196005541617600555565b614c79610766565b915f83525f60208401525f60408401525f60608401525f60808401525f60a08401525f60c08401525f60e0840152816101008401526101208301525f6101408301525f5561514b565b5f8052600d6020527f81955a0a11e65eac625c29e8882660bae4e165a75d72780094acae8ece9a29ee55614cf4615692565b610694740200000000000000000000000000000000000000007fffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff6012541617601255565b60046040517fc8ba615c000000000000000000000000000000000000000000000000000000008152fd5b50813b15614beb565b50853b15614be4565b50823b15614bdd565b60046040517f19849362000000000000000000000000000000000000000000000000000000008152fd5b3d15614dd1573d90614db8826107c9565b91614dc66040519384610745565b82523d5f602084013e565b606090565b908160209103126104fc575161499f81611429565b63ffffffff8091169081146141d65760010190565b903590601e19813603018212156104fc57018035906001600160401b0382116104fc576020019181360383136104fc57565b90156142485790565b3561499f81610676565b3561499f81610ca2565b90929192836001116104fc5783116104fc57600101915f190190565b906020116104fc5790602090565b906040116104fc5760200190602090565b909392938483116104fc5784116104fc578101920390565b6001600160a01b03807f000000000000000000000000803bd6a0577c083c1ede82da455c8e69e697c878163014614ef657600754165f8060405192368285378336915af4903d91825f833e15614ef457f35bfd5b60046040517fa4a9ab33000000000000000000000000000000000000000000000000000000008152fd5b600260175414614f31576002601755565b606460405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152fd5b60010190816001116141d657565b5f1981146141d65760010190565b90614fa0600783511015615e62565b600782015190614fb4601d84511015615e62565b614fc2601d84015193615ca4565b929050929190565b65ffffffffffff81165f9081527ff3ea710bd913b11d46a8bb045be6b74773714e429efd3633381568c675975c16602052604090206101f2926001600160801b0391829081905416911601918183116141d6576106949361505c92604051946150328661070e565b16845260ff602085015260035f5260205260405f209065ffffffffffff165f5260205260405f2090565b815181546fffffffffffffffffffffffffffffffff19166001600160801b039190911617815590602001517fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff70ff0000000000000000000000000000000083549260801b169116179055565b9061ffff1691825f526101f291826020526001600160801b039182806151018460405f209065ffffffffffff165f5260205260405f2090565b5416911601928284116141d6576106949461505c93604051956151238761070e565b16855260ff60208601525f5260205260405f209065ffffffffffff165f5260205260405f2090565b60405160208101916151668382516001600160401b03169052565b60208101516001600160401b03166040830152604081015163ffffffff16606083015260608101516001600160401b0316608083015260808101516001600160401b031660a083015260a081015163ffffffff1660c083015260c081015160e083015260e08101516101009081840152810151610120908184015281015190610140918284015201516101609081830152815261520281610729565b51902090565b61521c610fcb82516001600160401b031690565b546152268261514b565b03615518575f9180515f5b8181106152705750505060e001510361524657565b60046040517f8c59c66d000000000000000000000000000000000000000000000000000000008152fd5b60ff61527c8285615cd7565b905016600281145f146153f95750601161529682846149c5565b106153cf576152a59083615d48565b94906153c36152ba825165ffffffffffff1690565b610a1e6020918285019461532161531361530a6152d9895161ffff1690565b936152fe604096878301966152f861095989516001600160401b031690565b916150c8565b5165ffffffffffff1690565b975161ffff1690565b91516001600160401b031690565b915195869485019788927fffff000000000000000000000000000000000000000000000000000000000000907fffffffffffff00000000000000000000000000000000000000000000000000006001600160c01b0319946031979487527f0200000000000000000000000000000000000000000000000000000000000000602088015260d01b16602186015260f01b16602784015260c01b1660298201520190565b51902093905b90615231565b60046040517fa9c58615000000000000000000000000000000000000000000000000000000008152fd5b6001036154ee57600f61540c82846149c5565b106153cf5761541b9083615cf6565b94906154e4615430825165ffffffffffff1690565b91610a1e61546c61545e6020936152fe858201976154586109598a516001600160401b031690565b90614fca565b94516001600160401b031690565b6040519485938401968791927fffffffffffff0000000000000000000000000000000000000000000000000000602f946001600160c01b03199385527f0100000000000000000000000000000000000000000000000000000000000000602086015260d01b16602184015260c01b1660278201520190565b51902093906153c9565b60046040517ffdbe1dce000000000000000000000000000000000000000000000000000000008152fd5b6004604051637ddbe9e960e11b8152fd5b6001600160a01b03809116805f52601360205265ffffffffffff908160405f2054169283156155585750505090565b80919293506011541682145f14615570575050505f90565b601254161461557c5790565b50600190565b6001600160801b0390818111615596571690565b608460405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201527f32382062697473000000000000000000000000000000000000000000000000006064820152fd5b1561560757565b608460405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152fd5b61568b60ff60165460081c1661568681615600565b615600565b6001601755565b61570061569d610786565b5f815260016020820152606460408201526402540be4006060820152670fffffffffffffff6080820152620186a060a082015260015f526101f06020527f8ef17085e6277aed8f6f01c714d30dd99bcfb12fc3881d7c54391ffa2a994b0e614831565b5f80526101f160205261573b7fb90953adcbf35a931ac053a7d0fe43fdd42a03d188a6bab41f6e6ed74a0821d2805461ffff19166001179055565b6040517ff1b24e81016b9f39e2290cf2a9303264a07534a569df7e6200a39573d7f26b0c81806157a7839490620186a060c060e0840193600181525f602082015260016040820152606460608201526402540be4006080820152670fffffffffffffff60a08201520152565b0390a1600460206157c3610f276006546001600160a01b031690565b60405192838092631f209df760e11b82525afa908115611379576158e3916001600160a01b03915f916158e8575b50166158686157fe610786565b6001600160a01b038316815260016020820152620f4240604082015260016060820152670fffffffffffffff6080820152620f424060a082015260035f526101f06020527fdcc7741fcb270a804667781042ff853c8745e471ecf5da3709c83259ac436883614831565b615895615887826001600160a01b03165f526101f160205260405f2090565b805461ffff19166003179055565b604051918291829190916001600160a01b0360e0820193600383521660208201526001604082015260c0620f42409182606082015260016080820152670fffffffffffffff60a08201520152565b0390a1565b615901915060203d6020116127e2576127d38183610745565b5f6157f1565b6159c2915f806001600160a01b03604051946159778661596960209a8b8301987fa9059cbb000000000000000000000000000000000000000000000000000000008a5260248401602090939291936001600160a01b0360408201951681520152565b03601f198101885287610745565b1692604051946159868661070e565b8786527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656488870152519082855af16159bc614da7565b91615ef8565b805190828215928315615a45575b505050156159db5750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b615a559350820181019101614408565b5f82816159d0565b93906fffffffffffffffffffffffffffffffff1992937fffff0000000000000000000000000000000000000000000000000000000000009160405195602087019788527fffffffffffff0000000000000000000000000000000000000000000000000000809260d01b16604088015260d01b16604686015260f01b16604c84015260801b16604e820152603e8152606081018181106001600160401b038211176107095760405251902090565b359060208110615b18575090565b5f199060200360031b1b1690565b5f9160a08106615b535760a081049160068311615c4f57905f915b838310615b645750505049615b535790565b6004604051630aa47a8360e11b8152fd5b909193615b8c615b7386614a5b565b615b84615b7f88614f75565b614a5b565b908585614e8a565b615ba1615b9b82849594614e6b565b90615b0a565b91615baf615b9b8386614e79565b918849938415615b5357615bc9615bee9260019787615dc3565b6040948551928391602096878401948591606093918352602083015260408201520190565b0391615c02601f1993848101835282610745565b5190209289615c1a575050509050945b019190615b41565b615c3a615c46939551948592830196879091604092825260208201520190565b03908101835282610745565b51902094615c12565b6040517fd8ae856600000000000000000000000000000000000000000000000000000000815260048101849052602490fd5b6006820192918381116141d657600691615c9e8582511015615e62565b01015190565b60269190601e600891615c9e8582511015615e62565b6008820192918381116141d657600891615c9e8582511015615e62565b919060018101908181116141d657615cef919361428c565b5160f81c90565b6001600160401b039291615d3e65ffffffffffff60405193615d178561070e565b5f8552615d33615d2d60208701955f8752614f83565b82615c81565b929092168552615cba565b9490941690529190565b919060405190606082016001600160401b0394838210868311176107095765ffffffffffff916040525f845260208401905f8252615d8f615d2d60408701955f8752614f83565b93909316855260028301918284116141d65761ffff6002615d3e95615db78686511015615e62565b84010151169052615cba565b5f9192908291615df660408096838251948592602084019788528484013781018683820152036020810184520182610745565b5190600a5afa615e04614da7565b9015615e525781818051810103126104fc578101517f8c1258acd66282b7ccc627f7f65e27faac425bfd0001a40100000000ffffffff01615e425750565b600490516358c6239f60e11b8152fd5b600482516358c6239f60e11b8152fd5b15615e6957565b606460405162461bcd60e51b815260206004820152600160248201527f53000000000000000000000000000000000000000000000000000000000000006044820152fd5b15615eb457565b606460405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b91929015615f185750815115615f0c575090565b61499f903b1515615ead565b825190915015615f2b5750805190602001fd5b604460209160405192839162461bcd60e51b83528160048401528051918291826024860152018484015e5f828201840152601f01601f19168101030190fdfea26469706673582212204fc5262796ab368153795eb1d901315248b207f5b57e06a37b96ad813f57aea264736f6c63430008190033

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

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.