ETH Price: $1,814.38 (+10.96%)

Contract

0xb1a49C54192Ea59B233200eA38aB56650Dfb448C
 
Transaction Hash
Method
Block
From
To
Market Order By ...173485032025-04-01 11:27:0122 days ago1743506821IN
0xb1a49C54...50Dfb448C
0 ETH0.000002870.00124286
Market Order By ...173484872025-04-01 11:26:2922 days ago1743506789IN
0xb1a49C54...50Dfb448C
0 ETH0.000002680.00124368
Withdraw ERC20144632942025-01-24 16:33:2388 days ago1737736403IN
0xb1a49C54...50Dfb448C
0 ETH0.00000020.00330618
Withdraw ERC20144632682025-01-24 16:32:3188 days ago1737736351IN
0xb1a49C54...50Dfb448C
0 ETH0.000000180.00331587
Withdraw ERC20144631832025-01-24 16:29:4188 days ago1737736181IN
0xb1a49C54...50Dfb448C
0 ETH0.000000350.00345614
Withdraw ERC20144631482025-01-24 16:28:3188 days ago1737736111IN
0xb1a49C54...50Dfb448C
0 ETH0.000000360.00348424
Market Order By ...144353762025-01-24 1:02:4789 days ago1737680567IN
0xb1a49C54...50Dfb448C
0 ETH0.000014630.01075965
Market Order By ...144339712025-01-24 0:15:5789 days ago1737677757IN
0xb1a49C54...50Dfb448C
0 ETH0.000001980.00166726
Market Order By ...144339612025-01-24 0:15:3789 days ago1737677737IN
0xb1a49C54...50Dfb448C
0 ETH0.000003520.00162945
Market Order By ...124829542024-12-09 20:22:03134 days ago1733775723IN
0xb1a49C54...50Dfb448C
0 ETH0.00000160.00181855
Market Order By ...124828892024-12-09 20:19:53134 days ago1733775593IN
0xb1a49C54...50Dfb448C
0 ETH0.000003480.00180873
Market Order By ...123630392024-12-07 1:44:53137 days ago1733535893IN
0xb1a49C54...50Dfb448C
0 ETH0.000000320.00043924
Market Order By ...123630232024-12-07 1:44:21137 days ago1733535861IN
0xb1a49C54...50Dfb448C
0 ETH0.000000310.00044064
Market Order By ...123630172024-12-07 1:44:09137 days ago1733535849IN
0xb1a49C54...50Dfb448C
0 ETH0.000000440.00042802
Market Order By ...123627862024-12-07 1:36:27137 days ago1733535387IN
0xb1a49C54...50Dfb448C
0 ETH0.000000710.00049816
Market Order By ...123627792024-12-07 1:36:13137 days ago1733535373IN
0xb1a49C54...50Dfb448C
0 ETH0.000000720.00050595
Market Order By ...123627732024-12-07 1:36:01137 days ago1733535361IN
0xb1a49C54...50Dfb448C
0 ETH0.000000740.00051038
Market Order By ...123627582024-12-07 1:35:31137 days ago1733535331IN
0xb1a49C54...50Dfb448C
0 ETH0.000000940.00052285
Market Order By ...123626972024-12-07 1:33:29137 days ago1733535209IN
0xb1a49C54...50Dfb448C
0 ETH0.000000220.00052876
Market Order By ...120036232024-11-28 18:04:21145 days ago1732817061IN
0xb1a49C54...50Dfb448C
0 ETH0.000000430.00033602
Market Order By ...113857322024-11-14 10:47:59160 days ago1731581279IN
0xb1a49C54...50Dfb448C
0 ETH0.000000230.00500517
Market Order By ...112723782024-11-11 19:49:31162 days ago1731354571IN
0xb1a49C54...50Dfb448C
0 ETH0.000001060.00083242
Market Order By ...112469202024-11-11 5:40:55163 days ago1731303655IN
0xb1a49C54...50Dfb448C
0 ETH0.000004530.00196714
Market Order By ...109979292024-11-05 11:21:13169 days ago1730805673IN
0xb1a49C54...50Dfb448C
0 ETH0.000004410.00340025
Market Order By ...109207072024-11-03 16:27:09170 days ago1730651229IN
0xb1a49C54...50Dfb448C
0 ETH0.000000880.00371165
View all transactions

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
173485032025-04-01 11:27:0122 days ago1743506821
0xb1a49C54...50Dfb448C
0.00000039 ETH
173484872025-04-01 11:26:2922 days ago1743506789
0xb1a49C54...50Dfb448C
0.00000225 ETH
144339612025-01-24 0:15:3789 days ago1737677737
0xb1a49C54...50Dfb448C
0.00001144 ETH
124828892024-12-09 20:19:53134 days ago1733775593
0xb1a49C54...50Dfb448C
0.000007 ETH
123627582024-12-07 1:35:31137 days ago1733535331
0xb1a49C54...50Dfb448C
0.00000405 ETH
120764162024-11-30 10:30:47144 days ago1732962647
0xb1a49C54...50Dfb448C
0.00001056 ETH
120763372024-11-30 10:28:09144 days ago1732962489
0xb1a49C54...50Dfb448C
0.00001033 ETH
120762032024-11-30 10:23:41144 days ago1732962221
0xb1a49C54...50Dfb448C
0.00001055 ETH
120761852024-11-30 10:23:05144 days ago1732962185
0xb1a49C54...50Dfb448C
0.00001052 ETH
120761722024-11-30 10:22:39144 days ago1732962159
0xb1a49C54...50Dfb448C
0.00001048 ETH
120761392024-11-30 10:21:33144 days ago1732962093
0xb1a49C54...50Dfb448C
0.00001054 ETH
120761062024-11-30 10:20:27144 days ago1732962027
0xb1a49C54...50Dfb448C
0.0000105 ETH
120761002024-11-30 10:20:15144 days ago1732962015
0xb1a49C54...50Dfb448C
0.00001062 ETH
120760662024-11-30 10:19:07144 days ago1732961947
0xb1a49C54...50Dfb448C
0.00001056 ETH
120760462024-11-30 10:18:27144 days ago1732961907
0xb1a49C54...50Dfb448C
0.00001051 ETH
120760182024-11-30 10:17:31144 days ago1732961851
0xb1a49C54...50Dfb448C
0.00001056 ETH
120760092024-11-30 10:17:13144 days ago1732961833
0xb1a49C54...50Dfb448C
0.00001054 ETH
120759982024-11-30 10:16:51144 days ago1732961811
0xb1a49C54...50Dfb448C
0.00001054 ETH
120759922024-11-30 10:16:39144 days ago1732961799
0xb1a49C54...50Dfb448C
0.00001055 ETH
120759712024-11-30 10:15:57144 days ago1732961757
0xb1a49C54...50Dfb448C
0.00001052 ETH
120759632024-11-30 10:15:41144 days ago1732961741
0xb1a49C54...50Dfb448C
0.00001055 ETH
120759542024-11-30 10:15:23144 days ago1732961723
0xb1a49C54...50Dfb448C
0.00001055 ETH
120759192024-11-30 10:14:13144 days ago1732961653
0xb1a49C54...50Dfb448C
0.00001056 ETH
120759052024-11-30 10:13:45144 days ago1732961625
0xb1a49C54...50Dfb448C
0.00001052 ETH
120757682024-11-30 10:09:11144 days ago1732961351
0xb1a49C54...50Dfb448C
0.00001049 ETH
View All Internal Transactions

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
BlastMangrove

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
File 1 of 28 : BlastMangrove.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.10;

import {Mangrove} from "../../../core/Mangrove.sol";
import {IBlast} from "../interfaces/IBlast.sol";
import {IBlastPoints} from "../interfaces/IBlastPoints.sol";

/// @title BlastMangrove
/// @notice Mangrove extension that adds support for Blast yield and points.
/// @dev ETH yield MUST NOT use automatic mode as governance has no way to extract the yield.
///   This is a security measure, as governance would otherwise be able to steal users'
///   provisions which are stored on Mangrove.
/// @dev As for ETH yield, gas fees MUST NOT be stored on the contract.
//    Automatic claiming of gas fees is not currently supported, so not an issue in practice.
/// @dev WETH and USDB can safely use automatic mode as fees are already stored on the contract
///   and extractable by governance.
contract BlastMangrove is Mangrove {
  constructor(
    address governance,
    uint gasprice,
    uint gasmax,
    IBlast blastContract,
    address blastGovernor,
    IBlastPoints blastPointsContract,
    address blastPointsOperator
  ) Mangrove(governance, gasprice, gasmax) {
    // Ensure yield and gas fees are claimable by `blastGovernor`
    // NB: ETH yield MUST NOT use automatic mode as governance has no way to extract the yield.
    blastContract.configureClaimableYield();

    blastContract.configureClaimableGas();
    blastContract.configureGovernor(blastGovernor);

    blastPointsContract.configurePointsOperator(blastPointsOperator);
  }
}

File 2 of 28 : Mangrove.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.10;

import "@mgv/src/core/MgvLib.sol";

import {MgvOfferMaking} from "./MgvOfferMaking.sol";
import {MgvOfferTakingWithPermit} from "./MgvOfferTakingWithPermit.sol";
import {MgvAppendix} from "@mgv/src/core/MgvAppendix.sol";
import {MgvGovernable} from "@mgv/src/core/MgvGovernable.sol";

/* <a id="Mangrove"></a> The `Mangrove` contract inherits both the maker and taker functionality. It also deploys `MgvAppendix` when constructed. */
contract Mangrove is MgvOfferTakingWithPermit, MgvOfferMaking {
  address internal immutable APPENDIX;

  constructor(address governance, uint gasprice, uint gasmax) MgvOfferTakingWithPermit("Mangrove") {
    unchecked {
      emit NewMgv();

      APPENDIX = address(new MgvAppendix());

      /* Set initial gasprice, gasmax, recursion depth and max gasreq for failing offers.  See `MgvAppendix` for why this happens through a delegatecall. */
      bool success;
      (success,) = APPENDIX.delegatecall(abi.encodeCall(MgvGovernable.setGasprice, (gasprice)));
      require(success, "mgv/ctor/gasprice");
      (success,) = APPENDIX.delegatecall(abi.encodeCall(MgvGovernable.setGasmax, (gasmax)));
      require(success, "mgv/ctor/gasmax");
      (success,) =
        APPENDIX.delegatecall(abi.encodeCall(MgvGovernable.setMaxRecursionDepth, (INITIAL_MAX_RECURSION_DEPTH)));
      require(success, "mgv/ctor/maxRecursionDepth");
      (success,) = APPENDIX.delegatecall(
        abi.encodeCall(
          MgvGovernable.setMaxGasreqForFailingOffers, (INITIAL_MAX_GASREQ_FOR_FAILING_OFFERS_MULTIPLIER * gasmax)
        )
      );
      require(success, "mgv/ctor/maxGasreqForFailingOffers");
      /* Initially, governance is open to anyone so that Mangrove can set its own default parameters. After that, governance is set to the `governance` constructor argument. */
      (success,) = APPENDIX.delegatecall(abi.encodeCall(MgvGovernable.setGovernance, (governance)));
      require(success, "mgv/ctor/governance");
    }
  }

  /* Fallback to `APPENDIX` if function selector is unknown. */
  fallback(bytes calldata callData) external returns (bytes memory) {
    (bool success, bytes memory res) = APPENDIX.delegatecall(callData);
    if (success) {
      return res;
    } else {
      assembly ("memory-safe") {
        revert(add(res, 32), mload(res))
      }
    }
  }
}

File 3 of 28 : IBlast.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.10;

enum YieldMode {
  AUTOMATIC,
  VOID,
  CLAIMABLE
}

enum GasMode {
  VOID,
  CLAIMABLE
}

interface IBlast {
  // configure
  function configureContract(address contractAddress, YieldMode _yield, GasMode gasMode, address governor) external;
  function configure(YieldMode _yield, GasMode gasMode, address governor) external;

  // base configuration options
  function configureClaimableYield() external;
  function configureClaimableYieldOnBehalf(address contractAddress) external;
  function configureAutomaticYield() external;
  function configureAutomaticYieldOnBehalf(address contractAddress) external;
  function configureVoidYield() external;
  function configureVoidYieldOnBehalf(address contractAddress) external;
  function configureClaimableGas() external;
  function configureClaimableGasOnBehalf(address contractAddress) external;
  function configureVoidGas() external;
  function configureVoidGasOnBehalf(address contractAddress) external;
  function configureGovernor(address _governor) external;
  function configureGovernorOnBehalf(address _newGovernor, address contractAddress) external;

  // claim yield
  function claimYield(address contractAddress, address recipientOfYield, uint amount) external returns (uint);
  function claimAllYield(address contractAddress, address recipientOfYield) external returns (uint);

  // claim gas
  function claimAllGas(address contractAddress, address recipientOfGas) external returns (uint);
  function claimGasAtMinClaimRate(address contractAddress, address recipientOfGas, uint minClaimRateBips)
    external
    returns (uint);
  function claimMaxGas(address contractAddress, address recipientOfGas) external returns (uint);
  function claimGas(address contractAddress, address recipientOfGas, uint gasToClaim, uint gasSecondsToConsume)
    external
    returns (uint);

  // read functions
  function readClaimableYield(address contractAddress) external view returns (uint);
  function readYieldConfiguration(address contractAddress) external view returns (uint8);
  function readGasParams(address contractAddress)
    external
    view
    returns (uint etherSeconds, uint etherBalance, uint lastUpdated, GasMode);

  function isGovernor(address contractAddress) external view returns (bool);
  function governorNotSet(address contractAddress) external view returns (bool);
  function isAuthorized(address contractAddress) external view returns (bool);
}

File 4 of 28 : IBlastPoints.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.10;

/// @title IBlastPoints
/// @notice Interface to configure Blast Points
/// @dev Copied from the following sources on 2024-02-27:
///   - https://blastpublic.notion.site/PUBLIC-Blast-Mainnet-Points-API-f8abea9d6e67417890d4a300ecbe5827
///   - https://testnet.blastscan.io/address/0x2fc95838c71e76ec69ff817983BFf17c710F34E0/contract/168587773/code
interface IBlastPoints {
  /// @notice Set the Blast Points operator for the calling contract.
  /// @param operator The address of the Blast Points operator.
  /// @dev Note that the operator address should be an EOA whose private key is accessible
  ///   to an internet connected server. We recommended setting this value to a distinct
  ///   address so that other admin responsibilities are not co-mingled with this address,
  ///   whose key must live on a hot server.
  function configurePointsOperator(address operator) external;

  function isOperator(address contractAddress) external view returns (bool);

  function isAuthorized(address contractAddress) external view returns (bool);

  function configurePointsOperatorOnBehalf(address contractAddress, address newOperator) external;

  function readStatus(address contractAddress) external view returns (address operator, bool isBanned, uint codeLength);
}

File 5 of 28 : MgvLib.sol
// SPDX-License-Identifier: MIT

/* `MgvLib` contains data structures returned by external calls to Mangrove and the interfaces it uses for its own external calls. */

pragma solidity ^0.8.10;

import "@mgv/src/preprocessed/Structs.post.sol";
import {IERC20} from "@mgv/lib/IERC20.sol";
import {Density, DensityLib} from "@mgv/lib/core/DensityLib.sol";
import "@mgv/lib/core/TickTreeLib.sol";
import "@mgv/lib/core/TickLib.sol";

/* `OLKey` (for "OfferListKey") contains the information that characterizes an offer list:
  * `outbound_tkn`, token that goes from the maker to the taker (to remember the direction, imagine the token as going out of the Mangrove offer, towards the taker)
  * `inbound_tkn`, token that goes from the taker to the maker (to remember the direction, imagine the token as going into Mangrove from the taker)
  * `tickSpacing`, how many ticks should be jumped between available price points. More volatile outbound/inbound pairs should have a larger `tickSpacing`. */
struct OLKey {
  address outbound_tkn;
  address inbound_tkn;
  uint tickSpacing;
}

/* Globally enable `olKey.method(...)` */
using OLLib for OLKey global;

library OLLib {
  /* The id of an `OLKey` should be `keccak256(abi.encode(olKey))`
  To save gas, `id()` directly hashes the memory (which matches the ABI encoding; there is a fuzz test on that). If the memory layout changes, this function must be updated. */
  function hash(OLKey memory olKey) internal pure returns (bytes32 _id) {
    assembly ("memory-safe") {
      _id := keccak256(olKey, 96)
    }
  }

  /* Creates a flipped copy of the `olKey` with same `tickSpacing`. */
  function flipped(OLKey memory olKey) internal pure returns (OLKey memory) {
    return OLKey(olKey.inbound_tkn, olKey.outbound_tkn, olKey.tickSpacing);
  }

  /* Convert `tick` to bin according to `olKey.tickSpacing` */
  function nearestBin(OLKey memory olKey, Tick _tick) internal pure returns (Bin) {
    return _tick.nearestBin(olKey.tickSpacing);
  }

  /* Convert `bin` to `tick` according to `olKey.tickSpacing` */
  function tick(OLKey memory olKey, Bin bin) internal pure returns (Tick) {
    return bin.tick(olKey.tickSpacing);
  }
}

/* # Structs
The structs defined in `structs.js` have their counterpart as solidity structs that are easy to manipulate for outside contracts / callers of view functions. */

library MgvLib {
  /*
   Some miscellaneous data types useful to `Mangrove` and external contracts */
  //+clear+

  /* `SingleOrder` holds data about an order-offer match in a struct. Used by `marketOrder` (and some of its nested functions) to avoid stack too deep errors. It is used in market order and cleaning. */
  struct SingleOrder {
    OLKey olKey;
    uint offerId;
    /* The `offer` given to the maker will be cleaned of `prev`/`next` pointers. */
    Offer offer;
    /* `takerWants`/`takerGives` mutate over execution. Initially the `wants`/`gives` from the taker's pov, then actual `wants`/`gives` adjusted by offer's price and volume. */
    uint takerWants;
    uint takerGives;
    /* `offerDetail` is only populated when necessary. */
    OfferDetail offerDetail;
    Global global;
    /* The `local` given to the maker will be cleaned of tick tree information. */
    Local local;
  }

  /* <a id="MgvLib/OrderResult"></a> `OrderResult` holds additional data for the maker and is given to them _after_ they fulfilled an offer. It gives them their own returned data from the previous call and an `mgvData` specifying whether Mangrove encountered an error. */

  struct OrderResult {
    /* `makerData` holds a message that was either returned by the maker or passed as revert message at the end of the trade execution*/
    bytes32 makerData;
    /* `mgvData` is an [internal Mangrove status code](#MgvOfferTaking/statusCodes) code. */
    bytes32 mgvData;
  }

  /* `CleanTarget` holds data about an offer that should be cleaned, i.e. made to fail by executing it with the specified volume. It is used in `MgvOfferTaking.cleanByImpersonation`. */
  struct CleanTarget {
    uint offerId;
    Tick tick;
    uint gasreq;
    uint takerWants;
  }
}

/* # Events
The events emitted are listed here: */
interface HasMgvEvents {
  /* 
    Events in solidity is a hard thing to do in a optimal way. If you look at it as a purely gas efficient issue, you want to emit as few events as possible and with as few fields as possible. But as events also has to be usable for an off chain user, then doing this is not always the best solution.

    We tried to list the main forces that drive which events and data to emit:

    1. Use as little gas as possible.
    2. An indexer should be able to keep track of the state of Mangrove.
    3. Doing RPC calls directly, should be able to find offers and other information based on offer list, maker, taker, offer id, etc.

    These forces are in conflict and it is impossible to find a solution that is optimal for all three.
    The following events are therefore an attempt to balance the trade-offs.
  */

  /* ### Mangrove Creation

  Emitted at the creation of the new Mangrove contract */
  event NewMgv();

  /* ### Mangrove adds or removes wei from `maker`'s account */
  /* 
    * Credit event occurs when an offer is removed from Mangrove or when the `fund` function is called
      This is emitted when a user's account on Mangrove is credited with some native funds, to be used as provision for offers.

      It emits the `maker`'s address and the `amount` credited

      The `maker` address is indexed so that we can filter on it when doing RPC calls.

      These are the scenarios where it can happen:

      - Fund Mangrove directly
      - Fund Mangrove when posting an offer
      - When updating an offer
        - Funding Mangrove or
        - the updated offer needs less provision, than it already has. Meaning the user gets credited the difference.
      - When retracting an offer and deprovisioning it. Meaning the user gets credited the provision that was locked by the offer.
      - When an offer fails. The remaining provision gets credited back to the maker

      A challenge for an indexer is to know how much provision each offer locks. With the current events, an indexer is going to have to know the liveness, gasreq and gasprice of the offer. If the offer is not live, then also know if it has been deprovisioned. And know what the gasbase of the offer list was when the offer was posted. With this information an indexer can calculate the exact provision locked by the offer.

      The indexer also cannot deduce what scenario the credit event happened. E.g., we don't know if the credit event happened because an offer failed or because the user simply funded Mangrove.
  */
  event Credit(address indexed maker, uint amount);
  /* 
    * Debit event occurs when an offer is posted or when the `withdraw` function is called 
      This is emitted when a user's account on Mangrove is debited with some native funds.

      It emits the `maker`'s address and the `amount` debited.

      The `maker` address is indexed so that we can filter on it when doing RPC calls.

      These are the scenarios where it can happen:

      - Withdraw funds from Mangrove directly
      - When posting an offer. The user gets debited the provision that the offer locks.
      - When updating an offer and it requires more provision that it already has. Meaning the user gets debited the difference.

      Same challenges as for Credit
  */
  event Debit(address indexed maker, uint amount);

  /* ### Mangrove reconfiguration */
  /*  
  This event is emitted when an offer list is activated or deactivated. Meaning one half of a market is opened.

  It emits the `olKeyHash` and the boolean `value`. By emitting this, an indexer will be able to keep track of what offer lists are active and what their hash is.

  The `olKeyHash` and both token addresses are indexed, so that we can filter on it when doing RPC calls.
  */
  event SetActive(
    bytes32 indexed olKeyHash, address indexed outbound_tkn, address indexed inbound_tkn, uint tickSpacing, bool value
  );

  /*
  This event is emitted when the fee of an offer list is changed.

  It emits the `olKeyHash` and the `value`. By emitting this, an indexer will be able to keep track of what fee each offer list has.

  The `olKeyHash` is indexed, so that we can filter on it when doing RPC calls.
  */

  event SetFee(bytes32 indexed olKeyHash, uint value);

  /*
  This event is emitted when the gasbase of an offer list is changed.

  It emits the `olKeyHash` and the `offer_gasbase`. By emitting this, an indexer will be able to keep track of what gasbase each offer list has.

  The `olKeyHash` is indexed, so that we can filter on it when doing RPC calls.
  */
  event SetGasbase(bytes32 indexed olKeyHash, uint offer_gasbase);

  /*
  This event is emitted when the governance of Mangrove is set.

  It emits the `governance` address. By emitting this, an indexer will be able to keep track of what governance address Mangrove has.

  No fields are indexed as there is no need for RPC calls to filter on this.
  */
  event SetGovernance(address value);

  /*
  This event is emitted when the monitor address of Mangrove is set. Be aware that the address for Monitor is also the address for the oracle.

  It emits the `monitor` / `oracle` address. By emitting this, an indexer will be able to keep track of what monitor/oracle address Mangrove use.

  No fields are indexed as there is no need for RPC calls to filter on this.
  */
  event SetMonitor(address value);

  /*
  This event is emitted when the configuration for whether to use an oracle or not is set.

  It emits the `useOracle`, which is a boolean value, that controls whether or not Mangrove reads its `gasprice` and `density` from an oracle or uses its own local values. By emitting this, an indexer will be able to keep track of if Mangrove is using an oracle.

  No fields are indexed as there is no need for RPC calls to filter on this.
  */
  event SetUseOracle(bool value);

  /*
  This event is emitted when the configuration for notify on Mangrove is set.

  It emits a boolean value, to tell whether or not notify is active. By emitting this, an indexer will be able to keep track of whether or not Mangrove notifies the Monitor/Oracle when and offer is taken, either successfully or not.

  No fields are indexed as there is no need for RPC calls to filter on this.
  */
  event SetNotify(bool value);

  /*
  This event is emitted when the gasmax of Mangrove is set.

  It emits the `gasmax`. By emitting this, an indexer will be able to keep track of what gasmax Mangrove has. Read more about Mangroves gasmax on [docs.mangrove.exchange](https://docs.mangrove.exchange)

  No fields are indexed as there is no need for RPC calls to filter on this.
  */
  event SetGasmax(uint value);

  /*
  This event is emitted when the density of an offer list is changed.

  It emits the `olKeyHash` and the `density`. By emitting this, an indexer will be able to keep track of what density each offer list has.

  The `olKeyHash` is indexed, so that we can filter on it when doing RPC calls.
  */
  event SetDensity96X32(bytes32 indexed olKeyHash, uint value);

  /*
  This event is emitted when the max recursion depth of Mangrove is set.

  It emits the max depth `value`. By emitting this, an indexer will be able to keep track of what max recursion depth Mangrove has. Read more about Mangroves max recursion depth on [docs.mangrove.exchange](https://docs.mangrove.exchange)
  */

  event SetMaxRecursionDepth(uint value);

  /*
  This event is emitted when the max gasreq for failing offers of Mangrove is set.

  It emits the max gasreq for failing offers `value`. By emitting this, an indexer will be able to keep track of what max gasreq for failing offers Mangrove has. Read more about Mangroves max gasreq for failing offers on [docs.mangrove.exchange](https://docs.mangrove.exchange)
  */
  event SetMaxGasreqForFailingOffers(uint value);

  /*
  This event is emitted when the gasprice of Mangrove is set.

  It emits the `gasprice`. By emitting this, an indexer will be able to keep track of what gasprice Mangrove has. Read more about Mangroves gasprice on [docs.mangrove.exchange](https://docs.mangrove.exchange)

  No fields are indexed as there is no need for RPC calls to filter on this.
  */
  event SetGasprice(uint value);
  /* ### Clean order execution */
  /*
  This event is emitted when a user tries to clean offers on Mangrove, using the build in clean functionality.

  It emits the `olKeyHash`, the `taker` address and `offersToBeCleaned`, which is the number of offers that should be cleaned. By emitting this event, an indexer can save what `offer list` the user is trying to clean and what `taker` is being used. 
  This way it can keep a context for the following events being emitted (Just like `OrderStart`). The `offersToBeCleaned` is emitted so that an indexer can keep track of how many offers the user tried to clean. 
  Combining this with the amount of `OfferFail` events emitted, then an indexer can know how many offers the user actually managed to clean. This could be used for analytics.

  The fields `olKeyHash` and `taker` are indexed, so that we can filter on them when doing RPC calls.
  */
  event CleanStart(bytes32 indexed olKeyHash, address indexed taker, uint offersToBeCleaned);

  /*
  This event is emitted when a Clean operation is completed.

  It does not emit any fields. This event is only needed in order to know that the clean operation is completed. This way an indexer can know exactly in what context we are in. 
  It could emit the total bounty received, but in order to know who got the bounty, an indexer would still be needed or we would have to emit the taker address as well, in order for an RPC call to find the data. 
  But an indexer would still be able to find this info, by collecting all the previous events. So we do not emit the bounty.
  */
  event CleanComplete();

  /* ### Market order execution */
  /*
  This event is emitted when a market order is started on Mangrove.

  It emits the `olKeyHash`, the `taker`, the `maxTick`, `fillVolume` and `fillWants`.

  The fields `olKeyHash` and `taker` are indexed, so that we can filter on them when doing RPC calls.

  By emitting this an indexer can keep track of what context the current market order is in. 
  E.g. if a user starts a market order and one of the offers taken also starts a market order, then we can in an indexer have a stack of started market orders and thereby know exactly what offer list the order is running on and the taker.

  By emitting `maxTick`, `fillVolume` and `fillWants`, we can now also know how much of the market order was filled and if it matches the price given. See OrderComplete for more.
  */
  event OrderStart(bytes32 indexed olKeyHash, address indexed taker, Tick maxTick, uint fillVolume, bool fillWants);

  /*
  This event is emitted when a market order is finished.

  It emits `olKeyHash`, the `taker` and the total `fee paid`. We need this event, in order to know that a market order is completed. This way an indexer can know exactly in what context we are in.
  The total `fee paid` for that market order, is needed, as we do not emit this any other places. The fields `olKeyHash` and `taker` is not needed for an indexer, but they are emitted and indexed in order for RPC calls to filter and find the fee.
  */
  event OrderComplete(bytes32 indexed olKeyHash, address indexed taker, uint fee);

  /* ### Offer execution */
  /*
  This event is emitted when an offer is successfully taken. Meaning both maker and taker has gotten their funds.

  It emits the `olKeyHash`, `taker` address, the `offerId` and the `takerWants` and `takerGives` that the offer was taken at. 
  Just as for `OfferFail`, the `olKeyHash` and `taker` are not needed for an indexer to work, but are needed for RPC calls. 
  `olKeyHash` `taker` and `id` are indexed so that we can filter on them when doing RPC calls. As `maker` can be a strategy and not the actual owner, then we chose not to emit it here and to mark the field `id` indexed, 
  the strat should emit the relation between maker and offerId. This way you can still by RPC calls find the relevant offer successes
  So they are emitted and indexed for that reason. Just like `OfferFail` this event is emitted during posthook end, so it is emitted in reverse order compared to what order they are taken. 
  This event could be emitted at offer execution, but for consistency we emit it at posthook end.

  If the posthook of the offer fails. Then we emit `OfferSuccessWithPosthookData` instead of just `OfferSuccess`. This event has one extra field, which is the reason for the posthook failure. 
  By emitting the posthook data, an indexer can keep track of the reason posthook fails, this could for example be used for analytics.

  By emitting `offerId`, `wants` and `gives`, an indexer can keep track of whether the offer was partially or fully taken, by looking at the what the offer was posted at.
  */
  event OfferSuccess(
    bytes32 indexed olKeyHash, address indexed taker, uint indexed id, uint takerWants, uint takerGives
  );

  event OfferSuccessWithPosthookData(
    bytes32 indexed olKeyHash,
    address indexed taker,
    uint indexed id,
    uint takerWants,
    uint takerGives,
    bytes32 posthookData
  );

  /*
  This event is emitted when an offer fails, because of a maker error.

  It emits `olKeyHash`, `taker`, the `offerId`, the offers `takerWants`, `takerGives`, `penalty` and the `reason` for failure. 
  `olKeyHash` and `taker` are all fields that we do not need, in order for an indexer to work, as an indexer will be able the get that info from the former `OrderStart` and `OfferWrite` events. 
  But in order for RPC call to filter on this, we need to emit them. 
  `olKeyHash` `taker` and `id` are indexed so that we can filter on them when doing RPC calls. As `maker` can be a strategy and not the actual owner, then we chose to not emit it here and to mark the field `id` indexed, 
  the strat should emit the relation between maker and offerId. This way you can still by RPC calls find the relevant offer successes

  If the posthook of the offer fails. Then we emit `OfferFailWithPosthookData` instead of just `OfferFail`. 
  This event has one extra field, which is the reason for the posthook failure. By emitting the posthook data, an indexer can keep track of the reason posthook fails, this could for example be used for analytics.

  This event is emitted during posthook end, we wait to emit this event to the end, because we need the information of `penalty`, which is only available at the end of the posthook. 
  This means that `OfferFail` events are emitted in reverse order, compared to what order they are taken. This is due to the way we handle posthooks. The same goes for `OfferSuccess`.

  By emitting this event, an indexer can keep track of, if an offer failed and thereby if the offer is live. 
  By emitting the `takerWants` and `takerGives` that the offer was taken with, then an indexer can keep track of these amounts, which could be useful for e.g. strategy manager, to know if their offers fail at a certain amount.
  */
  event OfferFail(
    bytes32 indexed olKeyHash,
    address indexed taker,
    uint indexed id,
    uint takerWants,
    uint takerGives,
    uint penalty,
    // `mgvData` may only be `"mgv/makerRevert"`, `"mgv/makerTransferFail"` or `"mgv/makerReceiveFail"`
    bytes32 mgvData
  );

  event OfferFailWithPosthookData(
    bytes32 indexed olKeyHash,
    address indexed taker,
    uint indexed id,
    uint takerWants,
    uint takerGives,
    uint penalty,
    // `mgvData` may only be `"mgv/makerRevert"`, `"mgv/makerTransferFail"` or `"mgv/makerReceiveFail"`
    bytes32 mgvData,
    bytes32 posthookData
  );

  /* 
  ### After `permit` and `approve` 
    This is emitted when a user permits another address to use a certain amount of its funds to do market orders, or when a user revokes another address to use a certain amount of its funds.

    Approvals are based on the pair of outbound and inbound token. Be aware that it is not offer list bases, as an offer list also holds the tick spacing.

    We emit `outbound` token, `inbound` token, `owner`, msg.sender (`spender`), `value`. Where `owner` is the one who owns the funds, `spender` is the one who is allowed to use the funds and `value` is the amount of funds that is allowed to be used.

    Outbound, inbound and owner is indexed, this way one can filter on these fields when doing RPC calls. If we could somehow combine outbound and inbound into one field, then we could also index the spender.
  */
  event Approval(
    address indexed outbound_tkn, address indexed inbound_tkn, address indexed owner, address spender, uint value
  );

  /* ### Mangrove closure */
  event Kill();

  /* ### An offer was created or updated.
  This event is emitted when an offer is posted on Mangrove.

  It emits the `olKeyHash`, the `maker` address, the `tick`, the `gives`, the `gasprice`, `gasreq` and the offers `id`.

  By emitting the `olKeyHash` and `id`, an indexer will be able to keep track of each offer, because offer list and id together create a unique id for the offer. By emitting the `maker` address, we are able to keep track of who has posted what offer. The `tick` and `gives`, enables an indexer to know exactly how much an offer is willing to give and at what price, this could for example be used to calculate a return. The `gasprice` and `gasreq`, enables an indexer to calculate how much provision is locked by the offer, see `Credit` for more information.

  The fields `olKeyHash` and `maker` are indexed, so that we can filter on them when doing RPC calls.
  */
  event OfferWrite(
    bytes32 indexed olKeyHash, address indexed maker, int tick, uint gives, uint gasprice, uint gasreq, uint id
  );

  /*
  This event is emitted when a user retracts his offer.

  It emits the `olKeyHash`, the `maker` address, `offerId` and whether or not the user chose to `deprovision` the offer.

  By emitting this event an indexer knows whether or not an offer is live. And whether or not an offer is deprovisioned. This is important because we need to know this, when we try to calculate how much an offer locks in provision. See the description of `Credit` for more info.

  The `maker` is not needed for an indexer to work, but is needed for RPC calls, so it is emitted and indexed for that reason. The `olKeyHash` is only indexed because it is needed for RPC calls. 
  */
  event OfferRetract(bytes32 indexed olKeyHash, address indexed maker, uint id, bool deprovision);
}

/* # IMaker interface */
interface IMaker {
  /* Called upon offer execution. 
  - If the call throws, Mangrove will not try to transfer funds and the first 32 bytes of revert reason are passed to `makerPosthook` as `makerData`
  - If the call returns normally, `returnData` is passed to `makerPosthook` as `makerData` and Mangrove will attempt to transfer the funds.
  */
  function makerExecute(MgvLib.SingleOrder calldata order) external returns (bytes32);

  /* Called after all offers of an order have been executed. Posthook of the last executed order is called first and full reentrancy into Mangrove is enabled at this time. `order` recalls key arguments of the order that was processed and `result` recalls important information for updating the current offer. (see [above](#MgvLib/OrderResult))*/
  function makerPosthook(MgvLib.SingleOrder calldata order, MgvLib.OrderResult calldata result) external;
}

/* # Monitor interface
If enabled, the monitor receives notification after each offer execution and is read for each offer list's `gasprice` and `density`. */
interface IMgvMonitor {
  function notifySuccess(MgvLib.SingleOrder calldata sor, address taker) external;

  function notifyFail(MgvLib.SingleOrder calldata sor, address taker) external;

  function read(OLKey memory olKey) external view returns (uint gasprice, Density density);
}

File 6 of 28 : MgvOfferMaking.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.10;

import "@mgv/src/core/MgvLib.sol";
import {MgvHasOffers} from "./MgvHasOffers.sol";

/* `MgvOfferMaking` contains market-making-related functions. */
contract MgvOfferMaking is MgvHasOffers {
  /* # Public Maker operations
     ## New Offer */
  //+clear+
  /* In Mangrove, makers and takers call separate functions. Market makers call `newOffer` to fill the book, and takers call functions such as `marketOrder` to consume it.  */

  //+clear+

  /* The following structs holds offer creation/update parameters in memory. This frees up stack space for local variables. */
  struct OfferPack {
    OLKey olKey;
    uint gives;
    uint id;
    uint gasreq;
    uint gasprice;
    Global global;
    Local local;
    // used on update only
    Offer oldOffer;
  }

  /* The function `newOffer` is for market makers only; no match with the existing offer list is done. The maker specifies how much `olKey.outbound_tkn` token it `gives` and at which `tick` (which induces the price `1.0001^tick`). The actual tick of the offer will be the smallest tick offerTick > tick that satisfies offerTick % tickSpacing == 0.

     It also specify with `gasreq` how much gas should be given when executing their offer.

     `gasprice` indicates an upper bound on the gasprice (in Mwei) at which the maker is ready to be penalised if their offer fails. Any value below Mangrove's internal `gasprice` configuration value will be ignored.

    `gasreq`, together with `gasprice`, will contribute to determining the penalty provision set aside by Mangrove from the market maker's `balanceOf` balance.

  An offer cannot be inserted in a closed market, nor when a reentrancy lock for `outbound_tkn`,`inbound_tkn` is on.

  No more than $2^{32}-1$ offers can ever be created for one (`outbound`,`inbound`, `tickSpacing`) offer list.

  The actual contents of the function is in `writeOffer`, which is called by both `newOffer` and `updateOffer`. */

  function newOfferByTick(OLKey memory olKey, Tick tick, uint gives, uint gasreq, uint gasprice)
    public
    payable
    returns (uint offerId)
  {
    unchecked {
      /* In preparation for calling `writeOffer`, we read the `outbound_tkn`,`inbound_tkn`, `tickSpacing` offer list configuration, check for reentrancy and offer list liveness, fill the `OfferPack` struct and increment the offer list's `last`. */
      OfferPack memory ofp;
      OfferList storage offerList;
      (ofp.global, ofp.local, offerList) = _config(olKey);
      unlockedOfferListOnly(ofp.local);
      activeOfferListOnly(ofp.global, ofp.local);
      if (msg.value > 0) {
        creditWei(msg.sender, msg.value);
      }
      ofp.id = 1 + ofp.local.last();
      require(uint32(ofp.id) == ofp.id, "mgv/offerIdOverflow");

      ofp.local = ofp.local.last(ofp.id);

      ofp.olKey = olKey;
      ofp.gives = gives;
      ofp.gasreq = gasreq;
      ofp.gasprice = gasprice;

      /* The last parameter to writeOffer indicates that we are creating a new offer, not updating an existing one. */
      writeOffer(offerList, ofp, tick, false);

      /* Since we locally modified a field of the local configuration (`last`), we save the change to storage. Note that `writeOffer` may have further modified the local configuration by updating the currently cached tick tree branch. */
      offerList.local = ofp.local;
      return ofp.id;
    }
  }

  /* There is a `ByVolume` variant where the maker specifies how much `inbound_tkn` it `wants` and how much `outbound_tkn` it `gives`. Volumes should fit on 127 bits.

  */
  function newOfferByVolume(OLKey memory olKey, uint wants, uint gives, uint gasreq, uint gasprice)
    external
    payable
    returns (uint offerId)
  {
    unchecked {
      return newOfferByTick(olKey, TickLib.tickFromVolumes(wants, gives), gives, gasreq, gasprice);
    }
  }

  /* ## Update Offer */
  //+clear+
  /* Very similar to `newOffer`, `updateOffer` prepares an `OfferPack` for `writeOffer`. Makers should use it for updating live offers, but also to save on gas by reusing old, already consumed offers.

     Gas use is minimal when:
     1. The offer does not move in the offer list
     2. The offer does not change its `gasreq`
     3. The (`outbound_tkn`,`inbound_tkn`)'s `offer_gasbase` has not changed since the offer was last written
     4. `gasprice` has not changed since the offer was last written
     5. `gasprice` is greater than Mangrove's gasprice estimation
  */
  function updateOfferByTick(OLKey memory olKey, Tick tick, uint gives, uint gasreq, uint gasprice, uint offerId)
    public
    payable
  {
    unchecked {
      OfferPack memory ofp;
      OfferList storage offerList;
      (ofp.global, ofp.local, offerList) = _config(olKey);
      unlockedOfferListOnly(ofp.local);
      activeOfferListOnly(ofp.global, ofp.local);
      if (msg.value > 0) {
        creditWei(msg.sender, msg.value);
      }
      ofp.olKey = olKey;
      ofp.gives = gives;
      ofp.id = offerId;
      ofp.gasreq = gasreq;
      ofp.gasprice = gasprice;
      ofp.oldOffer = offerList.offerData[offerId].offer;
      // Save local config
      Local oldLocal = ofp.local;
      /* The second argument indicates that we are updating an existing offer, not creating a new one. */
      writeOffer(offerList, ofp, tick, true);
      /* We saved the current offer list's local configuration before calling `writeOffer`, since that function may update it. We now check for any change to the configuration and update it if needed. */
      if (!oldLocal.eq(ofp.local)) {
        offerList.local = ofp.local;
      }
    }
  }

  function updateOfferByVolume(OLKey memory olKey, uint wants, uint gives, uint gasreq, uint gasprice, uint offerId)
    external
    payable
  {
    unchecked {
      updateOfferByTick(olKey, TickLib.tickFromVolumes(wants, gives), gives, gasreq, gasprice, offerId);
    }
  }

  /* ## Retract Offer */
  //+clear+
  /* `retractOffer` takes the offer `offerId` out of the book. However, `deprovision == true` also refunds the provision associated with the offer. */
  function retractOffer(OLKey memory olKey, uint offerId, bool deprovision) external returns (uint provision) {
    unchecked {
      (, Local local, OfferList storage offerList) = _config(olKey);
      unlockedOfferListOnly(local);
      OfferData storage offerData = offerList.offerData[offerId];
      Offer offer = offerData.offer;
      OfferDetail offerDetail = offerData.detail;
      require(msg.sender == offerDetail.maker(), "mgv/retractOffer/unauthorized");

      /* Here, we are about to un-live an offer, so we start by taking it out of the tick tree. Note that unconditionally calling `dislodgeOffer` even if the offer is not `live` would break the offer list since it would connect offers that may have since moved. */
      if (offer.isLive()) {
        Local oldLocal = local;
        (local,) = dislodgeOffer(offerList, olKey.tickSpacing, offer, local, local.bestBin(), true);
        /* If calling `dislodgeOffer` has changed the current best offer, we update `local`. */
        if (!oldLocal.eq(local)) {
          offerList.local = local;
        }
      }
      /* Set `offer.gives` to 0 (which is encodes the fact that the offer is dead). The `deprovision` argument indicates whether the maker wishes to get their provision back (if true, `offer.gasprice` will be set to 0 as well). */
      dirtyDeleteOffer(offerData, offer, offerDetail, deprovision);

      /* If the user wants to get their provision back, we compute it from the offer's `gasprice`, `offer_gasbase` and `gasreq`. */
      if (deprovision) {
        provision = 1e6 * offerDetail.gasprice() //gasprice is 0 if offer was deprovisioned
          * (offerDetail.gasreq() + offerDetail.offer_gasbase());
        // credit `balanceOf` and log transfer
        creditWei(msg.sender, provision);
      }

      emit OfferRetract(olKey.hash(), offerDetail.maker(), offerId, deprovision);
    }
  }

  /* ## Provisioning
  Market makers must have enough provisions for possible penalties. These provisions are in native tokens. Every time a new offer is created or an offer is updated, `balanceOf` is adjusted to provision the offer's maximum possible penalty (`gasprice * (gasreq + offer_gasbase)`).

  For instance, if the current `balanceOf` of a maker is 1 ether and they create an offer that requires a provision of 0.01 ethers, their `balanceOf` will be reduced to 0.99 ethers. No ethers will move; this is just an internal accounting movement to make sure the maker cannot `withdraw` the provisioned amounts.

  */
  //+clear+

  /* Fund should be called with a nonzero value (hence the `payable` modifier). The provision will be given to `maker`, not `msg.sender`. */
  function fund(address maker) public payable {
    unchecked {
      (Global _global,,) = _config(OLKey(address(0), address(0), 0));
      liveMgvOnly(_global);
      creditWei(maker, msg.value);
    }
  }

  function fund() external payable {
    unchecked {
      fund(msg.sender);
    }
  }

  /* A transfer with enough gas to Mangrove will increase the caller's available `balanceOf` balance. _You should send enough gas to execute this function when sending money to Mangrove._  */
  receive() external payable {
    unchecked {
      fund(msg.sender);
    }
  }

  /* Any provision not currently held to secure an offer's possible penalty is available for withdrawal. */
  function withdraw(uint amount) external returns (bool noRevert) {
    unchecked {
      /* Since we only ever send money to the caller, we do not need to provide any particular amount of gas, the caller should manage this herself. */
      debitWei(msg.sender, amount);
      (noRevert,) = msg.sender.call{value: amount}("");
      require(noRevert, "mgv/withdrawCallRevert");
    }
  }

  /* # Low-level Maker functions */

  /* ## Write Offer */

  /* Used by `updateOfferBy*` and `newOfferBy*`, this function optionally removes an offer then (re)inserts it in the tick tree. The `update` argument indicates whether the call comes from `updateOfferBy*` or `newOfferBy*`. */
  function writeOffer(OfferList storage offerList, OfferPack memory ofp, Tick insertionTick, bool update) internal {
    unchecked {
      /* `gasprice`'s floor is Mangrove's own gasprice estimate, `ofp.global.gasprice`. We first check that gasprice fits in 26 bits. Otherwise it could be that `uint26(gasprice) < global_gasprice < gasprice` and the actual value we store is `uint26(gasprice)` (using pseudocode here since the type uint26 does not exist). */
      require(GlobalLib.gasprice_check(ofp.gasprice), "mgv/writeOffer/gasprice/tooBig");

      if (ofp.gasprice < ofp.global.gasprice()) {
        ofp.gasprice = ofp.global.gasprice();
      }

      /* * Check `gasreq` below limit. Implies `gasreq` at most 24 bits wide, which ensures no overflow in computation of `provision` (see below). */
      require(ofp.gasreq <= ofp.global.gasmax(), "mgv/writeOffer/gasreq/tooHigh");
      /* * Make sure `gives > 0` -- division by 0 would throw in several places otherwise, and `isLive` relies on it. */
      require(ofp.gives > 0, "mgv/writeOffer/gives/tooLow");
      /* * Make sure that the maker is posting a 'dense enough' offer: the ratio of `outbound_tkn` offered per gas consumed must be high enough. The actual gas cost paid by the taker is overapproximated by adding `offer_gasbase` to `gasreq`. */
      require(
        ofp.gives >= ofp.local.density().multiply(ofp.gasreq + ofp.local.offer_gasbase()),
        "mgv/writeOffer/density/tooLow"
      );

      /* The following checks are for the maker's convenience only. */
      require(OfferLib.gives_check(ofp.gives), "mgv/writeOffer/gives/tooBig");

      uint tickSpacing = ofp.olKey.tickSpacing;
      /* Derive bin from given tick, then normalize the tick: available ticks in an offer list are those who are equal to 0 modulo tickSpacing. */
      Bin insertionBin = insertionTick.nearestBin(tickSpacing);
      insertionTick = insertionBin.tick(tickSpacing);
      require(insertionTick.inRange(), "mgv/writeOffer/tick/outOfRange");

      /* Log the write offer event. */
      uint ofrId = ofp.id;
      emit OfferWrite(
        ofp.olKey.hash(), msg.sender, Tick.unwrap(insertionTick), ofp.gives, ofp.gasprice, ofp.gasreq, ofrId
      );

      /* We now write the new `offerDetails` and remember the previous provision (0 by default, for new offers) to balance out maker's `balanceOf`. */
      {
        uint oldProvision;
        OfferData storage offerData = offerList.offerData[ofrId];
        OfferDetail offerDetail = offerData.detail;
        if (update) {
          require(msg.sender == offerDetail.maker(), "mgv/updateOffer/unauthorized");
          oldProvision = 1e6 * offerDetail.gasprice() * (offerDetail.gasreq() + offerDetail.offer_gasbase());
        }

        /* If the offer is new, has a new `gasprice`, `gasreq`, or if Mangrove's `offer_gasbase` configuration parameter has changed, we also update `offerDetails`. */
        if (
          !update || offerDetail.gasreq() != ofp.gasreq || offerDetail.gasprice() != ofp.gasprice
            || offerDetail.offer_gasbase() != ofp.local.offer_gasbase()
        ) {
          uint offer_gasbase = ofp.local.offer_gasbase();
          offerData.detail = OfferDetailLib.pack({
            __maker: msg.sender,
            __gasreq: ofp.gasreq,
            __kilo_offer_gasbase: offer_gasbase / 1e3,
            __gasprice: ofp.gasprice
          });
        }

        /* With every change to an offer, a maker may deduct provisions from its `balanceOf` balance. It may also get provisions back if the updated offer requires fewer provisions than before. */
        uint provision = (ofp.gasreq + ofp.local.offer_gasbase()) * ofp.gasprice * 1e6;
        if (provision > oldProvision) {
          debitWei(msg.sender, provision - oldProvision);
        } else if (provision < oldProvision) {
          creditWei(msg.sender, oldProvision - provision);
        }
      }

      /* We now cache the current best bin in a stack variable. Since the current best bin is derived from `ofp.local`, and since `ofp.local` may change as a new branch is cached in `local`, not caching that value means potentially losing access to it. */
      Bin cachedBestBin;
      // Check if tick tree is currently empty. As an invariant, `local.level3` if empty, iff `local.level2` is empty, iff `local.level1` is empty, iff `local.root` is empty.
      if (ofp.local.level3().isEmpty()) {
        /* If the tick tree is empty, we consider the current best bin to be the bin of the written offer. This makes later comparisons between them pick the right conditional branch every time. */
        cachedBestBin = insertionBin;
      } else {
        /* If the tick tree is currently not empty, we cache the current best bin. */
        cachedBestBin = ofp.local.bestBin();
        /* If the written offer is currently stored in the tick tree, it must be removed. */
        if (ofp.oldOffer.isLive()) {
          /* If the insertion bin of the offer is better than the current best bin, the later call to `dislodgeOffer` does not need to update the cached branch in `local` since we (`writeOffer`) will take care of updating the branch as part of insertion the offer in its new bin. Otherwise, we may need `dislodgeOffer` to take care of updating the cached branch in `local`. However, that is only th the case if the written offer is currently the best offer of the tick tree. `dislodgeOffer` will make that determination. */
          bool shouldUpdateBranch = !insertionBin.strictlyBetter(cachedBestBin);

          /* `local` is updated, and `shouldUpdateBranch` now means "did update branch". */
          (ofp.local, shouldUpdateBranch) =
            dislodgeOffer(offerList, tickSpacing, ofp.oldOffer, ofp.local, cachedBestBin, shouldUpdateBranch);
          /* If `dislodgeOffer` did update the information in `local`, it means the cached best bin may be stale -- the best offer may now be in a different bin. So we update it. */
          if (shouldUpdateBranch) {
            if (ofp.local.level3().isEmpty()) {
              /* A call to `bestBin()` is invalid when the branch cached in `local` is for an empty tick tree. In that case, as we did earlier if the tick tree was already empty, we set the current best bin to the bin of the written offer. */
              cachedBestBin = insertionBin;
            } else {
              cachedBestBin = ofp.local.bestBin();
            }
          }
        }
      }

      /* We will now insert the offer to its new position in the tick tree. If the offer is now the best offer, we update the cached "bin position in leaf" information in `local`. */
      if (!cachedBestBin.strictlyBetter(insertionBin)) {
        ofp.local = ofp.local.binPosInLeaf(insertionBin.posInLeaf());
      }

      /* Next, we load the leaf of the offer to check whether it needs updating. */
      Leaf leaf = offerList.leafs[insertionBin.leafIndex()].clean();

      /* If the written offer's leaf was empty, the level3 above it needs updating. */
      if (leaf.isEmpty()) {
        /* We reuse the same `field` variable for all 3 level indices. */
        Field field;
        /* We reuse the same `insertionIndex` and `currentIndex` variables for all 3 level indices and for the leaf index. */
        int insertionIndex = insertionBin.level3Index();
        int currentIndex = cachedBestBin.level3Index();
        if (insertionIndex == currentIndex) {
          /* If the written offer's level3 is the cached level3, we load the written offer's level3 from `local`. */
          field = ofp.local.level3();
        } else {
          /* Otherwise we load the written offer's level3 from storage. */
          field = offerList.level3s[insertionIndex].clean();
          /* If the written offer's level3 is strictly better than the cached level3, we evict the cached level3. */
          if (insertionIndex < currentIndex) {
            Field localLevel3 = ofp.local.level3();
            bool shouldSaveLevel3 = !localLevel3.isEmpty();
            /* Clean/dirty management. `if`s are sequenced to avoid a useless SLOAD. */
            if (!shouldSaveLevel3) {
              shouldSaveLevel3 = !offerList.level3s[currentIndex].eq(DirtyFieldLib.CLEAN_EMPTY);
            }
            if (shouldSaveLevel3) {
              offerList.level3s[currentIndex] = localLevel3.dirty();
            }
          }
        }

        if (insertionIndex <= currentIndex) {
          /* If the written offer's level3 is as good as or better than the cached level3, we cache the written offer's level3 in `local`. */
          ofp.local = ofp.local.level3(field.flipBitAtLevel3(insertionBin));
        } else {
          /* Otherwise, we put it in storage */
          offerList.level3s[insertionIndex] = field.flipBitAtLevel3(insertionBin).dirty();
        }

        /* If the written offer's level3 was empty, the level2 above it needs updating. */
        if (field.isEmpty()) {
          insertionIndex = insertionBin.level2Index();
          currentIndex = cachedBestBin.level2Index();

          if (insertionIndex == currentIndex) {
            /* If the written offer's level2 is the cached level2, we load the written offer's level2 from `local`. */
            field = ofp.local.level2();
          } else {
            /* Otherwise we load the written offer's level2 from storage. */
            field = offerList.level2s[insertionIndex].clean();

            /* If the written offer's level2 is strictly better than the cached level2, we evict the cached level2. */
            if (insertionIndex < currentIndex) {
              Field localLevel2 = ofp.local.level2();
              bool shouldSaveLevel2 = !localLevel2.isEmpty();

              /* Clean/dirty management. `if`s are sequenced to avoid a useless SLOAD. */
              if (!shouldSaveLevel2) {
                shouldSaveLevel2 = !offerList.level2s[currentIndex].eq(DirtyFieldLib.CLEAN_EMPTY);
              }
              if (shouldSaveLevel2) {
                offerList.level2s[currentIndex] = localLevel2.dirty();
              }
            }
          }

          if (insertionIndex <= currentIndex) {
            /* If the written offer's level3 is as good as or better than the cached level3, we cache the written offer's level3 in `local`. */
            ofp.local = ofp.local.level2(field.flipBitAtLevel2(insertionBin));
          } else {
            /* Otherwise, we put it in storage */
            offerList.level2s[insertionIndex] = field.flipBitAtLevel2(insertionBin).dirty();
          }
          /* If the written offer's level2 was empty, the level1 above it needs updating. */
          if (field.isEmpty()) {
            insertionIndex = insertionBin.level1Index();
            currentIndex = cachedBestBin.level1Index();

            if (insertionIndex == currentIndex) {
              /* If the written offer's level1 is the cached level1, we load the written offer's level1 from `local`. */
              field = ofp.local.level1();
            } else {
              /* Otherwise we load the written offer's level1 from storage. */
              field = offerList.level1s[insertionIndex].clean();
              /* If the written offer's level1 is strictly better than the cached level1, we evict the cached level1. */
              if (insertionIndex < currentIndex) {
                /* Unlike with level2 and level3, level1 cannot be `CLEAN_EMPTY` (it gets dirtied in `activate`) */
                offerList.level1s[currentIndex] = ofp.local.level1().dirty();
              }
            }

            if (insertionIndex <= currentIndex) {
              /* If the written offer's level1 is as good as or better than the cached level1, we cache the written offer's level1 in `local`. */
              ofp.local = ofp.local.level1(field.flipBitAtLevel1(insertionBin));
            } else {
              /* Otherwise, we put it in storage */
              offerList.level1s[insertionIndex] = field.flipBitAtLevel1(insertionBin).dirty();
            }
            /* If the written offer's level1 was empty, the root needs updating. */
            if (field.isEmpty()) {
              ofp.local = ofp.local.root(ofp.local.root().flipBitAtRoot(insertionBin));
            }
          }
        }
      }

      /* Now that we are done checking the current state of the leaf, we can update it. By reading the last id of the written offer's bin in the offer's leaf, we can check if the bin is currently empty or not (as an invariant, an empty bin has both `firstId` and `lastId` equal to 0, and a nonempty bin has both ids different from 0). 

      Note that offers are always inserted at the end of their bin, so that earlier offer are taken first during market orders.
      */

      uint lastId = leaf.lastOfBin(insertionBin);
      if (lastId == 0) {
        /* If the bin was empty, we update the bin's first id (a bin with a single offer has that offer id as `firstId` and as `lastId`).*/
        leaf = leaf.setBinFirst(insertionBin, ofrId);
      } else {
        /* Otherwise, the written offer will become the new last offer of the bin, and the current last offer will have the written offer as next offer. */
        OfferData storage offerData = offerList.offerData[lastId];
        offerData.offer = offerData.offer.next(ofrId);
      }

      /* We now store the written offer id as the last offer of the bin. */
      leaf = leaf.setBinLast(insertionBin, ofrId);
      offerList.leafs[insertionBin.leafIndex()] = leaf.dirty();

      /* Finally, we store the offer information, including a pointer to the previous last offer of the bin (it may be 0). */
      Offer ofr = OfferLib.pack({__prev: lastId, __next: 0, __tick: insertionTick, __gives: ofp.gives});
      offerList.offerData[ofrId].offer = ofr;
    }
  }
}

File 7 of 28 : MgvOfferTakingWithPermit.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.10;

import "@mgv/src/core/MgvLib.sol";
import {MgvOfferTaking} from "./MgvOfferTaking.sol";
import {TickTreeLib} from "@mgv/lib/core/TickTreeLib.sol";

abstract contract MgvOfferTakingWithPermit is MgvOfferTaking {
  // Since DOMAIN_SEPARATOR is immutable, it cannot use MgvAppendix to provide an accessor (because the value will come from code, not from storage), so we generate the accessor here.
  bytes32 public immutable DOMAIN_SEPARATOR;

  constructor(string memory contractName) {
    /* Initialize [EIP712](https://eips.ethereum.org/EIPS/eip-712) `DOMAIN_SEPARATOR`. */
    DOMAIN_SEPARATOR = keccak256(
      abi.encode(
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
        keccak256(bytes(contractName)),
        keccak256(bytes("1")),
        block.chainid,
        address(this)
      )
    );
  }

  /* # Delegation public functions */

  /* Adapted from [Uniswap v2 contract](https://github.com/Uniswap/uniswap-v2-core/blob/55ae25109b7918565867e5c39f1e84b7edd19b2a/contracts/UniswapV2ERC20.sol#L81) */
  function permit(
    address outbound_tkn,
    address inbound_tkn,
    address owner,
    address spender,
    uint value,
    uint deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external {
    unchecked {
      require(deadline >= block.timestamp, "mgv/permit/expired");

      uint nonce = _nonces[owner]++;
      bytes32 digest = keccak256(
        abi.encodePacked(
          "\x19\x01",
          DOMAIN_SEPARATOR,
          keccak256(abi.encode(_PERMIT_TYPEHASH, outbound_tkn, inbound_tkn, owner, spender, value, nonce, deadline))
        )
      );
      address recoveredAddress = ecrecover(digest, v, r, s);
      require(recoveredAddress != address(0) && recoveredAddress == owner, "mgv/permit/invalidSignature");

      _allowance[outbound_tkn][inbound_tkn][owner][spender] = value;
      emit Approval(outbound_tkn, inbound_tkn, owner, spender, value);
    }
  }

  function approve(address outbound_tkn, address inbound_tkn, address spender, uint value) external returns (bool) {
    unchecked {
      _allowance[outbound_tkn][inbound_tkn][msg.sender][spender] = value;
      emit Approval(outbound_tkn, inbound_tkn, msg.sender, spender, value);
      return true;
    }
  }

  /* The delegate version of `marketOrder` is `marketOrderFor`, which takes a `taker` address as additional argument. Penalties incurred by failed offers will still be sent to `msg.sender`, but exchanged amounts will be transferred from and to the `taker`. If the `msg.sender`'s allowance for the given `outbound_tkn`,`inbound_tkn` and `taker` are strictly less than the total amount eventually spent by `taker`, the call will fail. */

  /* *Note:* `marketOrderFor` and `cleanByImpersonation` may emit ERC20 `Transfer` events of value 0 from `taker`, but that's already the case with common ERC20 implementations. */
  function marketOrderForByVolume(OLKey memory olKey, uint takerWants, uint takerGives, bool fillWants, address taker)
    external
    returns (uint takerGot, uint takerGave, uint bounty, uint feePaid)
  {
    unchecked {
      uint fillVolume = fillWants ? takerWants : takerGives;
      Tick tick = TickLib.tickFromVolumes(takerGives, takerWants);
      return marketOrderForByTick(olKey, tick, fillVolume, fillWants, taker);
    }
  }

  function marketOrderForByTick(OLKey memory olKey, Tick maxTick, uint fillVolume, bool fillWants, address taker)
    public
    returns (uint takerGot, uint takerGave, uint bounty, uint feePaid)
  {
    unchecked {
      (takerGot, takerGave, bounty, feePaid) = generalMarketOrder(olKey, maxTick, fillVolume, fillWants, taker, 0);
      /* The sender's allowance is verified after the order complete so that `takerGave` rather than `takerGives` is checked against the allowance. The former may be lower. */
      deductSenderAllowance(olKey.outbound_tkn, olKey.inbound_tkn, taker, takerGave);
    }
  }

  /* # Misc. low-level functions */

  /* Used by `*For` functions, it both checks that `msg.sender` was allowed to use the taker's funds and decreases the former's allowance. */
  function deductSenderAllowance(address outbound_tkn, address inbound_tkn, address owner, uint amount) internal {
    unchecked {
      mapping(address => uint) storage curriedAllow = _allowance[outbound_tkn][inbound_tkn][owner];
      uint allowed = curriedAllow[msg.sender];
      require(allowed >= amount, "mgv/lowAllowance");
      curriedAllow[msg.sender] = allowed - amount;

      emit Approval(outbound_tkn, inbound_tkn, owner, msg.sender, allowed - amount);
    }
  }
}

File 8 of 28 : MgvAppendix.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.10;

import "@mgv/src/core/MgvLib.sol";
import {MgvView} from "@mgv/src/core/MgvView.sol";
import {MgvGovernable} from "@mgv/src/core/MgvGovernable.sol";

/* The `MgvAppendix` contract contains Mangrove functions related to:
 - Getters (view functions)
 - Governance functions
 
Due to bytecode size limits, not all Mangrove code can reside at the address of Mangrove. So when constructed, Mangrove creates a `MgvAppendix` instance and sets up a fallback to that instance when receiving an unknown function selector.

The functions moved to `MgvAppendix` have been selected because they are less gas-sensitive than core Mangrove functionality. */
contract MgvAppendix is MgvView, MgvGovernable {}

File 9 of 28 : MgvGovernable.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.10;

import {MgvLib, IMgvMonitor, IERC20, Leaf, Field, Density, DensityLib, OLKey, DirtyFieldLib} from "./MgvLib.sol";
import "@mgv/src/core/MgvCommon.sol";

/* Contains governance functions, to reduce Mangrove contract size */
contract MgvGovernable is MgvCommon {
  /* ## `authOnly` check */

  function authOnly() internal view {
    unchecked {
      require(msg.sender == _governance || msg.sender == address(this) || _governance == address(0), "mgv/unauthorized");
    }
  }

  /* ## Transfer ERC20 tokens to governance.

    If this function is called while an order is executing, the reentrancy may prevent a taker from receiving their tokens. This is fine as the order execution will then fail and the tx will revert. So the most a malicious governance can do is render Mangrove unusable.
  */
  function withdrawERC20(address tokenAddress, uint value) external {
    authOnly();
    require(transferToken(tokenAddress, _governance, value), "mgv/withdrawERC20Fail");
  }

  /* # Set configuration and Mangrove state */

  /* ## Locals */
  /* ### `active` */
  function activate(OLKey memory olKey, uint fee, uint density96X32, uint offer_gasbase) public {
    unchecked {
      authOnly();
      bytes32 olKeyHash = olKey.hash();
      // save hash->key mapping
      _olKeys[olKeyHash] = olKey;
      OfferList storage offerList = offerLists[olKeyHash];
      // activate market
      offerList.local = offerList.local.active(true);
      emit SetActive(olKey.hash(), olKey.outbound_tkn, olKey.inbound_tkn, olKey.tickSpacing, true);
      setFee(olKey, fee);
      setDensity96X32(olKey, density96X32);
      setGasbase(olKey, offer_gasbase);
      // warm level1s
      offerList.level1s[-1] = DirtyFieldLib.DIRTY_EMPTY;
      offerList.level1s[0] = DirtyFieldLib.DIRTY_EMPTY;
    }
  }

  function deactivate(OLKey memory olKey) public {
    authOnly();
    OfferList storage offerList = offerLists[olKey.hash()];
    offerList.local = offerList.local.active(false);
    emit SetActive(olKey.hash(), olKey.outbound_tkn, olKey.inbound_tkn, olKey.tickSpacing, false);
  }

  /* ### `fee` */
  function setFee(OLKey memory olKey, uint fee) public {
    unchecked {
      authOnly();
      /* `fee` is in basis points, i.e. in percents of a percent. */
      require(LocalLib.fee_check(fee), LocalLib.fee_size_error);
      OfferList storage offerList = offerLists[olKey.hash()];
      offerList.local = offerList.local.fee(fee);
      emit SetFee(olKey.hash(), fee);
    }
  }

  /* ### `density` */
  /* Useless if `global.useOracle != 0` and oracle returns a valid density. */
  /* Density is given as a 96.32 fixed point number. It will be stored as a 9-bit float and be approximated towards 0. The maximum error is 20%. See `DensityLib` for more information. */
  function setDensity96X32(OLKey memory olKey, uint density96X32) public {
    unchecked {
      authOnly();

      //+clear+
      OfferList storage offerList = offerLists[olKey.hash()];
      /* Checking the size of `density` is necessary to prevent overflow before storing density as a float. */
      require(DensityLib.checkDensity96X32(density96X32), "mgv/config/density96X32/wrong");

      offerList.local = offerList.local.densityFrom96X32(density96X32);
      emit SetDensity96X32(olKey.hash(), density96X32);
    }
  }

  /* ### `gasbase` */
  function setGasbase(OLKey memory olKey, uint offer_gasbase) public {
    unchecked {
      authOnly();
      /* Checking the size of `offer_gasbase` is necessary to prevent a) data loss when copied to an `OfferDetail` struct and b) overflow when used in calculations. */
      require(LocalLib.kilo_offer_gasbase_check(offer_gasbase / 1e3), LocalLib.kilo_offer_gasbase_size_error);
      // require(uint24(offer_gasbase) == offer_gasbase, "mgv/config/offer_gasbase/24bits");
      //+clear+
      OfferList storage offerList = offerLists[olKey.hash()];
      offerList.local = offerList.local.offer_gasbase(offer_gasbase);
      emit SetGasbase(olKey.hash(), offer_gasbase);
    }
  }

  /* ## Globals */
  /* ### `kill` */
  function kill() public {
    unchecked {
      authOnly();
      internal_global = internal_global.dead(true);
      emit Kill();
    }
  }

  /* ### `gasprice` */
  /* Useless if `global.useOracle is != 0` */
  function setGasprice(uint gasprice) public {
    unchecked {
      authOnly();
      require(GlobalLib.gasprice_check(gasprice), GlobalLib.gasprice_size_error);

      //+clear+

      internal_global = internal_global.gasprice(gasprice);
      emit SetGasprice(gasprice);
    }
  }

  /* ### `gasmax` */
  function setGasmax(uint gasmax) public {
    unchecked {
      authOnly();
      /* Since any new `gasreq` is bounded above by `config.gasmax`, this check implies that all offers' `gasreq` is 24 bits wide at most. */
      require(GlobalLib.gasmax_check(gasmax), GlobalLib.gasmax_size_error);
      //+clear+
      internal_global = internal_global.gasmax(gasmax);
      emit SetGasmax(gasmax);
    }
  }

  /* ### `maxRecursionDepth` */
  function setMaxRecursionDepth(uint maxRecursionDepth) public {
    unchecked {
      authOnly();
      require(GlobalLib.maxRecursionDepth_check(maxRecursionDepth), GlobalLib.maxRecursionDepth_size_error);
      internal_global = internal_global.maxRecursionDepth(maxRecursionDepth);
      emit SetMaxRecursionDepth(maxRecursionDepth);
    }
  }

  /* ### `maxGasreqForFailingOffers` */
  function setMaxGasreqForFailingOffers(uint maxGasreqForFailingOffers) public {
    unchecked {
      authOnly();
      require(
        GlobalLib.maxGasreqForFailingOffers_check(maxGasreqForFailingOffers),
        GlobalLib.maxGasreqForFailingOffers_size_error
      );
      internal_global = internal_global.maxGasreqForFailingOffers(maxGasreqForFailingOffers);
      emit SetMaxGasreqForFailingOffers(maxGasreqForFailingOffers);
    }
  }

  /* ### `governance` */
  function setGovernance(address governanceAddress) public {
    unchecked {
      authOnly();
      require(governanceAddress != address(0), "mgv/config/gov/not0");
      _governance = governanceAddress;
      emit SetGovernance(governanceAddress);
    }
  }

  /* ### `monitor` */
  function setMonitor(address monitor) public {
    unchecked {
      authOnly();
      internal_global = internal_global.monitor(monitor);
      emit SetMonitor(monitor);
    }
  }

  /* ### `useOracle` */
  function setUseOracle(bool useOracle) public {
    unchecked {
      authOnly();
      internal_global = internal_global.useOracle(useOracle);
      emit SetUseOracle(useOracle);
    }
  }

  /* ### `notify` */
  function setNotify(bool notify) public {
    unchecked {
      authOnly();
      internal_global = internal_global.notify(notify);
      emit SetNotify(notify);
    }
  }
}

File 10 of 28 : Structs.post.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

/* ************************************************** *
            GENERATED FILE. DO NOT EDIT.
 * ************************************************** */


import {Offer, OfferUnpacked, OfferLib}  from "./Offer.post.sol";

import {OfferDetail, OfferDetailUnpacked, OfferDetailLib}  from "./OfferDetail.post.sol";

import {Global, GlobalUnpacked, GlobalLib}  from "./Global.post.sol";

import {Local, LocalUnpacked, LocalLib}  from "./Local.post.sol";

File 11 of 28 : IERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;

interface IERC20 {
  function totalSupply() external view returns (uint);

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

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

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

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

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

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

  event Transfer(address indexed from, address indexed to, uint value);
  event Approval(address indexed owner, address indexed spender, uint value);

  function decimals() external view returns (uint8);

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

File 12 of 28 : DensityLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import {Field} from "@mgv/lib/core/TickTreeLib.sol";
import {ONES} from "@mgv/lib/core/Constants.sol";
import {BitLib} from "@mgv/lib/core/BitLib.sol";

/*

The density of a semibook is the number of outbound tokens per gas required. An offer must a respect a semibook's density.

Density can be < 1.

The density of a semibook is stored as a 9 bits float. For convenience, governance functions read density as a 96.32 fixed point number. The functions below give conversion utilities between the two formats

As a guideline, fixed-point densities should be uints and should use hungarian notation (for instance `uint density96X32`). Floating-point densities should use the Density user-defined type.

The float <-> fixed conversion is format agnostic but the expectation is that fixed points are 96x32, and floats are 2-bit mantissa, 7bit exponent with bias 32. 

The encoding is nonstandard so the code can be simpler.

There are no subnormal floats in this encoding, `[exp][mantissa]` means:

```
if exp is 0 or 1:   0bmantissa   * 2^-32
otherwise:          0b1.mantissa * 2^(exp-32)
```

so the small values have some holes:

```
  coeff   exponent  available    |   coeff   exponent  available
  --------------------------------------------------------------
  0b0.00                         |  0b1.10     -31
  0b1.00     -32                 |  0b1.11     -31        no
  0b1.01     -32        no       |  0b1.00     -30
  0b1.10     -32        no       |  0b1.01     -30
  0b1.11     -32        no       |  0b1.10     -30
  0b1.00     -31                 |  0b1.11     -30
  0b1.01     -31        no       |  0b1.00     -29
```
*/

type Density is uint;
using DensityLib for Density global;

library DensityLib {
  /* Numbers in this file assume that density is 9 bits in structs.ts */
  uint constant BITS = 9; // must match structs.ts
  uint constant MANTISSA_BITS = 2;
  uint constant SUBNORMAL_LIMIT = ~(ONES << (MANTISSA_BITS+1));
  uint constant MANTISSA_MASK = ~(ONES << MANTISSA_BITS);
  uint constant MASK = ~(ONES << BITS);
  uint constant MANTISSA_INTEGER = 1 << MANTISSA_BITS;
  uint constant EXPONENT_BITS = BITS - MANTISSA_BITS;

  function eq(Density a, Density b) internal pure returns (bool) { unchecked {
    return Density.unwrap(a) == Density.unwrap(b);
  }}

  /* Check the size of a fixed-point formatted density */
  function checkDensity96X32(uint density96X32) internal pure returns (bool) { unchecked {
    return density96X32 < (1<<(96+32));
  }}

  /* fixed-point -> float conversion */
  /* Warning: no bit cleaning (expected to be done by Local's code), no checking that the input is on 128 bits. */
  /* floats with `[exp=1]` are not in the image of fromFixed. They are considered noncanonical. */
  function from96X32(uint density96X32) internal pure returns (Density) { unchecked {
    if (density96X32 <= MANTISSA_MASK) {
      return Density.wrap(density96X32);
    }
    // invariant: `exp >= 2` (so not 0)
    uint exp = BitLib.fls(density96X32);
    return make(density96X32 >> (exp-MANTISSA_BITS),exp);
  }}

  /* float -> fixed-point conversion */
  function to96X32(Density density) internal pure returns (uint) { unchecked {
    /* also accepts floats not generated by fixedToFloat, i.e. with exp=1 */
    if (Density.unwrap(density) <= SUBNORMAL_LIMIT) {
      return Density.unwrap(density) & MANTISSA_MASK;
    }
    /* assumes exp is on the right number of bits */
    // invariant: `exp >= 2`
    uint shift = (Density.unwrap(density) >> MANTISSA_BITS) - MANTISSA_BITS;
    return ((Density.unwrap(density) & MANTISSA_MASK) | MANTISSA_INTEGER) << shift;
  }}

  function mantissa(Density density) internal pure returns (uint) { unchecked {
    return Density.unwrap(density) & MANTISSA_MASK;
  }}

  function exponent(Density density) internal pure returns (uint) { unchecked {
    return Density.unwrap(density) >> MANTISSA_BITS;
  }}

  /* Make a float from a mantissa and an exponent. May make a noncanonical float. */
  /* Warning: no checks */
  function make(uint _mantissa, uint _exponent) internal pure returns (Density) { unchecked {
    return Density.wrap((_exponent << MANTISSA_BITS) | (_mantissa & MANTISSA_MASK));
  }}

  /* None of the functions below will overflow if m is 96bit wide.
     Density being a 96.32 number is useful because:
     - Most of its range is representable with the 9-bits float format chosen
     - It does not overflow when multiplied with a 96bit number, which is the size chosen to represent token amounts in Mangrove.
     - Densities below `2^-32` need `> 4e9` gasreq to force gives > 0, which is not realistic
  */
  /* Multiply the density with m, rounded towards zero. */
  /* May overflow if `|m|>9` */
  function multiply(Density density, uint m) internal pure returns (uint) { unchecked {
    return (m * density.to96X32())>>32;
  }}
  /* Multiply the density with m, rounded towards +infinity. */
  /* May overflow if `|m|>96` */
  function multiplyUp(Density density, uint m) internal pure returns (uint) { unchecked {
    uint part = m * density.to96X32();
    return (part >> 32) + (part%(2<<32) == 0 ? 0 : 1);
  }}

  /* Convenience function: get a fixed-point density from the given parameters. Computes the price of gas in outbound tokens (base units), then multiplies by cover_factor. */
  /* Warning: you must multiply input usd prices by 100 */
  /* not supposed to be gas optimized */
  function paramsTo96X32(
    uint outbound_decimals, 
    uint gasprice_in_Mwei, 
    uint eth_in_centiusd, 
    uint outbound_display_in_centiusd, 
    uint cover_factor
  ) internal pure returns (uint) {
    // Do not use unchecked here
    require(uint8(outbound_decimals) == outbound_decimals,"DensityLib/fixedFromParams1/decimals/wrong");
    uint num = cover_factor * gasprice_in_Mwei * (10**outbound_decimals) * eth_in_centiusd;
    // use * instead of << to trigger overflow check
    return (num * (1 << 32)) / (outbound_display_in_centiusd * 1e12);
  }

  /* Version with token in Mwei instead of usd */
  function paramsTo96X32(
    uint outbound_decimals, 
    uint gasprice_in_Mwei, 
    uint outbound_display_in_Mwei, 
    uint cover_factor
  ) internal pure returns (uint) {
    /* **Do not** use unchecked here. */
    require(uint8(outbound_decimals) == outbound_decimals,"DensityLib/fixedFromParams2/decimals/wrong");
    uint num = cover_factor * gasprice_in_Mwei * (10**outbound_decimals);
    /* use `*` instead of `<<` to trigger overflow check */
    return (num * (1 << 32)) / outbound_display_in_Mwei;
  }
}

File 13 of 28 : TickTreeLib.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.17;

import "@mgv/lib/core/Constants.sol";
import {Tick} from "@mgv/lib/core/TickLib.sol";
import {BitLib} from "@mgv/lib/core/BitLib.sol";
import {console2 as csf} from "@mgv/forge-std/console2.sol";
import {Local} from "@mgv/src/preprocessed/Local.post.sol";


/* # Libraries for tick tree manipulation 

Offers in Mangrove are structured in a tree so that offer insertion, removal, and update can happen in constant time.

The tree has the following structure: nodes at height 0, 1, 2 and 3 are bit fields (type `Field`) and nodes at height 4 (leaves) are arrays of pairs of offers (type `Leaf`).

- The root node is a 2-bit `Field` and has 2 child nodes.
- The nodes below are called `level1` nodes and are 64-bit `Field`s, with 64 child nodes each.
- The nodes below are called `level2` nodes and are 64-bit `Field`s with 64 child nodes each.
- The nodes below are called `level3` nodes and are 64-bit `Field`s with 64 child nodes each.
- The nodes below are called `leaf` nodes and are arrays of pairs of offers. Each pair of offers represents the first and last offer of a bin.
- Bins are linked lists of offers.

For each field, the i-th bit (starting from least significant) is set if there is at least one bin holding offers below the i-th child of the field. */


/* Globally enable `leaf.method(...)` */
type Leaf is uint;
using LeafLib for Leaf global;

/* Each `Leaf` holds information about 4 bins as follows: `[firstId,lastId] [firstId,lastId] [firstId,lastId] [firstId,lastId]`. For each bin `firstId` is used by `marketOrder` to start consuming offers in the bin (each offer contains a pointer to the next offer in the bin, until `lastId` is reacher). `lastId` is used when inserting offers in the bin: the newly inserted offer replaces the last offer.

Each `leaf` has an `index`: it is the number of leaves before it.
*/
library LeafLib {
  Leaf constant EMPTY = Leaf.wrap(uint(0));

  /* Checks if a leaf is dirty or not (see below for more on dirty leaves). */
  function dirty(Leaf leaf) internal pure returns (DirtyLeaf) {
    unchecked {
      assembly ("memory-safe") {
        leaf := or(iszero(leaf),leaf)
      }
      return DirtyLeaf.wrap(Leaf.unwrap(leaf));
    }
  }

  function eq(Leaf leaf1, Leaf leaf2) internal pure returns (bool) {
    unchecked {
      return Leaf.unwrap(leaf1) == Leaf.unwrap(leaf2);
    }
  }

  function isEmpty(Leaf leaf) internal pure returns (bool) {
    unchecked {
      return Leaf.unwrap(leaf) == Leaf.unwrap(EMPTY);
    }
  }

  /* `bool -> int` cast */
  function uint_of_bool(bool b) internal pure returns (uint u) {
    unchecked {
      assembly ("memory-safe") {
        u := b
      }
    }
  }

  /* Set either tick's first (at pos*size) or last (at pos*size + 1) */
  function setPosFirstOrLast(Leaf leaf, uint pos, uint id, bool last) internal pure returns (Leaf) {
    unchecked {
      uint before = OFFER_BITS * ((pos * 2) + uint_of_bool(last));

      uint cleanupMask = ~(OFFER_MASK << (256 - OFFER_BITS - before));

      uint shiftedId = id << (256 - OFFER_BITS - before);
      uint newLeaf = Leaf.unwrap(leaf) & cleanupMask | shiftedId;
      return Leaf.wrap(newLeaf);
    }
  }

  /* Assume `leaf` is the leaf of `bin`. Set the first offer of `bin` in leaf to `id`. */
  function setBinFirst(Leaf leaf, Bin bin, uint id) internal pure returns (Leaf) {
    unchecked {
      uint posInLeaf = bin.posInLeaf();
      return setPosFirstOrLast(leaf, posInLeaf, id, false);
    }
  }

  /* Assume `leaf` is the leaf of `bin`. Set the last offer of `bin` in leaf to `id`. */
  function setBinLast(Leaf leaf, Bin bin, uint id) internal pure returns (Leaf) {
    unchecked {
      uint posInLeaf = bin.posInLeaf();
      return setPosFirstOrLast(leaf, posInLeaf, id, true);
    }
  }

  // _This function is not used by Mangrove but is useful for MgvReader._ 
  /* Assumes `leaf` is the leaf of `bin`. Erase contents of bins in `leaf` to (not including) `bin`. */
  function eraseBelow(Leaf leaf, Bin bin) internal pure returns (Leaf) {
    unchecked {
      uint mask = ONES >> ((bin.posInLeaf() + 1) * OFFER_BITS * 2);
      return Leaf.wrap(Leaf.unwrap(leaf) & mask);
    }
  }

  // _This function is not used by Mangrove but is useful for MgvReader._ 
  /* Assumes `leaf` is the leaf of `bin`. Erase contents of bins in `leaf` from (not including) `bin`. */
  function eraseAbove(Leaf leaf, Bin bin) internal pure returns (Leaf) {
    unchecked {
      uint mask = ~(ONES >> (bin.posInLeaf() * OFFER_BITS * 2));
      return Leaf.wrap(Leaf.unwrap(leaf) & mask);
    }
  }


  // _This function is not used by Mangrove but is part of the library._
  /* Return the id of the first offer in `bin`, assuming `bin`'s leaf is `leaf`. */
  function firstOfBin(Leaf leaf, Bin bin) internal pure returns (uint) {
    unchecked {
      return firstOfPos(leaf, bin.posInLeaf());
    }
  }

  /* Return the id of the last offer in `bin`, assuming `bin`'s leaf is `leaf`. */
  function lastOfBin(Leaf leaf, Bin bin) internal pure returns (uint) {
    unchecked {
      return lastOfPos(leaf, bin.posInLeaf());
    }
  }

  /* Return first offer of pair in position `pos` */
  function firstOfPos(Leaf leaf, uint pos) internal pure returns (uint) {
    unchecked {
      uint raw = Leaf.unwrap(leaf);
      return uint(raw << (pos * OFFER_BITS * 2) >> (256 - OFFER_BITS));
    }
  }

  /* Return second offer of pair in position `pos` */
  function lastOfPos(Leaf leaf, uint pos) internal pure returns (uint) {
    unchecked {
      uint raw = Leaf.unwrap(leaf);
      return uint(raw << (OFFER_BITS * ((pos * 2) + 1)) >> (256 - OFFER_BITS));
    }
  }

  /* Return the position of the first pair of `leaf` (0, 1, 2 or 3) that has a nonzero offer.

  Explanation:
  Note that unlike in fields, have their low bin on the most significant bits.
  `pos` is initially 1 if `leaf` has some nonzero bit in its MSB half, 0 otherwise. Then `pos` is `A | B`, where `A` is `iszero(pos)<<1`, so `A` is 0 if leaf has some nonzero bit in its MSB half, 2 otherwise. And `B` is 0 if `leaf >> (pos << 7)` has some nonzero bit in its most significant 192 bits, 1 otherwise.
  */
  function bestNonEmptyBinPos(Leaf leaf) internal pure returns (uint pos) {
    assembly("memory-safe") {
      pos := gt(leaf,0xffffffffffffffffffffffffffffffff)
      pos := or(shl(1,iszero(pos)),iszero(gt(shr(shl(7,pos),leaf),0xffffffffffffffff)))
    }
  }

  /* Return the offer id of the first offer of the first non-empty pair in `leaf`. */
  function bestOfferId(Leaf leaf) internal pure returns (uint offerId) {
    unchecked {
      return firstOfPos(leaf,bestNonEmptyBinPos(leaf));
    }
  }
}


/* Bins are numbered from MIN_BIN to MAX_BIN (inclusive). Each bin contains the offers at a given price. For a given `tickSpacing`, bins represent the following prices (centered on the central bin): 
```
...
1.0001^-(tickSpacing*2)
1.0001^-(tickSpacing*1)
1.0001
1.0001^(tickSpacing*1)
1.0001^(tickSpacing*2)
...
``` 

There are 4 bins per leaf, `4 * 64` bins per level3, etc. The leaf of a bin is the leaf that holds its first/last offer id. The level3 of a bin is the level3 field above its leaf; the level2 of a bin is the level2 field above its level3, etc. */

/* Globally enable `bin.method(...)` */
type Bin is int;
using TickTreeLib for Bin global;

library TickTreeLib {

  function eq(Bin bin1, Bin bin2) internal pure returns (bool) {
    unchecked {
      return Bin.unwrap(bin1) == Bin.unwrap(bin2);
    }
  }

  function inRange(Bin bin) internal pure returns (bool) {
    unchecked {
      return Bin.unwrap(bin) >= MIN_BIN && Bin.unwrap(bin) <= MAX_BIN;
    }
  }

  // _This function is not used by Mangrove but is part of the library for convenience._
  
  /* Not optimized for gas. Returns the bin induced by the branch formed by the arguments. Returned value will make no sense if any of the `Field` arguments are empty. */
  function bestBinFromBranch(uint binPosInLeaf,Field level3, Field level2, Field level1, Field root) internal pure returns (Bin) {
    unchecked {
      Local local;
      local = local.binPosInLeaf(binPosInLeaf).level3(level3).level2(level2).level1(level1).root(root);
      return bestBinFromLocal(local);
    }
  }

  /* Returns tick held by the `bin`, given a `tickSpacing`. */
  function tick(Bin bin, uint tickSpacing) internal pure returns (Tick) {
    return Tick.wrap(Bin.unwrap(bin) * int(tickSpacing));
  }

  /* Returns the bin induced by the branch held in `local`. Returned value will make no sense if any of the fields of `local` are empty. */
  function bestBinFromLocal(Local local) internal pure returns (Bin) {
    unchecked {
      uint ubin = local.binPosInLeaf() |
        ((BitLib.ctz64(Field.unwrap(local.level3())) |
          (BitLib.ctz64(Field.unwrap(local.level2())) |
            (BitLib.ctz64(Field.unwrap(local.level1())) |
              uint(
                (int(BitLib.ctz64(Field.unwrap(local.root())))-ROOT_SIZE/2) << LEVEL_SIZE_BITS)) 
              << LEVEL_SIZE_BITS)
            << LEVEL_SIZE_BITS)
          << LEAF_SIZE_BITS);
      return Bin.wrap(int(ubin));
    }
  }

  /* Returns the index of the leaf that holds `bin` */
  function leafIndex(Bin bin) internal pure returns (int) {
    unchecked {
      return Bin.unwrap(bin) >> LEAF_SIZE_BITS;
    }
  }

  /* Returns the position of `bin` in its leaf. */
  function posInLeaf(Bin bin) internal pure returns (uint) {
    unchecked {
      return uint(Bin.unwrap(bin)) & LEAF_SIZE_MASK;
    }
  }

  /* Returns the index of `bin`'s level3 */
  function level3Index(Bin bin) internal pure returns (int) {
    unchecked {
      return Bin.unwrap(bin) >> (LEAF_SIZE_BITS + LEVEL_SIZE_BITS);
    }
  }

  /* Returns the position of `bin`'s leaf in `bin`'s level3 */
  function posInLevel3(Bin bin) internal pure returns (uint) {
    unchecked {
      return uint(bin.leafIndex()) & LEVEL_SIZE_MASK;
    }
  }

  /* Returns the index of `bin`'s level2 */
  function level2Index(Bin bin) internal pure returns (int) {
    unchecked {
      return Bin.unwrap(bin) >> (LEAF_SIZE_BITS + 2* LEVEL_SIZE_BITS);
    }
  }

  /* Returns the position of `bin`'s level3 in `bin`'s level2 */
  function posInLevel2(Bin bin) internal pure returns (uint) {
    unchecked {
      return uint(bin.level3Index()) & LEVEL_SIZE_MASK;
    }
  }

  /* Returns the index of `bin`'s level1 */
  function level1Index(Bin bin) internal pure returns (int) {
    unchecked {
      return Bin.unwrap(bin) >> (LEAF_SIZE_BITS + 3 * LEVEL_SIZE_BITS);
    }
  }

  /* Returns the position of `bin`'s level2 in `bin`'s level1 */
  function posInLevel1(Bin bin) internal pure returns (uint) {
    unchecked {
      return uint(bin.level2Index()) & LEVEL_SIZE_MASK;
    }
  }

  /* Returns the position of `bin`'s level1 in root */
  function posInRoot(Bin bin) internal pure returns (uint) {
    unchecked {
      return uint(bin.level1Index() + ROOT_SIZE / 2);
    }
  }

  /* `<`, typed for bin */
  function strictlyBetter(Bin bin1, Bin bin2) internal pure returns (bool) {
    unchecked {
      return Bin.unwrap(bin1) < Bin.unwrap(bin2);
    }
  }

  /* `<=`, typed for bin */
  function better(Bin bin1, Bin bin2) internal pure returns (bool) {
    unchecked {
      return Bin.unwrap(bin1) <= Bin.unwrap(bin2);
    }
  }  

}

/* Globally enable `field.method(...)` */
type Field is uint;
using FieldLib for Field global;

/* Fields are bit fields. Each bit position of a field corresponds to a child of the node in the tick tree. The i-th bit of a field is set iff its child is the parent of at least one non-emtpy field.

Using bit fields market orders can locate the next bin containing an offer in constant time: once a leaf has been emptied, one must at most walk up along the branch of the current bin, up to the first 1 in a field, then go down to the of the tree, then go down the corresponding child to the nonempty bin found.
*/

/* In fields, positions are counted from the least significant bits */
library FieldLib {
  Field constant EMPTY = Field.wrap(uint(0));

  /* Checks if a field is dirty or not (see below for more on dirty fields). */
  function dirty(Field field) internal pure returns (DirtyField) {
    unchecked {
      assembly ("memory-safe") {
        field := or(TOPBIT,field)
      }
      return DirtyField.wrap(Field.unwrap(field));
    }
  }

  function eq(Field field1, Field field2) internal pure returns (bool) {
    unchecked {
      return Field.unwrap(field1) == Field.unwrap(field2);
    }
  }

  function isEmpty(Field field) internal pure returns (bool) {
    unchecked {
      return Field.unwrap(field) == Field.unwrap(EMPTY);
    }
  }

  /* Flip the bit at the position of `bin`'s leaf */
  function flipBitAtLevel3(Field level3, Bin bin) internal pure returns (Field) {
    unchecked {
      uint pos = bin.posInLevel3();
      level3 = Field.wrap(Field.unwrap(level3) ^ (1 << pos));
      return level3;
    }
  }

  /* Flip the bit at the position of `bin`'s level3 */
  function flipBitAtLevel2(Field level2, Bin bin) internal pure returns (Field) {
    unchecked {
      uint pos = bin.posInLevel2();
      level2 = Field.wrap(Field.unwrap(level2) ^ (1 << pos));
      return level2;
    }
  }

  /* Flip the bit at the position of `bin`'s level2 */
  function flipBitAtLevel1(Field level1, Bin bin) internal pure returns (Field) {
    unchecked {
      uint pos = bin.posInLevel1();
      level1 = Field.wrap(Field.unwrap(level1) ^ (1 << pos));
      return level1;
    }
  }

  /* Flip the bit at the position of `bin`'s level1 */
  function flipBitAtRoot(Field root, Bin bin) internal pure returns (Field) {
    unchecked {
      uint pos = bin.posInRoot();
      root = Field.wrap(Field.unwrap(root) ^ (1 << pos));
      return root;
    }
  }

  // _This function is not used by Mangrove but is useful for MgvReader._ 
  /* Assumes `field` is the level3 of `bin`. Erase contents of field up to (not including) bin. */
  function eraseBelowInLevel3(Field field, Bin bin) internal pure returns (Field) {
    unchecked {
      uint mask = ONES << (bin.posInLevel3() + 1);
      return Field.wrap(Field.unwrap(field) & mask);
    }
  }

  // _This function is not used by Mangrove but is useful for MgvReader._ 
  /* Assumes `field` is the level3 of `bin`. Erase contents of field from (not including) bin. */
  function eraseAboveInLevel3(Field field, Bin bin) internal pure returns (Field) {
    unchecked {
      uint mask = ~(ONES << bin.posInLevel3());
      return Field.wrap(Field.unwrap(field) & mask);
    }
  }

  // _This function is not used by Mangrove but is useful for MgvReader._ 
  /* Assumes `field` is the level2 of `bin`. Erase contents of field up to (not including) bin. */
  function eraseBelowInLevel2(Field field, Bin bin) internal pure returns (Field) {
    unchecked {
      uint mask = ONES << (bin.posInLevel2() + 1);
      return Field.wrap(Field.unwrap(field) & mask);
    }
  }

  // _This function is not used by Mangrove but is useful for MgvReader._ 
  /* Assumes `field` is the level2 of `bin`. Erase contents of field from (not including) bin. */
  function eraseAboveInLevel2(Field field, Bin bin) internal pure returns (Field) {
    unchecked {
      uint mask = ~(ONES << bin.posInLevel2());
      return Field.wrap(Field.unwrap(field) & mask);
    }
  }

  // _This function is not used by Mangrove but is useful for MgvReader._ 
  /* Assumes `field` is the level1 of `bin`. Erase contents of field up to (not including) bin. */
  function eraseBelowInLevel1(Field field, Bin bin) internal pure returns (Field) {
    unchecked {
      uint mask = ONES << (bin.posInLevel1() + 1);
      return Field.wrap(Field.unwrap(field) & mask);
    }
  }

  // _This function is not used by Mangrove but is useful for MgvReader._ 
  /* Assumes `field` is the level1 of `bin`. Erase contents of field from (not including) bin. */
  function eraseAboveInLevel1(Field field, Bin bin) internal pure returns (Field) {
    unchecked {
      uint mask = ~(ONES << bin.posInLevel1());
      return Field.wrap(Field.unwrap(field) & mask);
    }
  }

  // _This function is not used by Mangrove but is useful for MgvReader._ 
  /* Assumes `field` is the root of `bin`. Erase contents of field up to (not including) bin. */
  function eraseBelowInRoot(Field field, Bin bin) internal pure returns (Field) {
    unchecked {
      uint mask = ONES << (bin.posInRoot() + 1);
      return Field.wrap(Field.unwrap(field) & mask);
    }
  }

  // _This function is not used by Mangrove but is useful for MgvReader._ 
  /* Assumes `field` is the root of `bin`. Erase contents of field from (not including) bin. */
  function eraseAboveInRoot(Field field, Bin bin) internal pure returns (Field) {
    unchecked {
      uint mask = ~(ONES << bin.posInRoot());
      return Field.wrap(Field.unwrap(field) & mask);
    }
  }

  /* Returns first nonzero position of `field`. Will return 64 if field is empty */
  function firstOnePosition(Field field) internal pure returns (uint) {
    unchecked {
      return BitLib.ctz64(Field.unwrap(field));
    }
  }

  // _This function is not used by Mangrove but is useful for MgvReader._
  /* Returns last nonzero position of `field`. Will return 256 if field is empty */
  function lastOnePosition(Field field) internal pure returns (uint) {
    unchecked {
      return BitLib.fls(Field.unwrap(field));
    }
  }

  /* Return index of the first nonempty level1 below `root` (`root` should not be empty) */
  function firstLevel1Index(Field root) internal pure returns (int) {
    unchecked {
      return int(root.firstOnePosition()) - ROOT_SIZE / 2;
    }
  }

  // _This function is not used by Mangrove but is useful for MgvReader._
  /* Return index of the last nonempty level1 below `root` (root should not be empty) */
  function lastLevel1Index(Field root) internal pure returns (int) {
    unchecked {
      return int(root.lastOnePosition()) - ROOT_SIZE / 2;
    }
  }

  /* Return index of the first nonempty level2 below `level1` assuming its index is `level1Index` (`level1` should not be empty). */
  function firstLevel2Index(Field level1, int level1Index) internal pure returns (int) {
    unchecked {
      return level1Index * LEVEL_SIZE + int(level1.firstOnePosition());
    }
  }
  // _This function is not used by Mangrove but is useful for MgvReader._
  /* Return index of the last nonempty level2 below `level1` assuming its index is `level1Index` (`level1` should not be empty). */
  function lastLevel2Index(Field level1, int level1Index) internal pure returns (int) {
    unchecked {
      return level1Index * LEVEL_SIZE + int(level1.lastOnePosition());
    }
  }

  /* Return index of the first nonempty level3 below `level2` assuming its index is `level2Index` (`level2` should not be empty). */
  function firstLevel3Index(Field level2, int level2Index) internal pure returns (int) {
    unchecked {
      return level2Index * LEVEL_SIZE + int(level2.firstOnePosition());
    }
  }

  // _This function is not used by Mangrove but is useful for MgvReader._
  /* Return index of the last nonempty level3 below `level2` assuming its index is `level2Index` (`level2` should not be empty). */
  function lastLevel3Index(Field level2, int level2Index) internal pure returns (int) {
    unchecked {
      return level2Index * LEVEL_SIZE + int(level2.lastOnePosition());
    }
  }

  /* Return index of the first nonempty leaf below `level3` assuming its index is `level3Index` (`level3` should not be empty). */
  function firstLeafIndex(Field level3, int level3Index) internal pure returns (int) {
    unchecked {
      return level3Index * LEVEL_SIZE + int(level3.firstOnePosition());
    }
  }

  // _This function is not used by Mangrove but is useful for MgvReader._
  /* Return index of the last nonempty leaf below `level3` assuming its index is `level3Index` (`level3` should not be empty). */
  function lastLeafIndex(Field level3, int level3Index) internal pure returns (int) {
    unchecked {
      return level3Index * LEVEL_SIZE + int(level3.lastOnePosition());
    }
  }

 }

/* ## Clean/Dirty Fields and Leaves

To save gas, leaves and fields at never zeroed out but written with a dirty bit. This is especially helpful when the price oscillates quickly between two nodes.

Leaves don't have 'available bits' so an empty leaf is encoded as 1. This is not a valid leaf because for every offer pair in a leaf, either both are 0 (the bin is empty) or both are nonzero (they are the same if the bin has a single offer).
*/

/* Globally enable `dirtyLeaf.method(...)` */
type DirtyLeaf is uint;
using DirtyLeafLib for DirtyLeaf global;

library DirtyLeafLib {
  
  /* Return 0 if leaf is 1, leaf otherwise */
  function clean(DirtyLeaf leaf) internal pure returns (Leaf) {
    unchecked {
      assembly ("memory-safe") {
        leaf := xor(eq(leaf,ONE),leaf)
      }
      return Leaf.wrap(DirtyLeaf.unwrap(leaf));
    }
  }
  function isDirty(DirtyLeaf leaf) internal pure returns (bool) {
    unchecked {
      return DirtyLeaf.unwrap(leaf) == ONE;
    }
  }
  function eq(DirtyLeaf leaf1, DirtyLeaf leaf2) internal pure returns (bool) {
    unchecked {
      return DirtyLeaf.unwrap(leaf1) == DirtyLeaf.unwrap(leaf2);
    }
  }
}

/* We use `TOPBIT` as the optimized in-storage value since 1 is a valid Field value */

/* For fields, it's simpler, since they do not use 64 bits we store the word with top bit at 1 and everything else at 0 to mark emptiness. */

/* Globally enable `dirtyField.method(...)` */
type DirtyField is uint;
using DirtyFieldLib for DirtyField global;

library DirtyFieldLib {
  DirtyField constant DIRTY_EMPTY = DirtyField.wrap(TOPBIT);
  DirtyField constant CLEAN_EMPTY = DirtyField.wrap(0);

  /* Return clean field with topbit set to 0 */
  function clean(DirtyField field) internal pure returns (Field) {
    unchecked {
      assembly ("memory-safe") {
        field := and(NOT_TOPBIT,field)
      }
      return Field.wrap(DirtyField.unwrap(field));
    }
  }
  function isDirty(DirtyField field) internal pure returns (bool) {
    unchecked {
      return DirtyField.unwrap(field) & TOPBIT == TOPBIT;
    }
  }

  function eq(DirtyField leaf1, DirtyField leaf2) internal pure returns (bool) {
    unchecked {
      return DirtyField.unwrap(leaf1) == DirtyField.unwrap(leaf2);
    }
  }
}

File 14 of 28 : TickLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import {Bin} from "@mgv/lib/core/TickTreeLib.sol";
import "@mgv/lib/core/BitLib.sol";
import "@mgv/lib/core/Constants.sol";

/* This file is inspired by Uniswap's approach to ticks, with the following notable changes:
- directly compute ticks base 1.0001 (not base `sqrt(1.0001)`)
- directly compute ratios (not `sqrt(ratio)`) (simpler code elsewhere when dealing with actual ratios and logs of ratios)
- ratios are floating-point numbers, not fixed-point numbers (increases precision when computing amounts)
*/


/* # TickLib

The `TickLib` file contains tick math-related code and utilities for manipulating ticks. It also holds functions related to ratios, which are represented as (mantissa,exponent) pairs. */

/* Globally enable `tick.method(...)` */
type Tick is int;
using TickLib for Tick global;

library TickLib {

  function inRange(Tick tick) internal pure returns (bool) {
    return Tick.unwrap(tick) >= MIN_TICK && Tick.unwrap(tick) <= MAX_TICK;
  }

  function eq(Tick tick1, Tick tick2) internal pure returns (bool) {
    unchecked {
      return Tick.unwrap(tick1) == Tick.unwrap(tick2);
    }
  }

  /* Returns the nearest, higher bin to the given `tick` at the given `tickSpacing`
    
    We do not force ticks to fit the tickSpacing (aka `tick%tickSpacing==0`). Ratios are rounded up that the maker is always paid at least what they asked for
  */
  function nearestBin(Tick tick, uint tickSpacing) internal pure returns (Bin bin) {
    unchecked {
      // By default division rounds towards 0. Since `smod` is signed we get the sign of `tick` and `tick%tickSpacing` in a single instruction.
      assembly("memory-safe") {
        bin := sdiv(tick,tickSpacing)
        bin := add(bin,sgt(smod(tick,tickSpacing),0))
      }
    }
  }

  /* ## Conversion functions */

  /* ### (inbound,tick) → outbound 
  `inboundFromOutbound[Up]` converts an outbound amount (i.e. an `offer.gives` or a `takerWants`), to an inbound amount, following the price induced by `tick`. There's a rounding-up and a rounding-down variant.

  `outboundAmt` should not exceed 127 bits.
  */
  function inboundFromOutbound(Tick tick, uint outboundAmt) internal pure returns (uint) {
    (uint sig, uint exp) = ratioFromTick(tick);
    return (sig * outboundAmt) >> exp;
  }

  function inboundFromOutboundUp(Tick tick, uint outboundAmt) internal pure returns (uint) {
    unchecked {
      (uint sig, uint exp) = ratioFromTick(tick);
      return divExpUp(sig*outboundAmt,exp);
    }
  }

  /* ### (outbound,tick) → inbound */
  /* `outboundFromInbound[Up]` converts an inbound amount (i.e. an `offer.wants` or a `takerGives`), to an outbound amount, following the price induced by `tick`. There's a rounding-up and a rounding-down variant.

  `inboundAmt` should not exceed 127 bits.
  */
  function outboundFromInbound(Tick tick, uint inboundAmt) internal pure returns (uint) {
    (uint sig, uint exp) = ratioFromTick(Tick.wrap(-Tick.unwrap(tick)));
    return (sig * inboundAmt) >> exp;
  }

  function outboundFromInboundUp(Tick tick, uint inboundAmt) internal pure returns (uint) {
    unchecked {
      (uint sig, uint exp) = ratioFromTick(Tick.wrap(-Tick.unwrap(tick)));
      return divExpUp(sig*inboundAmt,exp);
    }
  }

  /* ## Ratio representation

  Ratios are represented as a (mantissa,exponent) pair which represents the number `mantissa * 2**-exponent`.

  The exponent is negated so that, for ratios in the accepted range, the exponent is `>= 0`. This simplifies the code.

  Floats are normalized so that the mantissa uses exactly 128 bits. It enables easy comparison between floats and ensures they can be multiplied by amounts without overflow.

  The accepted ratio range is between `ratioFromTick(MIN_TICK)` and `ratioFromTick(MAX_TICK)` (inclusive).
  */
  

  /* ### (inbound,outbound) → ratio */

  /* `ratioFromVolumes` converts a pair of (inbound,outbound) volumes to a floating-point, normalized ratio. It rounds down.
  * `outboundAmt = 0` has a special meaning and the highest possible price will be returned.
  * `inboundAmt = 0` has a special meaning if `outboundAmt != 0` and the lowest possible price will be returned.
  */
  function ratioFromVolumes(uint inboundAmt, uint outboundAmt) internal pure returns (uint mantissa, uint exp) {
    unchecked {
      require(inboundAmt <= MAX_SAFE_VOLUME, "mgv/ratioFromVol/inbound/tooBig");
      require(outboundAmt <= MAX_SAFE_VOLUME, "mgv/ratioFromVol/outbound/tooBig");
      if (outboundAmt == 0) {
        return (MAX_RATIO_MANTISSA,uint(MAX_RATIO_EXP));
      } else if (inboundAmt == 0) {
        return (MIN_RATIO_MANTISSA,uint(MIN_RATIO_EXP));
      }
      uint ratio = (inboundAmt << MANTISSA_BITS) / outboundAmt; 
      uint log2 = BitLib.fls(ratio);
      require(ratio != 0,"mgv/ratioFromVolumes/zeroRatio");
      if (log2 > MANTISSA_BITS_MINUS_ONE) {
        uint diff = log2 - MANTISSA_BITS_MINUS_ONE;
        return (ratio >> diff, MANTISSA_BITS - diff);
      } else {
        uint diff = MANTISSA_BITS_MINUS_ONE - log2;
        return (ratio << diff, MANTISSA_BITS + diff);
      }
    }
  }

  /* ### (inbound,outbound) → tick */
  function tickFromVolumes(uint inboundAmt, uint outboundAmt) internal pure returns (Tick tick) {
    (uint man, uint exp) = ratioFromVolumes(inboundAmt, outboundAmt);
    return tickFromNormalizedRatio(man,exp);
  }

  /* ### ratio → tick */
  /* Does not require a normalized ratio. */
  function tickFromRatio(uint mantissa, int exp) internal pure returns (Tick) {
    uint normalized_exp;
    (mantissa, normalized_exp) = normalizeRatio(mantissa, exp);
    return tickFromNormalizedRatio(mantissa,normalized_exp);
  }

  /* ### low-level ratio → tick */
  /* Given `ratio`, return greatest tick `t` such that `ratioFromTick(t) <= ratio`. 
  * Input ratio must be within the maximum and minimum ratios returned by the available ticks. 
  * Does _not_ expected a normalized float.
  
  The function works as follows:
  * Approximate log2(ratio) to the 13th fractional digit.
  * Following <a href="https://hackmd.io/@mangrovedao/HJvl21zla">https://hackmd.io/@mangrovedao/HJvl21zla</a>, obtain `tickLow` and `tickHigh` such that $\log_{1.0001}(ratio)$ is between them
  * Return the highest one that yields a ratio below the input ratio.
  */
  function tickFromNormalizedRatio(uint mantissa, uint exp) internal pure returns (Tick tick) {
    if (floatLt(mantissa, exp, MIN_RATIO_MANTISSA, uint(MIN_RATIO_EXP))) {
      revert("mgv/tickFromRatio/tooLow");
    }
    if (floatLt(MAX_RATIO_MANTISSA, uint(MAX_RATIO_EXP), mantissa, exp)) {
      revert("mgv/tickFromRatio/tooHigh");
    }
    int log2ratio = int(MANTISSA_BITS_MINUS_ONE) - int(exp) << 64;
    uint mpow = mantissa >> MANTISSA_BITS_MINUS_ONE - 127; // give 129 bits of room left

    /* How the fractional digits of the log are computed: 
    * for a given `n` compute $n^2$. 
    * If $\lfloor\log_2(n^2)\rfloor = 2\lfloor\log_2(n)\rfloor$ then the fractional part of $\log_2(n^2)$ was $< 0.5$ (first digit is 0). 
    * If $\lfloor\log_2(n^2)\rfloor = 1 + 2\lfloor\log_2(n)\rfloor$ then the fractional part of $\log_2(n^2)$ was $\geq 0.5$ (first digit is 1).
    * Apply starting with `n=mpow` repeatedly by keeping `n` on 127 bits through right-shifts (has no impact on high fractional bits).
    */

    assembly ("memory-safe") {
      // 13 bits of precision
      mpow := shr(127, mul(mpow, mpow))
      let highbit := shr(128, mpow)
      log2ratio := or(log2ratio, shl(63, highbit))
      mpow := shr(highbit, mpow)

      mpow := shr(127, mul(mpow, mpow))
      highbit := shr(128, mpow)
      log2ratio := or(log2ratio, shl(62, highbit))
      mpow := shr(highbit, mpow)

      mpow := shr(127, mul(mpow, mpow))
      highbit := shr(128, mpow)
      log2ratio := or(log2ratio, shl(61, highbit))
      mpow := shr(highbit, mpow)

      mpow := shr(127, mul(mpow, mpow))
      highbit := shr(128, mpow)
      log2ratio := or(log2ratio, shl(60, highbit))
      mpow := shr(highbit, mpow)

      mpow := shr(127, mul(mpow, mpow))
      highbit := shr(128, mpow)
      log2ratio := or(log2ratio, shl(59, highbit))
      mpow := shr(highbit, mpow)

      mpow := shr(127, mul(mpow, mpow))
      highbit := shr(128, mpow)
      log2ratio := or(log2ratio, shl(58, highbit))
      mpow := shr(highbit, mpow)

      mpow := shr(127, mul(mpow, mpow))
      highbit := shr(128, mpow)
      log2ratio := or(log2ratio, shl(57, highbit))
      mpow := shr(highbit, mpow)

      mpow := shr(127, mul(mpow, mpow))
      highbit := shr(128, mpow)
      log2ratio := or(log2ratio, shl(56, highbit))
      mpow := shr(highbit, mpow)

      mpow := shr(127, mul(mpow, mpow))
      highbit := shr(128, mpow)
      log2ratio := or(log2ratio, shl(55, highbit))
      mpow := shr(highbit, mpow)

      mpow := shr(127, mul(mpow, mpow))
      highbit := shr(128, mpow)
      log2ratio := or(log2ratio, shl(54, highbit))
      mpow := shr(highbit, mpow)

      mpow := shr(127, mul(mpow, mpow))
      highbit := shr(128, mpow)
      log2ratio := or(log2ratio, shl(53, highbit))
      mpow := shr(highbit, mpow)

      mpow := shr(127, mul(mpow, mpow))
      highbit := shr(128, mpow)
      log2ratio := or(log2ratio, shl(52, highbit))
      mpow := shr(highbit, mpow)

      mpow := shr(127, mul(mpow, mpow))
      highbit := shr(128, mpow)
      log2ratio := or(log2ratio, shl(51, highbit))
    }

    // Convert log base 2 to log base 1.0001 (multiply by `log2(1.0001)^-1 << 64`), since log2ratio is x64 this yields a x128 number.
    int log_bp_ratio = log2ratio * 127869479499801913173571;

    // tickLow is approx - maximum error
    int tickLow = int((log_bp_ratio - 1701496478404567508395759362389778998) >> 128);
    // tickHigh is approx + minimum error
    int tickHigh = int((log_bp_ratio + 289637967442836606107396900709005211253) >> 128);

    (uint mantissaHigh, uint expHigh) = ratioFromTick(Tick.wrap(tickHigh));

    bool ratioHighGt = floatLt(mantissa, exp, mantissaHigh, expHigh);
    if (tickLow == tickHigh || ratioHighGt) {
      tick = Tick.wrap(tickLow);
    } else { 
      tick = Tick.wrap(tickHigh);
    }
  }

  /* ### tick → ratio conversion function */
  /* Returns a normalized (man,exp) ratio floating-point number. The mantissa is on 128 bits to avoid overflow when mulitplying with token amounts. The exponent has no bias. for easy comparison. */
  function ratioFromTick(Tick tick) internal pure returns (uint man, uint exp) {
    unchecked {
      (man, exp) = nonNormalizedRatioFromTick(tick);
      int shiftedTick = Tick.unwrap(tick) << LOG_BP_SHIFT;
      int log2ratio;
      // floor log2 of ratio towards negative infinity
      assembly ("memory-safe") {
        log2ratio := sdiv(shiftedTick,LOG_BP_2X235)
        log2ratio := sub(log2ratio,slt(smod(shiftedTick,LOG_BP_2X235),0))
      }
      int diff = log2ratio+int(exp)-int(MANTISSA_BITS_MINUS_ONE);
      if (diff > 0) {
        // For |tick| <= 887272, this drops at most 5 bits of precision
        man = man >> uint(diff);
      } else {
        man = man << uint(-diff);
      }
      // For |tick| << 887272, log2ratio <= 127
      exp = uint(int(MANTISSA_BITS_MINUS_ONE)-log2ratio);
    }
  }

  /* ### low-level tick → ratio conversion */
  /* Compute 1.0001^tick and returns it as a (mantissa,exponent) pair. Works by checking each set bit of `|tick|` multiplying by `1.0001^(-2**i)<<128` if the ith bit of tick is set. Since we inspect the absolute value of `tick`, `-1048576` is not a valid tick. If the tick is positive this computes `1.0001^-tick`, and we take the inverse at the end. For maximum precision some powers of 1.0001 are shifted until they occupy 128 bits. The `extra_shift` is recorded and added to the exponent.

  Since the resulting mantissa is left-shifted by 128 bits, if tick was positive, we divide `2**256` by the mantissa to get the 128-bit left-shifted inverse of the mantissa.

  The error (relative to 1.0001^tick) may be negative or positive.
  */
  function nonNormalizedRatioFromTick(Tick tick) internal pure returns (uint man, uint exp) {
    uint absTick = Tick.unwrap(tick) < 0 ? uint(-Tick.unwrap(tick)) : uint(Tick.unwrap(tick));
    require(absTick <= uint(MAX_TICK), "mgv/absTick/outOfBounds");

    int extra_shift;
    if (absTick & 0x1 != 0) {
      man = 0xfff97272373d413259a46990580e2139;
    } else {
      man = 0x100000000000000000000000000000000;
    }
    if (absTick & 0x2 != 0) {
      man = (man * 0xfff2e50f5f656932ef12357cf3c7fdcb) >> 128;
    }
    if (absTick & 0x4 != 0) {
      man = (man * 0xffe5caca7e10e4e61c3624eaa0941ccf) >> 128;
    }
    if (absTick & 0x8 != 0) {
      man = (man * 0xffcb9843d60f6159c9db58835c926643) >> 128;
    }
    if (absTick & 0x10 != 0) {
      man = (man * 0xff973b41fa98c081472e6896dfb254bf) >> 128;
    }
    if (absTick & 0x20 != 0) {
      man = (man * 0xff2ea16466c96a3843ec78b326b52860) >> 128;
    }
    if (absTick & 0x40 != 0) {
      man = (man * 0xfe5dee046a99a2a811c461f1969c3052) >> 128;
    }
    if (absTick & 0x80 != 0) {
      man = (man * 0xfcbe86c7900a88aedcffc83b479aa3a3) >> 128;
    }
    if (absTick & 0x100 != 0) {
      man = (man * 0xf987a7253ac413176f2b074cf7815e53) >> 128;
    }
    if (absTick & 0x200 != 0) {
      man = (man * 0xf3392b0822b70005940c7a398e4b70f2) >> 128;
    }
    if (absTick & 0x400 != 0) {
      man = (man * 0xe7159475a2c29b7443b29c7fa6e889d8) >> 128;
    }
    if (absTick & 0x800 != 0) {
      man = (man * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
    }
    if (absTick & 0x1000 != 0) {
      man = (man * 0xa9f746462d870fdf8a65dc1f90e061e4) >> 128;
    }
    if (absTick & 0x2000 != 0) {
      man = (man * 0xe1b0d342ada5437121767bec575e65ed) >> 128;
      extra_shift += 1;
    }
    if (absTick & 0x4000 != 0) {
      man = (man * 0xc6f84d7e5f423f66048c541550bf3e96) >> 128;
      extra_shift += 2;
    }
    if (absTick & 0x8000 != 0) {
      man = (man * 0x9aa508b5b7a84e1c677de54f3e99bc8f) >> 128;
      extra_shift += 4;
    }
    if (absTick & 0x10000 != 0) {
      man = (man * 0xbad5f1bdb70232cd33865244bdcc089c) >> 128;
      extra_shift += 9;
    }
    if (absTick & 0x20000 != 0) {
      man = (man * 0x885b9613d7e87aa498106fb7fa5edd37) >> 128;
      extra_shift += 18;
    }
    if (absTick & 0x40000 != 0) {
      man = (man * 0x9142e0723efb884889d1f447715afacd) >> 128;
      extra_shift += 37;
    }
    if (absTick & 0x80000 != 0) {
      man = (man * 0xa4d9a773d61316918f140bd96e8e6814) >> 128;
      extra_shift += 75;
    }
    if (Tick.unwrap(tick) > 0) {
      /* We use [Remco Bloemen's trick](https://xn--2-umb.com/17/512-bit-division/#divide-2-256-by-a-given-number) to divide `2**256` by `man`: */
      assembly("memory-safe") {
        man := add(div(sub(0, man), man), 1)
      }
      extra_shift = -extra_shift;
    }
    exp = uint(128 + extra_shift);

  }

  /* Shift mantissa so it occupies exactly `MANTISSA_BITS` and adjust `exp` in consequence.
  
  A float is normalized when its mantissa occupies exactly 128 bits. All in-range normalized floats have `exp >= 0`, so we can use a `uint` for exponents everywhere we expect a normalized float.

  When a non-normalized float is expected/used, `exp` can be negative since there is no constraint on the size of the mantissa.
  
   */
  function normalizeRatio(uint mantissa, int exp) internal pure returns (uint, uint) {
    require(mantissa != 0,"mgv/normalizeRatio/mantissaIs0");
    uint log2ratio = BitLib.fls(mantissa);
    int shift = int(MANTISSA_BITS_MINUS_ONE) - int(log2ratio);
    if (shift < 0) {
      mantissa = mantissa >> uint(-shift);
    } else {
      mantissa = mantissa << uint(shift);
    }
    exp = exp + shift;
    if (exp < 0) {
      revert("mgv/normalizeRatio/lowExp");
    }
    return (mantissa,uint(exp));
  }

  /* Return `a/(2**e)` rounded up */
  function divExpUp(uint a, uint e) internal pure returns (uint) {
    unchecked {
      uint rem;
      /* 
      Let mask be `(1<<e)-1`, `rem` is 1 if `a & mask > 0`, and 0 otherwise.
      Explanation:
      * if a is 0 then `rem` must be 0. `0 & mask` is 0.
      * else if `e > 255` then `0 < a < 2^e`, so `rem` must be 1. `(1<<e)-1` is `type(uint).max`, so `a & mask is a > 0`.
      * else `a & mask` is `a % 2**e`
      */
      assembly("memory-safe") {
        rem := gt(and(a,sub(shl(e,1),1)),0)
      }
      return (a>>e) + rem;
    }
  }

  /* Floats are normalized to 128 bits to ensure no overflow when multiplying with amounts, and for easier comparisons. Normalized in-range floats have `exp>=0`. */
  function floatLt(uint mantissa_a, uint exp_a, uint mantissa_b, uint exp_b) internal pure returns (bool) {
    /* Exponents are negated (so that exponents of ratios within the accepted range as >= 0, which simplifies the code), which explains the direction of the `exp_a > exp_b` comparison. */ 
    return (exp_a > exp_b || (exp_a == exp_b && mantissa_a < mantissa_b));
  }

}

File 15 of 28 : MgvHasOffers.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.10;

import "@mgv/src/core/MgvLib.sol";
import {MgvCommon} from "./MgvCommon.sol";

/* `MgvHasOffers` contains the state variables and functions common to both market-maker operations and market-taker operations. Mostly: storing offers, removing them, updating market makers' provisions. */
contract MgvHasOffers is MgvCommon {
  /* # Provision debit/credit utility functions */
  /* `balanceOf` is in wei of ETH. */
  function debitWei(address maker, uint amount) internal {
    unchecked {
      uint makerBalance = _balanceOf[maker];
      require(makerBalance >= amount, "mgv/insufficientProvision");
      _balanceOf[maker] = makerBalance - amount;
      emit Debit(maker, amount);
    }
  }

  function creditWei(address maker, uint amount) internal {
    unchecked {
      _balanceOf[maker] += amount;
      emit Credit(maker, amount);
    }
  }

  /* # Misc. low-level functions */
  /* ## Offer deletion */

  /* When an offer is deleted, it is marked as such by setting `gives` to 0 and leaving other fields intact. Note that provision accounting in Mangrove aims to minimize writes. Each maker `fund`s Mangrove to increase its balance. When an offer is created/updated, we compute how much should be reserved to pay for possible penalties. That amount can always be recomputed with

  
  ```
  offerDetail.gasprice * 1e6 * (offerDetail.gasreq + offerDetail.offer_gasbase)
  ``` 
  
  The balance is updated to reflect the remaining available ethers.

     Now, when an offer is deleted, the offer can stay provisioned, or be `deprovision`ed. In the latter case, we set `gasprice` to 0, which induces a provision of 0. All code calling `dirtyDeleteOffer` with `deprovision` set to `true` must be careful to correctly account for where that provision is going (back to the maker's `balanceOf`, or sent to a taker as compensation). */
  function dirtyDeleteOffer(OfferData storage offerData, Offer offer, OfferDetail offerDetail, bool deprovision)
    internal
  {
    unchecked {
      offer = offer.gives(0);
      if (deprovision) {
        offerDetail = offerDetail.gasprice(0);
      }
      offerData.offer = offer;
      offerData.detail = offerDetail;
    }
  }

  /* ## Removing an offer from the tick tree */

  /* To remove an offer from the tick tree, we do the following:
  - Remove the offer from the bin
  - If the bin is now empty, mark it as empty in its leaf
    - If the leaf is now empty, mark it as empty in its level3
      - If the level3 is now empty, mark it as empty in its level2
        - If the level2 is now empty, mark it as empty in its level1
          - If the level1 is now empty, mark it as empty in the root
            - If the root is now empty, return
  - Once we are done marking leaves/fields as empty, if the removed offer was the best there is at least one remaining offer, and if the caller requested it by setting `shouldUpdateBranch=true`, go down the tree and find the new best offer.

  Each step must take into account the fact that the branch of the best offer is cached in `local` and that loading a new best offer requires caching a different branch in `local`.

  The reason why the caller might set `shouldUpdateBranch=false` is that it is itself about to insert an offer better than the current best offer. In that case, it will take care of caching the branch of the new best offer after calling `dislodgeOffer`.

  The new updated local is returned, along with whether the branch in local ended up being updated.
  */
  function dislodgeOffer(
    OfferList storage offerList,
    uint tickSpacing,
    Offer offer,
    Local local,
    Bin bestBin,
    bool shouldUpdateBranch
  ) internal returns (Local, bool) {
    unchecked {
      Leaf leaf;
      Bin offerBin = offer.bin(tickSpacing);
      {
        // save stack space
        uint prevId = offer.prev();
        uint nextId = offer.next();
        /* Only load `offer`'s leaf if the offer leaf must be updated. If `offer` is in the middle of a bin's linked list, the bin's first&last offers will not change, so the leaf does not have to be loaded. */
        if (prevId == 0 || nextId == 0) {
          leaf = offerList.leafs[offerBin.leafIndex()].clean();
        }

        /* Update the forward pointer to `offer` (either in a leaf or in an offer's next pointer) */
        if (prevId == 0) {
          /* If `offer` was its bin's first offer, the new first offer is `nextId` (may be 0). */
          leaf = leaf.setBinFirst(offerBin, nextId);
        } else {
          /* Otherwise, the next pointer of `offer`'s prev becomes `nextId`. */
          OfferData storage prevOfferData = offerList.offerData[prevId];
          prevOfferData.offer = prevOfferData.offer.next(nextId);
        }

        /* Update the backward pointer to `offer` (either in a leaf or in an offer's prev pointer) */
        if (nextId == 0) {
          /* If `offer` was its bin's last offer, the new last offer is `prevId` (may be 0). */
          leaf = leaf.setBinLast(offerBin, prevId);
        } else {
          /* Otherwise, the prev pointer of `offer`'s next becomes `prevId`. */
          OfferData storage nextOfferData = offerList.offerData[nextId];
          nextOfferData.offer = nextOfferData.offer.prev(prevId);
        }

        /* If previous pointer updates only updated offer pointers, `offer`'s leaf has not changed and we can return early */
        if (prevId != 0 && nextId != 0) {
          return (local, false);
        }

        /* Only plan on updating the branch if the caller requested it and if `offer` is the best. */
        shouldUpdateBranch = shouldUpdateBranch && prevId == 0 && !bestBin.strictlyBetter(offerBin);
      }

      /* Since `offer` was the first or last of its bin, its leaf must be updated */
      offerList.leafs[offerBin.leafIndex()] = leaf.dirty();
      /* If the leaf is now empty, flip off its bit in `offer`'s level3 */
      if (leaf.isEmpty()) {
        /* We reuse the same `index` variable for all 3 level indices and for the leaf index. */
        int index = offerBin.level3Index();
        /* We reuse the same `field` variable for all 3 level indices. */
        Field field;
        /* _Local cache management conditional_ */
        if (index == bestBin.level3Index()) {
          /* If `offer`'s level3 is cached, update it in `local`. */
          field = local.level3().flipBitAtLevel3(offerBin);
          local = local.level3(field);
          /* If `shouldUpdateBranch=true` and the level3 is now empty, another level3 may take its place. We immediately evict the empty value of level3 to storage. (If the level3 is not empty, then the new best offer will use that level3, so no eviction necessary). */
          if (shouldUpdateBranch && field.isEmpty()) {
            /* Clean/dirty management. `if`s are nested to avoid a useless SLOAD. */
            if (!offerList.level3s[index].eq(DirtyFieldLib.CLEAN_EMPTY)) {
              offerList.level3s[index] = DirtyFieldLib.DIRTY_EMPTY;
            }
          }
        } else {
          /* If `offer`'s level3 is not cached, update it in storage. */
          field = offerList.level3s[index].clean().flipBitAtLevel3(offerBin);
          offerList.level3s[index] = field.dirty();
        }
        /* If `offer`'s level3 is now empty, flip off its bit in the removed offer's level2 */
        if (field.isEmpty()) {
          index = offerBin.level2Index();
          /* _Local cache management conditional_ */
          if (index == bestBin.level2Index()) {
            /* If `offer`'s level2 is cached, update it in `local`. */
            field = local.level2().flipBitAtLevel2(offerBin);
            local = local.level2(field);
            /* If `shouldUpdateBranch=true` and the level2 is now empty, another level2 may take its place. We immediately evict the empty value of level2 to storage. (If the level2 is not empty, then the new best offer will use that level2, so no eviction necessary). */
            if (shouldUpdateBranch && field.isEmpty()) {
              /* Clean/dirty management. Ifs are nested to avoid a useless SLOAD. */
              if (!offerList.level2s[index].eq(DirtyFieldLib.CLEAN_EMPTY)) {
                offerList.level2s[index] = DirtyFieldLib.DIRTY_EMPTY;
              }
            }
          } else {
            /* If `offer`'s level2 is not cached, update it in storage. */
            field = offerList.level2s[index].clean().flipBitAtLevel2(offerBin);
            offerList.level2s[index] = field.dirty();
          }
          /* If `offer`'s level2 is now empty, flip off its bit in `offer`'s level1 */
          if (field.isEmpty()) {
            index = offerBin.level1Index();
            /* _Local cache management conditional_ */
            if (index == bestBin.level1Index()) {
              /* If `offer`'s level1 is cached, update it in `local`. */
              field = local.level1().flipBitAtLevel1(offerBin);
              local = local.level1(field);
              /* If `shouldUpdateBranch=true` and the level1 is now empty, another level1 may take its place. We immediately evict the empty value of level1 to storage. (If the level1 is not empty, then the new best offer will use that level1, so no eviction necessary). */
              if (shouldUpdateBranch && field.isEmpty()) {
                /* Unlike with level3 and level2, level1 cannot be `CLEAN_EMPTY` (it gets dirtied in `activate`) */
                offerList.level1s[index] = field.dirty();
              }
            } else {
              /* If `offer`'s level1 is not cached, update it in storage. */
              field = offerList.level1s[index].clean().flipBitAtLevel1(offerBin);
              offerList.level1s[index] = field.dirty();
            }
            /* If `offer`'s level1 is now empty, flip off its bit in the root field. */
            if (field.isEmpty()) {
              /* root is always in `local` */
              field = local.root().flipBitAtRoot(offerBin);
              local = local.root(field);

              /* If the root is now empty, return the updated `local` */
              if (field.isEmpty()) {
                return (local, shouldUpdateBranch);
              }
              /* Since `offer`'s level1 became empty, if we have to update the branch, load the level1 containing the new best offer in `local`. */
              if (shouldUpdateBranch) {
                index = field.firstLevel1Index();
                field = offerList.level1s[index].clean();
                local = local.level1(field);
              }
            }
            /* Since `offer`'s level2 became empty, if we have to update the branch, load the level2 containing the new best offer in `local`. */
            if (shouldUpdateBranch) {
              index = field.firstLevel2Index(index);
              field = offerList.level2s[index].clean();
              local = local.level2(field);
            }
          }
          /* Since `offer`'s level3 became empty, if we have to update the branch, load the level3 containing the new best offer in `local`. */
          if (shouldUpdateBranch) {
            index = field.firstLevel3Index(index);
            field = offerList.level3s[index].clean();
            local = local.level3(field);
          }
        }
        /* Since `offer`'s leaf became empty, if we have to update the branch, load the leaf containing the new best offer in `leaf` (so that we can find the position of the first non-empty bin in the leaf). */
        if (shouldUpdateBranch) {
          leaf = offerList.leafs[field.firstLeafIndex(index)].clean();
        }
      }
      /* Since `offer`'s bin became empty if we have to update the branch, load the position of the first non-empty bin in the current leaf in `local`. */
      if (shouldUpdateBranch) {
        local = local.binPosInLeaf(leaf.bestNonEmptyBinPos());
      }
    }
    return (local, shouldUpdateBranch);
  }
}

File 16 of 28 : MgvOfferTaking.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.10;

import "./MgvLib.sol";
import {MgvHasOffers} from "./MgvHasOffers.sol";
import {TickTreeLib} from "@mgv/lib/core/TickTreeLib.sol";

/* There are 2 ways to take offers in Mangrove:
- **Market order**. A market order walks the offer list from the best offer and up, can specify a limit price, as well as a buy/sell behaviour (i.e. whether to limit the order buy the amount bought or by the amount sold).
- **Clean**. Since offers can fail, bots can 'clean' specific offers and walk away with the bounty. If an offer does not fail, cleaning it reverts and leaves it in place.
*/
abstract contract MgvOfferTaking is MgvHasOffers {
  /* # MultiOrder struct */
  /* The `MultiOrder` struct is used by market orders and cleans. Some of its fields are only used by market orders. We need a common data structure for both since low-level calls are shared between market orders and cleans. The struct is helpful in decreasing stack use. */
  struct MultiOrder {
    uint totalGot; // used globally by market order, per-offer by cleans
    uint totalGave; // used globally by market order, per-offer by cleans
    uint totalPenalty; // used globally
    address taker; // used globally
    bool fillWants; // used globally
    uint fillVolume; // used globally
    uint feePaid; // used globally
    Leaf leaf; // used by market order
    Tick maxTick; // used globally
    uint maxGasreqForFailingOffers; // used by market order
    uint gasreqForFailingOffers; // used by market order
    uint maxRecursionDepth; // used by market order
  }

  /* # Market Orders */

  /* ## Market Order */
  //+clear+

  /* A market order specifies a (`outbound`, `inbound`,`tickSpacing`) offer list, a limit price it is ready to pay (in the form of `maxTick`, the log base 1.0001 of the price), and a volume `fillVolume`. If `fillWants` is true, that volume is the amount of `olKey.outbound_tkn` the taker wants to buy. If `fillWants` is false, that volume is the amount of `olKey.inbound_tkn` the taker wants to sell.
  
  It returns four `uint`s: the total amount of `olKey.outbound_tkn` received, the total amount of `olKey.inbound_tkn` spent, the penalty received by msg.sender (in wei), and the fee paid by the taker (in wei of `olKey.outbound_tkn`).


  The market order stops when the price exceeds (an approximation of) 1.0001^`maxTick`, or when the end of the book has been reached, or:
  * If `fillWants` is true, the market order stops when `fillVolume` units of `olKey.outbound_tkn` have been obtained. To buy a specific volume of `olKey.outbound_tkn` at any price, set `fillWants` to true, set `fillVolume` to volume you want to buy, and set `maxTick` to the `MAX_TICK` constant.
  * If `fillWants` is false, the market order stops when `fillVolume` units of `olKey.inbound_tkn` have been paid. To sell a specific volume of `olKey.inbound_tkn` at any price, set `fillWants` to false, set `fillVolume` to the volume you want to sell, and set `maxTick` to the `MAX_TICK` constant.
  
  For a maximum `fillVolume` and a maximum (when `fillWants=true`) or minimum (when `fillWants=false`) price, the taker can end up receiving a volume of about `2**255` tokens. */

  function marketOrderByTick(OLKey memory olKey, Tick maxTick, uint fillVolume, bool fillWants)
    public
    returns (uint takerGot, uint takerGave, uint bounty, uint feePaid)
  {
    unchecked {
      return generalMarketOrder(olKey, maxTick, fillVolume, fillWants, msg.sender, 0);
    }
  }

  /* There is a `ByVolume` variant where the taker specifies a desired total amount of `olKey.outbound_tkn` tokens (`takerWants`) and an available total amount of `olKey.inbound_tkn` (`takerGives`). Volumes should fit on 127 bits. */
  function marketOrderByVolume(OLKey memory olKey, uint takerWants, uint takerGives, bool fillWants)
    public
    returns (uint takerGot, uint takerGave, uint bounty, uint feePaid)
  {
    uint fillVolume = fillWants ? takerWants : takerGives;
    Tick maxTick = TickLib.tickFromVolumes(takerGives, takerWants);
    return generalMarketOrder(olKey, maxTick, fillVolume, fillWants, msg.sender, 0);
  }

  /* If the offer list is filled with failing offers such that the default `maxGasreqForFailingOffers` is inadequate, this version of the market order lets the taker specify an upper bound on the gas they are ready to spend on failing offers. */
  function marketOrderByTickCustom(
    OLKey memory olKey,
    Tick maxTick,
    uint fillVolume,
    bool fillWants,
    uint maxGasreqForFailingOffers
  ) public returns (uint takerGot, uint takerGave, uint bounty, uint feePaid) {
    unchecked {
      return generalMarketOrder(olKey, maxTick, fillVolume, fillWants, msg.sender, maxGasreqForFailingOffers);
    }
  }

  /* Get offer after current offer. Will also remove the current offer and return the corresponding updated `local`.

  During a market order, once an offer has been executed, the next one must be fetched. Since offers are structured in a tick tree, the next offer might be:
  - In the same bin, referred to by the `currentOffer.next` pointer.
  - In the same leaf, but in another bin.
  - In a bin that descend from the same level3 but not the same leaf.
  - In a bin that descend from the same level2 but not the same level3.
  - In a bin that descend from the same level1 but not the same level1.
  - In a bin that descend from a different level1.
  Or there might not be a next offer.

  In any case, the 'current branch' is now the branch of the next offer, so `local` must be updated.

  `getNextBest` returns the id of the next offer if there is one (and id 0 otherwise), as well as the updated `local`.

  However, this function is very unsafe taken in isolation:
  - it does not update offer pointers. Since a market order repeatedly calls it and does not inspect the .prev of an offer, the optimization is correct as long as the market order updates the prev pointer of the last offer it sees to 0. After a call, it is your responsibility to do:
      ```
      OfferData storage = offerList.offerData[offerId];
      offerData.offer = offer.prev(0);
      ```
  - it does not flush an updated leaf to storage unless the current leaf has become empty and it needs to load a new one. After a call, it is your responsibility to write the new leaf to storage, if necessary.
  */
  function getNextBest(OfferList storage offerList, MultiOrder memory mor, Offer offer, Local local, uint tickSpacing)
    internal
    returns (uint offerId, Local)
  {
    /* Get tick from current offer tick and tickSpacing */
    Bin offerBin = offer.bin(tickSpacing);
    uint nextId = offer.next();

    /* Update the bin's first offer. If nextId is 0, then the bin's last offer will be updated immediately after. */
    Leaf leaf = mor.leaf;
    leaf = leaf.setBinFirst(offerBin, nextId);

    /* If the current bin is now empty, we go up the tick tree to find the next offer. */
    if (nextId == 0) {
      /* Mark the bin as empty. */
      leaf = leaf.setBinLast(offerBin, 0);
      /* If the current leaf is now empty, we keep going up the tick tree. */
      if (leaf.isEmpty()) {
        /* Flush the current empty leaf since we will load a new one. Note that the slot cannot be empty since we just emptied the leaf (and unlike fields, leaves don't stay cached in another slot). */
        offerList.leafs[offerBin.leafIndex()] = leaf.dirty();

        /* We reuse the same `field` variable for all 3 level indices. */
        Field field = local.level3().flipBitAtLevel3(offerBin);
        /* We reuse the same `index` variable for all 3 level indices and for the leaf index. */
        int index = offerBin.level3Index();
        /* If the current level3 is now empty, we keep going up the tick tree. */
        if (field.isEmpty()) {
          /* Flush the current empty level3 since we will load a new one. We avoid the write if the slot is already cleanly empty. */
          if (!offerList.level3s[index].eq(DirtyFieldLib.CLEAN_EMPTY)) {
            offerList.level3s[index] = DirtyFieldLib.DIRTY_EMPTY;
          }
          index = offerBin.level2Index();
          field = local.level2().flipBitAtLevel2(offerBin);
          /* If the current level2 is now empty, we keep going up the tick tree. */
          if (field.isEmpty()) {
            /* Flush the current empty level2 since we will load a new one. We avoid the write if the slot is already cleanly empty. */
            if (!offerList.level2s[index].eq(DirtyFieldLib.CLEAN_EMPTY)) {
              offerList.level2s[index] = DirtyFieldLib.DIRTY_EMPTY;
            }
            index = offerBin.level1Index();
            field = local.level1().flipBitAtLevel1(offerBin);
            /* If the current level1 is now empty, we keep going up the tick tree. */
            if (field.isEmpty()) {
              /* Flush the current empty level1 since we will load a new one. Unlike with level2 and level3, level1 cannot be `CLEAN_EMPTY` (it gets dirtied in `activate`) */
              offerList.level1s[index] = DirtyFieldLib.DIRTY_EMPTY;
              field = local.root().flipBitAtRoot(offerBin);
              local = local.root(field);
              /* If the root is now empty, we mark all fields in local and the current leaf as empty and return the 0 offer id. */
              if (field.isEmpty()) {
                local = local.level1(field);
                local = local.level2(field);
                local = local.level3(field);
                mor.leaf = LeafLib.EMPTY;
                return (0, local);
              }
              /* Last level1 was empty, load the new level1 */
              index = field.firstLevel1Index();
              // Low-level optim: avoid dirty/clean cycle, dirty bit will be erased anyway.
              field = Field.wrap(DirtyField.unwrap(offerList.level1s[index]));
            }
            /* Store current level1 in `local`. */
            local = local.level1(field);
            /* Last level2 was empty, load the new level2 */
            index = field.firstLevel2Index(index);
            // Low-level optim: avoid dirty/clean cycle, dirty bit will be erased anyway.
            field = Field.wrap(DirtyField.unwrap(offerList.level2s[index]));
          }
          /* Store current level2 in `local`. */
          local = local.level2(field);
          /* Last level3 was empty, load the new level3 */
          index = field.firstLevel3Index(index);
          // Low-level optim: avoid dirty/clean cycle, dirty bit will be erased anyway.
          field = Field.wrap(DirtyField.unwrap(offerList.level3s[index]));
        }
        /* Store current level3 in `local`. */
        local = local.level3(field);
        /* Last leaf was empty, load the new leaf. */
        leaf = offerList.leafs[field.firstLeafIndex(index)].clean();
      }
      /* Find the position of the best non-empty bin in the current leaf, save it to `local`, and read the first offer id of that bin. */
      uint bestNonEmptyBinPos = leaf.bestNonEmptyBinPos();
      local = local.binPosInLeaf(bestNonEmptyBinPos);
      nextId = leaf.firstOfPos(bestNonEmptyBinPos);
    }
    mor.leaf = leaf;
    return (nextId, local);
  }
  /* # General Market Order */
  //+clear+
  /* General market orders set up the market order with a given `taker` (`msg.sender` in the most common case). Returns `(totalGot, totalGave, penaltyReceived, feePaid)`.
  Note that the `taker` can be anyone. This is safe when `taker == msg.sender`, but `generalMarketOrder` must not be called with `taker != msg.sender` unless a security check is done after (see [`MgvOfferTakingWithPermit`](#mgvoffertakingwithpermit.sol)`. */

  function generalMarketOrder(
    OLKey memory olKey,
    Tick maxTick,
    uint fillVolume,
    bool fillWants,
    address taker,
    uint maxGasreqForFailingOffers
  ) internal returns (uint takerGot, uint takerGave, uint bounty, uint feePaid) {
    unchecked {
      /* Checking that `fillVolume` fits in 127 ensures no overflow during the market order recursion. */
      require(fillVolume <= MAX_SAFE_VOLUME, "mgv/mOrder/fillVolume/tooBig");
      require(maxTick.inRange(), "mgv/mOrder/tick/outOfRange");

      /* `MultiOrder` (defined above) maintains information related to the entire market order. */
      MultiOrder memory mor;
      mor.maxTick = maxTick;
      mor.taker = taker;
      mor.fillWants = fillWants;

      /* `SingleOrder` is defined in `MgvLib.sol` and holds information related to the execution of one offer. It also contains `olKey`, which concerns the entire market order, because it will be sent to the maker, who needs that information. */
      MgvLib.SingleOrder memory sor;
      sor.olKey = olKey;
      OfferList storage offerList;
      (sor.global, sor.local, offerList) = _config(olKey);
      mor.maxRecursionDepth = sor.global.maxRecursionDepth();
      /* We have an upper limit on total gasreq for failing offers to avoid failing offers delivering nothing and exhausting gaslimit for the transaction. */
      mor.maxGasreqForFailingOffers =
        maxGasreqForFailingOffers > 0 ? maxGasreqForFailingOffers : sor.global.maxGasreqForFailingOffers();

      /* Throughout the execution of the market order, the `sor`'s offer id and other parameters will change. We start with the current best offer id (0 if the book is empty). */

      mor.leaf = offerList.leafs[sor.local.bestBin().leafIndex()].clean();
      sor.offerId = mor.leaf.bestOfferId();
      sor.offer = offerList.offerData[sor.offerId].offer;

      /* Throughout the market order, `fillVolume` represents the amount left to buy (if `fillWants`) or sell (if `!fillWants`). */
      mor.fillVolume = fillVolume;

      /* For the market order to start, the offer list needs to be both active and not currently protected from reentrancy. */
      activeOfferListOnly(sor.global, sor.local);
      unlockedOfferListOnly(sor.local);

      /* ### Initialization */
      /* The market order will operate as follows : it will go through offers from best to worse, starting from `offerId`, and: */
      /* keep an up-to-date `fillVolume`.
       * not set `prev`/`next` pointers to their correct locations at each offer taken (this is an optimization enabled by forbidding reentrancy).
       * after consuming a segment of offers, will update the current `best` offer to be the best remaining offer on the book. */

      /* We start by enabling the reentrancy lock for this (`olKey.outbound_tkn`,`olKey.inbound_tkn`, `olKey.tickSpacing`) offer list. */
      sor.local = sor.local.lock(true);
      offerList.local = sor.local;

      emit OrderStart(sor.olKey.hash(), taker, maxTick, fillVolume, fillWants);

      /* Call recursive `internalMarketOrder` function.*/
      internalMarketOrder(offerList, mor, sor);

      /* Over the course of the market order, a penalty reserved for `msg.sender` has accumulated in `mor.totalPenalty`. No actual transfers have occurred yet -- all the ethers given by the makers as provision are owned by Mangrove. `sendPenalty` finally gives the accumulated penalty to `msg.sender`. */
      sendPenalty(mor.totalPenalty);

      emit OrderComplete(sor.olKey.hash(), taker, mor.feePaid);

      //+clear+
      return (mor.totalGot, mor.totalGave, mor.totalPenalty, mor.feePaid);
    }
  }

  /* ## Internal market order */
  //+clear+
  /* `internalMarketOrder` works recursively. Going downward, each successive offer is executed until the market order stops (due to: volume exhausted, bad price, or empty offer list). Then the [reentrancy lock is lifted](#internalMarketOrder/liftReentrancy). As the recursion unrolls, each offer's `maker` contract is called again with its remaining gas and given the chance to update its offers on the book. */
  function internalMarketOrder(OfferList storage offerList, MultiOrder memory mor, MgvLib.SingleOrder memory sor)
    internal
  {
    unchecked {
      /* The market order proceeds only if the following conditions are all met:
      - there is some volume left to buy/sell
      - the current best offer is not too expensive
      - there is a current best offer
      - we are not too deep in the recursive calls (stack overflow risk)
      - we are not at risk of consuming too much gas because of failing offers
      */
      if (
        mor.fillVolume > 0 && Tick.unwrap(sor.offer.tick()) <= Tick.unwrap(mor.maxTick) && sor.offerId > 0
          && mor.maxRecursionDepth > 0 && mor.gasreqForFailingOffers <= mor.maxGasreqForFailingOffers
      ) {
        /* ### Market order execution */
        mor.maxRecursionDepth--;

        uint gasused; // gas used by `makerExecute`
        bytes32 makerData; // data returned by maker

        /* <a id="MgvOfferTaking/statusCodes"></a> `mgvData` is a Mangrove status code. It appears in an [`OrderResult`](#MgvLib/OrderResult). Its possible values are:
      * `"mgv/tradeSuccess"`: offer execution succeeded.
      * `"mgv/makerRevert"`: execution of `makerExecute` reverted.
      * `"mgv/makerTransferFail"`: maker could not send olKey.outbound_tkn.
      * `"mgv/makerReceiveFail"`: maker could not receive olKey.inbound_tkn.

      `mgvData` should not be exploitable by the maker! */
        bytes32 mgvData;

        /* Load additional information about the offer. */
        sor.offerDetail = offerList.offerData[sor.offerId].detail;

        /* `execute` attempts to execute the offer by calling its maker. `execute` may modify `mor` and `sor`. It is crucial that an error due to `taker` triggers a revert. That way, if [`mgvData`](#MgvOfferTaking/statusCodes) is not `"mgv/tradeSuccess"`, then the maker is at fault. */
        /* Post-execution, `sor.takerWants`/`sor.takerGives` reflect how much was sent/taken by the offer. We will need it after the recursive call, so we save it in local variables. Same goes for `sor.offerId`, `sor.offer` and `sor.offerDetail`. */
        (gasused, makerData, mgvData) = execute(offerList, mor, sor);

        /* Keep cached copy of current `sor` values to restore them later to send to posthook. */
        uint takerWants = sor.takerWants;
        uint takerGives = sor.takerGives;
        uint offerId = sor.offerId;
        Offer offer = sor.offer;
        OfferDetail offerDetail = sor.offerDetail;

        /* If execution was successful, we decrease `fillVolume`. This cannot underflow, see the [`execute` function](#MgvOfferTaking/computeVolume) for details. */
        if (mgvData == "mgv/tradeSuccess") {
          mor.fillVolume -= mor.fillWants ? takerWants : takerGives;
        }

        /* We move `sor` to the next offer. Note that the current state is inconsistent, since we have not yet updated `sor.offerDetails`. */
        (sor.offerId, sor.local) = getNextBest(offerList, mor, offer, sor.local, sor.olKey.tickSpacing);

        sor.offer = offerList.offerData[sor.offerId].offer;

        /* Recursive call with the next offer. */
        internalMarketOrder(offerList, mor, sor);

        /* Restore `sor` values from before recursive call */
        sor.takerWants = takerWants;
        sor.takerGives = takerGives;
        sor.offerId = offerId;
        sor.offer = offer;
        sor.offerDetail = offerDetail;

        /* After an offer execution, we may run callbacks and increase the total penalty. As that part is common to market orders and cleaning, it lives in its own `postExecute` function. */
        postExecute(mor, sor, gasused, makerData, mgvData);
      } else {
        /* ### Market order end */
        Offer offer = sor.offer;
        Bin bin = offer.bin(sor.olKey.tickSpacing);

        /* If the offer list is not empty, the best offer may need a pointer update and the current leaf must be stored: */
        if (sor.offerId != 0) {
          /* 
          If `offer.prev` is 0, we ended the market order right at the beginning of a bin, so no write is necessary. 
          
          This also explains why the update below is safe when a market takes 0 offers: necessarily at this point offer.prev() is 0, since we are looking at the best offer of the offer list.

          **Warning**: do not locally update offer.prev() before this point, or the test wil spuriously fail and the updated offer will never be written to storage. */
          if (offer.prev() != 0) {
            offerList.offerData[sor.offerId].offer = sor.offer.prev(0);
          }

          int index = bin.leafIndex();
          /* This write may not be necessary if the mor.leaf was just loaded in the last `getNextBest`, but it will be a hot storage write anyway (so not expensive). */
          offerList.leafs[index] = mor.leaf.dirty();
        }

        /* <a id="internalMarketOrder/liftReentrancy"></a>Now that the market order is over, we can lift the lock on the book. In the same operation we

      * lift the reentrancy lock and
      * update the storage

      so we are free from out of order storage writes.
      */
        sor.local = sor.local.lock(false);
        offerList.local = sor.local;

        /* `payTakerMinusFees` keeps the fee in Mangrove, proportional to the amount purchased, and gives the rest to the taker */
        payTakerMinusFees(mor, sor);
      }
    }
  }

  /* # Cleaning */
  /* Cleans multiple offers, i.e. executes them and remove them from the book if they fail, transferring the failure penalty as bounty to the caller. If an offer succeeds, the execution of that offer is reverted, it stays in the book, and no bounty is paid; The `cleanByImpersonation` function itself will not revert.
  
  Its second argument is a `CleanTarget[]` with each `CleanTarget` identifying an offer to clean and the execution parameters that will make it fail. The return values are the number of successfully cleaned offers and the total bounty received.

  Note that Mangrove won't attempt to execute an offer if the values in a `CleanTarget` don't match its offer. To distinguish between a non-executed clean and a fail clean (due to the offer itself not failing), you must inspect the log (see `MgvLib.sol`) or check the received bounty.

  Any `taker` can be impersonated when cleaning because:
  - The function reverts if the offer succeeds, reverting any token transfers.
  - After a `clean` where the offer has failed, all ERC20 token transfers have also been reverted -- but the sender will still have received the bounty of the failing offers. */
  function cleanByImpersonation(OLKey memory olKey, MgvLib.CleanTarget[] calldata targets, address taker)
    external
    returns (uint successes, uint bounty)
  {
    unchecked {
      emit CleanStart(olKey.hash(), taker, targets.length);

      for (uint i = 0; i < targets.length; ++i) {
        bytes memory encodedCall;
        {
          MgvLib.CleanTarget calldata target = targets[i];
          encodedCall = abi.encodeCall(
            this.internalCleanByImpersonation,
            (olKey, target.offerId, target.tick, target.gasreq, target.takerWants, taker)
          );
        }
        bytes memory retdata;
        {
          bool success;
          (success, retdata) = address(this).call(encodedCall);
          if (!success) {
            continue;
          }
        }

        successes++;

        {
          (uint offerBounty) = abi.decode(retdata, (uint));
          bounty += offerBounty;
        }
      }
      sendPenalty(bounty);

      emit CleanComplete();
    }
  }

  function internalCleanByImpersonation(
    OLKey memory olKey,
    uint offerId,
    Tick tick,
    uint gasreq,
    uint takerWants,
    address taker
  ) external returns (uint bounty) {
    unchecked {
      /* `internalClean` must be used with a call (hence the `external` modifier) so its effect can be reverted. But a call from the outside would mean the bounty would get stuck in Mangrove. */
      require(msg.sender == address(this), "mgv/clean/protected");

      MultiOrder memory mor;
      {
        require(tick.inRange(), "mgv/clean/tick/outOfRange");
        mor.maxTick = tick;
      }
      {
        require(takerWants <= MAX_SAFE_VOLUME, "mgv/clean/takerWants/tooBig");
        mor.fillVolume = takerWants;
      }
      mor.taker = taker;
      mor.fillWants = true;

      /* Initialize single order struct. */
      MgvLib.SingleOrder memory sor;
      sor.olKey = olKey;
      OfferList storage offerList;
      (sor.global, sor.local, offerList) = _config(olKey);
      sor.offerId = offerId;
      OfferData storage offerData = offerList.offerData[sor.offerId];
      sor.offer = offerData.offer;
      sor.offerDetail = offerData.detail;

      /* For the cleaning to start, the offer list needs to be both active and not currently protected from reentrancy. */
      activeOfferListOnly(sor.global, sor.local);
      unlockedOfferListOnly(sor.local);

      require(sor.offer.isLive(), "mgv/clean/offerNotLive");
      /* We also check that `gasreq` is not worse than specified. A taker who does not care about `gasreq` can specify any amount larger than the maximum gasreq. */
      require(sor.offerDetail.gasreq() <= gasreq, "mgv/clean/gasreqTooLow");
      require(sor.offer.tick().eq(tick), "mgv/clean/tickMismatch");

      /* We start be enabling the reentrancy lock for this offer list. */
      sor.local = sor.local.lock(true);
      offerList.local = sor.local;

      {
        /* `execute` attempts to execute the offer by calling its maker. `execute` may modify `mor` and `sor`. It is crucial that an error due to `taker` triggers a revert. That way, if [`mgvData`](#MgvOfferTaking/statusCodes) is not `"mgv/tradeSuccess"`, then the maker is at fault. */
        /* Post-execution, `sor.takerWants`/`sor.takerGives` reflect how much was sent/taken by the offer. */
        (uint gasused, bytes32 makerData, bytes32 mgvData) = execute(offerList, mor, sor);

        require(mgvData != "mgv/tradeSuccess", "mgv/clean/offerDidNotFail");

        /* In the market order, we were able to avoid stitching back offers after every `execute` since we knew a segment starting from the best offer would be consumed. Here, we cannot do this optimisation since the offer may be anywhere in the book. So we stitch together offers immediately after `execute`. */
        (sor.local,) = dislodgeOffer(offerList, olKey.tickSpacing, sor.offer, sor.local, sor.local.bestBin(), true);

        /* <a id="internalCleans/liftReentrancy"></a> Now that the current clean is over, we can lift the lock on the book. In the same operation we

        * lift the reentrancy lock and
        * update the storage

        so we are free from out of order storage writes.
        */
        sor.local = sor.local.lock(false);
        offerList.local = sor.local;

        /* No fees are paid since offer execution failed. */

        /* After an offer execution, we may run callbacks and increase the total penalty. As that part is common to market orders and cleans, it lives in its own `postExecute` function. */
        postExecute(mor, sor, gasused, makerData, mgvData);
      }

      bounty = mor.totalPenalty;
    }
  }

  /* # General execution */
  /* During a market order or a clean, offers get executed. The following code takes care of executing a single offer with parameters given by a `SingleOrder` within a larger context given by a `MultiOrder`. */

  /* ## Execute */
  /* Execution of the offer will be attempted with volume limited by the offer's advertised volume.
     **Warning**: The caller must ensure that the price of the offer is low enough; This is not checked here.

     Summary of the meaning of the return values:
    * `gasused` is the gas consumed by the execution
    * `makerData` is the data returned after executing the offer
    * <a id="MgvOfferTaking/internalStatusCodes"></a>`internalMgvData` is a status code internal to `execute`. It can hold [any value that `mgvData` can hold](#MgvOfferTaking/statusCodes). Within `execute`, it can additionally hold the following values:
      * `"mgv/notEnoughGasForMakerTrade"`: cannot give maker close enough to `gasreq`. Triggers a revert of the entire order.
      * `"mgv/takerTransferFail"`: taker could not send olKey.inbound_tkn. Triggers a revert of the entire order.
  */
  function execute(OfferList storage offerList, MultiOrder memory mor, MgvLib.SingleOrder memory sor)
    internal
    returns (uint gasused, bytes32 makerData, bytes32 internalMgvData)
  {
    unchecked {
      {
        uint fillVolume = mor.fillVolume;
        uint offerGives = sor.offer.gives();
        uint offerWants = sor.offer.wants();
        /* <a id="MgvOfferTaking/computeVolume"></a> Volume requested depends on total gives (or wants) by taker. Let `volume = mor.fillWants ? sor.takerWants : sor.takerGives`. One can check that `volume <= fillVolume` in all cases. 
        
        Example with `fillWants=true`: if `offerGives < fillVolume` the first branch of the outer `if` sets `volume = offerGives` and we are done; otherwise the 1st branch of the inner if is taken and sets `volume = fillVolume` and we are done. */
        if ((mor.fillWants && offerGives <= fillVolume) || (!mor.fillWants && offerWants <= fillVolume)) {
          sor.takerWants = offerGives;
          sor.takerGives = offerWants;
        } else {
          if (mor.fillWants) {
            /* While a possible `offer.wants()=0` is the maker's responsibility, a small enough partial fill may round to 0, so we round up. It is immaterial but more fair to the maker. */
            sor.takerGives = sor.offer.tick().inboundFromOutboundUp(fillVolume);
            sor.takerWants = fillVolume;
          } else {
            sor.takerWants = sor.offer.tick().outboundFromInbound(fillVolume);
            sor.takerGives = fillVolume;
          }
        }
      }
      /* The flashloan is executed by call to `flashloan`. If the call reverts, it means the maker failed to send back `sor.takerWants` units of `olKey.outbound_tkn` to the taker. Notes :
       * `msg.sender` is Mangrove itself in those calls -- all operations related to the actual caller should be done outside of this call.
       * any spurious exception due to an error in Mangrove code will be falsely blamed on the Maker and its provision for the offer will be unfairly taken away.
       */
      bool success;
      bytes memory retdata;
      {
        /* Clear fields that maker must not see.

        NB: It should be more efficient to do this in `makerExecute` instead as we would not have to restore the fields afterwards.
        However, for unknown reasons that solution consumes significantly more gas, so we do it here instead. */
        Offer offer = sor.offer;
        sor.offer = offer.clearFieldsForMaker();
        Local local = sor.local;
        sor.local = local.clearFieldsForMaker();

        (success, retdata) = address(this).call(abi.encodeCall(this.flashloan, (sor, mor.taker)));

        /* Restore cleared fields */
        sor.offer = offer;
        sor.local = local;
      }

      /* `success` is true: trade is complete */
      if (success) {
        /* In case of success, `retdata` encodes the gas used by the offer and an arbitrary 256 bits word sent by the maker.  */
        (gasused, makerData) = abi.decode(retdata, (uint, bytes32));
        /* `internalMgvData` indicates trade success */
        internalMgvData = bytes32("mgv/tradeSuccess");

        /* If configured to do so, Mangrove notifies an external contract that a successful trade has taken place. */
        if (sor.global.notify()) {
          IMgvMonitor(sor.global.monitor()).notifySuccess(sor, mor.taker);
        }

        /* We update the totals in the multi order based on the adjusted `sor.takerWants`/`sor.takerGives`. */
        mor.totalGot += sor.takerWants;
        require(mor.totalGot >= sor.takerWants, "mgv/totalGot/overflow");
        mor.totalGave += sor.takerGives;
        require(mor.totalGave >= sor.takerGives, "mgv/totalGave/overflow");
      } else {
        /* In case of failure, `retdata` encodes an [internal status code](#MgvOfferTaking/internalStatusCodes), the gas used by the offer, and an arbitrary 256 bits word sent by the maker.  */
        (internalMgvData, gasused, makerData) = innerDecode(retdata);
        /* Note that in the literals returned are bytes32 (stack values), while the revert arguments are strings (memory pointers). */
        if (
          internalMgvData == "mgv/makerRevert" || internalMgvData == "mgv/makerTransferFail"
            || internalMgvData == "mgv/makerReceiveFail"
        ) {
          /* Update (an upper bound) on gasreq required for failing offers */
          mor.gasreqForFailingOffers += sor.offerDetail.gasreq();
          /* If configured to do so, Mangrove notifies an external contract that a failed trade has taken place. */
          if (sor.global.notify()) {
            IMgvMonitor(sor.global.monitor()).notifyFail(sor, mor.taker);
          }
          /* It is crucial that any error code which indicates an error caused by the taker triggers a revert, because functions that call `execute` consider that when `internalMgvData` is not `"mgv/tradeSuccess"`, then the maker should be blamed. */
        } else if (internalMgvData == "mgv/notEnoughGasForMakerTrade") {
          revert("mgv/notEnoughGasForMakerTrade");
        } else if (internalMgvData == "mgv/takerTransferFail") {
          revert("mgv/takerTransferFail");
        } else {
          /* This code must be unreachable except if the call to flashloan went OOG and there is enough gas to revert here. **Danger**: if a well-crafted offer/maker offer list can force a revert of `flashloan`, Mangrove will be stuck. */
          revert("mgv/swapError");
        }
      }

      /* Delete the offer. The last argument indicates whether the offer should be stripped of its provision (yes if execution failed, no otherwise). We cannot partially strip an offer provision (for instance, remove only the penalty from a failing offer and leave the rest) since the provision associated with an offer is always deduced from the (gasprice,gasbase,gasreq) parameters and not stored independently. We delete offers whether the amount remaining on offer is > density or not for the sake of uniformity (code is much simpler). We also expect prices to move often enough that the maker will want to update their offer anyway. To simulate leaving the remaining volume in the offer, the maker can program their `makerPosthook` to `updateOffer` and put the remaining volume back in. */
      dirtyDeleteOffer(
        offerList.offerData[sor.offerId], sor.offer, sor.offerDetail, internalMgvData != "mgv/tradeSuccess"
      );
    }
  }

  /* ## Flashloan */
  /* Externally called by `execute`, flashloan lends money to the maker then calls `makerExecute` to run the maker liquidity fetching code. If `makerExecute` is unsuccessful, `flashloan` reverts (but the larger orderbook traversal will continue). 

  In detail:
  1. Flashloans `takerGives` units of `sor.olKey.inbound_tkn` from the taker to the maker and returns false if the loan fails.
  2. Runs `offerDetail.maker`'s `execute` function.
  3. Returns the result of the operations, with optional `makerData` to help the maker debug.

  Made virtual so tests can instrument the function.
  */
  function flashloan(MgvLib.SingleOrder calldata sor, address taker)
    external
    virtual
    returns (uint gasused, bytes32 makerData)
  {
    unchecked {
      /* `flashloan` must be used with a call (hence the `external` modifier) so its effect can be reverted. But a call from the outside would be fatal. */
      require(msg.sender == address(this), "mgv/flashloan/protected");
      /* The transfer taker -> maker is in 2 steps. First, taker->mgv. Then
       mgv->maker. With a direct taker->maker transfer, if one of taker/maker
       is blacklisted, we can't tell which one. We need to know which one:
       if we incorrectly blame the taker, a blacklisted maker can block an offer list forever; if we incorrectly blame the maker, a blacklisted taker can unfairly make makers fail all the time. Of course we assume that Mangrove is not blacklisted. This 2-step transfer is incompatible with tokens that have transfer fees (more accurately, it uselessly incurs fees twice). */
      if (transferTokenFrom(sor.olKey.inbound_tkn, taker, address(this), sor.takerGives)) {
        if (transferToken(sor.olKey.inbound_tkn, sor.offerDetail.maker(), sor.takerGives)) {
          (gasused, makerData) = makerExecute(sor);
        } else {
          innerRevert([bytes32("mgv/makerReceiveFail"), bytes32(0), ""]);
        }
      } else {
        innerRevert([bytes32("mgv/takerTransferFail"), "", ""]);
      }
    }
  }

  /* ## Maker Execute */
  /* Called by `flashloan`, `makerExecute` runs the maker code and checks that it can safely send the desired assets to the taker. */

  function makerExecute(MgvLib.SingleOrder calldata sor) internal returns (uint gasused, bytes32 makerData) {
    unchecked {
      bytes memory cd = abi.encodeCall(IMaker.makerExecute, (sor));

      uint gasreq = sor.offerDetail.gasreq();
      address maker = sor.offerDetail.maker();
      uint oldGas = gasleft();
      /* We let the maker pay for the overhead of checking remaining gas and making the call, as well as handling the return data (constant gas since only the first 32 bytes of return data are read). So the `require` below is just an approximation: if the overhead of (`require` + cost of `CALL`) is $h$, the maker will receive at worst $\textrm{gasreq} - \frac{63h}{64}$ gas. */
      if (!(oldGas - oldGas / 64 >= gasreq)) {
        innerRevert([bytes32("mgv/notEnoughGasForMakerTrade"), "", ""]);
      }

      bool callSuccess;
      (callSuccess, makerData) = controlledCall(maker, gasreq, cd);

      gasused = oldGas - gasleft();

      if (!callSuccess) {
        innerRevert([bytes32("mgv/makerRevert"), bytes32(gasused), makerData]);
      }

      bool transferSuccess = transferTokenFrom(sor.olKey.outbound_tkn, maker, address(this), sor.takerWants);

      if (!transferSuccess) {
        innerRevert([bytes32("mgv/makerTransferFail"), bytes32(gasused), makerData]);
      }
    }
  }

  /* ## Post execute */
  /* At this point, we know an offer execution was attempted. After executing an offer (whether in a market order or in cleans), we
     1. Call the maker's posthook and sum the total gas used.
     2. If offer failed: sum total penalty due to msg.sender and give remainder to maker.

     Made virtual so tests can instrument it.
   */
  function postExecute(
    MultiOrder memory mor,
    MgvLib.SingleOrder memory sor,
    uint gasused,
    bytes32 makerData,
    bytes32 mgvData
  ) internal virtual {
    unchecked {
      uint gasreq = sor.offerDetail.gasreq();

      /* We are about to call back the maker, giving it its unused gas (`gasreq - gasused`). Since the gas used so far may exceed `gasreq`, we prevent underflow in the subtraction below by bounding `gasused` above with `gasreq`. We could have decided not to call back the maker at all when there is no gas left, but we do it for uniformity. */
      if (gasused > gasreq) {
        gasused = gasreq;
      }
      (uint posthookGas, bool callSuccess, bytes32 posthookData) =
        makerPosthook(sor, gasreq - gasused, makerData, mgvData);
      gasused = gasused + posthookGas;

      if (mgvData != "mgv/tradeSuccess") {
        uint penalty = applyPenalty(sor, gasused);
        mor.totalPenalty += penalty;
        if (!callSuccess) {
          emit OfferFailWithPosthookData(
            sor.olKey.hash(), mor.taker, sor.offerId, sor.takerWants, sor.takerGives, penalty, mgvData, posthookData
          );
        } else {
          emit OfferFail(sor.olKey.hash(), mor.taker, sor.offerId, sor.takerWants, sor.takerGives, penalty, mgvData);
        }
      } else {
        if (!callSuccess) {
          emit OfferSuccessWithPosthookData(
            sor.olKey.hash(), mor.taker, sor.offerId, sor.takerWants, sor.takerGives, posthookData
          );
        } else {
          emit OfferSuccess(sor.olKey.hash(), mor.taker, sor.offerId, sor.takerWants, sor.takerGives);
        }
      }
    }
  }

  /* ## Maker Posthook */
  function makerPosthook(MgvLib.SingleOrder memory sor, uint gasLeft, bytes32 makerData, bytes32 mgvData)
    internal
    virtual
    returns (uint gasused, bool callSuccess, bytes32 posthookData)
  {
    unchecked {
      bytes memory cd =
        abi.encodeCall(IMaker.makerPosthook, (sor, MgvLib.OrderResult({makerData: makerData, mgvData: mgvData})));

      address maker = sor.offerDetail.maker();

      uint oldGas = gasleft();
      /* We let the maker pay for the overhead of checking remaining gas and making the call. So the `require` below is just an approximation: if the overhead of (`require` + cost of `CALL`) is $h$, the maker will receive at worst $\textrm{gasreq} - \frac{63h}{64}$ gas. */
      if (!(oldGas - oldGas / 64 >= gasLeft)) {
        revert("mgv/notEnoughGasForMakerPosthook");
      }

      (callSuccess, posthookData) = controlledCall(maker, gasLeft, cd);

      gasused = oldGas - gasleft();
    }
  }

  /* ## `controlledCall` */
  /* Calls an external function with controlled gas expense. A direct call of the form `(,bytes memory retdata) = maker.call{gas}(selector,...args)` enables a griefing attack: the maker uses half its gas to write in its memory, then reverts with that memory segment as argument. After a low-level call, solidity automatically copies `returndatasize` bytes of `returndata` into memory. So the total gas consumed to execute a failing offer could exceed `gasreq + offer_gasbase` where `n` is the number of failing offers. In case of success, we read the first 32 bytes of returndata (the signature of `makerExecute` is `bytes32`). Otherwise, for compatibility with most errors that bubble up from contract calls and Solidity's `require`, we read 32 bytes of returndata starting from the 69th (4 bytes of method sig + 32 bytes of offset + 32 bytes of string length). */
  function controlledCall(address callee, uint gasreq, bytes memory cd) internal returns (bool success, bytes32 data) {
    unchecked {
      bytes32[4] memory retdata;

      /* if success, read returned bytes 1..32, otherwise read returned bytes 69..100. */
      assembly ("memory-safe") {
        success := call(gasreq, callee, 0, add(cd, 32), mload(cd), retdata, 100)
        data := mload(add(mul(iszero(success), 68), retdata))
      }
    }
  }

  /* # Penalties */
  /* Offers are just promises. They can fail. Penalty provisioning discourages from failing too much: we ask makers to provision more ETH than the expected gas cost of executing their offer and penalize them according to wasted gas.

     Under normal circumstances, we should expect to see bots with a profit expectation dry-running offers locally and executing `cleans` on failing offers, collecting the penalty. The result should be a mostly clean book for actual takers (i.e. a book with only successful offers).

     **Incentive issue**: if the gas price increases enough after an offer has been created, there may not be an immediately profitable way to remove the fake offers. In that case, we count on 3 factors to keep the book clean:
     1. Gas price eventually comes down.
     2. Other market makers want to keep Mangrove attractive and maintain their offer flow.
     3. Mangrove governance (who may collect a fee) wants to keep Mangrove attractive and maximize exchange volume. */

  //+clear+
  /* After an offer failed, part of its provision is given back to the maker and the rest is stored to be sent to the taker after the entire order completes. In `applyPenalty`, we _only_ credit the maker with its excess provision. So it looks like the maker is gaining something. In fact they're just getting back a fraction of what they provisioned earlier. */
  /*
     Penalty application summary:

   * `applyPenalty` is not called if the offer executes successfully, so the offer remains provisioned. The maker can move the provision from the offer to its internal balance by calling `retractOffer(olKey,offerId,true)`.
   * Otherwise, the maker loses the cost of `gasused + offer_gasbase` gas. The gas price is estimated by `gasprice`.
   * To create the offer, the maker had to provision for `gasreq + offer_gasbase` gas at a price of `offerDetail.gasprice`.
   * We do not consider the tx.gasprice.
   * `offerDetail.gasbase` and `offerDetail.gasprice` are the values of Mangrove parameters `config.offer_gasbase` and `config.gasprice` when the offer was created. Without caching those values, the provision set aside could end up insufficient to reimburse the maker (or to retribute the taker).
   */
  function applyPenalty(MgvLib.SingleOrder memory sor, uint gasused) internal returns (uint) {
    unchecked {
      uint gasreq = sor.offerDetail.gasreq();

      uint provision = 1e6 * sor.offerDetail.gasprice() * (gasreq + sor.offerDetail.offer_gasbase());

      /* We set `gasused = min(gasused,gasreq)` since `gasreq < gasused` is possible e.g. with `gasreq = 0` (all calls consume nonzero gas). */
      if (gasused > gasreq) {
        gasused = gasreq;
      }

      /* As an invariant, `applyPenalty` is only called when `mgvData` is in `["mgv/makerRevert","mgv/makerTransferFail","mgv/makerReceiveFail"]`. */
      uint penalty = 1e6 * sor.global.gasprice() * (gasused + sor.local.offer_gasbase());

      if (penalty > provision) {
        penalty = provision;
      }

      /* Here we write to storage the new maker balance. This occurs _after_ possible reentrant calls. How do we know we're not crediting twice the same amounts? Because the `offer`'s provision was set to 0 in storage (through `dirtyDeleteOffer`) before the reentrant calls. In this function, we are working with cached copies of the offer as it was before it was consumed. */
      creditWei(sor.offerDetail.maker(), provision - penalty);

      return penalty;
    }
  }

  function sendPenalty(uint amount) internal {
    unchecked {
      if (amount > 0) {
        (bool noRevert,) = msg.sender.call{value: amount}("");
        require(noRevert, "mgv/sendPenaltyReverted");
      }
    }
  }

  /* Post-trade, `payTakerMinusFees` sends what's due to the taker and keeps the rest (the fees). Routing through the Mangrove like that also deals with blacklisting issues (separates the maker-blacklisted and the taker-blacklisted cases). */
  function payTakerMinusFees(MultiOrder memory mor, MgvLib.SingleOrder memory sor) internal {
    unchecked {
      uint concreteFee = (mor.totalGot * sor.local.fee()) / 10_000;
      if (concreteFee > 0) {
        mor.totalGot -= concreteFee;
        mor.feePaid = concreteFee;
      }
      if (mor.totalGot > 0) {
        /* It should be statically provable that this transfer cannot return false under well-behaved ERC20s and a non-blacklisted, non-0 target, if governance does not call withdrawERC20 during order execution, unless the caller set a gas limit which precisely makes `transferToken` go OOG but retains enough gas to revert here. */
        require(transferToken(sor.olKey.outbound_tkn, mor.taker, mor.totalGot), "mgv/MgvFailToPayTaker");
      }
    }
  }

  /* # Misc. functions */

  /* Regular solidity reverts prepend the string argument with a [function signature](https://docs.soliditylang.org/en/v0.7.6/control-structures.html#revert). Since we wish to transfer data through a revert, the `innerRevert` function does a low-level revert with only the required data. `innerCode` decodes this data. */
  function innerDecode(bytes memory data) internal pure returns (bytes32 mgvData, uint gasused, bytes32 makerData) {
    unchecked {
      /* The `data` pointer is of the form `[mgvData,gasused,makerData]` where each array element is contiguous and has size 256 bits. */
      assembly ("memory-safe") {
        mgvData := mload(add(data, 32))
        gasused := mload(add(data, 64))
        makerData := mload(add(data, 96))
      }
    }
  }

  /* <a id="MgvOfferTaking/innerRevert"></a>`innerRevert` reverts a raw triple of values to be interpreted by `innerDecode`.    */
  function innerRevert(bytes32[3] memory data) internal pure {
    unchecked {
      assembly ("memory-safe") {
        revert(data, 96)
      }
    }
  }
}

File 17 of 28 : MgvView.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.10;

import "@mgv/src/core/MgvLib.sol";
import "@mgv/src/core/MgvCommon.sol";

/* Contains view functions, to reduce Mangrove contract size */
contract MgvView is MgvCommon {
  /* # Configuration Reads */
  /* Get the address of Mangrove's governance. Only governance can successfully call functions of `MgvGovernable`. */
  function governance() external view returns (address) {
    return _governance;
  }

  /* Reading the configuration for an offer list involves reading the config global to all offer lists and the local one. In addition, a global parameter (`gasprice`) and a local one (`density`) may be read from the oracle. */
  function config(OLKey memory olKey) external view returns (Global _global, Local _local) {
    unchecked {
      (_global, _local,) = _config(olKey);
      unlockedOfferListOnly(_local);
    }
  }

  /* Sugar for getting only local config */
  function local(OLKey memory olKey) external view returns (Local _local) {
    unchecked {
      (, _local,) = _config(olKey);
      unlockedOfferListOnly(_local);
    }
  }

  /* Reading the global configuration. In addition, a parameter (`gasprice`) may be read from the oracle. */
  function global() external view returns (Global _global) {
    unchecked {
      (_global,,) = _config(OLKey(address(0), address(0), 0));
    }
  }

  function balanceOf(address maker) external view returns (uint balance) {
    unchecked {
      balance = _balanceOf[maker];
    }
  }

  /* # Tick tree view functions */

  function leafs(OLKey memory olKey, int index) external view returns (Leaf) {
    unchecked {
      OfferList storage offerList = offerLists[olKey.hash()];
      unlockedOfferListOnly(offerList.local);
      return offerList.leafs[index].clean();
    }
  }

  function level3s(OLKey memory olKey, int index) external view returns (Field) {
    unchecked {
      OfferList storage offerList = offerLists[olKey.hash()];
      Local _local = offerList.local;
      unlockedOfferListOnly(_local);

      if (_local.bestBin().level3Index() == index) {
        return _local.level3();
      } else {
        return offerList.level3s[index].clean();
      }
    }
  }

  function level2s(OLKey memory olKey, int index) external view returns (Field) {
    unchecked {
      OfferList storage offerList = offerLists[olKey.hash()];
      Local _local = offerList.local;
      unlockedOfferListOnly(_local);

      if (_local.bestBin().level2Index() == index) {
        return _local.level2();
      } else {
        return offerList.level2s[index].clean();
      }
    }
  }

  function level1s(OLKey memory olKey, int index) external view returns (Field) {
    unchecked {
      OfferList storage offerList = offerLists[olKey.hash()];
      Local _local = offerList.local;
      unlockedOfferListOnly(_local);

      if (_local.bestBin().level1Index() == index) {
        return _local.level1();
      } else {
        return offerList.level1s[index].clean();
      }
    }
  }

  function root(OLKey memory olKey) external view returns (Field) {
    unchecked {
      OfferList storage offerList = offerLists[olKey.hash()];
      Local _local = offerList.local;
      unlockedOfferListOnly(_local);
      return _local.root();
    }
  }

  /* # Offer list view functions */

  /* Function to check whether given an offer list is locked. Contrary to other offer list view functions, this does not revert if the offer list is locked. */
  function locked(OLKey memory olKey) external view returns (bool) {
    unchecked {
      return offerLists[olKey.hash()].local.lock();
    }
  }

  /* Convenience function to get best offer of the given offerList */
  function best(OLKey memory olKey) external view returns (uint offerId) {
    unchecked {
      OfferList storage offerList = offerLists[olKey.hash()];
      Local _local = offerList.local;
      unlockedOfferListOnly(_local);
      return offerList.leafs[_local.bestBin().leafIndex()].clean().bestOfferId();
    }
  }

  /* Get the olKey that corresponds to a hash, only works for offer lists that have been activated > 0 times */
  function olKeys(bytes32 olKeyHash) external view returns (OLKey memory olKey) {
    unchecked {
      olKey = _olKeys[olKeyHash];
    }
  }

  /* # Offer view functions */

  /* Get an offer in packed format */
  function offers(OLKey memory olKey, uint offerId) external view returns (Offer offer) {
    unchecked {
      OfferList storage offerList = offerLists[olKey.hash()];
      unlockedOfferListOnly(offerList.local);
      return offerList.offerData[offerId].offer;
    }
  }

  /* Get an offer detail in packed format */
  function offerDetails(OLKey memory olKey, uint offerId) external view returns (OfferDetail offerDetail) {
    unchecked {
      OfferList storage offerList = offerLists[olKey.hash()];
      unlockedOfferListOnly(offerList.local);
      return offerList.offerData[offerId].detail;
    }
  }

  /* Get both offer and offer detail in packed format */
  function offerData(OLKey memory olKey, uint offerId) external view returns (Offer offer, OfferDetail offerDetail) {
    unchecked {
      OfferList storage offerList = offerLists[olKey.hash()];
      unlockedOfferListOnly(offerList.local);
      OfferData storage _offerData = offerList.offerData[offerId];
      return (_offerData.offer, _offerData.detail);
    }
  }

  /* Permit-related view functions */

  function allowance(address outbound_tkn, address inbound_tkn, address owner, address spender)
    external
    view
    returns (uint amount)
  {
    unchecked {
      amount = _allowance[outbound_tkn][inbound_tkn][owner][spender];
    }
  }

  function nonces(address owner) external view returns (uint nonce) {
    unchecked {
      nonce = _nonces[owner];
    }
  }

  /* Note: the accessor for `DOMAIN_SEPARATOR` is defined in `MgvOfferTakingWithPermit` */
  function PERMIT_TYPEHASH() external pure returns (bytes32) {
    unchecked {
      return _PERMIT_TYPEHASH;
    }
  }
}

File 18 of 28 : MgvCommon.sol
// SPDX-License-Identifier: BUSL-1.1

/* `MgvCommon` and its descendants describe an orderbook-based exchange ("Mangrove") where market makers *do not have to provision their offer*. In a nutshell: each offer created by a maker specifies an address (`maker`) to call upon offer execution by a taker. When an offer is executed, Mangrove transfers the amount to be paid by the taker to the maker, calls the maker, attempts to transfer the amount promised by the maker to the taker, and reverts if it cannot.
 */

pragma solidity ^0.8.10;

import "@mgv/src/core/MgvLib.sol";

/* `MgvCommon` contains state variables used everywhere in the operation of Mangrove and related gatekeeping functions. The main `Mangrove` contract inherits from `MgvCommon`, and the auxiliary `MgvAppendix` contract inherits from `MgvCommon` as well. This way, when `Mangrove` delegatecalls to `MgvAppendix`, the storage slots match. */
contract MgvCommon is HasMgvEvents {
  /* # State variables */
  //+clear+

  /* The `governance` address. Governance is the only address that can configure parameters. */
  address internal _governance;

  /* Global mgv configuration, encoded in a 256 bits word. The information encoded is detailed in [`structs.js`](#structs.js). */
  Global internal internal_global;

  /* `OfferData` contains all the information related to an offer. Each field contains packed information such as the volumes and the gas required. See [`structs.js`](#structs.js) for more information. */
  struct OfferData {
    Offer offer;
    OfferDetail detail;
  }

  /* `OfferList` contains all data specific to an offer list. */
  struct OfferList {
    /* `local` is the Mangrove configuration specific to the `outbound,inbound,tickSpacing` offer list. It contains e.g. the minimum offer `density`. It contains packed information, see [`structs.js`](#structs.js) for more.*/
    Local local;
    /* `level1s` maps a level1 index to a (dirty) field. Each field holds 64 bits marking the (non)empty state of 64 level2 fields. */
    mapping(int => DirtyField) level1s;
    /* `level2s` maps a level2 index to a (dirty) field. Each field holds 64 bits marking the (non)empty state of 64 level3 fields. */
    mapping(int => DirtyField) level2s;
    /* `level3s` maps a level3 index to a (dirty) field. Each field holds 64 bits marking the (non)empty state of 64 leaves. */
    mapping(int => DirtyField) level3s;
    /* `leafs` (intentionally not `leaves` for clarity) maps a leaf index to a leaf. Each leaf holds the first&last offer id of 4 bins. */
    mapping(int => DirtyLeaf) leafs;
    /* OfferData maps an offer id to a struct that holds the two storage words where the packed offer information resides. For more information see `Offer` and `OfferDetail`. */
    mapping(uint => OfferData) offerData;
  }

  /* OLKeys (see `MgvLib.sol`) are hashed to a bytes32 OLKey identifier, which get mapped to an `OfferList` struct. Having a single mapping instead of one mapping per field in `OfferList` means we can pass around a storage reference to that struct. */
  mapping(bytes32 => OfferList) internal offerLists;
  /* For convenience, and to enable future functions that access offer lists by directly supplying an OLKey identifier, Mangrove maintains an inverse `id -> key` mapping. */
  mapping(bytes32 => OLKey) internal _olKeys;

  /* Makers provision their possible penalties in the `balanceOf` mapping.

       Offers specify the amount of gas they require for successful execution ([`gasreq`](#structs.js/gasreq)). To minimize book spamming, market makers must provision an amount of native tokens that depends on their `gasreq` and on the offer list's [`offer_gasbase`](#structs.js/gasbase). This provision is deducted from their `balanceOf`. If an offer fails, part of that provision is given to the taker as a `penalty`. The exact amount depends on the gas used by the offer before failing and during the execution of its posthook.

       Mangrove keeps track of available balances in the `balanceOf` map, which is decremented every time a maker creates a new offer, and may be modified on offer updates/cancellations/takings.
     */
  mapping(address maker => uint balance) internal _balanceOf;

  /*
  # Gatekeeping

  Gatekeeping functions are safety checks called in various places.
  */

  /* `unlockedOfferListOnly` protects modifying the offer list while an order is in progress. Since external contracts are called during orders, allowing reentrancy would, for instance, let a market maker replace offers currently on the book with worse ones. Note that the external contracts _will_ be called again after the order is complete, this time without any lock on the offer list.  */
  function unlockedOfferListOnly(Local local) internal pure {
    require(!local.lock(), "mgv/reentrancyLocked");
  }

  /* <a id="Mangrove/definition/liveMgvOnly"></a>
     In case of emergency, Mangrove can be `kill`ed. It cannot be resurrected. When a Mangrove is dead, the following operations are disabled :
       * Executing an offer
       * Sending ETH to Mangrove the normal way. Usual [shenanigans](https://medium.com/@alexsherbuck/two-ways-to-force-ether-into-a-contract-1543c1311c56) are possible.
       * Creating a new offer
   */
  function liveMgvOnly(Global _global) internal pure {
    require(!_global.dead(), "mgv/dead");
  }

  /* When Mangrove is deployed, all offer lists are inactive by default (since `locals[outbound_tkn][inbound_tkn]` is 0 by default). Offers on inactive offer lists cannot be taken or created. They can be updated and retracted. */
  function activeOfferListOnly(Global _global, Local _local) internal pure {
    liveMgvOnly(_global);
    require(_local.active(), "mgv/inactive");
  }

  /* _config is the lower-level variant which opportunistically returns a pointer to the storage offer list induced by (`outbound_tkn,inbound_tkn,tickSpacing`). */
  function _config(OLKey memory olKey)
    internal
    view
    returns (Global _global, Local _local, OfferList storage offerList)
  {
    unchecked {
      offerList = offerLists[olKey.hash()];
      _global = internal_global;
      _local = offerList.local;
      if (_global.useOracle()) {
        (uint gasprice, Density density) = IMgvMonitor(_global.monitor()).read(olKey);
        /* Gas gasprice can be ignored by making sure the oracle's set gasprice does not pass the check below. */
        if (GlobalLib.gasprice_check(gasprice)) {
          _global = _global.gasprice(gasprice);
        }
        /* Oracle density can be ignored by making sure the oracle's set density does not pass the checks below. */

        /* Checking the size of `density` is necessary to prevent overflow when `density` is used in calculations. */

        if (LocalLib.density_check(density)) {
          _local = _local.density(density);
        }
      }
    }
  }
  /* # Token transfer functions */
  /* `transferTokenFrom` is adapted from [existing code](https://soliditydeveloper.com/safe-erc20) and in particular avoids the
    "no return value" bug. It never throws and returns true iff the transfer was successful according to `tokenAddress`.

      Note that any spurious exception due to an error in Mangrove code will be falsely blamed on `from`.
    */

  function transferTokenFrom(address tokenAddress, address from, address to, uint value) internal returns (bool) {
    unchecked {
      bytes memory cd = abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value);
      (bool noRevert, bytes memory data) = tokenAddress.call(cd);
      return (noRevert && (data.length == 0 || abi.decode(data, (bool))));
    }
  }

  function transferToken(address tokenAddress, address to, uint value) internal returns (bool) {
    unchecked {
      bytes memory cd = abi.encodeWithSelector(IERC20.transfer.selector, to, value);
      (bool noRevert, bytes memory data) = tokenAddress.call(cd);
      return (noRevert && (data.length == 0 || abi.decode(data, (bool))));
    }
  }

  /* # Permit-related functionality */

  /* Takers may provide allowances on specific offer lists, so other addresses can execute orders in their name. Allowance may be set using the usual `approve` function, or through an [EIP712](https://eips.ethereum.org/EIPS/eip-712) `permit`.

  The mapping is `outbound_tkn => inbound_tkn => owner => spender => allowance`. There is no `tickSpacing` specified since we assume the natural semantics of a permit are "`spender` has the right to trade token A against token B at any tickSpacing". */
  mapping(
    address outbound_tkn
      => mapping(address inbound_tkn => mapping(address owner => mapping(address spender => uint allowance)))
  ) internal _allowance;
  /* Storing nonces avoids replay attacks. */
  mapping(address owner => uint nonce) internal _nonces;

  /* Following [EIP712](https://eips.ethereum.org/EIPS/eip-712), structured data signing has `keccak256("Permit(address outbound_tkn,address inbound_tkn,address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")` in its prefix. */
  bytes32 internal constant _PERMIT_TYPEHASH = 0xf0ea0a7146fb6eedb561d97b593d57d9b7df3c94d689372dc01302e5780248f4;
  /* If you are looking for `DOMAIN_SEPARATOR`, it is defined in `MgvOfferTakingWithPermit`.

  If you define an immutable C, you must initialize it in the constructor of C, unless you use solidity >= 0.8.21. Then you can initialize it in the constructor of a contract that inherits from C. At the time of the writing 0.8.21 is too recent so we move `DOMAIN_SEPARATOR` to `MgvOfferTakingWithPermit`, which has a constructor and also initializes `DOMAIN_SEPARATOR`. */
}

File 19 of 28 : Offer.post.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

/* ************************************************** *
            GENERATED FILE. DO NOT EDIT.
 * ************************************************** */

/* since you can't convert bool to uint in an expression without conditionals,
 * we add a file-level function and rely on compiler optimization
 */
function uint_of_bool(bool b) pure returns (uint u) {
  assembly ("memory-safe") { u := b }
}
import "@mgv/lib/core/Constants.sol";

struct OfferUnpacked {
  uint prev;
  uint next;
  Tick tick;
  uint gives;
}

//some type safety for each struct
type Offer is uint;
using OfferLib for Offer global;

////////////// ADDITIONAL DEFINITIONS, IF ANY ////////////////
import {Bin} from "@mgv/lib/core/TickTreeLib.sol";
import {Tick} from "@mgv/lib/core/TickLib.sol";
import {OfferExtra,OfferUnpackedExtra} from "@mgv/lib/core/OfferExtra.sol";

using OfferExtra for Offer global;
using OfferUnpackedExtra for OfferUnpacked global;

////////////// END OF ADDITIONAL DEFINITIONS /////////////////

library OfferLib {

  // number of bits in each field
  uint constant prev_bits  = 32;
  uint constant next_bits  = 32;
  uint constant tick_bits  = 21;
  uint constant gives_bits = 127;

  // number of bits before each field
  uint constant prev_before  = 0           + 0;
  uint constant next_before  = prev_before + prev_bits;
  uint constant tick_before  = next_before + next_bits;
  uint constant gives_before = tick_before + tick_bits;

  // focus-mask: 1s at field location, 0s elsewhere
  uint constant prev_mask_inv  = (ONES << 256 - prev_bits) >> prev_before;
  uint constant next_mask_inv  = (ONES << 256 - next_bits) >> next_before;
  uint constant tick_mask_inv  = (ONES << 256 - tick_bits) >> tick_before;
  uint constant gives_mask_inv = (ONES << 256 - gives_bits) >> gives_before;

  // cleanup-mask: 0s at field location, 1s elsewhere
  uint constant prev_mask  = ~prev_mask_inv;
  uint constant next_mask  = ~next_mask_inv;
  uint constant tick_mask  = ~tick_mask_inv;
  uint constant gives_mask = ~gives_mask_inv;

  // cast-mask: 0s followed by |field| trailing 1s
  uint constant prev_cast_mask  = ~(ONES << prev_bits);
  uint constant next_cast_mask  = ~(ONES << next_bits);
  uint constant tick_cast_mask  = ~(ONES << tick_bits);
  uint constant gives_cast_mask = ~(ONES << gives_bits);

  // size-related error message
  string constant prev_size_error  = "mgv/config/prev/32bits";
  string constant next_size_error  = "mgv/config/next/32bits";
  string constant tick_size_error  = "mgv/config/tick/21bits";
  string constant gives_size_error = "mgv/config/gives/127bits";

  // from packed to in-memory struct
  function to_struct(Offer __packed) internal pure returns (OfferUnpacked memory __s) { unchecked {
    __s.prev  = uint(Offer.unwrap(__packed) << prev_before) >> (256 - prev_bits);
    __s.next  = uint(Offer.unwrap(__packed) << next_before) >> (256 - next_bits);
    __s.tick  = Tick.wrap(int(int(Offer.unwrap(__packed) << tick_before) >> (256 - tick_bits)));
    __s.gives = uint(Offer.unwrap(__packed) << gives_before) >> (256 - gives_bits);
  }}

  // equality checking
  function eq(Offer __packed1, Offer __packed2) internal pure returns (bool) { unchecked {
    return Offer.unwrap(__packed1) == Offer.unwrap(__packed2);
  }}

  // from packed to a tuple
  function unpack(Offer __packed) internal pure returns (uint __prev, uint __next, Tick __tick, uint __gives) { unchecked {
    __prev  = uint(Offer.unwrap(__packed) << prev_before) >> (256 - prev_bits);
    __next  = uint(Offer.unwrap(__packed) << next_before) >> (256 - next_bits);
    __tick  = Tick.wrap(int(int(Offer.unwrap(__packed) << tick_before) >> (256 - tick_bits)));
    __gives = uint(Offer.unwrap(__packed) << gives_before) >> (256 - gives_bits);
  }}

  // getters
  function prev(Offer __packed) internal pure returns(uint) { unchecked {
    return uint(Offer.unwrap(__packed) << prev_before) >> (256 - prev_bits);
  }}

  // setters
  function prev(Offer __packed,uint val) internal pure returns(Offer) { unchecked {
    return Offer.wrap((Offer.unwrap(__packed) & prev_mask) | (val << (256 - prev_bits)) >> prev_before);
  }}
  
  function next(Offer __packed) internal pure returns(uint) { unchecked {
    return uint(Offer.unwrap(__packed) << next_before) >> (256 - next_bits);
  }}

  // setters
  function next(Offer __packed,uint val) internal pure returns(Offer) { unchecked {
    return Offer.wrap((Offer.unwrap(__packed) & next_mask) | (val << (256 - next_bits)) >> next_before);
  }}
  
  function tick(Offer __packed) internal pure returns(Tick) { unchecked {
    return Tick.wrap(int(int(Offer.unwrap(__packed) << tick_before) >> (256 - tick_bits)));
  }}

  // setters
  function tick(Offer __packed,Tick val) internal pure returns(Offer) { unchecked {
    return Offer.wrap((Offer.unwrap(__packed) & tick_mask) | (uint(Tick.unwrap(val)) << (256 - tick_bits)) >> tick_before);
  }}
  
  function gives(Offer __packed) internal pure returns(uint) { unchecked {
    return uint(Offer.unwrap(__packed) << gives_before) >> (256 - gives_bits);
  }}

  // setters
  function gives(Offer __packed,uint val) internal pure returns(Offer) { unchecked {
    return Offer.wrap((Offer.unwrap(__packed) & gives_mask) | (val << (256 - gives_bits)) >> gives_before);
  }}
  

  // from in-memory struct to packed
  function t_of_struct(OfferUnpacked memory __s) internal pure returns (Offer) { unchecked {
    return pack(__s.prev, __s.next, __s.tick, __s.gives);
  }}

  // from arguments to packed
  function pack(uint __prev, uint __next, Tick __tick, uint __gives) internal pure returns (Offer) { unchecked {
    uint __packed;
    __packed |= (__prev << (256 - prev_bits)) >> prev_before;
    __packed |= (__next << (256 - next_bits)) >> next_before;
    __packed |= (uint(Tick.unwrap(__tick)) << (256 - tick_bits)) >> tick_before;
    __packed |= (__gives << (256 - gives_bits)) >> gives_before;
    return Offer.wrap(__packed);
  }}

  // input checking
  function prev_check(uint __prev) internal pure returns (bool) { unchecked {
    return (__prev & prev_cast_mask) == __prev;
  }}
  function next_check(uint __next) internal pure returns (bool) { unchecked {
    return (__next & next_cast_mask) == __next;
  }}
  function tick_check(Tick __tick) internal pure returns (bool) { unchecked {
    return (uint(Tick.unwrap(__tick)) & tick_cast_mask) == uint(Tick.unwrap(__tick));
  }}
  function gives_check(uint __gives) internal pure returns (bool) { unchecked {
    return (__gives & gives_cast_mask) == __gives;
  }}
}

File 20 of 28 : OfferDetail.post.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

/* ************************************************** *
            GENERATED FILE. DO NOT EDIT.
 * ************************************************** */

/* since you can't convert bool to uint in an expression without conditionals,
 * we add a file-level function and rely on compiler optimization
 */
function uint_of_bool(bool b) pure returns (uint u) {
  assembly ("memory-safe") { u := b }
}
import "@mgv/lib/core/Constants.sol";

struct OfferDetailUnpacked {
  address maker;
  uint gasreq;
  uint kilo_offer_gasbase;
  uint gasprice;
}

//some type safety for each struct
type OfferDetail is uint;
using OfferDetailLib for OfferDetail global;

////////////// ADDITIONAL DEFINITIONS, IF ANY ////////////////
import {OfferDetailExtra,OfferDetailUnpackedExtra} from "@mgv/lib/core/OfferDetailExtra.sol";
using OfferDetailExtra for OfferDetail global;
using OfferDetailUnpackedExtra for OfferDetailUnpacked global;

////////////// END OF ADDITIONAL DEFINITIONS /////////////////

library OfferDetailLib {

  // number of bits in each field
  uint constant maker_bits              = 160;
  uint constant gasreq_bits             = 24;
  uint constant kilo_offer_gasbase_bits = 9;
  uint constant gasprice_bits           = 26;

  // number of bits before each field
  uint constant maker_before              = 0                         + 0;
  uint constant gasreq_before             = maker_before              + maker_bits;
  uint constant kilo_offer_gasbase_before = gasreq_before             + gasreq_bits;
  uint constant gasprice_before           = kilo_offer_gasbase_before + kilo_offer_gasbase_bits;

  // focus-mask: 1s at field location, 0s elsewhere
  uint constant maker_mask_inv              = (ONES << 256 - maker_bits) >> maker_before;
  uint constant gasreq_mask_inv             = (ONES << 256 - gasreq_bits) >> gasreq_before;
  uint constant kilo_offer_gasbase_mask_inv = (ONES << 256 - kilo_offer_gasbase_bits) >> kilo_offer_gasbase_before;
  uint constant gasprice_mask_inv           = (ONES << 256 - gasprice_bits) >> gasprice_before;

  // cleanup-mask: 0s at field location, 1s elsewhere
  uint constant maker_mask              = ~maker_mask_inv;
  uint constant gasreq_mask             = ~gasreq_mask_inv;
  uint constant kilo_offer_gasbase_mask = ~kilo_offer_gasbase_mask_inv;
  uint constant gasprice_mask           = ~gasprice_mask_inv;

  // cast-mask: 0s followed by |field| trailing 1s
  uint constant maker_cast_mask              = ~(ONES << maker_bits);
  uint constant gasreq_cast_mask             = ~(ONES << gasreq_bits);
  uint constant kilo_offer_gasbase_cast_mask = ~(ONES << kilo_offer_gasbase_bits);
  uint constant gasprice_cast_mask           = ~(ONES << gasprice_bits);

  // size-related error message
  string constant maker_size_error              = "mgv/config/maker/160bits";
  string constant gasreq_size_error             = "mgv/config/gasreq/24bits";
  string constant kilo_offer_gasbase_size_error = "mgv/config/kilo_offer_gasbase/9bits";
  string constant gasprice_size_error           = "mgv/config/gasprice/26bits";

  // from packed to in-memory struct
  function to_struct(OfferDetail __packed) internal pure returns (OfferDetailUnpacked memory __s) { unchecked {
    __s.maker              = address(uint160(uint(OfferDetail.unwrap(__packed) << maker_before) >> (256 - maker_bits)));
    __s.gasreq             = uint(OfferDetail.unwrap(__packed) << gasreq_before) >> (256 - gasreq_bits);
    __s.kilo_offer_gasbase = uint(OfferDetail.unwrap(__packed) << kilo_offer_gasbase_before) >> (256 - kilo_offer_gasbase_bits);
    __s.gasprice           = uint(OfferDetail.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits);
  }}

  // equality checking
  function eq(OfferDetail __packed1, OfferDetail __packed2) internal pure returns (bool) { unchecked {
    return OfferDetail.unwrap(__packed1) == OfferDetail.unwrap(__packed2);
  }}

  // from packed to a tuple
  function unpack(OfferDetail __packed) internal pure returns (address __maker, uint __gasreq, uint __kilo_offer_gasbase, uint __gasprice) { unchecked {
    __maker              = address(uint160(uint(OfferDetail.unwrap(__packed) << maker_before) >> (256 - maker_bits)));
    __gasreq             = uint(OfferDetail.unwrap(__packed) << gasreq_before) >> (256 - gasreq_bits);
    __kilo_offer_gasbase = uint(OfferDetail.unwrap(__packed) << kilo_offer_gasbase_before) >> (256 - kilo_offer_gasbase_bits);
    __gasprice           = uint(OfferDetail.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits);
  }}

  // getters
  function maker(OfferDetail __packed) internal pure returns(address) { unchecked {
    return address(uint160(uint(OfferDetail.unwrap(__packed) << maker_before) >> (256 - maker_bits)));
  }}

  // setters
  function maker(OfferDetail __packed,address val) internal pure returns(OfferDetail) { unchecked {
    return OfferDetail.wrap((OfferDetail.unwrap(__packed) & maker_mask) | (uint(uint160(val)) << (256 - maker_bits)) >> maker_before);
  }}
  
  function gasreq(OfferDetail __packed) internal pure returns(uint) { unchecked {
    return uint(OfferDetail.unwrap(__packed) << gasreq_before) >> (256 - gasreq_bits);
  }}

  // setters
  function gasreq(OfferDetail __packed,uint val) internal pure returns(OfferDetail) { unchecked {
    return OfferDetail.wrap((OfferDetail.unwrap(__packed) & gasreq_mask) | (val << (256 - gasreq_bits)) >> gasreq_before);
  }}
  
  function kilo_offer_gasbase(OfferDetail __packed) internal pure returns(uint) { unchecked {
    return uint(OfferDetail.unwrap(__packed) << kilo_offer_gasbase_before) >> (256 - kilo_offer_gasbase_bits);
  }}

  // setters
  function kilo_offer_gasbase(OfferDetail __packed,uint val) internal pure returns(OfferDetail) { unchecked {
    return OfferDetail.wrap((OfferDetail.unwrap(__packed) & kilo_offer_gasbase_mask) | (val << (256 - kilo_offer_gasbase_bits)) >> kilo_offer_gasbase_before);
  }}
  
  function gasprice(OfferDetail __packed) internal pure returns(uint) { unchecked {
    return uint(OfferDetail.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits);
  }}

  // setters
  function gasprice(OfferDetail __packed,uint val) internal pure returns(OfferDetail) { unchecked {
    return OfferDetail.wrap((OfferDetail.unwrap(__packed) & gasprice_mask) | (val << (256 - gasprice_bits)) >> gasprice_before);
  }}
  

  // from in-memory struct to packed
  function t_of_struct(OfferDetailUnpacked memory __s) internal pure returns (OfferDetail) { unchecked {
    return pack(__s.maker, __s.gasreq, __s.kilo_offer_gasbase, __s.gasprice);
  }}

  // from arguments to packed
  function pack(address __maker, uint __gasreq, uint __kilo_offer_gasbase, uint __gasprice) internal pure returns (OfferDetail) { unchecked {
    uint __packed;
    __packed |= (uint(uint160(__maker)) << (256 - maker_bits)) >> maker_before;
    __packed |= (__gasreq << (256 - gasreq_bits)) >> gasreq_before;
    __packed |= (__kilo_offer_gasbase << (256 - kilo_offer_gasbase_bits)) >> kilo_offer_gasbase_before;
    __packed |= (__gasprice << (256 - gasprice_bits)) >> gasprice_before;
    return OfferDetail.wrap(__packed);
  }}

  // input checking
  function maker_check(address __maker) internal pure returns (bool) { unchecked {
    return (uint(uint160(__maker)) & maker_cast_mask) == uint(uint160(__maker));
  }}
  function gasreq_check(uint __gasreq) internal pure returns (bool) { unchecked {
    return (__gasreq & gasreq_cast_mask) == __gasreq;
  }}
  function kilo_offer_gasbase_check(uint __kilo_offer_gasbase) internal pure returns (bool) { unchecked {
    return (__kilo_offer_gasbase & kilo_offer_gasbase_cast_mask) == __kilo_offer_gasbase;
  }}
  function gasprice_check(uint __gasprice) internal pure returns (bool) { unchecked {
    return (__gasprice & gasprice_cast_mask) == __gasprice;
  }}
}

File 21 of 28 : Global.post.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

/* ************************************************** *
            GENERATED FILE. DO NOT EDIT.
 * ************************************************** */

/* since you can't convert bool to uint in an expression without conditionals,
 * we add a file-level function and rely on compiler optimization
 */
function uint_of_bool(bool b) pure returns (uint u) {
  assembly ("memory-safe") { u := b }
}
import "@mgv/lib/core/Constants.sol";

struct GlobalUnpacked {
  address monitor;
  bool useOracle;
  bool notify;
  uint gasprice;
  uint gasmax;
  bool dead;
  uint maxRecursionDepth;
  uint maxGasreqForFailingOffers;
}

//some type safety for each struct
type Global is uint;
using GlobalLib for Global global;

////////////// ADDITIONAL DEFINITIONS, IF ANY ////////////////

////////////// END OF ADDITIONAL DEFINITIONS /////////////////

library GlobalLib {

  // number of bits in each field
  uint constant monitor_bits                   = 160;
  uint constant useOracle_bits                 = 1;
  uint constant notify_bits                    = 1;
  uint constant gasprice_bits                  = 26;
  uint constant gasmax_bits                    = 24;
  uint constant dead_bits                      = 1;
  uint constant maxRecursionDepth_bits         = 8;
  uint constant maxGasreqForFailingOffers_bits = 32;

  // number of bits before each field
  uint constant monitor_before                   = 0                        + 0;
  uint constant useOracle_before                 = monitor_before           + monitor_bits;
  uint constant notify_before                    = useOracle_before         + useOracle_bits;
  uint constant gasprice_before                  = notify_before            + notify_bits;
  uint constant gasmax_before                    = gasprice_before          + gasprice_bits;
  uint constant dead_before                      = gasmax_before            + gasmax_bits;
  uint constant maxRecursionDepth_before         = dead_before              + dead_bits;
  uint constant maxGasreqForFailingOffers_before = maxRecursionDepth_before + maxRecursionDepth_bits;

  // focus-mask: 1s at field location, 0s elsewhere
  uint constant monitor_mask_inv                   = (ONES << 256 - monitor_bits) >> monitor_before;
  uint constant useOracle_mask_inv                 = (ONES << 256 - useOracle_bits) >> useOracle_before;
  uint constant notify_mask_inv                    = (ONES << 256 - notify_bits) >> notify_before;
  uint constant gasprice_mask_inv                  = (ONES << 256 - gasprice_bits) >> gasprice_before;
  uint constant gasmax_mask_inv                    = (ONES << 256 - gasmax_bits) >> gasmax_before;
  uint constant dead_mask_inv                      = (ONES << 256 - dead_bits) >> dead_before;
  uint constant maxRecursionDepth_mask_inv         = (ONES << 256 - maxRecursionDepth_bits) >> maxRecursionDepth_before;
  uint constant maxGasreqForFailingOffers_mask_inv = (ONES << 256 - maxGasreqForFailingOffers_bits) >> maxGasreqForFailingOffers_before;

  // cleanup-mask: 0s at field location, 1s elsewhere
  uint constant monitor_mask                   = ~monitor_mask_inv;
  uint constant useOracle_mask                 = ~useOracle_mask_inv;
  uint constant notify_mask                    = ~notify_mask_inv;
  uint constant gasprice_mask                  = ~gasprice_mask_inv;
  uint constant gasmax_mask                    = ~gasmax_mask_inv;
  uint constant dead_mask                      = ~dead_mask_inv;
  uint constant maxRecursionDepth_mask         = ~maxRecursionDepth_mask_inv;
  uint constant maxGasreqForFailingOffers_mask = ~maxGasreqForFailingOffers_mask_inv;

  // cast-mask: 0s followed by |field| trailing 1s
  uint constant monitor_cast_mask                   = ~(ONES << monitor_bits);
  uint constant useOracle_cast_mask                 = ~(ONES << useOracle_bits);
  uint constant notify_cast_mask                    = ~(ONES << notify_bits);
  uint constant gasprice_cast_mask                  = ~(ONES << gasprice_bits);
  uint constant gasmax_cast_mask                    = ~(ONES << gasmax_bits);
  uint constant dead_cast_mask                      = ~(ONES << dead_bits);
  uint constant maxRecursionDepth_cast_mask         = ~(ONES << maxRecursionDepth_bits);
  uint constant maxGasreqForFailingOffers_cast_mask = ~(ONES << maxGasreqForFailingOffers_bits);

  // size-related error message
  string constant monitor_size_error                   = "mgv/config/monitor/160bits";
  string constant useOracle_size_error                 = "mgv/config/useOracle/1bits";
  string constant notify_size_error                    = "mgv/config/notify/1bits";
  string constant gasprice_size_error                  = "mgv/config/gasprice/26bits";
  string constant gasmax_size_error                    = "mgv/config/gasmax/24bits";
  string constant dead_size_error                      = "mgv/config/dead/1bits";
  string constant maxRecursionDepth_size_error         = "mgv/config/maxRecursionDepth/8bits";
  string constant maxGasreqForFailingOffers_size_error = "mgv/config/maxGasreqForFailingOffers/32bits";

  // from packed to in-memory struct
  function to_struct(Global __packed) internal pure returns (GlobalUnpacked memory __s) { unchecked {
    __s.monitor                   = address(uint160(uint(Global.unwrap(__packed) << monitor_before) >> (256 - monitor_bits)));
    __s.useOracle                 = ((Global.unwrap(__packed) & useOracle_mask_inv) > 0);
    __s.notify                    = ((Global.unwrap(__packed) & notify_mask_inv) > 0);
    __s.gasprice                  = uint(Global.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits);
    __s.gasmax                    = uint(Global.unwrap(__packed) << gasmax_before) >> (256 - gasmax_bits);
    __s.dead                      = ((Global.unwrap(__packed) & dead_mask_inv) > 0);
    __s.maxRecursionDepth         = uint(Global.unwrap(__packed) << maxRecursionDepth_before) >> (256 - maxRecursionDepth_bits);
    __s.maxGasreqForFailingOffers = uint(Global.unwrap(__packed) << maxGasreqForFailingOffers_before) >> (256 - maxGasreqForFailingOffers_bits);
  }}

  // equality checking
  function eq(Global __packed1, Global __packed2) internal pure returns (bool) { unchecked {
    return Global.unwrap(__packed1) == Global.unwrap(__packed2);
  }}

  // from packed to a tuple
  function unpack(Global __packed) internal pure returns (address __monitor, bool __useOracle, bool __notify, uint __gasprice, uint __gasmax, bool __dead, uint __maxRecursionDepth, uint __maxGasreqForFailingOffers) { unchecked {
    __monitor                   = address(uint160(uint(Global.unwrap(__packed) << monitor_before) >> (256 - monitor_bits)));
    __useOracle                 = ((Global.unwrap(__packed) & useOracle_mask_inv) > 0);
    __notify                    = ((Global.unwrap(__packed) & notify_mask_inv) > 0);
    __gasprice                  = uint(Global.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits);
    __gasmax                    = uint(Global.unwrap(__packed) << gasmax_before) >> (256 - gasmax_bits);
    __dead                      = ((Global.unwrap(__packed) & dead_mask_inv) > 0);
    __maxRecursionDepth         = uint(Global.unwrap(__packed) << maxRecursionDepth_before) >> (256 - maxRecursionDepth_bits);
    __maxGasreqForFailingOffers = uint(Global.unwrap(__packed) << maxGasreqForFailingOffers_before) >> (256 - maxGasreqForFailingOffers_bits);
  }}

  // getters
  function monitor(Global __packed) internal pure returns(address) { unchecked {
    return address(uint160(uint(Global.unwrap(__packed) << monitor_before) >> (256 - monitor_bits)));
  }}

  // setters
  function monitor(Global __packed,address val) internal pure returns(Global) { unchecked {
    return Global.wrap((Global.unwrap(__packed) & monitor_mask) | (uint(uint160(val)) << (256 - monitor_bits)) >> monitor_before);
  }}
  
  function useOracle(Global __packed) internal pure returns(bool) { unchecked {
    return ((Global.unwrap(__packed) & useOracle_mask_inv) > 0);
  }}

  // setters
  function useOracle(Global __packed,bool val) internal pure returns(Global) { unchecked {
    return Global.wrap((Global.unwrap(__packed) & useOracle_mask) | (uint_of_bool(val) << (256 - useOracle_bits)) >> useOracle_before);
  }}
  
  function notify(Global __packed) internal pure returns(bool) { unchecked {
    return ((Global.unwrap(__packed) & notify_mask_inv) > 0);
  }}

  // setters
  function notify(Global __packed,bool val) internal pure returns(Global) { unchecked {
    return Global.wrap((Global.unwrap(__packed) & notify_mask) | (uint_of_bool(val) << (256 - notify_bits)) >> notify_before);
  }}
  
  function gasprice(Global __packed) internal pure returns(uint) { unchecked {
    return uint(Global.unwrap(__packed) << gasprice_before) >> (256 - gasprice_bits);
  }}

  // setters
  function gasprice(Global __packed,uint val) internal pure returns(Global) { unchecked {
    return Global.wrap((Global.unwrap(__packed) & gasprice_mask) | (val << (256 - gasprice_bits)) >> gasprice_before);
  }}
  
  function gasmax(Global __packed) internal pure returns(uint) { unchecked {
    return uint(Global.unwrap(__packed) << gasmax_before) >> (256 - gasmax_bits);
  }}

  // setters
  function gasmax(Global __packed,uint val) internal pure returns(Global) { unchecked {
    return Global.wrap((Global.unwrap(__packed) & gasmax_mask) | (val << (256 - gasmax_bits)) >> gasmax_before);
  }}
  
  function dead(Global __packed) internal pure returns(bool) { unchecked {
    return ((Global.unwrap(__packed) & dead_mask_inv) > 0);
  }}

  // setters
  function dead(Global __packed,bool val) internal pure returns(Global) { unchecked {
    return Global.wrap((Global.unwrap(__packed) & dead_mask) | (uint_of_bool(val) << (256 - dead_bits)) >> dead_before);
  }}
  
  function maxRecursionDepth(Global __packed) internal pure returns(uint) { unchecked {
    return uint(Global.unwrap(__packed) << maxRecursionDepth_before) >> (256 - maxRecursionDepth_bits);
  }}

  // setters
  function maxRecursionDepth(Global __packed,uint val) internal pure returns(Global) { unchecked {
    return Global.wrap((Global.unwrap(__packed) & maxRecursionDepth_mask) | (val << (256 - maxRecursionDepth_bits)) >> maxRecursionDepth_before);
  }}
  
  function maxGasreqForFailingOffers(Global __packed) internal pure returns(uint) { unchecked {
    return uint(Global.unwrap(__packed) << maxGasreqForFailingOffers_before) >> (256 - maxGasreqForFailingOffers_bits);
  }}

  // setters
  function maxGasreqForFailingOffers(Global __packed,uint val) internal pure returns(Global) { unchecked {
    return Global.wrap((Global.unwrap(__packed) & maxGasreqForFailingOffers_mask) | (val << (256 - maxGasreqForFailingOffers_bits)) >> maxGasreqForFailingOffers_before);
  }}
  

  // from in-memory struct to packed
  function t_of_struct(GlobalUnpacked memory __s) internal pure returns (Global) { unchecked {
    return pack(__s.monitor, __s.useOracle, __s.notify, __s.gasprice, __s.gasmax, __s.dead, __s.maxRecursionDepth, __s.maxGasreqForFailingOffers);
  }}

  // from arguments to packed
  function pack(address __monitor, bool __useOracle, bool __notify, uint __gasprice, uint __gasmax, bool __dead, uint __maxRecursionDepth, uint __maxGasreqForFailingOffers) internal pure returns (Global) { unchecked {
    uint __packed;
    __packed |= (uint(uint160(__monitor)) << (256 - monitor_bits)) >> monitor_before;
    __packed |= (uint_of_bool(__useOracle) << (256 - useOracle_bits)) >> useOracle_before;
    __packed |= (uint_of_bool(__notify) << (256 - notify_bits)) >> notify_before;
    __packed |= (__gasprice << (256 - gasprice_bits)) >> gasprice_before;
    __packed |= (__gasmax << (256 - gasmax_bits)) >> gasmax_before;
    __packed |= (uint_of_bool(__dead) << (256 - dead_bits)) >> dead_before;
    __packed |= (__maxRecursionDepth << (256 - maxRecursionDepth_bits)) >> maxRecursionDepth_before;
    __packed |= (__maxGasreqForFailingOffers << (256 - maxGasreqForFailingOffers_bits)) >> maxGasreqForFailingOffers_before;
    return Global.wrap(__packed);
  }}

  // input checking
  function monitor_check(address __monitor) internal pure returns (bool) { unchecked {
    return (uint(uint160(__monitor)) & monitor_cast_mask) == uint(uint160(__monitor));
  }}
  function useOracle_check(bool __useOracle) internal pure returns (bool) { unchecked {
    return (uint_of_bool(__useOracle) & useOracle_cast_mask) == uint_of_bool(__useOracle);
  }}
  function notify_check(bool __notify) internal pure returns (bool) { unchecked {
    return (uint_of_bool(__notify) & notify_cast_mask) == uint_of_bool(__notify);
  }}
  function gasprice_check(uint __gasprice) internal pure returns (bool) { unchecked {
    return (__gasprice & gasprice_cast_mask) == __gasprice;
  }}
  function gasmax_check(uint __gasmax) internal pure returns (bool) { unchecked {
    return (__gasmax & gasmax_cast_mask) == __gasmax;
  }}
  function dead_check(bool __dead) internal pure returns (bool) { unchecked {
    return (uint_of_bool(__dead) & dead_cast_mask) == uint_of_bool(__dead);
  }}
  function maxRecursionDepth_check(uint __maxRecursionDepth) internal pure returns (bool) { unchecked {
    return (__maxRecursionDepth & maxRecursionDepth_cast_mask) == __maxRecursionDepth;
  }}
  function maxGasreqForFailingOffers_check(uint __maxGasreqForFailingOffers) internal pure returns (bool) { unchecked {
    return (__maxGasreqForFailingOffers & maxGasreqForFailingOffers_cast_mask) == __maxGasreqForFailingOffers;
  }}
}

File 22 of 28 : Local.post.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

/* ************************************************** *
            GENERATED FILE. DO NOT EDIT.
 * ************************************************** */

/* since you can't convert bool to uint in an expression without conditionals,
 * we add a file-level function and rely on compiler optimization
 */
function uint_of_bool(bool b) pure returns (uint u) {
  assembly ("memory-safe") { u := b }
}
import "@mgv/lib/core/Constants.sol";

struct LocalUnpacked {
  bool active;
  uint fee;
  Density density;
  uint binPosInLeaf;
  Field level3;
  Field level2;
  Field level1;
  Field root;
  uint kilo_offer_gasbase;
  bool lock;
  uint last;
}

//some type safety for each struct
type Local is uint;
using LocalLib for Local global;

////////////// ADDITIONAL DEFINITIONS, IF ANY ////////////////
import {Density, DensityLib} from "@mgv/lib/core/DensityLib.sol";
import {Bin,TickTreeLib,Field} from "@mgv/lib/core/TickTreeLib.sol";
/* Globally enable global.method(...) */
import {LocalExtra,LocalUnpackedExtra} from "@mgv/lib/core/LocalExtra.sol";
using LocalExtra for Local global;
using LocalUnpackedExtra for LocalUnpacked global;

////////////// END OF ADDITIONAL DEFINITIONS /////////////////

library LocalLib {

  // number of bits in each field
  uint constant active_bits             = 1;
  uint constant fee_bits                = 8;
  uint constant density_bits            = 9;
  uint constant binPosInLeaf_bits       = 2;
  uint constant level3_bits             = 64;
  uint constant level2_bits             = 64;
  uint constant level1_bits             = 64;
  uint constant root_bits               = 2;
  uint constant kilo_offer_gasbase_bits = 9;
  uint constant lock_bits               = 1;
  uint constant last_bits               = 32;

  // number of bits before each field
  uint constant active_before             = 0                         + 0;
  uint constant fee_before                = active_before             + active_bits;
  uint constant density_before            = fee_before                + fee_bits;
  uint constant binPosInLeaf_before       = density_before            + density_bits;
  uint constant level3_before             = binPosInLeaf_before       + binPosInLeaf_bits;
  uint constant level2_before             = level3_before             + level3_bits;
  uint constant level1_before             = level2_before             + level2_bits;
  uint constant root_before               = level1_before             + level1_bits;
  uint constant kilo_offer_gasbase_before = root_before               + root_bits;
  uint constant lock_before               = kilo_offer_gasbase_before + kilo_offer_gasbase_bits;
  uint constant last_before               = lock_before               + lock_bits;

  // focus-mask: 1s at field location, 0s elsewhere
  uint constant active_mask_inv             = (ONES << 256 - active_bits) >> active_before;
  uint constant fee_mask_inv                = (ONES << 256 - fee_bits) >> fee_before;
  uint constant density_mask_inv            = (ONES << 256 - density_bits) >> density_before;
  uint constant binPosInLeaf_mask_inv       = (ONES << 256 - binPosInLeaf_bits) >> binPosInLeaf_before;
  uint constant level3_mask_inv             = (ONES << 256 - level3_bits) >> level3_before;
  uint constant level2_mask_inv             = (ONES << 256 - level2_bits) >> level2_before;
  uint constant level1_mask_inv             = (ONES << 256 - level1_bits) >> level1_before;
  uint constant root_mask_inv               = (ONES << 256 - root_bits) >> root_before;
  uint constant kilo_offer_gasbase_mask_inv = (ONES << 256 - kilo_offer_gasbase_bits) >> kilo_offer_gasbase_before;
  uint constant lock_mask_inv               = (ONES << 256 - lock_bits) >> lock_before;
  uint constant last_mask_inv               = (ONES << 256 - last_bits) >> last_before;

  // cleanup-mask: 0s at field location, 1s elsewhere
  uint constant active_mask             = ~active_mask_inv;
  uint constant fee_mask                = ~fee_mask_inv;
  uint constant density_mask            = ~density_mask_inv;
  uint constant binPosInLeaf_mask       = ~binPosInLeaf_mask_inv;
  uint constant level3_mask             = ~level3_mask_inv;
  uint constant level2_mask             = ~level2_mask_inv;
  uint constant level1_mask             = ~level1_mask_inv;
  uint constant root_mask               = ~root_mask_inv;
  uint constant kilo_offer_gasbase_mask = ~kilo_offer_gasbase_mask_inv;
  uint constant lock_mask               = ~lock_mask_inv;
  uint constant last_mask               = ~last_mask_inv;

  // cast-mask: 0s followed by |field| trailing 1s
  uint constant active_cast_mask             = ~(ONES << active_bits);
  uint constant fee_cast_mask                = ~(ONES << fee_bits);
  uint constant density_cast_mask            = ~(ONES << density_bits);
  uint constant binPosInLeaf_cast_mask       = ~(ONES << binPosInLeaf_bits);
  uint constant level3_cast_mask             = ~(ONES << level3_bits);
  uint constant level2_cast_mask             = ~(ONES << level2_bits);
  uint constant level1_cast_mask             = ~(ONES << level1_bits);
  uint constant root_cast_mask               = ~(ONES << root_bits);
  uint constant kilo_offer_gasbase_cast_mask = ~(ONES << kilo_offer_gasbase_bits);
  uint constant lock_cast_mask               = ~(ONES << lock_bits);
  uint constant last_cast_mask               = ~(ONES << last_bits);

  // size-related error message
  string constant active_size_error             = "mgv/config/active/1bits";
  string constant fee_size_error                = "mgv/config/fee/8bits";
  string constant density_size_error            = "mgv/config/density/9bits";
  string constant binPosInLeaf_size_error       = "mgv/config/binPosInLeaf/2bits";
  string constant level3_size_error             = "mgv/config/level3/64bits";
  string constant level2_size_error             = "mgv/config/level2/64bits";
  string constant level1_size_error             = "mgv/config/level1/64bits";
  string constant root_size_error               = "mgv/config/root/2bits";
  string constant kilo_offer_gasbase_size_error = "mgv/config/kilo_offer_gasbase/9bits";
  string constant lock_size_error               = "mgv/config/lock/1bits";
  string constant last_size_error               = "mgv/config/last/32bits";

  // from packed to in-memory struct
  function to_struct(Local __packed) internal pure returns (LocalUnpacked memory __s) { unchecked {
    __s.active             = ((Local.unwrap(__packed) & active_mask_inv) > 0);
    __s.fee                = uint(Local.unwrap(__packed) << fee_before) >> (256 - fee_bits);
    __s.density            = Density.wrap(uint(Local.unwrap(__packed) << density_before) >> (256 - density_bits));
    __s.binPosInLeaf       = uint(Local.unwrap(__packed) << binPosInLeaf_before) >> (256 - binPosInLeaf_bits);
    __s.level3             = Field.wrap(uint(Local.unwrap(__packed) << level3_before) >> (256 - level3_bits));
    __s.level2             = Field.wrap(uint(Local.unwrap(__packed) << level2_before) >> (256 - level2_bits));
    __s.level1             = Field.wrap(uint(Local.unwrap(__packed) << level1_before) >> (256 - level1_bits));
    __s.root               = Field.wrap(uint(Local.unwrap(__packed) << root_before) >> (256 - root_bits));
    __s.kilo_offer_gasbase = uint(Local.unwrap(__packed) << kilo_offer_gasbase_before) >> (256 - kilo_offer_gasbase_bits);
    __s.lock               = ((Local.unwrap(__packed) & lock_mask_inv) > 0);
    __s.last               = uint(Local.unwrap(__packed) << last_before) >> (256 - last_bits);
  }}

  // equality checking
  function eq(Local __packed1, Local __packed2) internal pure returns (bool) { unchecked {
    return Local.unwrap(__packed1) == Local.unwrap(__packed2);
  }}

  // from packed to a tuple
  function unpack(Local __packed) internal pure returns (bool __active, uint __fee, Density __density, uint __binPosInLeaf, Field __level3, Field __level2, Field __level1, Field __root, uint __kilo_offer_gasbase, bool __lock, uint __last) { unchecked {
    __active             = ((Local.unwrap(__packed) & active_mask_inv) > 0);
    __fee                = uint(Local.unwrap(__packed) << fee_before) >> (256 - fee_bits);
    __density            = Density.wrap(uint(Local.unwrap(__packed) << density_before) >> (256 - density_bits));
    __binPosInLeaf       = uint(Local.unwrap(__packed) << binPosInLeaf_before) >> (256 - binPosInLeaf_bits);
    __level3             = Field.wrap(uint(Local.unwrap(__packed) << level3_before) >> (256 - level3_bits));
    __level2             = Field.wrap(uint(Local.unwrap(__packed) << level2_before) >> (256 - level2_bits));
    __level1             = Field.wrap(uint(Local.unwrap(__packed) << level1_before) >> (256 - level1_bits));
    __root               = Field.wrap(uint(Local.unwrap(__packed) << root_before) >> (256 - root_bits));
    __kilo_offer_gasbase = uint(Local.unwrap(__packed) << kilo_offer_gasbase_before) >> (256 - kilo_offer_gasbase_bits);
    __lock               = ((Local.unwrap(__packed) & lock_mask_inv) > 0);
    __last               = uint(Local.unwrap(__packed) << last_before) >> (256 - last_bits);
  }}

  // getters
  function active(Local __packed) internal pure returns(bool) { unchecked {
    return ((Local.unwrap(__packed) & active_mask_inv) > 0);
  }}

  // setters
  function active(Local __packed,bool val) internal pure returns(Local) { unchecked {
    return Local.wrap((Local.unwrap(__packed) & active_mask) | (uint_of_bool(val) << (256 - active_bits)) >> active_before);
  }}
  
  function fee(Local __packed) internal pure returns(uint) { unchecked {
    return uint(Local.unwrap(__packed) << fee_before) >> (256 - fee_bits);
  }}

  // setters
  function fee(Local __packed,uint val) internal pure returns(Local) { unchecked {
    return Local.wrap((Local.unwrap(__packed) & fee_mask) | (val << (256 - fee_bits)) >> fee_before);
  }}
  
  function density(Local __packed) internal pure returns(Density) { unchecked {
    return Density.wrap(uint(Local.unwrap(__packed) << density_before) >> (256 - density_bits));
  }}

  // setters
  function density(Local __packed,Density val) internal pure returns(Local) { unchecked {
    return Local.wrap((Local.unwrap(__packed) & density_mask) | (Density.unwrap(val) << (256 - density_bits)) >> density_before);
  }}
  
  function binPosInLeaf(Local __packed) internal pure returns(uint) { unchecked {
    return uint(Local.unwrap(__packed) << binPosInLeaf_before) >> (256 - binPosInLeaf_bits);
  }}

  // setters
  function binPosInLeaf(Local __packed,uint val) internal pure returns(Local) { unchecked {
    return Local.wrap((Local.unwrap(__packed) & binPosInLeaf_mask) | (val << (256 - binPosInLeaf_bits)) >> binPosInLeaf_before);
  }}
  
  function level3(Local __packed) internal pure returns(Field) { unchecked {
    return Field.wrap(uint(Local.unwrap(__packed) << level3_before) >> (256 - level3_bits));
  }}

  // setters
  function level3(Local __packed,Field val) internal pure returns(Local) { unchecked {
    return Local.wrap((Local.unwrap(__packed) & level3_mask) | (Field.unwrap(val) << (256 - level3_bits)) >> level3_before);
  }}
  
  function level2(Local __packed) internal pure returns(Field) { unchecked {
    return Field.wrap(uint(Local.unwrap(__packed) << level2_before) >> (256 - level2_bits));
  }}

  // setters
  function level2(Local __packed,Field val) internal pure returns(Local) { unchecked {
    return Local.wrap((Local.unwrap(__packed) & level2_mask) | (Field.unwrap(val) << (256 - level2_bits)) >> level2_before);
  }}
  
  function level1(Local __packed) internal pure returns(Field) { unchecked {
    return Field.wrap(uint(Local.unwrap(__packed) << level1_before) >> (256 - level1_bits));
  }}

  // setters
  function level1(Local __packed,Field val) internal pure returns(Local) { unchecked {
    return Local.wrap((Local.unwrap(__packed) & level1_mask) | (Field.unwrap(val) << (256 - level1_bits)) >> level1_before);
  }}
  
  function root(Local __packed) internal pure returns(Field) { unchecked {
    return Field.wrap(uint(Local.unwrap(__packed) << root_before) >> (256 - root_bits));
  }}

  // setters
  function root(Local __packed,Field val) internal pure returns(Local) { unchecked {
    return Local.wrap((Local.unwrap(__packed) & root_mask) | (Field.unwrap(val) << (256 - root_bits)) >> root_before);
  }}
  
  function kilo_offer_gasbase(Local __packed) internal pure returns(uint) { unchecked {
    return uint(Local.unwrap(__packed) << kilo_offer_gasbase_before) >> (256 - kilo_offer_gasbase_bits);
  }}

  // setters
  function kilo_offer_gasbase(Local __packed,uint val) internal pure returns(Local) { unchecked {
    return Local.wrap((Local.unwrap(__packed) & kilo_offer_gasbase_mask) | (val << (256 - kilo_offer_gasbase_bits)) >> kilo_offer_gasbase_before);
  }}
  
  function lock(Local __packed) internal pure returns(bool) { unchecked {
    return ((Local.unwrap(__packed) & lock_mask_inv) > 0);
  }}

  // setters
  function lock(Local __packed,bool val) internal pure returns(Local) { unchecked {
    return Local.wrap((Local.unwrap(__packed) & lock_mask) | (uint_of_bool(val) << (256 - lock_bits)) >> lock_before);
  }}
  
  function last(Local __packed) internal pure returns(uint) { unchecked {
    return uint(Local.unwrap(__packed) << last_before) >> (256 - last_bits);
  }}

  // setters
  function last(Local __packed,uint val) internal pure returns(Local) { unchecked {
    return Local.wrap((Local.unwrap(__packed) & last_mask) | (val << (256 - last_bits)) >> last_before);
  }}
  

  // from in-memory struct to packed
  function t_of_struct(LocalUnpacked memory __s) internal pure returns (Local) { unchecked {
    return pack(__s.active, __s.fee, __s.density, __s.binPosInLeaf, __s.level3, __s.level2, __s.level1, __s.root, __s.kilo_offer_gasbase, __s.lock, __s.last);
  }}

  // from arguments to packed
  function pack(bool __active, uint __fee, Density __density, uint __binPosInLeaf, Field __level3, Field __level2, Field __level1, Field __root, uint __kilo_offer_gasbase, bool __lock, uint __last) internal pure returns (Local) { unchecked {
    uint __packed;
    __packed |= (uint_of_bool(__active) << (256 - active_bits)) >> active_before;
    __packed |= (__fee << (256 - fee_bits)) >> fee_before;
    __packed |= (Density.unwrap(__density) << (256 - density_bits)) >> density_before;
    __packed |= (__binPosInLeaf << (256 - binPosInLeaf_bits)) >> binPosInLeaf_before;
    __packed |= (Field.unwrap(__level3) << (256 - level3_bits)) >> level3_before;
    __packed |= (Field.unwrap(__level2) << (256 - level2_bits)) >> level2_before;
    __packed |= (Field.unwrap(__level1) << (256 - level1_bits)) >> level1_before;
    __packed |= (Field.unwrap(__root) << (256 - root_bits)) >> root_before;
    __packed |= (__kilo_offer_gasbase << (256 - kilo_offer_gasbase_bits)) >> kilo_offer_gasbase_before;
    __packed |= (uint_of_bool(__lock) << (256 - lock_bits)) >> lock_before;
    __packed |= (__last << (256 - last_bits)) >> last_before;
    return Local.wrap(__packed);
  }}

  // input checking
  function active_check(bool __active) internal pure returns (bool) { unchecked {
    return (uint_of_bool(__active) & active_cast_mask) == uint_of_bool(__active);
  }}
  function fee_check(uint __fee) internal pure returns (bool) { unchecked {
    return (__fee & fee_cast_mask) == __fee;
  }}
  function density_check(Density __density) internal pure returns (bool) { unchecked {
    return (Density.unwrap(__density) & density_cast_mask) == Density.unwrap(__density);
  }}
  function binPosInLeaf_check(uint __binPosInLeaf) internal pure returns (bool) { unchecked {
    return (__binPosInLeaf & binPosInLeaf_cast_mask) == __binPosInLeaf;
  }}
  function level3_check(Field __level3) internal pure returns (bool) { unchecked {
    return (Field.unwrap(__level3) & level3_cast_mask) == Field.unwrap(__level3);
  }}
  function level2_check(Field __level2) internal pure returns (bool) { unchecked {
    return (Field.unwrap(__level2) & level2_cast_mask) == Field.unwrap(__level2);
  }}
  function level1_check(Field __level1) internal pure returns (bool) { unchecked {
    return (Field.unwrap(__level1) & level1_cast_mask) == Field.unwrap(__level1);
  }}
  function root_check(Field __root) internal pure returns (bool) { unchecked {
    return (Field.unwrap(__root) & root_cast_mask) == Field.unwrap(__root);
  }}
  function kilo_offer_gasbase_check(uint __kilo_offer_gasbase) internal pure returns (bool) { unchecked {
    return (__kilo_offer_gasbase & kilo_offer_gasbase_cast_mask) == __kilo_offer_gasbase;
  }}
  function lock_check(bool __lock) internal pure returns (bool) { unchecked {
    return (uint_of_bool(__lock) & lock_cast_mask) == uint_of_bool(__lock);
  }}
  function last_check(uint __last) internal pure returns (bool) { unchecked {
    return (__last & last_cast_mask) == __last;
  }}
}

File 23 of 28 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/* The constants below are written as literals to optimize gas. For all the relevant constants here, the non-literal expression that computes them is checked in `Constants.t.sol`. */

uint constant ONE = 1;
uint constant ONES = type(uint).max;
uint constant TOPBIT = 0x8000000000000000000000000000000000000000000000000000000000000000;
uint constant NOT_TOPBIT = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;

/* **sizes must match field sizes in `structs.ts` where relevant** */

/* Number of bits used to represent a tick */
uint constant TICK_BITS = 21;
/* Number of bits used to represent an offer id */
uint constant OFFER_BITS = 32;
/* Maximum possible size of a field -- protects against wrong code changes. Constraint given by `BitLib.ctz64`. */
uint constant MAX_FIELD_SIZE = 64;

/* `X_SIZE_BITS` is 1+log2 of the size of `X`, where size is the number of elements it holds.In a field, an element is a bit; in a leaf, an element is a pair of offer ids. For `LEVEL`s and `ROOT`, the value must be exact, so only power-of-2 sizes are allowed. */
uint constant LEAF_SIZE_BITS = 2; 
uint constant LEVEL_SIZE_BITS = 6;
uint constant ROOT_SIZE_BITS = 1;

/* `X_SIZE` is `2**X_SIZE_BITS` */
int constant LEAF_SIZE = 4;
int constant LEVEL_SIZE = 64;
int constant ROOT_SIZE = 2;

/* `X_SIZE_MASK` is `0...01...1` where the number of 1s is `X_SIZE_BITS` */
uint constant LEAF_SIZE_MASK = 0x3;
uint constant LEVEL_SIZE_MASK = 0x3f;
uint constant ROOT_SIZE_MASK = 0x1;
/* `0...01...1` with `OFFER_BITS` 1s at the end */
uint constant OFFER_MASK = 0xffffffff;

/* Same as `ROOT_SIZE` */
int constant NUM_LEVEL1 = 2;
/* Same as `NUM_LEVEL1 * LEVEL_SIZE` */
int constant NUM_LEVEL2 = 128;
/* Same as `NUM_LEVEL2 * LEVEL_SIZE` */
int constant NUM_LEVEL3 = 8192;
/* Same as `NUM_LEVEL3 * LEVEL_SIZE` */
int constant NUM_LEAFS = 524288;
/* Same as `NUM_LEAFS * LEAF` */
int constant NUM_BINS = 2097152;
/* min and max bins are defined like min and max int. */
int constant MIN_BIN = -1048576;
int constant MAX_BIN = 1048575;

/* The tick range is the largest such that the mantissa of `1.0001^MAX_TICK` fits on 128 bits (and thus can be multiplied by volumes). */
int constant MIN_TICK = -887272;
int constant MAX_TICK = 887272;
/* These are reference values for what the function `tickFromRatio` function will return, not the most possible accurate values for the min and max tick. */
uint constant MIN_RATIO_MANTISSA = 170153974464283981435225617938057077692;
int constant MIN_RATIO_EXP = 255;
uint constant MAX_RATIO_MANTISSA = 340256786836388094050805785052946541084;
int constant MAX_RATIO_EXP = 0;
/* `MANTISSA_BITS` is the number of bits used in the mantissa of normalized floats that represent ratios. 128 means we can multiply all allowed volumes by the mantissa and not overflow. */
uint constant MANTISSA_BITS = 128;
uint constant MANTISSA_BITS_MINUS_ONE = 127;
/* 
With `|tick|<=887272` and normalized mantissas on 128 bits, the maximum possible mantissa is `340282295208261841796968287475569060645`, so the maximum safe volume before overflow is `NO_OVERFLOW_AMOUNT = 340282438633630198193436196978374475856` (slightly above `2**128`).

For ease of use, we could pick a simpler, slightly smaller max safe volume: `(1<<128)-1`.

However the `*ByVolume` functions get a price by (abstractly) performing `outboundAmount/inboundAmount`. If we limit all volumes to `NO_OVERFLOW_AMOUNT` but aren't more restrictive than that, since `type(uint128).max > 1.0001**MAX_TICK`, we will get ratios that are outside the price boundaries.

We thus pick a uniform, easy to remember constraint on volumes that works everywhere: `(1<<127)-1`
*/
uint constant MAX_SAFE_VOLUME = 170141183460469231731687303715884105727;

/* When a market order consumes offers, the implementation uses recursion consumes additional EVM stack space at each new offer. To avoid reverting due to stack overflow, Mangrove keeps a counter and stops the market order when it reaches a maximum recursion depth. `INITIAL_MAX_RECURSION_DEPTH` is the maximum recursion depth given at deployment time.

See `maxRecursionDepth` in `structs.ts`

Without optimizer enabled it fails above 79. With optimizer and 200 runs it fails above 80. Set default a bit lower to be safe. */
uint constant INITIAL_MAX_RECURSION_DEPTH = 75;

/* When a market order consumes offers, it may use gas on offers that fail to deliver. To avoid reverts after a string of failing offers that consumes more gas than is available in a block, Mangrove stops a market order after it has gone through failing offers such that their cumulative `gasreq` is greater than the global `maxGasreqForFailingOffers` parameter. At deployment, `maxGasreqForFailingOffers` is set to:
```
INITIAL_MAX_GASREQ_FOR_FAILING_OFFERS_MULTIPLIER * gasmax
``` */
uint constant INITIAL_MAX_GASREQ_FOR_FAILING_OFFERS_MULTIPLIER = 3;

/* Those two constants are used in `TickLib.ratioFromTick` to convert a log base 1.0001 to a log base 2. */
uint constant LOG_BP_SHIFT = 235;
uint constant LOG_BP_2X235 = 382733217082594961806491056566382061424140926068392360945012727618364717537;

File 24 of 28 : BitLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

library BitLib {
  // - if x is a nonzero uint64: 
  //   return number of zeroes in x that do not have a 1 to their right
  // - otherwise:
  //    return 64
  function ctz64(uint x) internal pure returns (uint256 c) {
    unchecked {
      assembly ("memory-safe") {
        // clean
        x:= and(x,0xffffffffffffffff)

        // 7th bit
        c:= shl(6,iszero(x))

        // isolate lsb
        x := and(x, add(not(x), 1))

        // 6th bit
        c := or(c,shl(5, lt(0xffffffff, x)))

        // debruijn lookup
        c := or(c, byte(shr(251, mul(shr(c, x), shl(224, 0x077cb531))), 
            0x00011c021d0e18031e16140f191104081f1b0d17151310071a0c12060b050a09))
      }
    }
  }

  // Function fls is MIT License. Copyright (c) 2022 Solady.
/// @dev find last set.
    /// Returns the index of the most significant bit of `x`,
    /// counting from the least significant bit position.
    /// If `x` is zero, returns 256.
    /// Equivalent to `log2(x)`, but without reverting for the zero case.
    function fls(uint256 x) internal pure returns (uint256 r) {
        assembly ("memory-safe") {
            r := shl(8, iszero(x))

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

            // For the remaining 32 bits, use a De Bruijn lookup.
            x := shr(r, x)
            x := or(x, shr(1, x))
            x := or(x, shr(2, x))
            x := or(x, shr(4, x))
            x := or(x, shr(8, x))
            x := or(x, shr(16, x))

            // forgefmt: disable-next-item
            r := or(r, byte(shr(251, mul(x, shl(224, 0x07c4acdd))),
                0x0009010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f))
        }
    }
}

File 25 of 28 : console2.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

/// @dev The original console.sol uses `int` and `uint` for computing function selectors, but it should
/// use `int256` and `uint256`. This modified version fixes that. This version is recommended
/// over `console.sol` if you don't need compatibility with Hardhat as the logs will show up in
/// forge stack traces. If you do need compatibility with Hardhat, you must use `console.sol`.
/// Reference: https://github.com/NomicFoundation/hardhat/issues/2178
library console2 {
    address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);

    function _castLogPayloadViewToPure(
        function(bytes memory) internal view fnIn
    ) internal pure returns (function(bytes memory) internal pure fnOut) {
        assembly {
            fnOut := fnIn
        }
    }

    function _sendLogPayload(bytes memory payload) internal pure {
        _castLogPayloadViewToPure(_sendLogPayloadView)(payload);
    }

    function _sendLogPayloadView(bytes memory payload) private view {
        uint256 payloadLength = payload.length;
        address consoleAddress = CONSOLE_ADDRESS;
        /// @solidity memory-safe-assembly
        assembly {
            let payloadStart := add(payload, 32)
            let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
        }
    }

    function log() internal pure {
        _sendLogPayload(abi.encodeWithSignature("log()"));
    }

    function logInt(int256 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(int256)", p0));
    }

    function logUint(uint256 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0));
    }

    function logString(string memory p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string)", p0));
    }

    function logBool(bool p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
    }

    function logAddress(address p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address)", p0));
    }

    function logBytes(bytes memory p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0));
    }

    function logBytes1(bytes1 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0));
    }

    function logBytes2(bytes2 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0));
    }

    function logBytes3(bytes3 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0));
    }

    function logBytes4(bytes4 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0));
    }

    function logBytes5(bytes5 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0));
    }

    function logBytes6(bytes6 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0));
    }

    function logBytes7(bytes7 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0));
    }

    function logBytes8(bytes8 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0));
    }

    function logBytes9(bytes9 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0));
    }

    function logBytes10(bytes10 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0));
    }

    function logBytes11(bytes11 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0));
    }

    function logBytes12(bytes12 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0));
    }

    function logBytes13(bytes13 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0));
    }

    function logBytes14(bytes14 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0));
    }

    function logBytes15(bytes15 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0));
    }

    function logBytes16(bytes16 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0));
    }

    function logBytes17(bytes17 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0));
    }

    function logBytes18(bytes18 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0));
    }

    function logBytes19(bytes19 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0));
    }

    function logBytes20(bytes20 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0));
    }

    function logBytes21(bytes21 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0));
    }

    function logBytes22(bytes22 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0));
    }

    function logBytes23(bytes23 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0));
    }

    function logBytes24(bytes24 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0));
    }

    function logBytes25(bytes25 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0));
    }

    function logBytes26(bytes26 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0));
    }

    function logBytes27(bytes27 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0));
    }

    function logBytes28(bytes28 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0));
    }

    function logBytes29(bytes29 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0));
    }

    function logBytes30(bytes30 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0));
    }

    function logBytes31(bytes31 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0));
    }

    function logBytes32(bytes32 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0));
    }

    function log(uint256 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0));
    }

    function log(int256 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(int256)", p0));
    }

    function log(string memory p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string)", p0));
    }

    function log(bool p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
    }

    function log(address p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address)", p0));
    }

    function log(uint256 p0, uint256 p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256)", p0, p1));
    }

    function log(uint256 p0, string memory p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string)", p0, p1));
    }

    function log(uint256 p0, bool p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool)", p0, p1));
    }

    function log(uint256 p0, address p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address)", p0, p1));
    }

    function log(string memory p0, uint256 p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1));
    }

    function log(string memory p0, int256 p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,int256)", p0, p1));
    }

    function log(string memory p0, string memory p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
    }

    function log(string memory p0, bool p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1));
    }

    function log(string memory p0, address p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1));
    }

    function log(bool p0, uint256 p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256)", p0, p1));
    }

    function log(bool p0, string memory p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1));
    }

    function log(bool p0, bool p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1));
    }

    function log(bool p0, address p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1));
    }

    function log(address p0, uint256 p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256)", p0, p1));
    }

    function log(address p0, string memory p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1));
    }

    function log(address p0, bool p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1));
    }

    function log(address p0, address p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1));
    }

    function log(uint256 p0, uint256 p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256)", p0, p1, p2));
    }

    function log(uint256 p0, uint256 p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string)", p0, p1, p2));
    }

    function log(uint256 p0, uint256 p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool)", p0, p1, p2));
    }

    function log(uint256 p0, uint256 p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address)", p0, p1, p2));
    }

    function log(uint256 p0, string memory p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256)", p0, p1, p2));
    }

    function log(uint256 p0, string memory p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string)", p0, p1, p2));
    }

    function log(uint256 p0, string memory p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool)", p0, p1, p2));
    }

    function log(uint256 p0, string memory p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address)", p0, p1, p2));
    }

    function log(uint256 p0, bool p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256)", p0, p1, p2));
    }

    function log(uint256 p0, bool p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string)", p0, p1, p2));
    }

    function log(uint256 p0, bool p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool)", p0, p1, p2));
    }

    function log(uint256 p0, bool p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address)", p0, p1, p2));
    }

    function log(uint256 p0, address p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256)", p0, p1, p2));
    }

    function log(uint256 p0, address p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string)", p0, p1, p2));
    }

    function log(uint256 p0, address p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool)", p0, p1, p2));
    }

    function log(uint256 p0, address p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address)", p0, p1, p2));
    }

    function log(string memory p0, uint256 p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256)", p0, p1, p2));
    }

    function log(string memory p0, uint256 p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string)", p0, p1, p2));
    }

    function log(string memory p0, uint256 p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool)", p0, p1, p2));
    }

    function log(string memory p0, uint256 p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2));
    }

    function log(string memory p0, address p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256)", p0, p1, p2));
    }

    function log(string memory p0, address p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2));
    }

    function log(string memory p0, address p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2));
    }

    function log(string memory p0, address p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2));
    }

    function log(bool p0, uint256 p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256)", p0, p1, p2));
    }

    function log(bool p0, uint256 p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string)", p0, p1, p2));
    }

    function log(bool p0, uint256 p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool)", p0, p1, p2));
    }

    function log(bool p0, uint256 p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2));
    }

    function log(bool p0, bool p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256)", p0, p1, p2));
    }

    function log(bool p0, bool p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2));
    }

    function log(bool p0, bool p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2));
    }

    function log(bool p0, bool p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2));
    }

    function log(bool p0, address p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256)", p0, p1, p2));
    }

    function log(bool p0, address p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2));
    }

    function log(bool p0, address p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2));
    }

    function log(bool p0, address p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2));
    }

    function log(address p0, uint256 p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256)", p0, p1, p2));
    }

    function log(address p0, uint256 p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string)", p0, p1, p2));
    }

    function log(address p0, uint256 p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool)", p0, p1, p2));
    }

    function log(address p0, uint256 p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address)", p0, p1, p2));
    }

    function log(address p0, string memory p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256)", p0, p1, p2));
    }

    function log(address p0, string memory p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2));
    }

    function log(address p0, string memory p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2));
    }

    function log(address p0, string memory p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2));
    }

    function log(address p0, bool p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256)", p0, p1, p2));
    }

    function log(address p0, bool p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2));
    }

    function log(address p0, bool p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2));
    }

    function log(address p0, bool p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2));
    }

    function log(address p0, address p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256)", p0, p1, p2));
    }

    function log(address p0, address p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2));
    }

    function log(address p0, address p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2));
    }

    function log(address p0, address p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2));
    }

    function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3));
    }

}

File 26 of 28 : OfferExtra.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "@mgv/lib/core/TickTreeLib.sol";
import "@mgv/lib/core/TickLib.sol";
import {Offer,OfferUnpacked,OfferLib} from "@mgv/src/preprocessed/Offer.post.sol";


/* cleanup-mask: 0s at location of fields to hide from maker, 1s elsewhere */
uint constant HIDE_FIELDS_FROM_MAKER_MASK = ~(OfferLib.prev_mask_inv | OfferLib.next_mask_inv);

/* Extra functions for the offer */
library OfferExtra {
  /* Compute wants from tick and gives */
  function wants(Offer offer) internal pure returns (uint) {
    return offer.tick().inboundFromOutboundUp(offer.gives());
  }

  /* Sugar to test offer liveness */
  function isLive(Offer offer) internal pure returns (bool resp) {
    uint gives = offer.gives();
    assembly ("memory-safe") {
      resp := iszero(iszero(gives))
    }
  }

  /* Get the bin where the offer is stored given an offer list that has `tickSpacing` */
  function bin(Offer offer, uint tickSpacing) internal pure returns (Bin) {
    return offer.tick().nearestBin(tickSpacing);
  }

  /* Removes prev/next pointers from an offer before sending it to the maker.  Ensures that the maker has no information about the state of the book when it gets called. */
  function clearFieldsForMaker(Offer offer) internal pure returns (Offer) {
    unchecked {
      return Offer.wrap(
        Offer.unwrap(offer)
        & HIDE_FIELDS_FROM_MAKER_MASK);
    }
  }
}

/* Extra functions for the struct version of the offer */
library OfferUnpackedExtra {
  /* Compute wants from tick and gives */
  function wants(OfferUnpacked memory offer) internal pure returns (uint) {
    return offer.tick.inboundFromOutboundUp(offer.gives);
  }

  /* Sugar to test offer liveness */
  function isLive(OfferUnpacked memory offer) internal pure returns (bool resp) {
    uint gives = offer.gives;
    assembly ("memory-safe") {
      resp := iszero(iszero(gives))
    }
  }

  /* Removes prev/next pointers from an offer before sending it to the maker; Ensures that the maker has no information about the state of the book when it gets called. */
  function bin(OfferUnpacked memory offer, uint tickSpacing) internal pure returns (Bin) {
    return offer.tick.nearestBin(tickSpacing);
  }

}

File 27 of 28 : OfferDetailExtra.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import {OfferDetail,OfferDetailUnpacked} from "@mgv/src/preprocessed/OfferDetail.post.sol";


/* Extra functions for the offer details */
library OfferDetailExtra {
  function offer_gasbase(OfferDetail offerDetail) internal pure returns (uint) { unchecked {
    return offerDetail.kilo_offer_gasbase() * 1e3;
  }}
  function offer_gasbase(OfferDetail offerDetail,uint val) internal pure returns (OfferDetail) { unchecked {
    return offerDetail.kilo_offer_gasbase(val/1e3);
  }}
}

/* Extra functions for the struct version of the offer details */
library OfferDetailUnpackedExtra {
  function offer_gasbase(OfferDetailUnpacked memory offerDetail) internal pure returns (uint) { unchecked {
    return offerDetail.kilo_offer_gasbase * 1e3;
  }}
  function offer_gasbase(OfferDetailUnpacked memory offerDetail,uint val) internal pure { unchecked {
    offerDetail.kilo_offer_gasbase = val/1e3;
  }}
}

File 28 of 28 : LocalExtra.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import {Bin,TickTreeLib,Field} from "@mgv/lib/core/TickTreeLib.sol";
import {Density, DensityLib} from "@mgv/lib/core/DensityLib.sol";
import {Local,LocalUnpacked,LocalLib} from "@mgv/src/preprocessed/Local.post.sol";



// cleanup-mask: 0s at location of fields to hide from maker, 1s elsewhere
uint constant HIDE_FIELDS_FROM_MAKER_MASK = ~(LocalLib.binPosInLeaf_mask_inv | LocalLib.level3_mask_inv | LocalLib.level2_mask_inv | LocalLib.level1_mask_inv | LocalLib.root_mask_inv | LocalLib.last_mask_inv);

/* Extra functions for local */
library LocalExtra {

  /* Sets the density in fixed-point 96X32 format (it is stored in floating-point, see `DensityLib` for more information). */
  function densityFrom96X32(Local local, uint density96X32) internal pure returns (Local) { unchecked {
    return local.density(DensityLib.from96X32(density96X32));
  }}

  /* Returns the gasbase in gas (it is stored in kilogas) */
  function offer_gasbase(Local local) internal pure returns (uint) { unchecked {
    return local.kilo_offer_gasbase() * 1e3;
  }}

  /* Sets the gasbase in gas (it is stored in kilogas) */
  function offer_gasbase(Local local,uint val) internal pure returns (Local) { unchecked {
    return local.kilo_offer_gasbase(val/1e3);
  }}

  /* Returns the bin that contains the best offer in \`local\`'s offer list */
  function bestBin(Local local) internal pure returns (Bin) { unchecked {
    return TickTreeLib.bestBinFromLocal(local);
  }}

  /* Erases field that give information about the current structure of the offer list. */
  function clearFieldsForMaker(Local local) internal pure returns (Local) { unchecked {
    return Local.wrap(
      Local.unwrap(local)
      & HIDE_FIELDS_FROM_MAKER_MASK);
  }}
}

/* Extra functions for the struct version of local */
library LocalUnpackedExtra {
  /* Sets the density in fixed-point 96X32 format (it is stored in floating-point, see `DensityLib` for more information). */
  function densityFrom96X32(LocalUnpacked memory local, uint density96X32) internal pure { unchecked {
    local.density = DensityLib.from96X32(density96X32);
  }}

  /* Returns the gasbase in gas (it is stored in kilogas) */
  function offer_gasbase(LocalUnpacked memory local) internal pure returns (uint) { unchecked {
    return local.kilo_offer_gasbase * 1e3;
  }}

  /* Sets the gasbase in gas (it is stored in kilogas) */
  function offer_gasbase(LocalUnpacked memory local,uint val) internal pure { unchecked {
    local.kilo_offer_gasbase = val/1e3;
  }}

  /* Returns the bin that contains the best offer in \`local\`'s offer list */
  function bestBin(LocalUnpacked memory local) internal pure returns (Bin) { unchecked {
    return TickTreeLib.bestBinFromBranch(local.binPosInLeaf,local.level3,local.level2,local.level1,local.root);
  }}
}

Settings
{
  "remappings": [
    "@mgv/src/=src/",
    "@mgv/lib/=lib/",
    "@mgv/test/=test/",
    "@mgv/script/=script/",
    "@mgv/forge-std/=lib/forge-std/src/",
    "core/=lib/core/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "preprocessed/=lib/preprocessed/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"governance","type":"address"},{"internalType":"uint256","name":"gasprice","type":"uint256"},{"internalType":"uint256","name":"gasmax","type":"uint256"},{"internalType":"contract IBlast","name":"blastContract","type":"address"},{"internalType":"address","name":"blastGovernor","type":"address"},{"internalType":"contract IBlastPoints","name":"blastPointsContract","type":"address"},{"internalType":"address","name":"blastPointsOperator","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[],"name":"CleanComplete","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"olKeyHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"taker","type":"address"},{"indexed":false,"internalType":"uint256","name":"offersToBeCleaned","type":"uint256"}],"name":"CleanStart","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"maker","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Credit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"maker","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Debit","type":"event"},{"anonymous":false,"inputs":[],"name":"Kill","type":"event"},{"anonymous":false,"inputs":[],"name":"NewMgv","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"olKeyHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"taker","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"takerWants","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"takerGives","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"penalty","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"mgvData","type":"bytes32"}],"name":"OfferFail","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"olKeyHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"taker","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"takerWants","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"takerGives","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"penalty","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"mgvData","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"posthookData","type":"bytes32"}],"name":"OfferFailWithPosthookData","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"olKeyHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"maker","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bool","name":"deprovision","type":"bool"}],"name":"OfferRetract","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"olKeyHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"taker","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"takerWants","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"takerGives","type":"uint256"}],"name":"OfferSuccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"olKeyHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"taker","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"takerWants","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"takerGives","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"posthookData","type":"bytes32"}],"name":"OfferSuccessWithPosthookData","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"olKeyHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"maker","type":"address"},{"indexed":false,"internalType":"int256","name":"tick","type":"int256"},{"indexed":false,"internalType":"uint256","name":"gives","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasprice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasreq","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"OfferWrite","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"olKeyHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"taker","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"OrderComplete","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"olKeyHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"taker","type":"address"},{"indexed":false,"internalType":"Tick","name":"maxTick","type":"int256"},{"indexed":false,"internalType":"uint256","name":"fillVolume","type":"uint256"},{"indexed":false,"internalType":"bool","name":"fillWants","type":"bool"}],"name":"OrderStart","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"olKeyHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"outbound_tkn","type":"address"},{"indexed":true,"internalType":"address","name":"inbound_tkn","type":"address"},{"indexed":false,"internalType":"uint256","name":"tickSpacing","type":"uint256"},{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"SetActive","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"olKeyHash","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"SetDensity96X32","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"olKeyHash","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"SetFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"olKeyHash","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"offer_gasbase","type":"uint256"}],"name":"SetGasbase","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"SetGasmax","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"SetGasprice","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"value","type":"address"}],"name":"SetGovernance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"SetMaxGasreqForFailingOffers","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"SetMaxRecursionDepth","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"value","type":"address"}],"name":"SetMonitor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"SetNotify","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"SetUseOracle","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"tickSpacing","type":"uint256"}],"internalType":"struct OLKey","name":"olKey","type":"tuple"},{"components":[{"internalType":"uint256","name":"offerId","type":"uint256"},{"internalType":"Tick","name":"tick","type":"int256"},{"internalType":"uint256","name":"gasreq","type":"uint256"},{"internalType":"uint256","name":"takerWants","type":"uint256"}],"internalType":"struct MgvLib.CleanTarget[]","name":"targets","type":"tuple[]"},{"internalType":"address","name":"taker","type":"address"}],"name":"cleanByImpersonation","outputs":[{"internalType":"uint256","name":"successes","type":"uint256"},{"internalType":"uint256","name":"bounty","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"tickSpacing","type":"uint256"}],"internalType":"struct OLKey","name":"olKey","type":"tuple"},{"internalType":"uint256","name":"offerId","type":"uint256"},{"internalType":"Offer","name":"offer","type":"uint256"},{"internalType":"uint256","name":"takerWants","type":"uint256"},{"internalType":"uint256","name":"takerGives","type":"uint256"},{"internalType":"OfferDetail","name":"offerDetail","type":"uint256"},{"internalType":"Global","name":"global","type":"uint256"},{"internalType":"Local","name":"local","type":"uint256"}],"internalType":"struct MgvLib.SingleOrder","name":"sor","type":"tuple"},{"internalType":"address","name":"taker","type":"address"}],"name":"flashloan","outputs":[{"internalType":"uint256","name":"gasused","type":"uint256"},{"internalType":"bytes32","name":"makerData","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"maker","type":"address"}],"name":"fund","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"fund","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"tickSpacing","type":"uint256"}],"internalType":"struct OLKey","name":"olKey","type":"tuple"},{"internalType":"uint256","name":"offerId","type":"uint256"},{"internalType":"Tick","name":"tick","type":"int256"},{"internalType":"uint256","name":"gasreq","type":"uint256"},{"internalType":"uint256","name":"takerWants","type":"uint256"},{"internalType":"address","name":"taker","type":"address"}],"name":"internalCleanByImpersonation","outputs":[{"internalType":"uint256","name":"bounty","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"tickSpacing","type":"uint256"}],"internalType":"struct OLKey","name":"olKey","type":"tuple"},{"internalType":"Tick","name":"maxTick","type":"int256"},{"internalType":"uint256","name":"fillVolume","type":"uint256"},{"internalType":"bool","name":"fillWants","type":"bool"}],"name":"marketOrderByTick","outputs":[{"internalType":"uint256","name":"takerGot","type":"uint256"},{"internalType":"uint256","name":"takerGave","type":"uint256"},{"internalType":"uint256","name":"bounty","type":"uint256"},{"internalType":"uint256","name":"feePaid","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"tickSpacing","type":"uint256"}],"internalType":"struct OLKey","name":"olKey","type":"tuple"},{"internalType":"Tick","name":"maxTick","type":"int256"},{"internalType":"uint256","name":"fillVolume","type":"uint256"},{"internalType":"bool","name":"fillWants","type":"bool"},{"internalType":"uint256","name":"maxGasreqForFailingOffers","type":"uint256"}],"name":"marketOrderByTickCustom","outputs":[{"internalType":"uint256","name":"takerGot","type":"uint256"},{"internalType":"uint256","name":"takerGave","type":"uint256"},{"internalType":"uint256","name":"bounty","type":"uint256"},{"internalType":"uint256","name":"feePaid","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"tickSpacing","type":"uint256"}],"internalType":"struct OLKey","name":"olKey","type":"tuple"},{"internalType":"uint256","name":"takerWants","type":"uint256"},{"internalType":"uint256","name":"takerGives","type":"uint256"},{"internalType":"bool","name":"fillWants","type":"bool"}],"name":"marketOrderByVolume","outputs":[{"internalType":"uint256","name":"takerGot","type":"uint256"},{"internalType":"uint256","name":"takerGave","type":"uint256"},{"internalType":"uint256","name":"bounty","type":"uint256"},{"internalType":"uint256","name":"feePaid","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"tickSpacing","type":"uint256"}],"internalType":"struct OLKey","name":"olKey","type":"tuple"},{"internalType":"Tick","name":"maxTick","type":"int256"},{"internalType":"uint256","name":"fillVolume","type":"uint256"},{"internalType":"bool","name":"fillWants","type":"bool"},{"internalType":"address","name":"taker","type":"address"}],"name":"marketOrderForByTick","outputs":[{"internalType":"uint256","name":"takerGot","type":"uint256"},{"internalType":"uint256","name":"takerGave","type":"uint256"},{"internalType":"uint256","name":"bounty","type":"uint256"},{"internalType":"uint256","name":"feePaid","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"tickSpacing","type":"uint256"}],"internalType":"struct OLKey","name":"olKey","type":"tuple"},{"internalType":"uint256","name":"takerWants","type":"uint256"},{"internalType":"uint256","name":"takerGives","type":"uint256"},{"internalType":"bool","name":"fillWants","type":"bool"},{"internalType":"address","name":"taker","type":"address"}],"name":"marketOrderForByVolume","outputs":[{"internalType":"uint256","name":"takerGot","type":"uint256"},{"internalType":"uint256","name":"takerGave","type":"uint256"},{"internalType":"uint256","name":"bounty","type":"uint256"},{"internalType":"uint256","name":"feePaid","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"tickSpacing","type":"uint256"}],"internalType":"struct OLKey","name":"olKey","type":"tuple"},{"internalType":"Tick","name":"tick","type":"int256"},{"internalType":"uint256","name":"gives","type":"uint256"},{"internalType":"uint256","name":"gasreq","type":"uint256"},{"internalType":"uint256","name":"gasprice","type":"uint256"}],"name":"newOfferByTick","outputs":[{"internalType":"uint256","name":"offerId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"tickSpacing","type":"uint256"}],"internalType":"struct OLKey","name":"olKey","type":"tuple"},{"internalType":"uint256","name":"wants","type":"uint256"},{"internalType":"uint256","name":"gives","type":"uint256"},{"internalType":"uint256","name":"gasreq","type":"uint256"},{"internalType":"uint256","name":"gasprice","type":"uint256"}],"name":"newOfferByVolume","outputs":[{"internalType":"uint256","name":"offerId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"tickSpacing","type":"uint256"}],"internalType":"struct OLKey","name":"olKey","type":"tuple"},{"internalType":"uint256","name":"offerId","type":"uint256"},{"internalType":"bool","name":"deprovision","type":"bool"}],"name":"retractOffer","outputs":[{"internalType":"uint256","name":"provision","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"tickSpacing","type":"uint256"}],"internalType":"struct OLKey","name":"olKey","type":"tuple"},{"internalType":"Tick","name":"tick","type":"int256"},{"internalType":"uint256","name":"gives","type":"uint256"},{"internalType":"uint256","name":"gasreq","type":"uint256"},{"internalType":"uint256","name":"gasprice","type":"uint256"},{"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"updateOfferByTick","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"outbound_tkn","type":"address"},{"internalType":"address","name":"inbound_tkn","type":"address"},{"internalType":"uint256","name":"tickSpacing","type":"uint256"}],"internalType":"struct OLKey","name":"olKey","type":"tuple"},{"internalType":"uint256","name":"wants","type":"uint256"},{"internalType":"uint256","name":"gives","type":"uint256"},{"internalType":"uint256","name":"gasreq","type":"uint256"},{"internalType":"uint256","name":"gasprice","type":"uint256"},{"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"updateOfferByVolume","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"bool","name":"noRevert","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60c06040523480156200001157600080fd5b506040516200773e3803806200773e83398101604081905262000034916200079d565b60408051808201825260088152674d616e67726f766560c01b6020808301919091528251808401845260018152603160f81b9082015282517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f918101919091527fad429437ccd58094880a2ef8967aa0dfef6b6570d8cd0712b6a3c11a4d76aa21928101929092527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608301524660808301523060a083015288918891889160c00160408051601f198184030181529082905280516020909101206080527f72201d073f249840099a96530e189c4291c1852040b1e96ff1f606db656be4ff9150600090a1604051620001489062000776565b604051809103906000f08015801562000165573d6000803e3d6000fd5b506001600160a01b031660a0819052604051602481018490526000919060440160408051601f198184030181529181526020820180516001600160e01b0316637e553f1760e11b17905251620001bc919062000831565b600060405180830381855af49150503d8060008114620001f9576040519150601f19603f3d011682016040523d82523d6000602084013e620001fe565b606091505b505080915050806200024b5760405162461bcd60e51b81526020600482015260116024820152706d67762f63746f722f676173707269636560781b60448201526064015b60405180910390fd5b60a0516001600160a01b0316826040516024016200026b91815260200190565b60408051601f198184030181529181526020820180516001600160e01b03166395999ee360e01b17905251620002a2919062000831565b600060405180830381855af49150503d8060008114620002df576040519150601f19603f3d011682016040523d82523d6000602084013e620002e4565b606091505b505080915050806200032b5760405162461bcd60e51b815260206004820152600f60248201526e0daceec5ec6e8dee45ecec2e6dac2f608b1b604482015260640162000242565b60a0516001600160a01b0316604b6040516024016200034c91815260200190565b60408051601f198184030181529181526020820180516001600160e01b031663e37de38b60e01b1790525162000383919062000831565b600060405180830381855af49150503d8060008114620003c0576040519150601f19603f3d011682016040523d82523d6000602084013e620003c5565b606091505b505080915050806200041a5760405162461bcd60e51b815260206004820152601a60248201527f6d67762f63746f722f6d6178526563757273696f6e4465707468000000000000604482015260640162000242565b60a0516001600160a01b0316826003026040516024016200043d91815260200190565b60408051601f198184030181529181526020820180516001600160e01b03166374bfac3360e01b1790525162000474919062000831565b600060405180830381855af49150503d8060008114620004b1576040519150601f19603f3d011682016040523d82523d6000602084013e620004b6565b606091505b50508091505080620005165760405162461bcd60e51b815260206004820152602260248201527f6d67762f63746f722f6d6178476173726571466f724661696c696e674f666665604482015261727360f01b606482015260840162000242565b60a0516040516001600160a01b0386811660248301529091169060440160408051601f198184030181529181526020820180516001600160e01b031663ab033ea960e01b179052516200056a919062000831565b600060405180830381855af49150503d8060008114620005a7576040519150601f19603f3d011682016040523d82523d6000602084013e620005ac565b606091505b50508091505080620006015760405162461bcd60e51b815260206004820152601360248201527f6d67762f63746f722f676f7665726e616e636500000000000000000000000000604482015260640162000242565b50505050836001600160a01b031663f098767a6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156200064157600080fd5b505af115801562000656573d6000803e3d6000fd5b50505050836001600160a01b0316634e606c476040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156200069657600080fd5b505af1158015620006ab573d6000803e3d6000fd5b5050604051631d70c8d360e31b81526001600160a01b0386811660048301528716925063eb8646989150602401600060405180830381600087803b158015620006f357600080fd5b505af115801562000708573d6000803e3d6000fd5b50506040516336b91f2b60e01b81526001600160a01b038481166004830152851692506336b91f2b9150602401600060405180830381600087803b1580156200075057600080fd5b505af115801562000765573d6000803e3d6000fd5b505050505050505050505062000862565b611aff8062005c3f83390190565b6001600160a01b03811681146200079a57600080fd5b50565b600080600080600080600060e0888a031215620007b957600080fd5b8751620007c68162000784565b8097505060208801519550604088015194506060880151620007e88162000784565b6080890151909450620007fb8162000784565b60a08901519093506200080e8162000784565b60c0890151909250620008218162000784565b8091505092959891949750929550565b6000825160005b8181101562000854576020818601810151858301520162000838565b506000920191825250919050565b60805160a0516153b06200088f600039600061013f0152600081816102e0015261101301526153b06000f3fe6080604052600436106101185760003560e01c806378393c88116100a0578063b60d428811610064578063b60d4288146103d6578063d6a522ce146103de578063e030e04b146103fe578063e9801a291461041e578063f95902aa1461043e57610128565b806378393c88146103505780637d3e616214610370578063a84b08c814610390578063b09d2a16146103a3578063b268813c146103c357610128565b80632e1a7d4d116100e75780632e1a7d4d1461026957806332810dd6146102995780633644e515146102ce57806359eba454146103105780636e7a5cb81461033057610128565b8063129d19a7146101de5780631440e604146101f15780632275123f14610236578063230244081461025657610128565b366101285761012633610451565b005b34801561013457600080fd5b5060003660606000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168585604051610177929190614b93565b600060405180830381855af49150503d80600081146101b2576040519150601f19603f3d011682016040523d82523d6000602084013e6101b7565b606091505b509150915081156101cb5791506101d39050565b805160208201fd5b915050805190602001f35b6101266101ec366004614c2f565b610491565b3480156101fd57600080fd5b5061021161020c366004614c89565b6104b0565b6040805194855260208501939093529183015260608201526080015b60405180910390f35b34801561024257600080fd5b50610211610251366004614cd2565b6104f8565b610126610264366004614d2c565b610451565b34801561027557600080fd5b50610289610284366004614d47565b610541565b604051901515815260200161022d565b3480156102a557600080fd5b506102b96102b4366004614d60565b6105e8565b6040805192835260208301919091520161022d565b3480156102da57600080fd5b506103027f000000000000000000000000000000000000000000000000000000000000000081565b60405190815260200161022d565b34801561031c57600080fd5b5061028961032b366004614df7565b6107a1565b34801561033c57600080fd5b5061030261034b366004614e42565b610828565b34801561035c57600080fd5b506102b961036b366004614e83565b6109af565b34801561037c57600080fd5b5061030261038b366004614ec1565b610ae9565b61012661039e366004614c2f565b610e72565b3480156103af57600080fd5b506101266103be366004614f1b565b610f27565b6103026103d1366004614fb1565b61123c565b61012661125e565b3480156103ea57600080fd5b506102116103f9366004614c89565b611269565b34801561040a57600080fd5b50610211610419366004614ff4565b611290565b34801561042a57600080fd5b50610211610439366004614cd2565b6112b7565b61030261044c366004614fb1565b6112e9565b60408051606081018252600080825260208201819052918101829052610476906113f3565b505090506104838161151c565b61048d8234611560565b5050565b6104a88661049f87876115bc565b86868686610e72565b505050505050565b6000806000806000856104c357866104c5565b875b905060006104d3888a6115bc565b90506104e48a82848a3360006115e4565b929d919c509a509098509650505050505050565b60008060008060008661050b578761050d565b885b9050600061051b898b6115bc565b905061052a8b82848b8b6112b7565b955095509550955050505b95509550955095915050565b600061054d3383611899565b60405133908390600081818185875af1925050503d806000811461058d576040519150601f19603f3d011682016040523d82523d6000602084013e610592565b606091505b505080915050806105e35760405162461bcd60e51b81526020600482015260166024820152751b59dd8bddda5d1a191c985dd0d85b1b14995d995c9d60521b60448201526064015b60405180910390fd5b919050565b600080826001600160a01b0316610600876060902090565b6040518681527fd301a4893957e66d0ef05ff8028181ee134fa453ff2aa849e0546aac493af6dd9060200160405180910390a360005b848110156107655760603687878481811061065357610653615045565b9050608002019050306001600160a01b0316637d3e61628a83600001358460200135856040013586606001358c60405160240161069596959493929190615082565b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505091505060606000306001600160a01b0316836040516106e191906150c2565b6000604051808303816000865af19150503d806000811461071e576040519150601f19603f3d011682016040523d82523d6000602084013e610723565b606091505b5092509050806107355750505061075d565b50848060010195505060008180602001905181019061075491906150f1565b94909401935050505b600101610636565b5061076f8161195e565b6040517f3ee675eb8551a3a94e52d27289fd8bc0ef6f57c148b6bc9a9104ac9d7269f8d590600090a194509492505050565b6001600160a01b03848116600081815260056020908152604080832088861680855290835281842033808652908452828520968916808652968452828520889055825196875292860187905292949193917f6e791f456e6f5034d4928b936c32bf7e650d8228543ce7985d632ab03934837d910160405180910390a4506001949350505050565b6000806000610836866113f3565b9250925050610844826119fc565b60008581526005820160205260409020805460018201546108658160601c90565b6001600160a01b0316336001600160a01b0316146108c55760405162461bcd60e51b815260206004820152601d60248201527f6d67762f726574726163744f666665722f756e617574686f72697a656400000060448201526064016105da565b602c82901c60016001607f1b0316156109085760008590506108f8858b6040015185896108f18b611a48565b6001611a53565b509550808614610906578585555b505b6109148383838a611f25565b861561094b5761092381611f62565b604882901c62ffffff1601602582901c6303ffffff16620f42400202955061094b3387611560565b6109558160601c90565b6001600160a01b03166109698a6060902090565b604080518b81528a151560208201527f69a8c809e58310d1995905640ab3d1e8efe2ca772c21432a693da69912a478f1910160405180910390a350505050509392505050565b600080333014610a015760405162461bcd60e51b815260206004820152601760248201527f6d67762f666c6173686c6f616e2f70726f74656374656400000000000000000060448201526064016105da565b610a20610a146040860160208701614d2c565b84308760c00135611f78565b15610aa457610a4a610a386040860160208701614d2c565b60e086013560601c8660c00135612056565b15610a6257610a588461212b565b9092509050610ae2565b60408051606081018252731b59dd8bdb585ad95c949958d95a5d9951985a5b60621b815260006020820181905291810191909152610a9f90606081fd5b610ae2565b60408051606081018252741b59dd8bdd185ad95c951c985b9cd9995c91985a5b605a1b815260006020820181905291810191909152610ae290606081fd5b9250929050565b6000333014610b305760405162461bcd60e51b81526020600482015260136024820152721b59dd8bd8db19585b8bdc1c9bdd1958dd1959606a1b60448201526064016105da565b610b38614aa9565b610b418661229b565b610b8d5760405162461bcd60e51b815260206004820152601960248201527f6d67762f636c65616e2f7469636b2f6f75744f6652616e67650000000000000060448201526064016105da565b610100810186905260016001607f1b03841115610bec5760405162461bcd60e51b815260206004820152601b60248201527f6d67762f636c65616e2f74616b657257616e74732f746f6f426967000000000060448201526064016105da565b60a081018490526001600160a01b038316606082015260016080820152610c11614b15565b8881526000610c1f8a6113f3565b60e0850191825260c0850192835260208086018d905260008d815260058301909152604090819020805491870191909152600181015460a087015292519151909350610c6b91906122b6565b610c788360e001516119fc565b6040830151602c1c60016001607f1b0316610cce5760405162461bcd60e51b81526020600482015260166024820152756d67762f636c65616e2f6f666665724e6f744c69766560501b60448201526064016105da565b60a0830151889060481c62ffffff161115610d245760405162461bcd60e51b81526020600482015260166024820152756d67762f636c65616e2f676173726571546f6f4c6f7760501b60448201526064016105da565b610d3d89610d39856040015160401b60eb1d90565b1490565b610d825760405162461bcd60e51b81526020600482015260166024820152750daceec5ec6d8cac2dc5ee8d2c6d69ad2e6dac2e8c6d60531b60448201526064016105da565b60e0830151610d92906001612301565b60e08401819052825560008080610daa85888861231d565b925092509250806f6d67762f74726164655375636365737360801b03610e125760405162461bcd60e51b815260206004820152601960248201527f6d67762f636c65616e2f6f666665724469644e6f744661696c0000000000000060448201526064016105da565b610e32858f6040015188604001518960e001516108f18b60e00151611a48565b5060e08701819052610e45906000612301565b60e087018190528555610e5b8787858585612889565b5050506040909301519a9950505050505050505050565b610e7a614b15565b6000610e85886113f3565b60c0850182905260a0850192909252909150610ea0906119fc565b610eb28260a001518360c001516122b6565b3415610ec257610ec23334611560565b87825260208083018790526040808401859052606084018790526080840186905260008581526005840190925290205460e083015260c0820151610f0982848a6001612ade565b60c08301518114610f1c5760c083015182555b505050505050505050565b42841015610f6c5760405162461bcd60e51b81526020600482015260126024820152711b59dd8bdc195c9b5a5d0bd95e1c1a5c995960721b60448201526064016105da565b6001600160a01b03878116600081815260066020908152604080832080546001810190915581517ff0ea0a7146fb6eedb561d97b593d57d9b7df3c94d689372dc01302e5780248f4818501528f8716818401528e871660608201526080810195909552948b1660a085015260c084018a905260e084018590526101008085018a9052815180860390910181526101208501909152805191012061190160f01b6101408401527f0000000000000000000000000000000000000000000000000000000000000000610142840152610162830152906101820160408051601f198184030181528282528051602091820120600080855291840180845281905260ff89169284019290925260608301879052608083018690529092509060019060a0016020604051602081039080840390855afa1580156110ae573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906110e45750896001600160a01b0316816001600160a01b0316145b6111305760405162461bcd60e51b815260206004820152601b60248201527f6d67762f7065726d69742f696e76616c69645369676e6174757265000000000060448201526064016105da565b87600560008e6001600160a01b03166001600160a01b0316815260200190815260200160002060008d6001600160a01b03166001600160a01b0316815260200190815260200160002060008c6001600160a01b03166001600160a01b0316815260200190815260200160002060008b6001600160a01b03166001600160a01b0316815260200190815260200160002081905550896001600160a01b03168b6001600160a01b03168d6001600160a01b03167f6e791f456e6f5034d4928b936c32bf7e650d8228543ce7985d632ab03934837d8c8c6040516112269291906001600160a01b03929092168252602082015260400190565b60405180910390a4505050505050505050505050565b60006112548661124c87876115bc565b8686866112e9565b9695505050505050565b61126733610451565b565b60008060008061127e888888883360006115e4565b929b919a509850909650945050505050565b6000806000806112a489898989338a6115e4565b929c919b50995090975095505050505050565b6000806000806112cc898989898960006115e4565b8c5160208e01519498509296509094509250610535918786613475565b60006112f3614b15565b60006112fe886113f3565b60c0850182905260a0850192909252909150611319906119fc565b61132b8260a001518360c001516122b6565b341561133b5761133b3334611560565b60c082015163ffffffff166001016040830181905263ffffffff81161461139a5760405162461bcd60e51b81526020600482015260136024820152726d67762f6f6666657249644f766572666c6f7760681b60448201526064016105da565b604082015160c083015163ffffffff191663ffffffff9091161760c08301528782526020820186905260608201859052608082018490526113de8183896000612ade565b60c08201519055604001519695505050505050565b600080600060026000611407866060902090565b81526020019081526020016000209050600154925080600001549150611432836001605f1b16151590565b15611515576000806114448560601c90565b6001600160a01b031663d87629ac876040518263ffffffff1660e01b815260040161146f919061510a565b6040805180830381865afa15801561148b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114af9190615118565b915091506114c2826303ffffff81161490565b156114ee576b3ffffff000000000000000001985166b3ffffff00000000000000000604484901b161794505b6101ff81168103611512576101ff60ee1b1984166101ff60ee1b60ee83901b161793505b50505b9193909250565b6508000000000081161561155d5760405162461bcd60e51b81526020600482015260086024820152671b59dd8bd919585960c21b60448201526064016105da565b50565b6001600160a01b03821660008181526004602052604090819020805484019055517f1bbf55d483639f8103dc4e035af71a4fbdb16c80be740fa3eef81198acefa094906115b09084815260200190565b60405180910390a25050565b60008060006115cb858561355c565b915091506115d9828261377c565b925050505b92915050565b60008060008060016001607f1b038811156116415760405162461bcd60e51b815260206004820152601c60248201527f6d67762f6d4f726465722f66696c6c566f6c756d652f746f6f4269670000000060448201526064016105da565b61164a8961229b565b6116965760405162461bcd60e51b815260206004820152601a60248201527f6d67762f6d4f726465722f7469636b2f6f75744f6652616e676500000000000060448201526064016105da565b61169e614aa9565b61010081018a90526001600160a01b038716606082015287151560808201526116c5614b15565b8b815260006116d38d6113f3565b60e085019190915260c08401829052915060231c60ff16610160840152876117085760c082015160031c63ffffffff1661170a565b875b8361012001818152505061174a81600401600061173361172d8660e00151611a48565b60021d90565b815260200190815260200160002054600181141890565b60e0840181905261175a90613aa6565b60208084018290526000918252600583019052604090819020549083015260a083018b905260c082015160e083015161179391906122b6565b6117a08260e001516119fc565b60e08201516117b0906001612301565b60e0830181905281558151606090819020604080518f8152602081018f90528d15158183015290516001600160a01b038d16937f730e8e2cc287cd5296445ccef0dcf7f0695d8b3d620dcc9dd19c671a6f5663a5928290030190a3611816818484613adb565b611823836040015161195e565b8151606090206001600160a01b038a16907feab9f920eda38e2e10cfc76b3f85201b8bbe82fac69de4c4509001b66e5e33af8560c0015160405161186991815260200190565b60405180910390a3505080516020820151604083015160c090930151919d909c50919a5098509650505050505050565b6001600160a01b038216600090815260046020526040902054818110156119025760405162461bcd60e51b815260206004820152601960248201527f6d67762f696e73756666696369656e7450726f766973696f6e0000000000000060448201526064016105da565b6001600160a01b038316600081815260046020526040908190208484039055517f59c79d79be0fadf59fe689b6952b7ebe90201a3a1f00d4a31982377890bc6046906119519085815260200190565b60405180910390a2505050565b801561155d57604051600090339083908381818185875af1925050503d80600081146119a6576040519150601f19603f3d011682016040523d82523d6000602084013e6119ab565b606091505b505090508061048d5760405162461bcd60e51b815260206004820152601760248201527f6d67762f73656e6450656e616c7479526576657274656400000000000000000060448201526064016105da565b64010000000081161561155d5760405162461bcd60e51b81526020600482015260146024820152731b59dd8bdc99595b9d1c985b98de531bd8dad95960621b60448201526064016105da565b60006115de82613d14565b6000808080611a62888a613d8d565b90506000611a708960e01c90565b905063ffffffff60c08a901c16811580611a88575080155b15611aa357611aa08c60040160006117338660021d90565b93505b81600003611abd57611ab6848483613db4565b9350611adc565b600082815260058d01602052604090208054611ad99083613dda565b90555b80600003611af657611aef848484613df7565b9350611b1a565b600081815260058d016020526040902080546001600160e01b031660e084901b1790555b8115801590611b2857508015155b15611b3d578860009550955050505050611f1a565b868015611b48575081155b8015611b545750878313155b96505050811582178a6004016000611b6c8460021d90565b815260208101919091526040016000205581611ed9576000611b8e8260081d90565b90506000611b9c8860081d90565b8203611c0c57611bbb8360ac8b901c6001600160401b03165b90613e1e565b9050611bc78982613e39565b9850868015611bd4575080155b15611c0757600082815260038d01602052604090205415611c0757600082815260038d0160205260409020600160ff1b90555b611c4c565b600082815260038d016020526040902054611c319084906001600160ff1b0316611bb5565b9050600160ff1b8117600083815260038e0160205260409020555b80611eba57611c5b83600e1d90565b9150611c6788600e1d90565b8203611cd757611c8683606c8b901c6001600160401b03165b90613e5e565b9050611c928982613e6a565b9850868015611c9f575080155b15611cd257600082815260028d01602052604090205415611cd257600082815260028d0160205260409020600160ff1b90555b611d17565b600082815260028d016020526040902054611cfc9084906001600160ff1b0316611c80565b9050600160ff1b8117600083815260028e0160205260409020555b80611e7e57611d268360141d90565b9150611d328860141d90565b8203611d8d57611d5183602c8b901c6001600160401b03165b90613e8f565b9050611d5d8982613e9b565b9850868015611d6a575080155b15611d8857600160ff1b8117600083815260018e0160205260409020555b611dcd565b600082815260018d016020526040902054611db29084906001600160ff1b0316611d4b565b9050600160ff1b8117600083815260018e0160205260409020555b80611e4257611de583602a8b901c6003165b90613ec6565b9050611df18982613ed6565b985080611e075788879550955050505050611f1a565b8615611e4257611e1681613ef1565b600081815260018e0160205260409020549092506001600160ff1b03169050611e3f8982613e9b565b98505b8615611e7e57611e528183613f05565b600081815260028e0160205260409020549092506001600160ff1b03169050611e7b8982613e6a565b98505b8615611eba57611e8e8183613f05565b600081815260038e0160205260409020549092506001600160ff1b03169050611eb78982613e39565b98505b8615611ed657611ed360048d0160006117338486613f05565b93505b50505b8415611f1157611f0e6001600160401b036001600160801b038411600781901b85901c9190911115901560011b178890613f1d565b96505b50508483915091505b965096945050505050565b7507fffffffffffffffffffffffffffffff0000000000019831692508015611f5657677fffffe00000000019821691505b50908255600190910155565b6000603f82901c6101ff165b6103e80292915050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b179052915160009283918291891690611fde9085906150c2565b6000604051808303816000865af19150503d806000811461201b576040519150601f19603f3d011682016040523d82523d6000602084013e612020565b606091505b509150915081801561204a57508051158061204a57508080602001905181019061204a919061513c565b98975050505050505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1790529151600092839182918816906120b49085906150c2565b6000604051808303816000865af19150503d80600081146120f1576040519150601f19603f3d011682016040523d82523d6000602084013e6120f6565b606091505b5091509150818015612120575080511580612120575080806020019051810190612120919061513c565b979650505050505050565b6000806000836040516024016121419190615159565b60408051808303601f19018152919052602081018051632380fafd60e01b6001600160e01b03909116179052905060e0840135604881901c62ffffff169060601c60005a90508260408204820310156121da57604080516060810182527f6d67762f6e6f74456e6f756768476173466f724d616b657254726164650000008152600060208201819052918101919091526121da90606081fd5b60006121e7838587613f34565b965090505a820396508061222d5761222d60405180606001604052806e1b59dd8bdb585ad95c94995d995c9d608a1b81526020018960001b815260200188815250606081fd5b600061224b61223f60208b018b614d2c565b85308c60a00135611f78565b905080612290576122906040518060600160405280741b59dd8bdb585ad95c951c985b9cd9995c91985a5b605a1b81526020018a60001b815260200189815250606081fd5b505050505050915091565b6000620d89e71982121580156115de575050620d89e8121590565b6122bf8261151c565b600160ff1b811661048d5760405162461bcd60e51b815260206004820152600c60248201526b6d67762f696e61637469766560a01b60448201526064016105da565b64010000000060209190911b1664010000000019919091161790565b60a08201516040820151600091829182919060016001607f1b03602c82901c1690839061234990613f62565b90508760800151801561235c5750828211155b80612374575087608001511580156123745750828111155b1561238c5760608701829052608087018190526123ea565b8760800151156123c2576123b1836123ab896040015160401b60eb1d90565b90613f82565b6080880152606087018390526123ea565b6123dd836123d7896040015160401b60eb1d90565b90613fae565b6060880152608087018390525b50505060408401516000906060906001600160c01b038116604088015260e08701516503ff00000001600160ee1b0319811660e08901526060890151604051309182916378393c8891612442918d919060240161524e565b60408051601f198184030181529181526020820180516001600160e01b031660e09490941b93909317909252905161247a92506150c2565b6000604051808303816000865af19150503d80600081146124b7576040519150601f19603f3d011682016040523d82523d6000602084013e6124bc565b606091505b5060408a019390935260e089019190915292509050811561263057808060200190518101906124eb9190615118565b60c088015191965094506f6d67762f74726164655375636365737360801b93506001605e1b161561257b5760c0860151606088810151604051630c29692960e01b81529290911c91630c29692991612548918a919060040161524e565b600060405180830381600087803b15801561256257600080fd5b505af1158015612576573d6000803e3d6000fd5b505050505b606086018051885101808952905111156125cf5760405162461bcd60e51b81526020600482015260156024820152746d67762f746f74616c476f742f6f766572666c6f7760581b60448201526064016105da565b608086018051602089018051909101908190529051111561262b5760405162461bcd60e51b81526020600482015260166024820152756d67762f746f74616c476176652f6f766572666c6f7760501b60448201526064016105da565b61283c565b602081015160408201516060830151909650945092506e1b59dd8bdb585ad95c94995d995c9d608a1b83148061267d575082741b59dd8bdb585ad95c951c985b9cd9995c91985a5b605a1b145b8061269e575082731b59dd8bdb585ad95c949958d95a5d9951985a5b60621b145b156127365760a086015160481c62ffffff166101408801805191909101905260c08601516001605e1b161561262b5760c08601516060888101516040516303027c4560e51b81529290911c9163604f88a0916126ff918a919060040161524e565b600060405180830381600087803b15801561271957600080fd5b505af115801561272d573d6000803e3d6000fd5b5050505061283c565b827f6d67762f6e6f74456e6f756768476173466f724d616b65725472616465000000036127a55760405162461bcd60e51b815260206004820152601d60248201527f6d67762f6e6f74456e6f756768476173466f724d616b6572547261646500000060448201526064016105da565b82741b59dd8bdd185ad95c951c985b9cd9995c91985a5b605a1b036128045760405162461bcd60e51b81526020600482015260156024820152741b59dd8bdd185ad95c951c985b9cd9995c91985a5b605a1b60448201526064016105da565b60405162461bcd60e51b815260206004820152600d60248201526c36b3bb17b9bbb0b822b93937b960991b60448201526064016105da565b61287e8860050160008860200151815260200190815260200160002087604001518860a00151866f6d67762f74726164655375636365737360801b1415611f25565b505093509350939050565b60a084015160481c62ffffff16808411156128a2578093505b60008060006128b5888886038888613fde565b9250925092508287019650846f6d67762f74726164655375636365737360801b146129f15760006128e689896140c8565b60408b018051820190529050826129775788602001518a606001516001600160a01b03166129198b600001516060902090565b6060808d01516080808f015160408051938452602084019190915282018790529181018b90529081018690527f4f6fba9fcbdb2ad30462ad0e0e92fd280ffee4e24885661c7a7497802240008d9060a00160405180910390a46129eb565b88602001518a606001516001600160a01b03166129998b600001516060902090565b6060808d01516080808f015160408051938452602084019190915282018790529181018b90527f8e83cc09450b5666c4b273f69ceff6631efac5291f291422fdc1e6e6c9223e19910160405180910390a45b50610f1c565b81612a6757876020015189606001516001600160a01b0316612a188a600001516060902090565b6060808c015160808d015160408051928352602083019190915281018690527f88e7a2023de6638f80f184ea4126f2f532f046b745646d0b5ac8c205cc1b4874910160405180910390a4610f1c565b876020015189606001516001600160a01b0316612a898a600001516060902090565b7f5575d7d7c01adb4eeee1e2ec4a63652fdf5086d71fd325fa1af8034796b899048b606001518c60800151604051612acb929190918252602082015260400190565b60405180910390a4505050505050505050565b60808301516303ffffff811614612b375760405162461bcd60e51b815260206004820152601e60248201527f6d67762f77726974654f666665722f67617370726963652f746f6f426967000060448201526064016105da565b60a083015160441c6303ffffff1683608001511015612b645760a083015160441c6303ffffff1660808401525b60a0830151602c1c62ffffff1683606001511115612bc45760405162461bcd60e51b815260206004820152601d60248201527f6d67762f77726974654f666665722f6761737265712f746f6f4869676800000060448201526064016105da565b6000836020015111612c185760405162461bcd60e51b815260206004820152601b60248201527f6d67762f77726974654f666665722f67697665732f746f6f4c6f77000000000060448201526064016105da565b612c42612c288460c00151614159565b606085015160c086015191019060ee1c6101ff1690614169565b83602001511015612c955760405162461bcd60e51b815260206004820152601d60248201527f6d67762f77726974654f666665722f64656e736974792f746f6f4c6f7700000060448201526064016105da565b602083015160016001607f1b03811614612cf15760405162461bcd60e51b815260206004820152601b60248201527f6d67762f77726974654f666665722f67697665732f746f6f426967000000000060448201526064016105da565b82516040015180830760001281840501612d0b8183614182565b9350612d168461229b565b612d625760405162461bcd60e51b815260206004820152601e60248201527f6d67762f77726974654f666665722f7469636b2f6f75744f6652616e6765000060448201526064016105da565b604085015185516060902033906020808901516080808b01516060808d0151604080518e8152968701959095529385019190915283019190915281018490527fbeb2dc87c4db0b489fe0485121086dfff34fcaac67b1120861b95f1b6649c97b9060a00160405180910390a36000818152600588016020526040812060018101548615612e7b57612df38160601c90565b6001600160a01b0316336001600160a01b031614612e535760405162461bcd60e51b815260206004820152601c60248201527f6d67762f7570646174654f666665722f756e617574686f72697a65640000000060448201526064016105da565b612e5c81611f62565b604882901c62ffffff1601602582901c6303ffffff16620f4240020292505b861580612e9557506060890151604882901c62ffffff1614155b80612eae57506080890151602582901c6303ffffff1614155b80612ecd5750612ec18960c00151614159565b612eca82611f62565b14155b15612f5b576000612ee18a60c00151614159565b9050612f54338b606001516103e88481612efd57612efd6151e1565b048d60800151677fffffe000000000602582901b1668ff8000000000000000603f84901b166bffffff000000000000000000604886901b166bffffffffffffffffffffffff19606088901b16171717949350505050565b6001840155505b60008960800151612f6f8b60c00151614159565b8b606001510102620f424002905083811115612f9657612f9133858303611899565b612faa565b83811015612faa57612faa33828603611560565b50505060c087015160009150612fca9060ac1c6001600160401b03161590565b15612fd6575081613062565b612fe38760c00151611a48565b60e0880151909150602c1c60016001607f1b03161561306257600083821315905061301a89868a60e001518b60c001518686611a53565b60c08a0191909152905080156130605760c08801516130439060ac1c6001600160401b03161590565b1561305057839150613060565b61305d8860c00151611a48565b91505b505b8083136130815761307b6003841660c089015190613f1d565b60c08801525b60006130968960040160006117338760021d90565b9050806133a0576000806130aa8660081d90565b905060006130b88560081d90565b90508082036130d95760c08b015160ac1c6001600160401b0316925061314e565b600082815260038d0160205260409020546001600160ff1b031692508082121561314e5760c08b015160ac1c6001600160401b03168015158061312b5750600082815260038e01602052604090205415155b801561314b57600083815260038f0160205260409020600160ff1b831790555b50505b8082136131775761316d6131628489613e1e565b60c08d015190613e39565b60c08c015261319f565b61318d6131848489613e1e565b600160ff1b1790565b600083815260038e0160205260409020555b8261339c576131ae87600e1d90565b91506131ba85600e1d90565b90508082036131db5760c08b0151606c1c6001600160401b03169250613250565b600082815260028d0160205260409020546001600160ff1b03169250808212156132505760c08b0151606c1c6001600160401b03168015158061322d5750600082815260028e01602052604090205415155b801561324d57600083815260028f0160205260409020600160ff1b831790555b50505b8082136132795761326f6132648489613e5e565b60c08d015190613e6a565b60c08c0152613298565b6132866131848489613e5e565b600083815260028e0160205260409020555b8261339c576132a78760141d90565b91506132b38560141d90565b90508082036132d45760c08b0151602c1c6001600160401b03169250613324565b600082815260018d0160205260409020546001600160ff1b03169250808212156133245760c08b015161331290602c1c6001600160401b0316613184565b600082815260018e0160205260409020555b80821361334d576133436133388489613e8f565b60c08d015190613e9b565b60c08c015261336c565b61335a6131848489613e8f565b600083815260018e0160205260409020555b8261339c5760c08b01516133969061338b908990602a1c600316611ddf565b60c08d015190613ed6565b60c08c01525b5050505b60006133ac828661418e565b9050806000036133c8576133c1828686613db4565b91506133e7565b600081815260058b016020526040902080546133e49086613dda565b90555b6133f2828686613df7565b9150811582178a60040160006134088860021d90565b81526020808201929092526040016000908120929092558a015160e083901b60ab8b901b621fffff60ab1b1617602c9190911b7507fffffffffffffffffffffffffffffff00000000000161760009586526005909b01602052505060409092209790975550505050505050565b6001600160a01b0380851660009081526005602090815260408083208785168452825280832093861683529281528282203383529081905291902054828110156134f45760405162461bcd60e51b815260206004820152601060248201526f6d67762f6c6f77416c6c6f77616e636560801b60448201526064016105da565b3360008181526020848152604091829020868503908190558251938452908301526001600160a01b038681169288821692918a16917f6e791f456e6f5034d4928b936c32bf7e650d8228543ce7985d632ab03934837d910160405180910390a4505050505050565b60008060016001607f1b038411156135b65760405162461bcd60e51b815260206004820152601f60248201527f6d67762f726174696f46726f6d566f6c2f696e626f756e642f746f6f4269670060448201526064016105da565b60016001607f1b0383111561360d5760405162461bcd60e51b815260206004820181905260248201527f6d67762f726174696f46726f6d566f6c2f6f7574626f756e642f746f6f42696760448201526064016105da565b8260000361363057506ffffb12cdf1087799c03e5a811a7aaa1c90506000610ae2565b8360000361365357506f800276a52a62a71c29fc1cc441e523bc905060ff610ae2565b600083608086901b81613668576136686151e1565b04905060006136f7827e09010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f6307c4acdd60e01b6001600160801b03831160071b8315600890811b9190911784811c6001600160401b031060061b1784811c63ffffffff1060051b1793841c600181901c17600281901c17600481901c179081901c17601081901c170260fb1c1a1790565b9050816000036137495760405162461bcd60e51b815260206004820152601e60248201527f6d67762f726174696f46726f6d566f6c756d65732f7a65726f526174696f000060448201526064016105da565b607f81111561376a57607e1981019190911c9250608003607f019050610ae2565b607f0390811b92506080019050610ae2565b600061379b83836f800276a52a62a71c29fc1cc441e523bc60ff6141a8565b156137e85760405162461bcd60e51b815260206004820152601860248201527f6d67762f7469636b46726f6d526174696f2f746f6f4c6f77000000000000000060448201526064016105da565b6138056ffffb12cdf1087799c03e5a811a7aaa1c600085856141a8565b156138525760405162461bcd60e51b815260206004820152601960248201527f6d67762f7469636b46726f6d526174696f2f746f6f486967680000000000000060448201526064016105da565b6000604061386184607f61528c565b901b90506000613872607f806152b3565b85901c800260c081901c6780000000000000001692909217607f83811c60ff94851c1c800260c181901c674000000000000000169290921782821c92851c9290921c800260c281901c672000000000000000169290921782821c92851c9290921c800260c381901c671000000000000000169290921782821c92851c9290921c800260c481901c670800000000000000169290921782821c92851c9290921c800260c581901c670400000000000000169290921782821c92851c9290921c800260c681901c670200000000000000169290921782821c92851c9290921c800260c781901c670100000000000000169290921782821c92851c9290921c800260c881901c6680000000000000169290921782821c92851c9290921c800260c981901c6640000000000000169290921782821c92851c9290921c800260ca81901c6620000000000000169290921782821c92851c9290921c800260cb81901c6610000000000000169290921782821c9290941c9190911c800260cc81901c6608000000000000169390931792901c90506000613a1683691b13d180eb882abba6436152c6565b905060006080613a366f0147b240d5bf82739a122ad4969722368461528c565b901d905060006080613a58846fd9e63e52eeeb7875a857348794e736756152f6565b901d9050600080613a68836141c7565b915091506000613a7a8b8b85856141a8565b905083851480613a875750805b15613a9457849850613a98565b8398505b505050505050505092915050565b60006115de826001600160401b036001600160801b038211600781901b83901c9190911115901560011b176040021b60e01c90565b60008260a00151118015613b045750816101000151613b01826040015160401b60eb1d90565b13155b8015613b14575060008160200151115b8015613b2557506000826101600151115b8015613b3b575081610120015182610140015111155b15613c5d5761016082018051600019019052602080820151600090815260058501909152604081206001015460a08301528080613b7986868661231d565b60608701516080880151602089015160408a015160a08b015196995094975092955090939092906f6d67762f74726164655375636365737360801b869003613bd9578960800151613bca5783613bcc565b845b60a08b0180519190910390525b613bf28b8b848c60e001518d6000015160400151614239565b60e08b01526020808b01829052600091825260058d01905260409081902054908a0152613c208b8b8b613adb565b6060890185905260808901849052602089018390526040890182905260a08901819052613c508a8a8a8a8a612889565b5050505050505050505050565b604080820151825190910151600090613c77908390613d8d565b90508260200151600014613ce957613c8f8260e01c90565b15613cba5760408301516001600160e01b031660208085015160009081526005880190915260409020555b6000613cc68260021d90565b9050613cd68560e0015180151790565b6000918252600487016020526040909120555b60e0830151613cf9906000612301565b60e084018190528555613d0c84846144f8565b50505b505050565b6000806002600680806001613d2f6003602a8a901c16614592565b03901b613d48602c88901c6001600160401b0316614592565b17901b613d61606c87901c6001600160401b0316614592565b17901b613d7a60ac86901c6001600160401b0316614592565b17901b60ec84901c600316179392505050565b6000613dad82613da08560401b60eb1d90565b9060008183071391050190565b9392505050565b6000600383166040810260e00383811b63ffffffff90911b198616175b95945050505050565b60c01b63ffffffff60c01b1663ffffffff60c01b19919091161790565b6000600383166002810260010160200260e00383811b63ffffffff90911b19861617613dd1565b600080613e2a836145ed565b6001901b939093189392505050565b60ac1b67ffffffffffffffff60ac1b1667ffffffffffffffff60ac1b19919091161790565b600080613e2a83614602565b606c1b67ffffffffffffffff606c1b1667ffffffffffffffff606c1b19919091161790565b600080613e2a83614610565b602c1b6d0ffffffffffffffff00000000000166d0ffffffffffffffff0000000000019919091161790565b600080613e2a8360141d60010190565b602a1b650c000000000016650c000000000019919091161790565b60006001613efe8361461e565b0392915050565b6000613f108361461e565b6040830201905092915050565b60ec1b600360ec1b16600360ec1b19919091161790565b600080613f3f614b75565b60648185516020870160008a8af192508060448415020151915050935093915050565b60006115de602c83901c60016001607f1b03166123ab8460401b60eb1d90565b6000806000613f90856141c7565b915091506115d9848302826001811b600019018216151591901c0190565b60008080613fc3613fbe8661531e565b6141c7565b909250905080613fd3858461533a565b901c95945050505050565b6000806000808760405180604001604052808881526020018781525060405160240161400b929190615351565b60408051601f198184030181529190526020810180516001600160e01b03166331f86ed160e21b17905260a089015190915060009061404a9060601c90565b905060005a90508860408204820310156140a65760405162461bcd60e51b815260206004820181905260248201527f6d67762f6e6f74456e6f756768476173466f724d616b6572506f7374686f6f6b60448201526064016105da565b6140b1828a85613f34565b90955093505a810395505050509450945094915050565b60a082015160009062ffffff604882901c169082906140e690611f62565b60a08601519083019060251c6303ffffff16620f4240020290508184111561410c578193505b600061411b8660e00151614159565b60c08701519086019060441c6303ffffff16620f424002029050818111156141405750805b6115d96141518760a0015160601c90565b828403611560565b6000602182901c6101ff16611f6e565b6000602061417684614629565b8302901c905092915050565b6000613dad82846152c6565b60006001600260038416020160200283901b60e01c613dad565b600081841180613dd157508184148015613dd157505050909110919050565b6000806141d383614650565b909250905060eb83901b60007ed89e8c075c4155dd3213326cbbcf86fd1f63f5c205dd2ad2cf44f9349b39e180830782139083050390607e1982850101908113156142215793841c9361422b565b8060000385901b94505b81607f039350505050915091565b600080806142478685613d8d565b60e088015190915063ffffffff60c088901c1690614266818484613db4565b9050816000036144e25761427c81846000613df7565b90508061449e57801581178a60040160006142978660021d90565b81526020019081526020016000208190555060006142c584611bb58a6001600160401b0360ac9190911c1690565b905060006142d38560081d90565b90508161447a57600081815260038d0160205260409020541561430857600081815260038d0160205260409020600160ff1b90555b61431285600e1d90565b905061432b85606c8b901c6001600160401b0316611c80565b91508161444e57600081815260028d0160205260409020541561436057600081815260028d0160205260409020600160ff1b90555b61436a8560141d90565b905061438385602c8b901c6001600160401b0316611d4b565b91508161442257600081815260018d0160205260409020600160ff1b90556143b285602a8b901c600316611ddf565b91506143be8983613ed6565b985081614403576143cf8983613e9b565b98506143db8983613e6a565b98506143e78983613e39565b600060e08d0181905290995096508895506144ee945050505050565b61440c82613ef1565b600081815260018e016020526040902054925090505b61442c8983613e9b565b98506144388282613f05565b600081815260028e016020526040902054925090505b6144588983613e6a565b98506144648282613f05565b600081815260038e016020526040902054925090505b6144848983613e39565b985061449960048d0160006117338585613f05565b925050505b6001600160401b036001600160801b038211600781901b83901c9190911115901560011b176144cd8882613f1d565b97506144de82826040021b60e01c90565b9250505b60e08901529250849150505b9550959350505050565b60e08101516000906127109060f71c60ff168451028161451a5761451a6151e1565b0490508015614532578251819003835260c083018190525b825115613d0f578151516060840151845161454e929190612056565b613d0f5760405162461bcd60e51b815260206004820152601560248201527436b3bb17a6b3bb2330b4b62a37a830bcaa30b5b2b960591b60448201526064016105da565b7e011c021d0e18031e16140f191104081f1b0d17151310071a0c12060b050a096001600160401b0382811680196001019093161663ffffffff811160051b921560061b9290921791821c63077cb53160e01b0260fb1c1a1790565b6000603f6145fb8360021d90565b1692915050565b6000603f6145fb8360081d90565b6000603f6145fb83600e1d90565b60006115de82614592565b60006007821161463a575060031690565b506004600382161760029190911c600119011b90565b6000806000808412614662578361466b565b61466b8461531e565b9050620d89e88111156146c05760405162461bcd60e51b815260206004820152601760248201527f6d67762f6162735469636b2f6f75744f66426f756e647300000000000000000060448201526064016105da565b600060018216156146e3576ffff97272373d413259a46990580e213993506146eb565b600160801b93505b6002821615614715576080614710856ffff2e50f5f656932ef12357cf3c7fdcb61533a565b901c93505b600482161561473f57608061473a856fffe5caca7e10e4e61c3624eaa0941ccf61533a565b901c93505b6008821615614769576080614764856fffcb9843d60f6159c9db58835c92664361533a565b901c93505b601082161561479357608061478e856fff973b41fa98c081472e6896dfb254bf61533a565b901c93505b60208216156147bd5760806147b8856fff2ea16466c96a3843ec78b326b5286061533a565b901c93505b60408216156147e75760806147e2856ffe5dee046a99a2a811c461f1969c305261533a565b901c93505b608082161561481157608061480c856ffcbe86c7900a88aedcffc83b479aa3a361533a565b901c93505b61010082161561483c576080614837856ff987a7253ac413176f2b074cf7815e5361533a565b901c93505b610200821615614867576080614862856ff3392b0822b70005940c7a398e4b70f261533a565b901c93505b61040082161561489257608061488d856fe7159475a2c29b7443b29c7fa6e889d861533a565b901c93505b6108008216156148bd5760806148b8856fd097f3bdfd2022b8845ad8f792aa582561533a565b901c93505b6110008216156148e85760806148e3856fa9f746462d870fdf8a65dc1f90e061e461533a565b901c93505b61200082161561492057608061490e856fe1b0d342ada5437121767bec575e65ed61533a565b901c935061491d6001826152f6565b90505b614000821615614958576080614946856fc6f84d7e5f423f66048c541550bf3e9661533a565b901c93506149556002826152f6565b90505b61800082161561499057608061497e856f9aa508b5b7a84e1c677de54f3e99bc8f61533a565b901c935061498d6004826152f6565b90505b620100008216156149c95760806149b7856fbad5f1bdb70232cd33865244bdcc089c61533a565b901c93506149c66009826152f6565b90505b62020000821615614a025760806149f0856f885b9613d7e87aa498106fb7fa5edd3761533a565b901c93506149ff6012826152f6565b90505b62040000821615614a3b576080614a29856f9142e0723efb884889d1f447715afacd61533a565b901c9350614a386025826152f6565b90505b62080000821615614a74576080614a62856fa4d9a773d61316918f140bd96e8e681461533a565b901c9350614a71604b826152f6565b90505b6000851315614a9557600184856000030401935080614a929061531e565b90505b614aa08160806152f6565b92505050915091565b60405180610180016040528060008152602001600081526020016000815260200160006001600160a01b03168152602001600015158152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60408051610160810190915260006101008201818152610120830182905261014083019190915281908152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60405180608001604052806004906020820280368337509192915050565b8183823760009101908152919050565b80356001600160a01b03811681146105e357600080fd5b600060608284031215614bcc57600080fd5b604051606081018181106001600160401b0382111715614bfc57634e487b7160e01b600052604160045260246000fd5b604052905080614c0b83614ba3565b8152614c1960208401614ba3565b6020820152604083013560408201525092915050565b6000806000806000806101008789031215614c4957600080fd5b614c538888614bba565b986060880135985060808801359760a0810135975060c0810135965060e00135945092505050565b801515811461155d57600080fd5b60008060008060c08587031215614c9f57600080fd5b614ca98686614bba565b9350606085013592506080850135915060a0850135614cc781614c7b565b939692955090935050565b600080600080600060e08688031215614cea57600080fd5b614cf48787614bba565b9450606086013593506080860135925060a0860135614d1281614c7b565b9150614d2060c08701614ba3565b90509295509295909350565b600060208284031215614d3e57600080fd5b613dad82614ba3565b600060208284031215614d5957600080fd5b5035919050565b60008060008060a08587031215614d7657600080fd5b614d808686614bba565b935060608501356001600160401b0380821115614d9c57600080fd5b818701915087601f830112614db057600080fd5b813581811115614dbf57600080fd5b8860208260071b8501011115614dd457600080fd5b602083019550809450505050614dec60808601614ba3565b905092959194509250565b60008060008060808587031215614e0d57600080fd5b614e1685614ba3565b9350614e2460208601614ba3565b9250614e3260408601614ba3565b9396929550929360600135925050565b600080600060a08486031215614e5757600080fd5b614e618585614bba565b9250606084013591506080840135614e7881614c7b565b809150509250925092565b600080828403610160811215614e9857600080fd5b61014080821215614ea857600080fd5b849350614eb6818601614ba3565b925050509250929050565b6000806000806000806101008789031215614edb57600080fd5b614ee58888614bba565b9550606087013594506080870135935060a0870135925060c08701359150614f0f60e08801614ba3565b90509295509295509295565b60008060008060008060008060006101208a8c031215614f3a57600080fd5b614f438a614ba3565b9850614f5160208b01614ba3565b9750614f5f60408b01614ba3565b9650614f6d60608b01614ba3565b955060808a0135945060a08a0135935060c08a013560ff81168114614f9157600080fd5b8093505060e08a013591506101008a013590509295985092959850929598565b600080600080600060e08688031215614fc957600080fd5b614fd38787614bba565b976060870135975060808701359660a0810135965060c00135945092505050565b600080600080600060e0868803121561500c57600080fd5b6150168787614bba565b9450606086013593506080860135925060a086013561503481614c7b565b9497939650919460c0013592915050565b634e487b7160e01b600052603260045260246000fd5b80516001600160a01b03908116835260208083015190911690830152604090810151910152565b6101008101615091828961505b565b6060820196909652608081019490945260a084019290925260c08301526001600160a01b031660e090910152919050565b6000825160005b818110156150e357602081860181015185830152016150c9565b506000920191825250919050565b60006020828403121561510357600080fd5b5051919050565b606081016115de828461505b565b6000806040838503121561512b57600080fd5b505080516020909101519092909150565b60006020828403121561514e57600080fd5b8151613dad81614c7b565b61014081016001600160a01b038061517085614ba3565b1683528061518060208601614ba3565b1660208401525060408301356040830152606083013560608301526080830135608083015260a083013560a083015260c083013560c083015260e083013560e083015261010080840135818401525061012080840135818401525092915050565b634e487b7160e01b600052601260045260246000fd5b61520282825161505b565b6020810151606083015260408101516080830152606081015160a0830152608081015160c083015260a081015160e083015260c081015161010083015260e08101516101208301525050565b610160810161525d82856151f7565b6001600160a01b03929092166101409190910152919050565b634e487b7160e01b600052601160045260246000fd5b81810360008312801583831316838312821617156152ac576152ac615276565b5092915050565b818103818111156115de576115de615276565b80820260008212600160ff1b841416156152e2576152e2615276565b81810583148215176115de576115de615276565b808201828112600083128015821682158216171561531657615316615276565b505092915050565b6000600160ff1b820161533357615333615276565b5060000390565b80820281158282048414176115de576115de615276565b610180810161536082856151f7565b82516101408301526020830151610160830152939250505056fea2646970667358221220f20c555a0b8c655435611cf76124491da7fdaa415277e469ea93b70baa3a993164736f6c63430008140033608060405234801561001057600080fd5b50611adf806100206000396000f3fe608060405234801561001057600080fd5b50600436106102065760003560e01c806374bfac331161011a578063c7b06e87116100ad578063e982fb8d1161007c578063e982fb8d1461055a578063eeba33681461056d578063f514244414610580578063fcaa7e2e14610593578063fec777b4146105a657600080fd5b8063c7b06e87146104d3578063cd6a7ca7146104e6578063ce5bbece14610534578063e37de38b1461054757600080fd5b806396b4c30f116100e957806396b4c30f14610492578063a05f9906146104a5578063a1db9782146104ad578063ab033ea9146104c057600080fd5b806374bfac33146104305780637ecebe0014610443578063902455301461046c57806395999ee31461047f57600080fd5b80635275b2121161019d5780635cd823901161016c5780635cd82390146103395780635d49dc111461034c5780636804f613146103e15780636c6fda56146103f457806370a082311461040757600080fd5b80635275b212146102d05780635a611f4e146102f85780635aa6e6751461030b5780635b2752961461032657600080fd5b806330adf81f116101d957806330adf81f1461027a57806341b12acf146102a057806341c0e1b5146102b3578063443759fb146102bd57600080fd5b80630469bb661461020b57806304a7a1b4146102335780630adf51a9146102545780631c04321514610267575b600080fd5b61021e610219366004611827565b6105b9565b60405190151581526020015b60405180910390f35b610246610241366004611843565b6105f1565b60405190815260200161022a565b610246610262366004611843565b610636565b610246610275366004611827565b6106b6565b7ff0ea0a7146fb6eedb561d97b593d57d9b7df3c94d689372dc01302e5780248f4610246565b6102466102ae366004611827565b6106d4565b6102bb610715565b005b6102bb6102cb366004611843565b61075f565b6102e36102de366004611827565b61083a565b6040805192835260208301919091520161022a565b6102bb61030636600461187c565b61085a565b6000546040516001600160a01b03909116815260200161022a565b6102bb610334366004611827565b6108b4565b6102bb610347366004611899565b610957565b6103b161035a3660046118b4565b60408051606080820183526000808352602080840182905292840181905293845260038252928290208251938401835280546001600160a01b03908116855260018201541691840191909152600201549082015290565b6040805182516001600160a01b03908116825260208085015190911690820152918101519082015260600161022a565b6102bb6103ef36600461187c565b6109c1565b610246610402366004611843565b610a14565b610246610415366004611899565b6001600160a01b031660009081526004602052604090205490565b6102bb61043e3660046118b4565b610a8f565b610246610451366004611899565b6001600160a01b031660009081526006602052604090205490565b61024661047a366004611827565b610b23565b6102bb61048d3660046118b4565b610b8d565b6102bb6104a03660046118cd565b610c42565b610246610d59565b6102bb6104bb366004611907565b610d86565b6102bb6104ce366004611899565b610dee565b6102e36104e1366004611843565b610e90565b6102466104f4366004611931565b6001600160a01b03938416600090815260056020908152604080832095871683529481528482209386168252928352838120919094168452905290205490565b610246610542366004611843565b610ee1565b6102bb6105553660046118b4565b610f2b565b610246610568366004611843565b610fbe565b6102bb61057b366004611843565b611006565b6102bb61058e366004611843565b6110bd565b6102bb6105a13660046118b4565b611181565b6102466105b4366004611843565b611233565b60006105eb600260006105cd856060902090565b81526020019081526020016000206000015464010000000016151590565b92915050565b60008060026000610603866060902090565b8152602001908152602001600020905061062081600001546112ae565b6000928352600501602052506040902054919050565b60008060026000610648866060902090565b815260208101919091526040016000208054909150610666816112ae565b83610679610673836112fd565b600e1d90565b0361069757606c81901c67ffffffffffffffff165b925050506105eb565b60008481526002830160205260409020546001600160ff1b031661068e565b60006106c182611308565b5091506106cf9050816112ae565b919050565b600080600260006106e6856060902090565b815260208101919091526040016000208054909150610704816112ae565b602a81901c6003165b949350505050565b61071d611431565b60015465080000000000191665080000000000176001556040517fbe26733c2bf6ff3ea5ba8cfe744422bd49052ff9ed5685c9e81e6f9321dbaddd90600090a1565b610767611431565b6040805180820190915260148152736d67762f636f6e6669672f6665652f386269747360601b602082015260ff821682146107be5760405162461bcd60e51b81526004016107b591906119a9565b60405180910390fd5b506000600260006107d0856060902090565b815260208101919091526040016000208054909150600161010160f71b031660ff60f71b60f784901b16178155606083206040518381527f139d6f58e9a127229667c8e3b36e88890a66cfc8ab1024ddc513e189e125b75b906020015b60405180910390a2505050565b60008061084683611308565b509092509050610855816112ae565b915091565b610862611431565b6001546001605e1b1916605e82901b6001605e1b161760015560405181151581527f9b84fe4d9ffc14a6f7ec50c18884ee0d4bb8165aa718a28558df12ab4bd32b14906020015b60405180910390a150565b6108bc611431565b6000600260006108cd846060902090565b815260200190815260200160002090506108f56000826000015461149e90919063ffffffff16565b8155602082015182516001600160a01b039182169116610916846060902090565b6040808601518151908152600060208201527fae754ab4fca7273731e24a2db3350b4f89e22d7eff0819e2e3ab99aadca96de2910160405180910390a45050565b61095f611431565b6001546bffffffffffffffffffffffff166bffffffffffffffffffffffff19606083901b16176001556040516001600160a01b03821681527ff592e42c32883b35bebfbf9d9b801570a25d5ccbba452310746ad2cfd20d548f906020016108a9565b6109c9611431565b6001546001605f1b1916605f82901b6001605f1b161760015560405181151581527fbf39fbfab835c119637f4dbc4fb0f428c559c210200405d1ec25ddb50a93f9d4906020016108a9565b60008060026000610a26866060902090565b815260208101919091526040016000208054909150610a44816112ae565b83610a57610a51836112fd565b60141d90565b03610a7057602c81901c67ffffffffffffffff1661068e565b60008481526001830160205260409020546001600160ff1b031661068e565b610a97611431565b63ffffffff811681146040518060600160405280602b8152602001611a7f602b913990610ad75760405162461bcd60e51b81526004016107b591906119a9565b506001546407fffffff819166407fffffff8600383901b16176001556040518181527fd03e2a424e8f5cf53988a496e7a6a3b7e11e7c082a349bdfdb61f4623863bc01906020016108a9565b60008060026000610b35856060902090565b815260208101919091526040016000208054909150610b53816112ae565b61070d610b88836004016000610b71610b6b866112fd565b60021d90565b815260200190815260200160002054600181141890565b6114b1565b610b95611431565b60408051808201909152601881527f6d67762f636f6e6669672f6761736d61782f3234626974730000000000000000602082015262ffffff82168214610bee5760405162461bcd60e51b81526004016107b591906119a9565b50600154680ffffff000000000001916680ffffff00000000000602c83901b16176001556040518181527f8cdff0cd5ed2543bcdf7eb93d8ec1c23626e579925f4531beeef15d84cf78d18906020016108a9565b610c4a611431565b606084206000818152600360209081526040808320885181546001600160a01b03199081166001600160a01b03928316178355848b0151600184018054909216921691909117905581890151600291820155909152902080546001600160ff1b0316600160ff1b178155602086015186516001600160a01b039182169116610cd3886060902090565b6040808a01518151908152600160208201527fae754ab4fca7273731e24a2db3350b4f89e22d7eff0819e2e3ab99aadca96de2910160405180910390a4610d1a868661075f565b610d2486856110bd565b610d2e8684611006565b600019600090815260019091016020526040808220600160ff1b908190558280529120555050505050565b60408051606081018252600080825260208201819052918101829052610d7e90611308565b509092915050565b610d8e611431565b600054610da69083906001600160a01b0316836114e7565b610dea5760405162461bcd60e51b81526020600482015260156024820152741b59dd8bddda5d1a191c985dd15490cc8c11985a5b605a1b60448201526064016107b5565b5050565b610df6611431565b6001600160a01b038116610e425760405162461bcd60e51b815260206004820152601360248201527206d67762f636f6e6669672f676f762f6e6f743606c1b60448201526064016107b5565b600080546001600160a01b0319166001600160a01b0383169081179091556040519081527f24a8c4807b324a269a51827c3446b8ac1cc13810d7d0c0ca1efafabddd7b6219906020016108a9565b600080600060026000610ea4876060902090565b81526020019081526020016000209050610ec181600001546112ae565b600093845260050160205250506040902080546001909101549092909150565b60008060026000610ef3866060902090565b81526020019081526020016000209050610f1081600001546112ae565b60009283526004016020525060409020546001811418919050565b610f33611431565b60ff81168114604051806060016040528060228152602001611a3a6022913990610f705760405162461bcd60e51b81526004016107b591906119a9565b506001546507f80000000019166507f800000000602383901b16176001556040518181527ff9212b044703fa96962c8e40c32a62753a62752f2508fc05be2648e74dd24d85906020016108a9565b60008060026000610fd0866060902090565b81526020019081526020016000209050610fed81600001546112ae565b6000928352600501602052506040902060010154919050565b61100e611431565b61101f6103e882046101ff81161490565b604051806060016040528060238152602001611a5c60239139906110565760405162461bcd60e51b81526004016107b591906119a9565b50600060026000611068856060902090565b81526020810191909152604001600020805490915061108790836115bc565b8155606083206040518381527f4e29e260c5052737cbcd072416e6066a3db8a95f90ed5a03d4b424fc04c54b839060200161082d565b6110c5611431565b6000600260006110d6856060902090565b815260200190815260200160002090506110f382600160801b1190565b61113f5760405162461bcd60e51b815260206004820152601d60248201527f6d67762f636f6e6669672f64656e7369747939365833322f77726f6e6700000060448201526064016107b5565b805461114b90836115e1565b8155606083206040518381527f0bd5b439c78e74e0095d271b67b573d223bc701828c7e2f0bb943a44c5eaa4fe9060200161082d565b611189611431565b60408051808201909152601a81527f6d67762f636f6e6669672f67617370726963652f32366269747300000000000060208201526303ffffff821682146111e35760405162461bcd60e51b81526004016107b591906119a9565b506001546303ffffff60441b19166303ffffff60441b604483901b16176001556040518181527fdbebd814ae648f654dcc50c734aa76e55a32e96b7d85303a08e2ddf11874a0dd906020016108a9565b60008060026000611245866060902090565b815260208101919091526040016000208054909150611263816112ae565b83611276611270836112fd565b60081d90565b0361128f5760ac81901c67ffffffffffffffff1661068e565b60008481526003830160205260409020546001600160ff1b031661068e565b6401000000008116156112fa5760405162461bcd60e51b81526020600482015260146024820152731b59dd8bdc99595b9d1c985b98de531bd8dad95960621b60448201526064016107b5565b50565b60006105eb826115f6565b60008060006002600061131c866060902090565b81526020019081526020016000209050600154925080600001549150611347836001605f1b16151590565b1561142a576000806113598560601c90565b6040805163361d8a6b60e21b815288516001600160a01b03908116600483015260208a01518116602483015291890151604482015291169063d87629ac906064016040805180830381865afa1580156113b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113da91906119dc565b915091506113ed826303ffffff81161490565b1561140f576303ffffff60441b1985166303ffffff60441b604484901b161794505b6101ff81168103611427576114248482611672565b93505b50505b9193909250565b6000546001600160a01b031633148061144957503330145b8061145d57506000546001600160a01b0316155b61149c5760405162461bcd60e51b815260206004820152601060248201526f1b59dd8bdd5b985d5d1a1bdc9a5e995960821b60448201526064016107b5565b565b60ff1b6001600160ff1b03919091161790565b60006105eb8267ffffffffffffffff6001600160801b038211600781901b83901c9190911115901560011b176040021b60e01c90565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b179052915160009283918291881690611545908590611a00565b6000604051808303816000865af19150503d8060008114611582576040519150601f19603f3d011682016040523d82523d6000602084013e611587565b606091505b50915091508180156115b15750805115806115b15750808060200190518101906115b19190611a1c565b979650505050505050565b60006503fe000000001983166503fe000000006103e8840460211b16175b9392505050565b60006115da6115ef8361168b565b8490611672565b60008060026006808060016116116003602a8a901c1661173e565b03901b61162b602c88901c67ffffffffffffffff1661173e565b17901b611645606c87901c67ffffffffffffffff1661173e565b17901b61165f60ac86901c67ffffffffffffffff1661173e565b17901b60ec84901c600316179392505050565b60ee1b6101ff60ee1b166101ff60ee1b19919091161790565b600060038211611699575090565b6000611726837e09010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f6307c4acdd60e01b6001600160801b03831160071b8315600890811b9190911784811c67ffffffffffffffff1060061b1784811c63ffffffff1060051b1793841c600181901c17600281901c17600481901c179081901c17601081901c170260fb1c1a1790565b90506003600119820184901c16600282901b176115da565b7e011c021d0e18031e16140f191104081f1b0d17151310071a0c12060b050a0967ffffffffffffffff82811680196001019093161663ffffffff811160051b921560061b9290921791821c63077cb53160e01b0260fb1c1a1790565b80356001600160a01b03811681146106cf57600080fd5b6000606082840312156117c357600080fd5b6040516060810181811067ffffffffffffffff821117156117f457634e487b7160e01b600052604160045260246000fd5b6040529050806118038361179a565b81526118116020840161179a565b6020820152604083013560408201525092915050565b60006060828403121561183957600080fd5b6115da83836117b1565b6000806080838503121561185657600080fd5b61186084846117b1565b946060939093013593505050565b80151581146112fa57600080fd5b60006020828403121561188e57600080fd5b81356115da8161186e565b6000602082840312156118ab57600080fd5b6115da8261179a565b6000602082840312156118c657600080fd5b5035919050565b60008060008060c085870312156118e357600080fd5b6118ed86866117b1565b966060860135965060808601359560a00135945092505050565b6000806040838503121561191a57600080fd5b6119238361179a565b946020939093013593505050565b6000806000806080858703121561194757600080fd5b6119508561179a565b935061195e6020860161179a565b925061196c6040860161179a565b915061197a6060860161179a565b905092959194509250565b60005b838110156119a0578181015183820152602001611988565b50506000910152565b60208152600082518060208401526119c8816040850160208701611985565b601f01601f19169190910160400192915050565b600080604083850312156119ef57600080fd5b505080516020909101519092909150565b60008251611a12818460208701611985565b9190910192915050565b600060208284031215611a2e57600080fd5b81516115da8161186e56fe6d67762f636f6e6669672f6d6178526563757273696f6e44657074682f38626974736d67762f636f6e6669672f6b696c6f5f6f666665725f676173626173652f39626974736d67762f636f6e6669672f6d6178476173726571466f724661696c696e674f66666572732f333262697473a2646970667358221220c34d86be73a6cf73dabf952d90dcba34207c8c7526afa5e9503914a4acb2b01264736f6c63430008140033000000000000000000000000d137d6c5e565c480773e3031a5dab5b9327668e300000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000001e84800000000000000000000000004300000000000000000000000000000000000002000000000000000000000000d70b96bcc12d45f347b9b70899dcbb3dfe13384f0000000000000000000000002536fe9ab3f511540f2f9e2ec2a805005c3dd800000000000000000000000000441f6a3d415708f89e4adbb6b995371554aa909e

Deployed Bytecode

0x6080604052600436106101185760003560e01c806378393c88116100a0578063b60d428811610064578063b60d4288146103d6578063d6a522ce146103de578063e030e04b146103fe578063e9801a291461041e578063f95902aa1461043e57610128565b806378393c88146103505780637d3e616214610370578063a84b08c814610390578063b09d2a16146103a3578063b268813c146103c357610128565b80632e1a7d4d116100e75780632e1a7d4d1461026957806332810dd6146102995780633644e515146102ce57806359eba454146103105780636e7a5cb81461033057610128565b8063129d19a7146101de5780631440e604146101f15780632275123f14610236578063230244081461025657610128565b366101285761012633610451565b005b34801561013457600080fd5b5060003660606000807f000000000000000000000000d527ff4ad938aa5fd622fb3fb398409bd072cecc6001600160a01b03168585604051610177929190614b93565b600060405180830381855af49150503d80600081146101b2576040519150601f19603f3d011682016040523d82523d6000602084013e6101b7565b606091505b509150915081156101cb5791506101d39050565b805160208201fd5b915050805190602001f35b6101266101ec366004614c2f565b610491565b3480156101fd57600080fd5b5061021161020c366004614c89565b6104b0565b6040805194855260208501939093529183015260608201526080015b60405180910390f35b34801561024257600080fd5b50610211610251366004614cd2565b6104f8565b610126610264366004614d2c565b610451565b34801561027557600080fd5b50610289610284366004614d47565b610541565b604051901515815260200161022d565b3480156102a557600080fd5b506102b96102b4366004614d60565b6105e8565b6040805192835260208301919091520161022d565b3480156102da57600080fd5b506103027f13e03db65abbd3745a080291f179e56d8cc90d8c84023254b2dca1a6ca8245f181565b60405190815260200161022d565b34801561031c57600080fd5b5061028961032b366004614df7565b6107a1565b34801561033c57600080fd5b5061030261034b366004614e42565b610828565b34801561035c57600080fd5b506102b961036b366004614e83565b6109af565b34801561037c57600080fd5b5061030261038b366004614ec1565b610ae9565b61012661039e366004614c2f565b610e72565b3480156103af57600080fd5b506101266103be366004614f1b565b610f27565b6103026103d1366004614fb1565b61123c565b61012661125e565b3480156103ea57600080fd5b506102116103f9366004614c89565b611269565b34801561040a57600080fd5b50610211610419366004614ff4565b611290565b34801561042a57600080fd5b50610211610439366004614cd2565b6112b7565b61030261044c366004614fb1565b6112e9565b60408051606081018252600080825260208201819052918101829052610476906113f3565b505090506104838161151c565b61048d8234611560565b5050565b6104a88661049f87876115bc565b86868686610e72565b505050505050565b6000806000806000856104c357866104c5565b875b905060006104d3888a6115bc565b90506104e48a82848a3360006115e4565b929d919c509a509098509650505050505050565b60008060008060008661050b578761050d565b885b9050600061051b898b6115bc565b905061052a8b82848b8b6112b7565b955095509550955050505b95509550955095915050565b600061054d3383611899565b60405133908390600081818185875af1925050503d806000811461058d576040519150601f19603f3d011682016040523d82523d6000602084013e610592565b606091505b505080915050806105e35760405162461bcd60e51b81526020600482015260166024820152751b59dd8bddda5d1a191c985dd0d85b1b14995d995c9d60521b60448201526064015b60405180910390fd5b919050565b600080826001600160a01b0316610600876060902090565b6040518681527fd301a4893957e66d0ef05ff8028181ee134fa453ff2aa849e0546aac493af6dd9060200160405180910390a360005b848110156107655760603687878481811061065357610653615045565b9050608002019050306001600160a01b0316637d3e61628a83600001358460200135856040013586606001358c60405160240161069596959493929190615082565b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505091505060606000306001600160a01b0316836040516106e191906150c2565b6000604051808303816000865af19150503d806000811461071e576040519150601f19603f3d011682016040523d82523d6000602084013e610723565b606091505b5092509050806107355750505061075d565b50848060010195505060008180602001905181019061075491906150f1565b94909401935050505b600101610636565b5061076f8161195e565b6040517f3ee675eb8551a3a94e52d27289fd8bc0ef6f57c148b6bc9a9104ac9d7269f8d590600090a194509492505050565b6001600160a01b03848116600081815260056020908152604080832088861680855290835281842033808652908452828520968916808652968452828520889055825196875292860187905292949193917f6e791f456e6f5034d4928b936c32bf7e650d8228543ce7985d632ab03934837d910160405180910390a4506001949350505050565b6000806000610836866113f3565b9250925050610844826119fc565b60008581526005820160205260409020805460018201546108658160601c90565b6001600160a01b0316336001600160a01b0316146108c55760405162461bcd60e51b815260206004820152601d60248201527f6d67762f726574726163744f666665722f756e617574686f72697a656400000060448201526064016105da565b602c82901c60016001607f1b0316156109085760008590506108f8858b6040015185896108f18b611a48565b6001611a53565b509550808614610906578585555b505b6109148383838a611f25565b861561094b5761092381611f62565b604882901c62ffffff1601602582901c6303ffffff16620f42400202955061094b3387611560565b6109558160601c90565b6001600160a01b03166109698a6060902090565b604080518b81528a151560208201527f69a8c809e58310d1995905640ab3d1e8efe2ca772c21432a693da69912a478f1910160405180910390a350505050509392505050565b600080333014610a015760405162461bcd60e51b815260206004820152601760248201527f6d67762f666c6173686c6f616e2f70726f74656374656400000000000000000060448201526064016105da565b610a20610a146040860160208701614d2c565b84308760c00135611f78565b15610aa457610a4a610a386040860160208701614d2c565b60e086013560601c8660c00135612056565b15610a6257610a588461212b565b9092509050610ae2565b60408051606081018252731b59dd8bdb585ad95c949958d95a5d9951985a5b60621b815260006020820181905291810191909152610a9f90606081fd5b610ae2565b60408051606081018252741b59dd8bdd185ad95c951c985b9cd9995c91985a5b605a1b815260006020820181905291810191909152610ae290606081fd5b9250929050565b6000333014610b305760405162461bcd60e51b81526020600482015260136024820152721b59dd8bd8db19585b8bdc1c9bdd1958dd1959606a1b60448201526064016105da565b610b38614aa9565b610b418661229b565b610b8d5760405162461bcd60e51b815260206004820152601960248201527f6d67762f636c65616e2f7469636b2f6f75744f6652616e67650000000000000060448201526064016105da565b610100810186905260016001607f1b03841115610bec5760405162461bcd60e51b815260206004820152601b60248201527f6d67762f636c65616e2f74616b657257616e74732f746f6f426967000000000060448201526064016105da565b60a081018490526001600160a01b038316606082015260016080820152610c11614b15565b8881526000610c1f8a6113f3565b60e0850191825260c0850192835260208086018d905260008d815260058301909152604090819020805491870191909152600181015460a087015292519151909350610c6b91906122b6565b610c788360e001516119fc565b6040830151602c1c60016001607f1b0316610cce5760405162461bcd60e51b81526020600482015260166024820152756d67762f636c65616e2f6f666665724e6f744c69766560501b60448201526064016105da565b60a0830151889060481c62ffffff161115610d245760405162461bcd60e51b81526020600482015260166024820152756d67762f636c65616e2f676173726571546f6f4c6f7760501b60448201526064016105da565b610d3d89610d39856040015160401b60eb1d90565b1490565b610d825760405162461bcd60e51b81526020600482015260166024820152750daceec5ec6d8cac2dc5ee8d2c6d69ad2e6dac2e8c6d60531b60448201526064016105da565b60e0830151610d92906001612301565b60e08401819052825560008080610daa85888861231d565b925092509250806f6d67762f74726164655375636365737360801b03610e125760405162461bcd60e51b815260206004820152601960248201527f6d67762f636c65616e2f6f666665724469644e6f744661696c0000000000000060448201526064016105da565b610e32858f6040015188604001518960e001516108f18b60e00151611a48565b5060e08701819052610e45906000612301565b60e087018190528555610e5b8787858585612889565b5050506040909301519a9950505050505050505050565b610e7a614b15565b6000610e85886113f3565b60c0850182905260a0850192909252909150610ea0906119fc565b610eb28260a001518360c001516122b6565b3415610ec257610ec23334611560565b87825260208083018790526040808401859052606084018790526080840186905260008581526005840190925290205460e083015260c0820151610f0982848a6001612ade565b60c08301518114610f1c5760c083015182555b505050505050505050565b42841015610f6c5760405162461bcd60e51b81526020600482015260126024820152711b59dd8bdc195c9b5a5d0bd95e1c1a5c995960721b60448201526064016105da565b6001600160a01b03878116600081815260066020908152604080832080546001810190915581517ff0ea0a7146fb6eedb561d97b593d57d9b7df3c94d689372dc01302e5780248f4818501528f8716818401528e871660608201526080810195909552948b1660a085015260c084018a905260e084018590526101008085018a9052815180860390910181526101208501909152805191012061190160f01b6101408401527f13e03db65abbd3745a080291f179e56d8cc90d8c84023254b2dca1a6ca8245f1610142840152610162830152906101820160408051601f198184030181528282528051602091820120600080855291840180845281905260ff89169284019290925260608301879052608083018690529092509060019060a0016020604051602081039080840390855afa1580156110ae573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906110e45750896001600160a01b0316816001600160a01b0316145b6111305760405162461bcd60e51b815260206004820152601b60248201527f6d67762f7065726d69742f696e76616c69645369676e6174757265000000000060448201526064016105da565b87600560008e6001600160a01b03166001600160a01b0316815260200190815260200160002060008d6001600160a01b03166001600160a01b0316815260200190815260200160002060008c6001600160a01b03166001600160a01b0316815260200190815260200160002060008b6001600160a01b03166001600160a01b0316815260200190815260200160002081905550896001600160a01b03168b6001600160a01b03168d6001600160a01b03167f6e791f456e6f5034d4928b936c32bf7e650d8228543ce7985d632ab03934837d8c8c6040516112269291906001600160a01b03929092168252602082015260400190565b60405180910390a4505050505050505050505050565b60006112548661124c87876115bc565b8686866112e9565b9695505050505050565b61126733610451565b565b60008060008061127e888888883360006115e4565b929b919a509850909650945050505050565b6000806000806112a489898989338a6115e4565b929c919b50995090975095505050505050565b6000806000806112cc898989898960006115e4565b8c5160208e01519498509296509094509250610535918786613475565b60006112f3614b15565b60006112fe886113f3565b60c0850182905260a0850192909252909150611319906119fc565b61132b8260a001518360c001516122b6565b341561133b5761133b3334611560565b60c082015163ffffffff166001016040830181905263ffffffff81161461139a5760405162461bcd60e51b81526020600482015260136024820152726d67762f6f6666657249644f766572666c6f7760681b60448201526064016105da565b604082015160c083015163ffffffff191663ffffffff9091161760c08301528782526020820186905260608201859052608082018490526113de8183896000612ade565b60c08201519055604001519695505050505050565b600080600060026000611407866060902090565b81526020019081526020016000209050600154925080600001549150611432836001605f1b16151590565b15611515576000806114448560601c90565b6001600160a01b031663d87629ac876040518263ffffffff1660e01b815260040161146f919061510a565b6040805180830381865afa15801561148b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114af9190615118565b915091506114c2826303ffffff81161490565b156114ee576b3ffffff000000000000000001985166b3ffffff00000000000000000604484901b161794505b6101ff81168103611512576101ff60ee1b1984166101ff60ee1b60ee83901b161793505b50505b9193909250565b6508000000000081161561155d5760405162461bcd60e51b81526020600482015260086024820152671b59dd8bd919585960c21b60448201526064016105da565b50565b6001600160a01b03821660008181526004602052604090819020805484019055517f1bbf55d483639f8103dc4e035af71a4fbdb16c80be740fa3eef81198acefa094906115b09084815260200190565b60405180910390a25050565b60008060006115cb858561355c565b915091506115d9828261377c565b925050505b92915050565b60008060008060016001607f1b038811156116415760405162461bcd60e51b815260206004820152601c60248201527f6d67762f6d4f726465722f66696c6c566f6c756d652f746f6f4269670000000060448201526064016105da565b61164a8961229b565b6116965760405162461bcd60e51b815260206004820152601a60248201527f6d67762f6d4f726465722f7469636b2f6f75744f6652616e676500000000000060448201526064016105da565b61169e614aa9565b61010081018a90526001600160a01b038716606082015287151560808201526116c5614b15565b8b815260006116d38d6113f3565b60e085019190915260c08401829052915060231c60ff16610160840152876117085760c082015160031c63ffffffff1661170a565b875b8361012001818152505061174a81600401600061173361172d8660e00151611a48565b60021d90565b815260200190815260200160002054600181141890565b60e0840181905261175a90613aa6565b60208084018290526000918252600583019052604090819020549083015260a083018b905260c082015160e083015161179391906122b6565b6117a08260e001516119fc565b60e08201516117b0906001612301565b60e0830181905281558151606090819020604080518f8152602081018f90528d15158183015290516001600160a01b038d16937f730e8e2cc287cd5296445ccef0dcf7f0695d8b3d620dcc9dd19c671a6f5663a5928290030190a3611816818484613adb565b611823836040015161195e565b8151606090206001600160a01b038a16907feab9f920eda38e2e10cfc76b3f85201b8bbe82fac69de4c4509001b66e5e33af8560c0015160405161186991815260200190565b60405180910390a3505080516020820151604083015160c090930151919d909c50919a5098509650505050505050565b6001600160a01b038216600090815260046020526040902054818110156119025760405162461bcd60e51b815260206004820152601960248201527f6d67762f696e73756666696369656e7450726f766973696f6e0000000000000060448201526064016105da565b6001600160a01b038316600081815260046020526040908190208484039055517f59c79d79be0fadf59fe689b6952b7ebe90201a3a1f00d4a31982377890bc6046906119519085815260200190565b60405180910390a2505050565b801561155d57604051600090339083908381818185875af1925050503d80600081146119a6576040519150601f19603f3d011682016040523d82523d6000602084013e6119ab565b606091505b505090508061048d5760405162461bcd60e51b815260206004820152601760248201527f6d67762f73656e6450656e616c7479526576657274656400000000000000000060448201526064016105da565b64010000000081161561155d5760405162461bcd60e51b81526020600482015260146024820152731b59dd8bdc99595b9d1c985b98de531bd8dad95960621b60448201526064016105da565b60006115de82613d14565b6000808080611a62888a613d8d565b90506000611a708960e01c90565b905063ffffffff60c08a901c16811580611a88575080155b15611aa357611aa08c60040160006117338660021d90565b93505b81600003611abd57611ab6848483613db4565b9350611adc565b600082815260058d01602052604090208054611ad99083613dda565b90555b80600003611af657611aef848484613df7565b9350611b1a565b600081815260058d016020526040902080546001600160e01b031660e084901b1790555b8115801590611b2857508015155b15611b3d578860009550955050505050611f1a565b868015611b48575081155b8015611b545750878313155b96505050811582178a6004016000611b6c8460021d90565b815260208101919091526040016000205581611ed9576000611b8e8260081d90565b90506000611b9c8860081d90565b8203611c0c57611bbb8360ac8b901c6001600160401b03165b90613e1e565b9050611bc78982613e39565b9850868015611bd4575080155b15611c0757600082815260038d01602052604090205415611c0757600082815260038d0160205260409020600160ff1b90555b611c4c565b600082815260038d016020526040902054611c319084906001600160ff1b0316611bb5565b9050600160ff1b8117600083815260038e0160205260409020555b80611eba57611c5b83600e1d90565b9150611c6788600e1d90565b8203611cd757611c8683606c8b901c6001600160401b03165b90613e5e565b9050611c928982613e6a565b9850868015611c9f575080155b15611cd257600082815260028d01602052604090205415611cd257600082815260028d0160205260409020600160ff1b90555b611d17565b600082815260028d016020526040902054611cfc9084906001600160ff1b0316611c80565b9050600160ff1b8117600083815260028e0160205260409020555b80611e7e57611d268360141d90565b9150611d328860141d90565b8203611d8d57611d5183602c8b901c6001600160401b03165b90613e8f565b9050611d5d8982613e9b565b9850868015611d6a575080155b15611d8857600160ff1b8117600083815260018e0160205260409020555b611dcd565b600082815260018d016020526040902054611db29084906001600160ff1b0316611d4b565b9050600160ff1b8117600083815260018e0160205260409020555b80611e4257611de583602a8b901c6003165b90613ec6565b9050611df18982613ed6565b985080611e075788879550955050505050611f1a565b8615611e4257611e1681613ef1565b600081815260018e0160205260409020549092506001600160ff1b03169050611e3f8982613e9b565b98505b8615611e7e57611e528183613f05565b600081815260028e0160205260409020549092506001600160ff1b03169050611e7b8982613e6a565b98505b8615611eba57611e8e8183613f05565b600081815260038e0160205260409020549092506001600160ff1b03169050611eb78982613e39565b98505b8615611ed657611ed360048d0160006117338486613f05565b93505b50505b8415611f1157611f0e6001600160401b036001600160801b038411600781901b85901c9190911115901560011b178890613f1d565b96505b50508483915091505b965096945050505050565b7507fffffffffffffffffffffffffffffff0000000000019831692508015611f5657677fffffe00000000019821691505b50908255600190910155565b6000603f82901c6101ff165b6103e80292915050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b179052915160009283918291891690611fde9085906150c2565b6000604051808303816000865af19150503d806000811461201b576040519150601f19603f3d011682016040523d82523d6000602084013e612020565b606091505b509150915081801561204a57508051158061204a57508080602001905181019061204a919061513c565b98975050505050505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1790529151600092839182918816906120b49085906150c2565b6000604051808303816000865af19150503d80600081146120f1576040519150601f19603f3d011682016040523d82523d6000602084013e6120f6565b606091505b5091509150818015612120575080511580612120575080806020019051810190612120919061513c565b979650505050505050565b6000806000836040516024016121419190615159565b60408051808303601f19018152919052602081018051632380fafd60e01b6001600160e01b03909116179052905060e0840135604881901c62ffffff169060601c60005a90508260408204820310156121da57604080516060810182527f6d67762f6e6f74456e6f756768476173466f724d616b657254726164650000008152600060208201819052918101919091526121da90606081fd5b60006121e7838587613f34565b965090505a820396508061222d5761222d60405180606001604052806e1b59dd8bdb585ad95c94995d995c9d608a1b81526020018960001b815260200188815250606081fd5b600061224b61223f60208b018b614d2c565b85308c60a00135611f78565b905080612290576122906040518060600160405280741b59dd8bdb585ad95c951c985b9cd9995c91985a5b605a1b81526020018a60001b815260200189815250606081fd5b505050505050915091565b6000620d89e71982121580156115de575050620d89e8121590565b6122bf8261151c565b600160ff1b811661048d5760405162461bcd60e51b815260206004820152600c60248201526b6d67762f696e61637469766560a01b60448201526064016105da565b64010000000060209190911b1664010000000019919091161790565b60a08201516040820151600091829182919060016001607f1b03602c82901c1690839061234990613f62565b90508760800151801561235c5750828211155b80612374575087608001511580156123745750828111155b1561238c5760608701829052608087018190526123ea565b8760800151156123c2576123b1836123ab896040015160401b60eb1d90565b90613f82565b6080880152606087018390526123ea565b6123dd836123d7896040015160401b60eb1d90565b90613fae565b6060880152608087018390525b50505060408401516000906060906001600160c01b038116604088015260e08701516503ff00000001600160ee1b0319811660e08901526060890151604051309182916378393c8891612442918d919060240161524e565b60408051601f198184030181529181526020820180516001600160e01b031660e09490941b93909317909252905161247a92506150c2565b6000604051808303816000865af19150503d80600081146124b7576040519150601f19603f3d011682016040523d82523d6000602084013e6124bc565b606091505b5060408a019390935260e089019190915292509050811561263057808060200190518101906124eb9190615118565b60c088015191965094506f6d67762f74726164655375636365737360801b93506001605e1b161561257b5760c0860151606088810151604051630c29692960e01b81529290911c91630c29692991612548918a919060040161524e565b600060405180830381600087803b15801561256257600080fd5b505af1158015612576573d6000803e3d6000fd5b505050505b606086018051885101808952905111156125cf5760405162461bcd60e51b81526020600482015260156024820152746d67762f746f74616c476f742f6f766572666c6f7760581b60448201526064016105da565b608086018051602089018051909101908190529051111561262b5760405162461bcd60e51b81526020600482015260166024820152756d67762f746f74616c476176652f6f766572666c6f7760501b60448201526064016105da565b61283c565b602081015160408201516060830151909650945092506e1b59dd8bdb585ad95c94995d995c9d608a1b83148061267d575082741b59dd8bdb585ad95c951c985b9cd9995c91985a5b605a1b145b8061269e575082731b59dd8bdb585ad95c949958d95a5d9951985a5b60621b145b156127365760a086015160481c62ffffff166101408801805191909101905260c08601516001605e1b161561262b5760c08601516060888101516040516303027c4560e51b81529290911c9163604f88a0916126ff918a919060040161524e565b600060405180830381600087803b15801561271957600080fd5b505af115801561272d573d6000803e3d6000fd5b5050505061283c565b827f6d67762f6e6f74456e6f756768476173466f724d616b65725472616465000000036127a55760405162461bcd60e51b815260206004820152601d60248201527f6d67762f6e6f74456e6f756768476173466f724d616b6572547261646500000060448201526064016105da565b82741b59dd8bdd185ad95c951c985b9cd9995c91985a5b605a1b036128045760405162461bcd60e51b81526020600482015260156024820152741b59dd8bdd185ad95c951c985b9cd9995c91985a5b605a1b60448201526064016105da565b60405162461bcd60e51b815260206004820152600d60248201526c36b3bb17b9bbb0b822b93937b960991b60448201526064016105da565b61287e8860050160008860200151815260200190815260200160002087604001518860a00151866f6d67762f74726164655375636365737360801b1415611f25565b505093509350939050565b60a084015160481c62ffffff16808411156128a2578093505b60008060006128b5888886038888613fde565b9250925092508287019650846f6d67762f74726164655375636365737360801b146129f15760006128e689896140c8565b60408b018051820190529050826129775788602001518a606001516001600160a01b03166129198b600001516060902090565b6060808d01516080808f015160408051938452602084019190915282018790529181018b90529081018690527f4f6fba9fcbdb2ad30462ad0e0e92fd280ffee4e24885661c7a7497802240008d9060a00160405180910390a46129eb565b88602001518a606001516001600160a01b03166129998b600001516060902090565b6060808d01516080808f015160408051938452602084019190915282018790529181018b90527f8e83cc09450b5666c4b273f69ceff6631efac5291f291422fdc1e6e6c9223e19910160405180910390a45b50610f1c565b81612a6757876020015189606001516001600160a01b0316612a188a600001516060902090565b6060808c015160808d015160408051928352602083019190915281018690527f88e7a2023de6638f80f184ea4126f2f532f046b745646d0b5ac8c205cc1b4874910160405180910390a4610f1c565b876020015189606001516001600160a01b0316612a898a600001516060902090565b7f5575d7d7c01adb4eeee1e2ec4a63652fdf5086d71fd325fa1af8034796b899048b606001518c60800151604051612acb929190918252602082015260400190565b60405180910390a4505050505050505050565b60808301516303ffffff811614612b375760405162461bcd60e51b815260206004820152601e60248201527f6d67762f77726974654f666665722f67617370726963652f746f6f426967000060448201526064016105da565b60a083015160441c6303ffffff1683608001511015612b645760a083015160441c6303ffffff1660808401525b60a0830151602c1c62ffffff1683606001511115612bc45760405162461bcd60e51b815260206004820152601d60248201527f6d67762f77726974654f666665722f6761737265712f746f6f4869676800000060448201526064016105da565b6000836020015111612c185760405162461bcd60e51b815260206004820152601b60248201527f6d67762f77726974654f666665722f67697665732f746f6f4c6f77000000000060448201526064016105da565b612c42612c288460c00151614159565b606085015160c086015191019060ee1c6101ff1690614169565b83602001511015612c955760405162461bcd60e51b815260206004820152601d60248201527f6d67762f77726974654f666665722f64656e736974792f746f6f4c6f7700000060448201526064016105da565b602083015160016001607f1b03811614612cf15760405162461bcd60e51b815260206004820152601b60248201527f6d67762f77726974654f666665722f67697665732f746f6f426967000000000060448201526064016105da565b82516040015180830760001281840501612d0b8183614182565b9350612d168461229b565b612d625760405162461bcd60e51b815260206004820152601e60248201527f6d67762f77726974654f666665722f7469636b2f6f75744f6652616e6765000060448201526064016105da565b604085015185516060902033906020808901516080808b01516060808d0151604080518e8152968701959095529385019190915283019190915281018490527fbeb2dc87c4db0b489fe0485121086dfff34fcaac67b1120861b95f1b6649c97b9060a00160405180910390a36000818152600588016020526040812060018101548615612e7b57612df38160601c90565b6001600160a01b0316336001600160a01b031614612e535760405162461bcd60e51b815260206004820152601c60248201527f6d67762f7570646174654f666665722f756e617574686f72697a65640000000060448201526064016105da565b612e5c81611f62565b604882901c62ffffff1601602582901c6303ffffff16620f4240020292505b861580612e9557506060890151604882901c62ffffff1614155b80612eae57506080890151602582901c6303ffffff1614155b80612ecd5750612ec18960c00151614159565b612eca82611f62565b14155b15612f5b576000612ee18a60c00151614159565b9050612f54338b606001516103e88481612efd57612efd6151e1565b048d60800151677fffffe000000000602582901b1668ff8000000000000000603f84901b166bffffff000000000000000000604886901b166bffffffffffffffffffffffff19606088901b16171717949350505050565b6001840155505b60008960800151612f6f8b60c00151614159565b8b606001510102620f424002905083811115612f9657612f9133858303611899565b612faa565b83811015612faa57612faa33828603611560565b50505060c087015160009150612fca9060ac1c6001600160401b03161590565b15612fd6575081613062565b612fe38760c00151611a48565b60e0880151909150602c1c60016001607f1b03161561306257600083821315905061301a89868a60e001518b60c001518686611a53565b60c08a0191909152905080156130605760c08801516130439060ac1c6001600160401b03161590565b1561305057839150613060565b61305d8860c00151611a48565b91505b505b8083136130815761307b6003841660c089015190613f1d565b60c08801525b60006130968960040160006117338760021d90565b9050806133a0576000806130aa8660081d90565b905060006130b88560081d90565b90508082036130d95760c08b015160ac1c6001600160401b0316925061314e565b600082815260038d0160205260409020546001600160ff1b031692508082121561314e5760c08b015160ac1c6001600160401b03168015158061312b5750600082815260038e01602052604090205415155b801561314b57600083815260038f0160205260409020600160ff1b831790555b50505b8082136131775761316d6131628489613e1e565b60c08d015190613e39565b60c08c015261319f565b61318d6131848489613e1e565b600160ff1b1790565b600083815260038e0160205260409020555b8261339c576131ae87600e1d90565b91506131ba85600e1d90565b90508082036131db5760c08b0151606c1c6001600160401b03169250613250565b600082815260028d0160205260409020546001600160ff1b03169250808212156132505760c08b0151606c1c6001600160401b03168015158061322d5750600082815260028e01602052604090205415155b801561324d57600083815260028f0160205260409020600160ff1b831790555b50505b8082136132795761326f6132648489613e5e565b60c08d015190613e6a565b60c08c0152613298565b6132866131848489613e5e565b600083815260028e0160205260409020555b8261339c576132a78760141d90565b91506132b38560141d90565b90508082036132d45760c08b0151602c1c6001600160401b03169250613324565b600082815260018d0160205260409020546001600160ff1b03169250808212156133245760c08b015161331290602c1c6001600160401b0316613184565b600082815260018e0160205260409020555b80821361334d576133436133388489613e8f565b60c08d015190613e9b565b60c08c015261336c565b61335a6131848489613e8f565b600083815260018e0160205260409020555b8261339c5760c08b01516133969061338b908990602a1c600316611ddf565b60c08d015190613ed6565b60c08c01525b5050505b60006133ac828661418e565b9050806000036133c8576133c1828686613db4565b91506133e7565b600081815260058b016020526040902080546133e49086613dda565b90555b6133f2828686613df7565b9150811582178a60040160006134088860021d90565b81526020808201929092526040016000908120929092558a015160e083901b60ab8b901b621fffff60ab1b1617602c9190911b7507fffffffffffffffffffffffffffffff00000000000161760009586526005909b01602052505060409092209790975550505050505050565b6001600160a01b0380851660009081526005602090815260408083208785168452825280832093861683529281528282203383529081905291902054828110156134f45760405162461bcd60e51b815260206004820152601060248201526f6d67762f6c6f77416c6c6f77616e636560801b60448201526064016105da565b3360008181526020848152604091829020868503908190558251938452908301526001600160a01b038681169288821692918a16917f6e791f456e6f5034d4928b936c32bf7e650d8228543ce7985d632ab03934837d910160405180910390a4505050505050565b60008060016001607f1b038411156135b65760405162461bcd60e51b815260206004820152601f60248201527f6d67762f726174696f46726f6d566f6c2f696e626f756e642f746f6f4269670060448201526064016105da565b60016001607f1b0383111561360d5760405162461bcd60e51b815260206004820181905260248201527f6d67762f726174696f46726f6d566f6c2f6f7574626f756e642f746f6f42696760448201526064016105da565b8260000361363057506ffffb12cdf1087799c03e5a811a7aaa1c90506000610ae2565b8360000361365357506f800276a52a62a71c29fc1cc441e523bc905060ff610ae2565b600083608086901b81613668576136686151e1565b04905060006136f7827e09010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f6307c4acdd60e01b6001600160801b03831160071b8315600890811b9190911784811c6001600160401b031060061b1784811c63ffffffff1060051b1793841c600181901c17600281901c17600481901c179081901c17601081901c170260fb1c1a1790565b9050816000036137495760405162461bcd60e51b815260206004820152601e60248201527f6d67762f726174696f46726f6d566f6c756d65732f7a65726f526174696f000060448201526064016105da565b607f81111561376a57607e1981019190911c9250608003607f019050610ae2565b607f0390811b92506080019050610ae2565b600061379b83836f800276a52a62a71c29fc1cc441e523bc60ff6141a8565b156137e85760405162461bcd60e51b815260206004820152601860248201527f6d67762f7469636b46726f6d526174696f2f746f6f4c6f77000000000000000060448201526064016105da565b6138056ffffb12cdf1087799c03e5a811a7aaa1c600085856141a8565b156138525760405162461bcd60e51b815260206004820152601960248201527f6d67762f7469636b46726f6d526174696f2f746f6f486967680000000000000060448201526064016105da565b6000604061386184607f61528c565b901b90506000613872607f806152b3565b85901c800260c081901c6780000000000000001692909217607f83811c60ff94851c1c800260c181901c674000000000000000169290921782821c92851c9290921c800260c281901c672000000000000000169290921782821c92851c9290921c800260c381901c671000000000000000169290921782821c92851c9290921c800260c481901c670800000000000000169290921782821c92851c9290921c800260c581901c670400000000000000169290921782821c92851c9290921c800260c681901c670200000000000000169290921782821c92851c9290921c800260c781901c670100000000000000169290921782821c92851c9290921c800260c881901c6680000000000000169290921782821c92851c9290921c800260c981901c6640000000000000169290921782821c92851c9290921c800260ca81901c6620000000000000169290921782821c92851c9290921c800260cb81901c6610000000000000169290921782821c9290941c9190911c800260cc81901c6608000000000000169390931792901c90506000613a1683691b13d180eb882abba6436152c6565b905060006080613a366f0147b240d5bf82739a122ad4969722368461528c565b901d905060006080613a58846fd9e63e52eeeb7875a857348794e736756152f6565b901d9050600080613a68836141c7565b915091506000613a7a8b8b85856141a8565b905083851480613a875750805b15613a9457849850613a98565b8398505b505050505050505092915050565b60006115de826001600160401b036001600160801b038211600781901b83901c9190911115901560011b176040021b60e01c90565b60008260a00151118015613b045750816101000151613b01826040015160401b60eb1d90565b13155b8015613b14575060008160200151115b8015613b2557506000826101600151115b8015613b3b575081610120015182610140015111155b15613c5d5761016082018051600019019052602080820151600090815260058501909152604081206001015460a08301528080613b7986868661231d565b60608701516080880151602089015160408a015160a08b015196995094975092955090939092906f6d67762f74726164655375636365737360801b869003613bd9578960800151613bca5783613bcc565b845b60a08b0180519190910390525b613bf28b8b848c60e001518d6000015160400151614239565b60e08b01526020808b01829052600091825260058d01905260409081902054908a0152613c208b8b8b613adb565b6060890185905260808901849052602089018390526040890182905260a08901819052613c508a8a8a8a8a612889565b5050505050505050505050565b604080820151825190910151600090613c77908390613d8d565b90508260200151600014613ce957613c8f8260e01c90565b15613cba5760408301516001600160e01b031660208085015160009081526005880190915260409020555b6000613cc68260021d90565b9050613cd68560e0015180151790565b6000918252600487016020526040909120555b60e0830151613cf9906000612301565b60e084018190528555613d0c84846144f8565b50505b505050565b6000806002600680806001613d2f6003602a8a901c16614592565b03901b613d48602c88901c6001600160401b0316614592565b17901b613d61606c87901c6001600160401b0316614592565b17901b613d7a60ac86901c6001600160401b0316614592565b17901b60ec84901c600316179392505050565b6000613dad82613da08560401b60eb1d90565b9060008183071391050190565b9392505050565b6000600383166040810260e00383811b63ffffffff90911b198616175b95945050505050565b60c01b63ffffffff60c01b1663ffffffff60c01b19919091161790565b6000600383166002810260010160200260e00383811b63ffffffff90911b19861617613dd1565b600080613e2a836145ed565b6001901b939093189392505050565b60ac1b67ffffffffffffffff60ac1b1667ffffffffffffffff60ac1b19919091161790565b600080613e2a83614602565b606c1b67ffffffffffffffff606c1b1667ffffffffffffffff606c1b19919091161790565b600080613e2a83614610565b602c1b6d0ffffffffffffffff00000000000166d0ffffffffffffffff0000000000019919091161790565b600080613e2a8360141d60010190565b602a1b650c000000000016650c000000000019919091161790565b60006001613efe8361461e565b0392915050565b6000613f108361461e565b6040830201905092915050565b60ec1b600360ec1b16600360ec1b19919091161790565b600080613f3f614b75565b60648185516020870160008a8af192508060448415020151915050935093915050565b60006115de602c83901c60016001607f1b03166123ab8460401b60eb1d90565b6000806000613f90856141c7565b915091506115d9848302826001811b600019018216151591901c0190565b60008080613fc3613fbe8661531e565b6141c7565b909250905080613fd3858461533a565b901c95945050505050565b6000806000808760405180604001604052808881526020018781525060405160240161400b929190615351565b60408051601f198184030181529190526020810180516001600160e01b03166331f86ed160e21b17905260a089015190915060009061404a9060601c90565b905060005a90508860408204820310156140a65760405162461bcd60e51b815260206004820181905260248201527f6d67762f6e6f74456e6f756768476173466f724d616b6572506f7374686f6f6b60448201526064016105da565b6140b1828a85613f34565b90955093505a810395505050509450945094915050565b60a082015160009062ffffff604882901c169082906140e690611f62565b60a08601519083019060251c6303ffffff16620f4240020290508184111561410c578193505b600061411b8660e00151614159565b60c08701519086019060441c6303ffffff16620f424002029050818111156141405750805b6115d96141518760a0015160601c90565b828403611560565b6000602182901c6101ff16611f6e565b6000602061417684614629565b8302901c905092915050565b6000613dad82846152c6565b60006001600260038416020160200283901b60e01c613dad565b600081841180613dd157508184148015613dd157505050909110919050565b6000806141d383614650565b909250905060eb83901b60007ed89e8c075c4155dd3213326cbbcf86fd1f63f5c205dd2ad2cf44f9349b39e180830782139083050390607e1982850101908113156142215793841c9361422b565b8060000385901b94505b81607f039350505050915091565b600080806142478685613d8d565b60e088015190915063ffffffff60c088901c1690614266818484613db4565b9050816000036144e25761427c81846000613df7565b90508061449e57801581178a60040160006142978660021d90565b81526020019081526020016000208190555060006142c584611bb58a6001600160401b0360ac9190911c1690565b905060006142d38560081d90565b90508161447a57600081815260038d0160205260409020541561430857600081815260038d0160205260409020600160ff1b90555b61431285600e1d90565b905061432b85606c8b901c6001600160401b0316611c80565b91508161444e57600081815260028d0160205260409020541561436057600081815260028d0160205260409020600160ff1b90555b61436a8560141d90565b905061438385602c8b901c6001600160401b0316611d4b565b91508161442257600081815260018d0160205260409020600160ff1b90556143b285602a8b901c600316611ddf565b91506143be8983613ed6565b985081614403576143cf8983613e9b565b98506143db8983613e6a565b98506143e78983613e39565b600060e08d0181905290995096508895506144ee945050505050565b61440c82613ef1565b600081815260018e016020526040902054925090505b61442c8983613e9b565b98506144388282613f05565b600081815260028e016020526040902054925090505b6144588983613e6a565b98506144648282613f05565b600081815260038e016020526040902054925090505b6144848983613e39565b985061449960048d0160006117338585613f05565b925050505b6001600160401b036001600160801b038211600781901b83901c9190911115901560011b176144cd8882613f1d565b97506144de82826040021b60e01c90565b9250505b60e08901529250849150505b9550959350505050565b60e08101516000906127109060f71c60ff168451028161451a5761451a6151e1565b0490508015614532578251819003835260c083018190525b825115613d0f578151516060840151845161454e929190612056565b613d0f5760405162461bcd60e51b815260206004820152601560248201527436b3bb17a6b3bb2330b4b62a37a830bcaa30b5b2b960591b60448201526064016105da565b7e011c021d0e18031e16140f191104081f1b0d17151310071a0c12060b050a096001600160401b0382811680196001019093161663ffffffff811160051b921560061b9290921791821c63077cb53160e01b0260fb1c1a1790565b6000603f6145fb8360021d90565b1692915050565b6000603f6145fb8360081d90565b6000603f6145fb83600e1d90565b60006115de82614592565b60006007821161463a575060031690565b506004600382161760029190911c600119011b90565b6000806000808412614662578361466b565b61466b8461531e565b9050620d89e88111156146c05760405162461bcd60e51b815260206004820152601760248201527f6d67762f6162735469636b2f6f75744f66426f756e647300000000000000000060448201526064016105da565b600060018216156146e3576ffff97272373d413259a46990580e213993506146eb565b600160801b93505b6002821615614715576080614710856ffff2e50f5f656932ef12357cf3c7fdcb61533a565b901c93505b600482161561473f57608061473a856fffe5caca7e10e4e61c3624eaa0941ccf61533a565b901c93505b6008821615614769576080614764856fffcb9843d60f6159c9db58835c92664361533a565b901c93505b601082161561479357608061478e856fff973b41fa98c081472e6896dfb254bf61533a565b901c93505b60208216156147bd5760806147b8856fff2ea16466c96a3843ec78b326b5286061533a565b901c93505b60408216156147e75760806147e2856ffe5dee046a99a2a811c461f1969c305261533a565b901c93505b608082161561481157608061480c856ffcbe86c7900a88aedcffc83b479aa3a361533a565b901c93505b61010082161561483c576080614837856ff987a7253ac413176f2b074cf7815e5361533a565b901c93505b610200821615614867576080614862856ff3392b0822b70005940c7a398e4b70f261533a565b901c93505b61040082161561489257608061488d856fe7159475a2c29b7443b29c7fa6e889d861533a565b901c93505b6108008216156148bd5760806148b8856fd097f3bdfd2022b8845ad8f792aa582561533a565b901c93505b6110008216156148e85760806148e3856fa9f746462d870fdf8a65dc1f90e061e461533a565b901c93505b61200082161561492057608061490e856fe1b0d342ada5437121767bec575e65ed61533a565b901c935061491d6001826152f6565b90505b614000821615614958576080614946856fc6f84d7e5f423f66048c541550bf3e9661533a565b901c93506149556002826152f6565b90505b61800082161561499057608061497e856f9aa508b5b7a84e1c677de54f3e99bc8f61533a565b901c935061498d6004826152f6565b90505b620100008216156149c95760806149b7856fbad5f1bdb70232cd33865244bdcc089c61533a565b901c93506149c66009826152f6565b90505b62020000821615614a025760806149f0856f885b9613d7e87aa498106fb7fa5edd3761533a565b901c93506149ff6012826152f6565b90505b62040000821615614a3b576080614a29856f9142e0723efb884889d1f447715afacd61533a565b901c9350614a386025826152f6565b90505b62080000821615614a74576080614a62856fa4d9a773d61316918f140bd96e8e681461533a565b901c9350614a71604b826152f6565b90505b6000851315614a9557600184856000030401935080614a929061531e565b90505b614aa08160806152f6565b92505050915091565b60405180610180016040528060008152602001600081526020016000815260200160006001600160a01b03168152602001600015158152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60408051610160810190915260006101008201818152610120830182905261014083019190915281908152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60405180608001604052806004906020820280368337509192915050565b8183823760009101908152919050565b80356001600160a01b03811681146105e357600080fd5b600060608284031215614bcc57600080fd5b604051606081018181106001600160401b0382111715614bfc57634e487b7160e01b600052604160045260246000fd5b604052905080614c0b83614ba3565b8152614c1960208401614ba3565b6020820152604083013560408201525092915050565b6000806000806000806101008789031215614c4957600080fd5b614c538888614bba565b986060880135985060808801359760a0810135975060c0810135965060e00135945092505050565b801515811461155d57600080fd5b60008060008060c08587031215614c9f57600080fd5b614ca98686614bba565b9350606085013592506080850135915060a0850135614cc781614c7b565b939692955090935050565b600080600080600060e08688031215614cea57600080fd5b614cf48787614bba565b9450606086013593506080860135925060a0860135614d1281614c7b565b9150614d2060c08701614ba3565b90509295509295909350565b600060208284031215614d3e57600080fd5b613dad82614ba3565b600060208284031215614d5957600080fd5b5035919050565b60008060008060a08587031215614d7657600080fd5b614d808686614bba565b935060608501356001600160401b0380821115614d9c57600080fd5b818701915087601f830112614db057600080fd5b813581811115614dbf57600080fd5b8860208260071b8501011115614dd457600080fd5b602083019550809450505050614dec60808601614ba3565b905092959194509250565b60008060008060808587031215614e0d57600080fd5b614e1685614ba3565b9350614e2460208601614ba3565b9250614e3260408601614ba3565b9396929550929360600135925050565b600080600060a08486031215614e5757600080fd5b614e618585614bba565b9250606084013591506080840135614e7881614c7b565b809150509250925092565b600080828403610160811215614e9857600080fd5b61014080821215614ea857600080fd5b849350614eb6818601614ba3565b925050509250929050565b6000806000806000806101008789031215614edb57600080fd5b614ee58888614bba565b9550606087013594506080870135935060a0870135925060c08701359150614f0f60e08801614ba3565b90509295509295509295565b60008060008060008060008060006101208a8c031215614f3a57600080fd5b614f438a614ba3565b9850614f5160208b01614ba3565b9750614f5f60408b01614ba3565b9650614f6d60608b01614ba3565b955060808a0135945060a08a0135935060c08a013560ff81168114614f9157600080fd5b8093505060e08a013591506101008a013590509295985092959850929598565b600080600080600060e08688031215614fc957600080fd5b614fd38787614bba565b976060870135975060808701359660a0810135965060c00135945092505050565b600080600080600060e0868803121561500c57600080fd5b6150168787614bba565b9450606086013593506080860135925060a086013561503481614c7b565b9497939650919460c0013592915050565b634e487b7160e01b600052603260045260246000fd5b80516001600160a01b03908116835260208083015190911690830152604090810151910152565b6101008101615091828961505b565b6060820196909652608081019490945260a084019290925260c08301526001600160a01b031660e090910152919050565b6000825160005b818110156150e357602081860181015185830152016150c9565b506000920191825250919050565b60006020828403121561510357600080fd5b5051919050565b606081016115de828461505b565b6000806040838503121561512b57600080fd5b505080516020909101519092909150565b60006020828403121561514e57600080fd5b8151613dad81614c7b565b61014081016001600160a01b038061517085614ba3565b1683528061518060208601614ba3565b1660208401525060408301356040830152606083013560608301526080830135608083015260a083013560a083015260c083013560c083015260e083013560e083015261010080840135818401525061012080840135818401525092915050565b634e487b7160e01b600052601260045260246000fd5b61520282825161505b565b6020810151606083015260408101516080830152606081015160a0830152608081015160c083015260a081015160e083015260c081015161010083015260e08101516101208301525050565b610160810161525d82856151f7565b6001600160a01b03929092166101409190910152919050565b634e487b7160e01b600052601160045260246000fd5b81810360008312801583831316838312821617156152ac576152ac615276565b5092915050565b818103818111156115de576115de615276565b80820260008212600160ff1b841416156152e2576152e2615276565b81810583148215176115de576115de615276565b808201828112600083128015821682158216171561531657615316615276565b505092915050565b6000600160ff1b820161533357615333615276565b5060000390565b80820281158282048414176115de576115de615276565b610180810161536082856151f7565b82516101408301526020830151610160830152939250505056fea2646970667358221220f20c555a0b8c655435611cf76124491da7fdaa415277e469ea93b70baa3a993164736f6c63430008140033

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

000000000000000000000000d137d6c5e565c480773e3031a5dab5b9327668e300000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000001e84800000000000000000000000004300000000000000000000000000000000000002000000000000000000000000d70b96bcc12d45f347b9b70899dcbb3dfe13384f0000000000000000000000002536fe9ab3f511540f2f9e2ec2a805005c3dd800000000000000000000000000441f6a3d415708f89e4adbb6b995371554aa909e

-----Decoded View---------------
Arg [0] : governance (address): 0xd137D6C5e565c480773e3031a5DaB5B9327668e3
Arg [1] : gasprice (uint256): 500
Arg [2] : gasmax (uint256): 2000000
Arg [3] : blastContract (address): 0x4300000000000000000000000000000000000002
Arg [4] : blastGovernor (address): 0xD70b96BcC12D45f347b9B70899dCbb3DFE13384F
Arg [5] : blastPointsContract (address): 0x2536FE9ab3F511540F2f9e2eC2A805005C3Dd800
Arg [6] : blastPointsOperator (address): 0x441f6A3D415708f89e4AdBB6b995371554aA909E

-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 000000000000000000000000d137d6c5e565c480773e3031a5dab5b9327668e3
Arg [1] : 00000000000000000000000000000000000000000000000000000000000001f4
Arg [2] : 00000000000000000000000000000000000000000000000000000000001e8480
Arg [3] : 0000000000000000000000004300000000000000000000000000000000000002
Arg [4] : 000000000000000000000000d70b96bcc12d45f347b9b70899dcbb3dfe13384f
Arg [5] : 0000000000000000000000002536fe9ab3f511540f2f9e2ec2a805005c3dd800
Arg [6] : 000000000000000000000000441f6a3d415708f89e4adbb6b995371554aa909e


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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